<?php

namespace Drupal\castorcito\Form;

use Drupal\castorcito\CastorcitoComponentInterface;
use Drupal\Core\Field\FieldFilteredMarkup;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Form\SubformState;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

/**
 * Provides a base form for castorcito component field.
 */
abstract class CastorcitoComponentFieldFormBase extends FormBase {

  /**
   * The Component.
   *
   * @var \Drupal\castorcito\CastorcitoComponentInterface
   */
  protected $component;

  /**
   * The Component field.
   *
   * @var \Drupal\castorcito\CastorcitoComponentFieldInterface
   */
  protected $componentField;

  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'castorcito_component_field_add_form';
  }

  /**
   * {@inheritdoc}
   *
   * @param array $form
   *   A nested array form elements comprising the form.
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   *   The current state of the form.
   * @param \Drupal\castorcito\CastorcitoComponentInterface|null $castorcito_component
   *   The Castorcito component, or NULL if not provided.
   * @param string|null $field_type
   *   The component field type, or NULL if not provided.
   *
   * @return array
   *   The form structure.
   *
   * @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
   */
  public function buildForm(array $form, FormStateInterface $form_state, ?CastorcitoComponentInterface $castorcito_component = NULL, $field_type = NULL) {
    $this->component = $castorcito_component;
    try {
      $this->componentField = $this->prepareComponentField($field_type);
    }
    catch (PluginNotFoundException $e) {
      throw new NotFoundHttpException("Invalid field id: '$field_type'.");
    }

    $form['#parents'] = [];
    $form['cf_label'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Label'),
      '#default_value' => $this->componentField->getFieldLabel(),
      '#required' => TRUE,
    ];

    $form['cf_type'] = [
      '#type' => 'machine_name',
      '#default_value' => $this->componentField->getFieldName(),
      '#disabled' => !empty($this->componentField->getFieldName()),
      '#machine_name' => [
        'exists' => [
          $this,
          'componentFieldExists',
        ],
        'source' => [
          'cf_label',
        ],
      ],
    ];

    $form['settings'] = [];
    $form['settings']['#parents'] = ['settings'];
    $subform_state = SubformState::createForSubform($form['settings'], $form, $form_state);
    $form['settings'] = $this->componentField->buildConfigurationForm($form['settings'], $subform_state);
    $form['settings']['#tree'] = TRUE;

    $form['cf_description'] = [
      '#type' => 'textarea',
      '#title' => $this->t('Help text'),
      '#default_value' => $this->componentField->getDescription(),
      '#rows' => 5,
      '#description' => $this->t('Instructions to present to the user below this field on the editing form.<br />Allowed HTML tags: @tags', ['@tags' => FieldFilteredMarkup::displayAllowedTags()]) . '<br />',
    ];

    $form['cf_required'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Required field'),
      '#default_value' => $this->componentField->isRequired(),
    ];

    $form['cf_hide_label'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Hide label'),
      '#default_value' => $this->componentField->isHideLabel(),
      '#description' => $this->t('Checking this box hides the field label.'),
    ];

    $form['actions'] = ['#type' => 'actions'];
    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#button_type' => 'primary',
    ];

    $form['actions']['cancel'] = [
      '#type' => 'link',
      '#title' => $this->t('Cancel'),
      '#url' => $this->component->toUrl('edit-form'),
      '#attributes' => ['class' => ['button']],
    ];

    switch ($this->componentField->getPluginId()) {
      case 'list_text':
        $form['cf_required']['#disabled'] = TRUE;
        $form['cf_required']['#default_value'] = TRUE;
        break;
      case 'container':
      case 'advanced_container':
        $form['cf_required']['#access'] = FALSE;
        break;
      default:
        break;
    }

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    // The component field configuration is stored in the 'settings' key in the form,
    // pass that through for validation.
    $this->componentField->validateConfigurationForm($form['settings'], SubformState::createForSubform($form['settings'], $form, $form_state));
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $form_state->cleanValues();
    $field_label = $form_state->getValue('cf_label');
    $field_name = $form_state->getValue('cf_type');
    $field_description = $form_state->getValue('cf_description');
    $is_required = $form_state->getValue('cf_required');
    $is_hide_label = $form_state->getValue('cf_hide_label');

    // Save field model.
    $component_field_model = $this->componentField->defaultModel();
    $component_field_model = ['type' => $this->componentField->getPluginId()] + $component_field_model;
    $this->component->setModelField($field_name, $component_field_model);

    // Save field comfiguration.
    $this->componentField->submitConfigurationForm($form['settings'], SubformState::createForSubform($form['settings'], $form, $form_state));
    $this->componentField->setFieldlabel($field_label);
    $this->componentField->setDescription($field_description);
    $this->componentField->setRequired($is_required);
    $this->componentField->setHideLabel($is_hide_label);

    if (!$this->componentField->getFieldName()) {
      $configuration = $this->componentField->getConfiguration();
      $configuration['name'] = $field_name;
      $this->component->addComponentField($configuration);
    }
    else {
      // TODO: Review why this code is necessary to save the changes when making modifications to the form with ajax.
      $fields_configuration = $this->component->get('field_configuration');
      $fields_configuration[$field_name] = $this->componentField->getConfiguration();
      $this->component->set('field_configuration', $fields_configuration);
    }

    $this->component->save();
    $this->messenger()->addStatus($this->t('The field configuration was saved successfully.'));
    $form_state->setRedirectUrl($this->component->toUrl('edit-form'));
  }

  /**
   * Determines if the action already exists.
   *
   * @param string $id
   *   The action ID.
   *
   * @return bool
   *   TRUE if the action exists, FALSE otherwise.
   */
  public function componentFieldExists($id) {
    if (!$this->component->getComponentFields()->has($id)) {
      return FALSE;
    }

    return TRUE;
  }

  /**
   * Converts an field type into an object.
   *
   * @param string $field_type
   *   The component field type.
   *
   * @return \Drupal\castorcito\CastorcitoComponentFieldInterface
   *   The component field object.
   */
  abstract protected function prepareComponentField($field_type);

}
