<?php

declare(strict_types=1);

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

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\ImmutableConfig;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\flowdrop_runtime\Service\Logging\FilteredLoggerChannel;
use Drupal\Tests\UnitTestCase;
use Psr\Log\LogLevel;

/**
 * Unit tests for FilteredLoggerChannel.
 *
 * Confirms that the central FlowDrop logging settings (verbosity and the
 * "log to watchdog" toggle) gate every write to the flowdrop_runtime channel.
 *
 * @coversDefaultClass \Drupal\flowdrop_runtime\Service\Logging\FilteredLoggerChannel
 * @group flowdrop_runtime
 */
class FilteredLoggerChannelTest extends UnitTestCase {

  /**
   * Builds a channel configured with the given settings.
   *
   * @param string $verbosity
   *   The verbosity setting value.
   * @param bool $logToWatchdog
   *   The "log to watchdog" setting value.
   *
   * @return array{0: \Drupal\flowdrop_runtime\Service\Logging\FilteredLoggerChannel, 1: \PHPUnit\Framework\MockObject\MockObject}
   *   The channel under test and the mocked inner channel it wraps.
   */
  private function buildChannel(string $verbosity, bool $logToWatchdog): array {
    $config = $this->createMock(ImmutableConfig::class);
    $config->method('get')->willReturnMap([
      ['logging.verbosity', $verbosity],
      ['logging.log_to_watchdog', $logToWatchdog],
    ]);

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

    $inner = $this->createMock(LoggerChannelInterface::class);

    return [new FilteredLoggerChannel($inner, $configFactory), $inner];
  }

  /**
   * Tests that the watchdog toggle suppresses everything, including errors.
   *
   * @covers ::log
   */
  public function testWatchdogDisabledSuppressesEverything(): void {
    [$channel, $inner] = $this->buildChannel('debug', FALSE);
    $inner->expects($this->never())->method('log');

    $channel->error('boom');
    $channel->info('hello');
    $channel->debug('trace');
  }

  /**
   * Tests that silent verbosity suppresses everything, including errors.
   *
   * @covers ::log
   */
  public function testSilentSuppressesEverything(): void {
    [$channel, $inner] = $this->buildChannel('silent', TRUE);
    $inner->expects($this->never())->method('log');

    $channel->error('boom');
    $channel->info('hello');
  }

  /**
   * Tests that minimal verbosity passes errors but drops info and debug.
   *
   * @covers ::log
   */
  public function testMinimalPassesErrorsOnly(): void {
    [$channel, $inner] = $this->buildChannel('minimal', TRUE);
    $inner->expects($this->once())
      ->method('log')
      ->with(LogLevel::ERROR, 'boom', []);

    $channel->info('hello');
    $channel->debug('trace');
    $channel->error('boom');
  }

  /**
   * Tests that standard verbosity passes info but drops debug.
   *
   * @covers ::log
   */
  public function testStandardDropsDebug(): void {
    [$channel, $inner] = $this->buildChannel('standard', TRUE);
    $inner->expects($this->once())
      ->method('log')
      ->with(LogLevel::INFO, 'hello', []);

    $channel->debug('trace');
    $channel->info('hello');
  }

  /**
   * Tests that debug verbosity passes every level.
   *
   * @covers ::log
   */
  public function testDebugPassesEverything(): void {
    [$channel, $inner] = $this->buildChannel('debug', TRUE);
    $inner->expects($this->exactly(2))->method('log');

    $channel->info('hello');
    $channel->debug('trace');
  }

  /**
   * Tests that an unknown verbosity string falls back to standard behavior.
   *
   * @covers ::log
   */
  public function testUnknownVerbosityFallsBackToStandard(): void {
    [$channel, $inner] = $this->buildChannel('nonsense', TRUE);
    $inner->expects($this->once())
      ->method('log')
      ->with(LogLevel::INFO, 'hello', []);

    $channel->debug('trace');
    $channel->info('hello');
  }

  /**
   * Tests that channel-management calls delegate to the inner channel.
   *
   * @covers ::setLoggers
   * @covers ::setRequestStack
   */
  public function testDelegatesChannelManagement(): void {
    [$channel, $inner] = $this->buildChannel('standard', TRUE);
    $inner->expects($this->once())->method('setLoggers')->with([]);
    $inner->expects($this->once())->method('setRequestStack')->with(NULL);

    $channel->setLoggers([]);
    $channel->setRequestStack(NULL);
  }

}
