<?php

declare(strict_types=1);

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

use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\flowdrop_runtime\DTO\Compiler\ExecutionNode;
use Drupal\flowdrop_runtime\Service\Compiler\WorkflowCompiler;
use Drupal\flowdrop_runtime\Service\Orchestrator\SynchronousOrchestrator;
use Drupal\flowdrop_runtime\Service\RealTime\RealTimeManager;
use Drupal\flowdrop_runtime\Service\Runtime\ExecutionContext;
use Drupal\flowdrop_runtime\Service\Runtime\NodeRuntimeService;
use PHPUnit\Framework\TestCase;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;

/**
 * Tests for SynchronousOrchestrator agent node handling.
 *
 * Agent and Iterator detection is done via ExecutionNode::isAgent() and
 * ExecutionNode::isIterator() methods. This test validates the orchestrator
 * configuration and ExecutionNode special type detection.
 *
 * @coversDefaultClass \Drupal\flowdrop_runtime\Service\Orchestrator\SynchronousOrchestrator
 * @group flowdrop_runtime
 */
class SynchronousOrchestratorAgentTest extends TestCase {

  /**
   * The orchestrator under test.
   *
   * @var \Drupal\flowdrop_runtime\Service\Orchestrator\SynchronousOrchestrator
   */
  protected SynchronousOrchestrator $orchestrator;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();

    $nodeRuntime = $this->createMock(NodeRuntimeService::class);
    $executionContext = $this->createMock(ExecutionContext::class);
    $realTimeManager = $this->createMock(RealTimeManager::class);
    $workflowCompiler = $this->createMock(WorkflowCompiler::class);
    $eventDispatcher = $this->createMock(EventDispatcherInterface::class);

    $logger = $this->createMock(LoggerChannelInterface::class);
    $loggerFactory = $this->createMock(LoggerChannelFactoryInterface::class);
    $loggerFactory->method("get")->willReturn($logger);

    $this->orchestrator = new SynchronousOrchestrator(
      $nodeRuntime,
      $executionContext,
      $realTimeManager,
      $workflowCompiler,
      $loggerFactory,
      $eventDispatcher,
    );
  }

  /**
   * Test that ExecutionNode correctly detects agent nodes via special type.
   *
   * Agent detection is done on ExecutionNode with SPECIAL_TYPE_AGENT.
   *
   * @covers \Drupal\flowdrop_runtime\DTO\Compiler\ExecutionNode::isAgent
   */
  public function testExecutionNodeAgentDetection(): void {
    // Create an agent execution node.
    $agentNode = new ExecutionNode(
      id: "agent_1",
      typeId: "agent",
      config: [],
      triggerDependencies: [],
      dataDependencies: [],
      isRoot: TRUE,
      isLeaf: TRUE,
      specialType: ExecutionNode::SPECIAL_TYPE_AGENT,
      metadata: [],
    );

    $this->assertTrue($agentNode->isAgent());
    $this->assertFalse($agentNode->isIterator());
    $this->assertTrue($agentNode->requiresSpecialHandling());
    $this->assertEquals(ExecutionNode::SPECIAL_TYPE_AGENT, $agentNode->getSpecialType());
  }

  /**
   * Test that ExecutionNode correctly detects iterator nodes via special type.
   *
   * Iterator detection is done on ExecutionNode with SPECIAL_TYPE_ITERATOR.
   *
   * @covers \Drupal\flowdrop_runtime\DTO\Compiler\ExecutionNode::isIterator
   */
  public function testExecutionNodeIteratorDetection(): void {
    // Create an iterator execution node.
    $iteratorNode = new ExecutionNode(
      id: "iterator_1",
      typeId: "iterator",
      config: [],
      triggerDependencies: [],
      dataDependencies: [],
      isRoot: FALSE,
      isLeaf: FALSE,
      specialType: ExecutionNode::SPECIAL_TYPE_ITERATOR,
      metadata: [],
    );

    $this->assertTrue($iteratorNode->isIterator());
    $this->assertFalse($iteratorNode->isAgent());
    $this->assertTrue($iteratorNode->requiresSpecialHandling());
    $this->assertEquals(ExecutionNode::SPECIAL_TYPE_ITERATOR, $iteratorNode->getSpecialType());
  }

  /**
   * Test that regular nodes don't have special types.
   *
   * @covers \Drupal\flowdrop_runtime\DTO\Compiler\ExecutionNode::requiresSpecialHandling
   */
  public function testRegularNodeHasNoSpecialType(): void {
    // Create a regular execution node.
    $regularNode = new ExecutionNode(
      id: "http_request_1",
      typeId: "http_request",
      config: [],
      triggerDependencies: [],
      dataDependencies: [],
      isRoot: TRUE,
      isLeaf: TRUE,
      specialType: NULL,
      metadata: [],
    );

    $this->assertFalse($regularNode->isAgent());
    $this->assertFalse($regularNode->isIterator());
    $this->assertFalse($regularNode->requiresSpecialHandling());
    $this->assertNull($regularNode->getSpecialType());
  }

  /**
   * Test orchestrator type.
   *
   * @covers ::getType
   */
  public function testGetType(): void {
    $this->assertSame("synchronous", $this->orchestrator->getType());
  }

}
