<?php
/**
 * MessageBus Security Manager
 *
 * Handles CSRF protection and origin validation for MessageBus
 * communications transparently (developers don't need to think about it).
 *
 * @package FleekDash\Integration
 * @since 2.3.0
 *
 * SECURITY FIX #2: CSRF Protection
 * - Session nonce (auto-generated, auto-validated)
 * - Origin validation (automatic)
 * - Transparent to developers (zero friction)
 */

namespace FleekDash\Integration;

defined('ABSPATH') || exit;

class MessageBusSecurity
{
    /**
     * Session key for MessageBus nonce
     */
    const SESSION_KEY = 'fleekdash_messagebus_nonce';

    /**
     * Nonce lifetime (30 minutes)
     */
    const NONCE_LIFETIME = 1800;

    /**
     * Generate MessageBus session nonce
     *
     * Auto-generates and stores in session.
     * Called automatically when integration page loads.
     *
     * @return string Session nonce
     */
    public static function generateSessionNonce(): string
    {
        // Ensure session is started
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }

        // Generate new nonce
        $nonce = \wp_create_nonce('fleekdash_messagebus_' . \get_current_user_id());

        // Store in session with timestamp
        $_SESSION[self::SESSION_KEY] = [
            'nonce' => $nonce,
            'created' => time(),
            'user_id' => \get_current_user_id(),
        ];

        return $nonce;
    }

    /**
     * Validate MessageBus nonce
     *
     * Called automatically by MessageBus handler.
     * Developers don't need to call this manually.
     *
     * @param string $nonce Nonce to validate
     * @return bool True if valid
     */
    public static function validateNonce(string $nonce): bool
    {
        // Ensure session is started
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }

        // Check if session nonce exists
        if (!isset($_SESSION[self::SESSION_KEY])) {
            return false;
        }

        $session_data = $_SESSION[self::SESSION_KEY];

        // Check expiration
        if ((time() - $session_data['created']) > self::NONCE_LIFETIME) {
            // Expired - clean up
            unset($_SESSION[self::SESSION_KEY]);
            return false;
        }

        // Validate nonce
        $valid = \wp_verify_nonce($nonce, 'fleekdash_messagebus_' . $session_data['user_id']);

        return $valid !== false;
    }

    /**
     * Validate origin (automatic - transparent)
     *
     * @param string $origin Origin header
     * @param string $integration_id Integration ID
     * @return bool True if origin is allowed
     */
    public static function validateOrigin(string $origin, string $integration_id): bool
    {
        // Get allowed origins for this integration
        $allowed_origins = self::getAllowedOrigins($integration_id);

        // Empty origin = same-origin (always allowed)
        if (empty($origin)) {
            return true;
        }

        // Parse origin
        $parsed = parse_url($origin);
        $origin_host = $parsed['host'] ?? '';

        // Check against allowed origins
        foreach ($allowed_origins as $allowed) {
            if ($origin_host === $allowed || $origin === $allowed) {
                return true;
            }
        }

        // Site URL always allowed (same-origin)
        $site_url = parse_url(\site_url(), PHP_URL_HOST);
        if ($origin_host === $site_url) {
            return true;
        }

        return false;
    }

    /**
     * Get allowed origins for integration
     *
     * @param string $integration_id Integration ID
     * @return array List of allowed origins
     */
    private static function getAllowedOrigins(string $integration_id): array
    {
        global $wpdb;

        $table = $wpdb->prefix . 'fleekdash_integration_routes';

        $origins = $wpdb->get_col(
            $wpdb->prepare(
                "SELECT `allowed_origins` FROM `{$table}`
                 WHERE `integration_id` = %s AND `allowed_origins` IS NOT NULL",
                $integration_id
            )
        );

        $allowed = [];
        foreach ($origins as $json) {
            $decoded = json_decode($json, true);
            if (is_array($decoded)) {
                $allowed = array_merge($allowed, $decoded);
            }
        }

        return array_unique($allowed);
    }

    /**
     * Get nonce for frontend (auto-included in page)
     *
     * This is called automatically by IntegrationWindowPage.
     * Developers don't need to call this.
     *
     * @return array Nonce data for frontend
     */
    public static function getNonceForFrontend(): array
    {
        $nonce = self::generateSessionNonce();

        return [
            'nonce' => $nonce,
            'lifetime' => self::NONCE_LIFETIME,
            'user_id' => \get_current_user_id(),
        ];
    }

    /**
     * Clean expired nonces (cron job)
     *
     * @return int Number of cleaned sessions
     */
    public static function cleanExpiredNonces(): int
    {
        if (session_status() === PHP_SESSION_NONE) {
            return 0;
        }

        $cleaned = 0;

        if (isset($_SESSION[self::SESSION_KEY])) {
            $session_data = $_SESSION[self::SESSION_KEY];

            if ((time() - $session_data['created']) > self::NONCE_LIFETIME) {
                unset($_SESSION[self::SESSION_KEY]);
                $cleaned++;
            }
        }

        return $cleaned;
    }
}
