<?php

declare(strict_types=1);

namespace App\Framework\Logging\Handlers;

use App\Framework\Health\HealthCheckCategory;
use App\Framework\Health\HealthCheckInterface;
use App\Framework\Health\HealthCheckResult;
use App\Framework\Health\HealthStatus;
use App\Framework\Logging\LogHandler;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\LogRecord;
use App\Framework\Logging\Sampling\SamplingConfig;

/**
 * Sampling Log Handler
 *
 * Sampelt Logs basierend auf konfigurierbaren Raten pro Level.
 * Reduziert Log-Volume in High-Load-Szenarien ohne kritische Logs zu verlieren.
 *
 * Features:
 * - Level-basiertes Sampling
 * - Probabilistic Sampling
 * - Garantiert: ERROR+ Levels werden immer geloggt (konfigurierbar)
 * - Metriken über gesampelte/verworfene Logs
 */
final class SamplingLogHandler implements LogHandler, HealthCheckInterface
{
    private int $acceptedCount = 0;
    private int $droppedCount = 0;
    private array $droppedByLevel = [];

    public function __construct(
        private readonly LogHandler $handler,
        private readonly SamplingConfig $config = new SamplingConfig()
    ) {
    }

    public function handle(LogRecord $record): void
    {
        // Sampling-Entscheidung
        $accepted = $this->config->shouldSample($record->level);

        if (!$accepted) {
            $this->recordDropped($record->level);

            // Report sampling metrics
            if (class_exists(\App\Framework\Logging\Metrics\LogMetricsCollector::class)) {
                \App\Framework\Logging\Metrics\LogMetricsCollector::getInstance()
                    ->recordSampling($record->level, false);
            }

            return;
        }

        $this->acceptedCount++;

        // Report sampling metrics
        if (class_exists(\App\Framework\Logging\Metrics\LogMetricsCollector::class)) {
            \App\Framework\Logging\Metrics\LogMetricsCollector::getInstance()
                ->recordSampling($record->level, true);
        }

        $this->handler->handle($record);
    }

    private function recordDropped(LogLevel $level): void
    {
        $this->droppedCount++;

        $levelName = $level->getName();
        if (!isset($this->droppedByLevel[$levelName])) {
            $this->droppedByLevel[$levelName] = 0;
        }
        $this->droppedByLevel[$levelName]++;
    }

    public function getAcceptedCount(): int
    {
        return $this->acceptedCount;
    }

    public function getDroppedCount(): int
    {
        return $this->droppedCount;
    }

    public function getTotalCount(): int
    {
        return $this->acceptedCount + $this->droppedCount;
    }

    public function getDropRate(): float
    {
        $total = $this->getTotalCount();

        if ($total === 0) {
            return 0.0;
        }

        return $this->droppedCount / $total;
    }

    /**
     * @return array<string, int>
     */
    public function getDroppedByLevel(): array
    {
        return $this->droppedByLevel;
    }

    public function resetMetrics(): void
    {
        $this->acceptedCount = 0;
        $this->droppedCount = 0;
        $this->droppedByLevel = [];
    }

    public function check(): HealthCheckResult
    {
        $dropRate = $this->getDropRate();

        $details = [
            'accepted' => $this->acceptedCount,
            'dropped' => $this->droppedCount,
            'total' => $this->getTotalCount(),
            'drop_rate' => round($dropRate * 100, 2) . '%',
            'dropped_by_level' => $this->droppedByLevel,
            'config' => $this->config->toArray(),
        ];

        // Warning bei sehr hoher Drop-Rate (> 95%)
        $status = HealthStatus::HEALTHY;
        $message = 'Sampling working as expected';

        if ($dropRate > 0.95 && $this->getTotalCount() > 100) {
            $status = HealthStatus::DEGRADED;
            $message = sprintf('Very high sampling drop rate: %.1f%%', $dropRate * 100);
        }

        return new HealthCheckResult(
            status: $status,
            componentName: $this->getName(),
            message: $message,
            details: $details,
            timestamp: new \DateTimeImmutable()
        );
    }

    public function getName(): string
    {
        return 'sampling_handler';
    }

    public function isHandling(LogRecord $record): bool
    {
        // We handle all levels but might sample them
        return $this->handler->isHandling($record);
    }

    public function getCategory(): HealthCheckCategory
    {
        return HealthCheckCategory::INFRASTRUCTURE;
    }

    public function getTimeout(): int
    {
        return 3000; // 3 seconds timeout for sampling handler health check
    }
}
