<?php

namespace Drupal\languagewire_translation_provider\Ui\CheckoutUi;

use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\languagewire_translation_provider\Adapter\Database\LanguageWireConfigurationItemRepositoryInterface;
use Drupal\languagewire_translation_provider\Adapter\SystemInterface;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtJobInterface;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtTranslatorInterface;
use Drupal\languagewire_translation_provider\Domain\ProjectTemplate;
use Drupal\languagewire_translation_provider\Settings\JobSettings;
use Drupal\languagewire_translation_provider\TranslatorUi;
use Drupal\languagewire_translation_provider\Ui\ItemsInteractor;
use function array_values;
use function json_encode;

/**
 * Template or service form.
 */
final class TemplateOrServiceForm {
  use TranslatorUi;
  use StringTranslationTrait;

  /**
   * Asset library path.
   */
  private const ASSET_LIBRARY_PATH = 'languagewire_translation_provider/languagewire_translation_provider_asset_library';

  /**
   * Drupal system.
   *
   * @var \Drupal\languagewire_translation_provider\Adapter\SystemInterface
   */
  protected SystemInterface $system;

  /**
   * LanguageWire configuration item repository.
   *
   * @var \Drupal\languagewire_translation_provider\Adapter\Database\LanguageWireConfigurationItemRepositoryInterface
   */
  protected LanguageWireConfigurationItemRepositoryInterface $configurationItemRepository;

  /**
   * Constructs a new TemplateOrServiceForm object.
   *
   * @param \Drupal\languagewire_translation_provider\Adapter\SystemInterface $system
   *   Drupal system.
   * @param \Drupal\languagewire_translation_provider\Adapter\Database\LanguageWireConfigurationItemRepositoryInterface $configurationItemRepository
   *   LanguageWire configuration item repository.
   */
  public function __construct(
    SystemInterface $system,
    LanguageWireConfigurationItemRepositoryInterface $configurationItemRepository
  ) {
    $this->system = $system;
    $this->configurationItemRepository = $configurationItemRepository;
  }

  /**
   * Build template or service form.
   */
  public function buildTemplateOrServiceForm(TmgmtJobInterface $job, array $form): array {
    $jobSettings = $job->settings();
    $translator = $job->translator();

    $availableProjectTemplates = $this->getAllAvailableProjectTemplatesForJob($job);

    $defaultValue = ($jobSettings->getProjectTemplate() != NULL)
            ? $this->prefixTemplateId($jobSettings->getProjectTemplate())
            : $this->prefixServiceId($jobSettings->getService());

    $specificValidator = new TemplateOrServiceFormValidator($job, $availableProjectTemplates);

    $form['template_or_service'] = [
      '#type' => 'select',
      '#title' => $this->t('Templates & Services'),
      '#required' => TRUE,
      '#default_value' => $defaultValue,
      '#description' => $this->t('Please select either a template or a service for your translation job.'),
      '#options' => $this->getTemplatesAndServicesDropdown($translator, $availableProjectTemplates),
      '#attributes' => [
        'data-project-templates' => $this->serializeProjectTemplates($translator, $availableProjectTemplates),
        'data-language-mappings' => $this->serializeLanguageMappings($translator),
      ],
      '#element_validate' => [[$specificValidator, 'validateDropdownCallback']],
    ];

    return $this->attachJavascript($form);
  }

  /**
   * Get templates and services dropdown.
   */
  private function getTemplatesAndServicesDropdown(TmgmtTranslatorInterface $translator, array $availableProjectTemplates): array {
    $checkoutServices = ItemsInteractor::sortItems($this->getCheckoutServices($translator));
    $dropdown = ['Services' => $this->prefixArrayKeys($checkoutServices, JobSettings::SERVICE_PREFIX)];
    $projectTemplates = $this->prefixArrayKeys($this->getCheckoutProjectTemplates($translator, $availableProjectTemplates), JobSettings::PROJECT_TEMPLATE_PREFIX);

    if (!empty($projectTemplates)) {
      $dropdown["Project Templates"] = $projectTemplates;
    }

    return $dropdown;
  }

  /**
   * Get prefixed field with fallback.
   */
  private function prefixServiceId(?int $serviceId): string {
    return JobSettings::SERVICE_PREFIX . $serviceId;
  }

  /**
   * Get prefixed template id.
   */
  private function prefixTemplateId(?int $projectTemplateId): string {
    return JobSettings::PROJECT_TEMPLATE_PREFIX . $projectTemplateId;
  }

