<?php

namespace Drupal\castorcito\Form;

use Drupal\castorcito\CastorcitoComponentFieldManager;
use Drupal\castorcito\CastorcitoManager;
use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\Core\Link;
use Drupal\Core\Theme\ComponentPluginManager;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Controller for castorcito component edit form.
 *
 * @internal
 */
class CastorcitoComponentEditForm extends CastorcitoComponentFormBase {

  /**
   * The castorcito component field manager service.
   *
   * @var \Drupal\castorcito\CastorcitoComponentFieldManager
   */
  protected $componentFieldManager;

  /**
   * The SDC component plugin manager.
   *
   * @var \Drupal\Core\Theme\ComponentPluginManager;
   */
  protected $componentPluginManager;

  /**
   * Constructs an CastorcitoComponentEditForm object.
   *
   * @param \Drupal\Core\Entity\EntityStorageInterface $component_storage
   *   The entity storage.
   * @param \Drupal\castorcito\CastorcitoComponentFieldManager $component_field_manager
   *   The castorcito component field manager service.
   * @param \Drupal\Core\Theme\ComponentPluginManager; $component_plugin_manager
   *   The SDC component plugin manager.
   */
  public function __construct(EntityStorageInterface $component_storage, CastorcitoComponentFieldManager $component_field_manager, CastorcitoManager $castorcitoManager,  ComponentPluginManager $component_plugin_manager) {
    parent::__construct($component_storage, $castorcitoManager);
    $this->componentFieldManager = $component_field_manager;
    $this->componentPluginManager = $component_plugin_manager;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('entity_type.manager')->getStorage('castorcito_component'),
      $container->get('plugin.manager.castorcito_component_field'),
      $container->get('castorcito.manager'),
      $container->get('plugin.manager.sdc'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function form(array $form, FormStateInterface $form_state) {
    $form = parent::form($form, $form_state);
    $cfm = $this->componentFieldManager->getDefinitions();
    $model = $this->entity->getModel();
    $new_field_options = [];

    if (!empty($cfm)) {
      foreach ($cfm as $field => $definition) {
        $new_field_options[$field] = $definition['label'];
      }
    }

    $form['#title'] = $this->t('Edit %label component', [
      '%label' => $this->entity->label(),
    ]);

    $form['#attached']['library'][] = 'castorcito/castorcito.admin';

    $component_form_settings = $this->entity->get('form_settings');
    $form['component_form_settings'] = [
      '#type' => 'details',
      '#title' => $this->t('Form settings'),
      '#tree' => TRUE,
    ];

    $form['component_form_settings']['enable_edit_name'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Enable editing of component name'),
      '#description' => $this->t('Checking this option displays a button that allows you to define a component name for better content management.'),
      '#default_value' => $component_form_settings['enable_edit_name'] ?? FALSE,
    ];

    $form['component_form_settings']['collapse_component'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Collapse component, set <strong>close</strong> by default'),
      '#default_value' => $component_form_settings['collapse_component'] ?? FALSE,
    ];

    if (!$this->entity->get('inside_container')) {
      $form['component_display_settings'] = [
        '#type' => 'details',
        '#title' => $this->t('Display settings'),
      ];

      $url_help = Url::fromRoute('help.page', ['name' => 'castorcito']);
      $link_help = Link::fromTextAndUrl('See predefined options', $url_help)->toString();
      $form['component_display_settings']['html'] = [
        '#markup' => $this->t('<p>With the <strong>Add option</strong> button, you can add configuration options for the component display, such as the slideshow speed or the component\'s width. These settings can be overridden in the <em>Manage Display</em> section of the field in Drupal.</p>'
          . '<p>There are <strong>predefined display options</strong> that are added as attributes of the html tag containing the component. ' . $link_help . '</p>'),
      ];

      $form['component_display_settings']['add_display_settings_option'] = [
        '#type' => 'link',
        '#title' => $this->t('Add option'),
        '#url' => Url::fromRoute('castorcito.component_display_settings', [
          'castorcito_component' => $this->entity->id(),
        ]),
        '#attributes' => ['class' => ['button']],
      ];

      $display_settings = $this->sortFieldsByDependency($this->entity->get('display_settings'));
      if ($display_settings) {
        $form['component_display_settings']['#open'] = TRUE;
        $form['component_display_settings']['component_display_settings_table'] = [
          '#type' => 'table',
          '#header' => [
            $this->t('Options'),
            $this->t('Actions'),
          ],
          '#empty' => $this->t('Currently there are no display settings options for the component.'),
        ];

        foreach ($display_settings as $ds_key => $setting) {
          $element = [
            '#type' => $setting['type'],
            '#title' => $this->t($setting['label']),
            '#default_value' => $setting['default_value'] ?? '',
            '#description' => $setting['description'] ?? '',
            '#required' => $setting['required'] ?? '',
            '#prefix' => '<div class="component-display-settings-element">',
            '#suffix' => '</div>',
          ];

          if ($setting['type'] === 'select' && !empty($setting['options'])) {
            $element['#options'] = array_column($setting['options'], 'label', 'key');
          }

          $form['component_display_settings']['component_display_settings_table'][$ds_key]['default_value'] = $element;
          $form['component_display_settings']['component_display_settings_table'][$ds_key]['buttons'] = [
            '#type' => 'container',
          ];

          if (isset($setting['visibility_condition']) && !empty($setting['visibility_condition'])) {
            $trigger = [$setting['visibility_condition']['trigger'] => $setting['visibility_condition']['value']];
            if ($setting['visibility_condition']['trigger'] === 'checked' ||  $setting['visibility_condition']['trigger'] === 'unchecked') {
              $trigger = [$setting['visibility_condition']['trigger'] => TRUE];
            }

            $form['component_display_settings']['component_display_settings_table'][$ds_key]['default_value']['#states'] = [
              $setting['visibility_condition']['state'] => [
                ':input[name="component_display_settings_table[' . $setting['visibility_condition']['conditional_option'] . '][default_value]"]' => $trigger,
              ],
            ];
          }

          $form['component_display_settings']['component_display_settings_table'][$ds_key]['buttons']['edit'] = [
            '#type' => 'link',
            '#title' => $this->t('Edit'),
            '#url' => Url::fromRoute('castorcito.component_display_settings', [
              'castorcito_component' => $this->entity->id(),
              'field_name' => $ds_key,
            ]),
            '#attributes' => ['class' => ['button']],
          ];

          $form['component_display_settings']['component_display_settings_table'][$ds_key]['buttons']['delete'] = [
            '#type' => 'link',
            '#title' => $this->t('Delete'),
            '#url' => Url::fromRoute('castorcito.component_display_settings_delete', [
              'castorcito_component' => $this->entity->id(),
              'field_name' => $ds_key,
            ]),
            '#attributes' => ['class' => ['button']],
          ];
        }
      }
    }

    $components_sdc = $this->entity->get('sdc');
    if (!empty($components_sdc) && !empty($components_sdc['id']) && !$this->componentPluginManager->hasDefinition($components_sdc['id'])) {
      $this->messenger()->addWarning(t('The configured SDC ID “@id” is missing or unavailable, the default SDC will be used until the problem is resolved.', [
        '@id' => $components_sdc['id'],
      ]));
    }

    $form['components_sdc'] = [
      '#type' => 'details',
      '#title' => $this->t('Single Directory Component'),
      '#tree' => TRUE,
    ];

    $form['components_sdc']['fixed_sdc'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Fixed SDC'),
      '#default_value' => $components_sdc['fixed_sdc'] ?? FALSE,
    ];

    $form['components_sdc']['provider'] = [
      '#type' => 'select',
      '#title' => $this->t('Provider'),
      '#options' => $this->getSdcData('provider'),
      '#default_value' => $components_sdc['provider'] ?? '',
      '#empty_option' => $this->t('- Select -'),
      '#states' => [
        'visible' => [
          ':input[name="components_sdc[fixed_sdc]"]' => ['checked' => TRUE],
        ],
      ],
      '#ajax' => [
        'callback' => '::updateSdcId',
        'wrapper' => 'sdc-id-wrapper',
      ],
    ];


    $provider = $form_state->getValue(['components_sdc', 'provider']) ?? $components_sdc['provider'] ?? '';
    $form['components_sdc']['id'] = [
      '#type' => 'select',
      '#title' => $this->t('ID'),
      '#options' => !empty($provider) ? $this->getSdcIdByProvider($provider) : [],
      '#default_value' => $components_sdc['id'] ?? '',
      '#prefix' => '<div id="sdc-id-wrapper">',
      '#suffix' => '</div>',
      '#states' => [
        'visible' => [
          ':input[name="components_sdc[fixed_sdc]"]' => ['checked' => TRUE],
          ':input[name="components_sdc[provider]"]' => ['!value' => ''],
        ],
      ],
    ];

    $form['components_field'] = [
      '#type' => 'details',
      '#title' => $this->t('Fields'),
      '#open' => TRUE,
    ];

    $form['components_field']['component_item'] = [
      '#type' => 'item',
      '#title' => 'Add field',
      '#description' => $this->t('Shows all the fields defined in the Castorcito module plugin, if you want to add more fields you can do it by creating your own custom module.'),
    ];

    $form['components_field']['component_item']['new_field'] = [
      '#type' => 'select',
      '#title' => '',
      '#options' => $new_field_options,
      '#empty_option' => $this->t('- Select a field type -'),
      '#prefix' => '<div class="cc-field-new clearfix">',
    ];

    $form['components_field']['component_item']['new_add'] = [
      '#type' => 'submit',
      '#value' => $this->t('Add'),
      '#validate' => ['::componentFieldValidate'],
      '#submit' => ['::componentFieldConfiguration'],
      '#suffix' => '</div>',
    ];

    $form['components_field']['help_text'] = [
      '#markup' => $this->t('<p>The following table presents the cfields associated with the component. Each row incorporates <strong>vertical scroll icons that allow their order to be modified</strong>. The configured order <strong>is applied directly in the content filling form,</strong> defining the sequence in which the cfields will be presented to the user.</p>'),
    ];

    $form['components_field']['component_field_table'] = [
      '#type' => 'table',
      '#header' => [
        $this->t('Label'),
        $this->t('Machine name'),
        $this->t('Field type'),
        $this->t('Weight'),
        $this->t('Required'),
        $this->t('Operations'),
      ],
      '#tabledrag' => [
        [
          'action' => 'order',
          'relationship' => 'sibling',
          'group' => 'component-field-order-weight',
        ],
      ],
      '#empty' => $this->t('There are currently no fields for the component. Add by selecting a field in the "Add field" option.'),
      '#weight' => 5,
    ];

    if (!empty($model['fields'])) {
      $number = 0;

      foreach ($model['fields'] as $key => $value) {
        $field = $this->entity->getComponentField($key);
        $form['components_field']['component_field_table'][$key]['#attributes']['class'][] = 'draggable';
        $form['components_field']['component_field_table'][$key]['label'] = [
          '#tree' => FALSE,
          'data' => [
            'label' => [
              '#plain_text' => $field->getFieldLabel(),
            ],
          ],
        ];

        $form['components_field']['component_field_table'][$key]['machine_name'] = [
          '#tree' => FALSE,
          'data' => [
            'label' => [
              '#plain_text' => $field->getFieldName(),
            ],
          ],
        ];

        $form['components_field']['component_field_table'][$key]['field_type'] = [
          '#tree' => FALSE,
          'data' => [
            'label' => [
              '#plain_text' => $field->getPluginId(),
            ],
          ],
        ];

        $form['components_field']['component_field_table'][$key]['weight'] = [
          '#type' => 'weight',
          '#title' => $this->t('Weight for'),
          '#title_display' => 'invisible',
          '#default_value' => $number,
          '#attributes' => [
            'class' => ['component-field-order-weight'],
          ],
        ];

        $form['components_field']['component_field_table'][$key]['required'] = [
          '#tree' => FALSE,
          'data' => [
            'label' => [
              '#plain_text' => $field->getConfiguration()['required'] ? '✔' : '',
            ],
          ],
        ];

        $operations['edit'] = [
          'title' => $this->t('Edit'),
          'url' => Url::fromRoute('castorcito.component_field_edit_form', [
            'castorcito_component' => $this->entity->id(),
            'field_type' => $key,
          ]),
        ];

        $operations['delete'] = [
          'title' => $this->t('Delete'),
          'url' => Url::fromRoute('castorcito.component_field_delete_form', [
            'castorcito_component' => $this->entity->id(),
            'field_type' => $key,
          ]),
        ];

        $form['components_field']['component_field_table'][$key]['operations'] = [
          '#type' => 'operations',
          '#links' => $operations,
        ];

        $number++;
      }
    }

    $form['#validate'][] = [$this, 'customValidator'];

    return $form;
  }

  /**
   * Function to add custom validate.
   * @param array $form
   * @param FormStateInterface $form_state
   */
  public function customValidator(array &$form, FormStateInterface $form_state) {
    $component_display_settings_table = $form_state->getValue('component_display_settings_table');
    if (isset($component_display_settings_table['component_classes_text'])) {
      $component_classes = $component_display_settings_table['component_classes_text']['default_value'];
      $pattern_class = '/^([a-zA-Z_-][a-zA-Z0-9_-]*)(\s+[a-zA-Z_-][a-zA-Z0-9_-]*)*$/';
      if (!empty($component_classes) && !preg_match($pattern_class, $component_classes)) {
        $form_state->setErrorByName('component_display_settings_table][component_classes_text', $this->t('The entered classes do not comply with the CSS class naming guidelines.'));
      }
    }

    if (isset($component_display_settings_table['component_id'])) {
      $component_id = $component_display_settings_table['component_id']['default_value'];
      $pattern_id = '/^([a-zA-Z_-][a-zA-Z0-9_-]*)$/';
      if (!empty($component_id) && !preg_match($pattern_id, $component_id)) {
        $form_state->setErrorByName('component_display_settings_table][component_id', $this->t('The id entered does not comply with the HTML id naming guidelines.'));
      }
    }
  }

  /**
   * Validate handler for component field.
   */
  public function componentFieldValidate($form, FormStateInterface $form_state) {
    if (!$form_state->getValue('new_field')) {
      $form_state->setErrorByName('new_field', $this->t('Select the field to add.'));
    }
  }

  /**
   * Submit handler for component field.
   */
  public function componentFieldConfiguration($form, FormStateInterface $form_state) {

    $form_state->setRedirect(
      'castorcito.component_field_add_form',
      [
        'castorcito_component' => $this->entity->id(),
        'field_type' => $form_state->getValue('new_field'),
      ],
    );
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state) {
    $components_sdc = $form_state->getValue('components_sdc');

    if ($components_sdc['fixed_sdc'] && empty($components_sdc['provider'])) {
      $form_state->setErrorByName('provider', $this->t('You must select a provider for the Single Directory Component.'));
    }
  }

  /**
   * {@inheritdoc}
   */
  public function save(array $form, FormStateInterface $form_state) {
    $this->entity->setBaseModel($form_state->getValue('label'));

    if (!empty($form_state->getValue('component_field_table'))) {
      $this->orderModel($form_state->getValue('component_field_table'));
    }

    if (!empty($form_state->getValue('component_display_settings_table'))) {
      $this->entity->setComponentDisplaySettingsValues($form_state->getValue('component_display_settings_table'));
    }

    $this->entity->set('form_settings', $form_state->getValue('component_form_settings'));
    $this->entity->set('sdc', $form_state->getValue('components_sdc'));
    parent::save($form, $form_state);
    $this->messenger()->addStatus($this->t('Changes to the component have been saved.'));
  }

  /**
   * {@inheritdoc}
   */
  private function orderModel($order) {
    $model = $this->entity->getModel();
    $model['fields'] = array_merge($order, $model['fields']);
    $this->entity->setModel($model);
  }

  /**
   * Retrieves a list of available SDC providers or component IDs based on criteria.
   *
   * @param string $property
   *   The type of data to retrieve: either 'provider' or 'id'.
   * @param string $provider
   *   (Optional) The specific provider to filter by when retrieving component IDs.
   *
   * @return array
   *   An associative array of providers or component IDs.
   */
  private function getSdcData($property, $provider = '') {
    $sdcData = [];
    $definitions = $this->componentPluginManager->getDefinitions();

    foreach ($definitions as $definition) {
      if ($property === 'provider') {
        $sdcData[$definition['provider']] = $this->getProviderLabel($definition['provider'], $definition['extension_type']->value);
      }
      elseif ($property === 'id' && $provider === $definition['provider']) {
        $sdcData[$definition['id']] = $definition['id'];
      }
    }

    return $sdcData;
  }

  /**
   * Gets the human-readable name of a module or theme provider.
   *
   * @param string $provider
   *   The machine name of the provider.
   * @param string $extension_type
   *   The extension type of the provider.
   *
   * @return string|null
   *   The label of the module or theme, or NULL if not found.
   */
  private function getProviderLabel($provider, $extension_type) {
    $extension = \Drupal::service('extension.list.' . $extension_type)->get($provider);
    if ($extension) {
      return $extension->info['name'] ?? $provider;
    }

    return $provider;
  }

  /**
   * Ajax callback for the color dropdown.
   */
  public function updateSdcId(array $form, FormStateInterface $form_state) {
    return $form['components_sdc']['id'];
  }

  /**
   * Returns SDC component IDs that correspond to the given provider.
   *
   * @param string $provider
   *   The provider name used to filter SDC component definitions.
   *
   * @return array
   *   An associative array of SDC component IDs provided by the given provider,
   *   suitable for use as form options.
   */
  protected function getSdcIdByProvider($provider) {
    return $this->getSdcData('id', $provider);
  }

  /**
   * Sorts an array of fields by their dependencies.
   *
   * @param array $fields
   *   An associative array of fields, where each key is the field ID and each
   *   value is an array of field settings.
   *
   * @return array
   *   The sorted array of fields.
   */
  private function sortFieldsByDependency(array $fields) : array {
    $child_fields = [];

    foreach ($fields as $id => $config) {
      if (!empty($config['visibility_condition'])) {
        $parent_id = $config['visibility_condition']['conditional_option'];
        $child_fields[$parent_id][$id] = $config;
      }
    }

    if (empty($child_fields)) {
      return $fields;
    }

    $ordered_fields = [];

    foreach ($fields as $id => $config) {
        $ordered_fields[$id] = $config;

      if (isset($child_fields[$id])) {
        foreach ($child_fields[$id] as $child_id => $child_config) {
          $ordered_fields[$child_id] = $child_config;
        }
      }
    }

    return $ordered_fields;
  }

}
