<?php

namespace Drupal\straker_translate\Form;

use Drupal\Component\Serialization\Json;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Drupal\Core\Url;
use Drupal\language\ConfigurableLanguageInterface;
use Drupal\straker_translate\Exception\StrakerTranslateApiException;
use Drupal\straker_translate\LanguageLocaleMapperInterface;
use Drupal\straker_translate\StrakerTranslateConfigurationServiceInterface;
use Drupal\straker_translate\StrakerTranslateInterface;

/**
 * Alters the Drupal language module language forms.
 *
 * @package Drupal\straker_translate\Form
 */
class StrakerTranslateLanguageForm {

  use StringTranslationTrait;

  /**
   * A straker_translate connector object.
   *
   * @var \Drupal\straker_translate\StrakerTranslateInterface
   */
  protected $straker_translate;

  /**
   * The language-locale mapper.
   *
   * @var \Drupal\straker_translate\LanguageLocaleMapperInterface
   */
  protected $languageLocaleMapper;

  /**
   * The Straker Translate configuration service.
   *
   * @var \Drupal\straker_translate\StrakerTranslateConfigurationServiceInterface
   */
  protected $straker_translateConfiguration;

  /**
   * Constructs a new StrakerTranslateLanguageForm object.
   *
   * @param \Drupal\straker_translate\StrakerTranslateInterface $straker_translate
   *   A straker_translate object.
   * @param \Drupal\straker_translate\LanguageLocaleMapperInterface $language_locale_mapper
   *   The language-locale mapper.
   * @param \Drupal\straker_translate\StrakerTranslateConfigurationServiceInterface $straker_translate_configuration
   *   The Straker Translate configuration service.
   */
  public function __construct(StrakerTranslateInterface $straker_translate, LanguageLocaleMapperInterface $language_locale_mapper, StrakerTranslateConfigurationServiceInterface $straker_translate_configuration) {
    $this->straker_translate = $straker_translate;
    $this->languageLocaleMapper = $language_locale_mapper;
    $this->straker_translateConfiguration = $straker_translate_configuration;
  }

  /**
   * Alters the configurable language entity edit and add form.
   *
   * @param array $form
   *   The form definition array for the configurable language entity.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   */
  public function form(array &$form, FormStateInterface $form_state) {
    /** @var \Drupal\Core\Entity\EntityForm $form_object */
    $form_object = $form_state->getFormObject();
    /** @var \Drupal\language\ConfigurableLanguageInterface $language */
    $language = $form_object->getEntity();
    $langcode = $language->getId();

    $form['custom_language']['straker_translate'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('Straker translation'),
      '#weight' => 0,
    ];

    $form['custom_language']['straker_translate']['straker_translate_disabled'] = [
      '#type' => 'checkbox',
      '#title' => t('Disabled for Straker translation'),
      // If we have a langcode, check if there is a locale or default to the one we can guess.
      '#default_value' => $langcode !== NULL ? (!$this->straker_translateConfiguration->isLanguageEnabled($language)) : FALSE,
      '#description' => $this->t('Check this if you want Straker Translate to ignore this language or locale.'),
    ];

    $form['custom_language']['straker_translate']['straker_translate_locale'] = [
      '#type' => 'textfield',
      '#title' => t('Locale'),
      '#autocomplete_route_name' => 'straker_translate.supported_locales_autocomplete',
      // If we have a langcode, check if there is a locale or default to the one we can guess.
      '#default_value' => $langcode !== NULL ? $this->languageLocaleMapper->getLocaleForLangcode($langcode) : '',
      '#description' => $this->t('The Straker Translate locale this language maps to.') . ' ' .
      $this->t('Use locale codes as <a href=":w3ctags">defined by the W3C</a> for interoperability. <em>Examples: "en", "en-gb" and "zh-hant".</em>', [':w3ctags' => 'http://www.w3.org/International/articles/language-tags/']),
    ];
    $form['custom_language']['straker_translate']['straker_translate_locale_link'] = [
      '#type' => 'link',
      '#title' => $this->t('Straker Translate supported locales list'),
      '#url' => Url::fromRoute('straker_translate.supported_locales'),
      '#attributes' => [
        'class' => ['use-ajax'],
        'data-dialog-type' => 'dialog',
        'data-dialog-options' => Json::encode([
          'width' => 861,
          'height' => 700,
          'draggable' => TRUE,
          'autoResize' => FALSE,
        ]),
      ],
    ];

    // Buttons are different if adding or editing a language. We need validation
    // on both cases.
    if ($langcode) {
      $form['actions']['submit']['#validate'][] = StrakerTranslateLanguageForm::class . '::validateLocale';
    }
    else {
      $form['custom_language']['submit']['#validate'][] = StrakerTranslateLanguageForm::class . '::validateLocale';
    }
    $form['#entity_builders'][] = StrakerTranslateLanguageForm::class . '::languageEntityBuilder';
  }

  /**
   * Entity builder for the configurable language type form with straker_translate options.
   *
   * @param string $entity_type
   *   The entity type.
   * @param \Drupal\language\ConfigurableLanguageInterface $language
   *   The language object.
   * @param array $form
   *   The form definition array for the configurable language entity.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   *
   * @see straker_translate_form_language_admin_add_form_alter()
   * @see straker_translate_form_language_admin_edit_form_alter()
   */
  public static function languageEntityBuilder($entity_type, ConfigurableLanguageInterface $language, array &$form, FormStateInterface $form_state) {
    // We need to check if the value exists, as we are enabling by default those
    // predefined languages in Drupal.
    if ($form_state->hasValue(['straker_translate_locale'])) {
      $straker_translate_locale = $form_state->getValue(['straker_translate_locale']);
      $straker_translateDisabled = $form_state->getValue(['straker_translate_disabled']);
      $language->setThirdPartySetting('straker_translate', 'locale', $straker_translate_locale);
      $language->setThirdPartySetting('straker_translate', 'disabled', $straker_translateDisabled);
      \Drupal::service('plugin.manager.straker_translate_form_bulk_action')->clearCachedDefinitions();
    }
  }

  /**
   * Validate the configurable language type form with straker_translate options.
   *
   * @param array $form
   *   The form definition array for the configurable language entity.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   *
   * @see straker_translate_form_language_admin_add_form_alter()
   * @see straker_translate_form_language_admin_edit_form_alter()
   */
  public static function validateLocale(&$form, FormStateInterface $form_state) {
    $form_key = ['straker_translate_locale'];
    if (!$form_state->isValueEmpty($form_key)) {
      $value = $form_state->getValue($form_key);
      try {
        if (!self::isValidLocale($value)) {
          $form_state->setErrorByName('straker_translate_locale', t('The Straker Translate locale %locale does not exist.', ['%locale' => $value]));
        }
      }
      catch (StrakerTranslateApiException $straker_translateApiException) {
        if ($straker_translateApiException->getCode() === 401) {
          \Drupal::messenger()->addWarning("The Straker Translate locale has not been validated.", 'warning');
        }
      }
    }
  }

  /**
   * Checks if a locale is valid.
   *
   * @param string $locale
   *   The locale to validate.
   *
   * @return bool
   *   TRUE if it's a valid locale in Straker Translate. FALSE if not.
   */
  public static function isValidLocale($locale) {
    $locales = \Drupal::service('straker_translate')->getLocalesInfo();
    if (isset($locales[$locale])) {
      return TRUE;
    }
    return FALSE;
  }

}
