<?php

declare(strict_types=1);

namespace Drupal\flowdrop_trigger\Service;

use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\flowdrop_trigger\FlowDropTriggerConfigInterface;

/**
 * Builds JSON Schema for trigger configuration forms.
 *
 * Generates dynamic schemas based on event type and current configuration.
 * Used by the API controller to provide schema for the workflow editor.
 */
class TriggerConfigSchemaBuilder {

  /**
   * Constructs a TriggerConfigSchemaBuilder.
   *
   * @param \Drupal\flowdrop_trigger\Service\EventTypePluginManager $eventTypeManager
   *   The event type plugin manager.
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The entity type manager.
   * @param \Drupal\Core\Entity\EntityTypeBundleInfoInterface $bundleInfo
   *   The bundle info service.
   */
  public function __construct(
    protected EventTypePluginManager $eventTypeManager,
    protected EntityTypeManagerInterface $entityTypeManager,
    protected EntityTypeBundleInfoInterface $bundleInfo,
  ) {}

  /**
   * Build schema for a trigger config entity.
   *
   * @param \Drupal\flowdrop_trigger\FlowDropTriggerConfigInterface $entity
   *   The trigger config entity.
   *
   * @return array<string, mixed>
   *   JSON Schema.
   */
  public function buildSchema(FlowDropTriggerConfigInterface $entity): array {
    return $this->buildSchemaForEventType(
      $entity->getEventType(),
      $entity->getConditions()
    );
  }

  /**
   * Build schema for an event type.
   *
   * @param string $eventType
   *   The event type plugin ID.
   * @param array<string, mixed> $currentConditions
   *   Current condition values (for dependent fields).
   *
   * @return array<string, mixed>
   *   JSON Schema.
   */
  public function buildSchemaForEventType(
    string $eventType,
    array $currentConditions = [],
  ): array {
    $schema = [
      "type" => "object",
      "properties" => [
        "label" => [
          "type" => "string",
          "title" => "Label",
          "description" => "Human-readable name for this trigger",
        ],
        "description" => [
          "type" => "string",
          "title" => "Description",
          "description" => "Optional description of what this trigger does",
        ],
        "event_type" => $this->buildEventTypeField(),
        "conditions" => $this->buildConditionsSchema($eventType, $currentConditions),
        "orchestrator_settings" => $this->buildOrchestratorSettingsSchema($eventType),
        "status" => [
          "type" => "boolean",
          "title" => "Enabled",
          "description" => "Whether this trigger is active",
          "default" => TRUE,
        ],
        "weight" => [
          "type" => "integer",
          "title" => "Weight",
          "description" => "Order of execution when multiple triggers fire",
          "default" => 0,
        ],
      ],
      "required" => ["event_type"],
    ];

    return $schema;
  }

  /**
   * Build UI schema for a trigger config entity.
   *
   * @param \Drupal\flowdrop_trigger\FlowDropTriggerConfigInterface $entity
   *   The trigger config entity.
   *
   * @return array<string, mixed>
   *   UI Schema for form rendering.
   */
  public function buildUiSchema(FlowDropTriggerConfigInterface $entity): array {
    return $this->buildUiSchemaForEventType($entity->getEventType());
  }

  /**
   * Build UI schema for an event type.
   *
   * @param string $eventType
   *   The event type plugin ID.
   *
   * @return array<string, mixed>
   *   UI Schema.
   */
  public function buildUiSchemaForEventType(string $eventType): array {
    $uiSchema = [
      "ui:order" => [
        "label",
        "event_type",
        "conditions",
        "orchestrator_settings",
        "status",
        "weight",
        "description",
      ],
      "label" => [
        "ui:widget" => "text",
        "ui:placeholder" => "Enter a descriptive name",
      ],
      "description" => [
        "ui:widget" => "textarea",
      ],
      "event_type" => [
        "ui:widget" => "select",
      ],
      "conditions" => [
        "entity_types" => [
          "ui:widget" => "checkboxes",
        ],
        "bundles" => [
          "ui:widget" => "checkboxes",
          "ui:dependsOn" => "conditions.entity_types",
        ],
        "roles" => [
          "ui:widget" => "checkboxes",
        ],
      ],
      "orchestrator_settings" => [
        "ui:collapsed" => TRUE,
        "ui:title" => "Advanced Settings",
        "type" => [
          "ui:widget" => "select",
        ],
        "pipeline_mode" => [
          "ui:widget" => "radio",
        ],
        "pipeline_id" => [
          "ui:widget" => "text",
          "ui:dependsOn" => "orchestrator_settings.pipeline_mode",
          "ui:showIf" => ["orchestrator_settings.pipeline_mode" => "reuse"],
        ],
        "retry" => [
          "ui:collapsed" => TRUE,
        ],
      ],
      "weight" => [
        "ui:widget" => "number",
      ],
    ];

    return $uiSchema;
  }

