<?php

namespace Drupal\eb\Form;

use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;
use Drupal\eb\Service\DefinitionFactory;
use Drupal\eb\Service\DefinitionGeneratorInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Form for generating definition entities from existing Drupal entities.
 *
 * This form uses the definition-centric export approach: it generates an
 * EbDefinition entity from selected bundles, which can then be
 * exported to YAML via the definition export controller.
 */
class EbExportForm extends FormBase {

  /**
   * Constructor.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The entity type manager.
   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundleInfo
   *   The bundle info service.
   * @param \Drupal\eb\Service\DefinitionGeneratorInterface $definitionGenerator
   *   The definition generator service.
   * @param \Drupal\eb\Service\DefinitionFactory $definitionFactory
   *   The definition factory service.
   */
  public function __construct(
    protected EntityTypeManagerInterface $entityTypeManager,
    protected EntityTypeBundleInfoInterface $bundleInfo,
    protected DefinitionGeneratorInterface $definitionGenerator,
    protected DefinitionFactory $definitionFactory,
  ) {}

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): self {
    return new self(
      $container->get('entity_type.manager'),
      $container->get('entity_type.bundle.info'),
      $container->get('eb.definition_generator'),
      $container->get('eb.definition_factory'),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId(): string {
    return 'eb_export_form';
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state): array {
    $form['#attached']['library'][] = 'eb/export.select_all';

    $form['help'] = [
      '#type' => 'item',
      '#markup' => $this->t('Generate a definition entity from existing Drupal bundles. The definition can then be exported to YAML.'),
    ];

    // Definition metadata.
    $form['definition'] = [
      '#type' => 'details',
      '#title' => $this->t('Definition Settings'),
      '#open' => TRUE,
    ];

    $form['definition']['definition_label'] = [
      '#type' => 'textfield',
      '#title' => $this->t('Definition Label'),
      '#description' => $this->t('Human-readable name for this definition.'),
      '#required' => TRUE,
    ];

    $form['definition']['definition_id'] = [
      '#type' => 'machine_name',
      '#title' => $this->t('Definition ID'),
      '#description' => $this->t('A unique machine name for this definition.'),
      '#required' => TRUE,
      '#machine_name' => [
        'exists' => [$this, 'definitionExists'],
        'source' => ['definition', 'definition_label'],
      ],
    ];

    $form['definition']['definition_description'] = [
      '#type' => 'textarea',
      '#title' => $this->t('Description'),
      '#description' => $this->t('Optional description of what this definition contains.'),
      '#rows' => 2,
    ];

    $form['definition']['update_existing'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Update existing definition if it exists'),
      '#description' => $this->t('If checked, an existing definition with this ID will be updated instead of rejected.'),
    ];

    // Entity type selection.
    $entity_types = $this->getFieldableEntityTypes();

    $form['entity_selection'] = [
      '#type' => 'details',
      '#title' => $this->t('Select Bundles to Export'),
      '#open' => TRUE,
    ];

    foreach ($entity_types as $entity_type_id => $entity_type_label) {
      $bundles = $this->bundleInfo->getBundleInfo($entity_type_id);

      if (empty($bundles)) {
        continue;
      }

      $form['entity_selection'][$entity_type_id] = [
        '#type' => 'details',
        '#title' => $entity_type_label,
        '#open' => FALSE,
        '#tree' => TRUE,
      ];

      $bundle_options = [];
      foreach ($bundles as $bundle_id => $bundle_info) {
        $bundle_options[$bundle_id] = $bundle_info['label'];
      }

      $form['entity_selection'][$entity_type_id]['select_all'] = [
        '#type' => 'checkbox',
        '#title' => $this->t('Select all'),
        '#weight' => -10,
        '#attributes' => ['class' => ['eb-export-select-all']],
      ];

      $form['entity_selection'][$entity_type_id]['bundles'] = [
        '#type' => 'checkboxes',
        '#title' => $this->t('Bundles'),
        '#options' => $bundle_options,
      ];

      // Add class to each bundle checkbox for JavaScript targeting.
      foreach ($bundle_options as $bundle_id => $bundle_label) {
        $form['entity_selection'][$entity_type_id]['bundles'][$bundle_id] = [
          '#attributes' => ['class' => ['eb-export-bundle-checkbox']],
        ];
      }
    }

    // Export options.
    $form['options'] = [
      '#type' => 'details',
      '#title' => $this->t('Export Options'),
      '#open' => TRUE,
      '#tree' => TRUE,
    ];

    $form['options']['include_fields'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Include fields'),
      '#default_value' => TRUE,
    ];

    $form['options']['include_displays'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Include display configuration'),
      '#default_value' => TRUE,
    ];

    $form['options']['include_extensions'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Include extension data (field groups, pathauto, etc.)'),
      '#default_value' => TRUE,
    ];

    $form['options']['normalize_settings'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Normalize settings (exclude Drupal defaults)'),
      '#default_value' => TRUE,
      '#description' => $this->t('Produces cleaner output by removing settings that match field type defaults.'),
    ];

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

    $form['actions']['submit'] = [
      '#type' => 'submit',
      '#value' => $this->t('Generate Definition'),
      '#button_type' => 'primary',
    ];

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function validateForm(array &$form, FormStateInterface $form_state): void {
    // Check if at least one bundle is selected.
    $entity_types = $this->getFieldableEntityTypes();
    $has_selection = FALSE;

    foreach ($entity_types as $entity_type_id => $entity_type_label) {
      $entity_type_values = $form_state->getValue($entity_type_id, []);

      $bundles = [];
      if (is_array($entity_type_values) && isset($entity_type_values['bundles'])) {
        $bundles = $entity_type_values['bundles'];
      }

      $bundles = array_filter($bundles);

      if (!empty($bundles)) {
        $has_selection = TRUE;
        break;
      }
    }

    if (!$has_selection) {
      $form_state->setErrorByName('entity_selection', $this->t('Please select at least one bundle to export.'));
    }

    // Check if definition already exists and update not allowed.
    $definition_id = $form_state->getValue('definition_id');
    $update_existing = $form_state->getValue('update_existing');

    if ($definition_id && !$update_existing && $this->definitionFactory->definitionExists($definition_id)) {
      $form_state->setErrorByName('definition_id', $this->t('A definition with ID "@id" already exists. Check "Update existing" to overwrite it.', [
        '@id' => $definition_id,
      ]));
    }
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state): void {
    $entity_types = $this->getFieldableEntityTypes();

    // Collect selected bundles.
    $bundle_selection = [];
    foreach ($entity_types as $entity_type_id => $entity_type_label) {
      $entity_type_values = $form_state->getValue($entity_type_id, []);

      $bundles = [];
      if (is_array($entity_type_values) && isset($entity_type_values['bundles'])) {
        $bundles = array_filter($entity_type_values['bundles']);
      }

      if (!empty($bundles)) {
        $bundle_selection[$entity_type_id] = array_values($bundles);
      }
    }

    if (empty($bundle_selection)) {
      $this->messenger()->addWarning($this->t('No bundles selected.'));
      return;
    }

    // Build options.
    $options = [
      'include_fields' => (bool) $form_state->getValue(['options', 'include_fields']),
      'include_displays' => (bool) $form_state->getValue(['options', 'include_displays']),
      'include_extensions' => (bool) $form_state->getValue(['options', 'include_extensions']),
      'normalize_settings' => (bool) $form_state->getValue(['options', 'normalize_settings']),
    ];

    try {
      // Generate definition data from Drupal entities.
      $definition_data = $this->definitionGenerator->generate($bundle_selection, $options);

      // Add metadata.
      $definition_data['id'] = $form_state->getValue('definition_id');
      $definition_data['label'] = $form_state->getValue('definition_label');

      $description = $form_state->getValue('definition_description');
      if (!empty($description)) {
        $definition_data['description'] = $description;
      }

      // Create or update definition.
      $definition_id = $form_state->getValue('definition_id');
      $update_existing = $form_state->getValue('update_existing');

      if ($update_existing && $this->definitionFactory->definitionExists($definition_id)) {
        // Update existing.
        $definition = $this->definitionFactory->loadDefinition($definition_id);
        $definition = $this->definitionFactory->updateFromYaml($definition, $definition_data);
        $definition->save();

        $this->messenger()->addStatus($this->t('Updated definition "@label".', [
          '@label' => $definition->label(),
        ]));
      }
      else {
        // Create new.
        $definition = $this->definitionFactory->createFromYaml($definition_data);
        $definition->save();

        $this->messenger()->addStatus($this->t('Created definition "@label".', [
          '@label' => $definition->label(),
        ]));
      }

      // Redirect to definition view page.
      $form_state->setRedirectUrl(Url::fromRoute('entity.eb_definition.canonical', [
        'eb_definition' => $definition->id(),
      ]));
    }
    catch (\Exception $e) {
      $this->messenger()->addError($this->t('Error generating definition: @message', [
        '@message' => $e->getMessage(),
      ]));
    }
  }

  /**
   * Checks if a definition with the given ID already exists.
   *
   * @param string $id
   *   The definition ID.
   *
   * @return bool
   *   TRUE if the definition exists.
   */
  public function definitionExists(string $id): bool {
    return $this->definitionFactory->definitionExists($id);
  }

  /**
   * Gets fieldable entity types.
   *
   * @return array<string, string>
   *   Array of entity type IDs and labels.
   */
  protected function getFieldableEntityTypes(): array {
    $entity_types = [];

    foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) {
      if ($entity_type->hasKey('bundle') && $entity_type->getBundleEntityType()) {
        $entity_types[$entity_type_id] = (string) $entity_type->getLabel();
      }
    }

    return $entity_types;
  }

}
