<?php

/**
 * Plugin Name: Paapi Product Search for Amazon
 * Description: Search and display Amazon products with filters and infinite scroll
 * Version: 2.12.2
 * Author: PaapiPlugin
 * Author URI: https://paapiplugin.com
 * License: GPL v2 or later
 * Text Domain: paapi-product-search-for-amazon
 * Requires at least: 5.8
 * Requires PHP: 7.4
 */
// Prevent direct access
if (!defined('ABSPATH')) {
    exit;
}
if (!function_exists('psfa_fs')) {
    // Create a helper function for easy SDK access.
    function psfa_fs()
    {
        global $psfa_fs;

        if (!isset($psfa_fs)) {
            // Include Freemius SDK.
            require_once dirname(__FILE__) . '/src/vendor/freemius/start.php';

            $psfa_fs = fs_dynamic_init(array(
                'id' => '22205',
                'slug' => 'paapi-product-search-for-amazon',
                'type' => 'plugin',
                'public_key' => 'pk_970a210428f883332e658fd4e9240',
                'is_premium' => false,
                'is_org_compliant' => true,
                // If your plugin is a serviceware, set this option to false.
                'has_premium_version' => true,
                'has_addons' => true,
                'has_paid_plans' => true,
                'has_affiliation' => 'selected',
                'menu' => array(
                    'slug' => 'paapi-product-search-for-amazon',
                    'support' => false,
                    'addons' => true,
                    'account' => true,
                    'pricing' => true,
                    'contact' => true,
                ),
            ));
        }

        return $psfa_fs;
    }

    // Init Freemius.
    psfa_fs();

    // Set custom icon for Freemius dialogs
    psfa_fs()->add_filter( 'plugin_icon', function() {
        return dirname( __FILE__ ) . '/src/assets/img/icon-128x128.png';
    });

    // Signal that SDK was initiated.
    do_action('psfa_fs_loaded');
}