  /**
   * Build event type selection field.
   *
   * @return array<string, mixed>
   *   JSON Schema for event type field.
   */
  protected function buildEventTypeField(): array {
    $definitions = $this->eventTypeManager->getDefinitions();
    $enum = [];
    $enumLabels = [];

    foreach ($definitions as $id => $definition) {
      $enum[] = $id;
      $enumLabels[] = (string) ($definition["label"] ?? $id);
    }

    return [
      "type" => "string",
      "title" => "Event Type",
      "description" => "The event that triggers this workflow",
      "enum" => $enum,
      "enumLabels" => $enumLabels,
      "default" => "entity.insert",
    ];
  }

  /**
   * Build conditions schema based on event type.
   *
   * @param string $eventType
   *   The event type plugin ID.
   * @param array<string, mixed> $currentConditions
   *   Current condition values.
   *
   * @return array<string, mixed>
   *   JSON Schema for conditions.
   */
  protected function buildConditionsSchema(
    string $eventType,
    array $currentConditions,
  ): array {
    $schema = [
      "type" => "object",
      "title" => "Conditions",
      "description" => "Filter which events trigger this workflow",
      "properties" => [],
    ];

    // Entity-based event types.
    if (str_starts_with($eventType, "entity.")) {
      $schema["properties"]["entity_types"] = $this->buildEntityTypesField();
      $schema["properties"]["bundles"] = $this->buildBundlesField(
        $currentConditions["entity_types"] ?? []
      );
    }

    // User-based event types.
    if (str_starts_with($eventType, "user.")) {
      $schema["properties"]["roles"] = $this->buildRolesField();
    }

    // Form-based event types.
    if (str_starts_with($eventType, "form.")) {
      $schema["properties"]["form_ids"] = $this->buildFormIdsField();
    }

    return $schema;
  }

  /**
   * Build entity types selection field.
   *
   * @return array<string, mixed>
   *   JSON Schema for entity types field.
   */
  protected function buildEntityTypesField(): array {
    $contentEntityTypes = [];
    $entityTypes = $this->entityTypeManager->getDefinitions();

    foreach ($entityTypes as $id => $definition) {
      if ($definition->getGroup() === "content") {
        $contentEntityTypes[$id] = (string) $definition->getLabel();
      }
    }

    // Sort alphabetically by label.
    asort($contentEntityTypes);

    return [
      "type" => "array",
      "title" => "Entity Types",
      "description" => "Which entity types to trigger on (empty = all)",
      "items" => [
        "type" => "string",
        "enum" => array_keys($contentEntityTypes),
        "enumLabels" => array_values($contentEntityTypes),
      ],
      "uniqueItems" => TRUE,
      "default" => [],
    ];
  }

  /**
   * Build bundles selection field based on selected entity types.
   *
   * @param array<string> $entityTypes
   *   Selected entity types.
   *
   * @return array<string, mixed>
   *   JSON Schema for bundles field.
   */
  protected function buildBundlesField(array $entityTypes): array {
    $bundles = [];

    foreach ($entityTypes as $entityType) {
      $entityBundles = $this->bundleInfo->getBundleInfo($entityType);
      foreach ($entityBundles as $bundleId => $bundleInfo) {
        $bundles[$bundleId] = sprintf(
          "%s: %s",
          $entityType,
          $bundleInfo["label"] ?? $bundleId
        );
      }
    }

    if (empty($bundles)) {
      // No entity types selected - show placeholder.
      return [
        "type" => "array",
        "title" => "Bundles",
        "description" => "Select entity types first to see available bundles",
        "items" => ["type" => "string"],
        "default" => [],
      ];
    }

    return [
      "type" => "array",
      "title" => "Bundles",
      "description" => "Which bundles to trigger on (empty = all)",
      "items" => [
        "type" => "string",
        "enum" => array_keys($bundles),
        "enumLabels" => array_values($bundles),
      ],
      "uniqueItems" => TRUE,
      "default" => [],
    ];
  }

  /**
   * Build roles selection field.
   *
   * @return array<string, mixed>
   *   JSON Schema for roles field.
   */
  protected function buildRolesField(): array {
    $roleStorage = $this->entityTypeManager->getStorage("user_role");

    /** @var \Drupal\user\RoleInterface[] $roles */
    $roles = $roleStorage->loadMultiple();

    $roleOptions = [];
    foreach ($roles as $role) {
      $roleId = $role->id();
      if ($roleId !== NULL) {
        $roleOptions[$roleId] = (string) $role->label();
      }
    }

    return [
      "type" => "array",
      "title" => "Roles",
      "description" => "Trigger only for users with these roles (empty = all)",
      "items" => [
        "type" => "string",
        "enum" => array_keys($roleOptions),
        "enumLabels" => array_values($roleOptions),
      ],
      "uniqueItems" => TRUE,
      "default" => [],
    ];
  }

