<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_workflow\Kernel;

use Drupal\KernelTests\Core\Entity\EntityKernelTestBase;
use Drupal\flowdrop_node_type\Entity\FlowDropNodeType;
use Drupal\flowdrop_workflow\DTO\NodeCapability;
use Drupal\flowdrop_workflow\Service\WorkflowCapabilityManifestBuilder;

/**
 * Tests the capability manifest against the real calculator plugin.
 *
 * @group flowdrop_workflow
 * @covers \Drupal\flowdrop_workflow\Service\WorkflowCapabilityManifestBuilder
 * @covers \Drupal\flowdrop_workflow\DTO\WorkflowCapabilityManifest
 * @covers \Drupal\flowdrop_workflow\DTO\NodeCapability
 */
class WorkflowCapabilityManifestTest extends EntityKernelTestBase {

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

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();
    $this->installConfig(['system']);
    $this->installEntitySchema('flowdrop_node_category');
    $this->installEntitySchema('flowdrop_node_type');
    $this->config('system.logging')->set('error_level', 'hide')->save();
  }

  /**
   * Returns the manifest builder service.
   */
  private function builder(): WorkflowCapabilityManifestBuilder {
    return $this->container->get('flowdrop_workflow.capability_manifest_builder');
  }

  /**
   * Builds a node referencing a node type and plugin.
   *
   * @param string $id
   *   The node instance id.
   * @param string $nodeTypeId
   *   The node type id (metadata.id).
   * @param string $pluginId
   *   The executor plugin id.
   *
   * @return array<string, mixed>
   *   The workflow node.
   */
  private function node(string $id, string $nodeTypeId, string $pluginId): array {
    return [
      'id' => $id,
      'data' => ['metadata' => ['node_type_id' => $nodeTypeId, 'executor_plugin' => $pluginId]],
    ];
  }

  /**
   * An installed plugin with no node type yet is generatable.
   */
  public function testInstalledPluginWithoutNodeTypeIsGeneratable(): void {
    $manifest = $this->builder()->build([
      $this->node('n1', 'calculator', 'flowdrop_node_processor:calculator'),
    ]);

    $this->assertTrue($manifest->canApply());
    $this->assertCount(1, $manifest->generatable());
    $this->assertSame(NodeCapability::GENERATABLE, $manifest->all()[0]->getStatus());
  }

  /**
   * An installed plugin with an existing node type is available.
   */
  public function testExistingNodeTypeIsAvailable(): void {
    FlowDropNodeType::create([
      'id' => 'calculator',
      'label' => 'Calculator',
      'executor_plugin' => 'flowdrop_node_processor:calculator',
    ])->save();

    $manifest = $this->builder()->build([
      $this->node('n1', 'calculator', 'flowdrop_node_processor:calculator'),
    ]);

    $this->assertTrue($manifest->canApply());
    $this->assertCount(1, $manifest->available());
    $this->assertSame([], $manifest->missingModules());
  }

  /**
   * A node referencing an uninstalled plugin is missing and names the module.
   */
  public function testUninstalledPluginIsMissing(): void {
    $manifest = $this->builder()->build([
      $this->node('n1', 'ghost_type', 'ghost_module:ghost'),
    ]);

    $this->assertFalse($manifest->canApply());
    $this->assertCount(1, $manifest->missing());
    $this->assertSame(['ghost_module'], $manifest->missingModules());
  }

  /**
   * Multiple nodes of one node type collapse to a single capability.
   */
  public function testDuplicateNodeTypesAreDeduplicated(): void {
    $manifest = $this->builder()->build([
      $this->node('n1', 'calculator', 'flowdrop_node_processor:calculator'),
      $this->node('n2', 'calculator', 'flowdrop_node_processor:calculator'),
    ]);

    $this->assertCount(1, $manifest->all());
  }

  /**
   * A mixed workflow buckets each capability and blocks on the missing one.
   */
  public function testMixedWorkflowBucketsAndBlocks(): void {
    $manifest = $this->builder()->build([
      $this->node('n1', 'calculator', 'flowdrop_node_processor:calculator'),
      $this->node('n2', 'ghost_type', 'ghost_module:ghost'),
    ]);

    $this->assertCount(1, $manifest->generatable());
    $this->assertCount(1, $manifest->missing());
    $this->assertFalse($manifest->canApply());
  }

}
