<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_runtime\Unit\Service\Compiler;

use Drupal\Core\Entity\EntityStorageInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Tests\UnitTestCase;
use Drupal\Tests\flowdrop_runtime\Unit\Service\Compiler\Fixtures\PlainConsumerStub;
use Drupal\Tests\flowdrop_runtime\Unit\Service\Compiler\Fixtures\ToolPassthroughStub;
use Drupal\Tests\flowdrop_runtime\Unit\Service\Compiler\Fixtures\ToolsAwareConsumerStub;
use Drupal\flowdrop\Service\FlowDropNodeProcessorPluginManager;
use Drupal\flowdrop_node_type\FlowDropNodeTypeInterface;
use Drupal\flowdrop_runtime\DTO\Compiler\DependencyEdge;
use Drupal\flowdrop_runtime\DTO\Compiler\DependencyGraph;
use Drupal\flowdrop_runtime\DTO\Compiler\DependencyNode;
use Drupal\flowdrop_runtime\Service\Compiler\ToolConsumerValidator;
use Prophecy\PhpUnit\ProphecyTrait;
use Psr\Log\LoggerInterface;

/**
 * Tests the ToolConsumerValidator.
 *
 * @coversDefaultClass \Drupal\flowdrop_runtime\Service\Compiler\ToolConsumerValidator
 * @group flowdrop_runtime
 */
class ToolConsumerValidatorTest extends UnitTestCase {

  use ProphecyTrait;

  /**
   * Builds a graph: tool --tool_availability--> consumer.
   */
  private function graph(): DependencyGraph {
    $nodes = [
      "consumer" => new DependencyNode("consumer", "consumer_type", "Agent"),
      "tool" => new DependencyNode("tool", "http_request", "Web Search"),
    ];
    $edge = new DependencyEdge(
      "e1",
      "tool",
      "consumer",
      "",
      "",
      DependencyEdge::TYPE_TOOL_AVAILABILITY,
      "",
      "",
    );
    return new DependencyGraph($nodes, ["e1" => $edge], [], []);
  }

  /**
   * Builds a validator whose consumer resolves to the given processor class.
   */
  private function validatorForClass(string $class): ToolConsumerValidator {
    $nodeType = $this->prophesize(FlowDropNodeTypeInterface::class);
    $nodeType->getExecutorPlugin()->willReturn("consumer_plugin");
    $storage = $this->prophesize(EntityStorageInterface::class);
    $storage->load("consumer_type")->willReturn($nodeType->reveal());
    $etm = $this->prophesize(EntityTypeManagerInterface::class);
    $etm->getStorage("flowdrop_node_type")->willReturn($storage->reveal());

    $pm = $this->prophesize(FlowDropNodeProcessorPluginManager::class);
    $pm->getDefinition("consumer_plugin", FALSE)->willReturn(["class" => $class]);

    return new ToolConsumerValidator(
      $pm->reveal(),
      $etm->reveal(),
      $this->prophesize(LoggerInterface::class)->reveal(),
    );
  }

  /**
   * A ToolsAware consumer produces no errors.
   *
   * @covers ::validate
   */
  public function testToolsAwareConsumerPasses(): void {
    $validator = $this->validatorForClass(ToolsAwareConsumerStub::class);
    $this->assertSame([], $validator->validate($this->graph()));
  }

  /**
   * A passthrough target (ToolBox) legitimately receives tool edges.
   *
   * @covers ::validate
   */
  public function testPassthroughConsumerPasses(): void {
    $validator = $this->validatorForClass(ToolPassthroughStub::class);
    $this->assertSame([], $validator->validate($this->graph()));
  }

  /**
   * A non-ToolsAware consumer with tools wired is flagged.
   *
   * @covers ::validate
   */
  public function testPlainConsumerIsFlagged(): void {
    $validator = $this->validatorForClass(PlainConsumerStub::class);
    $errors = $validator->validate($this->graph());
    $this->assertCount(1, $errors);
    $this->assertStringContainsString("consumer", $errors[0]);
    $this->assertStringContainsString("ToolsAwareInterface", $errors[0]);
  }

  /**
   * An unresolvable consumer is skipped (no false positive).
   *
   * @covers ::validate
   */
  public function testUnresolvableConsumerIsSkipped(): void {
    $storage = $this->prophesize(EntityStorageInterface::class);
    $storage->load("consumer_type")->willReturn(NULL);
    $etm = $this->prophesize(EntityTypeManagerInterface::class);
    $etm->getStorage("flowdrop_node_type")->willReturn($storage->reveal());
    $pm = $this->prophesize(FlowDropNodeProcessorPluginManager::class);

    $validator = new ToolConsumerValidator(
      $pm->reveal(),
      $etm->reveal(),
      $this->prophesize(LoggerInterface::class)->reveal(),
    );

    $this->assertSame([], $validator->validate($this->graph()));
  }

}
