<?php

declare(strict_types=1);

namespace Drupal\languagewire_translation_provider\Ui;

use Drupal\Core\Datetime\DateFormatterInterface;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\languagewire_translation_provider\Adapter\SystemInterface;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtJob;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtJobInterface;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtTranslatorInterface;
use Drupal\languagewire_translation_provider\LanguageWireConnector;
use Drupal\languagewire_translation_provider\Platform\DeliveryDateCalculator;
use Drupal\languagewire_translation_provider\Settings\OrderType;
use Drupal\languagewire_translation_provider\TranslatorUi;
use Drupal\languagewire_translation_provider\Ui\CheckoutUi\TemplateOrServiceForm;
use Drupal\tmgmt\JobInterface;
use function date_default_timezone_get;

/**
 * Checkout UI.
 */
final class CheckoutUi {
  use TranslatorUi;
  use StringTranslationTrait;

  /**
   * System adapter.
   *
   * @var \Drupal\languagewire_translation_provider\Adapter\SystemInterface
   */
  private SystemInterface $system;
  /**
   * Date formatter.
   *
   * @var \Drupal\Core\Datetime\DateFormatterInterface
   */
  private DateFormatterInterface $dateFormatter;
  /**
   * Template or service form.
   *
   * @var \Drupal\languagewire_translation_provider\Ui\CheckoutUi\TemplateOrServiceForm
   */
  private TemplateOrServiceForm $templateOrServiceForm;

  /**
   * Constructs a new CheckoutUi object.
   *
   * @param \Drupal\languagewire_translation_provider\Adapter\SystemInterface $system
   *   System adapter.
   * @param \Drupal\languagewire_translation_provider\Ui\CheckoutUi\TemplateOrServiceForm $templateOrServiceForm
   *   Template or service form.
   * @param \Drupal\Core\Datetime\DateFormatterInterface $dateFormatter
   *   Date formatter.
   */
  public function __construct(SystemInterface $system, TemplateOrServiceForm $templateOrServiceForm, DateFormatterInterface $dateFormatter) {
    $this->system = $system;
    $this->templateOrServiceForm = $templateOrServiceForm;
    $this->dateFormatter = $dateFormatter;
  }

