<?php

declare(strict_types=1);

namespace Tests\Unit\Framework\Logging;

use App\Framework\Logging\DefaultLogger;
use App\Framework\Logging\HasChannel;
use App\Framework\Logging\LogChannel;
use App\Framework\Logging\LogHandler;
use App\Framework\Logging\Logger;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\LogRecord;
use App\Framework\Logging\ProcessorManager;
use App\Framework\Logging\ValueObjects\LogContext;
use PHPUnit\Framework\TestCase;

final class DefaultLoggerChannelTest extends TestCase
{
    private array $capturedRecords = [];

    private DefaultLogger $logger;

    protected function setUp(): void
    {
        $this->capturedRecords = [];

        // Mock Handler der alle Records captured
        $mockHandler = $this->createMock(LogHandler::class);
        $mockHandler->method('isHandling')->willReturn(true);
        $mockHandler->method('handle')->willReturnCallback(function (LogRecord $record) {
            $this->capturedRecords[] = $record;
        });

        $this->logger = new DefaultLogger(
            minLevel: LogLevel::DEBUG,
            handlers: [$mockHandler],
            processorManager: new ProcessorManager()
        );
    }

    public function test_logger_has_all_channel_loggers(): void
    {
        $channelLogger = $this->logger->channel(LogChannel::SECURITY);
        expect($channelLogger)->toBeInstanceOf(Logger::class);
        expect($channelLogger)->toBeInstanceOf(HasChannel::class);

        expect($this->logger->channel(LogChannel::CACHE))->toBeInstanceOf(Logger::class);
        expect($this->logger->channel(LogChannel::DATABASE))->toBeInstanceOf(Logger::class);
        expect($this->logger->channel(LogChannel::FRAMEWORK))->toBeInstanceOf(Logger::class);
        expect($this->logger->channel(LogChannel::ERROR))->toBeInstanceOf(Logger::class);
    }

    public function test_channel_loggers_have_correct_channels(): void
    {
        expect($this->logger->channel(LogChannel::SECURITY)->channel)->toBe(LogChannel::SECURITY);
        expect($this->logger->channel(LogChannel::CACHE)->channel)->toBe(LogChannel::CACHE);
        expect($this->logger->channel(LogChannel::DATABASE)->channel)->toBe(LogChannel::DATABASE);
        expect($this->logger->channel(LogChannel::FRAMEWORK)->channel)->toBe(LogChannel::FRAMEWORK);
        expect($this->logger->channel(LogChannel::ERROR)->channel)->toBe(LogChannel::ERROR);
    }

    public function test_log_to_channel_creates_record_with_channel(): void
    {
        $this->logger->logToChannel(LogChannel::SECURITY, LogLevel::WARNING, 'Test message', LogContext::withData(['key' => 'value']));

        expect($this->capturedRecords)->toHaveCount(1);

        $record = $this->capturedRecords[0];
        expect($record->getChannel())->toBe('security');
        expect($record->getMessage())->toBe('Test message');
        expect($record->getLevel())->toBe(LogLevel::WARNING);
        expect($record->getContext())->toMatchArray(['key' => 'value']);
    }

    public function test_channel_logger_creates_records_with_correct_channel(): void
    {
        $this->logger->channel(LogChannel::SECURITY)->error('Security alert', LogContext::withData(['ip' => '192.168.1.1']));

        expect($this->capturedRecords)->toHaveCount(1);

        $record = $this->capturedRecords[0];
        expect($record->getChannel())->toBe('security');
        expect($record->getMessage())->toBe('Security alert');
        expect($record->getLevel())->toBe(LogLevel::ERROR);
        expect($record->getContext())->toBe(['ip' => '192.168.1.1']);
    }

    public function test_different_channels_create_different_records(): void
    {
        $this->logger->channel(LogChannel::SECURITY)->warning('Security warning');
        $this->logger->channel(LogChannel::CACHE)->debug('Cache debug');
        $this->logger->channel(LogChannel::DATABASE)->error('DB error');

        expect($this->capturedRecords)->toHaveCount(3);

        expect($this->capturedRecords[0]->getChannel())->toBe('security');
        expect($this->capturedRecords[0]->getLevel())->toBe(LogLevel::WARNING);

        expect($this->capturedRecords[1]->getChannel())->toBe('cache');
        expect($this->capturedRecords[1]->getLevel())->toBe(LogLevel::DEBUG);

        expect($this->capturedRecords[2]->getChannel())->toBe('database');
        expect($this->capturedRecords[2]->getLevel())->toBe(LogLevel::ERROR);
    }

    public function test_standard_logging_still_works(): void
    {
        $this->logger->info('Standard log message');

        expect($this->capturedRecords)->toHaveCount(1);

        $record = $this->capturedRecords[0];
        expect($record->getChannel())->toBeNull(); // Standard logs haben keinen Channel
        expect($record->getMessage())->toBe('Standard log message');
        expect($record->getLevel())->toBe(LogLevel::INFO);
    }

    public function test_log_to_channel_respects_min_level(): void
    {
        // Logger mit höherem Min-Level erstellen
        $logger = new DefaultLogger(minLevel: LogLevel::ERROR);

        // Debug-Level sollte ignoriert werden
        $logger->logToChannel(LogChannel::SECURITY, LogLevel::DEBUG, 'Debug message');

        // Keine Records sollten erstellt werden, da kein Handler gesetzt ist
        // aber der Level-Check sollte funktionieren
        expect(true)->toBeTrue(); // Test dass keine Exception geworfen wird
    }

    public function test_log_to_channel_with_empty_context(): void
    {
        $this->logger->logToChannel(LogChannel::FRAMEWORK, LogLevel::INFO, 'Framework message');

        expect($this->capturedRecords)->toHaveCount(1);

        $record = $this->capturedRecords[0];
        expect($record->getChannel())->toBe('framework');
        expect($record->getContext())->toBe([]);
    }

    public function test_mixed_logging_works_correctly(): void
    {
        // Mix aus Standard- und Channel-Logging
        $this->logger->info('Standard info');
        $this->logger->channel(LogChannel::SECURITY)->warning('Security warning');
        $this->logger->error('Standard error');
        $this->logger->channel(LogChannel::CACHE)->debug('Cache debug');

        expect($this->capturedRecords)->toHaveCount(4);

        // Standard logs haben keinen Channel
        expect($this->capturedRecords[0]->getChannel())->toBeNull();
        expect($this->capturedRecords[2]->getChannel())->toBeNull();

        // Channel logs haben korrekte Channels
        expect($this->capturedRecords[1]->getChannel())->toBe('security');
        expect($this->capturedRecords[3]->getChannel())->toBe('cache');
    }
}
