<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_runtime\Integration;

use Drupal\KernelTests\KernelTestBase;
use Drupal\flowdrop_runtime\Plugin\Orchestrator\AsynchronousOrchestratorPlugin;
use Drupal\flowdrop_runtime\Service\Orchestrator\AsynchronousOrchestrator;
use Drupal\flowdrop_workflow\FlowDropWorkflowInterface;
use Drupal\Tests\flowdrop\Kernel\FlowDropKernelTestTrait;

/**
 * Integration test for asynchronous execution capabilities.
 *
 * @group flowdrop_runtime
 */
class AsynchronousExecutionTest extends KernelTestBase {

  use FlowDropKernelTestTrait;

  /**
   * {@inheritdoc}
   */
  protected static $modules = [
    'system',
    'user',
    'field',
    'options',
    'text',
    'flowdrop',
    'flowdrop_workflow',
    'flowdrop_orchestration',
    'flowdrop_runtime',
    'flowdrop_pipeline',
    'flowdrop_job',
    'flowdrop_node_type',
  ];

  /**
   * The asynchronous orchestrator service.
   *
   * @var \Drupal\flowdrop_runtime\Service\Orchestrator\AsynchronousOrchestrator
   */
  protected $asynchronousOrchestrator;

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

    $this->installEntitySchema('user');
    $this->installEntitySchema('flowdrop_workflow');
    $this->installEntitySchema('flowdrop_workflow_snapshot');
    $this->installEntitySchema('flowdrop_pipeline');
    $this->installEntitySchema('flowdrop_job');
    $this->installEntitySchema('flowdrop_node_type');

    $this->asynchronousOrchestrator = $this->container->get('flowdrop_runtime.asynchronous_orchestrator');
  }

  /**
   * Test that the asynchronous orchestrator service is available.
   */
  public function testAsynchronousOrchestratorService(): void {
    $this->assertInstanceOf(AsynchronousOrchestrator::class, $this->asynchronousOrchestrator);
    $this->assertEquals('flowdrop_runtime:asynchronous', $this->asynchronousOrchestrator->getType());
  }

  /**
   * Test queue configuration is properly loaded.
   */
  public function testQueueConfiguration(): void {
    $queue_factory = $this->container->get('queue');

    // Test pipeline execution queue.
    $pipeline_queue = $queue_factory->get('flowdrop_runtime_pipeline_execution');
    $this->assertNotNull($pipeline_queue);

    // Test job execution queue.
    $job_queue = $queue_factory->get('flowdrop_runtime_job_execution');
    $this->assertNotNull($job_queue);
  }

  /**
   * Test orchestrator type registration.
   */
  public function testOrchestratorRegistration(): void {
    $orchestrator_manager = $this->container->get('flowdrop_orchestration.plugin_manager');

    // Check if asynchronous orchestrator is registered.
    $definitions = $orchestrator_manager->getDefinitions();
    $this->assertArrayHasKey('flowdrop_runtime:asynchronous', $definitions);

    // Test creating the orchestrator instance.
    $instance = $orchestrator_manager->createInstance('flowdrop_runtime:asynchronous');
    $this->assertInstanceOf(AsynchronousOrchestratorPlugin::class, $instance);
  }

  /**
   * Test pipeline creation from workflow.
   */
  public function testPipelineCreation(): void {
    // Create a simple workflow for testing.
    $workflow = $this->createMockWorkflow();

    $workflow_array = [
      'id' => $workflow->id(),
      'label' => $workflow->label(),
      'nodes' => $workflow->getNodes(),
      'edges' => $workflow->getEdges(),
    ];
    $initial_data = ['test_input' => 'test_value'];
    $options = ['max_concurrent_jobs' => 3];

    // Use reflection to access the protected method for testing.
    $reflection = new \ReflectionMethod($this->asynchronousOrchestrator, 'createPipelineFromWorkflow');
    $pipeline = $reflection->invoke($this->asynchronousOrchestrator, $workflow_array, $initial_data, $options);

    $this->assertNotNull($pipeline);
    $this->assertEquals($workflow->id(), $pipeline->getWorkflowId());
    $this->assertEquals('pending', $pipeline->getStatus());
    $this->assertEquals(3, $pipeline->getMaxConcurrentJobs());
  }

  /**
   * Create a mock workflow for testing.
   *
   * @return \Drupal\flowdrop_workflow\FlowDropWorkflowInterface
   *   A mock workflow.
   */
  protected function createMockWorkflow() {
    $workflow_storage = $this->container->get('entity_type.manager')->getStorage('flowdrop_workflow');

    $workflow = $workflow_storage->create([
      'id' => 'test_workflow_' . uniqid(),
      'label' => 'Test Workflow',
      'nodes' => [
        [
          'id' => 'node1',
          'type' => 'universalNode',
          'position' => ['x' => 0, 'y' => 0],
          'data' => [
            'label' => 'Input Node',
            'metadata' => [
              'id' => 'input',
              'name' => 'Input',
              'type' => 'default',
              'description' => 'Input node',
              'category' => 'input',
              'inputs' => [],
              'outputs' => [],
            ],
          ],
        ],
        [
          'id' => 'node2',
          'type' => 'universalNode',
          'position' => ['x' => 100, 'y' => 0],
          'data' => [
            'label' => 'Output Node',
            'metadata' => [
              'id' => 'output',
              'name' => 'Output',
              'type' => 'default',
              'description' => 'Output node',
              'category' => 'output',
              'inputs' => [],
              'outputs' => [],
            ],
          ],
        ],
      ],
      'edges' => [
        [
          'id' => 'edge1',
          'source' => 'node1',
          'target' => 'node2',
        ],
      ],
    ]);

    $workflow->save();

    assert($workflow instanceof FlowDropWorkflowInterface);
    return $workflow;
  }

}
