<?php

declare(strict_types=1);

namespace App\Framework\Exception;

use App\Framework\Exception\SecurityEvent\SecurityEventInterface;

/**
 * Security-spezifische Exception mit OWASP-konformen Event-Logging
 */
class SecurityException extends FrameworkException
{
    protected SecurityEventInterface $securityEvent;

    public function __construct(
        SecurityEventInterface $securityEvent,
        string $message = '',
        int $code = 0,
        ?\Throwable $previous = null,
        ?ExceptionContext $additionalContext = null
    ) {
        $this->securityEvent = $securityEvent;

        // Erstelle Security-Context
        $context = $this->createSecurityContext($securityEvent, $additionalContext);

        // Verwende Event-Beschreibung als Message falls nicht gesetzt
        $finalMessage = $message ?: $securityEvent->getDescription();

        parent::__construct($finalMessage, $code, $previous, $context);
    }

    /**
     * Factory Method für Security-Events
     */
    public static function fromEvent(SecurityEventInterface $event, string $message = '', int $code = 0): self
    {
        return new self($event, $message, $code);
    }

    /**
     * Erstellt Security-spezifischen Context
     */
    private function createSecurityContext(
        SecurityEventInterface $securityEvent,
        ?ExceptionContext $additionalContext
    ): ExceptionContext {
        $baseContext = ExceptionContext::forOperation(
            'security.' . $securityEvent->getCategory(),
            'Security'
        )->withData([
            'event_type' => $securityEvent->getEventIdentifier(),
            'event_category' => $securityEvent->getCategory(),
            'event_data' => $securityEvent->toArray(),
            'client_ip' => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
            'request_uri' => $_SERVER['REQUEST_URI'] ?? null,
            'request_method' => $_SERVER['REQUEST_METHOD'] ?? null,
            'timestamp' => time()
        ])->withMetadata([
            'security_event' => $securityEvent->getEventIdentifier(),
            'security_level' => $securityEvent->getLogLevel()->value,
            'security_description' => $securityEvent->getDescription(),
            'requires_alert' => $securityEvent->requiresAlert(),
            'event_category' => $securityEvent->getCategory()
        ]);

        // Merge mit zusätzlichem Context falls vorhanden
        if ($additionalContext) {
            $baseContext = $baseContext
                ->withData($additionalContext->data)
                ->withDebug($additionalContext->debug)
                ->withMetadata($additionalContext->metadata);
        }

        return $baseContext;
    }

    /**
     * Gibt Security-Event zurück
     */
    public function getSecurityEvent(): SecurityEventInterface
    {
        return $this->securityEvent;
    }

    /**
     * Gibt Security-Level zurück
     */
    public function getSecurityLevel(): SecurityLogLevel
    {
        return $this->securityEvent->getLogLevel();
    }

    /**
     * Prüft ob Alert erforderlich ist
     */
    public function requiresAlert(): bool
    {
        return $this->securityEvent->requiresAlert();
    }
}
