<?php

declare(strict_types=1);

namespace Drupal\Tests\flowdrop_memory\Unit\Plugin\FlowDropNodeProcessor;

use Drupal\flowdrop\DTO\ExecutionContextDTO;
use Drupal\flowdrop_memory\Plugin\FlowDropNodeProcessor\ScopeResolutionTrait;
use Drupal\Tests\UnitTestCase;

/**
 * Tests the ScopeResolutionTrait.
 *
 * @coversDefaultClass \Drupal\flowdrop_memory\Plugin\FlowDropNodeProcessor\ScopeResolutionTrait
 * @group flowdrop_memory
 */
class ScopeResolutionTraitTest extends UnitTestCase {

  /**
   * A minimal consumer that uses the trait and exposes its methods.
   *
   * @var object
   */
  private object $consumer;

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

    $this->consumer = new class() {
      use ScopeResolutionTrait;

      /**
       * Public wrapper for the protected resolveScopeId method.
       */
      public function resolve(string $scope): string {
        return $this->resolveScopeId($scope);
      }

    };
  }

  /**
   * Builds an ExecutionContextDTO for testing.
   *
   * @param string $workflowId
   *   The workflow ID.
   * @param string $pipelineId
   *   The pipeline ID.
   * @param string $executionId
   *   The execution ID.
   * @param array<string, mixed> $metadata
   *   Optional metadata.
   *
   * @return \Drupal\flowdrop\DTO\ExecutionContextDTO
   *   The context DTO.
   */
  private function makeContext(
    string $workflowId = 'wf-id',
    string $pipelineId = 'pl-id',
    string $executionId = 'ex-id',
    array $metadata = [],
  ): ExecutionContextDTO {
    return new ExecutionContextDTO([], $workflowId, $pipelineId, $executionId, 'node-id', $metadata);
  }

  /**
   * @covers ::resolveScopeId
   */
  public function testResolveScopeIdWithoutContextReturnsEmpty(): void {
    // No context set — all scopes should return empty string.
    $this->assertSame('', $this->consumer->resolve('workflow'));
    $this->assertSame('', $this->consumer->resolve('pipeline'));
    $this->assertSame('', $this->consumer->resolve('execution'));
    $this->assertSame('', $this->consumer->resolve('session'));
    $this->assertSame('', $this->consumer->resolve('global'));
  }

  /**
   * @covers ::resolveScopeId
   */
  public function testResolveScopeIdWorkflowReturnsWorkflowId(): void {
    $this->consumer->setExecutionContext($this->makeContext(workflowId: 'my-workflow'));
    $this->assertSame('my-workflow', $this->consumer->resolve('workflow'));
  }

  /**
   * @covers ::resolveScopeId
   */
  public function testResolveScopeIdPipelineReturnsPipelineId(): void {
    $this->consumer->setExecutionContext($this->makeContext(pipelineId: 'my-pipeline'));
    $this->assertSame('my-pipeline', $this->consumer->resolve('pipeline'));
  }

  /**
   * @covers ::resolveScopeId
   */
  public function testResolveScopeIdExecutionReturnsExecutionId(): void {
    $this->consumer->setExecutionContext($this->makeContext(executionId: 'my-execution'));
    $this->assertSame('my-execution', $this->consumer->resolve('execution'));
  }

  /**
   * @covers ::resolveScopeId
   */
  public function testResolveScopeIdSessionReturnsSessionIdFromMetadata(): void {
    $this->consumer->setExecutionContext(
      $this->makeContext(metadata: ['session_id' => 'sess-abc-123'])
    );
    $this->assertSame('sess-abc-123', $this->consumer->resolve('session'));
  }

  /**
   * @covers ::resolveScopeId
   */
  public function testResolveScopeIdSessionWithMissingMetadataReturnsEmpty(): void {
    $this->consumer->setExecutionContext($this->makeContext());
    $this->assertSame('', $this->consumer->resolve('session'));
  }

  /**
   * @covers ::resolveScopeId
   */
  public function testResolveScopeIdGlobalReturnsEmpty(): void {
    $this->consumer->setExecutionContext($this->makeContext());
    $this->assertSame('', $this->consumer->resolve('global'));
  }

  /**
   * @covers ::resolveScopeId
   */
  public function testResolveScopeIdUnknownScopeReturnsEmpty(): void {
    $this->consumer->setExecutionContext($this->makeContext());
    $this->assertSame('', $this->consumer->resolve('custom_unknown'));
    $this->assertSame('', $this->consumer->resolve(''));
  }

  /**
   * @covers ::setExecutionContext
   * @covers ::resolveScopeId
   */
  public function testSetExecutionContextReplacesExistingContext(): void {
    $ctx1 = $this->makeContext(workflowId: 'wf-first');
    $ctx2 = $this->makeContext(workflowId: 'wf-second');

    $this->consumer->setExecutionContext($ctx1);
    $this->assertSame('wf-first', $this->consumer->resolve('workflow'));

    $this->consumer->setExecutionContext($ctx2);
    $this->assertSame('wf-second', $this->consumer->resolve('workflow'));
  }

  /**
   * @covers ::resolveScopeId
   */
  public function testEachScopeResolvesDifferentContextValue(): void {
    $this->consumer->setExecutionContext($this->makeContext(
      workflowId: 'the-workflow',
      pipelineId: 'the-pipeline',
      executionId: 'the-execution',
      metadata: ['session_id' => 'the-session'],
    ));

    $this->assertSame('the-workflow', $this->consumer->resolve('workflow'));
    $this->assertSame('the-pipeline', $this->consumer->resolve('pipeline'));
    $this->assertSame('the-execution', $this->consumer->resolve('execution'));
    $this->assertSame('the-session', $this->consumer->resolve('session'));
    $this->assertSame('', $this->consumer->resolve('global'));
  }

}
