<?php

declare(strict_types=1);

namespace App\Framework\Http\Middlewares;

use App\Framework\DI\Container;
use App\Framework\Exception\Security\CsrfValidationFailedException;
use App\Framework\Http\HttpMiddleware;
use App\Framework\Http\Method;
use App\Framework\Http\MiddlewareContext;
use App\Framework\Http\MiddlewarePriority;
use App\Framework\Http\MiddlewarePriorityAttribute;
use App\Framework\Http\Next;
use App\Framework\Http\Request;
use App\Framework\Http\RequestStateManager;
use App\Framework\Http\Session\SessionInterface;
use App\Framework\Security\CsrfToken;

#[MiddlewarePriorityAttribute(MiddlewarePriority::SECURITY)] // Explizite Reihenfolge in MiddlewareManager
final readonly class CsrfMiddleware implements HttpMiddleware
{
    public function __construct(
        private Container $container,
    ) {
    }

    public function __invoke(MiddlewareContext $context, Next $next, RequestStateManager $stateManager): MiddlewareContext
    {
        $request = $context->request;

        // Skip CSRF validation for API routes and LiveComponent AJAX endpoints
        // LiveComponents use stateless, component-scoped security model instead
        if (str_starts_with($request->path, '/api/') ||
            str_starts_with($request->path, '/live-component/') ||
            str_starts_with($request->path, '/livecomponent/')) {
            error_log("CsrfMiddleware: Skipping CSRF validation for: " . $request->path);

            return $next($context);
        }

        // Try to get session from container - graceful fallback if not available
        try {
            $session = $this->container->get(SessionInterface::class);
        } catch (\Throwable $e) {
            // Session not available - skip CSRF validation gracefully
            error_log("CsrfMiddleware: Session not available, skipping CSRF validation");

            return $next($context);
        }

        if (in_array($request->method, [Method::POST, Method::PUT, Method::DELETE, Method::PATCH])) {
            $this->validateCsrfToken($request, $session);
        }

        $context = $next($context);

        // After successful request processing, optionally rotate token for enhanced security
        // This can be enabled for high-security applications
        // $this->rotateTokenIfNeeded($request, $session);

        return $context;
    }

    /**
     * Validates CSRF token from the request
     */
    private function validateCsrfToken(Request $request, SessionInterface $session): void
    {
        $formId = $request->parsedBody->get('_form_id') ??
                  $request->headers->getFirst('X-CSRF-Form-ID');
        $tokenValue = $request->parsedBody->get('_token') ??
                      $request->headers->getFirst('X-CSRF-Token');

        // Debug: Log CSRF validation attempt
        error_log("CSRF Debug: Validating tokens for form_id='$formId'");

        if (! $formId || ! $tokenValue) {
            throw CsrfValidationFailedException::missingTokenOrFormId(
                missingFormId: !$formId,
                missingToken: !$tokenValue
            );
        }

        try {
            $token = CsrfToken::fromString($tokenValue);
        } catch (\InvalidArgumentException $e) {
            throw CsrfValidationFailedException::invalidTokenFormat($e->getMessage());
        }

        if (! $session->csrf->validateToken($formId, $token)) {
            error_log("CSRF validation failed for form: " . $formId);

            throw CsrfValidationFailedException::tokenValidationFailed($formId);
        }
    }

    /**
     * Rotates CSRF token after successful form submission for enhanced security
     */
    private function rotateTokenIfNeeded($request): void
    {
        $formId = $request->parsedBody->get('_form_id');
        if ($formId) {
            // Generate a fresh token for the next request
            $this->session->csrf->rotateToken($formId);
        }
    }
}
