<?php

declare(strict_types=1);

namespace Drupal\flowdrop_trigger\Drush\Commands;

use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\State\StateInterface;
use Drupal\flowdrop_trigger\Service\TriggerManagerInterface;
use Drush\Attributes as CLI;
use Drush\Commands\AutowireTrait;
use Drush\Commands\DrushCommands;

/**
 * Drush commands for testing FlowDrop triggers.
 *
 * @internal This class is not part of the public API and may change without notice.
 */
final class TriggerTestCommands extends DrushCommands {

  use AutowireTrait;

  /**
   * Constructs a TriggerTestCommands object.
   *
   * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager
   *   The entity type manager.
   * @param \Drupal\flowdrop_trigger\Service\TriggerManagerInterface $triggerManager
   *   The trigger manager.
   * @param \Drupal\Core\State\StateInterface $state
   *   The state service.
   */
  public function __construct(
    private readonly EntityTypeManagerInterface $entityTypeManager,
    private readonly TriggerManagerInterface $triggerManager,
    private readonly StateInterface $state,
  ) {
    parent::__construct();
  }

  /**
   * Manually fire a trigger event for testing.
   *
   * For update events, the original entity is automatically set on the entity
   * by loading the unchanged version from storage, simulating how Drupal
   * behaves during actual entity save operations.
   *
   * @param string $eventType
   *   The event type (e.g., entity.insert, entity.update, entity.delete).
   * @param string $entityType
   *   The entity type (e.g., node, user).
   * @param string|int $entityId
   *   The entity ID.
   */
  #[CLI\Command(name: "flowdrop:trigger:test", aliases: ["fd:trigger:test"])]
  #[CLI\Argument(name: "eventType", description: "The event type (e.g., entity.insert, entity.update, entity.delete)")]
  #[CLI\Argument(name: "entityType", description: "The entity type (e.g., node, user)")]
  #[CLI\Argument(name: "entityId", description: "The entity ID")]
  #[CLI\Usage(name: "flowdrop:trigger:test entity.insert node 1", description: "Fire an entity.insert event for node 1")]
  #[CLI\Usage(name: "flowdrop:trigger:test entity.update node 1", description: "Fire an entity.update event for node 1 (includes original entity)")]
  #[CLI\Usage(name: "flowdrop:trigger:test entity.delete node 1", description: "Fire an entity.delete event for node 1")]
  public function testTrigger(
    string $eventType,
    string $entityType,
    string|int $entityId,
  ): void {
    try {
      $storage = $this->entityTypeManager->getStorage($entityType);
      $entity = $storage->load($entityId);

      if ($entity === NULL) {
        $this->logger()->error("Entity not found: @type @id", [
          "@type" => $entityType,
          "@id" => $entityId,
        ]);
        return;
      }

      // For update/presave events, set the original entity to simulate
      // how Drupal behaves during actual entity save operations.
      if (in_array($eventType, ["entity.update", "entity.presave"], TRUE)) {
        $entity->original = $storage->loadUnchanged($entityId);
      }

      $this->logger()->notice("Firing trigger event @event for @type @id", [
        "@event" => $eventType,
        "@type" => $entityType,
        "@id" => $entityId,
      ]);

      $this->triggerManager->processEvent($eventType, $entity);

      $this->logger()->success("Trigger event fired successfully. Check logs for execution details.");
    }
    catch (\Exception $e) {
      $this->logger()->error("Failed to fire trigger: @message", [
        "@message" => $e->getMessage(),
      ]);
    }
  }

  /**
   * Manually fire a cron trigger event for testing.
   *
   * This simulates a Drupal cron run and fires the cron.run event
   * to test cron-based workflow triggers.
   */
  #[CLI\Command(name: "flowdrop:trigger:test-cron", aliases: ["fd:trigger:test-cron"])]
  #[CLI\Usage(name: "flowdrop:trigger:test-cron", description: "Fire a cron.run event to test cron triggers")]
  public function testCronTrigger(): void {
    try {
      $this->logger()->notice("Firing cron.run trigger event for testing");

      // Build context with cron-specific information.
      $context = [
        "last_run" => $this->state->get("flowdrop_trigger.last_cron_run", 0),
        "cron_key" => NULL,
      ];

      $this->triggerManager->processCronEvent("cron.run", $context);

      $this->logger()->success("Cron trigger event fired successfully. Check logs for execution details.");
    }
    catch (\Exception $e) {
      $this->logger()->error("Failed to fire cron trigger: @message", [
        "@message" => $e->getMessage(),
      ]);
    }
  }

