<?php

declare(strict_types=1);

namespace App\Framework\Logging\ValueObjects;

use App\Framework\Exception\SecurityLogLevel;

/**
 * Security Context Value Object für strukturiertes Security-Event-Logging
 *
 * Kapselt alle Security-relevanten Informationen für OWASP-konforme Logs.
 * Nutzt Framework's SecurityLogLevel aus Exception-Modul.
 *
 * OWASP Logging Cheat Sheet:
 * https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html
 */
final readonly class SecurityContext
{
    /**
     * @param string $eventId OWASP Event Identifier (z.B. "AUTHN_login_failure")
     * @param SecurityLogLevel $level Security-spezifisches Log Level
     * @param string $description Human-readable Event-Beschreibung
     * @param string|null $category Event-Kategorie (z.B. "authentication", "authorization")
     * @param bool $requiresAlert Ob Event ein Security-Alert triggern soll
     * @param string|null $userId Betroffene User-ID (falls zutreffend)
     * @param string|null $username Betroffener Username (falls zutreffend)
     * @param string|null $sourceIp Source IP-Adresse
     * @param string|null $userAgent User-Agent String
     * @param array<string, mixed> $eventData Zusätzliche Event-spezifische Daten
     */
    public function __construct(
        public string $eventId,
        public SecurityLogLevel $level,
        public string $description,
        public ?string $category = null,
        public bool $requiresAlert = false,
        public ?string $userId = null,
        public ?string $username = null,
        public ?string $sourceIp = null,
        public ?string $userAgent = null,
        public array $eventData = []
    ) {
    }

    /**
     * Factory: Erstellt SecurityContext für Authentication-Events
     */
    public static function forAuthentication(
        string $eventId,
        string $description,
        SecurityLogLevel $level = SecurityLogLevel::WARN,
        bool $requiresAlert = false,
        array $eventData = []
    ): self {
        return new self(
            eventId: $eventId,
            level: $level,
            description: $description,
            category: 'authentication',
            requiresAlert: $requiresAlert,
            eventData: $eventData
        );
    }

    /**
     * Factory: Erstellt SecurityContext für Authorization-Events
     */
    public static function forAuthorization(
        string $eventId,
        string $description,
        SecurityLogLevel $level = SecurityLogLevel::WARN,
        bool $requiresAlert = false,
        array $eventData = []
    ): self {
        return new self(
            eventId: $eventId,
            level: $level,
            description: $description,
            category: 'authorization',
            requiresAlert: $requiresAlert,
            eventData: $eventData
        );
    }

    /**
     * Factory: Erstellt SecurityContext für Input-Validation-Events
     */
    public static function forInputValidation(
        string $eventId,
        string $description,
        SecurityLogLevel $level = SecurityLogLevel::WARN,
        bool $requiresAlert = false,
        array $eventData = []
    ): self {
        return new self(
            eventId: $eventId,
            level: $level,
            description: $description,
            category: 'input_validation',
            requiresAlert: $requiresAlert,
            eventData: $eventData
        );
    }

    /**
     * Factory: Erstellt SecurityContext für Session-Events
     */
    public static function forSession(
        string $eventId,
        string $description,
        SecurityLogLevel $level = SecurityLogLevel::INFO,
        bool $requiresAlert = false,
        array $eventData = []
    ): self {
        return new self(
            eventId: $eventId,
            level: $level,
            description: $description,
            category: 'session',
            requiresAlert: $requiresAlert,
            eventData: $eventData
        );
    }

    /**
     * Factory: Erstellt SecurityContext für Intrusion-Detection-Events
     */
    public static function forIntrusion(
        string $eventId,
        string $description,
        SecurityLogLevel $level = SecurityLogLevel::FATAL,
        bool $requiresAlert = true,
        array $eventData = []
    ): self {
        return new self(
            eventId: $eventId,
            level: $level,
            description: $description,
            category: 'intrusion_detection',
            requiresAlert: $requiresAlert,
            eventData: $eventData
        );
    }

    /**
     * Setzt User-Informationen
     */
    public function withUser(?string $userId, ?string $username = null): self
    {
        return new self(
            eventId: $this->eventId,
            level: $this->level,
            description: $this->description,
            category: $this->category,
            requiresAlert: $this->requiresAlert,
            userId: $userId,
            username: $username,
            sourceIp: $this->sourceIp,
            userAgent: $this->userAgent,
            eventData: $this->eventData
        );
    }

    /**
     * Setzt Request-Informationen
     */
    public function withRequestInfo(?string $sourceIp, ?string $userAgent = null): self
    {
        return new self(
            eventId: $this->eventId,
            level: $this->level,
            description: $this->description,
            category: $this->category,
            requiresAlert: $this->requiresAlert,
            userId: $this->userId,
            username: $this->username,
            sourceIp: $sourceIp,
            userAgent: $userAgent,
            eventData: $this->eventData
        );
    }

    /**
     * Fügt Event-Daten hinzu
     */
    public function withEventData(array $eventData): self
    {
        return new self(
            eventId: $this->eventId,
            level: $this->level,
            description: $this->description,
            category: $this->category,
            requiresAlert: $this->requiresAlert,
            userId: $this->userId,
            username: $this->username,
            sourceIp: $this->sourceIp,
            userAgent: $this->userAgent,
            eventData: array_merge($this->eventData, $eventData)
        );
    }

    /**
     * Konvertiert zu Array für Serialisierung
     */
    public function toArray(): array
    {
        $result = [
            'event_id' => $this->eventId,
            'level' => $this->level->value,
            'description' => $this->description,
        ];

        if ($this->category !== null) {
            $result['category'] = $this->category;
        }

        if ($this->requiresAlert) {
            $result['requires_alert'] = true;
        }

        if ($this->userId !== null) {
            $result['user_id'] = $this->userId;
        }

        if ($this->username !== null) {
            $result['username'] = $this->username;
        }

        if ($this->sourceIp !== null) {
            $result['source_ip'] = $this->sourceIp;
        }

        if ($this->userAgent !== null) {
            $result['user_agent'] = $this->userAgent;
        }

        if (!empty($this->eventData)) {
            $result['event_data'] = $this->eventData;
        }

        return $result;
    }
}
