<?php

declare(strict_types=1);

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

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Logger\LoggerChannelFactoryInterface;
use Drupal\Core\Logger\LoggerChannelInterface;
use Drupal\flowdrop_runtime\Service\Logging\FilteredLoggerChannel;
use Drupal\flowdrop_runtime\Service\Logging\FilteredLoggerChannelFactory;
use Drupal\Tests\UnitTestCase;

/**
 * Unit tests for FilteredLoggerChannelFactory.
 *
 * @coversDefaultClass \Drupal\flowdrop_runtime\Service\Logging\FilteredLoggerChannelFactory
 * @group flowdrop_runtime
 */
class FilteredLoggerChannelFactoryTest extends UnitTestCase {

  /**
   * The inner factory that the decorator wraps.
   *
   * @var \Drupal\Core\Logger\LoggerChannelFactoryInterface&\PHPUnit\Framework\MockObject\MockObject
   */
  private LoggerChannelFactoryInterface $inner;

  /**
   * The factory under test.
   *
   * @var \Drupal\flowdrop_runtime\Service\Logging\FilteredLoggerChannelFactory
   */
  private FilteredLoggerChannelFactory $factory;

  /**
   * {@inheritdoc}
   */
  protected function setUp(): void {
    parent::setUp();
    $this->inner = $this->createMock(LoggerChannelFactoryInterface::class);
    $this->inner->method('get')->willReturnCallback(
      fn () => $this->createMock(LoggerChannelInterface::class)
    );
    $this->factory = new FilteredLoggerChannelFactory(
      $this->inner,
      $this->createMock(ConfigFactoryInterface::class),
    );
  }

  /**
   * Tests that FlowDrop execution-engine channels are wrapped in the filter.
   *
   * @covers ::get
   * @dataProvider filteredChannelProvider
   */
  public function testEngineChannelsAreFiltered(string $channel): void {
    $this->assertInstanceOf(FilteredLoggerChannel::class, $this->factory->get($channel));
  }

  /**
   * Provides the channels that must be filtered.
   *
   * @return array<string, array{0: string}>
   *   Channel names.
   */
  public static function filteredChannelProvider(): array {
    return [
      'runtime' => ['flowdrop_runtime'],
      'stategraph' => ['flowdrop_stategraph'],
      'workflow_executor' => ['flowdrop_workflow_executor'],
    ];
  }

  /**
   * Tests that unrelated channels are returned untouched.
   *
   * @covers ::get
   * @dataProvider unfilteredChannelProvider
   */
  public function testOtherChannelsAreNotFiltered(string $channel): void {
    $this->assertNotInstanceOf(FilteredLoggerChannel::class, $this->factory->get($channel));
  }

  /**
   * Provides channels that must pass through unwrapped.
   *
   * @return array<string, array{0: string}>
   *   Channel names.
   */
  public static function unfilteredChannelProvider(): array {
    return [
      'interrupt' => ['flowdrop_interrupt'],
      'cron' => ['cron'],
      'default' => ['default'],
    ];
  }

  /**
   * Tests that repeated get() calls return the same wrapper instance.
   *
   * @covers ::get
   */
  public function testWrapperIsCached(): void {
    $first = $this->factory->get('flowdrop_runtime');
    $second = $this->factory->get('flowdrop_runtime');
    $this->assertSame($first, $second);
  }

  /**
   * Tests that addLogger is delegated to the inner factory.
   *
   * @covers ::addLogger
   */
  public function testAddLoggerDelegates(): void {
    $logger = $this->createMock(LoggerChannelInterface::class);
    $this->inner->expects($this->once())->method('addLogger')->with($logger, 5);
    $this->factory->addLogger($logger, 5);
  }

}