  /**
   * List all active triggers grouped by event type.
   */
  #[CLI\Command(name: "flowdrop:trigger:list", aliases: ["fd:trigger:list"])]
  #[CLI\Usage(name: "flowdrop:trigger:list", description: "List all active trigger configurations")]
  public function listTriggers(): void {
    $triggers = $this->triggerManager->getAllActiveTriggers();

    if (empty($triggers)) {
      $this->logger()->warning("No active triggers found.");
      return;
    }

    $this->output()->writeln("\n<info>Active Triggers:</info>\n");

    foreach ($triggers as $eventType => $configs) {
      $this->output()->writeln("<comment>Event Type: {$eventType}</comment>");
      foreach ($configs as $config) {
        $this->output()->writeln(sprintf(
          "  - ID: %s | Workflow: %s | Node: %s | Weight: %d",
          $config->id(),
          $config->getWorkflowId(),
          $config->getNodeId(),
          $config->getWeight()
        ));
      }
      $this->output()->writeln("");
    }
  }

  /**
   * Test a specific trigger configuration by ID.
   *
   * @param string $triggerId
   *   The trigger configuration ID (format: workflow_id__node_id).
   * @param string $eventType
   *   The event type to simulate.
   * @param string $entityType
   *   The entity type.
   * @param string|int $entityId
   *   The entity ID.
   */
  #[CLI\Command(name: "flowdrop:trigger:test-config", aliases: ["fd:trigger:test-config"])]
  #[CLI\Argument(name: "triggerId", description: "The trigger configuration ID (format: workflow_id__node_id)")]
  #[CLI\Argument(name: "eventType", description: "The event type to simulate")]
  #[CLI\Argument(name: "entityType", description: "The entity type")]
  #[CLI\Argument(name: "entityId", description: "The entity ID")]
  #[CLI\Usage(name: "flowdrop:trigger:test-config my_workflow__node_123 entity.insert node 1", description: "Test a specific trigger configuration")]
  public function testTriggerConfig(
    string $triggerId,
    string $eventType,
    string $entityType,
    string|int $entityId,
  ): void {
    try {
      $storage = $this->entityTypeManager->getStorage("flowdrop_trigger_config");
      $triggerConfig = $storage->load($triggerId);

      if ($triggerConfig === NULL) {
        $this->logger()->error("Trigger config not found: @id", [
          "@id" => $triggerId,
        ]);
        return;
      }

      $entityStorage = $this->entityTypeManager->getStorage($entityType);
      $entity = $entityStorage->load($entityId);

      if ($entity === NULL) {
        $this->logger()->error("Entity not found: @type @id", [
          "@type" => $entityType,
          "@id" => $entityId,
        ]);
        return;
      }

      $this->logger()->notice("Testing trigger config @trigger with event @event", [
        "@trigger" => $triggerId,
        "@event" => $eventType,
      ]);

      // For update/presave events, set the original entity to simulate
      // how Drupal behaves during actual entity save operations.
      if (in_array($eventType, ["entity.update", "entity.presave"], TRUE)) {
        $entity->original = $entityStorage->loadUnchanged($entityId);
      }

      // Check if trigger matches conditions.
      $context = [
        "entity_type" => $entity->getEntityTypeId(),
        "bundle" => $entity->bundle(),
        "entity" => $entity,
        "original" => $entity->original ?? NULL,
      ];

      if (!$triggerConfig->matches($eventType, $context)) {
        $this->logger()->warning("Trigger config conditions do not match. Event will not fire.");
        return;
      }

      $this->triggerManager->processEvent($eventType, $entity);

      $this->logger()->success("Trigger config tested successfully.");
    }
    catch (\Exception $e) {
      $this->logger()->error("Failed to test trigger config: @message", [
        "@message" => $e->getMessage(),
      ]);
    }
  }

}
