<?php
/**
 * GGM Commerce MCP Authentication
 *
 * Handles API key authentication and rate limiting for MCP requests
 *
 * @package GGM_Commerce
 * @since 1.0.0
 */

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

class GGM_Commerce_MCP_Auth {

    /**
     * Option name for storing API keys
     * @var string
     */
    const API_KEYS_OPTION = 'ggm_commerce_mcp_api_keys';

    /**
     * Rate limit: requests per minute
     * @var int
     */
    const RATE_LIMIT = 100;

    /**
     * Authenticate request
     *
     * @param WP_REST_Request $request WordPress REST request
     * @return bool|WP_Error
     */
    public function authenticate_request($request) {
        // Check if authentication is disabled (for local development)
        if (get_option('ggm_mcp_disable_auth', false)) {
            return true;
        }

        $api_key = null;

        // Method 1: Check Authorization header (Bearer token)
        $auth_header = $request->get_header('Authorization');
        if (!empty($auth_header) && strpos($auth_header, 'Bearer ') === 0) {
            $api_key = substr($auth_header, 7);
        }

        // Method 2: Check query parameter (for Claude Desktop / mcp-remote)
        if (empty($api_key)) {
            $api_key = $request->get_param('api_key');
        }

        // Method 3: Check X-API-Key header
        if (empty($api_key)) {
            $api_key = $request->get_header('X-API-Key');
        }

        if (empty($api_key)) {
            return new WP_Error(
                'no_auth',
                'API key required. Provide via Authorization header (Bearer token), api_key query parameter, or X-API-Key header.',
                ['status' => 401]
            );
        }

        return $this->validate_api_key($api_key);
    }

    /**
     * Validate API key
     *
     * @param string $api_key The API key to validate
     * @return bool|WP_Error
     */
    private function validate_api_key($api_key) {
        if (empty($api_key)) {
            return new WP_Error(
                'empty_key',
                'API key cannot be empty',
                ['status' => 401]
            );
        }

        // Get stored API keys
        $stored_keys = get_option(self::API_KEYS_OPTION, []);

        if (!isset($stored_keys[$api_key])) {
            return new WP_Error(
                'invalid_key',
                'Invalid API key',
                ['status' => 401]
            );
        }

        $key_data = $stored_keys[$api_key];

        // Check if key is active
        if (isset($key_data['status']) && $key_data['status'] !== 'active') {
            return new WP_Error(
                'inactive_key',
                'API key is not active',
                ['status' => 401]
            );
        }

        // Check expiration
        if (isset($key_data['expires']) && time() > $key_data['expires']) {
            return new WP_Error(
                'expired_key',
                'API key has expired',
                ['status' => 401]
            );
        }

        // Check rate limit
        $rate_limit_check = $this->check_rate_limit($api_key);
        if (is_wp_error($rate_limit_check)) {
            return $rate_limit_check;
        }

        // Update last used timestamp
        $this->update_last_used($api_key);

        // Store key data in request for later use
        // This allows other functions to access permissions, user info, etc.
        return true;
    }

    /**
     * Check rate limit for API key
     *
     * @param string $api_key The API key
     * @return bool|WP_Error
     */
    private function check_rate_limit($api_key) {
        $key = 'mcp_rate_limit_' . md5($api_key);
        $requests = get_transient($key);

        if ($requests === false) {
            // First request in this minute
            set_transient($key, 1, MINUTE_IN_SECONDS);
            return true;
        }

        if ($requests >= self::RATE_LIMIT) {
            return new WP_Error(
                'rate_limit_exceeded',
                'Rate limit exceeded. Maximum ' . self::RATE_LIMIT . ' requests per minute.',
                ['status' => 429]
            );
        }

        // Increment counter
        set_transient($key, $requests + 1, MINUTE_IN_SECONDS);
        return true;
    }

