<?php

declare(strict_types=1);

namespace Drupal\languagewire_translation_provider\Ui;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Render\Renderer;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\languagewire_translation_provider\Adapter\SystemInterface;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtTranslator;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\TmgmtTranslatorInterface;
use Drupal\languagewire_translation_provider\Adapter\TMGMT\TranslatorPluginConfiguration;
use Drupal\languagewire_translation_provider\Content\HtmlPreview\HtmlPreviewSettingsChecker;
use Drupal\languagewire_translation_provider\Database\ProjectTemplateRepositoryInterface;
use Drupal\languagewire_translation_provider\Settings\OrderType;
use Drupal\languagewire_translation_provider\TranslatorUi;

/**
* Configuration form UI.
 */
final class ConfigurationFormUi {
  use TranslatorUi;
  use StringTranslationTrait;

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

  /**
   * Project template repository.
   *
   * @var \Drupal\languagewire_translation_provider\Database\ProjectTemplateRepositoryInterface
   */
  private ProjectTemplateRepositoryInterface $projectTemplateRepository;

  /**
   * Html preview settings checker.
   *
   * @var \Drupal\languagewire_translation_provider\Content\HtmlPreview\HtmlPreviewSettingsChecker
   */
  private HtmlPreviewSettingsChecker $htmlPreviewSettingsChecker;

  /**
   * Renderer.
   *
   * @var \Drupal\Core\Render\Renderer
   */
  private Renderer $renderer;

  /**
   * Constructs a new ConfigurationFormUi object.
   *
   * @param \Drupal\languagewire_translation_provider\Adapter\SystemInterface $system
   *   System.
   * @param \Drupal\languagewire_translation_provider\Database\ProjectTemplateRepositoryInterface $projectTemplateRepository
   *   Project template repository.
   * @param \Drupal\languagewire_translation_provider\Content\HtmlPreview\HtmlPreviewSettingsChecker $htmlPreviewSettingsChecker
   *   Html preview settings checker.
   * @param \Drupal\Core\Render\Renderer $renderer
   *   Renderer.
   */
  public function __construct(
    SystemInterface $system,
    ProjectTemplateRepositoryInterface $projectTemplateRepository,
    HtmlPreviewSettingsChecker $htmlPreviewSettingsChecker,
    Renderer $renderer
  ) {
    $this->system = $system;
    $this->projectTemplateRepository = $projectTemplateRepository;
    $this->htmlPreviewSettingsChecker = $htmlPreviewSettingsChecker;
    $this->renderer = $renderer;
  }

  /**
   * Create configuration form.
   *
   * @throws \Exception
   */
  public function createConfigurationForm(array $form, FormStateInterface $formState): array {
    $translator = new TmgmtTranslator($formState->getFormObject()->getEntity());
    $translatorPluginConfiguration = $translator->getPluginConfiguration($this->system);

    $clientID = $translator->getClientId();

    $form['client_id_value'] = [
      '#type' => 'hidden',
      '#value' => $clientID,
    ];

    $form['client_id'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Client ID'),
      '#required' => FALSE,
      '#default_value' => $clientID,
      '#description' => $this->t('Use the client id provided to you by LanguageWire to activate the connector.'),
    ];

    $form['client_secret'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Client Secret'),
      '#required' => FALSE,
      '#default_value' => $translator->getClientSecret(),
      '#description' => $this->t('Use the client secret provided to you by LanguageWire to activate the connector.'),
    ];

    $form['client_id_change'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Changing the Client ID'),
      '#default_value' => empty($clientID),
      '#description' => $this->t('By changing the Client ID you understand that all associated projects to this provider will be cancelled.'),
      '#element_validate' => [[self::class, 'validateClientIdChange']],
    ];

    $form = $this->addConfigurationRefreshButtonToForm($form, $translator);
    $form = $this->displayConfigurationItems($form, $translator, $translatorPluginConfiguration);

    if (!$translatorPluginConfiguration->isFullyInitialized()) {
      $form = $this->displayInvalidApiWarning($form);
    }

    return $form;
  }

