<?php

declare(strict_types=1);

namespace App\Framework\View\Processors;

use App\Framework\Template\Processing\StringProcessor;
use App\Framework\View\RenderContext;

/**
 * CSRF-Token-Processor für Cache-sichere CSRF-Token-Behandlung
 *
 * Dieser Processor löst das Problem mit gecachten CSRF-Tokens:
 * 1. Beim Caching: Ersetzt CSRF-Tokens mit Platzhaltern
 * 2. Beim Rendern: Ersetzt Platzhalter mit frischen Tokens
 */
final class CsrfTokenProcessor implements StringProcessor
{
    private const string CSRF_PLACEHOLDER = '__CSRF_TOKEN_PLACEHOLDER__';
    private const string META_PLACEHOLDER = '__CSRF_META_PLACEHOLDER__';
    private const string FIELD_PLACEHOLDER = '__CSRF_FIELD_PLACEHOLDER__';

    public function __construct(
        private readonly bool $cacheMode = false,
        private readonly bool $debugMode = false
    ) {
    }

    public function process(string $html, RenderContext $context): string
    {
        return $html;

        if (! $this->shouldProcessCsrf($context)) {
            return $html;
        }

        if ($this->cacheMode) {
            // Cache-Modus: Ersetze echte Tokens mit Platzhaltern
            return $this->replaceTokensWithPlaceholders($html, $context);
        } else {
            // Render-Modus: Ersetze Platzhalter mit echten Tokens
            return $this->replacePlaceholdersWithTokens($html, $context);
        }
    }

    /**
     * Prüft ob das Template CSRF-Token-Processing benötigt
     */
    private function shouldProcessCsrf(RenderContext $context): bool
    {
        // Check 1: Data enthält CSRF-Token
        if (isset($context->data['csrf_token']) || isset($context->data['_token'])) {
            return true;
        }

        // Check 2: Template-Name deutet auf Formulare hin
        $formKeywords = ['form', 'contact', 'login', 'register', 'checkout', 'admin'];
        foreach ($formKeywords as $keyword) {
            if (str_contains($context->template, $keyword)) {
                return true;
            }
        }

        return false;
    }

    /**
     * Cache-Modus: Ersetzt echte CSRF-Tokens mit Platzhaltern
     */
    private function replaceTokensWithPlaceholders(string $html, RenderContext $context): string
    {
        $this->debugLog("Converting CSRF tokens to placeholders for caching");

        // 1. Ersetze Template-Syntax CSRF-Aufrufe
        $html = $this->replaceTemplateCsrfCalls($html);

        // 2. Ersetze echte Token-Werte falls vorhanden
        if (isset($context->data['csrf_token'])) {
            $html = str_replace($context->data['csrf_token'], self::CSRF_PLACEHOLDER, $html);
        }

        if (isset($context->data['_token'])) {
            $html = str_replace($context->data['_token'], self::CSRF_PLACEHOLDER, $html);
        }

        // 3. Ersetze HTML-Pattern
        $html = $this->replaceHtmlCsrfPatterns($html);

        return $html;
    }

    /**
     * Render-Modus: Ersetzt Platzhalter mit echten CSRF-Tokens
     */
    private function replacePlaceholdersWithTokens(string $html, RenderContext $context): string
    {
        $this->debugLog("Converting placeholders to fresh CSRF tokens");

        $csrfToken = $this->getCsrfToken($context);

        // Ersetze alle Platzhalter-Types
        $replacements = [
            self::CSRF_PLACEHOLDER => $csrfToken,
            self::META_PLACEHOLDER => $csrfToken,
            self::FIELD_PLACEHOLDER => $this->generateCsrfField($csrfToken),
        ];

        return str_replace(array_keys($replacements), array_values($replacements), $html);
    }

    /**
     * Ersetzt Template-Syntax CSRF-Aufrufe mit Platzhaltern
     */
    private function replaceTemplateCsrfCalls(string $html): string
    {
        $patterns = [
            // {{ csrf_token() }} oder {{ csrf_token }}
            '/\{\{\s*csrf_token\(\s*\)\s*\}\}/' => self::CSRF_PLACEHOLDER,
            '/\{\{\s*csrf_token\s*\}\}/' => self::CSRF_PLACEHOLDER,

            // {!! csrf_token() !!}
            '/\{!!\s*csrf_token\(\s*\)\s*!!\}/' => self::CSRF_PLACEHOLDER,

            // {{ _token }}
            '/\{\{\s*_token\s*\}\}/' => self::CSRF_PLACEHOLDER,

            // @csrf Blade-Direktive
            '/@csrf/' => self::FIELD_PLACEHOLDER,

            // csrf_field() Helper
            '/\{\{\s*csrf_field\(\s*\)\s*\}\}/' => self::FIELD_PLACEHOLDER,
            '{!! csrf_field() !!}' => self::FIELD_PLACEHOLDER,
        ];

        return preg_replace(array_keys($patterns), array_values($patterns), $html);
    }

    /**
     * Ersetzt HTML-CSRF-Patterns mit Platzhaltern
     */
    private function replaceHtmlCsrfPatterns(string $html): string
    {
        // Hidden Input Fields mit CSRF-Token
        $html = preg_replace(
            '/<input[^>]*name=["\']_token["\'][^>]*value=["\']([^"\']+)["\'][^>]*>/',
            '<input type="hidden" name="_token" value="' . self::CSRF_PLACEHOLDER . '">',
            $html
        );

        // Meta Tags mit CSRF-Token
        $html = preg_replace(
            '/<meta[^>]*name=["\']csrf-token["\'][^>]*content=["\']([^"\']+)["\'][^>]*>/',
            '<meta name="csrf-token" content="' . self::META_PLACEHOLDER . '">',
            $html
        );

        return $html;
    }

    /**
     * Holt den aktuellen CSRF-Token
     */
    private function getCsrfToken(RenderContext $context): string
    {
        // Priorität: Context-Daten > Generierter Token
        return $context->data['csrf_token'] ??
            $context->data['_token'] ??
            $this->generateFreshCsrfToken();
    }

    /**
     * Generiert einen neuen CSRF-Token
     */
    private function generateFreshCsrfToken(): string
    {
        // Integration mit deinem CSRF-System
        // Das könnte auch ein Service-Call sein
        return bin2hex(random_bytes(32));
    }

    /**
     * Generiert ein komplettes CSRF-Hidden-Field
     */
    private function generateCsrfField(string $token): string
    {
        return '<input type="hidden" name="_token" value="' . $token . '">';
    }

    /**
     * Debug-Logging
     */
    private function debugLog(string $message): void
    {
        // Debug logging removed for production
        // Use proper logger in production environment
    }

    /**
     * Factory-Methode für Cache-Modus
     */
    public static function forCaching(bool $debugMode = false): self
    {
        return new self(cacheMode: true, debugMode: $debugMode);
    }

    /**
     * Factory-Methode für Render-Modus
     */
    public static function forRendering(bool $debugMode = false): self
    {
        return new self(cacheMode: false, debugMode: $debugMode);
    }
}