    /**
     * Update last used timestamp for API key
     *
     * @param string $api_key The API key
     */
    private function update_last_used($api_key) {
        $stored_keys = get_option(self::API_KEYS_OPTION, []);

        if (isset($stored_keys[$api_key])) {
            $stored_keys[$api_key]['last_used'] = time();
            $stored_keys[$api_key]['request_count'] = isset($stored_keys[$api_key]['request_count'])
                ? $stored_keys[$api_key]['request_count'] + 1
                : 1;

            update_option(self::API_KEYS_OPTION, $stored_keys);
        }
    }

    /**
     * Generate a new API key
     *
     * @param string $name Descriptive name for the API key
     * @param array $capabilities Permissions for this key
     * @param int $expires_in_days Days until expiration (0 = never expires)
     * @return string|WP_Error The generated API key or error
     */
    public function generate_api_key($name, $capabilities = [], $expires_in_days = 365) {
        if (empty($name)) {
            return new WP_Error('invalid_name', 'API key name is required');
        }

        // Generate secure random key
        $api_key = $this->generate_secure_key();

        // Get existing keys
        $stored_keys = get_option(self::API_KEYS_OPTION, []);

        // Prepare key data
        $key_data = [
            'name' => sanitize_text_field($name),
            'capabilities' => $capabilities,
            'created' => time(),
            'status' => 'active',
            'request_count' => 0,
            'last_used' => null
        ];

        // Set expiration if specified
        if ($expires_in_days > 0) {
            $key_data['expires'] = time() + ($expires_in_days * DAY_IN_SECONDS);
        }

        // Store key
        $stored_keys[$api_key] = $key_data;
        update_option(self::API_KEYS_OPTION, $stored_keys);

        return $api_key;
    }

    /**
     * Revoke an API key
     *
     * @param string $api_key The API key to revoke
     * @return bool True if revoked, false if not found
     */
    public function revoke_api_key($api_key) {
        $stored_keys = get_option(self::API_KEYS_OPTION, []);

        if (!isset($stored_keys[$api_key])) {
            return false;
        }

        unset($stored_keys[$api_key]);
        update_option(self::API_KEYS_OPTION, $stored_keys);

        return true;
    }

    /**
     * Get all API keys (without exposing the actual keys)
     *
     * @return array List of API key information
     */
    public function get_all_api_keys() {
        $stored_keys = get_option(self::API_KEYS_OPTION, []);
        $keys_info = [];

        foreach ($stored_keys as $key => $data) {
            // Show only last 8 characters of the key
            $masked_key = '****' . substr($key, -8);

            $keys_info[] = [
                'key_preview' => $masked_key,
                'full_key_hash' => md5($key), // For identification purposes
                'name' => $data['name'],
                'created' => $data['created'],
                'expires' => isset($data['expires']) ? $data['expires'] : null,
                'status' => $data['status'],
                'request_count' => isset($data['request_count']) ? $data['request_count'] : 0,
                'last_used' => isset($data['last_used']) ? $data['last_used'] : null,
                'capabilities' => isset($data['capabilities']) ? $data['capabilities'] : []
            ];
        }

        return $keys_info;
    }

    /**
     * Update API key status
     *
     * @param string $key_hash MD5 hash of the key
     * @param string $status New status (active/inactive)
     * @return bool True if updated, false if not found
     */
    public function update_api_key_status($key_hash, $status) {
        $stored_keys = get_option(self::API_KEYS_OPTION, []);

        foreach ($stored_keys as $key => $data) {
            if (md5($key) === $key_hash) {
                $stored_keys[$key]['status'] = $status;
                update_option(self::API_KEYS_OPTION, $stored_keys);
                return true;
            }
        }

        return false;
    }

    /**
     * Generate a secure random API key
     *
     * @return string 64-character hex string
     */
    private function generate_secure_key() {
        return bin2hex(random_bytes(32));
    }

    /**
     * Clear rate limit for an API key (useful for testing)
     *
     * @param string $api_key The API key
     */
    public function clear_rate_limit($api_key) {
        $key = 'mcp_rate_limit_' . md5($api_key);
        delete_transient($key);
    }
}