  /**
   * Display configuration items.
   */
  public function displayConfigurationItems(array $form, TmgmtTranslator $translator, TranslatorPluginConfiguration $pluginConfiguration): array {
    $form['services'] = [
      '#type' => 'select',
      '#title' => $this->t('LanguageWire Services'),
      '#required' => FALSE,
      '#default_value' => $translator->getSelectedServices(),
      '#description' => $this->t('Please select LanguageWire service for your translation job.'),
      '#options' => ItemsInteractor::sortItems($pluginConfiguration->getAvailableServicesFlat()),
      '#multiple' => TRUE,
    ];
    $availableUser = ItemsInteractor::sortItems($pluginConfiguration->getAvailableUsersFlat());
    $form['user'] = empty($availableUser)
      ? [
        '#type' => 'item',
        '#title' => $this->t('Users'),
        '#description' => $this->t('No users available.'),
      ] : [
        '#type' => 'select',
        '#title' => $this->t('Users'),
        '#required' => FALSE,
        '#default_value' => $translator->getSelectedUser(),
        '#description' => $this->t('Please select user to make available for your translation job.'),
        '#options' => $availableUser,
        '#multiple' => TRUE,
      ];
    $availableProjectTemplates = ItemsInteractor::sortItems($pluginConfiguration->getAvailableProjectTemplatesFlat());
    $form['project_templates'] = empty($availableProjectTemplates)
      ? [
        '#type' => 'item',
        '#title' => $this->t('Project Templates'),
        '#description' => $this->t('No templates available.'),
      ] : [
        '#type' => 'select',
        '#title' => $this->t('Project Templates'),
        '#required' => FALSE,
        '#default_value' => $translator->getSelectedProjectTemplateIds(),
        '#description' => $this->t('Please select project templates to make available for your translation job.'),
        '#options' => $availableProjectTemplates,
        '#multiple' => TRUE,
      ];
    $form['work_areas'] = [
      '#type' => 'select',
      '#title' => $this->t('Work Areas'),
      '#required' => FALSE,
      '#default_value' => $translator->getSelectedWorkAreas(),
      '#description' => $this->t('Please select a work area for your translation job.'),
      '#options' => ItemsInteractor::sortItems($pluginConfiguration->getAvailableWorkAreasFlat(), ItemsInteractor::GENERAL_SUFFIX),
      '#multiple' => TRUE,
    ];
    $form['translation_memories'] = [
      '#type' => 'select',
      '#title' => $this->t('Translation Memories'),
      '#required' => FALSE,
      '#default_value' => $translator->getSelectedTranslationMemories(),
      '#description' => $this->t('Please select a translation memory for your translation job.'),
      '#options' => ItemsInteractor::sortItems($pluginConfiguration->getAvailableTranslationMemoriesFlat()),
      '#multiple' => TRUE,
    ];
    $availableTermBases = ItemsInteractor::sortItems($pluginConfiguration->getAvailableTermBasesFlat());
    $form['term_bases'] = empty($availableTermBases)
      ? [
        '#type' => 'item',
        '#title' => $this->t('Term Bases'),
        '#description' => $this->t('No term bases available.'),
      ] : [
        '#type' => 'select',
        '#title' => $this->t('Term Bases'),
        '#required' => FALSE,
        '#default_value' => $translator->getSelectedTermBases(),
        '#description' => $this->t('Please select a term base for your translation job.'),
        '#options' => $availableTermBases,
        '#multiple' => TRUE,
      ];
    $form['invoicing_accounts'] = [
      '#type' => 'select',
      '#title' => $this->t('Invoicing Accounts'),
      '#required' => FALSE,
      '#default_value' => $translator->getSelectedInvoicingAccounts(),
      '#description' => $this->t('Please select an invoicing account for your translation job.'),
      '#options' => ItemsInteractor::sortItems($pluginConfiguration->getAvailableInvoicingAccountsFlat()),
      '#multiple' => TRUE,
    ];
    $form['order_types'] = [
      '#type' => 'select',
      '#title' => $this->t('Order Type'),
      '#required' => FALSE,
      '#default_value' => $translator->getSelectedOrderTypes(),
      '#description' => $this->t('Please select an order type (default is order).'),
      '#options' => OrderType::getAll(),
      '#multiple' => FALSE,
    ];
    $form['bundling'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Bundle languages'),
      '#required' => FALSE,
      '#default_value' => $translator->isBundlingEnabled(),
      '#description' => $this->t('When enabled, the connector will automatically bundle multiple selected languages into a single project.'),
    ];
    $form['default_delivery_date'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Enable default desired delivery date'),
      '#required' => FALSE,
      '#default_value' => $translator->isDefaultDeliveryDateEnabled(),
      '#description' => $this->t('When enabled, the connector will automatically fill the Desired Delivery Date field with a date 7 days in the future.'),
    ];

    $form = $this->insertHtmlPreviewSection($translator, $form);

    $form['item_upload_batch_size'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Project content item upload batch size'),
      '#required' => FALSE,
      '#default_value' => $translator->getItemUploadBatchSize(),
      '#description' => $this->t('Adjusts the number of project content items uploaded per minute. When in doubt, leave it on the default setting.'),
      '#element_validate' => [[self::class, 'validateItemUploadBatchSize']],
    ];

    return $form;
  }

