<?php

declare(strict_types=1);

namespace App\Framework\Logging\Handlers;

use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\Health\HealthCheckCategory;
use App\Framework\Health\HealthCheckInterface;
use App\Framework\Health\HealthCheckResult;
use App\Framework\Health\HealthStatus;
use App\Framework\Logging\Aggregation\AggregatedLogEntry;
use App\Framework\Logging\Aggregation\AggregationConfig;
use App\Framework\Logging\Aggregation\MessageFingerprint;
use App\Framework\Logging\LogHandler;
use App\Framework\Logging\LogLevel;
use App\Framework\Logging\LogRecord;
use App\Framework\Logging\ValueObjects\LogContext;

/**
 * Aggregating Log Handler
 *
 * Gruppiert identische/ähnliche Logs und gibt sie als Summary aus.
 * Reduziert Log-Volume massiv bei wiederkehrenden Messages.
 *
 * Features:
 * - Message Normalisierung (entfernt IDs, UUIDs, Zahlen)
 * - Time-Window basierte Aggregation
 * - Automatischer Flush nach Intervall
 * - Flood-Protection
 * - Beispiel-Messages für Debugging
 */
final class AggregatingLogHandler implements LogHandler, HealthCheckInterface
{
    /** @var array<string, AggregatedLogEntry> */
    private array $aggregatedEntries = [];
    private Timestamp $windowStart;
    private int $totalProcessed = 0;
    private int $totalAggregated = 0;

    public function __construct(
        private readonly LogHandler $handler,
        private readonly AggregationConfig $config = new AggregationConfig()
    ) {
        $this->windowStart = Timestamp::now();
    }

    public function handle(LogRecord $record): void
    {
        $this->totalProcessed++;

        // Prüfe ob Window abgelaufen ist
        $this->maybeFlush();

        // Prüfe ob Level aggregiert werden soll
        if (!$this->config->shouldAggregate($record->level)) {
            $this->handler->handle($record);
            return;
        }

        // Erstelle Fingerprint
        $fingerprint = MessageFingerprint::fromLogRecord($record);
        $hash = $fingerprint->getHash();

        // Füge zu existierendem Eintrag hinzu oder erstelle neuen
        if (isset($this->aggregatedEntries[$hash])) {
            $this->aggregatedEntries[$hash]->add($record);
            $this->totalAggregated++;
        } else {
            $this->aggregatedEntries[$hash] = new AggregatedLogEntry($fingerprint, $record);
        }

        // Schutz vor Memory-Overflow
        if (count($this->aggregatedEntries) > $this->config->maxEntriesPerWindow) {
            $this->flush();
        }
    }

    private function maybeFlush(): void
    {
        $elapsed = Timestamp::now()->diffInSeconds($this->windowStart);

        if ($elapsed >= $this->config->flushIntervalSeconds) {
            $this->flush();
        }
    }

    public function flush(): void
    {
        if (empty($this->aggregatedEntries)) {
            return;
        }

        // Sortiere nach Häufigkeit (meiste zuerst)
        uasort($this->aggregatedEntries, fn($a, $b) => $b->getCount() <=> $a->getCount());

        // Calculate aggregation metrics
        $originalCount = 0;
        $aggregatedCount = 0;

        foreach ($this->aggregatedEntries as $entry) {
            $originalCount += $entry->getCount();
            $aggregatedCount++;
        }

        // Report aggregation metrics
        if (class_exists(\App\Framework\Logging\Metrics\LogMetricsCollector::class)) {
            \App\Framework\Logging\Metrics\LogMetricsCollector::getInstance()
                ->recordAggregation($originalCount, $aggregatedCount);
        }

        foreach ($this->aggregatedEntries as $entry) {
            // Nur aggregierte Logs ausgeben (mindestens X Vorkommen)
            if ($entry->getCount() >= $this->config->minOccurrencesForAggregation) {
                try {
                    $this->handler->handle($entry->toLogRecord());
                } catch (\Throwable $e) {
                    // Silent fail - Aggregation darf nicht crashen
                    error_log('Aggregation flush failed: ' . $e->getMessage());
                }
            } else {
                // Einzelne Logs normal ausgeben
                try {
                    $this->handler->handle($entry->getFirstRecord());
                } catch (\Throwable) {
                    // Silent fail
                }
            }
        }

        // Reset
        $this->aggregatedEntries = [];
        $this->windowStart = Timestamp::now();
    }

    public function __destruct()
    {
        try {
            $this->flush();
        } catch (\Throwable) {
            // Silent fail im Destruktor
        }
    }

    public function getAggregatedCount(): int
    {
        return count($this->aggregatedEntries);
    }

    public function getTotalProcessed(): int
    {
        return $this->totalProcessed;
    }

    public function getTotalAggregated(): int
    {
        return $this->totalAggregated;
    }

    public function getAggregationRate(): float
    {
        if ($this->totalProcessed === 0) {
            return 0.0;
        }

        return $this->totalAggregated / $this->totalProcessed;
    }

    public function check(): HealthCheckResult
    {
        $aggregationRate = $this->getAggregationRate();

        $details = [
            'total_processed' => $this->totalProcessed,
            'total_aggregated' => $this->totalAggregated,
            'aggregation_rate' => round($aggregationRate * 100, 2) . '%',
            'current_window_entries' => count($this->aggregatedEntries),
            'max_entries' => $this->config->maxEntriesPerWindow,
            'window_age_seconds' => Timestamp::now()->diffInSeconds($this->windowStart),
            'config' => [
                'flush_interval' => $this->config->flushIntervalSeconds,
                'min_occurrences' => $this->config->minOccurrencesForAggregation,
            ],
        ];

        $status = HealthStatus::HEALTHY;
        $message = 'Aggregation working normally';

        // Warning bei sehr hoher Aggregation (kann auf Probleme hinweisen)
        if ($aggregationRate > 0.9 && $this->totalProcessed > 100) {
            $status = HealthStatus::DEGRADED;
            $message = sprintf('Very high aggregation rate: %.1f%%', $aggregationRate * 100);
        }

        // Warning bei fast vollem Window
        if (count($this->aggregatedEntries) > ($this->config->maxEntriesPerWindow * 0.8)) {
            $status = HealthStatus::DEGRADED;
            $message = 'Aggregation window nearly full';
        }

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

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

    public function isHandling(LogRecord $record): bool
    {
        // Delegate to wrapped handler
        return $this->handler->isHandling($record);
    }

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

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