<?php

declare(strict_types=1);

namespace Drupal\flowdrop_runtime\Plugin\Orchestrator;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\Core\StringTranslation\TranslatableMarkup;
use Drupal\flowdrop_orchestration\Attribute\Orchestrator;
use Drupal\flowdrop_orchestration\DTO\Orchestrator\OrchestrationRequest;
use Drupal\flowdrop_orchestration\DTO\Orchestrator\OrchestrationResponse;
use Drupal\flowdrop_orchestration\Plugin\Orchestrator\OrchestratorPluginBase;
use Drupal\flowdrop_runtime\Service\Orchestrator\AsynchronousOrchestrator;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Asynchronous workflow orchestrator plugin.
 *
 * This orchestrator provides pipeline and job management through Drupal's
 * queue system, enabling background processing and scalable execution.
 *
 * This plugin delegates to the AsynchronousOrchestrator service which is
 * configured with all required dependencies including the SynchronousPipeline
 * Orchestrator (which has the WorkflowStateManager for snapshot generation).
 *
 * @internal This class is not part of the public API and may change without notice.
 */
#[Orchestrator(
  id: "asynchronous",
  label: new TranslatableMarkup("Asynchronous Orchestrator"),
  description: "Executes workflows asynchronously using queue system",
  capabilities: [
    "async_execution",
    "parallel_execution",
    "queue_based",
    "retry_support",
    "error_recovery",
    "workflow_compilation",
    "pipeline_management",
    "interrupt_support",
  ],
)]
class AsynchronousOrchestratorPlugin extends OrchestratorPluginBase implements ContainerFactoryPluginInterface {

  /**
   * The underlying orchestrator service.
   *
   * @var \Drupal\flowdrop_runtime\Service\Orchestrator\AsynchronousOrchestrator
   */
  protected AsynchronousOrchestrator $orchestratorService;

