<?php

declare(strict_types=1);

namespace App\Framework\Http\Session;

final readonly class SessionFingerprintConfig
{
    /**
     * @param bool $strictMode Ob der strikte Modus aktiviert ist (keine Abweichungen erlaubt)
     * @param bool $userAgent Ob der User-Agent in den Fingerprint einbezogen wird
     * @param bool $acceptLanguage Ob Accept-Language Header einbezogen wird
     * @param bool $acceptEncoding Ob Accept-Encoding Header einbezogen wird
     * @param bool $ipPrefix Ob IP-Prefix (erste 3 Oktette) einbezogen wird
     * @param bool $secChUa Ob Sec-CH-UA Header einbezogen wird (Client Hints)
     * @param bool $dnt Ob DNT (Do Not Track) Header einbezogen wird
     * @param float $similarityThreshold Mindest-Ähnlichkeit im flexiblen Modus (0.0 - 1.0)
     */
    public function __construct(
        public bool $strictMode = false,
        public bool $userAgent = true,
        public bool $acceptLanguage = true,
        public bool $acceptEncoding = false,
        public bool $ipPrefix = true,
        public bool $secChUa = false,
        public bool $dnt = false,
        public float $similarityThreshold = 0.7,
    ) {
        $this->validate();
    }

    /**
     * Factory-Methode für strikte Sicherheit
     */
    public static function strict(): self
    {
        return new self(
            strictMode: true,
            userAgent: true,
            acceptLanguage: true,
            acceptEncoding: true,
            ipPrefix: true,
            secChUa: true,
            dnt: true,
            similarityThreshold: 1.0,
        );
    }

    /**
     * Factory-Methode für ausgewogene Sicherheit (Standard)
     */
    public static function balanced(): self
    {
        return new self(
            strictMode: false,
            userAgent: true,
            acceptLanguage: true,
            acceptEncoding: false, // Kann sich bei Proxies ändern
            ipPrefix: true,
            secChUa: false,
            dnt: false,
            similarityThreshold: 0.7,
        );
    }

    /**
     * Factory-Methode für minimale Sicherheit (höchste Kompatibilität)
     */
    public static function minimal(): self
    {
        return new self(
            strictMode: false,
            userAgent: true,
            acceptLanguage: false,
            acceptEncoding: false,
            ipPrefix: false, // Für mobile Nutzer mit wechselnden IPs
            secChUa: false,
            dnt: false,
            similarityThreshold: 0.5,
        );
    }

    /**
     * Factory-Methode für API-Clients
     */
    public static function forApi(): self
    {
        return new self(
            strictMode: true,
            userAgent: true, // API Client identifier
            acceptLanguage: false,
            acceptEncoding: false,
            ipPrefix: true,
            secChUa: false,
            dnt: false,
            similarityThreshold: 1.0,
        );
    }

    /**
     * Konvertiert zu Array für SessionFingerprint
     */
    public function toComponentArray(): array
    {
        return [
            'user_agent' => $this->userAgent,
            'accept_language' => $this->acceptLanguage,
            'accept_encoding' => $this->acceptEncoding,
            'ip_prefix' => $this->ipPrefix,
            'sec_ch_ua' => $this->secChUa,
            'dnt' => $this->dnt,
        ];
    }

    /**
     * Gibt die Anzahl der aktiven Komponenten zurück
     */
    public function getActiveComponentCount(): int
    {
        $count = 0;
        foreach ($this->toComponentArray() as $enabled) {
            if ($enabled) {
                $count++;
            }
        }

        return $count;
    }

    /**
     * Validiert die Konfiguration
     */
    private function validate(): void
    {
        if ($this->similarityThreshold < 0.0 || $this->similarityThreshold > 1.0) {
            throw new \InvalidArgumentException(
                'Similarity threshold must be between 0.0 and 1.0'
            );
        }

        // Im strikten Modus sollte mindestens eine Komponente aktiv sein
        if ($this->strictMode && $this->getActiveComponentCount() === 0) {
            throw new \InvalidArgumentException(
                'At least one component must be enabled in strict mode'
            );
        }

        // Im strikten Modus sollte der Threshold 1.0 sein
        if ($this->strictMode && $this->similarityThreshold < 1.0) {
            throw new \InvalidArgumentException(
                'Similarity threshold should be 1.0 in strict mode'
            );
        }
    }

    /**
     * Erstellt eine Konfiguration aus Umgebungsvariablen
     */
    public static function fromEnvironment(): self
    {
        $strictMode = filter_var(
            $_ENV['SESSION_FINGERPRINT_STRICT'] ?? false,
            FILTER_VALIDATE_BOOLEAN
        );

        // Default threshold abhängig vom Modus
        $defaultThreshold = $strictMode ? 1.0 : 0.7;
        $threshold = isset($_ENV['SESSION_FINGERPRINT_THRESHOLD'])
            ? (float) $_ENV['SESSION_FINGERPRINT_THRESHOLD']
            : $defaultThreshold;

        // Auto-Korrektur: Im strict mode MUSS threshold 1.0 sein
        if ($strictMode && $threshold < 1.0) {
            $threshold = 1.0;
        }

        // Basis-Konfiguration basierend auf Modus
        $config = $strictMode ? self::strict() : self::balanced();

        // Überschreibe mit spezifischen Env-Vars wenn vorhanden
        return new self(
            strictMode: $strictMode,
            userAgent: filter_var(
                $_ENV['SESSION_FINGERPRINT_USER_AGENT'] ?? $config->userAgent,
                FILTER_VALIDATE_BOOLEAN
            ),
            acceptLanguage: filter_var(
                $_ENV['SESSION_FINGERPRINT_ACCEPT_LANGUAGE'] ?? $config->acceptLanguage,
                FILTER_VALIDATE_BOOLEAN
            ),
            acceptEncoding: filter_var(
                $_ENV['SESSION_FINGERPRINT_ACCEPT_ENCODING'] ?? $config->acceptEncoding,
                FILTER_VALIDATE_BOOLEAN
            ),
            ipPrefix: filter_var(
                $_ENV['SESSION_FINGERPRINT_IP_PREFIX'] ?? $config->ipPrefix,
                FILTER_VALIDATE_BOOLEAN
            ),
            secChUa: filter_var(
                $_ENV['SESSION_FINGERPRINT_SEC_CH_UA'] ?? $config->secChUa,
                FILTER_VALIDATE_BOOLEAN
            ),
            dnt: filter_var(
                $_ENV['SESSION_FINGERPRINT_DNT'] ?? $config->dnt,
                FILTER_VALIDATE_BOOLEAN
            ),
            similarityThreshold: $threshold,
        );
    }
}
