<?php
/**
 * Cloudflare AJAX Handlers
 * 
 * Handles all AJAX requests for Cloudflare Worker deployment wizard.
 * 
 * @package suspended-cloud/staticq
 * @since 2.1.0
 */

if (!defined('ABSPATH')) {
    exit;
}

if (class_exists('SQMedia_Cloudflare_Ajax')) {
    return;
}

class SQMedia_Cloudflare_Ajax {
    
    /**
     * Initialize AJAX handlers
     */
    public static function init(): void {
        // Verify token and get account info
        add_action('wp_ajax_sqmedia_cf_verify_token', [self::class, 'verify_token']);
        
        // Verify AND save token to database
        add_action('wp_ajax_sqmedia_cf_verify_and_save_token', [self::class, 'verify_and_save_token']);
        
        // Verify token from wp-config.php constant
        add_action('wp_ajax_sqmedia_cf_verify_config_token', [self::class, 'verify_config_token']);
        
        // Get zones for dropdown
        add_action('wp_ajax_sqmedia_cf_get_zones', [self::class, 'get_zones']);
        
        // Get existing buckets for dropdown
        add_action('wp_ajax_sqmedia_cf_get_buckets', [self::class, 'get_buckets']);
        
        // Check availability (worker name, bucket name, route)
        add_action('wp_ajax_sqmedia_cf_check_availability', [self::class, 'check_availability']);
        
        // Run pre-flight checks
        add_action('wp_ajax_sqmedia_cf_preflight', [self::class, 'preflight_checks']);
        
        // Deploy worker
        add_action('wp_ajax_sqmedia_cf_deploy', [self::class, 'deploy']);
        
        // Test deployment
        add_action('wp_ajax_sqmedia_cf_test', [self::class, 'test_deployment']);
        
        // Undeploy/remove worker
        add_action('wp_ajax_sqmedia_cf_undeploy', [self::class, 'undeploy']);
        
        // Clear local settings only (no API calls)
        add_action('wp_ajax_sqmedia_cf_clear_local', [self::class, 'clear_local']);
        
        // Save token
        add_action('wp_ajax_sqmedia_cf_save_token', [self::class, 'save_token']);
        
        // Get deployment status
        add_action('wp_ajax_sqmedia_cf_status', [self::class, 'get_status']);
    }
    