  /**
   * Build checkout form.
   *
   * @throws \Drupal\tmgmt\TMGMTException
   */
  public function buildCheckoutForm(array $form, JobInterface $tmgmtJob): array {
    $job = new TmgmtJob($tmgmtJob);
    $jobSettings = $job->settings();
    $translator = $job->translator();

    $form = $this->templateOrServiceForm->buildTemplateOrServiceForm($job, $form);

    $form['user'] = [
      '#type' => 'select',
      '#title' => $this->t('User'),
      '#required' => TRUE,
      '#default_value' => $jobSettings->getUser(),
      '#description' => $this->t('Please select a user for your translation job.'),
      '#options' => ItemsInteractor::sortItems($this->getCheckoutUser($translator), ItemsInteractor::GENERAL_SUFFIX),
    // Allow values not present in #options, due to project templates.
      '#validated' => TRUE,
    ];
    $form['work_area'] = [
      '#type' => 'select',
      '#title' => $this->t('Work Area'),
      '#required' => TRUE,
      '#default_value' => $jobSettings->getWorkArea(),
      '#description' => $this->t('Please select a work area for your translation job.'),
      '#options' => ItemsInteractor::sortItems($this->getCheckoutWorkAreas($translator), ItemsInteractor::GENERAL_SUFFIX),
    // Allow values not present in #options, due to project templates.
      '#validated' => TRUE,
    ];
    $form['translation_memory'] = [
      '#type' => 'select',
      '#title' => $this->t('Translation Memory'),
      '#required' => TRUE,
      '#default_value' => $jobSettings->getTranslationMemory(),
      '#description' => $this->t('Please select a translation memory for your translation job.'),
      '#options' => ItemsInteractor::sortItems($this->getCheckoutTranslationMemories($translator)),
    // Allow values not present in #options, due to project templates.
      '#validated' => TRUE,
    ];
    $form['term_base'] = [
      '#type' => 'select',
      '#title' => $this->t('Term Base'),
      '#required' => FALSE,
      '#default_value' => $jobSettings->getTermBase(),
      '#description' => $this->t('Please select a term base for your translation job.'),
      '#options' => [0 => $this->t('- Select -')] + ItemsInteractor::sortItems($this->getCheckoutTermBases($translator)),
    // Allow values not present in #options, due to project templates.
      '#validated' => TRUE,
    ];
    $form['invoicing_account'] = [
      '#type' => 'select',
      '#title' => $this->t('Invoicing Account'),
      '#required' => TRUE,
      '#default_value' => $jobSettings->getInvoicingAccount(),
      '#description' => $this->t('Please select an invoicing account for your translation job.'),
      '#options' => ItemsInteractor::sortItems($this->getCheckoutInvoicingAccounts($translator)),
    // Allow values not present in #options, due to project templates.
      '#validated' => TRUE,
    ];
    $form['order_type'] = [
      '#type' => 'select',
      '#title' => $this->t('Order Type'),
      '#required' => TRUE,
      '#default_value' => $jobSettings->getOrderType() ?? $translator->getSelectedOrderTypes(),
      '#description' => $this->t('Please select an order type for your translation job.'),
      '#options' => OrderType::getAll(),
    // Allow values not present in #options, due to project templates.
      '#validated' => TRUE,
    ];

    try {
      $defaultDeadline = $jobSettings->getDeadline() ?: $this->getDefaultDeadline($translator);
    }
    catch (\DateMalformedStringException | \DateInvalidTimeZoneException) {
      $defaultDeadline = date('Y-m-d', strtotime('+1 days'));
    }

    $form['deadline'] = [
      '#type' => 'date',
      '#date_type_element' => 'none',
      '#title' => $this->t('Desired delivery date'),
      '#description' => $this->suggestedDeliveryDateMessage($job),
      '#required' => TRUE,
      '#default_value' => $defaultDeadline,
      '#element_validate' => [[self::class, 'validateDeadline']],
    ];
    $form['briefing'] = [
      '#type' => 'text_format',
      '#format' => 'basic_html',
      '#allowed_formats' => ['basic_html'],
      '#title' => $this->t('Briefing'),
      '#default_value' => $jobSettings->getBriefing(),
      '#description' => $this->t('Briefing to LanguageWire.'),
      '#required' => FALSE,
    ];
    $form['bundling'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Bundle languages'),
      '#default_value' => $translator->isBundlingEnabled() || $jobSettings->isBundling(),
      '#description' => $this->t('Enable languages bundling.'),
      '#required' => FALSE,
      '#prefix' => '<div class="hidden">',
      '#suffix' => '</div>',
    ];

    $form['content_settings'] = [
      '#type' => 'details',
      '#title' => $this->t('Content settings'),
      '#prefix' => '<div id="tmgmt-ui-translator-settings">',
      '#suffix' => '</div>',
      '#tree' => TRUE,
      '#description' => $this->t('Include/exclude content fields from translation.'),
    ];
    $form['content_settings'] += $this->generateContentSettingsSubform($job);

    return $form;
  }

  /**
   * Generate content settings subform.
   */
  private function generateContentSettingsSubform(TmgmtJobInterface $job): array {
    $form = [];
    foreach ($job->items() as $item) {
      $itemId = $item->itemId();
      $form["item-$itemId"] = [
        '#type' => 'details',
        '#title' => "Job item \"{$item->label()}\"",
        '#collapsible' => TRUE,
        '#collapsed' => TRUE,
        '#tree' => TRUE,
      ];
      foreach ($item->getContent() as $content) {
        $contentId = $content->getSanitizedContentId();
        $isEnabled = $job->settings()->isItemContentEnabledForTranslation($itemId, $contentId);
        $form["item-$itemId"]["content-$contentId"] = [
          '#type' => 'checkbox',
          '#title' => $content->getLabel(),
          '#default_value' => $isEnabled,
          '#required' => FALSE,
        ];
      }
    }

    return $form;
  }

