<?php

namespace Drupal\castorcito\Controller;

use Drupal\castorcito\CastorcitoManager;
use Drupal\Component\Serialization\Json;
use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;



/**
 * Returns responses for CastorcitoComponentOverrideConfigController.
 */
class CastorcitoComponentOverrideConfigController extends ControllerBase {

  /**
   * List of container types used to group components.
   */
  protected const CONTAINERS = ['container', 'advanced_container'];

  /**
   * Castorcito Manager Service.
   *
   * @var \Drupal\castorcito\CastorcitoManager
   */
  protected $castorcitoManager;

  /**
   * The entity display repository.
   *
   * @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface
   */
  protected $entityDisplayRepository;

  /**
   * Constructs a new CastorcitoComponentOverrideConfigController.
   *
   * @param \Drupal\castorcito\CastorcitoManager $castorcitoManager
   *   Castorcito manager service.
   * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
   *   The entity display repository.
   */
  public function __construct(
    CastorcitoManager $castorcitoManager,
    EntityDisplayRepositoryInterface $entity_display_repository
  ) {
    $this->castorcitoManager = $castorcitoManager;
    $this->entityDisplayRepository = $entity_display_repository;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('castorcito.manager'),
      $container->get('entity_display.repository')
    );
  }

  /**
   * Provides the override configuration list for a specific component.
   *
   * @param string $entity_type
   *   The entity type ID where the component is used (e.g., 'node', 'block_content').
   * @param string $bundle
   *   The bundle of the entity type (e.g., 'article', 'basic_page').
   * @param string $field_name
   *   The field name in which the component is configured.
   * @param string $component_id
   *   The machine name of the component to list override settings for.
   *
   * @return array
   *   A render array representing the configuration table.
   */
  public function listOverrideComponentConfig(string $entity_type, string $bundle,  string $field_name, string $component_id) {
    $form_display = $this->entityDisplayRepository->getFormDisplay($entity_type, $bundle);
    $widget = $form_display->getComponent($field_name);
    $component_overridden_config = $widget['settings']['components_settings'] ?? [];
    $component = $this->castorcitoManager->castorcitoComponent($component_id);

    // Route parameters used to build the URL for the override configuration form
    // (castorcito.override_config_form), which allows overriding the component config.
    $route_params = [
      'entity_type' => $entity_type,
      'bundle' => $bundle,
      'field_name' => $field_name,
    ];

    $message = [
      '#markup' => '<div class="messages messages--warning">' . $this->t('<strong>Important:</strong> For the settings on this screen to take effect, you must have selected and saved the <strong>' . $component->label() . '</strong> component in the widget on the <em>Manage Form Display</em> screen. If you haven\'t, return to the <em>Manage Form Display</em> tab and click the <strong>Update</strong> button in the widget settings section and <strong>Save</strong> the <em>Manage Form Display</em>.') . '</div>',
    ];

    return $message + $this->listComponentConfig($component, $component_overridden_config, $route_params);
  }

  /**
   * Title callback for the override component page.
   *
   * @param string $entity_type
   * @param string $bundle
   * @param string $field_name
   * @param string $component_id
   *
   * @return string
   *   The page title including the component label.
   */
  public function listOverrideComponentConfigTitle(string $entity_type, string $bundle, string $field_name, string $component_id): string {
    $component = $this->castorcitoManager->castorcitoComponent($component_id);
    return $this->t('Component @label override config', ['@label' => $component->label()]);
  }

  /**
   * Recursively builds the configuration tree for a component.
   *
   * @param objet $component
   *   The component to render.
   * @param array $component_overridden_config
   *   The overridden configuration for the component, as a nested array.
   * @param array $parameters
   *   Route parameters to build URLs for actions (e.g., edit links).
   * @param array $parent_keys
   *   The array keys representing the current hierarchy path, within the configuration tree.
   *
   * @return array
   *   A nested render array representing the component configuration tree.
   */
  private function listComponentConfig($component, array $component_overridden_config, array $parameters, array $parent_keys = []): array {
    $list_component_config = [];
    $current_keys = [...$parent_keys, $component->id()];

    // Add parent component container if we are in a nested call.
    if (!empty($parent_keys)) {
      NestedArray::setValue($list_component_config, $current_keys, [
        '#type' => 'details',
        '#title' => $component->label(),
      ]);
    }

    // Add "Form settings" section.
    $settings_keys = [...$current_keys, 'settings'];
    NestedArray::setValue($list_component_config, $settings_keys, [
      '#type' => 'details',
      '#title' => $this->t('Form settings'),
      '#open' => TRUE,
    ]);

    NestedArray::setValue($list_component_config,
      [...$settings_keys, 'form_settings_table'],
      $this->buildComponentConfigTable($this->componentFormSettings(), $component_overridden_config, $parameters, $settings_keys)
    );

    // Add "CFields" section.
    $cfields_keys = [...$current_keys, 'cfields'];
    $cfield_configurations = $component->get('field_configuration');
    NestedArray::setValue($list_component_config, $cfields_keys, [
      '#type' => 'fieldset',
      '#title' => $this->t('CFields'),
    ]);

    foreach ($cfield_configurations as $f_key => $configuration) {
      $field_keys = [...$cfields_keys, $f_key];
      $field_settings_keys = [...$field_keys, 'settings'];
      NestedArray::setValue($list_component_config, $field_keys, [
        '#type' => 'details',
        '#title' => $this->t($configuration['label']),
      ]);

      $component_field_overridable_config = $this->getComponentFieldOverridableConfiguration($configuration['id']);
      if (!empty($component_field_overridable_config)) {
        NestedArray::setValue($list_component_config, $field_settings_keys, [
          '#type' => 'details',
          '#title' => $this->t('Settings'),
          '#open' => TRUE
        ]);
      }

      if (in_array($configuration['id'], self::CONTAINERS, TRUE)) {
        if ($configuration['id'] === 'advanced_container') {
          $cfield_head_container_current_keys = [...$field_settings_keys, 'allowed_head_component'];
          NestedArray::setValue($list_component_config, $cfield_head_container_current_keys, [
            '#type' => 'details',
            '#title' => $this->t('Head component'),
          ]);

          $head_component = $this->castorcitoManager->castorcitoComponent($configuration['settings']['head_component']);
          $child_config = $this->listComponentConfig($head_component, $component_overridden_config, $parameters, $cfield_head_container_current_keys);
          $list_component_config = NestedArray::mergeDeepArray([$list_component_config, $child_config]);
        }

        $allowed_children_keys = [...$field_settings_keys, 'allowed_components'];
        NestedArray::setValue($list_component_config, $allowed_children_keys, [
          '#type' => 'details',
          '#title' => $this->t('Allowed components'),
        ]);

        foreach ($configuration['settings']['allowed_children'] as $child_id) {
          $child_component = $this->castorcitoManager->castorcitoComponent($child_id);
          $child_config = $this->listComponentConfig($child_component, $component_overridden_config, $parameters, $allowed_children_keys);
          $list_component_config = NestedArray::mergeDeepArray([$list_component_config, $child_config]);
        }
      }

      NestedArray::setValue($list_component_config,
        [...$field_settings_keys, 'cfield_element_table'],
        $this->buildComponentConfigTable($component_field_overridable_config, $component_overridden_config, $parameters, $field_settings_keys)
      );

      NestedArray::setValue($list_component_config,
        [...$field_keys, 'general_element_table'],
        $this->buildComponentConfigTable(['description' => $this->t('Help text')], $component_overridden_config, $parameters, $field_keys)
      );
    }

    return $list_component_config;
  }

  /**
   * Builds a configuration table for component settings.
   *
   * @param array $elements
   *   The array of configuration elements to display in the table.
   * @param array $component_overridden_config
   *   The overridden configuration for the component, as a nested array.
   * @param array $parameters
   *   Route parameters to build URLs for operation links.
   * @param array $keys
   *   The hierarchy path within the component configuration tree.
   *
   * @return array
   *   A render array representing the configuration table.
   */
  private function buildComponentConfigTable(array $elements, array $component_overridden_config, array $parameters, array $keys) {
    $rows = [];
    foreach ($elements as $item_key => $item) {
      $config_key = implode('.', [...$keys, $item_key]);
      $parameters['config_key'] = $config_key;
      $overridden_config = NestedArray::getValue($component_overridden_config, [...$keys, $item_key]);

      $row_data['label'] = [
        'data' => $item
      ];

      $row_data['overridden'] = [
        'data' => !is_null($overridden_config) ? $this->t('Overridden') : $this->t('Default'),
      ];

      $operations = [];
      $operations['edit'] = [
        'title' => !is_null($overridden_config) ? $this->t('Edit') : $this->t('Override'),
        'url' => Url::fromRoute('castorcito.override_config_form')
        ->setRouteParameters($parameters)
        ->setOptions([
          // ‘ccoc_source’ refers to “CastorcitoComponentOverrideConfig”.
          // It is used as a query parameter to identify the source of the action.
          'query' => ['ccoc_source' => 'edit'],
          'attributes' => [
            'class' => ['use-ajax'],
            'data-dialog-type' => 'modal',
            'data-dialog-options' => Json::encode([
              'width' => 800,
              'title' => $this->t('Override config')
            ]),
          ],
        ]),
      ];

      if (!is_null($overridden_config)) {
        $operations['use_default'] = [
          'title' => $this->t('Use default'),
          'url' => Url::fromRoute('castorcito.override_config_form')
          ->setRouteParameters($parameters)
          ->setOptions([
            // ‘ccoc_source’ refers to “CastorcitoComponentOverrideConfig”.
            // It is used as a query parameter to identify the source of the action.
            'query' => ['ccoc_source' => 'use_default'],
            'attributes' => [
              'class' => ['use-ajax'],
              'data-dialog-type' => 'modal',
              'data-dialog-options' => Json::encode([
                'width' => 800,
                'title' => $this->t('Use default config')
              ]),
            ],
          ]),
        ];
      }

      $row_data['operations'] = [
        'data' => [
          '#type' => 'operations',
          '#links' => $operations,
        ]
      ];

      $rows[] = $row_data;
    }


    return  [
      '#type' => 'table',
      '#header' => [],
      '#rows' => $rows,
    ];
  }

  /**
   * Gets the overridable configuration for a component field.
   *
   * @param string $plugin_id
   *   The plugin ID of the component field instance.
   *
   * @return array
   */
  private function getComponentFieldOverridableConfiguration($plugin_id) {
    $cfield_instance = $this->castorcitoManager->getComponentFieldInstance($plugin_id);
    $overridable_configuration = $cfield_instance->overridableConfiguration();

    if (empty($overridable_configuration)) {
      return [];
    }

    $build_configuration_form = $this->castorcitoManager->getComponentFieldInstanceConfigurationForm($cfield_instance);
    $overridable_elements = [];
    foreach ($overridable_configuration as $config) {
      $overridable_elements[$config] = $build_configuration_form[$config]['#title'];
    }

    return $overridable_elements;
  }

  /**
   * Provides the default form settings for a component.
   *
   * @return array
   *   An associative array.
   */
  private function componentFormSettings() {
    return [
      'enable_edit_name' => $this->t('Enable editing of component name'),
      'collapse_component' => $this->t('Collapse component, set <strong>close</strong> by default')
    ];
  }

}
