<?php

declare(strict_types=1);

namespace App\Tests\Unit\Shared\Infrastructure\Monitoring;

use App\Shared\Domain\CorrelationId;
use App\Shared\Infrastructure\Audit\CorrelationIdHolder;
use App\Shared\Infrastructure\Monitoring\CorrelationIdLogProcessor;
use App\Shared\Infrastructure\Tenant\TenantConfig;
use App\Shared\Infrastructure\Tenant\TenantContext;
use App\Shared\Infrastructure\Tenant\TenantId;
use DateTimeImmutable;
use Monolog\Level;
use Monolog\LogRecord;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\Test;
use PHPUnit\Framework\TestCase;

/**
 * @see Story 1.8 - T5.2: Processor to add correlation_id automatically
 * @see Story 1.8 - T5.3: Processor to add tenant_id automatically
 */
#[CoversClass(CorrelationIdLogProcessor::class)]
final class CorrelationIdLogProcessorTest extends TestCase
{
    private TenantContext $tenantContext;
    private CorrelationIdLogProcessor $processor;

    protected function setUp(): void
    {
        CorrelationIdHolder::clear();
        $this->tenantContext = new TenantContext();
        $this->processor = new CorrelationIdLogProcessor($this->tenantContext);
    }

    protected function tearDown(): void
    {
        CorrelationIdHolder::clear();
        $this->tenantContext->clear();
    }

    #[Test]
    public function itAddsCorrelationIdToLogRecord(): void
    {
        $correlationId = CorrelationId::fromString('01234567-89ab-cdef-0123-456789abcdef');
        CorrelationIdHolder::set($correlationId);

        $record = $this->createLogRecord();
        $result = ($this->processor)($record);

        self::assertArrayHasKey('correlation_id', $result->extra);
        self::assertSame('01234567-89ab-cdef-0123-456789abcdef', $result->extra['correlation_id']);
    }

    #[Test]
    public function itAddsTenantIdToLogRecord(): void
    {
        $tenantConfig = new TenantConfig(
            tenantId: TenantId::fromString('a1b2c3d4-e5f6-7890-abcd-ef1234567890'),
            subdomain: 'test-school',
            databaseUrl: 'postgresql://test@localhost/test',
        );
        $this->tenantContext->setCurrentTenant($tenantConfig);

        $record = $this->createLogRecord();
        $result = ($this->processor)($record);

        self::assertArrayHasKey('tenant_id', $result->extra);
        self::assertSame('test-school', $result->extra['tenant_id']);
    }

    #[Test]
    public function itAddsBothCorrelationIdAndTenantId(): void
    {
        $correlationId = CorrelationId::fromString('11111111-2222-3333-4444-555555555555');
        CorrelationIdHolder::set($correlationId);

        $tenantConfig = new TenantConfig(
            tenantId: TenantId::fromString('66666666-7777-8888-9999-aaaaaaaaaaaa'),
            subdomain: 'school',
            databaseUrl: 'postgresql://test@localhost/school',
        );
        $this->tenantContext->setCurrentTenant($tenantConfig);

        $record = $this->createLogRecord();
        $result = ($this->processor)($record);

        self::assertSame('11111111-2222-3333-4444-555555555555', $result->extra['correlation_id']);
        self::assertSame('school', $result->extra['tenant_id']);
    }

    #[Test]
    public function itDoesNotAddCorrelationIdWhenNotSet(): void
    {
        $record = $this->createLogRecord();
        $result = ($this->processor)($record);

        self::assertArrayNotHasKey('correlation_id', $result->extra);
    }

    #[Test]
    public function itDoesNotAddTenantIdWhenNoTenant(): void
    {
        $record = $this->createLogRecord();
        $result = ($this->processor)($record);

        self::assertArrayNotHasKey('tenant_id', $result->extra);
    }

    #[Test]
    public function itPreservesExistingExtraData(): void
    {
        $correlationId = CorrelationId::fromString('abcdef12-3456-7890-abcd-ef1234567890');
        CorrelationIdHolder::set($correlationId);

        $record = $this->createLogRecord(extra: ['existing_key' => 'existing_value']);
        $result = ($this->processor)($record);

        self::assertSame('existing_value', $result->extra['existing_key']);
        self::assertSame('abcdef12-3456-7890-abcd-ef1234567890', $result->extra['correlation_id']);
    }

    /**
     * @param array<string, mixed> $extra
     */
    private function createLogRecord(array $extra = []): LogRecord
    {
        return new LogRecord(
            datetime: new DateTimeImmutable(),
            channel: 'test',
            level: Level::Info,
            message: 'Test message',
            context: [],
            extra: $extra,
        );
    }
}