  /**
   * Build form IDs selection field.
   *
   * @return array<string, mixed>
   *   JSON Schema for form IDs field.
   */
  protected function buildFormIdsField(): array {
    return [
      "type" => "array",
      "title" => "Form IDs",
      "description" => "Only trigger for these form IDs (empty = all forms)",
      "items" => [
        "type" => "string",
      ],
      "default" => [],
    ];
  }

  /**
   * Build orchestrator settings schema.
   *
   * @param string $eventType
   *   The event type plugin ID.
   *
   * @return array<string, mixed>
   *   JSON Schema for orchestrator settings.
   */
  protected function buildOrchestratorSettingsSchema(string $eventType): array {
    // Determine default orchestrator based on event type.
    $defaultOrchestrator = "default";
    if ($this->eventTypeManager->hasDefinition($eventType)) {
      try {
        $plugin = $this->eventTypeManager->createInstance($eventType);
        $defaultOrchestrator = $plugin->getDefaultOrchestrator();
      }
      catch (\Exception) {
        // Use default.
      }
    }

    return [
      "type" => "object",
      "title" => "Execution Settings",
      "description" => "How the workflow should be executed",
      "properties" => [
        "type" => [
          "type" => "string",
          "title" => "Orchestrator",
          "description" => "How to execute the workflow",
          "enum" => ["default", "synchronous", "synchronous_pipeline", "asynchronous"],
          "enumLabels" => [
            "Default (based on event)",
            "Synchronous (direct execution)",
            "Synchronous Pipeline (with job tracking)",
            "Asynchronous",
          ],
          "default" => $defaultOrchestrator,
        ],
        "pipeline_mode" => [
          "type" => "string",
          "title" => "Pipeline Mode",
          "description" => "How to handle pipeline creation",
          "enum" => ["new", "reuse", "singleton"],
          "enumLabels" => [
            "New pipeline each time",
            "Reuse existing pipeline",
            "Singleton (one at a time)",
          ],
          "default" => "new",
        ],
        "pipeline_id" => [
          "type" => "string",
          "title" => "Pipeline ID",
          "description" => "Pipeline ID for reuse mode",
          "default" => "",
        ],
        "priority" => [
          "type" => "string",
          "title" => "Priority",
          "enum" => ["low", "normal", "high", "critical"],
          "enumLabels" => ["Low", "Normal", "High", "Critical"],
          "default" => "normal",
        ],
        "timeout" => [
          "type" => "integer",
          "title" => "Timeout (seconds)",
          "description" => "Maximum execution time",
          "default" => 300,
          "minimum" => 1,
          "maximum" => 86400,
        ],
        "retry" => [
          "type" => "object",
          "title" => "Retry Settings",
          "properties" => [
            "enabled" => [
              "type" => "boolean",
              "title" => "Enable Retry",
              "default" => FALSE,
            ],
            "max_attempts" => [
              "type" => "integer",
              "title" => "Max Attempts",
              "default" => 3,
              "minimum" => 1,
              "maximum" => 10,
            ],
            "delay" => [
              "type" => "integer",
              "title" => "Delay (seconds)",
              "default" => 60,
              "minimum" => 1,
            ],
            "backoff" => [
              "type" => "string",
              "title" => "Backoff Strategy",
              "enum" => ["fixed", "linear", "exponential"],
              "enumLabels" => ["Fixed", "Linear", "Exponential"],
              "default" => "exponential",
            ],
          ],
        ],
      ],
    ];
  }

  /**
   * Get default values for a new trigger.
   *
   * @param string $eventType
   *   The event type.
   *
   * @return array<string, mixed>
   *   Default values.
   */
  public function getDefaultValues(string $eventType): array {
    $defaultOrchestrator = "asynchronous";

    if ($this->eventTypeManager->hasDefinition($eventType)) {
      try {
        $plugin = $this->eventTypeManager->createInstance($eventType);
        $defaultOrchestrator = $plugin->getDefaultOrchestrator();
      }
      catch (\Exception) {
        // Use default.
      }
    }

    return [
      "label" => "",
      "description" => "",
      "event_type" => $eventType,
      "conditions" => [
        "entity_types" => [],
        "bundles" => [],
        "roles" => [],
        "form_ids" => [],
      ],
      "orchestrator_settings" => [
        "type" => $defaultOrchestrator,
        "pipeline_mode" => "new",
        "pipeline_id" => "",
        "priority" => "normal",
        "timeout" => 300,
        "retry" => [
          "enabled" => FALSE,
          "max_attempts" => 3,
          "delay" => 60,
          "backoff" => "exponential",
        ],
      ],
      "status" => TRUE,
      "weight" => 0,
    ];
  }

}
