<?php

declare(strict_types=1);

namespace Drupal\flowdrop_trigger\Form;

use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\flowdrop_trigger\Service\EventTypePluginManager;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Configuration form for FlowDrop Trigger module settings.
 *
 * Allows site admins to restrict which event types, entity types, and bundles
 * triggers will be registered for, helping to reduce noise (logging) and
 * exit early for unwanted events.
 */
final class TriggerSettingsForm extends ConfigFormBase {

  /**
   * The event type plugin manager.
   *
   * @var \Drupal\flowdrop_trigger\Service\EventTypePluginManager
   */
  protected EventTypePluginManager $eventTypeManager;

  /**
   * The entity type manager.
   *
   * @var \Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected EntityTypeManagerInterface $entityTypeManager;

  /**
   * The entity type bundle info service.
   *
   * @var \Drupal\Core\Entity\EntityTypeBundleInfoInterface
   */
  protected EntityTypeBundleInfoInterface $bundleInfo;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container): static {
    $instance = parent::create($container);
    $instance->eventTypeManager = $container->get("plugin.manager.flowdrop_event_type");
    $instance->entityTypeManager = $container->get("entity_type.manager");
    $instance->bundleInfo = $container->get("entity_type.bundle.info");
    return $instance;
  }

  /**
   * {@inheritdoc}
   */
  public function getFormId(): string {
    return "flowdrop_trigger_settings";
  }

  /**
   * {@inheritdoc}
   */
  protected function getEditableConfigNames(): array {
    return ["flowdrop_trigger.settings"];
  }

  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state): array {
    $config = $this->config("flowdrop_trigger.settings");

    // Get current configuration values.
    $allowedEventTypes = $config->get("allowed_event_types") ?? [];
    $allowedEntityTypes = $config->get("allowed_entity_types") ?? [];
    $allowedBundles = $config->get("allowed_bundles") ?? [];

    // Get all available event types grouped by category.
    $eventTypesByCategory = $this->eventTypeManager->getEventTypesByCategory();

    $form["description"] = [
      "#type" => "markup",
      "#markup" => "<p>" . $this->t("Configure which events should trigger workflows. Leave sections empty to allow all. This helps reduce noise (logging) and allows early exit for unwanted events.") . "</p>",
    ];

    // =========================================================================
    // Event Types Section.
    // =========================================================================
    $form["event_types_wrapper"] = [
      "#type" => "details",
      "#title" => $this->t("Event Types"),
      "#open" => TRUE,
      "#description" => $this->t("Select which event types should trigger workflows. If none selected, all event types are allowed."),
    ];

    $options = $this->getEventTypeOptions($eventTypesByCategory);

    $form["event_types_wrapper"]["allowed_event_types"] = [
      "#type" => "checkboxes",
      "#title" => $this->t("Allowed Event Types"),
      "#default_value" => array_combine($allowedEventTypes, $allowedEventTypes),
      "#options" => $options,
    ];

    // Add descriptions for each event type if available.
    foreach ($eventTypesByCategory as $eventTypes) {
      foreach ($eventTypes as $eventTypeId => $eventTypeDefinition) {
        $description = $eventTypeDefinition["description"] ?? "";
        if (!empty($description)) {
          $form["event_types_wrapper"]["allowed_event_types"][$eventTypeId]["#description"] = $description;
        }
      }
    }

    // =========================================================================
    // Entity Types Section.
    // =========================================================================
    $form["entity_types_wrapper"] = [
      "#type" => "details",
      "#title" => $this->t("Entity Types"),
      "#open" => !empty($allowedEntityTypes),
      "#description" => $this->t("Select which entity types should trigger workflows. Only applies to entity events (entity.insert, entity.update, etc.). If none selected, all entity types are allowed."),
    ];

    $entityTypeOptions = $this->getContentEntityTypeOptions();

    $form["entity_types_wrapper"]["allowed_entity_types"] = [
      "#type" => "checkboxes",
      "#title" => $this->t("Allowed Entity Types"),
      "#default_value" => array_combine($allowedEntityTypes, $allowedEntityTypes),
      "#options" => $entityTypeOptions,
    ];

    // =========================================================================
    // Bundles Section.
    // =========================================================================
    $form["bundles_wrapper"] = [
      "#type" => "details",
      "#title" => $this->t("Bundles"),
      "#open" => !empty($allowedBundles),
      "#description" => $this->t("Select which bundles should trigger workflows. Only applies to entity events. If none selected, all bundles are allowed. Format: entity_type:bundle."),
    ];

    // Parse allowed bundles to get default values.
    $defaultBundles = [];
    foreach ($allowedBundles as $bundleKey) {
      $defaultBundles[$bundleKey] = $bundleKey;
    }

    // Build bundle options grouped by entity type.
    $bundleOptions = $this->getBundleOptions();

    $form["bundles_wrapper"]["allowed_bundles"] = [
      "#type" => "checkboxes",
      "#title" => $this->t("Allowed Bundles"),
      "#default_value" => $defaultBundles,
      "#options" => $bundleOptions,
    ];

    return parent::buildForm($form, $form_state);
  }

  /**
   * Get event type options for the form.
   *
   * @param array<string, array<string, array<string, mixed>>> $eventTypesByCategory
   *   Event types grouped by category.
   *
   * @return array<string, string>
   *   Array of event type options keyed by event type ID.
   */
  protected function getEventTypeOptions(array $eventTypesByCategory): array {
    $options = [];

    foreach ($eventTypesByCategory as $category => $eventTypes) {
      $categoryLabel = ucfirst($category);
      foreach ($eventTypes as $eventTypeId => $eventTypeDefinition) {
        $label = $eventTypeDefinition["label"] ?? $eventTypeId;
        // Include category in the label for clarity.
        $options[$eventTypeId] = sprintf("[%s] %s", $categoryLabel, $label);
      }
    }

    return $options;
  }

  /**
   * Get content entity type options for the form.
   *
   * @return array<string, string>
   *   Array of entity type options keyed by entity type ID.
   */
  protected function getContentEntityTypeOptions(): array {
    $options = [];

    $entityTypeDefinitions = $this->entityTypeManager->getDefinitions();

    foreach ($entityTypeDefinitions as $entityTypeId => $entityType) {
      // Only include content entities (not config entities).
      if ($entityType->getGroup() === "content") {
        $label = $entityType->getLabel();
        $options[$entityTypeId] = $label instanceof \Stringable ? (string) $label : $entityTypeId;
      }
    }

    // Sort by label.
    asort($options);

    return $options;
  }

  /**
   * Get bundle options grouped by entity type.
   *
   * @return array<string, string>
   *   Array of bundle options keyed by entity_type:bundle format.
   */
  protected function getBundleOptions(): array {
    $options = [];

    $entityTypeDefinitions = $this->entityTypeManager->getDefinitions();

    foreach ($entityTypeDefinitions as $entityTypeId => $entityType) {
      // Only include content entities.
      if ($entityType->getGroup() !== "content") {
        continue;
      }

      $entityTypeLabel = $entityType->getLabel();
      $entityTypeLabelString = $entityTypeLabel instanceof \Stringable
        ? (string) $entityTypeLabel
        : $entityTypeId;

      $bundles = $this->bundleInfo->getBundleInfo($entityTypeId);

      foreach ($bundles as $bundleId => $bundleInfo) {
        $bundleLabel = $bundleInfo["label"] ?? $bundleId;
        $bundleLabelString = $bundleLabel instanceof \Stringable
          ? (string) $bundleLabel
          : $bundleId;

        // Use entity_type:bundle format as the key.
        $key = sprintf("%s:%s", $entityTypeId, $bundleId);
        $options[$key] = sprintf("%s: %s", $entityTypeLabelString, $bundleLabelString);
      }
    }

    // Sort by label.
    asort($options);

    return $options;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state): void {
    $config = $this->config("flowdrop_trigger.settings");

    // Process event types.
    $allowedEventTypes = $this->filterCheckboxValues(
      $form_state->getValue("allowed_event_types", [])
    );

    // Process entity types.
    $allowedEntityTypes = $this->filterCheckboxValues(
      $form_state->getValue("allowed_entity_types", [])
    );

    // Process bundles.
    $allowedBundles = $this->filterCheckboxValues(
      $form_state->getValue("allowed_bundles", [])
    );

    $config->set("allowed_event_types", $allowedEventTypes);
    $config->set("allowed_entity_types", $allowedEntityTypes);
    $config->set("allowed_bundles", $allowedBundles);
    $config->save();

    parent::submitForm($form, $form_state);
  }

  /**
   * Filter checkbox values to get only checked items.
   *
   * @param array<string, mixed> $values
   *   The checkbox values from form state.
   *
   * @return array<string>
   *   Array of checked item keys, reindexed.
   */
  protected function filterCheckboxValues(array $values): array {
    // Filter out unchecked values (checkboxes return 0 for unchecked).
    $filtered = array_filter(
      $values,
      fn($value) => $value !== 0 && $value !== FALSE && $value !== NULL && $value !== ""
    );

    // Return keys (the actual values) as a reindexed array.
    return array_values(array_keys($filtered));
  }

}