    /**
     * Verify user has permission
     */
    private static function verify_permission(): bool {
        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => __('Permission denied.', 'staticq-media')], 403);
            return false;
        }
        return true;
    }
    
    /**
     * Verify nonce
     */
    private static function verify_nonce(): bool {
        if (!check_ajax_referer('sqmedia_cf_nonce', 'nonce', false)) {
            wp_send_json_error(['message' => __('Security check failed.', 'staticq-media')], 403);
            return false;
        }
        return true;
    }

    /**
     * AJAX: Verify API token
     */
    public static function verify_token(): void {
        self::verify_permission();
        self::verify_nonce();
        
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified via self::verify_nonce() above.
        $token = sanitize_text_field( wp_unslash( $_POST['token'] ?? '' ) );

        if (empty($token)) {
            wp_send_json_error(['message' => __('No token provided.', 'staticq-media')]);
            return;
        }

        $api = new SQMedia_Cloudflare_API($token);

        // Verify token
        $verify_result = $api->verify_token();

        if (is_wp_error($verify_result)) {
            wp_send_json_error([
                'message' => $verify_result->get_error_message(),
                'code' => $verify_result->get_error_code()
            ]);
            return;
        }

        // Get account info
        $accounts = $api->get_accounts();
        if (is_wp_error($accounts)) {
            wp_send_json_error([
                'message' => $accounts->get_error_message(),
                'code' => $accounts->get_error_code()
            ]);
            return;
        }

        $account = $accounts['result'][0] ?? null;
        if (!$account) {
            wp_send_json_error(['message' => __('No account found for this token.', 'staticq-media')]);
            return;
        }

        wp_send_json_success([
            'account_id' => $account['id'],
            'account_name' => $account['name'],
            'token_status' => $verify_result['result']['status'] ?? 'active'
        ]);
    }

    /**
     * AJAX: Verify AND save token to database
     */
    public static function verify_and_save_token(): void {
        self::verify_permission();
        self::verify_nonce();

        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified via self::verify_nonce() above.
        $token = sanitize_text_field( wp_unslash( $_POST['token'] ?? '' ) );
        
        if (empty($token)) {
            wp_send_json_error(['message' => __('No token provided.', 'staticq-media')]);
            return;
        }
        
        $api = new SQMedia_Cloudflare_API($token);
        
        // Verify token
        $verify_result = $api->verify_token();
        
        if (is_wp_error($verify_result)) {
            wp_send_json_error([
                'message' => $verify_result->get_error_message(),
                'code' => $verify_result->get_error_code()
            ]);
            return;
        }
        
        // Get account info
        $accounts = $api->get_accounts();
        if (is_wp_error($accounts)) {
            wp_send_json_error([
                'message' => $accounts->get_error_message(),
                'code' => $accounts->get_error_code()
            ]);
            return;
        }
        
        $account = $accounts['result'][0] ?? null;
        if (!$account) {
            wp_send_json_error(['message' => __('No account found for this token.', 'staticq-media')]);
            return;
        }
        
        // Save encrypted token to database
        $encrypted = $api->encrypt_token($token);
        if ($encrypted) {
            update_option('sqmedia_cf_token_encrypted', $encrypted);
            update_option('sqmedia_cf_account_id', $account['id']);
        }
        
        wp_send_json_success([
            'account_id' => $account['id'],
            'account_name' => $account['name'],
            'token_status' => $verify_result['result']['status'] ?? 'active'
        ]);
    }
    
    /**
     * AJAX: Verify token from wp-config.php constant
     */
    public static function verify_config_token(): void {
        self::verify_permission();
        self::verify_nonce();
        
        // Check if constant is defined
        if (!defined('SQMEDIA_CF_TOKEN') || empty(SQMEDIA_CF_TOKEN)) {
            wp_send_json_error([
                'message' => __('SQMEDIA_CF_TOKEN constant not found in wp-config.php.', 'staticq-media'),
                'code' => 'not_defined'
            ]);
            return;
        }
        
        $token = SQMEDIA_CF_TOKEN;
        $api = new SQMedia_Cloudflare_API($token);
        
        // Verify token
        $verify_result = $api->verify_token();
        
        if (is_wp_error($verify_result)) {
            wp_send_json_error([
                'message' => $verify_result->get_error_message(),
                'code' => $verify_result->get_error_code()
            ]);
            return;
        }
        
        // Get account info
        $accounts = $api->get_accounts();
        if (is_wp_error($accounts)) {
            wp_send_json_error([
                'message' => $accounts->get_error_message(),
                'code' => $accounts->get_error_code()
            ]);
            return;
        }
        
        $account = $accounts['result'][0] ?? null;
        if (!$account) {
            wp_send_json_error(['message' => __('No account found for this token.', 'staticq-media')]);
            return;
        }
        
        // Save account ID (but not the token - it's in wp-config)
        update_option('sqmedia_cf_account_id', $account['id']);
        
        wp_send_json_success([
            'account_id' => $account['id'],
            'account_name' => $account['name'],
            'token_status' => $verify_result['result']['status'] ?? 'active'
        ]);
    }
    
    /**
     * AJAX: Get zones (domains)
     */
    public static function get_zones(): void {
        self::verify_permission();
        self::verify_nonce();
        
        $token = self::get_token_from_request();
        if (!$token) {
            wp_send_json_error(['message' => __('No token available.', 'staticq-media')]);
            return;
        }
        
        $api = new SQMedia_Cloudflare_API($token);
        $zones = $api->get_zones();
        
        if (is_wp_error($zones)) {
            wp_send_json_error([
                'message' => $zones->get_error_message(),
                'code' => $zones->get_error_code()
            ]);
            return;
        }
        
        $zone_list = [];
        foreach ($zones['result'] ?? [] as $zone) {
            $zone_list[] = [
                'id' => $zone['id'],
                'name' => $zone['name'],
                'status' => $zone['status']
            ];
        }
        
        wp_send_json_success(['zones' => $zone_list]);
    }
    
    /**
     * AJAX: Get existing R2 buckets
     */
    public static function get_buckets(): void {
        self::verify_permission();
        self::verify_nonce();
        
        $token = self::get_token_from_request();
        if (!$token) {
            wp_send_json_error(['message' => __('No token available.', 'staticq-media')]);
            return;
        }
        
        $api = new SQMedia_Cloudflare_API($token);
        $buckets = $api->list_buckets();
        
        if (is_wp_error($buckets)) {
            wp_send_json_error([
                'message' => $buckets->get_error_message(),
                'code' => $buckets->get_error_code()
            ]);
            return;
        }
        
        $bucket_list = [];
        foreach ($buckets['result']['buckets'] ?? [] as $bucket) {
            $bucket_list[] = [
                'name' => $bucket['name'],
                'created' => $bucket['creation_date'] ?? ''
            ];
        }
        
        wp_send_json_success(['buckets' => $bucket_list]);
    }
    
    /**
     * AJAX: Check availability of worker name, bucket name, or route
     */
    public static function check_availability(): void {
        self::verify_permission();
        self::verify_nonce();
        
        $token = self::get_token_from_request();
        if (!$token) {
            wp_send_json_error(['message' => __('No token available.', 'staticq-media')]);
            return;
        }
        
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified via self::verify_nonce() above.
        $check_type = sanitize_text_field( wp_unslash( $_POST['check_type'] ?? '' ) );
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified via self::verify_nonce() above.
        $value = sanitize_text_field( wp_unslash( $_POST['value'] ?? '' ) );
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified via self::verify_nonce() above.
        $zone_id = sanitize_text_field( wp_unslash( $_POST['zone_id'] ?? '' ) );
        
        if (empty($value)) {
            wp_send_json_error(['message' => __('No value provided.', 'staticq-media')]);
            return;
        }
        
        $api = new SQMedia_Cloudflare_API($token);
        
        switch ($check_type) {
            case 'worker':
                $result = $api->is_worker_name_available($value);
                if (is_wp_error($result)) {
                    wp_send_json_error(['message' => $result->get_error_message()]);
                    return;
                }
                wp_send_json_success([
                    'available' => $result,
                    'message' => $result 
                        ? __('Worker name is available', 'staticq-media')
                        : __('Worker name already exists', 'staticq-media')
                ]);
                break;
                
            case 'bucket':
                $result = $api->is_bucket_name_available($value);
                if (is_wp_error($result)) {
                    wp_send_json_error(['message' => $result->get_error_message()]);
                    return;
                }
                wp_send_json_success([
                    'available' => $result,
                    'message' => $result 
                        ? __('Bucket name is available', 'staticq-media')
                        : __('Bucket name already exists', 'staticq-media')
                ]);
                break;
                
            case 'route':
                if (empty($zone_id)) {
                    wp_send_json_error(['message' => __('Zone ID required for route check.', 'staticq-media')]);
                    return;
                }
                $result = $api->check_route_availability($zone_id, $value);
                if (is_wp_error($result)) {
                    wp_send_json_error(['message' => $result->get_error_message()]);
                    return;
                }
                wp_send_json_success([
                    'available' => $result['available'],
                    'conflict' => $result['conflict'] ?? null,
                    'message' => $result['available']
                        ? __('Route pattern is available', 'staticq-media')
                        // translators: %s is the name of the worker script that already uses this route.
                        : sprintf(__('Route already assigned to "%s"', 'staticq-media'), $result['conflict']['script'] ?? 'unknown')
                ]);
                break;
                
            default:
                wp_send_json_error(['message' => __('Invalid check type.', 'staticq-media')]);
        }
    }
    
    /**
     * AJAX: Run pre-flight checks
     */
    public static function preflight_checks(): void {
        self::verify_permission();
        self::verify_nonce();

        $token = self::get_token_from_request();
        if (!$token) {
            wp_send_json_error(['message' => __('No token available.', 'staticq-media')]);
            return;
        }
        
        // phpcs:disable WordPress.Security.NonceVerification.Missing -- nonce verified via self::verify_nonce() above.
        $config = [
            'zone_id'             => sanitize_text_field( wp_unslash( $_POST['zone_id'] ?? '' ) ),
            'worker_name'         => sanitize_text_field( wp_unslash( $_POST['worker_name'] ?? '' ) ),
            'bucket_name'         => sanitize_text_field( wp_unslash( $_POST['bucket_name'] ?? '' ) ),
            'use_existing_bucket' => ! empty( $_POST['use_existing_bucket'] ),
            'route_pattern'       => sanitize_text_field( wp_unslash( $_POST['route_pattern'] ?? '' ) ),
        ];
        // phpcs:enable WordPress.Security.NonceVerification.Missing

        $api = new SQMedia_Cloudflare_API($token);
        $deployer = new SQMedia_Cloudflare_Deployer($api);

        $result = $deployer->preflight_checks($config);

        if ($result['success']) {
            wp_send_json_success($result);
        } else {
            wp_send_json_error($result);
        }
    }

    /**
     * AJAX: Deploy worker
     */
    public static function deploy(): void {
        self::verify_permission();
        self::verify_nonce();

        $token = self::get_token_from_request();
        if (!$token) {
            wp_send_json_error(['message' => __('No token available.', 'staticq-media')]);
            return;
        }

        // phpcs:disable WordPress.Security.NonceVerification.Missing -- nonce verified via self::verify_nonce() above.
        $config = [
            'zone_id'             => sanitize_text_field( wp_unslash( $_POST['zone_id'] ?? '' ) ),
            'zone_name'           => sanitize_text_field( wp_unslash( $_POST['zone_name'] ?? '' ) ),
            'worker_name'         => sanitize_text_field( wp_unslash( $_POST['worker_name'] ?? '' ) ),
            'bucket_name'         => sanitize_text_field( wp_unslash( $_POST['bucket_name'] ?? '' ) ),
            'use_existing_bucket' => ! empty( $_POST['use_existing_bucket'] ),
            'route_pattern'       => sanitize_text_field( wp_unslash( $_POST['route_pattern'] ?? '' ) ),
            'storage_provider'    => 'r2',
            'uploads_prefix'      => sanitize_text_field( wp_unslash( $_POST['uploads_prefix'] ?? '/wp-content/uploads/' ) ),
            'bucket_prefix'       => SQMedia_Settings::get_current_prefix(),
            'enable_fallback'     => ! isset( $_POST['enable_fallback'] ) || ! empty( $_POST['enable_fallback'] ),
            'cache_ttl'           => absint( $_POST['cache_ttl'] ?? 2592000 ),
        ];
        // phpcs:enable WordPress.Security.NonceVerification.Missing
        
        // Validate required fields
        $required = ['zone_id', 'worker_name', 'bucket_name', 'route_pattern'];
        foreach ($required as $field) {
            if (empty($config[$field])) {
                // translators: %s is the name of the missing configuration field (e.g. zone_id, worker_name).
                wp_send_json_error(['message' => sprintf(__('Missing required field: %s', 'staticq-media'), $field)]);
                return;
            }
        }
        
        $api = new SQMedia_Cloudflare_API($token);
        $deployer = new SQMedia_Cloudflare_Deployer($api);
        
        $result = $deployer->deploy($config);
        
        if ($result['success']) {
            wp_send_json_success($result);
        } else {
            wp_send_json_error($result);
        }
    }
    
    /**
     * AJAX: Test deployment
     */
    public static function test_deployment(): void {
        self::verify_permission();
        self::verify_nonce();

        $token = self::get_token_from_request();
        if (!$token) {
            wp_send_json_error(['message' => __('No token available.', 'staticq-media')]);
            return;
        }
        
        $deployment = SQMedia_Cloudflare_Deployer::get_deployment_status();
        if (!$deployment) {
            wp_send_json_error(['message' => __('No deployment found.', 'staticq-media')]);
            return;
        }
        
        $api = new SQMedia_Cloudflare_API($token);
        $deployer = new SQMedia_Cloudflare_Deployer($api);
        
        $result = $deployer->test_deployment($deployment);
        
        if ($result['success']) {
            // Update last test time
            $deployment['last_test'] = current_time('mysql');
            $deployment['last_test_result'] = $result;
            update_option('sqmedia_cf_deployment', $deployment);
            
            wp_send_json_success($result);
        } else {
            wp_send_json_error($result);
        }
    }
    
    /**
     * AJAX: Undeploy/remove worker
     */
    public static function undeploy(): void {
        self::verify_permission();
        self::verify_nonce();

        $token = self::get_token_from_request();
        if (!$token) {
            wp_send_json_error(['message' => __('No token available.', 'staticq-media')]);
            return;
        }
        
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified via self::verify_nonce() above.
        $delete_bucket = ! empty( $_POST['delete_bucket'] );
        
        $api = new SQMedia_Cloudflare_API($token);
        $deployer = new SQMedia_Cloudflare_Deployer($api);
        
        $result = $deployer->undeploy($delete_bucket);
        
        if ($result['success']) {
            wp_send_json_success($result);
        } else {
            wp_send_json_error($result);
        }
    }
    
    /**
     * AJAX: Save token to database (encrypted)
     */
    public static function save_token(): void {
        self::verify_permission();
        self::verify_nonce();
        
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified via self::verify_nonce() above.
        $token = sanitize_text_field( wp_unslash( $_POST['token'] ?? '' ) );
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified via self::verify_nonce() above.
        $storage_method = sanitize_text_field( wp_unslash( $_POST['storage_method'] ?? 'database' ) );
        
        if ($storage_method === 'database' && !empty($token)) {
            $api = new SQMedia_Cloudflare_API();
            $encrypted = $api->encrypt_token($token);
            update_option('sqmedia_cf_token_encrypted', $encrypted);
            wp_send_json_success(['message' => __('Token saved securely.', 'staticq-media')]);
        } elseif ($storage_method === 'config') {
            // Just confirm - user will add to wp-config.php manually
            delete_option('sqmedia_cf_token_encrypted');
            wp_send_json_success([
                'message' => __('Add this to your wp-config.php:', 'staticq-media'),
                'code' => "define('SQMEDIA_CF_TOKEN', '" . esc_js($token) . "');"
            ]);
        } else {
            wp_send_json_error(['message' => __('Invalid request.', 'staticq-media')]);
        }
    }
    
    /**
     * AJAX: Get current deployment status
     */
    public static function get_status(): void {
        self::verify_permission();
        self::verify_nonce();
        
        $deployment = SQMedia_Cloudflare_Deployer::get_deployment_status();
        $has_token = self::has_token_configured();
        
        wp_send_json_success([
            'deployed' => $deployment !== null,
            'has_token' => $has_token,
            'deployment' => $deployment
        ]);
    }
    
    /**
     * AJAX: Clear local settings only (no API calls)
     * 
     * Use this when the token has expired or been revoked.
     * The user will need to manually clean up Cloudflare resources.
     */
    public static function clear_local(): void {
        self::verify_permission();
        self::verify_nonce();
        
        // Delete all Cloudflare-related options
        delete_option('sqmedia_cf_deployment');
        delete_option('sqmedia_cf_token_encrypted');
        delete_option('sqmedia_cf_account_id');
        
        wp_send_json_success([
            'message' => __('Local settings cleared. Remember to manually delete the worker and route from your Cloudflare dashboard if they still exist.', 'staticq-media')
        ]);
    }
    
    /**
     * Get token from request or storage
     */
    private static function get_token_from_request(): ?string {
        // Check if token provided in request (used during wizard with database storage)
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified by the calling public AJAX handler.
        $token = sanitize_text_field( wp_unslash( $_POST['token'] ?? '' ) );
        if (!empty($token)) {
            return $token;
        }
        
        // Check wp-config constant (preferred for wp-config storage method)
        if (defined('SQMEDIA_CF_TOKEN') && !empty(SQMEDIA_CF_TOKEN)) {
            return SQMEDIA_CF_TOKEN;
        }
        
        // Check encrypted database option
        $encrypted = get_option('sqmedia_cf_token_encrypted', '');
        if (!empty($encrypted)) {
            $api = new SQMedia_Cloudflare_API();
            $decrypted = $api->decrypt_token($encrypted);
            if ($decrypted) {
                return $decrypted;
            }
        }
        
        return null;
    }
    
    /**
     * Check if token is configured anywhere
     */
    private static function has_token_configured(): bool {
        if (defined('SQMEDIA_CF_TOKEN') && !empty(SQMEDIA_CF_TOKEN)) {
            return true;
        }
        
        $encrypted = get_option('sqmedia_cf_token_encrypted', '');
        return !empty($encrypted);
    }
}