<?php
/**
 * CSRF Protection Middleware for Ton Sui Mining
 * 
 * Provides Cross-Site Request Forgery (CSRF) protection for all state-changing requests
 */

class CsrfMiddleware {
    // CSRF token name
    private const CSRF_TOKEN_NAME = 'csrf_token';
    
    // CSRF token lifetime in seconds (30 minutes)
    private const CSRF_TOKEN_LIFETIME = 1800;
    
    /**
     * Generate a new CSRF token
     * 
     * @return string The generated CSRF token
     */
    public static function generateCsrfToken() {
        if (empty($_SESSION[self::CSRF_TOKEN_NAME])) {
            $_SESSION[self::CSRF_TOKEN_NAME] = bin2hex(random_bytes(32));
            $_SESSION[self::CSRF_TOKEN_NAME . '_time'] = time();
        } elseif (time() - $_SESSION[self::CSRF_TOKEN_NAME . '_time'] > self::CSRF_TOKEN_LIFETIME) {
            // Regenerate token if expired
            $_SESSION[self::CSRF_TOKEN_NAME] = bin2hex(random_bytes(32));
            $_SESSION[self::CSRF_TOKEN_NAME . '_time'] = time();
        }
        
        return $_SESSION[self::CSRF_TOKEN_NAME];
    }
    
    /**
     * Validate a CSRF token
     * 
     * @param string $token The token to validate
     * @return bool True if valid, false otherwise
     */
    public static function validateCsrfToken($token) {
        if (empty($_SESSION[self::CSRF_TOKEN_NAME]) || empty($token)) {
            return false;
        }
        
        // Check if token is expired
        if (time() - $_SESSION[self::CSRF_TOKEN_NAME . '_time'] > self::CSRF_TOKEN_LIFETIME) {
            self::regenerateCsrfToken();
            return false;
        }
        
        // Use hash_equals for timing attack prevention
        return hash_equals($_SESSION[self::CSRF_TOKEN_NAME], $token);
    }
    
    /**
     * Regenerate CSRF token
     */
    public static function regenerateCsrfToken() {
        unset($_SESSION[self::CSRF_TOKEN_NAME]);
        unset($_SESSION[self::CSRF_TOKEN_NAME . '_time']);
        return self::generateCsrfToken();
    }
    
    /**
     * Get the current CSRF token
     * 
     * @return string The current CSRF token
     */
    public static function getCsrfToken() {
        return self::generateCsrfToken();
    }
    
    /**
     * Get CSRF token meta tag for HTML forms
     * 
     * @return string HTML meta tag with CSRF token
     */
    public static function getCsrfMetaTag() {
        return sprintf(
            '<meta name="csrf-token" content="%s">',
            htmlspecialchars(self::getCsrfToken(), ENT_QUOTES, 'UTF-8')
        );
    }
    
    /**
     * Get CSRF token as a hidden input field for forms
     * 
     * @return string HTML input field with CSRF token
     */
    public static function getCsrfField() {
        return sprintf(
            '<input type="hidden" name="_csrf" value="%s">',
            htmlspecialchars(self::getCsrfToken(), ENT_QUOTES, 'UTF-8')
        );
    }
    
    /**
     * Middleware to validate CSRF token for requests
     * 
     * @param callable $next The next middleware/handler to call
     * @return mixed
     */
    public static function handle($next) {
        // Skip CSRF check for safe methods
        if (in_array($_SERVER['REQUEST_METHOD'], ['GET', 'HEAD', 'OPTIONS'])) {
            return $next();
        }
        
        // Get token from POST or header
        $token = $_POST['_csrf'] ?? $_SERVER['HTTP_X_CSRF_TOKEN'] ?? '';
        
        if (!self::validateCsrfToken($token)) {
            http_response_code(403);
            header('Content-Type: application/json');
            echo json_encode([
                'success' => false, 
                'message' => 'Invalid or expired CSRF token',
                'code' => 'invalid_csrf_token'
            ]);
            exit;
        }
        
        // Token is valid, proceed to next middleware/handler
        return $next();
    }
}

// Apply CSRF protection to all requests
if (!defined('DISABLE_CSRF_PROTECTION')) {
    // This will be called when the file is included
    if (session_status() === PHP_SESSION_NONE) {
        session_start();
    }
    
    // Generate CSRF token if it doesn't exist
    CsrfMiddleware::generateCsrfToken();
}
