<?php

declare(strict_types=1);

namespace App\Framework\Logging\Handlers;

use App\Framework\CircuitBreaker\CircuitBreaker;
use App\Framework\CircuitBreaker\CircuitBreakerConfig;
use App\Framework\Health\HealthCheckCategory;
use App\Framework\Logging\LogHandler;
use App\Framework\Logging\LogRecord;

/**
 * Resilient Log Handler mit Fallback und Circuit Breaker
 *
 * Stellt sicher, dass Logging-Fehler niemals die Anwendung zum Absturz bringen.
 * Bei Problemen mit dem primären Handler wird automatisch auf Fallback umgeschaltet.
 *
 * Features:
 * - Circuit Breaker verhindert wiederholte Fehlerversuche
 * - Automatischer Fallback bei Primär-Handler-Fehlern
 * - Silent Failures als letztes Mittel (error_log)
 * - Selbstheilend durch Circuit Breaker Half-Open State
 */
final class ResilientLogHandler implements LogHandler, \App\Framework\Health\HealthCheckInterface
{
    private CircuitBreaker $circuitBreaker;

    public function __construct(
        private readonly LogHandler $primaryHandler,
        private readonly LogHandler $fallbackHandler,
        ?CircuitBreaker $circuitBreaker = null
    ) {
        $this->circuitBreaker = $circuitBreaker ?? new CircuitBreaker(
            new CircuitBreakerConfig(
                name: 'logging.primary',
                failureThreshold: 5,
                successThreshold: 2,
                timeout: 60.0,
                halfOpenRequests: 1
            )
        );
    }

    public function handle(LogRecord $record): void
    {
        // Circuit Breaker ist offen - direkt Fallback nutzen
        if ($this->circuitBreaker->isOpen()) {
            $this->handleWithFallback($record);
            return;
        }

        try {
            // Versuch mit primärem Handler
            $this->circuitBreaker->call(
                fn() => $this->primaryHandler->handle($record)
            );
        } catch (\Throwable $e) {
            // Primärer Handler fehlgeschlagen - Fallback nutzen
            $this->handleWithFallback($record, $e);
        }
    }

    private function handleWithFallback(LogRecord $record, ?\Throwable $primaryError = null): void
    {
        try {
            $this->fallbackHandler->handle($record);

            // Log den Grund für Fallback-Nutzung (nur bei Fehlern)
            if ($primaryError !== null) {
                $this->fallbackHandler->handle(
                    new LogRecord(
                        level: $record->level,
                        message: 'Primary log handler failed, using fallback',
                        channel: 'logging',
                        context: $record->context->addData('error', $primaryError->getMessage()),
                        timestamp: $record->timestamp
                    )
                );
            }
        } catch (\Throwable $fallbackError) {
            // Beide Handler fehlgeschlagen - Silent Fail mit error_log
            $this->emergencyLog($record, $primaryError, $fallbackError);
        }
    }

    /**
     * Letzter Ausweg: PHP error_log
     * Logging darf niemals die Anwendung crashen
     */
    private function emergencyLog(
        LogRecord $record,
        ?\Throwable $primaryError,
        \Throwable $fallbackError
    ): void {
        $message = sprintf(
            "[CRITICAL] All log handlers failed. Original: %s [%s] | Primary Error: %s | Fallback Error: %s",
            $record->message,
            $record->level->getName(),
            $primaryError?->getMessage() ?? 'N/A',
            $fallbackError->getMessage()
        );

        error_log($message);
    }

    public function isHealthy(): bool
    {
        return !$this->circuitBreaker->isOpen();
    }

    public function getCircuitBreakerState(): string
    {
        return $this->circuitBreaker->getState()->value;
    }

    public function check(): \App\Framework\Health\HealthCheckResult
    {
        $state = $this->circuitBreaker->getState();
        $details = [
            'circuit_breaker_state' => $state->value,
            'is_healthy' => $this->isHealthy(),
        ];

        $status = \App\Framework\Health\HealthStatus::HEALTHY;
        $message = 'Primary handler operational';

        if ($state === \App\Framework\CircuitBreaker\CircuitBreakerState::OPEN) {
            $status = \App\Framework\Health\HealthStatus::UNHEALTHY;
            $message = 'Circuit breaker is open - using fallback handler';
        } elseif ($state === \App\Framework\CircuitBreaker\CircuitBreakerState::HALF_OPEN) {
            $status = \App\Framework\Health\HealthStatus::DEGRADED;
            $message = 'Circuit breaker is half-open - recovering';
        }

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

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

    public function isHandling(LogRecord $record): bool
    {
        // Delegate to primary handler (if circuit is closed)
        return $this->primaryHandler->isHandling($record);
    }

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

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