<?php

declare(strict_types=1);

namespace Tests\Unit\Framework\Logging;

use App\Framework\Logging\Handlers\SamplingLogHandler;
use App\Framework\Logging\LogHandler;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\LogRecord;
use App\Framework\Logging\Sampling\SamplingConfig;
use App\Framework\Logging\ValueObjects\LogContext;
use PHPUnit\Framework\TestCase;

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

        // Config: 100% ERROR, 0% INFO
        $config = SamplingConfig::custom([
            'ERROR' => 1.0,
            'INFO' => 0.0,
        ]);

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

        // ERROR sollte durchkommen
        $inner->expects($this->once())->method('handle');
        $handler->handle($this->createLogRecord('error', LogLevel::ERROR));

        // INFO sollte gedroppt werden
        $handler->handle($this->createLogRecord('info', LogLevel::INFO));

        $this->assertEquals(1, $handler->getAcceptedCount());
        $this->assertEquals(1, $handler->getDroppedCount());
    }

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

        $config = SamplingConfig::production();
        $handler = new SamplingLogHandler($inner, $config);

        $criticalLevels = [
            LogLevel::ERROR,
            LogLevel::CRITICAL,
            LogLevel::ALERT,
            LogLevel::EMERGENCY,
        ];

        $inner->expects($this->exactly(count($criticalLevels)))
            ->method('handle');

        foreach ($criticalLevels as $level) {
            $handler->handle($this->createLogRecord('test', $level));
        }

        $this->assertEquals(count($criticalLevels), $handler->getAcceptedCount());
        $this->assertEquals(0, $handler->getDroppedCount());
    }

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

        $config = SamplingConfig::custom(['INFO' => 0.0]);
        $handler = new SamplingLogHandler($inner, $config);

        for ($i = 0; $i < 10; $i++) {
            $handler->handle($this->createLogRecord('info', LogLevel::INFO));
        }

        $dropped = $handler->getDroppedByLevel();

        $this->assertArrayHasKey('INFO', $dropped);
        $this->assertEquals(10, $dropped['INFO']);
    }

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

        $config = SamplingConfig::custom(['INFO' => 0.5]);
        $handler = new SamplingLogHandler($inner, $config);

        // Simulate: sollte ca. 50% droppen
        // Für deterministische Tests mocken wir mt_rand nicht,
        // aber prüfen nur dass Drop-Rate berechnet wird

        $this->assertEquals(0.0, $handler->getDropRate()); // Noch keine Logs
    }

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

        $health = $handler->check();

        $this->assertEquals(\App\Framework\Health\HealthStatus::HEALTHY, $health->status);
        $this->assertArrayHasKey('accepted', $health->details);
        $this->assertArrayHasKey('dropped', $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()
        );
    }
}