// Define plugin constants
define('PSFA_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('PSFA_PLUGIN_URL', plugin_dir_url(__FILE__));
define('PSFA_VERSION', '2.12.2');

/**
 * Initialize the plugin
 */
class PSFA_Product_Search
{
    private static $instance = null;

    public static function get_instance()
    {
        if (null === self::$instance) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    private function __construct()
    {
        // Load cache layer early - addons call these functions from shortcodes
        // which render independently of the search module.
        require_once PSFA_PLUGIN_DIR . 'src/core/cache/product-cache.php';
        require_once PSFA_PLUGIN_DIR . 'src/core/cache/search-cache.php';

        // Load AJAX handlers
        require_once PSFA_PLUGIN_DIR . 'src/core/admin/ajax.php';

        // Load dev-only test tools (only in mock mode, excluded from free build)
        if (defined('PSFA_MOCK_MODE') && PSFA_MOCK_MODE) {
            if (file_exists(PSFA_PLUGIN_DIR . 'src/core/admin/offers-comparison-test.php')) {
                require_once PSFA_PLUGIN_DIR . 'src/core/admin/offers-comparison-test.php';
            }
            if (file_exists(PSFA_PLUGIN_DIR . 'src/core/admin/oauth-credentials-test.php')) {
                require_once PSFA_PLUGIN_DIR . 'src/core/admin/oauth-credentials-test.php';
            }
        }

        // Handle POST redirects early (before any output)
        add_action('template_redirect', array($this, 'handle_early_post_redirect'));

        // Add admin menu for settings
        add_action('admin_menu', array($this, 'add_admin_menu'));
        add_action('admin_init', array($this, 'register_settings'));
        add_action('admin_init', array($this, 'handle_reset_customization'));
        add_action('admin_init', array($this, 'handle_mock_section_redirect'));

        // Reset API client cache when auth settings change
        add_action('update_option_psfa_auth_type', array($this, 'reset_api_client_cache'));
        add_action('update_option_psfa_oauth_client_id', array($this, 'reset_api_client_cache'));
        add_action('update_option_psfa_oauth_client_secret', array($this, 'reset_api_client_cache'));
        add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_styles'));
        // Register shortcode (prefixed per WordPress.org guidelines)
        add_shortcode('psfa_search', array($this, 'render_search_page'));
        // Enqueue scripts and styles
        add_action('wp_enqueue_scripts', array($this, 'enqueue_scripts'));
        // AJAX handler for infinite scroll
        add_action('wp_ajax_psfa_load_more', 'psfa_ajax_load_more');
        add_action('wp_ajax_nopriv_psfa_load_more', 'psfa_ajax_load_more');
        // AJAX handler for autocomplete
        add_action('wp_ajax_psfa_autocomplete', 'psfa_ajax_autocomplete');
        add_action('wp_ajax_nopriv_psfa_autocomplete', 'psfa_ajax_autocomplete');
        // AJAX handler for test connection (admin only)
        add_action('wp_ajax_psfa_test_connection', array($this, 'ajax_test_connection'));
        // AJAX handler for saving settings (React admin)
        add_action('wp_ajax_psfa_save_settings', array($this, 'ajax_save_settings'));
        // AJAX handler for saving credentials (React admin)
        add_action('wp_ajax_psfa_save_credentials', array($this, 'ajax_save_credentials'));
        // AJAX handler for resetting settings (React admin)
        add_action('wp_ajax_psfa_reset_settings', array($this, 'ajax_reset_settings'));
        // AJAX handler for clearing cache (React admin)
        add_action('wp_ajax_psfa_clear_cache', array($this, 'ajax_clear_cache'));
        // AJAX handler for cache stats (React admin)
        add_action('wp_ajax_psfa_cache_stats', array($this, 'ajax_cache_stats'));
        // Auto-invalidate search cache when marketplace or associate tag changes.
        add_action('update_option_psfa_marketplace', array($this, 'auto_clear_search_cache'));
        add_action('update_option_psfa_associate_tag', array($this, 'auto_clear_search_cache'));
    }

    /**
     * Handle POST form redirects early (before any output).
     * This ensures wp_safe_redirect works properly.
     */
    public function handle_early_post_redirect()
    {
        // Only process POST requests with our form fields
        if (!isset($_SERVER['REQUEST_METHOD']) || $_SERVER['REQUEST_METHOD'] !== 'POST') {
            return;
        }

        if (!isset($_POST['amazon_search_query']) &&
                !isset($_POST['amazon_search_brand']) &&
                !isset($_POST['amazon_search_price_min']) &&
                !isset($_POST['amazon_search_price_max'])) {
            return;
        }

        // Verify nonce
        $nonce = isset($_POST['psfa_nonce']) ? sanitize_text_field(wp_unslash($_POST['psfa_nonce'])) : '';
        if (empty($nonce) || !wp_verify_nonce($nonce, 'psfa_search_nonce')) {
            return;  // Let the shortcode handle the error
        }

        // Build redirect parameters
        $params = array();

        if (isset($_POST['amazon_search_query']) && !empty($_POST['amazon_search_query'])) {
            $params['q'] = sanitize_text_field(wp_unslash($_POST['amazon_search_query']));
        }
        if (isset($_POST['amazon_search_category']) && !empty($_POST['amazon_search_category'])) {
            $params['category'] = sanitize_text_field(wp_unslash($_POST['amazon_search_category']));
        }
        if (isset($_POST['amazon_search_price_min']) && $_POST['amazon_search_price_min'] !== '') {
            $params['min_price'] = floatval($_POST['amazon_search_price_min']);
        }
        if (isset($_POST['amazon_search_price_max']) && $_POST['amazon_search_price_max'] !== '') {
            $params['max_price'] = floatval($_POST['amazon_search_price_max']);
        }
        if (isset($_POST['amazon_search_brand']) && !empty($_POST['amazon_search_brand'])) {
            $params['brand'] = sanitize_text_field(wp_unslash($_POST['amazon_search_brand']));
        }
        if (isset($_POST['amazon_search_sort']) && !empty($_POST['amazon_search_sort'])) {
            $params['sort'] = sanitize_text_field(wp_unslash($_POST['amazon_search_sort']));
        }
        if (isset($_POST['amazon_search_condition']) && !empty($_POST['amazon_search_condition'])) {
            $params['condition'] = sanitize_text_field(wp_unslash($_POST['amazon_search_condition']));
        }
        if (isset($_POST['amazon_search_merchant']) && !empty($_POST['amazon_search_merchant'])) {
            $params['merchant'] = sanitize_text_field(wp_unslash($_POST['amazon_search_merchant']));
        }
        if (isset($_POST['amazon_search_availability']) && !empty($_POST['amazon_search_availability']) && $_POST['amazon_search_availability'] !== 'Available') {
            $params['availability'] = sanitize_text_field(wp_unslash($_POST['amazon_search_availability']));
        }
        // Premium filters
        if (isset($_POST['prime']) && !empty($_POST['prime'])) {
            $params['prime'] = sanitize_text_field(wp_unslash($_POST['prime']));
        }
        if (isset($_POST['has_deal']) && !empty($_POST['has_deal'])) {
            $params['has_deal'] = sanitize_text_field(wp_unslash($_POST['has_deal']));
        }
        if (isset($_POST['min_saving_percent']) && $_POST['min_saving_percent'] !== '') {
            $params['min_saving_percent'] = absint($_POST['min_saving_percent']);
        }

        // Only redirect if we have parameters
        if (empty($params)) {
            return;
        }

        // Build redirect URL
        $redirect_url = get_permalink();
        if (!empty($params)) {
            $join_char = (false === strpos($redirect_url, '?')) ? '?' : '&';
            $redirect_url .= $join_char . http_build_query($params);
        }

        wp_safe_redirect($redirect_url);
        exit;
    }

    /**
     * Add admin menu
     */
    public function add_admin_menu()
    {
        // Main settings page - top-level menu
        add_menu_page(
            'Paapi Product Search for Amazon Settings',
            'PaapiPlugin PSFA',
            'manage_options',
            'paapi-product-search-for-amazon',
            array($this, 'render_settings_page'),
            'data:image/svg+xml;base64,' . base64_encode( file_get_contents( plugin_dir_path( __FILE__ ) . 'src/assets/img/wp-menu-icon.svg' ) ),
            56
        );
    }

    /**
     * Register plugin settings
     */
    public function register_settings()
    {
        // Auth type selection
        register_setting('psfa_settings', 'psfa_auth_type', array(
            'sanitize_callback' => array($this, 'sanitize_auth_type'),
            'default' => 'awssig4',
        ));

        // AWS Sig V4 Credentials
        register_setting('psfa_settings', 'psfa_access_key_id', array(
            'sanitize_callback' => 'sanitize_text_field',
        ));
        register_setting('psfa_settings', 'psfa_secret_access_key', array(
            'sanitize_callback' => 'sanitize_text_field',
        ));
        register_setting('psfa_settings', 'psfa_associate_tag', array(
            'sanitize_callback' => 'sanitize_text_field',
        ));

        // OAuth 2.0 Credentials (Creators API)
        register_setting('psfa_settings', 'psfa_oauth_client_id', array(
            'sanitize_callback' => 'sanitize_text_field',
        ));
        register_setting('psfa_settings', 'psfa_oauth_client_secret', array(
            'sanitize_callback' => 'sanitize_text_field',
        ));
        register_setting('psfa_settings', 'psfa_oauth_credential_version', array(
            'sanitize_callback' => 'sanitize_text_field',
        ));
        register_setting('psfa_settings', 'psfa_oauth_auth_region', array(
            'sanitize_callback' => 'sanitize_text_field',
            'default' => 'us-east-1',
        ));
        register_setting('psfa_settings', 'psfa_marketplace', array(
            'sanitize_callback' => 'sanitize_text_field',
            'default' => 'www.amazon.com',
        ));

        // Appearance axes
        register_setting('psfa_settings', 'psfa_base_color', array(
            'sanitize_callback' => 'sanitize_text_field',
            'default' => 'neutral',
        ));
        register_setting('psfa_settings', 'psfa_accent_color', array(
            'sanitize_callback' => 'sanitize_text_field',
            'default' => 'orange',
        ));
        register_setting('psfa_settings', 'psfa_style', array(
            'sanitize_callback' => 'sanitize_text_field',
            'default' => 'vega',
        ));
        register_setting('psfa_settings', 'psfa_radius', array(
            'sanitize_callback' => 'sanitize_text_field',
            'default' => '0.5',
        ));
        register_setting('psfa_settings', 'psfa_dark_mode', array(
            'sanitize_callback' => array($this, 'sanitize_checkbox'),
            'default' => '0',
        ));
        register_setting('psfa_settings', 'psfa_font', array(
            'sanitize_callback' => 'sanitize_text_field',
            'default' => '',
        ));

        // Display mode (Pro only)
        register_setting('psfa_settings', 'psfa_display_mode', array(
            'sanitize_callback' => array($this, 'sanitize_display_mode'),
            'default' => 'grid',
        ));

        // Hide category banner (Free feature)
        register_setting('psfa_settings', 'psfa_hide_category_banner', array(
            'sanitize_callback' => array($this, 'sanitize_checkbox'),
            'default' => false,
        ));

        // Hide search bar (Free feature)
        register_setting('psfa_settings', 'psfa_hide_search', array(
            'sanitize_callback' => array($this, 'sanitize_checkbox'),
            'default' => false,
        ));

        // Hide filters (Free feature)
        register_setting('psfa_settings', 'psfa_hide_filters', array(
            'sanitize_callback' => array($this, 'sanitize_checkbox'),
            'default' => false,
        ));

        // Filter visibility settings (Free feature)
        register_setting('psfa_settings', 'psfa_filter_visibility', array(
            'sanitize_callback' => array($this, 'sanitize_filter_visibility'),
            'default' => array(
                'brand' => true,
                'price' => true,
                'sort' => true,
                'condition' => true,
                'merchant' => true,
                'availability' => true,
                'has_deal' => true,
                'deals' => true,
            ),
        ));

        // Premium customization settings
        register_setting('psfa_settings', 'psfa_button_text', array(
            'sanitize_callback' => array($this, 'sanitize_text_field_safe'),
            'default' => 'Buy on Amazon',
        ));
        // Hide Amazon icon on buy button
        register_setting('psfa_settings', 'psfa_hide_amazon_icon', array(
            'sanitize_callback' => array($this, 'sanitize_checkbox'),
            'default' => '0',
        ));
        // Premium Feature Toggles
        register_setting('psfa_settings', 'psfa_features', array(
            'sanitize_callback' => array($this, 'sanitize_features'),
            'default' => array(
                'deal_badge' => true,
                'deal_countdown' => true,
                'prime_exclusive' => true,
                'low_stock' => true,
                'buybox_winner' => false,
                'used_price' => false,
            ),
        ));

        // White-label mode toggle (Premium) - when ON, hides branding
        register_setting('psfa_settings', 'psfa_white_label', array(
            'sanitize_callback' => array($this, 'sanitize_checkbox'),
            'default' => '0',
        ));
    }

    /**
     * Sanitize auth type selection
     */
    public function sanitize_auth_type($type)
    {
        $valid_types = array('awssig4', 'oauth');
        if (in_array($type, $valid_types, true)) {
            return $type;
        }
        return 'awssig4';
    }

    /**
     * Reset API client cache when auth settings change
     */
    public function reset_api_client_cache()
    {
        require_once PSFA_PLUGIN_DIR . 'src/core/api/class-api-factory.php';
        PSFA_API_Factory::reset();
        // Clear cached OAuth token to force re-authentication
        delete_option('psfa_oauth_access_token');
        delete_option('psfa_oauth_token_expires');
    }

    /**
     * AJAX handler for testing API connection
     */
    public function ajax_test_connection()
    {
        // Accept both psfa_test_connection (PHP admin) and psfa_admin_nonce (React admin)
        if (!check_ajax_referer('psfa_test_connection', 'nonce', false) && 
            !check_ajax_referer('psfa_admin_nonce', 'nonce', false)) {
            wp_send_json_error(['message' => 'Security check failed']);
            return;
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error(['message' => 'Unauthorized']);
            return;
        }

        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified in check_ajax_referer above
        $auth_type = isset($_POST['auth_type']) ? sanitize_text_field(wp_unslash($_POST['auth_type'])) : 'awssig4';

        if ($auth_type === 'oauth') {
            $result = $this->test_oauth_connection();
        } else {
            $result = $this->test_awssig4_connection();
        }

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

    /**
     * Test OAuth connection with provided credentials
     */
    private function test_oauth_connection(): array
    {
        // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified in ajax_test_connection
        $client_id = isset($_POST['client_id']) ? sanitize_text_field(wp_unslash($_POST['client_id'])) : '';
        $client_secret = isset($_POST['client_secret']) ? sanitize_text_field(wp_unslash($_POST['client_secret'])) : '';
        $credential_version = isset($_POST['credential_version']) ? sanitize_text_field(wp_unslash($_POST['credential_version'])) : '';
        $auth_region = isset($_POST['auth_region']) ? sanitize_text_field(wp_unslash($_POST['auth_region'])) : 'us-east-1';
        $marketplace = isset($_POST['marketplace']) ? sanitize_text_field(wp_unslash($_POST['marketplace'])) : 'www.amazon.com';
        $associate_tag = isset($_POST['associate_tag']) ? sanitize_text_field(wp_unslash($_POST['associate_tag'])) : '';
        // phpcs:enable WordPress.Security.NonceVerification.Missing

        if (empty($client_id) || empty($client_secret) || empty($credential_version)) {
            return ['success' => false, 'message' => 'Missing required OAuth credentials'];
        }

        // Get OAuth token
        $token_url = "https://creatorsapi.auth.{$auth_region}.amazoncognito.com/oauth2/token";
        $basic_auth = base64_encode($client_id . ':' . $client_secret);

        $token_response = wp_remote_post($token_url, [
            'headers' => [
                'Content-Type' => 'application/x-www-form-urlencoded',
                'Authorization' => 'Basic ' . $basic_auth,
            ],
            'body' => 'grant_type=client_credentials&scope=creatorsapi/default',
            'timeout' => 30,
        ]);

        if (is_wp_error($token_response)) {
            return ['success' => false, 'message' => 'Token request failed: ' . $token_response->get_error_message()];
        }

        $token_code = wp_remote_retrieve_response_code($token_response);
        $token_body = json_decode(wp_remote_retrieve_body($token_response), true);

        if ($token_code !== 200 || !isset($token_body['access_token'])) {
            $error = $token_body['error'] ?? 'Unknown error';
            return ['success' => false, 'message' => 'Token error: ' . $error];
        }

        $access_token = $token_body['access_token'];

        // Test API call
        require_once PSFA_PLUGIN_DIR . 'src/core/api/payload.php';
        $api_url = 'https://creatorsapi.amazon/catalog/v1/searchItems';
        $payload = [
            'keywords' => 'test',
            'partnerTag' => $associate_tag,
            'marketplace' => $marketplace,
            'resources' => psfa_get_creators_api_resources(),
            'itemCount' => 1,
        ];

        $api_response = wp_remote_post($api_url, [
            'headers' => [
                'Authorization' => 'Bearer ' . $access_token . ', Version ' . $credential_version,
                'Content-Type' => 'application/json',
                'x-marketplace' => $marketplace,
            ],
            'body' => wp_json_encode($payload),
            'timeout' => 15,
        ]);

        if (is_wp_error($api_response)) {
            return ['success' => false, 'message' => 'API request failed: ' . $api_response->get_error_message()];
        }

        $api_code = wp_remote_retrieve_response_code($api_response);

        if ($api_code === 200) {
            return ['success' => true, 'message' => 'Connection successful! OAuth credentials are valid.'];
        }

        $api_body = json_decode(wp_remote_retrieve_body($api_response), true);
        $error = $api_body['errors'][0]['message'] ?? 'HTTP ' . $api_code;
        return ['success' => false, 'message' => 'API error: ' . $error];
    }

    /**
     * Test AWS Sig V4 connection with provided credentials
     */
    private function test_awssig4_connection(): array
    {
        // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified in ajax_test_connection
        $access_key = isset($_POST['access_key']) ? sanitize_text_field(wp_unslash($_POST['access_key'])) : '';
        $secret_key = isset($_POST['secret_key']) ? sanitize_text_field(wp_unslash($_POST['secret_key'])) : '';
        $associate_tag = isset($_POST['associate_tag']) ? sanitize_text_field(wp_unslash($_POST['associate_tag'])) : '';
        // phpcs:enable WordPress.Security.NonceVerification.Missing

        if (empty($access_key) || empty($secret_key) || empty($associate_tag)) {
            return ['success' => false, 'message' => 'Missing required AWS credentials'];
        }

        // Use existing AWS Sig4 client to test
        require_once PSFA_PLUGIN_DIR . 'src/core/api/class-awssig4-client.php';
        require_once PSFA_PLUGIN_DIR . 'src/core/api/payload.php';

        $config = [
            'access_key' => $access_key,
            'secret_key' => $secret_key,
            'associate_tag' => $associate_tag,
            'region' => 'us-east-1',
            'host' => 'webservices.amazon.com',
        ];

        $client = new PSFA_AWSSig4_Client($config);
        $payload = psfa_build_search_payload(['search_term' => 'test', 'page' => 1], $associate_tag);

        if (isset($payload['error'])) {
            return ['success' => false, 'message' => $payload['error']];
        }

        $result = $client->search_items($payload);

        if (isset($result['error'])) {
            return ['success' => false, 'message' => $result['error']];
        }

        return ['success' => true, 'message' => 'Connection successful! AWS credentials are valid.'];
    }

    /**
     * AJAX handler for saving settings from React admin
     */
    public function ajax_save_settings()
    {
        // Verify nonce
        if (!check_ajax_referer('psfa_admin_nonce', 'nonce', false)) {
            wp_send_json_error(array('message' => 'Security check failed.'));
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Permission denied.'));
        }

        // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified above
        $updated = array();

        // API Credentials
        if (isset($_POST['psfa_auth_type'])) {
            update_option('psfa_auth_type', sanitize_text_field(wp_unslash($_POST['psfa_auth_type'])));
            $updated[] = 'auth_type';
        }
        if (isset($_POST['psfa_access_key_id'])) {
            update_option('psfa_access_key_id', sanitize_text_field(wp_unslash($_POST['psfa_access_key_id'])));
            $updated[] = 'access_key';
        }
        if (isset($_POST['psfa_secret_access_key'])) {
            update_option('psfa_secret_access_key', sanitize_text_field(wp_unslash($_POST['psfa_secret_access_key'])));
            $updated[] = 'secret_key';
        }
        if (isset($_POST['psfa_associate_tag'])) {
            update_option('psfa_associate_tag', sanitize_text_field(wp_unslash($_POST['psfa_associate_tag'])));
            $updated[] = 'associate_tag';
        }
        if (isset($_POST['psfa_oauth_client_id'])) {
            update_option('psfa_oauth_client_id', sanitize_text_field(wp_unslash($_POST['psfa_oauth_client_id'])));
            $updated[] = 'oauth_client_id';
        }
        if (isset($_POST['psfa_oauth_client_secret'])) {
            update_option('psfa_oauth_client_secret', sanitize_text_field(wp_unslash($_POST['psfa_oauth_client_secret'])));
            $updated[] = 'oauth_client_secret';
        }
        if (isset($_POST['psfa_oauth_credential_version'])) {
            update_option('psfa_oauth_credential_version', sanitize_text_field(wp_unslash($_POST['psfa_oauth_credential_version'])));
            $updated[] = 'oauth_credential_version';
        }
        if (isset($_POST['psfa_oauth_auth_region'])) {
            update_option('psfa_oauth_auth_region', sanitize_text_field(wp_unslash($_POST['psfa_oauth_auth_region'])));
            $updated[] = 'oauth_auth_region';
        }
        if (isset($_POST['psfa_marketplace'])) {
            update_option('psfa_marketplace', sanitize_text_field(wp_unslash($_POST['psfa_marketplace'])));
            $updated[] = 'marketplace';
        }

        // Appearance axes - save user preference without license validation
        // Validation happens on read (psfa_get_selected_*) to allow preference persistence
        if (isset($_POST['psfa_base_color'])) {
            $base = sanitize_text_field(wp_unslash($_POST['psfa_base_color']));
            $valid_bases = array('neutral', 'stone', 'slate', 'zinc', 'gray');
            if (in_array($base, $valid_bases, true)) {
                update_option('psfa_base_color', $base);
                $updated[] = 'base_color';
            }
        }
        if (isset($_POST['psfa_accent_color'])) {
            $accent = sanitize_text_field(wp_unslash($_POST['psfa_accent_color']));
            $valid_accents = array('red', 'orange', 'amber', 'yellow', 'lime', 'green', 'emerald', 'teal', 'cyan', 'sky', 'blue', 'indigo', 'violet', 'purple', 'fuchsia', 'pink', 'rose');
            if (in_array($accent, $valid_accents, true)) {
                update_option('psfa_accent_color', $accent);
                $updated[] = 'accent_color';
            }
        }
        if (isset($_POST['psfa_style'])) {
            $style = sanitize_text_field(wp_unslash($_POST['psfa_style']));
            $valid_styles = array('vega', 'nova', 'maia', 'lyra', 'mira');
            if (in_array($style, $valid_styles, true)) {
                update_option('psfa_style', $style);
                $updated[] = 'style';
            }
        }
        if (isset($_POST['psfa_radius'])) {
            $radius = sanitize_text_field(wp_unslash($_POST['psfa_radius']));
            $valid_radii = array('0', '0.3', '0.5', '0.75', '1.0');
            if (in_array($radius, $valid_radii, true)) {
                update_option('psfa_radius', $radius);
                $updated[] = 'radius';
            }
        }
        if (isset($_POST['psfa_dark_mode'])) {
            update_option('psfa_dark_mode', sanitize_text_field(wp_unslash($_POST['psfa_dark_mode'])) === '1' ? '1' : '0');
            $updated[] = 'dark_mode';
        }
        if (isset($_POST['psfa_font'])) {
            $font = sanitize_text_field(wp_unslash($_POST['psfa_font']));
            $valid_fonts = array('', 'inter', 'roboto', 'open-sans', 'lato', 'montserrat', 'poppins', 'dm-sans', 'plus-jakarta', 'outfit', 'space-grotesk', 'rubik', 'source-serif', 'merriweather', 'playfair', 'lora');
            if (in_array($font, $valid_fonts, true)) {
                update_option('psfa_font', $font);
                $updated[] = 'font';
            }
        }

        // Layout settings
        if (isset($_POST['psfa_display_mode'])) {
            $mode = sanitize_text_field(wp_unslash($_POST['psfa_display_mode']));
            if (in_array($mode, array('grid', 'list'), true)) {
                update_option('psfa_display_mode', $mode);
                $updated[] = 'display_mode';
            }
        }
        if (isset($_POST['psfa_hide_category_banner'])) {
            update_option('psfa_hide_category_banner', sanitize_text_field(wp_unslash($_POST['psfa_hide_category_banner'])) === '1' ? '1' : '0');
            $updated[] = 'hide_banner';
        }
        if (isset($_POST['psfa_white_label'])) {
            update_option('psfa_white_label', sanitize_text_field(wp_unslash($_POST['psfa_white_label'])) === '1' ? '1' : '0');
            $updated[] = 'white_label';
        }
        if (isset($_POST['psfa_hide_search'])) {
            update_option('psfa_hide_search', sanitize_text_field(wp_unslash($_POST['psfa_hide_search'])) === '1' ? '1' : '0');
            $updated[] = 'hide_search';
        }
        if (isset($_POST['psfa_hide_filters'])) {
            update_option('psfa_hide_filters', sanitize_text_field(wp_unslash($_POST['psfa_hide_filters'])) === '1' ? '1' : '0');
            $updated[] = 'hide_filters';
        }

        // Customization settings
        if (isset($_POST['psfa_button_text'])) {
            update_option('psfa_button_text', sanitize_text_field(wp_unslash($_POST['psfa_button_text'])));
            $updated[] = 'button_text';
        }
        if (isset($_POST['psfa_hide_amazon_icon'])) {
            update_option('psfa_hide_amazon_icon', sanitize_text_field(wp_unslash($_POST['psfa_hide_amazon_icon'])) === '1' ? '1' : '0');
            $updated[] = 'hide_amazon_icon';
        }
        // Filter visibility (JSON)
        if (isset($_POST['psfa_filter_visibility'])) {
            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON string sanitized after decoding
            $filters_raw = wp_unslash($_POST['psfa_filter_visibility']);
            $filters = is_string($filters_raw) ? json_decode($filters_raw, true) : $filters_raw;
            if (is_array($filters)) {
                $sanitized_filters = array();
                foreach ($filters as $key => $value) {
                    $sanitized_filters[sanitize_key($key)] = (bool) $value;
                }
                update_option('psfa_filter_visibility', $sanitized_filters);
                $updated[] = 'filter_visibility';
            }
        }

        // Features (JSON)
        if (isset($_POST['psfa_features'])) {
            // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- JSON string sanitized after decoding
            $features_raw = wp_unslash($_POST['psfa_features']);
            $features = is_string($features_raw) ? json_decode($features_raw, true) : $features_raw;
            if (is_array($features)) {
                $sanitized_features = array();
                foreach ($features as $key => $value) {
                    $sanitized_features[sanitize_key($key)] = (bool) $value;
                }
                update_option('psfa_features', $sanitized_features);
                $updated[] = 'features';
            }
        }
        // phpcs:enable WordPress.Security.NonceVerification.Missing

        wp_send_json_success(array(
            'message' => 'Settings saved successfully.',
            'updated' => $updated,
        ));
    }

    /**
     * AJAX handler for saving credentials from React admin
     */
    public function ajax_save_credentials()
    {
        // Verify nonce
        if (!check_ajax_referer('psfa_admin_nonce', 'nonce', false)) {
            wp_send_json_error(array('message' => 'Security check failed.'));
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Permission denied.'));
        }

        $updated = array();

        // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verified above
        if (isset($_POST['auth_type'])) {
            $auth_type = sanitize_text_field(wp_unslash($_POST['auth_type']));
            if (in_array($auth_type, array('oauth', 'awssig4'), true)) {
                update_option('psfa_auth_type', $auth_type);
                $updated[] = 'auth_type';
            }
        }

        if (isset($_POST['access_key'])) {
            update_option('psfa_access_key_id', sanitize_text_field(wp_unslash($_POST['access_key'])));
            $updated[] = 'access_key';
        }

        if (isset($_POST['secret_key'])) {
            update_option('psfa_secret_access_key', sanitize_text_field(wp_unslash($_POST['secret_key'])));
            $updated[] = 'secret_key';
        }

        if (isset($_POST['associate_tag'])) {
            update_option('psfa_associate_tag', sanitize_text_field(wp_unslash($_POST['associate_tag'])));
            $updated[] = 'associate_tag';
        }

        if (isset($_POST['oauth_client_id'])) {
            update_option('psfa_oauth_client_id', sanitize_text_field(wp_unslash($_POST['oauth_client_id'])));
            $updated[] = 'oauth_client_id';
        }

        if (isset($_POST['oauth_client_secret'])) {
            update_option('psfa_oauth_client_secret', sanitize_text_field(wp_unslash($_POST['oauth_client_secret'])));
            $updated[] = 'oauth_client_secret';
        }

        if (isset($_POST['oauth_credential_version'])) {
            update_option('psfa_oauth_credential_version', sanitize_text_field(wp_unslash($_POST['oauth_credential_version'])));
            $updated[] = 'oauth_credential_version';
        }

        if (isset($_POST['oauth_auth_region'])) {
            update_option('psfa_oauth_auth_region', sanitize_text_field(wp_unslash($_POST['oauth_auth_region'])));
            $updated[] = 'oauth_auth_region';
        }

        if (isset($_POST['marketplace'])) {
            update_option('psfa_marketplace', sanitize_text_field(wp_unslash($_POST['marketplace'])));
            $updated[] = 'marketplace';
        }
        // phpcs:enable WordPress.Security.NonceVerification.Missing

        wp_send_json_success(array(
            'message' => 'Credentials saved successfully.',
            'updated' => $updated,
        ));
    }

    /**
     * AJAX handler for clearing all plugin caches.
     *
     * Clears both product and search result transients.
     */
    public function ajax_clear_cache()
    {
        // Verify nonce.
        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'psfa_admin_nonce' ) ) {
            wp_send_json_error( array( 'message' => __( 'Security check failed.', 'paapi-product-search-for-amazon' ) ) );
        }

        // Only admins can clear cache.
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'paapi-product-search-for-amazon' ) ) );
        }

        // Ensure cache files are loaded (they may not be if no search has run yet).
        require_once PSFA_PLUGIN_DIR . 'src/core/cache/product-cache.php';
        require_once PSFA_PLUGIN_DIR . 'src/core/cache/search-cache.php';

        if ( function_exists( 'psfa_clear_all_caches' ) ) {
            $counts = psfa_clear_all_caches();
            wp_send_json_success( array(
                'message'  => sprintf(
                    /* translators: %1$d: product cache count, %2$d: search cache count */
                    __( 'Cache cleared: %1$d product entries, %2$d search entries.', 'paapi-product-search-for-amazon' ),
                    $counts['products'],
                    $counts['searches']
                ),
                'products' => $counts['products'],
                'searches' => $counts['searches'],
            ) );
            return;
        }

        wp_send_json_error( array( 'message' => __( 'Cache functions not available.', 'paapi-product-search-for-amazon' ) ) );
    }

    /**
     * Auto-clear search cache when marketplace or associate tag changes.
     *
     * Product cache is NOT cleared because ASINs are cross-marketplace for titles/images.
     */
    public function auto_clear_search_cache()
    {
        require_once PSFA_PLUGIN_DIR . 'src/core/cache/search-cache.php';
        require_once PSFA_PLUGIN_DIR . 'src/core/cache/product-cache.php';

        if ( function_exists( 'psfa_clear_search_cache' ) ) {
            psfa_clear_search_cache();
        }
    }

    /**
     * AJAX handler for cache statistics (React admin Cache page).
     */
    public function ajax_cache_stats() {
        if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'psfa_admin_nonce' ) ) {
            wp_send_json_error( array( 'message' => 'Security check failed.' ) );
        }
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( array( 'message' => 'Permission denied.' ) );
        }

        global $wpdb;

        $entries = array();
        $now     = time();

        // Product cache entries.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $product_rows = $wpdb->get_results(
            "SELECT option_name, option_value FROM {$wpdb->options}
             WHERE option_name LIKE '_transient_psfa\_product\_%'
             AND option_name NOT LIKE '_transient_timeout_%'
             ORDER BY option_name
             LIMIT 200"
        );

        // Search cache entries (include value for metadata extraction).
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $search_rows = $wpdb->get_results(
            "SELECT option_name, option_value FROM {$wpdb->options}
             WHERE option_name LIKE '_transient_psfa\_search\_%'
             AND option_name NOT LIKE '_transient_timeout_%'
             ORDER BY option_name
             LIMIT 200"
        );

        // Batch-fetch all timeouts in one query.
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $timeout_rows = $wpdb->get_results(
            "SELECT option_name, option_value FROM {$wpdb->options}
             WHERE option_name LIKE '_transient_timeout_psfa\_product\_%'
             OR option_name LIKE '_transient_timeout_psfa\_search\_%'"
        );
        $timeouts = array();
        foreach ( $timeout_rows as $row ) {
            // Strip '_transient_timeout_' prefix to match against '_transient_' keys.
            $key             = str_replace( '_transient_timeout_', '_transient_', $row->option_name );
            $timeouts[ $key ] = (int) $row->option_value;
        }

        // Build product entries with title + ASIN + metadata.
        foreach ( $product_rows as $row ) {
            $asin  = str_replace( '_transient_psfa_product_', '', $row->option_name );
            $data  = maybe_unserialize( $row->option_value );
            $title = is_array( $data ) && isset( $data['title'] ) ? $data['title'] : '';
            $price = is_array( $data ) && isset( $data['price'] ) ? $data['price'] : '';
            $image = is_array( $data ) && isset( $data['image'] ) ? $data['image'] : '';
            $exp   = isset( $timeouts[ $row->option_name ] ) ? $timeouts[ $row->option_name ] : 0;

            $created = is_array( $data ) && isset( $data['_psfa_created_at'] ) ? (int) $data['_psfa_created_at'] : 0;

            $entries[] = array(
                'type'       => 'product',
                'key'        => $asin,
                'label'      => $title ? $title : $asin,
                'expires'    => $exp > 0 ? $exp - $now : 0,
                'created_at' => $created,
                'meta'       => array(
                    'asin'  => $asin,
                    'title' => $title,
                    'price' => $price,
                    'image' => $image,
                ),
            );
        }

        // Build search entries with metadata from embedded _psfa_meta.
        foreach ( $search_rows as $row ) {
            $hash = str_replace( '_transient_psfa_search_', '', $row->option_name );
            $exp  = isset( $timeouts[ $row->option_name ] ) ? $timeouts[ $row->option_name ] : 0;
            $data = maybe_unserialize( $row->option_value );

            // Extract embedded metadata (added in 2.11.0).
            $meta     = is_array( $data ) && isset( $data['_psfa_meta'] ) ? $data['_psfa_meta'] : array();
            $keywords = ! empty( $meta['keywords'] ) ? $meta['keywords'] : '';
            $category = ! empty( $meta['category'] ) ? $meta['category'] : 'All';
            $page     = ! empty( $meta['page'] ) ? (int) $meta['page'] : 1;
            $results  = is_array( $data ) && isset( $data['items'] ) ? count( $data['items'] ) : 0;

            // Build a human-readable label.
            $label = $keywords ? $keywords : 'Search #' . substr( $hash, 0, 8 );
            if ( $page > 1 ) {
                $label .= ' (page ' . $page . ')';
            }

            $created = ! empty( $meta['created_at'] ) ? (int) $meta['created_at'] : 0;

            $entries[] = array(
                'type'       => 'search',
                'key'        => $hash,
                'label'      => $label,
                'expires'    => $exp > 0 ? $exp - $now : 0,
                'created_at' => $created,
                'meta'       => array(
                    'keywords'            => $keywords,
                    'category'            => $category,
                    'page'                => $page,
                    'results'             => $results,
                    'sort'                => ! empty( $meta['sort'] ) ? $meta['sort'] : '',
                    'brand'               => ! empty( $meta['brand'] ) ? $meta['brand'] : '',
                    'condition'           => ! empty( $meta['condition'] ) ? $meta['condition'] : '',
                    'merchant'            => ! empty( $meta['merchant'] ) ? $meta['merchant'] : '',
                    'availability'        => ! empty( $meta['availability'] ) ? $meta['availability'] : '',
                    'min_price'           => ! empty( $meta['min_price'] ) ? (int) $meta['min_price'] : 0,
                    'max_price'           => ! empty( $meta['max_price'] ) ? (int) $meta['max_price'] : 0,
                    'min_saving_percent'  => ! empty( $meta['min_saving_percent'] ) ? (int) $meta['min_saving_percent'] : 0,
                    'min_reviews_rating'  => ! empty( $meta['min_reviews_rating'] ) ? (int) $meta['min_reviews_rating'] : 0,
                    'has_deal'            => ! empty( $meta['has_deal'] ) ? $meta['has_deal'] : '',
                    'purpose'             => ! empty( $meta['purpose'] ) ? $meta['purpose'] : '',
                ),
            );
        }

        wp_send_json_success( array(
            'products' => count( $product_rows ),
            'searches' => count( $search_rows ),
            'entries'  => $entries,
        ) );
    }

    /**
     * AJAX handler for resetting settings from React admin
     */
    public function ajax_reset_settings()
    {
        // Verify nonce
        if (!check_ajax_referer('psfa_admin_nonce', 'nonce', false)) {
            wp_send_json_error(array('message' => 'Security check failed.'));
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Permission denied.'));
        }

        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified above
        $section = isset($_POST['section']) ? sanitize_text_field(wp_unslash($_POST['section'])) : 'all';

        $reset = array();

        if ($section === 'appearance' || $section === 'themes' || $section === 'all') {
            update_option('psfa_base_color', 'neutral');
            update_option('psfa_accent_color', 'orange');
            update_option('psfa_style', 'vega');
            update_option('psfa_radius', '0.5');
            update_option('psfa_dark_mode', '0');
            update_option('psfa_font', '');
            update_option('psfa_button_text', 'Buy on Amazon');
            update_option('psfa_hide_amazon_icon', '0');
            $reset[] = 'appearance';
        }

        if ($section === 'layout' || $section === 'all') {
            update_option('psfa_display_mode', 'grid');
            update_option('psfa_hide_category_banner', '0');
            update_option('psfa_hide_search', '0');
            update_option('psfa_hide_filters', '0');
            update_option('psfa_white_label', '0');
            update_option('psfa_filter_visibility', array(
                'brand' => true, 'price' => true, 'sort' => true, 'condition' => true,
                'merchant' => true, 'availability' => true, 'has_deal' => true, 'deals' => true,
            ));
            $reset[] = 'layout';
        }

        if ($section === 'features' || $section === 'all') {
            delete_option('psfa_features');
            $reset[] = 'features';
        }

        wp_send_json_success(array(
            'message' => 'Settings have been reset to defaults.',
            'reset' => $reset,
        ));
    }

    /**
     * Sanitize display mode
     *
     * IMPORTANT:
     * When saving from other tabs (themes/customization), the display mode field
     * is not posted. In those cases WordPress may pass null/empty here.
     * To avoid resetting the user's choice back to "grid", we keep the existing
     * option value when no new value is submitted.
     */
    public function sanitize_display_mode($mode)
    {
        if ($mode === null || $mode === '') {
            $existing = get_option('psfa_display_mode', 'grid');
            return $existing ? $existing : 'grid';
        }
        $valid_modes = array('grid', 'list');
        if (in_array($mode, $valid_modes)) {
            return $mode;
        }
        return 'grid';
    }

    /**
     * Sanitize text field safely (handles null values)
     */
    public function sanitize_text_field_safe($value)
    {
        if ($value === null || $value === false) {
            return '';
        }
        return sanitize_text_field($value);
    }

    /**
     * Sanitize hex color safely (handles null values)
     */
    public function sanitize_hex_color_safe($value)
    {
        if ($value === null || $value === false || $value === '') {
            return '';
        }
        return sanitize_hex_color($value);
    }

    /**
     * Sanitize checkbox value
     */
    public function sanitize_checkbox($value)
    {
        return isset($value) && $value === '1' ? '1' : '0';
    }

    /**
     * Sanitize filter visibility settings
     */
    public function sanitize_filter_visibility($input)
    {
        $valid_filters = array('brand', 'price', 'sort', 'condition', 'merchant', 'availability', 'has_deal', 'deals');
        $sanitized = array();

        foreach ($valid_filters as $filter) {
            $sanitized[$filter] = !empty($input[$filter]);
        }

        return $sanitized;
    }

    /**
     * Sanitize premium feature toggles
     */
    public function sanitize_features($input)
    {
        $valid_features = array('deal_badge', 'deal_countdown', 'prime_exclusive', 'low_stock', 'buybox_winner', 'used_price');
        $sanitized = array();

        foreach ($valid_features as $feature) {
            $sanitized[$feature] = !empty($input[$feature]);
        }

        return $sanitized;
    }

    /**
     * Helper function: Check if premium features are available (Freemius)
     */
    private function is_premium()
    {
        if (function_exists('psfa_fs')) {
            return psfa_fs()->can_use_premium_code();
        }
        return false;
    }

    /**
     * Helper function: Get max pages allowed
     */
    private function get_max_pages()
    {
        if ($this->is_premium()) {
            return 999;
            // Unlimited for premium
        }
        return 5;
        // Free limit
    }

    /**
     * Helper function: Get available categories
     */
    private function get_available_categories()
    {
        $all_categories = array(
            'Electronics',
            'Books',
            'Fashion',
            'ToysAndGames',
            'HomeGarden',
            'TodaysDeals',
            'Automotive',
            'Baby',
            'Beauty',
            'Computers',
            'Grocery',
            'HealthPersonalCare',
            'HomeImprovement',
            'Industrial',
            'KindleStore',
            'Luggage',
            'MoviesTV',
            'Music',
            'MusicalInstruments',
            'OfficeProducts',
            'PetSupplies',
            'Software',
            'SportsOutdoors',
            'ToolsHomeImprovement',
            'VideoGames',
            'Watches'
        );
        if ($this->is_premium()) {
            return $all_categories;
        }
        // Free version: only 5 categories
        return array(
            'Electronics',
            'Books',
            'Fashion',
            'ToysAndGames',
            'HomeGarden'
        );
    }

    /**
     * Helper function: Check if category is available
     */
    private function is_category_available($category)
    {
        $available = $this->get_available_categories();
        return in_array($category, $available);
    }

    /**
     * Enqueue admin styles and scripts
     */
    public function enqueue_admin_styles($hook)
    {
        // Only load on our settings page (top-level menu hook format: toplevel_page_{slug})
        if ($hook !== 'toplevel_page_paapi-product-search-for-amazon') {
            return;
        }

        // Load Tailwind CSS via CDN
        require_once PSFA_PLUGIN_DIR . 'src/core/admin/admin-styles.php';
        psfa_enqueue_tailwind_admin();
        wp_add_inline_style('wp-admin', psfa_get_tailwind_admin_styles());

        // Always use React admin
        $this->enqueue_react_admin_assets();
    }

    /**
     * Enqueue React admin assets
     */
    private function enqueue_react_admin_assets()
    {
        // Load view helpers for psfa_is_premium() function
        require_once PSFA_PLUGIN_DIR . 'src/core/search/view-helpers.php';
        
        

        // Enqueue React admin CSS (if generated)
        $css_path = PSFA_PLUGIN_DIR . 'src/assets/js/admin-app.css';
        if (file_exists($css_path)) {
            wp_enqueue_style(
                'psfa-react-admin',
                PSFA_PLUGIN_URL . 'src/assets/js/admin-app.css',
                array(),
                PSFA_VERSION
            );
        }

        // Enqueue React admin JS
        wp_enqueue_script(
            'psfa-react-admin',
            PSFA_PLUGIN_URL . 'src/assets/js/admin-app.js',
            array(),
            PSFA_VERSION,
            true
        );

        // Localize script with admin config
        $is_premium = false;
        $is_premium_code = false;
        $needs_license = false;
        $fs_affix = '';
        $upgrade_url = 'https://paapiplugin.com/pricing/#plans';
        

        // Get addon data if available
        $addon_data = array(
            'comparisonTables' => array(),
            'productBoxes' => array(),
        );
        if (class_exists('PSFA_Comparison_Tables')) {
            $tables = PSFA_Comparison_Tables::get_tables();
            foreach ($tables as $table) {
                $addon_data['comparisonTables'][] = array(
                    'id' => $table['id'],
                    'name' => $table['name'],
                    'product_count' => PSFA_Comparison_Tables::get_product_count($table['id']),
                );
            }
        }
        if (class_exists('PSFA_Product_Boxes')) {
            $boxes = PSFA_Product_Boxes::get_boxes();
            foreach ($boxes as $box) {
                $addon_data['productBoxes'][] = array(
                    'id' => $box['id'] ?? 0,
                    'asin' => $box['asin'],
                    'title' => $box['title'] ?? '',
                    'image' => $box['image_url'] ?? '',
                    'rating' => isset($box['rating']) ? (float) $box['rating'] : null,
                    'review_count' => isset($box['review_count']) ? (int) $box['review_count'] : null,
                    'badge_type' => $box['badge_type'] ?? '',
                );
            }
        }

        wp_localize_script('psfa-react-admin', 'psfaAdmin', array(
            'ajaxUrl' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('psfa_admin_nonce'),
            'isPremium' => $is_premium,
            'isPremiumCode' => $is_premium_code,
            'needsLicense' => $needs_license,
            'fsAffix' => $fs_affix,
            'isMockMode' => defined('PSFA_MOCK_MODE') && PSFA_MOCK_MODE,
            'upgradeUrl' => $upgrade_url,
            'pluginUrl' => PSFA_PLUGIN_URL,
            'addons' => array(
                'productBoxes' => class_exists('PSFA_Boxes_Admin'),
                'comparisonTables' => class_exists('PSFA_Tables_Admin'),
            ),
            'addonNeedsUpgrade' => array(
                'productBoxes' => class_exists('PSFA_Boxes_Promo') && ! class_exists('PSFA_Boxes_Admin'),
                'comparisonTables' => class_exists('PSFA_Tables_Promo') && ! class_exists('PSFA_Tables_Admin'),
            ),
            'addonDeactivateUrls' => array(
                'productBoxes' => class_exists('PSFA_Boxes_Promo') && defined('PSFA_BOXES_BASENAME')
                    ? add_query_arg( '_wpnonce', wp_create_nonce( 'deactivate-plugin_' . PSFA_BOXES_BASENAME ), admin_url( 'plugins.php?action=deactivate&plugin=' . PSFA_BOXES_BASENAME ) )
                    : '',
                'comparisonTables' => class_exists('PSFA_Tables_Promo') && defined('PSFA_TABLES_BASENAME')
                    ? add_query_arg( '_wpnonce', wp_create_nonce( 'deactivate-plugin_' . PSFA_TABLES_BASENAME ), admin_url( 'plugins.php?action=deactivate&plugin=' . PSFA_TABLES_BASENAME ) )
                    : '',
            ),
            'addonData' => $addon_data,
            'addonNonces' => array(
                'comparisonTables' => wp_create_nonce('psfa_tables_admin_nonce'),
                'comparisonTablesFrontend' => wp_create_nonce('psfa_tables_nonce'),
                'productBoxes' => wp_create_nonce('psfa_boxes_admin_nonce'),
                'productBoxesFrontend' => wp_create_nonce('psfa_boxes_nonce'),
            ),
            'settings' => array(
                // Credentials
                'authType' => get_option('psfa_auth_type', 'oauth'),
                'accessKey' => get_option('psfa_access_key_id', ''),
                'secretKey' => get_option('psfa_secret_access_key', ''),
                'associateTag' => get_option('psfa_associate_tag', ''),
                'oauthClientId' => get_option('psfa_oauth_client_id', ''),
                'oauthClientSecret' => get_option('psfa_oauth_client_secret', ''),
                'oauthCredentialVersion' => get_option('psfa_oauth_credential_version', ''),
                'oauthAuthRegion' => get_option('psfa_oauth_auth_region', 'us-east-1'),
                'marketplace' => get_option('psfa_marketplace', 'www.amazon.com'),
                // Appearance axes
                'baseColor' => get_option('psfa_base_color', 'neutral'),
                'accentColor' => get_option('psfa_accent_color', 'orange'),
                'style' => get_option('psfa_style', 'vega'),
                'radius' => get_option('psfa_radius', '0.5'),
                'darkMode' => get_option('psfa_dark_mode', '0') === '1',
                'font' => get_option('psfa_font', ''),
                // Layout
                'displayMode' => get_option('psfa_display_mode', 'grid'),
                'hideSearch' => get_option('psfa_hide_search', '0') === '1',
                'hideFilters' => get_option('psfa_hide_filters', '0') === '1',
                'hideCategoryBanner' => get_option('psfa_hide_category_banner', '0') === '1',
                'whiteLabel' => get_option('psfa_white_label', '0') === '1',
                'filterVisibility' => (object) get_option('psfa_filter_visibility', array(
                    'brand' => true, 'price' => true, 'sort' => true, 'condition' => true,
                    'merchant' => true, 'availability' => true, 'has_deal' => true, 'deals' => true,
                )),
                'features' => (object) array_merge(
                    array('deal_badge' => true, 'prime_exclusive' => true, 'low_stock' => true, 'deal_countdown' => true),
                    get_option('psfa_features', array())
                ),
                // Customization (premium-gated - free users always get defaults)
                'buttonText' => $is_premium ? get_option('psfa_button_text', 'Buy on Amazon') : 'Buy on Amazon',
                'hideAmazonIcon' => $is_premium && get_option('psfa_hide_amazon_icon', '0') === '1',
            ),
        ));
    }

    /**
     * Handle reset customization request
     * Called early in admin_init to avoid headers already sent errors
     */
    public function handle_reset_customization()
    {
        // Only process on our settings page
        if (!isset($_GET['page']) || $_GET['page'] !== 'paapi-product-search-for-amazon') {
            return;
        }

        // Check if reset was requested via GET (link)
        if (isset($_GET['psfa_reset_customization']) && $_GET['psfa_reset_customization'] === '1') {
            // Verify nonce for GET request
            $nonce = isset($_GET['_wpnonce']) ? sanitize_text_field(wp_unslash($_GET['_wpnonce'])) : '';
            if (empty($nonce) || !wp_verify_nonce($nonce, 'psfa_reset_customization')) {
                wp_die('Security check failed');
            }

            // Determine current section so we can reset only that section's settings.
            // Support both 'section' (new) and 'tab' (legacy) parameters
            $section = 'customization';
            // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only parameter used for redirect and branching.
            $raw_section = isset($_GET['section']) ? sanitize_text_field(wp_unslash($_GET['section'])) : (isset($_GET['tab']) ? sanitize_text_field(wp_unslash($_GET['tab'])) : '');
            if (!empty($raw_section)) {
                $sanitized_section = sanitize_text_field($raw_section);
                $allowed_sections = array('credentials', 'appearance', 'themes', 'customization', 'features', 'layout');
                if (in_array($sanitized_section, $allowed_sections, true)) {
                    $section = $sanitized_section;
                }
            }

            // Reset settings depending on section (section-scoped reset).
            if ($section === 'appearance' || $section === 'themes') {
                update_option('psfa_base_color', 'neutral');
                update_option('psfa_accent_color', 'orange');
                update_option('psfa_style', 'vega');
                update_option('psfa_radius', '0.5');
                update_option('psfa_dark_mode', '0');
                update_option('psfa_font', '');
                update_option('psfa_button_text', 'Buy on Amazon');
                update_option('psfa_hide_amazon_icon', '0');
            } elseif ($section === 'layout') {
                // Reset only layout-related options.
                update_option('psfa_display_mode', 'grid');
                update_option('psfa_hide_category_banner', '0');
                update_option('psfa_hide_search', '0');
                update_option('psfa_hide_filters', '0');
                update_option('psfa_filter_visibility', array(
                    'brand' => true,
                    'price' => true,
                    'sort' => true,
                    'condition' => true,
                    'merchant' => true,
                    'availability' => true,
                    'has_deal' => true,
                    'deals' => true,
                ));
            } elseif ($section === 'customization') {
                update_option('psfa_button_text', 'Buy on Amazon');
            } elseif ($section === 'features') {
                // Reset feature toggles to defaults.
                delete_option('psfa_features');
            }

            // Redirect immediately before any output, keeping current section.
            $redirect_url = add_query_arg(
                array(
                    'page' => 'paapi-product-search-for-amazon',
                    'section' => $section,
                    'settings-updated' => 'true',
                    'reset' => '1',
                ),
                admin_url('admin.php')
            );
            wp_safe_redirect($redirect_url);
            exit;
        }
    }

    /**
     * Redirect mock-mode-only sections to credentials if not in mock mode
     * Called early in admin_init to avoid headers already sent errors
     */
    public function handle_mock_section_redirect()
    {
        // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only redirect check
        // Only process on our settings page
        if (!isset($_GET['page']) || $_GET['page'] !== 'paapi-product-search-for-amazon') {
            return;
        }

        $section = isset($_GET['section']) ? sanitize_text_field(wp_unslash($_GET['section'])) : '';
        // phpcs:enable WordPress.Security.NonceVerification.Recommended

        $mock_only_sections = array('api-offers-test', 'api-oauth-test');
        if (in_array($section, $mock_only_sections, true) && !(defined('PSFA_MOCK_MODE') && PSFA_MOCK_MODE)) {
            wp_safe_redirect(admin_url('admin.php?page=paapi-product-search-for-amazon&section=credentials'));
            exit;
        }
    }

    /**
     * Render settings page
     */
    public function render_settings_page()
    {
        if (!current_user_can('manage_options')) {
            return;
        }

        // Display success message if reset was performed
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Display-only parameter, wp_unslash handles sanitization, value is compared to literal '1'
        if (isset($_GET['reset']) && wp_unslash($_GET['reset']) === '1') {
            add_settings_error(
                'psfa_settings',
                'psfa_settings_reset',
                'Settings have been reset to defaults.',
                'success'
            );
        }

        // Render React admin
        $this->render_react_admin();
    }

    /**
     * Render React admin container
     */
    private function render_react_admin()
    {
        ?>
        <div id="psfa-admin-root" class="psfa-react-admin tw:min-h-min">
            <div class="tw:font-rubik tw:antialiased">
                <div class="tw:flex tw:min-h-screen tw:bg-gray-50 tw:items-center tw:justify-center">
                    <div class="tw:text-gray-500">Loading...</div>
                </div>
            </div>
        </div>
        <?php
    }

    /**
     * Enqueue scripts and styles
     */
    public function enqueue_scripts()
    {
        // Check if shortcode is in post content
        global $post;
        $has_shortcode = false;
        if ($post instanceof WP_Post && !empty($post->post_content)) {
            $has_shortcode = has_shortcode($post->post_content, 'psfa_search');
        }

        // Always load in mock mode (demo sites)
        $is_mock_mode = defined('PSFA_MOCK_MODE') && PSFA_MOCK_MODE;

        if ($has_shortcode || $is_mock_mode) {
            require_once PSFA_PLUGIN_DIR . 'src/core/shared/theme-manager.php';

            // Load frontend Tailwind CSS (tree-shaken, only used classes)
            $tailwind_frontend_path = PSFA_PLUGIN_DIR . 'src/assets/css/tailwind-frontend.min.css';
            if (file_exists($tailwind_frontend_path)) {
                wp_enqueue_style(
                    'psfa-tailwind',
                    PSFA_PLUGIN_URL . 'src/assets/css/tailwind-frontend.min.css',
                    array(),
                    PSFA_VERSION
                );
            }

            // Layered appearance CSS (base → dark override → accent → style → product → dark rules)
            $base_color = psfa_get_selected_base_color();
            $accent_color = psfa_get_selected_accent_color();
            $is_dark = psfa_is_dark_mode();
            $font_slug = psfa_get_selected_font();
            $last_handle = 'psfa-tailwind';

            // 1. Base color (always load light version for :root portal fallbacks)
            $base_url = psfa_get_base_css_url($base_color);
            if ($base_url) {
                wp_enqueue_style('psfa-base', $base_url, array('psfa-tailwind'), PSFA_VERSION);
                $last_handle = 'psfa-base';
            }

            // 2. Dark base override (when dark mode, higher specificity compound selector)
            if ($is_dark) {
                $dark_base_url = psfa_get_base_dark_css_url($base_color);
                if ($dark_base_url) {
                    wp_enqueue_style('psfa-base-dark', $dark_base_url, array($last_handle), PSFA_VERSION);
                    $last_handle = 'psfa-base-dark';
                }
            }

            // 3. Accent color
            $accent_url = psfa_get_accent_css_url($accent_color);
            if ($accent_url) {
                wp_enqueue_style('psfa-accent', $accent_url, array($last_handle), PSFA_VERSION);
                $last_handle = 'psfa-accent';
            }

            // 4. Style (card padding, border, shadow, button radius, spacing)
            $style = psfa_get_selected_style();
            $style_url = psfa_get_style_css_url($style);
            if ($style_url) {
                wp_enqueue_style('psfa-style', $style_url, array($last_handle), PSFA_VERSION);
                $last_handle = 'psfa-style';
            }

            // 5. Product Amazon (buy button gradient, always loaded)
            $product_url = psfa_get_product_css_url();
            if ($product_url) {
                wp_enqueue_style('psfa-product', $product_url, array($last_handle), PSFA_VERSION);
                $last_handle = 'psfa-product';
            }

            // 6. Dark mode component rules (when dark mode)
            if ($is_dark) {
                $dark_rules_url = psfa_get_dark_rules_css_url();
                if ($dark_rules_url) {
                    wp_enqueue_style('psfa-dark-rules', $dark_rules_url, array($last_handle), PSFA_VERSION);
                    $last_handle = 'psfa-dark-rules';
                }
            }

            // 7. Google Font (when a custom font is selected)
            if (!empty($font_slug)) {
                $font_url = psfa_get_google_font_url($font_slug);
                if ($font_url) {
                    wp_enqueue_style('psfa-google-font', $font_url, array(), PSFA_VERSION);
                }
            }

            // 8. Demo/mock mode: preload ALL theme CSS variants for instant preview
            // Each file scopes to its own class (e.g. .psfa-base-stone), so loading all
            // is safe - only the matching class on the container activates the rules.
            if ($is_mock_mode) {
                $all_bases = array_keys(psfa_get_base_colors());
                foreach ($all_bases as $b) {
                    if ($b !== $base_color) {
                        $url = psfa_get_base_css_url($b);
                        if ($url) {
                            wp_enqueue_style("psfa-base-{$b}", $url, array('psfa-tailwind'), PSFA_VERSION);
                        }
                        $dark_url = psfa_get_base_dark_css_url($b);
                        if ($dark_url) {
                            wp_enqueue_style("psfa-base-dark-{$b}", $dark_url, array('psfa-tailwind'), PSFA_VERSION);
                        }
                    }
                }
                // Load current base dark variant if not already loaded (dark mode off)
                if (!$is_dark) {
                    $dark_base_url = psfa_get_base_dark_css_url($base_color);
                    if ($dark_base_url) {
                        wp_enqueue_style('psfa-base-dark', $dark_base_url, array('psfa-tailwind'), PSFA_VERSION);
                    }
                }
                $all_accents = array_keys(psfa_get_accent_colors());
                foreach ($all_accents as $a) {
                    if ($a !== $accent_color) {
                        $url = psfa_get_accent_css_url($a);
                        if ($url) {
                            wp_enqueue_style("psfa-accent-{$a}", $url, array('psfa-tailwind'), PSFA_VERSION);
                        }
                    }
                }
                $all_styles = array_keys(psfa_get_styles());
                foreach ($all_styles as $s) {
                    if ($s !== $style) {
                        $url = psfa_get_style_css_url($s);
                        if ($url) {
                            wp_enqueue_style("psfa-style-{$s}", $url, array('psfa-tailwind'), PSFA_VERSION);
                        }
                    }
                }
                // Load dark rules even when dark mode is off
                if (!$is_dark) {
                    $dark_rules_url = psfa_get_dark_rules_css_url();
                    if ($dark_rules_url) {
                        wp_enqueue_style('psfa-dark-rules', $dark_rules_url, array('psfa-tailwind'), PSFA_VERSION);
                    }
                }
            }

            // Enqueue React frontend app (React 19 + components)
            wp_enqueue_script(
                'psfa-frontend',
                PSFA_PLUGIN_URL . 'src/assets/js/frontend-app.js',
                array(),
                PSFA_VERSION,
                true
            );

            // Localize script for AJAX
            wp_localize_script('psfa-frontend', 'psfaAjax', array(
                'ajaxurl' => admin_url('admin-ajax.php'),
                'nonce' => wp_create_nonce('psfa_ajax_nonce'),
                'pluginUrl' => plugin_dir_url(__FILE__),
            ));
        }
    }

    /**
     * Render the search page (shortcode handler)
     *
     * Supported shortcode attributes:
     * - category: Pre-select a category (e.g., "Electronics")
     * - auto_detect: Auto-detect category from page context (default: "yes")
     * - query: Pre-fill search query
     * - brand: Pre-fill brand filter
     * - sort: Pre-set sort order
     * - hide_search: Hide the search bar ("true" or "false")
     * - hide_filters: Hide all filters ("true" or "false")
     * - pagination: Pagination type ("infinite", "button", or "none")
     *
     * Examples:
     * [psfa_search] - Default with search and filters
     * [psfa_search query="laptop" category="Electronics"]
     * [psfa_search query="headphones" hide_search="true" hide_filters="true"]
     * [psfa_search category="Books" pagination="button"]
     */
    public function render_search_page($atts)
    {
        // Parse shortcode attributes
        $atts = shortcode_atts(array(
            'category' => '',
            'auto_detect' => 'yes',
            'query' => '',
            'brand' => '',
            'sort' => '',
            'hide_search' => '',
            'hide_filters' => '',
            'hide_category_banner' => '',
            'pagination' => 'infinite',  // infinite, button, none
            'count' => 20,  // Number of products to display (2-20), only applies when pagination="none"
        ), $atts, 'psfa_search');

        // Allow auto_detect to work with 'yes' value
        if (strtolower($atts['auto_detect']) === 'yes') {
            $atts['auto_detect'] = 'no';
        }

        // Normalize boolean attributes - keep empty string if not explicitly set (falls through to admin setting)
        if ($atts['hide_search'] !== '') {
            $atts['hide_search'] = in_array(strtolower($atts['hide_search']), array('true', '1', 'yes'), true);
        }
        if ($atts['hide_filters'] !== '') {
            $atts['hide_filters'] = in_array(strtolower($atts['hide_filters']), array('true', '1', 'yes'), true);
        }
        if ($atts['hide_category_banner'] !== '') {
            $atts['hide_category_banner'] = in_array(strtolower($atts['hide_category_banner']), array('true', '1', 'yes'), true);
        }

        // Validate pagination type
        if (!in_array($atts['pagination'], array('infinite', 'button', 'none'), true)) {
            $atts['pagination'] = 'infinite';
        }

        // Validate count (2-20, only applies when pagination="none")
        $atts['count'] = max(2, min(20, absint($atts['count'])));

        // Auto-detect category from page context if enabled and no category specified
        if (empty($atts['category']) && $atts['auto_detect'] === 'no') {
            $atts['category'] = $this->detect_category_from_context();
        }

        // Pass shortcode attributes to template via global variable
        global $psfa_shortcode_atts;
        $psfa_shortcode_atts = $atts;

        // Legacy support: also set category separately
        global $psfa_shortcode_category;
        $psfa_shortcode_category = $atts['category'];
        // Include the main search functionality
        ob_start();
        include PSFA_PLUGIN_DIR . 'src/core/frontend/template.php';
        return ob_get_clean();
    }

    /**
     * Detect category from page context (slug, title, etc.)
     */
    private function detect_category_from_context()
    {
        global $post;
        if (!$post) {
            return '';
        }
        // Get page slug
        $slug = $post->post_name;
        $title = strtolower($post->post_title);
        // Category mapping (case-insensitive)
        $category_keywords = array(
            'fashion' => 'Fashion',
            'apparel' => 'Fashion',
            'clothing' => 'Fashion',
            'electronics' => 'Electronics',
            'books' => 'Books',
            'toys' => 'ToysAndGames',
            'toysandgames' => 'ToysAndGames',
            'home' => 'HomeGarden',
            'homegarden' => 'HomeGarden',
            'garden' => 'HomeGarden',
        );
        // Check slug
        foreach ($category_keywords as $keyword => $category) {
            if (strpos($slug, $keyword) !== false) {
                return $category;
            }
        }
        // Check title
        foreach ($category_keywords as $keyword => $category) {
            if (strpos($title, $keyword) !== false) {
                return $category;
            }
        }
        // Check page template name (if using custom template)
        $template = get_page_template_slug($post->ID);
        if ($template) {
            foreach ($category_keywords as $keyword => $category) {
                if (strpos(strtolower($template), $keyword) !== false) {
                    return $category;
                }
            }
        }
        return '';
    }
}

// Initialize the plugin
PSFA_Product_Search::get_instance();
