<?php

declare(strict_types=1);

namespace Drupal\Tests\native_observability_spans\Unit\Service;

use Drupal\Tests\UnitTestCase;
use Drupal\native_observability_spans\Service\ActiveSpanContextStack;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RequestStack;

/**
 * Covers active span stack operations bound to the current request.
 */
final class ActiveSpanContextStackTest extends UnitTestCase {

  /**
   * Verifies push, current-span lookup and pop operations.
   */
  public function testPushGetCurrentAndPopWorkInStackOrder(): void {
    $requestStack = new RequestStack();
    $requestStack->push(new Request());

    $stack = new ActiveSpanContextStack($requestStack);
    $stack->push('span-root');
    $stack->push('span-child');

    $this->assertSame('span-child', $stack->getCurrentSpanId());
    $this->assertSame('span-child', $stack->pop());
    $this->assertSame('span-root', $stack->getCurrentSpanId());
    $this->assertSame('span-root', $stack->pop());
    $this->assertNull($stack->getCurrentSpanId());
  }

  /**
   * Verifies that calls are safely ignored when no request is available.
   */
  public function testOperationsAreSafeWithoutCurrentRequest(): void {
    $stack = new ActiveSpanContextStack(new RequestStack());

    $stack->push('ignored-span');

    $this->assertNull($stack->getCurrentSpanId());
    $this->assertNull($stack->pop());
  }

  /**
   * Verifies that an expected span mismatch still removes the requested span.
   */
  public function testPopWithExpectedSpanIdRemovesMismatchedAncestor(): void {
    $requestStack = new RequestStack();
    $requestStack->push(new Request());

    $stack = new ActiveSpanContextStack($requestStack);
    $stack->push('span-a');
    $stack->push('span-b');
    $stack->push('span-c');

    $popped = $stack->pop('span-b');

    $this->assertSame('span-c', $popped);
    $this->assertSame('span-a', $stack->getCurrentSpanId());
  }

}
