<?php

declare(strict_types=1);

namespace Drupal\flowdrop_trigger\Service;

use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Plugin\DefaultPluginManager;
use Drupal\flowdrop_trigger\Attribute\FlowDropEventType;
use Drupal\flowdrop_trigger\Enum\EventCategory;
use Drupal\flowdrop_trigger\FlowDropEventTypeInterface;

/**
 * Plugin manager for FlowDrop Event Type plugins.
 *
 * Manages discovery and instantiation of event type plugins that define
 * the different types of events that can trigger workflow execution.
 *
 * @see \Drupal\flowdrop_trigger\Attribute\FlowDropEventType
 * @see \Drupal\flowdrop_trigger\FlowDropEventTypeInterface
 *
 * @internal This class is not part of the public API and may change without notice.
 */
class EventTypePluginManager extends DefaultPluginManager {

  /**
   * Constructs an EventTypePluginManager.
   *
   * @param \Traversable $namespaces
   *   An object that implements \Traversable which contains the root paths
   *   keyed by the corresponding namespace to look for plugin implementations.
   * @param \Drupal\Core\Cache\CacheBackendInterface $cacheBackend
   *   The cache backend.
   * @param \Drupal\Core\Extension\ModuleHandlerInterface $moduleHandler
   *   The module handler.
   */
  public function __construct(
    \Traversable $namespaces,
    CacheBackendInterface $cacheBackend,
    ModuleHandlerInterface $moduleHandler,
  ) {
    parent::__construct(
      "Plugin/FlowDropEventType",
      $namespaces,
      $moduleHandler,
      FlowDropEventTypeInterface::class,
      FlowDropEventType::class
    );

    $this->alterInfo("flowdrop_event_type_info");
    $this->setCacheBackend($cacheBackend, "flowdrop_event_type_plugins");
  }

  /**
   * Get all available event type definitions.
   *
   * @return array
   *   Array of event type definitions keyed by plugin ID.
   */
  public function getEventTypes(): array {
    return $this->getDefinitions();
  }

  /**
   * Get event types grouped by category.
   *
   * @return array
   *   Array of event types grouped by category.
   */
  public function getEventTypesByCategory(): array {
    $definitions = $this->getDefinitions();
    $byCategory = [];

    foreach ($definitions as $id => $definition) {
      $category = $definition["category"] ?? "other";
      $categoryKey = $category instanceof EventCategory ? $category->value : $category;
      $byCategory[$categoryKey][$id] = $definition;
    }

    return $byCategory;
  }

  /**
   * Get an event type plugin instance.
   *
   * @param string $eventTypeId
   *   The event type plugin ID.
   *
   * @return \Drupal\flowdrop_trigger\FlowDropEventTypeInterface|null
   *   The event type plugin instance, or NULL if not found.
   */
  public function getEventType(string $eventTypeId): ?FlowDropEventTypeInterface {
    if (!$this->hasDefinition($eventTypeId)) {
      return NULL;
    }

    $instance = $this->createInstance($eventTypeId);

    if ($instance instanceof FlowDropEventTypeInterface) {
      return $instance;
    }

    return NULL;
  }

  /**
   * Check if an event type exists.
   *
   * @param string $eventTypeId
   *   The event type plugin ID.
   *
   * @return bool
   *   TRUE if the event type exists.
   */
  public function hasEventType(string $eventTypeId): bool {
    return $this->hasDefinition($eventTypeId);
  }

  /**
   * Get the default orchestrator for an event type.
   *
   * @param string $eventTypeId
   *   The event type plugin ID.
   *
   * @return string
   *   The default orchestrator (synchronous or asynchronous).
   */
  public function getDefaultOrchestrator(string $eventTypeId): string {
    $eventType = $this->getEventType($eventTypeId);

    if ($eventType !== NULL) {
      return $eventType->getDefaultOrchestrator();
    }

    // Default to asynchronous for unknown event types.
    return "flowdrop_runtime:asynchronous";
  }

  /**
   * Get event types that support a specific entity type.
   *
   * @param string $entityType
   *   The entity type ID.
   *
   * @return array
   *   Array of event type definitions that support the entity type.
   */
  public function getEventTypesForEntityType(string $entityType): array {
    $definitions = $this->getDefinitions();
    $matching = [];

    foreach ($definitions as $id => $definition) {
      $supportedTypes = $definition["supportedEntityTypes"] ?? [];

      // Empty array means all entity types supported.
      if (empty($supportedTypes) || in_array($entityType, $supportedTypes, TRUE)) {
        $matching[$id] = $definition;
      }
    }

    return $matching;
  }

}