  /**
   * Prefix array keys.
   */
  private function prefixArrayKeys(array $options, string $prefix): array {
    $result = [];

    foreach ($options as $key => $value) {
      $result[$prefix . $key] = $value;
    }

    return $result;
  }

  /**
   * Get checkout services.
   */
  private function getCheckoutServices(TmgmtTranslatorInterface $translator): array {
    $selectedServices = $translator->getSelectedServices();
    $pluginConfiguration = $translator->getPluginConfiguration($this->system);
    $availableServices = $pluginConfiguration->getAvailableServicesFlat();

    return $this->filterOutNotSelectedItems($availableServices, $selectedServices);
  }

  /**
   * Get checkout project templates.
   */
  private function getCheckoutProjectTemplates(TmgmtTranslatorInterface $translator, array $availableProjectTemplates): array {
    $selectedProjectTemplates = $translator->getSelectedProjectTemplateIds();
    $availableProjectTemplateDictionary = $this->generateProjectTemplateDictionary($availableProjectTemplates);

    return $this->filterOutNotSelectedItems($availableProjectTemplateDictionary, $selectedProjectTemplates);
  }

  /**
   * Attach javascript.
   */
  private function attachJavascript(array $form): array {
    $form['#attached']['library'][] = self::ASSET_LIBRARY_PATH;

    return $form;
  }

  /**
   * Serialize project templates.
   */
  private function serializeProjectTemplates(TmgmtTranslatorInterface $translator, array $availableProjectTemplates): string {
    $selectedProjectTemplateIds = $translator->getSelectedProjectTemplateIds();

    $selectedProjectTemplates = array_filter($availableProjectTemplates, function (ProjectTemplate $projectTemplate) use ($selectedProjectTemplateIds) {
      return in_array($projectTemplate->getPlatformId(), $selectedProjectTemplateIds);
    });

    $enrichedProjectTemplates = array_map(function (ProjectTemplate $projectTemplate) use ($translator) {
      $user = $this->configurationItemRepository->getByIdAndType($translator,
        $projectTemplate->getUserId(),
        'user');
      $workArea = $this->configurationItemRepository->getByIdAndType($translator,
        $projectTemplate->getWorkAreaId(),
        'work_area');
      $translationMemory = $this->configurationItemRepository->getByIdAndType($translator,
        $projectTemplate->getTranslationMemoryId() ?? 0,
        'translation_memory');
      $termBase = $this->configurationItemRepository->getByIdAndType($translator,
        $projectTemplate->getTermBaseId() ?? 0,
        'term_base');
      $invoicingAccount = $this->configurationItemRepository->getByIdAndType($translator,
        $projectTemplate->getInvoicingAccountId(),
        'invoicing_account');

      return [
        "user" => $user,
        "workArea" => $workArea,
        "translationMemory" => $translationMemory,
        "termBase" => $termBase,
        "invoicingAccount" => $invoicingAccount,
        "order_type" => $projectTemplate->getOrderType(),
        "projectTemplate" => $projectTemplate,
      ];
    }, $selectedProjectTemplates);

    return json_encode(array_values($enrichedProjectTemplates));
  }

  /**
   * Serialize language mappings.
   */
  private function serializeLanguageMappings(TmgmtTranslatorInterface $translator): string {
    return json_encode($translator->getRemoteLanguagesMappings());
  }

  /**
   * Get all available project templates for job.
   */
  private function getAllAvailableProjectTemplatesForJob(TmgmtJobInterface $job): array {
    $translator = $job->translator();
    $pluginConfiguration = $translator->getPluginConfiguration($this->system);
    $projectTemplates = $pluginConfiguration->getAvailableProjectTemplates();

    $matchingSourceLanguageProjectTemplates = array_filter($projectTemplates, function ($projectTemplate) use ($job) {
        return $projectTemplate->getSourceLanguage() == $job->sourceLanguage()->remoteLanguage();
    });

    return array_filter($matchingSourceLanguageProjectTemplates, function ($projectTemplate) use ($job) {
        return in_array($job->targetLanguage()->remoteLanguage(), $projectTemplate->getTargetLanguages());
    });
  }

  /**
   * Generate project template dictionary.
   */
  private function generateProjectTemplateDictionary(array $projectTemplates): array {
    $pairs = [];

    foreach ($projectTemplates as $projectTemplate) {
      $pairs[$projectTemplate->getPlatformId()] = $projectTemplate->getName();
    }

    return $pairs;
  }

}
