<?php
/**
 * Smart Authentication Middleware
 *
 * Provides robust, performance-optimized authentication for API routes
 * with intelligent caching and fallback mechanisms.
 *
 * @package FleekDash\Core
 */

namespace FleekDash\Core;

// Load debug helpers
if (!function_exists('fleekdash_log_debug')) {
    require_once FLEEKDASH_PLUGIN_DIR . 'includes/Core/debug-helpers.php';
}

class AuthMiddleware
{
    /**
     * Cache group for authentication checks
     */
    const CACHE_GROUP = 'fleekdash_auth';

    /**
     * Cache TTL for authentication checks (seconds)
     */
    const CACHE_TTL = 300; // 5 minutes

    /**
     * Security levels for routes
     */
    const SECURITY_LEVELS = [
        'public' => null,
        'authenticated' => 'read',
        'editor' => 'edit_posts',
        'admin' => 'manage_options',
        'super_admin' => 'manage_network'
    ];

    /**
     * Check authentication with intelligent caching
     *
     * @param string $security_level Security level required
     * @param bool $use_cache Whether to use caching
     * @return array Authentication result
     */
    public static function check($security_level = 'authenticated', $use_cache = true)
    {
        // Public routes don't need authentication
        if ($security_level === 'public') {
            return [
                'authenticated' => true,
                'user_id' => 0,
                'level' => 'public',
                'cached' => false
            ];
        }

        $user_id = \get_current_user_id();
        $required_capability = self::SECURITY_LEVELS[$security_level] ?? 'read';

        // Check cache first (performance optimization)
        if ($use_cache) {
            $cache_key = "auth_check_{$user_id}_{$security_level}";
            $cached_result = wp_cache_get($cache_key, self::CACHE_GROUP);

            if ($cached_result !== false) {
                $cached_result['cached'] = true;
                return $cached_result;
            }
        }

        // Perform authentication check
        $result = self::performAuthCheck($user_id, $required_capability, $security_level);

        // Cache the result (only cache successful authentications)
        if ($use_cache && $result['authenticated']) {
            wp_cache_set("auth_check_{$user_id}_{$security_level}", $result, self::CACHE_GROUP, self::CACHE_TTL);
        }

        $result['cached'] = false;
        return $result;
    }

    /**
     * Perform the actual authentication check
     *
     * @param int $user_id Current user ID
     * @param string $required_capability Required capability
     * @param string $security_level Security level
     * @return array<string, mixed> Authentication result
     */
    private static function performAuthCheck(int $user_id, string $required_capability, string $security_level): array
    {
        // For REST API requests, try to authenticate via cookie if user_id is 0
        if (defined('REST_REQUEST') && REST_REQUEST && $user_id === 0) {
            $cookie_hash = COOKIEHASH;
            if (isset($_COOKIE["wordpress_logged_in_{$cookie_hash}"])) {
                $authenticated_user_id = wp_validate_auth_cookie('', 'logged_in');
                if ($authenticated_user_id) {
                    $user_id = $authenticated_user_id;
                    // Set the current user for this request
                    \wp_set_current_user($user_id);
                }
            }
        }

        $result = [
            'authenticated' => false,
            'user_id' => 0,
            'level' => $security_level,
            'reason' => 'not_logged_in',
            'error_code' => 401,
        ];

        if (\is_user_logged_in() && $user_id > 0) {
            $result = self::buildLoggedInAuthResult($user_id, $required_capability, $security_level);
        }

        return $result;
    }

    private static function buildLoggedInAuthResult(int $user_id, string $required_capability, string $security_level): array
    {
        $result = [
            'authenticated' => false,
            'user_id' => $user_id,
            'level' => $security_level,
            'reason' => 'insufficient_permissions',
            'error_code' => 403,
        ];

        if (\current_user_can($required_capability)) {
            $result = [
                'authenticated' => false,
                'user_id' => $user_id,
                'level' => $security_level,
                'reason' => 'invalid_session',
                'error_code' => 401,
            ];
            if (self::isSessionValid()) {
                $result = [
                    'authenticated' => true,
                    'user_id' => $user_id,
                    'level' => $security_level,
                    'capabilities' => self::getUserCapabilities($user_id),
                    'error_code' => null,
                ];
            }
        }

        return $result;
    }

    /**
     * Check if the current session is valid
     *
     * @return bool
     */
    private static function isSessionValid()
    {
        $token = \wp_get_session_token();

        return \is_string($token) && $token !== '';
    }

    /**
     * Get user capabilities for caching
     *
     * @param int $user_id User ID
     * @return array User capabilities
     */
    private static function getUserCapabilities(int $user_id): array
    {
        $user = \get_user_by('id', $user_id);
        if (!$user) {
            return [];
        }

        return array_keys($user->allcaps);
    }

    /**
     * Create a permission callback for WordPress REST API
     *
     * @param string $security_level Security level required
     * @return callable Permission callback
     */
    public static function createPermissionCallback($security_level = 'authenticated')
    {
        return function () use ($security_level) {
            $auth_result = self::check($security_level);

            if (!$auth_result['authenticated']) {
                // Log failed authentication attempts (security)
                fleekdash_log_error(sprintf(
                    'FleekDash Auth Failed: User %d, Level %s, Reason: %s',
                    $auth_result['user_id'],
                    $security_level,
                    $auth_result['reason'],
                ));
            }

            return $auth_result['authenticated'];
        };
    }

    /**
     * WordPress REST permission callback for routes that require a logged-in user.
     *
     * @return bool
     */
    public static function createAuthenticatedPermissionCallback(?\WP_REST_Request $request = null): bool
    {
        $callback = self::createPermissionCallback('authenticated');

        return (bool) $callback() && self::validateRestNonceForMutation($request);
    }

    /**
     * WordPress REST permission callback for routes that require manage_options.
     *
     * @return bool
     */
    public static function createAdminPermissionCallback(?\WP_REST_Request $request = null): bool
    {
        $callback = self::createPermissionCallback('admin');

        return (bool) $callback() && self::validateRestNonceForMutation($request);
    }

    /**
     * Require a valid REST nonce for state-changing cookie-authenticated requests.
     * The refresh endpoint is intentionally exempt because it exists to recover from stale nonces
     * after confirming the WordPress logged-in cookie through AuthMiddleware.
     */
    public static function validateRestNonceForMutation(?\WP_REST_Request $request): bool
    {
        $valid = true;
        if ($request instanceof \WP_REST_Request) {
            $method = \strtoupper($request->get_method());
            $needs_nonce = \in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'], true)
                && $request->get_route() !== '/fleekdash/v1/auth/refresh-nonce';
            if ($needs_nonce) {
                $nonce = (string) ($request->get_header('X-WP-Nonce') ?: $request->get_param('_wpnonce'));
                $valid = $nonce !== '' && (bool) \wp_verify_nonce($nonce, 'wp_rest');
            }
        }

        return $valid;
    }

    /**
     * Clear authentication cache for a user
     *
     * @param int $user_id User ID (0 for current user)
     */
    public static function clearUserCache($user_id = 0)
    {
        if ($user_id === 0) {
            $user_id = \get_current_user_id();
        }

        foreach (array_keys(self::SECURITY_LEVELS) as $level) {
            wp_cache_delete("auth_check_{$user_id}_{$level}", self::CACHE_GROUP);
        }
    }

    /**
     * Clear all authentication cache
     */
    public static function clearAllCache()
    {
        wp_cache_flush_group(self::CACHE_GROUP);
    }
}
