<?php

declare(strict_types=1);

namespace App\Framework\Http\Session;

final readonly class CsrfProtection
{
    private const int TOKEN_LIFETIME = 3600; //1h
    private const int MAX_TOKENS_PER_FORM = 3;

    private Session $session;

    public function __construct(Session $session)
    {
        $this->session = $session;

        // Sicherstellen, dass CSRF-Bereich existiert
        if (!$this->session->has(SessionKey::CSRF->value)) {
            $this->session->set(SessionKey::CSRF->value, []);
        }
    }

    public function generateToken(string $formId): string
    {
        $token = bin2hex(random_bytes(32));
        $csrf = $this->session->get(SessionKey::CSRF->value, []);

        if (!isset($csrf[$formId])) {
            $csrf[$formId] = [];
        }

        $csrf[$formId][] = [
            'token' => $token,
            'created_at' => time()
        ];

        $csrf[$formId] = $this->cleanupOldTokens($csrf[$formId]);

        $this->session->set(SessionKey::CSRF->value, $csrf);

        return $token;
    }

    public function validateToken(string $formId, string $token): bool
    {
        $csrf = $this->session->get(SessionKey::CSRF->value, []);

        if (!isset($csrf[$formId])) {
            return false;
        }

        foreach ($csrf[$formId] as $index => $tokenData) {
            if ($tokenData['token'] === $token) {
                // Token ist gültig - NICHT löschen, nur als "used" markieren
                $csrf[$formId][$index]['used_at'] = time();
                $this->session->set(SessionKey::CSRF->value, $csrf);
                return true;
            }
        }

        return false;
    }

    private function cleanupOldTokens(array $tokens): array
    {
        $now = time();
        $cleaned = [];

        foreach ($tokens as $tokenData) {
            // Behalte Token die:
            // 1. Noch nicht abgelaufen sind
            // 2. Kürzlich verwendet wurden (für Re-Submits)
            $age = $now - $tokenData['created_at'];
            $usedRecently = isset($tokenData['used_at']) &&
                ($now - $tokenData['used_at']) < 300; // 5 Minuten

            if ($age < self::TOKEN_LIFETIME || $usedRecently) {
                $cleaned[] = $tokenData;
            }
        }

        // Behalte nur die neuesten N Tokens
        return array_slice($cleaned, -self::MAX_TOKENS_PER_FORM);
    }

    // Diese Methode nur für spezielle Fälle behalten (z.B. AJAX)
    /*public function renderHiddenFields(string $formId): string
    {
        $token = $this->generateToken($formId);
        return sprintf(
            '<input type="hidden" name="_token" value="%s"><input type="hidden" name="_form_id" value="%s">',
            htmlspecialchars($token),
            htmlspecialchars($formId)
        );
    }*/
}
