<?php

declare(strict_types=1);

namespace Drupal\Tests\native_observability_otel\Unit\Hook;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Core\Routing\UrlGeneratorInterface;
use Drupal\Tests\UnitTestCase;
use PHPUnit\Framework\Attributes\CoversFunction;
use Symfony\Component\DependencyInjection\ContainerBuilder;

/**
 * Unit tests for native_observability_otel_requirements().
 *
 * The hook function is procedural and uses \Drupal::config() and
 * Url::fromRoute()->toString(). setUp() wires a minimal mock container so
 * both calls resolve without a real Drupal bootstrap. The REQUIREMENT_*
 * constants are defined in install.inc which is included once in
 * setUpBeforeClass().
 */
#[CoversFunction('native_observability_otel_requirements')]
final class NativeObservabilityOtelRequirementsTest extends UnitTestCase {

  /**
   * Includes the module file and defines REQUIREMENT_* constants exactly once.
   *
   * This runs before any test in the class and before setUp().
   */
  public static function setUpBeforeClass(): void {
    parent::setUpBeforeClass();

    // REQUIREMENT_OK etc. are global constants defined in install.inc.
    // The unit-test bootstrap does not include that file, so define them here
    // if they are not yet available.
    if (!defined('REQUIREMENT_OK')) {
      define('REQUIREMENT_OK', 0);
    }
    if (!defined('REQUIREMENT_INFO')) {
      define('REQUIREMENT_INFO', -1);
    }
    if (!defined('REQUIREMENT_WARNING')) {
      define('REQUIREMENT_WARNING', 1);
    }
    if (!defined('REQUIREMENT_ERROR')) {
      define('REQUIREMENT_ERROR', 2);
    }

    // Load the module file so native_observability_otel_requirements() exists.
    require_once dirname(__DIR__, 4) . '/native_observability_otel.install';
  }

  /**
   * Wires a mock container for \Drupal::config() and Url::toString().
   *
   * @param string $bearerToken
   *   The bearer token value to return from config.
   */
  private function setupContainer(string $bearerToken): void {
    $config = $this->createMock(ImmutableConfig::class);
    $config->method('get')
      ->with('prometheus_bearer_token')
      ->willReturn($bearerToken);

    $factory = $this->createMock(ConfigFactoryInterface::class);
    $factory->method('get')
      ->with('native_observability_otel.settings')
      ->willReturn($config);

    $urlGenerator = $this->createMock(UrlGeneratorInterface::class);
    $urlGenerator->method('generateFromRoute')
      ->willReturn('/admin/config/development/native-observability/otel');

    $container = new ContainerBuilder();
    $container->set('config.factory', $factory);
    $container->set('url_generator', $urlGenerator);
    \Drupal::setContainer($container);
  }

  /**
   * Verifies the hook returns an empty array for the 'install' phase.
   */
  public function testReturnsEmptyArrayForInstallPhase(): void {
    $result = native_observability_otel_requirements('install');

    $this->assertSame([], $result);
  }

  /**
   * Verifies the hook returns an empty array for the 'update' phase.
   */
  public function testReturnsEmptyArrayForUpdatePhase(): void {
    $result = native_observability_otel_requirements('update');

    $this->assertSame([], $result);
  }

  /**
   * Verifies REQUIREMENT_OK is reported when the bearer token is set.
   */
  public function testReturnsOkWhenTokenIsSet(): void {
    $this->setupContainer('abc123');

    $result = native_observability_otel_requirements('runtime');

    $this->assertArrayHasKey(
      'native_observability_otel_prometheus_token',
      $result,
    );
    $this->assertSame(
      REQUIREMENT_OK,
      $result['native_observability_otel_prometheus_token']['severity'],
    );
  }

  /**
   * Verifies REQUIREMENT_WARNING is reported when the bearer token is empty.
   */
  public function testReturnsWarningWhenTokenIsEmpty(): void {
    $this->setupContainer('');

    $result = native_observability_otel_requirements('runtime');

    $this->assertArrayHasKey(
      'native_observability_otel_prometheus_token',
      $result,
    );
    $this->assertSame(
      REQUIREMENT_WARNING,
      $result['native_observability_otel_prometheus_token']['severity'],
    );
  }

  /**
   * Verifies REQUIREMENT_WARNING is reported when the token is NULL.
   *
   * NULL is cast to '' by the hook: (string) (NULL ?? '') === ''.
   */
  public function testReturnsWarningWhenTokenIsNull(): void {
    $config = $this->createMock(ImmutableConfig::class);
    $config->method('get')
      ->with('prometheus_bearer_token')
      ->willReturn(NULL);

    $factory = $this->createMock(ConfigFactoryInterface::class);
    $factory->method('get')
      ->with('native_observability_otel.settings')
      ->willReturn($config);

    $urlGenerator = $this->createMock(UrlGeneratorInterface::class);
    $urlGenerator->method('generateFromRoute')
      ->willReturn('/admin/config/development/native-observability/otel');

    $container = new ContainerBuilder();
    $container->set('config.factory', $factory);
    $container->set('url_generator', $urlGenerator);
    \Drupal::setContainer($container);

    $result = native_observability_otel_requirements('runtime');

    $this->assertSame(
      REQUIREMENT_WARNING,
      $result['native_observability_otel_prometheus_token']['severity'],
    );
  }

}
