<?php

declare(strict_types=1);

namespace Tests\Unit\Framework\Logging;

use App\Framework\Logging\Aggregation\AggregationConfig;
use App\Framework\Logging\Handlers\AggregatingLogHandler;
use App\Framework\Logging\LogHandler;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\LogRecord;
use App\Framework\Logging\ValueObjects\LogContext;
use PHPUnit\Framework\TestCase;

final class AggregatingLogHandlerTest extends TestCase
{
    public function test_aggregates_identical_messages(): void
    {
        $inner = $this->createMock(LogHandler::class);

        $config = new AggregationConfig(
            flushIntervalSeconds: 60,
            minOccurrencesForAggregation: 2
        );

        $handler = new AggregatingLogHandler($inner, $config);

        // Keine Ausgabe bis flush
        $inner->expects($this->never())->method('handle');

        // Sende 5 identische Messages
        for ($i = 0; $i < 5; $i++) {
            $handler->handle($this->createLogRecord('Identical error message', LogLevel::INFO));
        }

        $this->assertEquals(5, $handler->getTotalProcessed());
        $this->assertEquals(4, $handler->getTotalAggregated()); // Erste nicht aggregiert
    }

    public function test_normalizes_messages_with_variables(): void
    {
        $inner = $this->createMock(LogHandler::class);

        $config = AggregationConfig::production();
        $handler = new AggregatingLogHandler($inner, $config);

        // Diese Messages sollten als identisch erkannt werden
        $handler->handle($this->createLogRecord('User 123 logged in', LogLevel::INFO));
        $handler->handle($this->createLogRecord('User 456 logged in', LogLevel::INFO));
        $handler->handle($this->createLogRecord('User 789 logged in', LogLevel::INFO));

        // Sollten aggregiert werden
        $this->assertEquals(1, $handler->getAggregatedCount());
    }

    public function test_does_not_aggregate_critical_levels(): void
    {
        $inner = $this->createMock(LogHandler::class);

        $config = AggregationConfig::production(); // Aggregiert nur INFO, NOTICE, WARNING
        $handler = new AggregatingLogHandler($inner, $config);

        // ERROR sollte direkt durchgereicht werden
        $inner->expects($this->once())->method('handle');

        $handler->handle($this->createLogRecord('Critical error', LogLevel::ERROR));
    }

    public function test_flushes_on_interval(): void
    {
        $inner = $this->createMock(LogHandler::class);

        $config = new AggregationConfig(
            flushIntervalSeconds: 0, // Sofortiger Flush für Test
            minOccurrencesForAggregation: 2
        );

        $handler = new AggregatingLogHandler($inner, $config);

        $handler->handle($this->createLogRecord('Test', LogLevel::INFO));
        $handler->handle($this->createLogRecord('Test', LogLevel::INFO));

        // Trigger flush durch neuen Handle
        $inner->expects($this->atLeastOnce())->method('handle');
        $handler->handle($this->createLogRecord('Another', LogLevel::INFO));
    }

    public function test_outputs_aggregation_summary(): void
    {
        $actualRecord = null;
        $inner = $this->createMock(LogHandler::class);
        $inner->method('handle')->willReturnCallback(function($record) use (&$actualRecord) {
            $actualRecord = $record;
        });

        $config = new AggregationConfig(
            minOccurrencesForAggregation: 2
        );

        $handler = new AggregatingLogHandler($inner, $config);

        // Sende mehrere identische Messages
        for ($i = 0; $i < 5; $i++) {
            $handler->handle($this->createLogRecord('Database query failed', LogLevel::WARNING));
        }

        $handler->flush();

        $this->assertNotNull($actualRecord);
        $this->assertStringContainsString('occurred', $actualRecord->message);
        $this->assertArrayHasKey('aggregated', $actualRecord->context->structured);
        $this->assertEquals(5, $actualRecord->context->structured['occurrence_count']);
    }

    public function test_health_check(): void
    {
        $inner = $this->createMock(LogHandler::class);
        $handler = new AggregatingLogHandler($inner);

        $health = $handler->check();

        $this->assertEquals(\App\Framework\Health\HealthStatus::HEALTHY, $health->status);
        $this->assertArrayHasKey('total_processed', $health->details);
        $this->assertArrayHasKey('aggregation_rate', $health->details);
    }

    private function createLogRecord(
        string $message = 'test',
        LogLevel $level = LogLevel::INFO
    ): LogRecord {
        return new LogRecord(
            level: $level,
            message: $message,
            channel: 'test',
            context: LogContext::empty(),
            timestamp: new \DateTimeImmutable()
        );
    }
}
