<?php
declare(strict_types=1);

namespace App\Framework\Core\ErrorHandler;

use App\Application\Security\ExceptionHandlers\SecurityExceptionHandler;
use App\Application\Security\Events\System\SystemAnomalyEvent;
use App\Framework\Core\Events\EventDispatcher;
use Psr\Log\LoggerInterface;
use Throwable;

final class GlobalErrorHandler
{
    public function __construct(
        private LoggerInterface $logger,
        private EventDispatcher $eventDispatcher,
        private SecurityExceptionHandler $securityHandler,
        private bool $debugMode = false
    ) {}

    public function handleException(Throwable $exception): void
    {
        // Zuerst Security Events dispatchen
        $this->handleSecurityAspects($exception);

        // Dann Standard Error Handling
        $this->logException($exception);

        if ($this->debugMode) {
            $this->displayDebugInfo($exception);
        } else {
            $this->displayUserFriendlyError($exception);
        }
    }

    public function handleError(int $severity, string $message, string $file, int $line): bool
    {
        // PHP Errors in Exceptions umwandeln für einheitliche Behandlung
        $exception = new \ErrorException($message, 0, $severity, $file, $line);

        // Security relevante Errors erkennen
        if ($this->isSecurityRelevantError($severity, $message)) {
            $this->handleSecurityAspects($exception);
        }

        $this->logError($severity, $message, $file, $line);

        // true = Error wurde behandelt, false = PHP default error handler
        return !$this->debugMode;
    }

    public function handleFatalError(): void
    {
        $error = error_get_last();
        if ($error && $this->isFatal($error['type'])) {
            $exception = new \ErrorException(
                $error['message'],
                0,
                $error['type'],
                $error['file'],
                $error['line']
            );

            $this->handleSecurityAspects($exception);
            $this->logException($exception);

            if (!$this->debugMode) {
                $this->displayFatalErrorPage();
            }
        }
    }

    private function handleSecurityAspects(Throwable $exception): void
    {
        try {
            // Delegiere an speziellen Security Exception Handler
            $this->securityHandler->handle($exception);

            // Zusätzlich: System-weite Anomalien erkennen
            if ($this->isSystemAnomaly($exception)) {
                $this->dispatchSystemAnomaly($exception);
            }

        } catch (Throwable $securityHandlingException) {
            // Fallback: Security Handler selbst darf nicht crashen
            $this->logger->critical('Security exception handler failed', [
                'original_exception' => $exception->getMessage(),
                'security_handler_exception' => $securityHandlingException->getMessage(),
                'trace' => $securityHandlingException->getTraceAsString()
            ]);
        }
    }

    private function isSecurityRelevantError(int $severity, string $message): bool
    {
        // Sicherheitskritische PHP Errors identifizieren
        $securityKeywords = [
            'permission denied', 'access denied', 'authentication',
            'authorization', 'file_get_contents', 'file_put_contents',
            'openssl_', 'hash_', 'password_', 'session_',
            'mysqli_', 'pdo_', 'curl_', 'file_upload'
        ];

        $messageLower = strtolower($message);
        foreach ($securityKeywords as $keyword) {
            if (strpos($messageLower, $keyword) !== false) {
                return true;
            }
        }

        // Bestimmte Error-Level sind grundsätzlich sicherheitsrelevant
        return in_array($severity, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR]);
    }

    private function isSystemAnomaly(Throwable $exception): bool
    {
        // System-Anomalien erkennen
        return $exception instanceof \Error ||
               $exception instanceof \OutOfMemoryError ||
               $exception instanceof \ParseError ||
               strpos($exception->getMessage(), 'Maximum execution time') !== false ||
               strpos($exception->getMessage(), 'Allowed memory size') !== false;
    }

    private function dispatchSystemAnomaly(Throwable $exception): void
    {
        $metrics = [
            'memory_usage' => memory_get_usage(true),
            'memory_peak' => memory_get_peak_usage(true),
            'execution_time' => microtime(true) - ($_SERVER['REQUEST_TIME_FLOAT'] ?? microtime(true))
        ];

        $severity = match (true) {
            $exception instanceof \OutOfMemoryError => 'critical',
            $exception instanceof \Error => 'high',
            default => 'medium'
        };

        $this->eventDispatcher->dispatch(new SystemAnomalyEvent(
            anomalyType: get_class($exception),
            description: $exception->getMessage(),
            metrics: $metrics,
            severity: $severity
        ));
    }

    private function isFatal(int $type): bool
    {
        return in_array($type, [E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE]);
    }

    private function logException(Throwable $exception): void
    {
        $context = [
            'exception_class' => get_class($exception),
            'message' => $exception->getMessage(),
            'file' => $exception->getFile(),
            'line' => $exception->getLine(),
            'trace' => $exception->getTraceAsString(),
            'user_id' => $_SESSION['user_id'] ?? null,
            'request_uri' => $_SERVER['REQUEST_URI'] ?? null,
            'request_method' => $_SERVER['REQUEST_METHOD'] ?? null,
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null,
            'remote_addr' => $_SERVER['REMOTE_ADDR'] ?? null
        ];

        $this->logger->error('Unhandled exception: ' . $exception->getMessage(), $context);
    }

    private function logError(int $severity, string $message, string $file, int $line): void
    {
        $level = $this->mapErrorSeverityToLogLevel($severity);

        $this->logger->log($level, "PHP Error: {$message}", [
            'severity' => $severity,
            'file' => $file,
            'line' => $line,
            'user_id' => $_SESSION['user_id'] ?? null
        ]);
    }

    private function mapErrorSeverityToLogLevel(int $severity): string
    {
        return match ($severity) {
            E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR => 'error',
            E_WARNING, E_CORE_WARNING, E_COMPILE_WARNING, E_USER_WARNING => 'warning',
            E_NOTICE, E_USER_NOTICE => 'notice',
            E_STRICT => 'info',
            default => 'debug'
        };
    }

    private function displayDebugInfo(Throwable $exception): void
    {
        echo "<h1>Exception: " . get_class($exception) . "</h1>";
        echo "<p><strong>Message:</strong> " . htmlspecialchars($exception->getMessage()) . "</p>";
        echo "<p><strong>File:</strong> " . htmlspecialchars($exception->getFile()) . ":" . $exception->getLine() . "</p>";
        echo "<pre>" . htmlspecialchars($exception->getTraceAsString()) . "</pre>";
    }

    private function displayUserFriendlyError(Throwable $exception): void
    {
        http_response_code(500);
        echo "<!DOCTYPE html><html><head><title>Fehler</title></head><body>";
        echo "<h1>Es ist ein Fehler aufgetreten</h1>";
        echo "<p>Entschuldigung, es ist ein unerwarteter Fehler aufgetreten. Bitte versuchen Sie es später erneut.</p>";
        echo "</body></html>";
    }

    private function displayFatalErrorPage(): void
    {
        http_response_code(500);
        echo "<!DOCTYPE html><html><head><title>Schwerwiegender Fehler</title></head><body>";
        echo "<h1>Schwerwiegender Systemfehler</h1>";
        echo "<p>Die Anwendung musste aufgrund eines kritischen Fehlers beendet werden.</p>";
        echo "</body></html>";
    }
}
