<?php

declare(strict_types=1);

namespace App\Application\Security\ExceptionHandlers;

use App\Application\Security\Events\{
    Authorization\AccessDeniedEvent,
    Crypto\CryptographicFailureEvent,
    Input\InputValidationFailureEvent,
    System\SystemAnomalyEvent
};
use App\Framework\Core\Events\EventDispatcher;
use App\Framework\Exception\FrameworkException;
use App\Framework\Logging\Logger;
use App\Framework\Validation\Exceptions\ValidationException;
use Symfony\Component\Finder\Exception\AccessDeniedException;
use Throwable;

final class SecurityExceptionHandler
{
    public function __construct(
        private EventDispatcher $eventDispatcher,
        private Logger $logger
    ) {
    }

    public function handle(Throwable $exception): void
    {
        match (true) {
            $exception instanceof AccessDeniedException => $this->handleAccessDenied($exception),
            $exception instanceof ValidationException => $this->handleValidationError($exception),
            $exception instanceof FrameworkException && $this->isCryptographicError($exception) => $this->handleCryptographicError($exception),
            $exception instanceof \Error => $this->handleSystemError($exception),
            default => $this->handleGenericSecurityIssue($exception)
        };
    }

    private function handleAccessDenied(AccessDeniedException $exception): void
    {
        $this->eventDispatcher->dispatch(new AccessDeniedEvent(
            email: $this->getCurrentUserEmail(),
            resource: $exception->getResource(),
            action: $exception->getAction(),
            requiredPermission: $exception->getRequiredPermission()
        ));
    }

    private function handleValidationError(ValidationException $exception): void
    {
        foreach ($exception->getErrors() as $field => $errors) {
            $this->eventDispatcher->dispatch(new InputValidationFailureEvent(
                fieldName: $field,
                invalidValue: $exception->getInvalidValue($field) ?? '',
                validationRule: implode(', ', $errors),
                email: $this->getCurrentUserEmail()
            ));
        }
    }

    private function isCryptographicError(FrameworkException $exception): bool
    {
        $context = $exception->getContext();

        return str_contains(strtolower($exception->getMessage()), 'cryptographic') ||
               str_contains(strtolower($context->getOperation() ?? ''), 'crypto') ||
               str_contains(strtolower($context->getComponent() ?? ''), 'crypto');
    }

    private function handleCryptographicError(FrameworkException $exception): void
    {
        $context = $exception->getContext();
        $this->eventDispatcher->dispatch(new CryptographicFailureEvent(
            operation: $context->getOperation() ?? 'unknown',
            algorithm: $context->getData()['algorithm'] ?? 'unknown',
            errorMessage: $exception->getMessage(),
            email: $this->getCurrentUserEmail()
        ));
    }

    private function handleSystemError(\Error $exception): void
    {
        $this->eventDispatcher->dispatch(new SystemAnomalyEvent(
            anomalyType: 'system_error',
            description: $exception->getMessage(),
            metrics: [
                'error_type' => get_class($exception),
                'file' => $exception->getFile(),
                'line' => $exception->getLine(),
                'memory_usage' => memory_get_usage(true),
            ],
            severity: 'high'
        ));
    }

    private function handleGenericSecurityIssue(Throwable $exception): void
    {
        // Nur bei tatsächlich sicherheitsrelevanten Exceptions loggen
        if ($this->isSecurityRelevant($exception)) {
            $this->logger->warning('Security-relevant exception detected', [
                'exception_class' => get_class($exception),
                'message' => $exception->getMessage(),
                'user_email' => $this->getCurrentUserEmail(),
                'security_context' => [
                    'ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
                    'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null,
                    'request_uri' => $_SERVER['REQUEST_URI'] ?? null,
                ],
            ]);
        }
    }

    private function isSecurityRelevant(Throwable $exception): bool
    {
        $securityRelevantClasses = [
            'AccessDeniedException',
            'AuthenticationException',
            'AuthorizationException',
            'ValidationException',
            'CryptographicException',
            'FileUploadException',
            'SessionException',
        ];

        $className = get_class($exception);
        foreach ($securityRelevantClasses as $securityClass) {
            if (str_contains($className, $securityClass)) {
                return true;
            }
        }

        // Sicherheitsrelevante Nachrichten prüfen
        $message = strtolower($exception->getMessage());
        $securityKeywords = [
            'access denied', 'unauthorized', 'permission', 'authentication',
            'authorization', 'csrf', 'xss', 'injection', 'validation failed',
        ];

        foreach ($securityKeywords as $keyword) {
            if (str_contains($message, $keyword)) {
                return true;
            }
        }

        return false;
    }

    private function getCurrentUserEmail(): ?string
    {
        return $_SESSION['user_email'] ?? null;
    }
}