  /**
   * Display invalid API warning.
   *
   * @throws \Exception
   */
  public function displayInvalidApiWarning(array $form): array {
    $warningIconConfig = [
      '#theme' => 'image',
      '#uri' => 'core/misc/icons/e29700/warning.svg',
      '#alt' => $this->t('Please provide valid API Credentials'),
      '#title' => $this->t('Please provide valid API Credentials'),
      '#width' => 14,
    ];
    $icon = $this->renderer->render($warningIconConfig);
    $form['client_id']['#field_suffix'] = "$icon Please provide a valid Client ID";
    return $form;
  }

  /**
   * Validate client ID change.
   */
  public static function validateClientIdChange(array $element, FormStateInterface $formState): void {
    $settingsValues = $formState->getValues()['settings'];

    if (
      $settingsValues['client_id_change'] === 0
      && $settingsValues['client_id'] !== $settingsValues['client_id_value']
    ) {
      $formState->setError(
        $element,
        t('You must check the @field checkbox.', ['@field' => $element['#title']])
      );
    }
  }

  /**
   * Validate item upload batch size.
   */
  public static function validateItemUploadBatchSize(array $element, FormStateInterface $formState): void {
    if (!isset($element['#value'])) {
      $formState->setError($element, t('The @field is unset. Please set to a numeric value.', ['@field' => $element['#title']]));
      return;
    }

    if (!is_numeric($element['#value'])) {
      $formState->setError($element, t('The @field must be a numeric value.', ['@field' => $element['#title']]));
      return;
    }

    if (intval($element['#value']) <= 0) {
      $formState->setError($element, t('The @field must have a value greater than zero.', ['@field' => $element['#title']]));
    }
  }

  /**
   * Add configuration refresh button to form.
   */
  private function addConfigurationRefreshButtonToForm(array $form, TmgmtTranslatorInterface $translator): array {
    $form['configuration_refresh'] = [
      '#type' => 'item',
    ];

    $form['configuration_refresh']['description'] = [
      '#type' => 'item',
      '#title' => $this->t('Refresh platform configuration items'),
      '#markup' => $this->t("Update services, project templates, work areas, translation memories, term bases and invoicing accounts."),
    ];

    // @todo get this URL dynamically from routes
    $linkMarkup = "<a class='button' href='/admin/languagewire/refreshPlatformConfigurationItems?translator="
            . $translator->id() . "'>" . $this->t('Refresh platform configuration items') . "</a>";

    $form['configuration_refresh']['refresh_link'] = [
      '#type' => 'item',
      '#markup' => $linkMarkup,
    ];

    return $form;
  }

  /**
   * Insert HTML preview section.
   */
  private function insertHtmlPreviewSection(TmgmtTranslator $translator, array $form): array {
    $errors = $this->htmlPreviewSettingsChecker->getFailedRequirements();
    $canGenerateHtmlPreview = count($errors) == 0;

    $form['html_preview'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Enable HTML preview generation'),
      '#required' => FALSE,
      '#default_value' => $translator->isHtmlPreviewEnabled(),
      '#description' => $this->t('Content sent to the LanguageWire platform can be accompanied by an HTML preview package
             that will provide meaningful context for the translators.'),
    ];

    if (!$canGenerateHtmlPreview) {
      $form['html_preview']['#disabled'] = TRUE;

      // Show the setting as 'unchecked' to prevent confusing the user
      // from thinking that HTML Preview will
      // occur regardless of invalid settings.
      $form['html_preview']['#default_value'] = FALSE;

      $form['html_preview_errors'] = [
        '#prefix' => '<div>',
        '#suffix' => '</div>',
        '#markup' => $this->t($this->getInvalidHtmlPreviewSettingsMessage($errors)),
      ];
    }

    return $form;
  }

  /**
   * Get invalid HTML preview settings message.
   *
   * @param string[] $errors
   *   List of errors.
   *
   * @return string
   *   Message.
   */
  private function getInvalidHtmlPreviewSettingsMessage(array $errors): string {
    $errorListItemsHtml = implode("\n", array_map(function (string $error): string {
        return "<li>$error</li>";
    }, $errors));
    return sprintf(
          '<div class="messages messages--error">HTML Preview can not be enabled due to the following reasons: <ul>%s</ul></div>',
          $errorListItemsHtml
      );
  }

}