  /**
   * Validate deadline.
   */
  public static function validateDeadline(array $element, FormStateInterface $formState): void {
    if (!isset($element['#value'])) {
      $formState->setError($element, t('The @dateField is unset. Please select date from calendar.', ['@dateField' => $element['#title']]));
      return;
    }

    $validDeadlineFormat = \DateTimeImmutable::createFromFormat(LanguageWireConnector::DEADLINE_DATE_FORMAT, $element['#value']);
    if (!$validDeadlineFormat) {
      $formState->setError($element, t('The @dateField is invalid. Please select date from calendar.', ['@dateField' => $element['#title']]));
      return;
    }

    $now = new DrupalDateTime();
    $userSelectedDeadline = new DrupalDateTime($element['#value']);
    if ($userSelectedDeadline <= $now) {
      $formState->setError($element, t('The @dateField must be a future date!', ['@dateField' => $element['#title']]));
    }
  }

  /**
   * Get default deadline.
   *
   * @throws \DateMalformedStringException
   * @throws \DateInvalidTimeZoneException
   */
  private function getDefaultDeadline(TmgmtTranslatorInterface $translator): ?string {
    if (!$translator->isDefaultDeliveryDateEnabled()) {
      return NULL;
    }

    $timezone = new \DateTimeZone(date_default_timezone_get());
    $sevenDaysInTheFuture = new \DateTimeImmutable('+7 days', $timezone);
    return $this->dateFormatter->format($sevenDaysInTheFuture->getTimestamp(), 'html_date');
  }

  /**
   * Get checkout users.
   */
  private function getCheckoutUser(TmgmtTranslatorInterface $translator): array {
    $selectedUser  = $translator->getSelectedUser();
    $availableUser = $translator->getPluginConfiguration($this->system)->getAvailableUsersFlat();

    return $this->filterOutNotSelectedItems($availableUser, $selectedUser);
  }

  /**
   * Get checkout invoicing accounts.
   */
  private function getCheckoutInvoicingAccounts(TmgmtTranslatorInterface $translator): array {
    $selectedInvoicingAccounts = $translator->getSelectedInvoicingAccounts();
    $availableInvoicingAccounts = $translator->getPluginConfiguration($this->system)->getAvailableInvoicingAccountsFlat();
    return $this->filterOutNotSelectedItems($availableInvoicingAccounts, $selectedInvoicingAccounts);
  }

  /**
   * Get checkout translation memories.
   */
  private function getCheckoutTranslationMemories(TmgmtTranslatorInterface $translator): array {
    $selectedTranslationMemories = $translator->getSelectedTranslationMemories();
    $availableTranslationMemories = $translator->getPluginConfiguration($this->system)->getAvailableTranslationMemoriesFlat();
    return $this->filterOutNotSelectedItems($availableTranslationMemories, $selectedTranslationMemories);
  }

  /**
   * Get checkout term bases.
   */
  private function getCheckoutTermBases(TmgmtTranslatorInterface $translator): array {
    $selectedTermBases = $translator->getSelectedTermBases();
    $availableTermBases = $translator->getPluginConfiguration($this->system)->getAvailableTermBasesFlat();

    return $this->filterOutNotSelectedItems($availableTermBases, $selectedTermBases);
  }

  /**
   * Get checkout work areas.
   */
  private function getCheckoutWorkAreas(TmgmtTranslatorInterface $translator): array {
    $selectedWorkAreas = $translator->getSelectedWorkAreas();
    $availableWorkAreas = $translator->getPluginConfiguration($this->system)->getAvailableWorkAreasFlat();
    return $this->filterOutNotSelectedItems($availableWorkAreas, $selectedWorkAreas);
  }

  /**
   * Suggested delivery date message.
   */
  private function suggestedDeliveryDateMessage(TmgmtJobInterface $job): TranslatableMarkup {
    $wordCount = $job->wordCount();
    $days = DeliveryDateCalculator::calculateFor($wordCount);
    $dayString = $days > 1 ? 'days' : 'day';
    return $this->t(
        'Based on the word count, the suggested delivery date should be scheduled for at least <strong>@numberOf working @days</strong>.',
        [
          '@numberOf' => $days,
          '@days' => $dayString,
        ]
    );
  }

}
