<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_node_type\Kernel;

use Drupal\KernelTests\KernelTestBase;
use Drupal\flowdrop\Constants\ReservedName;
use Drupal\flowdrop_node_type\Controller\Api\NodesController;

/**
 * Pins the reserved `trigger` input port injected on node types.
 *
 * Every ordinary node type gets a `trigger` input port injected into its API
 * metadata so it can be wired downstream of another node. Start nodes
 * (StartNodeProcessorInterface, e.g. any trigger) are workflow entry points —
 * nothing upstream fires them — so the port is meaningless there and must not
 * be injected.
 *
 * @group flowdrop_node_type
 * @coversDefaultClass \Drupal\flowdrop_node_type\Service\NodeMetadataResolver
 */
class TriggerInputInjectionTest extends KernelTestBase {

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'system',
    'user',
    'flowdrop',
    'flowdrop_node_category',
    'flowdrop_node_type',
    'flowdrop_node_processor',
    'flowdrop_node_type_test',
  ];

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();
    $this->installEntitySchema('flowdrop_node_category');
    $this->installEntitySchema('flowdrop_node_type');
  }

  /**
   * Creates a node type and returns its API metadata input ports.
   *
   * @param string $id
   *   The node type ID.
   * @param string $executorPlugin
   *   The executor plugin ID.
   *
   * @return array<int, array<string, mixed>>
   *   The input port arrays from the metadata response.
   */
  private function inputPortsFor(string $id, string $executorPlugin): array {
    $this->container->get('entity_type.manager')
      ->getStorage('flowdrop_node_type')
      ->create([
        'id' => $id,
        'label' => ucfirst($id),
        'enabled' => TRUE,
        'executor_plugin' => $executorPlugin,
      ])->save();

    $controller = NodesController::create($this->container);
    $response = $controller->getNodeMetadata($id);
    $this->assertSame(200, $response->getStatusCode(), (string) $response->getContent());

    $payload = json_decode((string) $response->getContent(), TRUE);
    return $payload['data']['metadata']['inputs'] ?? [];
  }

  /**
   * Filters ports down to those with the reserved trigger id.
   *
   * @param array<int, array<string, mixed>> $ports
   *   The input port arrays.
   *
   * @return array<int, array<string, mixed>>
   *   The trigger ports.
   */
  private function triggerPorts(array $ports): array {
    return array_values(array_filter(
      $ports,
      static fn (array $port): bool => ($port['id'] ?? '') === ReservedName::PORT_TRIGGER,
    ));
  }

  /**
   * An ordinary (non-start) node type gets the reserved trigger input port.
   *
   * @covers ::buildForNodeType
   */
  public function testTriggerInputInjectedForOrdinaryNode(): void {
    $ports = $this->inputPortsFor('proc', 'flowdrop_node_processor:nop');
    $triggerPorts = $this->triggerPorts($ports);

    $this->assertCount(1, $triggerPorts, json_encode($ports));
    $this->assertSame('input', $triggerPorts[0]['type']);
  }

  /**
   * A start node (StartNodeProcessorInterface) does not get the trigger input.
   *
   * @covers ::buildForNodeType
   */
  public function testTriggerInputNotInjectedForStartNode(): void {
    $ports = $this->inputPortsFor('startType', 'flowdrop_node_type_test:start_probe');

    $this->assertSame([], $this->triggerPorts($ports), json_encode($ports));
  }

}
