<?php

namespace Licenzo\Http\Middleware;

// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}

use Licenzo\Framework\Http\Request;
use Licenzo\Framework\Http\Response;
use Licenzo\Helper\Security;

class VerifyCsrfToken
{
    /**
     * The URIs that should be excluded from CSRF verification.
     */
    protected array $except = [
        'licenzo/v1/activation',
        'licenzo/v1/activation/*',
        'licenzo/v1/public/*',
    ];

    /**
     * Handle an incoming request.
     */
    public function handle(Request $request, \Closure $next)
    {
        // Skip CSRF verification for GET, HEAD, OPTIONS requests
        if ($this->isReading($request)) {
            return $next($request);
        }

        // Skip CSRF verification for excluded URIs
        if ($this->inExceptArray($request)) {
            return $next($request);
        }

        // Verify CSRF token for state-changing requests
        if (!$this->tokensMatch($request)) {
            return new Response([
                'success' => false,
                'message' => 'CSRF token mismatch',
                'error' => 'Invalid or missing CSRF token'
            ], 419); // 419 Authentication Timeout (Laravel convention for CSRF)
        }

        return $next($request);
    }

    /**
     * Determine if the HTTP request uses a 'read' verb.
     */
    protected function isReading(Request $request): bool
    {
        return in_array($request->getMethod(), ['HEAD', 'GET', 'OPTIONS']);
    }

    /**
     * Determine if the request has a URI that should pass through CSRF verification.
     */
    protected function inExceptArray(Request $request): bool
    {
        $uri = $request->getUri();

        foreach ($this->except as $except) {
            if ($except !== '/') {
                $except = trim($except, '/');
            }

            // Handle wildcard patterns
            if (str_contains($except, '*')) {
                $pattern = str_replace('*', '.*', preg_quote($except, '#'));
                if (preg_match('#^' . $pattern . '$#', $uri)) {
                    return true;
                }
            } elseif ($uri === $except) {
                return true;
            }
        }

        return false;
    }

    /**
     * Determine if the session and input CSRF tokens match.
     */
    protected function tokensMatch(Request $request): bool
    {
        $token = $this->getTokenFromRequest($request);

        if (!$token) {
            return false;
        }

        // Use WordPress nonce verification
        return Security::verifyNonce($token, $this->getNonceAction($request));
    }

    /**
     * Get the CSRF token from the request.
     */
    protected function getTokenFromRequest(Request $request): ?string
    {
        // Try to get token from various sources
        $token = $request->input('_token')
               ?? $request->input('_wpnonce')
               ?? $request->header('X-CSRF-TOKEN')
               ?? $request->header('X-WP-Nonce');

        return $token;
    }

    /**
     * Get the nonce action for the request.
     */
    protected function getNonceAction(Request $request): string
    {
        // Use route-specific nonce action if available
        $uri = $request->getUri();
        $method = $request->getMethod();

        // Create a unique action based on route and method
        return 'licenzo_' . strtolower($method) . '_' . str_replace(['/', '-'], '_', $uri);
    }

    /**
     * Add the CSRF token to the response headers.
     */
    public function addCsrfTokenToResponse(Response $response, Request $request): Response
    {
        $action = $this->getNonceAction($request);
        $token = wp_create_nonce($action);

        $response->header('X-CSRF-TOKEN', $token);
        $response->header('X-WP-Nonce', $token);

        return $response;
    }

    /**
     * Generate a CSRF token for a specific action.
     */
    public static function generateToken(string $action = 'licenzo_csrf'): string
    {
        return wp_create_nonce($action);
    }

    /**
     * Verify a CSRF token for a specific action.
     */
    public static function verifyToken(string $token, string $action = 'licenzo_csrf'): bool
    {
        return Security::verifyNonce($token, $action);
    }
}
