<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_interrupt\Unit\Service;

use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\flowdrop_interrupt\Plugin\InterruptHandler\HitlInterruptHandlerInterface;
use Drupal\flowdrop_interrupt\Plugin\InterruptHandler\InterruptHandlerInterface;
use Drupal\flowdrop_interrupt\Service\InterruptHandlerPluginManager;
use Drupal\Tests\UnitTestCase;
use Prophecy\PhpUnit\ProphecyTrait;

/**
 * Tests the InterruptHandlerPluginManager.
 *
 * Behavioural coverage of the legacy type-ID routing (confirmation, choice,
 * text, form → schema_form) and the HITL-ID filter that backs the inbox
 * query.
 *
 * Uses a stub subclass that overrides {@see DefaultPluginManager} internals
 * — testing a Drupal plugin manager at the unit level requires a small
 * scaffolding subclass since {@see DefaultPluginManager::getDefinitions()}
 * and {@see DefaultPluginManager::createInstance()} would otherwise hit the
 * full discovery pipeline.
 *
 * @coversDefaultClass \Drupal\flowdrop_interrupt\Service\InterruptHandlerPluginManager
 * @group flowdrop_interrupt
 */
class InterruptHandlerPluginManagerTest extends UnitTestCase {

  use ProphecyTrait;

  /**
   * Builds a manager primed with a given set of plugin fixtures.
   *
   * @param array<string, \Drupal\flowdrop_interrupt\Plugin\InterruptHandler\InterruptHandlerInterface> $instances
   *   Plugin instances keyed by plugin ID.
   */
  private function buildManager(array $instances): InterruptHandlerPluginManager {
    $namespaces = new \ArrayObject([]);
    $cache = $this->prophesize(CacheBackendInterface::class)->reveal();
    $module = $this->prophesize(ModuleHandlerInterface::class)->reveal();
    return new class($namespaces, $cache, $module, $instances) extends InterruptHandlerPluginManager {

      /** @var array<string, \Drupal\flowdrop_interrupt\Plugin\InterruptHandler\InterruptHandlerInterface> */
      private array $fixtures;

      public function __construct($namespaces, $cache, $module, array $fixtures) {
        parent::__construct($namespaces, $cache, $module);
        $this->fixtures = $fixtures;
      }

      /**
       * {@inheritdoc}
       */
      public function hasDefinition($plugin_id): bool {
        return isset($this->fixtures[$plugin_id]);
      }

      /**
       * {@inheritdoc}
       */
      public function getDefinitions(): array {
        $defs = [];
        foreach ($this->fixtures as $id => $_) {
          $defs[$id] = ['id' => $id];
        }
        return $defs;
      }

      /**
       * {@inheritdoc}
       */
      public function createInstance($plugin_id, array $configuration = []) {
        if (!isset($this->fixtures[$plugin_id])) {
          throw new PluginNotFoundException($plugin_id);
        }
        return $this->fixtures[$plugin_id];
      }

    };
  }

  /**
   * Legacy "confirmation" ID resolves to the schema_form handler.
   */
  public function testLegacyConfirmationRoutesToSchemaForm(): void {
    $schemaForm = $this->prophesize(HitlInterruptHandlerInterface::class)->reveal();
    $manager = $this->buildManager(['schema_form' => $schemaForm]);
    $this->assertSame($schemaForm, $manager->loadHandler('confirmation'));
  }

  /**
   * Legacy "choice", "text", "form" all route to schema_form.
   */
  public function testAllLegacyHitlIdsRouteToSchemaForm(): void {
    $schemaForm = $this->prophesize(HitlInterruptHandlerInterface::class)->reveal();
    $manager = $this->buildManager(['schema_form' => $schemaForm]);
    foreach (['choice', 'text', 'form'] as $legacy) {
      $this->assertSame($schemaForm, $manager->loadHandler($legacy));
    }
  }

  /**
   * Unknown type IDs return NULL (no fallback, no exception).
   */
  public function testUnknownTypeReturnsNull(): void {
    $manager = $this->buildManager([]);
    $this->assertNull($manager->loadHandler('does_not_exist'));
  }

  /**
   * Native plugin IDs are loaded directly without legacy routing.
   */
  public function testNativeIdLoadsDirectly(): void {
    $custom = $this->prophesize(InterruptHandlerInterface::class)->reveal();
    $manager = $this->buildManager(['my_module.custom' => $custom]);
    $this->assertSame($custom, $manager->loadHandler('my_module.custom'));
  }

  /**
   * Repeated loadHandler() calls for the same ID return the cached instance.
   */
  public function testHandlerInstancesAreCachedPerRequest(): void {
    $handler = $this->prophesize(InterruptHandlerInterface::class)->reveal();
    $manager = $this->buildManager(['x' => $handler]);
    $first = $manager->loadHandler('x');
    $second = $manager->loadHandler('x');
    $this->assertSame($first, $second);
  }

  /**
   * Tests that getHitlTypeIds() returns native HITL plugins and legacy IDs.
   *
   * The legacy IDs are included when they route to a native HITL handler.
   */
  public function testGetHitlTypeIdsIncludesLegacyRoutes(): void {
    $schemaForm = $this->prophesize(HitlInterruptHandlerInterface::class)->reveal();
    $custom = $this->prophesize(HitlInterruptHandlerInterface::class)->reveal();
    $manager = $this->buildManager([
      'schema_form' => $schemaForm,
      'my_module.custom' => $custom,
    ]);
    $ids = $manager->getHitlTypeIds();
    $this->assertContains('schema_form', $ids);
    $this->assertContains('my_module.custom', $ids);
    // Legacy IDs that route to a HITL handler are also exposed for the
    // inbox query so pre-Phase-1 rows still show up.
    $this->assertContains('confirmation', $ids);
    $this->assertContains('choice', $ids);
    $this->assertContains('text', $ids);
    $this->assertContains('form', $ids);
  }

  /**
   * GetHitlTypeIds() excludes non-HITL handlers (operational types).
   */
  public function testGetHitlTypeIdsExcludesOperationalHandlers(): void {
    $operational = $this->prophesize(InterruptHandlerInterface::class)->reveal();
    $manager = $this->buildManager(['workflow_execution' => $operational]);
    $ids = $manager->getHitlTypeIds();
    $this->assertNotContains('workflow_execution', $ids);
  }

}