  /**
   * Constructs a new AsynchronousOrchestratorPlugin.
   *
   * @param array<string, mixed> $configuration
   *   A configuration array.
   * @param string $plugin_id
   *   The plugin ID.
   * @param mixed $plugin_definition
   *   The plugin definition.
   * @param \Drupal\flowdrop_runtime\Service\Orchestrator\AsynchronousOrchestrator $orchestratorService
   *   The asynchronous orchestrator service (fully configured with all deps).
   */
  public function __construct(
    array $configuration,
    $plugin_id,
    $plugin_definition,
    AsynchronousOrchestrator $orchestratorService,
  ) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->orchestratorService = $orchestratorService;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition): static {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get("flowdrop_runtime.asynchronous_orchestrator"),
    );
  }

  /**
   * {@inheritdoc}
   */
  public function orchestrate(OrchestrationRequest $request): OrchestrationResponse {
    return $this->orchestratorService->orchestrate($request);
  }

  /**
   * {@inheritdoc}
   */
  public function supportsWorkflow(array $workflow): bool {
    return $this->orchestratorService->supportsWorkflow($workflow);
  }

  /**
   * {@inheritdoc}
   */
  public function getCapabilities(): array {
    return $this->orchestratorService->getCapabilities();
  }

  /**
   * {@inheritdoc}
   */
  public function buildConfigurationForm(array $form, FormStateInterface $form_state, array $settings): array {
    // Merge with defaults to ensure all settings have values.
    $settings = array_merge($this->getDefaultConfiguration(), $settings);

    $form["info"] = [
      "#type" => "markup",
      "#markup" => "<p>" . $this->t("The asynchronous orchestrator queues workflow execution for background processing. It is ideal for long-running workflows and high-throughput scenarios.") . "</p>",
    ];

    // Queue configuration.
    $form["queue"] = [
      "#type" => "fieldset",
      "#title" => $this->t("Queue Settings"),
      "#description" => $this->t("Configure how workflows are queued for asynchronous execution."),
    ];

    $form["queue"]["queue_name"] = [
      "#type" => "textfield",
      "#title" => $this->t("Queue Name"),
      "#description" => $this->t("The Drupal queue to use for job processing. Leave empty for the default FlowDrop queue."),
      "#default_value" => $settings["queue_name"] ?? "",
      "#maxlength" => 255,
    ];

    $form["queue"]["batch_size"] = [
      "#type" => "number",
      "#title" => $this->t("Batch Size"),
      "#description" => $this->t("Number of jobs to process per cron run or queue worker invocation."),
      "#default_value" => $settings["batch_size"] ?? 10,
      "#min" => 1,
      "#max" => 100,
    ];

    // Pipeline settings (inherits from synchronous pipeline).
    $form["pipeline"] = [
      "#type" => "fieldset",
      "#title" => $this->t("Pipeline Settings"),
      "#description" => $this->t("Configure pipeline creation and management."),
    ];

    $form["pipeline"]["pipeline_mode"] = [
      "#type" => "select",
      "#title" => $this->t("Pipeline Mode"),
      "#description" => $this->t("Controls how pipelines are created when this trigger fires."),
      "#options" => [
        "new" => $this->t("New pipeline each time"),
        "reuse" => $this->t("Reuse existing pipeline"),
        "singleton" => $this->t("Singleton (one at a time)"),
      ],
      "#default_value" => $settings["pipeline_mode"] ?? "new",
    ];

    $form["pipeline"]["overlap_policy"] = [
      "#type" => "select",
      "#title" => $this->t("Schedule Overlap Policy"),
      "#description" => $this->t("What to do when a cron trigger fires while a previous pipeline from the same workflow is still active."),
      "#options" => [
        "skip" => $this->t("Skip — do not start while a run is active"),
        "buffer" => $this->t("Buffer — queue one run; start it when the active run finishes"),
        "cancel" => $this->t("Cancel — cancel the active pipeline and start a new one"),
        "terminate" => $this->t("Terminate — cancel the active pipeline and its jobs, then start a new one"),
      ],
      "#default_value" => $settings["overlap_policy"] ?? "skip",
    ];

    $form["pipeline"]["pipeline_id"] = [
      "#type" => "textfield",
      "#title" => $this->t("Pipeline ID"),
      "#description" => $this->t("Specify a pipeline ID for 'reuse' mode."),
      "#default_value" => $settings["pipeline_id"] ?? "",
      "#maxlength" => 255,
      "#states" => [
        "visible" => [
          ":input[name=\"orchestrator_settings[pipeline][pipeline_mode]\"]" => ["value" => "reuse"],
        ],
      ],
    ];

    // Retry settings.
    $form["retry"] = [
      "#type" => "details",
      "#title" => $this->t("Retry Settings"),
      "#description" => $this->t("Configure retry behavior for failed jobs."),
      "#open" => FALSE,
    ];

    $form["retry"]["retry_enabled"] = [
      "#type" => "checkbox",
      "#title" => $this->t("Enable Retry"),
      "#description" => $this->t("Automatically retry failed jobs."),
      "#default_value" => $settings["retry"]["enabled"] ?? TRUE,
    ];

    $form["retry"]["retry_max_attempts"] = [
      "#type" => "number",
      "#title" => $this->t("Max Retry Attempts"),
      "#default_value" => $settings["retry"]["max_attempts"] ?? 3,
      "#min" => 1,
      "#max" => 10,
      "#states" => [
        "visible" => [
          ":input[name=\"orchestrator_settings[retry][retry_enabled]\"]" => ["checked" => TRUE],
        ],
      ],
    ];

    $form["retry"]["retry_delay"] = [
      "#type" => "number",
      "#title" => $this->t("Retry Delay (seconds)"),
      "#default_value" => $settings["retry"]["delay"] ?? 60,
      "#min" => 0,
      "#max" => 3600,
      "#states" => [
        "visible" => [
          ":input[name=\"orchestrator_settings[retry][retry_enabled]\"]" => ["checked" => TRUE],
        ],
      ],
    ];

    // Jitter settings.
    $jitter = $settings['jitter'] ?? [];
    $form['jitter'] = [
      '#type' => 'details',
      '#title' => $this->t('Jitter Settings'),
      '#description' => $this->t('Spread cron trigger execution across a random delay window to avoid thundering-herd when many workflows fire at the same time.'),
      '#open' => FALSE,
    ];

    $form['jitter']['jitter_enabled'] = [
      '#type' => 'checkbox',
      '#title' => $this->t('Enable Jitter'),
      '#description' => $this->t('Add a random delay before each cron execution.'),
      '#default_value' => $jitter['enabled'] ?? FALSE,
    ];

    $form['jitter']['jitter_min_seconds'] = [
      '#type' => 'number',
      '#title' => $this->t('Minimum delay (seconds)'),
      '#default_value' => $jitter['min_seconds'] ?? 0,
      '#min' => 0,
      '#max' => 3600,
      '#states' => [
        'visible' => [
          ':input[name="orchestrator_settings[jitter][jitter_enabled]"]' => ['checked' => TRUE],
        ],
      ],
    ];

    $form['jitter']['jitter_max_seconds'] = [
      '#type' => 'number',
      '#title' => $this->t('Maximum delay (seconds)'),
      '#default_value' => $jitter['max_seconds'] ?? 30,
      '#min' => 0,
      '#max' => 3600,
      '#states' => [
        'visible' => [
          ':input[name="orchestrator_settings[jitter][jitter_enabled]"]' => ['checked' => TRUE],
        ],
      ],
    ];

    // Add common fields from parent (priority, timeout).
    $form = parent::buildConfigurationForm($form, $form_state, $settings);

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitConfigurationForm(array $form, FormStateInterface $form_state): array {
    // Get base configuration from parent.
    $config = parent::submitConfigurationForm($form, $form_state);

    // Extract asynchronous-specific values.
    $values = $form_state->getValue("orchestrator_settings") ?? [];

    // Queue settings.
    $queueSettings = $values["queue"] ?? [];
    $config["queue_name"] = $queueSettings["queue_name"] ?? "";
    $config["batch_size"] = (int) ($queueSettings["batch_size"] ?? 10);

    // Pipeline settings.
    $pipelineSettings = $values["pipeline"] ?? [];
    $config["pipeline_mode"] = $pipelineSettings["pipeline_mode"] ?? "new";
    $config["pipeline_id"] = $pipelineSettings["pipeline_id"] ?? "";
    $config["overlap_policy"] = $pipelineSettings["overlap_policy"] ?? "skip";

    // Retry settings.
    $retrySettings = $values["retry"] ?? [];
    $config["retry"] = [
      "enabled" => (bool) ($retrySettings["retry_enabled"] ?? TRUE),
      "max_attempts" => (int) ($retrySettings["retry_max_attempts"] ?? 3),
      "delay" => (int) ($retrySettings["retry_delay"] ?? 60),
    ];

    // Jitter settings.
    $jitterSettings = $values["jitter"] ?? [];
    $config["jitter"] = [
      "enabled" => (bool) ($jitterSettings["jitter_enabled"] ?? FALSE),
      "min_seconds" => (int) ($jitterSettings["jitter_min_seconds"] ?? 0),
      "max_seconds" => (int) ($jitterSettings["jitter_max_seconds"] ?? 30),
    ];

    return $config;
  }

  /**
   * {@inheritdoc}
   */
  public function getDefaultConfiguration(): array {
    return [
      "type" => $this->getPluginId(),
      "priority" => "normal",
      "timeout" => 300,
      "queue_name" => "",
      "batch_size" => 10,
      "pipeline_mode" => "new",
      "pipeline_id" => "",
      "overlap_policy" => "skip",
      "retry" => [
        "enabled" => TRUE,
        "max_attempts" => 3,
        "delay" => 60,
      ],
      "jitter" => [
        "enabled" => FALSE,
        "min_seconds" => 0,
        "max_seconds" => 30,
      ],
    ];
  }

}
