<?php
/**
 * Setup Wizard - Guided First-Run Configuration
 *
 * @package   IPAIS\Admin
 * @since 1.0.0
 * @author    InfyPress
 * @license   GPL-2.0-or-later
 */

namespace IPAIS\Admin;

// Exit if accessed directly.
if ( ! defined( 'ABSPATH' ) ) {
    exit;
}

use IPAIS\Core\INFYP_API_Key_Manager;
use IPAIS\Core\INFYP_Language_Manager;

/**
 * Setup Wizard Class
 *
 * Provides a fullscreen, step-by-step setup experience for first-time users
 * to configure API keys and preferences before using the plugin.
 *
 * Wizard Steps:
 * 1. Welcome   — Introduction and feature overview
 * 2. API Keys  — Configure at least one AI provider (OpenAI, Gemini, Anthropic)
 * 3. Preferences — Website type, language, image count, newsletter opt-in
 * 4. Complete  — Success confirmation and redirect to dashboard
 *
 * Manages the fullscreen setup wizard including activation detection,
 * step rendering, AJAX save handlers, and admin notice display.
 *
 * Lifecycle:
 * 1. Singleton instantiated during plugin bootstrap
 * 2. Hooks registered in constructor (admin_menu, admin_init, admin_notices, AJAX)
 * 3. On wizard page request: intercept at admin_init → render fullscreen → exit
 * 4. On AJAX: validate nonce/capability → process step data → respond
 * 5. On non-wizard pages: check if notice should display
 *
 * Hook Integration:
 * - `admin_menu`: Register hidden wizard submenu page (under options.php)
 * - `admin_init` (priority 1): Redirect to wizard on first activation
 * - `admin_init` (priority 5): Intercept wizard page for fullscreen rendering
 * - `admin_notices`: Show setup reminder on non-wizard admin pages
 * - `wp_ajax_infyp_wizard_*`: Five AJAX handlers for wizard interactions
 *
 * Assets:
 * - CSS: framework/variables.css, framework/utilities.css, plugin/setup-wizard.css
 * - JS: setup-wizard.js (handles step navigation, API key validation, form submission)
 * - Fonts: Local plugin fonts (no external requests)
 *
 * @package   IPAIS\Admin
 * @since 1.0.0
 *
 * @see       INFYP_API_Key_Manager For API key storage and validation
 * @see       INFYP_Language_Manager For available language list
 * @see       includes/views/setup-wizard/wizard-page.php Main wizard template
 */
class INFYP_Setup_Wizard {

    /**
     * Singleton instance.
     *
     * @since 1.0.0
     * @var   INFYP_Setup_Wizard|null
     */
    private static $instance = null;

    /**
     * Wizard steps configuration.
     *
     * Lazy-loaded array of step definitions, each containing:
     * - 'name'  (string) Translated display label for the step indicator
     * - 'view'  (string) Template filename in views/setup-wizard/ (without .php)
     *
     * Populated on first call to get_steps() to avoid early translation loading.
     *
     * @since 1.0.0
     * @var   array<string, array{name: string, view: string}>
     */
    private $steps = [];

    /**
     * Returns the singleton instance.
     *
     * @since 1.0.0
     * @return INFYP_Setup_Wizard
     */
    public static function get_instance() {
        if ( self::$instance === null ) {
            self::$instance = new self();
        }
        return self::$instance;
    }

    /**
     * Constructor. Registers all WordPress hooks for wizard functionality.
     *
     * Hooks are split by timing:
     * - admin_menu: Register the hidden wizard submenu page
     * - admin_init (priority 1): Check for activation redirect transient
     * - admin_init (priority 5): Intercept wizard page for fullscreen rendering
     * - admin_notices: Show setup reminder notice
     * - wp_ajax_*: Five AJAX handlers for wizard interactions
     *
     * @since 1.0.0
     */
    public function __construct() {
        add_action( 'admin_menu', [ $this, 'register_wizard_page' ] );
        add_action( 'admin_init', [ $this, 'maybe_redirect_to_wizard' ], 1 );
        add_action( 'admin_init', [ $this, 'maybe_render_fullscreen_wizard' ], 5 );
        add_action( 'admin_notices', [ $this, 'maybe_show_wizard_notice' ] );

        // AJAX handlers
        add_action( 'wp_ajax_infyp_wizard_save_step', [ $this, 'ajax_save_step' ] );
        add_action( 'wp_ajax_infyp_wizard_complete', [ $this, 'ajax_complete_wizard' ] );
        add_action( 'wp_ajax_infyp_wizard_skip', [ $this, 'ajax_skip_wizard' ] );
        add_action( 'wp_ajax_infyp_wizard_validate_api_key', [ $this, 'ajax_validate_api_key' ] );
        add_action( 'wp_ajax_infyp_dismiss_wizard_notice', [ $this, 'ajax_dismiss_wizard_notice' ] );
    }

    /**
     * Returns the wizard step definitions, lazy-loaded on first call.
     *
     * Steps are defined here (not in the constructor) because WordPress
     * translation functions (__()) must not be called before the 'init' hook.
     * Since admin_init fires after init, lazy-loading here is safe.
     *
     * @since 1.0.0
     * @access private
     *
     * @return array<string, array{name: string, view: string}> Step definitions keyed by slug.
     */
    private function get_steps() {
        if ( empty( $this->steps ) ) {
            $this->steps = [
                'welcome' => [
                    'name'    => __( 'Welcome', 'infy-ai-image-seo-optimizer' ),
                    'view'    => 'step-welcome',
                ],
                'api-keys' => [
                    'name'    => __( 'API Keys', 'infy-ai-image-seo-optimizer' ),
                    'view'    => 'step-api-keys',
                ],
                'preferences' => [
                    'name'    => __( 'Preferences', 'infy-ai-image-seo-optimizer' ),
                    'view'    => 'step-preferences',
                ],
                'complete' => [
                    'name'    => __( 'Complete', 'infy-ai-image-seo-optimizer' ),
                    'view'    => 'step-complete',
                ],
            ];
        }
        return $this->steps;
    }

    /**
     * Register the wizard page (hidden from menu)
     *
     * The wizard page is registered under 'options.php' to hide it from
     * the admin menu while still being accessible via direct URL.
     *
     * @since 1.0.0
     *
     * @return void
     */
    public function register_wizard_page() {
        add_submenu_page(
            'options.php', // Hidden from menu
            __( 'Setup Wizard', 'infy-ai-image-seo-optimizer' ),
            __( 'Setup Wizard', 'infy-ai-image-seo-optimizer' ),
            'manage_options',
            'ai-image-seo-optimizer-wizard',
            [ $this, 'render_wizard_page' ]
        );
    }

    /**
     * Check if wizard should be shown and redirect if needed
     *
     * Handles automatic redirect to the setup wizard on first plugin activation.
     * Uses a transient to track the activation event and prevent redirect loops.
     * Includes fallback to option storage for managed hosts where transients
     * may not persist (WPEngine, Kinsta, etc.).
     *
     * Security considerations:
     * - Only redirects admin users with 'manage_options' capability
     * - Uses wp_safe_redirect() for secure redirects
     * - Skips redirect during AJAX, bulk activations, or if already on wizard page
     *
     * @since 1.0.0
     *
     * @return void
     */
    public function maybe_redirect_to_wizard() {
        // Only on admin
        if ( ! is_admin() || wp_doing_ajax() ) {
            return;
        }

        // Don't redirect if headers already sent
        if ( headers_sent() ) {
            return;
        }

        // Check user capability
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }

        // Don't redirect if wizard is completed
        if ( get_option( 'infyp_wizard_completed' ) ) {
            return;
        }

        // Don't redirect if already on wizard page
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        if ( isset( $_GET['page'] ) && $_GET['page'] === 'ai-image-seo-optimizer-wizard' ) {
            return;
        }

        // Don't redirect on bulk activations
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        if ( isset( $_GET['activate-multi'] ) ) {
            return;
        }

        // Check for activation redirect transient
        $should_redirect = false;

        if ( get_transient( 'infyp_wizard_redirect' ) ) {
            delete_transient( 'infyp_wizard_redirect' );
            $should_redirect = true;
        }

        // Fallback: Check option-based redirect for hosts with aggressive cache clearing
        // This handles WPEngine, Kinsta, and other managed hosts where transients
        // stored in object cache may be cleared during activation
        if ( ! $should_redirect ) {
            $redirect_time = get_option( 'infyp_wizard_redirect_time' );
            if ( $redirect_time && ( time() - (int) $redirect_time ) < 300 ) {
                // Within 5 minutes of activation, redirect to wizard
                delete_option( 'infyp_wizard_redirect_time' );
                $should_redirect = true;
            }
        }

        if ( $should_redirect ) {
            wp_safe_redirect( admin_url( 'admin.php?page=ai-image-seo-optimizer-wizard' ) );
            exit;
        }
    }

    /**
     * Render fullscreen wizard page (bypasses WordPress admin wrapper)
     *
     * Intercepts requests to the wizard page and renders a fullscreen
     * template that provides a focused setup experience without the
     * WordPress admin chrome.
     *
     * Security: Checks 'manage_options' capability before rendering.
     *
     * @since 1.0.0
     *
     * @return void
     */
    public function maybe_render_fullscreen_wizard() {
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        if ( ! isset( $_GET['page'] ) || $_GET['page'] !== 'ai-image-seo-optimizer-wizard' ) {
            return;
        }

        // Check permissions
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }

        // Render the fullscreen wizard and exit
        $this->render_fullscreen_wizard();
        exit;
    }

    /**
     * Renders the fullscreen wizard page.
     *
     * Outputs a complete standalone HTML page (outside the WP admin wrapper)
     * with the wizard interface, including all necessary styles and scripts.
     * The template is responsible for the full HTML document structure.
     *
     * Data passed to the template ($data array):
     * - steps: Full step definitions from get_steps()
     * - current_step: Current step slug (from ?step= parameter, default 'welcome')
     * - current_index: Zero-based integer index of current step
     * - wizard_data: Previously saved wizard preferences from wp_options
     * - has_openai/has_google/has_anthropic: Whether each provider key is configured
     * - languages: Available language list from INFYP_Language_Manager
     *
     * @since 1.0.0
     * @access private
     *
     * @return void Outputs HTML. Caller must call exit() after this method.
     */
    private function render_fullscreen_wizard() {
        $steps = $this->get_steps();

        // Get current step
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $current_step = isset( $_GET['step'] ) ? sanitize_key( $_GET['step'] ) : 'welcome';

        if ( ! isset( $steps[ $current_step ] ) ) {
            $current_step = 'welcome';
        }

        // Get step index for progress
        $step_keys = array_keys( $steps );
        $current_index = array_search( $current_step, $step_keys, true );

        // Get existing data
        $wizard_data = get_option( 'infyp_wizard_data', [] );
        $api_key_manager = INFYP_API_Key_Manager::get_instance();

        // Prepare data for template
        $data = [
            'steps'         => $steps,
            'current_step'  => $current_step,
            'current_index' => $current_index,
            'wizard_data'   => $wizard_data,
            'has_openai'    => $api_key_manager->has_api_key( 'openai' ),
            'has_google'    => $api_key_manager->has_api_key( 'google' ),
            'has_anthropic' => $api_key_manager->has_api_key( 'anthropic' ),
            'languages'     => INFYP_Language_Manager::get_available_languages(),
        ];

        // Enqueue wizard assets before rendering
        $this->enqueue_wizard_assets_direct();

        // Load the fullscreen wizard template
        include INFYP_PLUGIN_DIR . 'includes/views/setup-wizard/wizard-page.php';
    }

    /**
     * Enqueues wizard assets directly for the fullscreen wizard page.
     *
     * Called inside render_fullscreen_wizard() rather than via admin_enqueue_scripts
     * because the fullscreen wizard bypasses the normal WordPress admin rendering.
     * Assets must be registered and enqueued before the template calls wp_head().
     *
     * Asset chain (in dependency order):
     * 1. infyp-variables — CSS custom properties (colors, spacing, typography)
     * 2. infyp-utilities — Utility classes (flexbox, spacing, text)
     * 3. infyp-setup-wizard — Wizard-specific styles (steps, cards, forms)
     * 4. infyp-setup-wizard (JS) — Step navigation, AJAX saves, API validation
     *
     * Also cleans up the page by removing emoji scripts/styles and IE conditional
     * comments (deprecated in WP 6.9) to prevent console warnings.
     *
     * @since 1.0.0
     * @access private
     *
     * @return void
     */
    private function enqueue_wizard_assets_direct() {
        // Remove unnecessary WordPress defaults for clean wizard page
        remove_action( 'wp_head', 'print_emoji_detection_script', 7 );
        remove_action( 'wp_print_styles', 'print_emoji_styles' );
        remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
        remove_action( 'admin_print_styles', 'print_emoji_styles' );

        // Remove IE conditional comments from scripts/styles to prevent WP 6.9+ deprecation warnings
        // These are added by themes/plugins for IE compatibility but are deprecated in WP 6.9
        add_action( 'wp_print_styles', [ $this, 'remove_ie_conditionals' ], 1 );
        add_action( 'wp_print_scripts', [ $this, 'remove_ie_conditionals' ], 1 );

        // Production: Load bundled CSS (includes variables, wizard styles)
        wp_enqueue_style(
            'infyp-admin',
            INFYP_PLUGIN_URL . 'assets/css/dist/infyp.min.css',
            [],
            INFYP_VERSION
        );

        // Add inline styles for fullscreen wizard
        wp_add_inline_style(
            'infyp-admin',
            'html, body { margin: 0; padding: 0; background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); min-height: 100vh; }'
        );

        // Enqueue scripts
        wp_enqueue_script(
            'infyp-setup-wizard',
            INFYP_PLUGIN_URL . 'assets/js/dist/setup-wizard.min.js',
            [ 'jquery' ],
            INFYP_VERSION,
            true
        );

        // Localize script
        wp_localize_script( 'infyp-setup-wizard', 'infyp_wizard', [
            'ajax_url'      => admin_url( 'admin-ajax.php' ),
            'nonce'         => wp_create_nonce( 'infyp_wizard_nonce' ),
            'dashboard_url' => admin_url( 'admin.php?page=infy-ai-image-seo-optimizer' ),
            'settings_url'  => admin_url( 'admin.php?page=ai-image-seo-optimizer-settings' ),
            'strings'       => [
                'validating'       => __( 'Validating...', 'infy-ai-image-seo-optimizer' ),
                'valid'            => __( 'Valid', 'infy-ai-image-seo-optimizer' ),
                'invalid'          => __( 'Invalid', 'infy-ai-image-seo-optimizer' ),
                'error'            => __( 'Error', 'infy-ai-image-seo-optimizer' ),
                'saving'           => __( 'Saving...', 'infy-ai-image-seo-optimizer' ),
                'skip_title'       => __( 'Skip Setup?', 'infy-ai-image-seo-optimizer' ),
                'skip_confirm'     => __( 'Are you sure you want to skip the setup wizard? You can configure these settings later from the plugin settings page.', 'infy-ai-image-seo-optimizer' ),
                'skip_button'      => __( 'Skip Setup', 'infy-ai-image-seo-optimizer' ),
                'cancel'           => __( 'Cancel', 'infy-ai-image-seo-optimizer' ),
                'api_key_required' => __( 'Please enter at least one API key to continue.', 'infy-ai-image-seo-optimizer' ),
            ],
        ] );
    }

    /**
     * Renders the wizard page within the WordPress admin wrapper.
     *
     * Fallback callback for the admin submenu page registration.
     * In normal operation, maybe_render_fullscreen_wizard() intercepts
     * the request and renders the fullscreen version before this is called.
     * This method exists as a safety net if the fullscreen intercept is bypassed
     * (e.g., by another plugin filtering admin_init).
     *
     * @since 1.0.0
     *
     * @return void
     */
    public function render_wizard_page() {
        $steps = $this->get_steps();

        // Get current step
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        $current_step = isset( $_GET['step'] ) ? sanitize_key( $_GET['step'] ) : 'welcome';

        if ( ! isset( $steps[ $current_step ] ) ) {
            $current_step = 'welcome';
        }

        // Get step index for progress
        $step_keys = array_keys( $steps );
        $current_index = array_search( $current_step, $step_keys, true );

        // Get existing data
        $wizard_data = get_option( 'infyp_wizard_data', [] );
        $api_key_manager = INFYP_API_Key_Manager::get_instance();

        // Prepare data for template
        $data = [
            'steps'         => $steps,
            'current_step'  => $current_step,
            'current_index' => $current_index,
            'wizard_data'   => $wizard_data,
            'has_openai'    => $api_key_manager->has_api_key( 'openai' ),
            'has_google'    => $api_key_manager->has_api_key( 'google' ),
            'has_anthropic' => $api_key_manager->has_api_key( 'anthropic' ),
            'languages'     => INFYP_Language_Manager::get_available_languages(),
        ];

        // Load the wizard template
        include INFYP_PLUGIN_DIR . 'includes/views/setup-wizard/wizard-page.php';
    }

    /**
     * AJAX handler: Save step data
     *
     * Processes and saves data for each wizard step. Validates input
     * and delegates to step-specific handlers.
     *
     * Security:
     * - Verifies 'infyp_wizard_nonce' nonce
     * - Requires 'manage_options' capability
     * - Sanitizes all input data
     *
     * @since 1.0.0
     *
     * @return void Sends JSON response and terminates.
     */
    public function ajax_save_step() {
        // Verify nonce
        if ( ! check_ajax_referer( 'infyp_wizard_nonce', 'nonce', false ) ) {
            wp_send_json_error( [ 'message' => __( 'Security check failed.', 'infy-ai-image-seo-optimizer' ) ] );
        }

        // Check permissions
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( [ 'message' => __( 'Insufficient permissions.', 'infy-ai-image-seo-optimizer' ) ] );
        }

        $step = isset( $_POST['step'] ) ? sanitize_key( $_POST['step'] ) : '';
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized in individual handler methods
        $data = isset( $_POST['data'] ) && is_array( $_POST['data'] ) ? wp_unslash( $_POST['data'] ) : [];

        if ( empty( $step ) ) {
            wp_send_json_error( [ 'message' => __( 'Invalid step.', 'infy-ai-image-seo-optimizer' ) ] );
        }

        // Process data based on step
        switch ( $step ) {
            case 'api-keys':
                $this->save_api_keys( $data );
                break;

            case 'preferences':
                $this->save_preferences( $data );
                break;
        }

        wp_send_json_success( [ 'message' => __( 'Step saved successfully.', 'infy-ai-image-seo-optimizer' ) ] );
    }

    /**
     * Save API keys from wizard
     *
     * Securely stores API keys for configured AI providers.
     * Keys are sanitized before storage.
     *
     * @since 1.0.0
     * @access private
     *
     * @param array $data API key data with provider names as keys.
     * @return void
     */
    private function save_api_keys( $data ) {
        $api_key_manager = INFYP_API_Key_Manager::get_instance();

        /** This filter is documented in INFYP_Settings::sanitize_api_keys() */
        $providers = apply_filters( 'infyp_allowed_api_key_providers', [ 'openai', 'google', 'anthropic' ] );

        foreach ( $providers as $provider ) {
            if ( ! empty( $data[ $provider ] ) ) {
                $api_key_manager->set_api_key( $provider, sanitize_text_field( $data[ $provider ] ) );
            }
        }
    }

    /**
     * Save user preferences from wizard
     *
     * Stores user preferences including website type, language,
     * image count estimates, and handles newsletter subscription.
     *
     * @since 1.0.0
     * @access private
     *
     * @param array $data Preference data from the wizard form.
     * @return void
     */
    private function save_preferences( $data ) {
        $wizard_data = get_option( 'infyp_wizard_data', [] );

        // Save website type
        if ( ! empty( $data['website_type'] ) ) {
            $wizard_data['website_type'] = sanitize_text_field( $data['website_type'] );
        }

        // Save image count estimate
        if ( ! empty( $data['image_count'] ) ) {
            $wizard_data['image_count'] = sanitize_text_field( $data['image_count'] );
        }

        // Subscribe to newsletter if consent given (uses logged-in user's email)
        if ( ! empty( $data['newsletter_consent'] ) && ! empty( $data['user_email'] ) ) {
            $wizard_data['newsletter_consent'] = true;
            $wizard_data['subscribed_email'] = sanitize_email( $data['user_email'] );

            // Subscribe using the current user's WordPress account info
            $this->subscribe_to_newsletter( $data['user_email'], $data );
        }

        update_option( 'infyp_wizard_data', $wizard_data );

        // Save language preference to main settings
        if ( ! empty( $data['language'] ) ) {
            $prompt_settings = get_option( 'infyp_prompt_settings', [] );
            $prompt_settings['language'] = sanitize_text_field( $data['language'] );
            update_option( 'infyp_prompt_settings', $prompt_settings );
        }

        // Save default AI model
        if ( ! empty( $data['default_model'] ) ) {
            $optimization_settings = get_option( 'infyp_optimization_settings', [] );
            $optimization_settings['default_model'] = sanitize_text_field( $data['default_model'] );
            update_option( 'infyp_optimization_settings', $optimization_settings );
        }
    }

    /**
     * Subscribe user to newsletter via API
     *
     * Uses the logged-in WordPress user's email and name for subscription.
     * This provides an extra layer of security since we know the email
     * belongs to a real WordPress user account.
     *
     * Security measures:
     * - Verifies submitted email matches current WordPress user (case-insensitive)
     * - Uses sanitize_email() on all email inputs
     * - Includes site telemetry only when user has explicitly opted in
     *
     * @since 1.0.0
     * @access private
     *
     * @param string $email User email address (from WordPress account).
     * @param array  $data  All wizard preference data including user_name, user_id.
     * @return void
     */
    private function subscribe_to_newsletter( $email, $data = [] ) {
        // Get current user for additional verification
        $current_user = wp_get_current_user();

        // Sanitize email for comparison
        $email = sanitize_email( trim( $email ) );
        $current_user_email = sanitize_email( trim( $current_user->user_email ) );

        // Security check: verify the email matches the current user (case-insensitive)
        // This prevents users from subscribing other people's emails
        if ( strtolower( $current_user_email ) !== strtolower( $email ) ) {
            return;
        }

        // Collect site telemetry data filtered by user preferences
        $telemetry_prefs = [
            'environment' => ! empty( $data['telemetry_environment'] ),
            'theme'       => ! empty( $data['telemetry_theme'] ),
            'content'     => ! empty( $data['telemetry_content'] ),
            'plugins'     => ! empty( $data['telemetry_plugins'] ),
        ];
        $site_data = $this->collect_site_telemetry( $telemetry_prefs );

        // Prepare metadata with all wizard preferences, WordPress user info, and site telemetry
        $metadata = array_merge(
            [
                // Wizard preferences
                'website_type'   => sanitize_text_field( $data['website_type'] ?? '' ),
                'language'       => sanitize_text_field( $data['language'] ?? '' ),
                'image_count'    => sanitize_text_field( $data['image_count'] ?? '' ),

                // User info
                'wp_user_id'     => $current_user->ID,
                'wp_user_role'   => implode( ', ', $current_user->roles ),

                // Timestamp
                'subscribed_at'  => current_time( 'mysql' ),
            ],
            $site_data
        );

        // Use the WordPress user's display name
        $user_name = $current_user->display_name ?: $current_user->user_login;

        $request_data = [
            'email'         => sanitize_email( $email ),
            'name'          => sanitize_text_field( $user_name ),
            'source'        => 'setup-wizard',
            'source_plugin' => 'infy-ai-image-seo-optimizer',
            'consent'       => true,
            'metadata'      => $metadata,
        ];

        // Newsletter API endpoint - InfyPress central newsletter service
        $api_url = 'https://infypress.com/wp-json/ai-products/v1/newsletter/subscribe';

        // Authenticate with the plugin slug — server validates against registered plugins.
        $plugin_slug = defined( 'INFYP_NEWSLETTER_PLUGIN_SLUG' ) ? INFYP_NEWSLETTER_PLUGIN_SLUG : 'infy-ai-image-seo-optimizer';

        $args = [
            'timeout'  => 15,
            'blocking' => false,
            'headers'  => [
                'Content-Type'       => 'application/json',
                'X-Newsletter-Slug'  => $plugin_slug,
            ],
            'body'     => wp_json_encode( $request_data ),
        ];

        // Disable SSL verification for localhost (development environment)
        if ( strpos( $api_url, 'localhost' ) !== false || strpos( $api_url, '127.0.0.1' ) !== false ) {
            $args['sslverify'] = false;
        }

        // Send subscription request (fire and forget - don't block on response)
        wp_remote_post( $api_url, $args );
    }

    /**
     * Collects site telemetry data for newsletter subscription.
     *
     * Gathers non-personal site information to help improve the plugin.
     * Each telemetry category is independently opt-in via the $prefs parameter,
     * which maps to checkboxes in the wizard preferences step.
     *
     * Data categories:
     * - Always sent: site_url, plugin_version, locale, timezone
     * - Environment ($prefs['environment']): WP/PHP/MySQL versions, server, memory, debug
     * - Theme ($prefs['theme']): Active theme name, version, parent theme
     * - Content ($prefs['content']): Post/page/media/product counts
     * - Plugins ($prefs['plugins']): Active plugin names, SEO/builder detection
     *
     * Privacy: No personal user data is collected. Site URL is included
     * to help with debugging and support requests. Plugin list is capped
     * at 30 active / 50 installed to limit payload size.
     *
     * @since 1.0.0
     * @access private
     *
     * @param array $prefs {
     *     Optional. Category opt-in flags. Default all true.
     *
     *     @type bool $environment Whether to include environment data.
     *     @type bool $theme       Whether to include theme data.
     *     @type bool $content     Whether to include content counts.
     *     @type bool $plugins     Whether to include plugin data.
     * }
     * @return array Associative array of telemetry data.
     */
    private function collect_site_telemetry( $prefs = [] ) {
        global $wpdb;

        $prefs = wp_parse_args( $prefs, [
            'environment' => true,
            'theme'       => true,
            'content'     => true,
            'plugins'     => true,
        ] );

        // Always sent (non-optional)
        $data = [
            'site_url'       => home_url(),
            'plugin_version' => defined( 'INFYP_VERSION' ) ? INFYP_VERSION : '',
            'locale'         => get_locale(),
            'timezone'       => wp_timezone_string(),
        ];

        // Environment data
        if ( $prefs['environment'] ) {
            $server_software = isset( $_SERVER['SERVER_SOFTWARE'] ) ? sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) : '';

            $data['wp_version']      = get_bloginfo( 'version' );
            $data['php_version']     = phpversion();
            $data['mysql_version']   = $wpdb->db_version();
            $data['server_software'] = $server_software;
            $data['is_multisite']    = is_multisite();
            $data['wp_memory_limit'] = WP_MEMORY_LIMIT;
            $data['wp_debug']        = defined( 'WP_DEBUG' ) && WP_DEBUG;
        }

        // Theme data
        if ( $prefs['theme'] ) {
            $theme = wp_get_theme();

            $data['theme_name']    = $theme->get( 'Name' );
            $data['theme_version'] = $theme->get( 'Version' );
            $data['theme_parent']  = $theme->parent() ? $theme->parent()->get( 'Name' ) : '';
        }

        // Content counts
        if ( $prefs['content'] ) {
            $data['media_count'] = (int) wp_count_posts( 'attachment' )->inherit;

            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Simple count query, caching not beneficial
            $data['image_count'] = (int) $wpdb->get_var(
                "SELECT COUNT(*) FROM {$wpdb->posts}
                WHERE post_type = 'attachment'
                AND post_mime_type LIKE 'image/%'"
            );

            $data['post_count'] = (int) wp_count_posts( 'post' )->publish;
            $data['page_count'] = (int) wp_count_posts( 'page' )->publish;

            $data['product_count'] = 0;
            if ( post_type_exists( 'product' ) ) {
                $data['product_count'] = (int) wp_count_posts( 'product' )->publish;
            }
        }

        // Plugin detection
        if ( $prefs['plugins'] ) {
            $data['has_woocommerce'] = class_exists( 'WooCommerce' );
            $data['has_yoast']       = defined( 'WPSEO_VERSION' );
            $data['has_rankmath']    = class_exists( 'RankMath' );
            $data['has_elementor']   = defined( 'ELEMENTOR_VERSION' );

            // Active plugins (names only)
            $active_plugins      = get_option( 'active_plugins', [] );
            $active_plugin_names = [];
            foreach ( $active_plugins as $plugin_path ) {
                $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin_path, false, false );
                if ( ! empty( $plugin_data['Name'] ) ) {
                    $active_plugin_names[] = $plugin_data['Name'];
                }
            }
            $data['active_plugins'] = implode( ', ', array_slice( $active_plugin_names, 0, 30 ) );

            // All installed plugins (including inactive)
            if ( ! function_exists( 'get_plugins' ) ) {
                require_once ABSPATH . 'wp-admin/includes/plugin.php';
            }
            $all_plugins            = get_plugins();
            $installed_plugin_names = [];
            foreach ( $all_plugins as $plugin_path => $plugin_data ) {
                if ( ! empty( $plugin_data['Name'] ) ) {
                    $installed_plugin_names[] = $plugin_data['Name'];
                }
            }
            $data['installed_plugins'] = implode( ', ', array_slice( $installed_plugin_names, 0, 50 ) );
        }

        return $data;
    }

    /**
     * AJAX handler: Complete wizard
     *
     * Marks the wizard as completed and redirects user to the plugin dashboard.
     * Stores the completion timestamp for future reference.
     *
     * Security:
     * - Verifies 'infyp_wizard_nonce' nonce
     * - Requires 'manage_options' capability
     *
     * @since 1.0.0
     *
     * @return void Sends JSON response with redirect URL and terminates.
     */
    public function ajax_complete_wizard() {
        // Verify nonce
        if ( ! check_ajax_referer( 'infyp_wizard_nonce', 'nonce', false ) ) {
            wp_send_json_error( [ 'message' => __( 'Security check failed.', 'infy-ai-image-seo-optimizer' ) ] );
        }

        // Check permissions
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( [ 'message' => __( 'Insufficient permissions.', 'infy-ai-image-seo-optimizer' ) ] );
        }

        // Mark wizard as completed
        update_option( 'infyp_wizard_completed', time() );

        // Clean up redirect-related options/transients
        delete_option( 'infyp_wizard_redirect_time' );
        delete_transient( 'infyp_wizard_redirect' );

        wp_send_json_success( [
            'message'      => __( 'Setup complete!', 'infy-ai-image-seo-optimizer' ),
            'redirect_url' => admin_url( 'admin.php?page=infy-ai-image-seo-optimizer' ),
        ] );
    }

    /**
     * AJAX handler: Skip wizard
     *
     * Allows users to skip the setup wizard and configure settings manually.
     * Marks the wizard as 'skipped' rather than completed.
     *
     * Security:
     * - Verifies 'infyp_wizard_nonce' nonce
     * - Requires 'manage_options' capability
     *
     * @since 1.0.0
     *
     * @return void Sends JSON response with redirect URL and terminates.
     */
    public function ajax_skip_wizard() {
        // Verify nonce
        if ( ! check_ajax_referer( 'infyp_wizard_nonce', 'nonce', false ) ) {
            wp_send_json_error( [ 'message' => __( 'Security check failed.', 'infy-ai-image-seo-optimizer' ) ] );
        }

        // Check permissions
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( [ 'message' => __( 'Insufficient permissions.', 'infy-ai-image-seo-optimizer' ) ] );
        }

        // Mark wizard as skipped
        update_option( 'infyp_wizard_completed', 'skipped' );

        // Clean up redirect-related options/transients
        delete_option( 'infyp_wizard_redirect_time' );
        delete_transient( 'infyp_wizard_redirect' );

        wp_send_json_success( [
            'message'      => __( 'Wizard skipped.', 'infy-ai-image-seo-optimizer' ),
            'redirect_url' => admin_url( 'admin.php?page=infy-ai-image-seo-optimizer' ),
        ] );
    }

    /**
     * AJAX handler: Validate API key
     *
     * Tests an API key against its respective AI provider to verify it's valid.
     * Uses the API Key Manager to perform the actual validation.
     *
     * Security:
     * - Verifies 'infyp_wizard_nonce' nonce
     * - Requires 'manage_options' capability
     * - Sanitizes provider name and API key
     *
     * @since 1.0.0
     *
     * @return void Sends JSON success/error response and terminates.
     */
    public function ajax_validate_api_key() {
        // Verify nonce
        if ( ! check_ajax_referer( 'infyp_wizard_nonce', 'nonce', false ) ) {
            wp_send_json_error( [ 'message' => __( 'Security check failed.', 'infy-ai-image-seo-optimizer' ) ] );
        }

        // Check permissions
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( [ 'message' => __( 'Insufficient permissions.', 'infy-ai-image-seo-optimizer' ) ] );
        }

        $provider = isset( $_POST['provider'] ) ? sanitize_key( $_POST['provider'] ) : '';
        $api_key = isset( $_POST['api_key'] ) ? sanitize_text_field( wp_unslash( $_POST['api_key'] ) ) : '';

        if ( empty( $provider ) || empty( $api_key ) ) {
            wp_send_json_error( [ 'message' => __( 'Provider and API key are required.', 'infy-ai-image-seo-optimizer' ) ] );
        }

        // Use the existing API key manager validation
        $api_key_manager = INFYP_API_Key_Manager::get_instance();
        $validation = $api_key_manager->validate_api_key( $provider, $api_key );

        if ( is_wp_error( $validation ) ) {
            wp_send_json_error( [ 'message' => $validation->get_error_message() ] );
        } else {
            wp_send_json_success( [ 'message' => __( 'API key is valid.', 'infy-ai-image-seo-optimizer' ) ] );
        }
    }

    /**
     * Determines whether the setup wizard should be displayed.
     *
     * Static utility method used by other classes (e.g., INFYP_Admin_Pages)
     * to conditionally show wizard-related UI elements. Returns true only
     * when the wizard hasn't been completed (or skipped) AND the current
     * user has the 'manage_options' capability.
     *
     * @since 1.0.0
     *
     * @return bool True if wizard should be shown, false otherwise.
     */
    public static function should_show_wizard() {
        return ! get_option( 'infyp_wizard_completed' ) && current_user_can( 'manage_options' );
    }

    /**
     * Displays a dismissible admin notice prompting wizard completion.
     *
     * Shown on all WordPress admin pages (except the wizard itself) when:
     * - Wizard is not completed or skipped
     * - Notice has not been dismissed
     * - Current user has 'manage_options' capability
     *
     * The dismiss action is handled via an inline jQuery script added to
     * admin_footer, which fires the infyp_dismiss_wizard_notice AJAX action.
     *
     * @since 1.0.0
     *
     * @return void Outputs HTML notice directly.
     */
    public function maybe_show_wizard_notice() {
        // Don't show if wizard is completed
        if ( get_option( 'infyp_wizard_completed' ) ) {
            return;
        }

        // Don't show if notice was dismissed
        if ( get_option( 'infyp_wizard_notice_dismissed' ) ) {
            return;
        }

        // Only show to admins
        if ( ! current_user_can( 'manage_options' ) ) {
            return;
        }

        // Don't show on the wizard page itself
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        if ( isset( $_GET['page'] ) && $_GET['page'] === 'ai-image-seo-optimizer-wizard' ) {
            return;
        }

        $wizard_url = admin_url( 'admin.php?page=ai-image-seo-optimizer-wizard' );
        ?>
        <div class="notice notice-info is-dismissible infyp-wizard-notice">
            <p>
                <strong><?php esc_html_e( 'AI Image SEO Optimizer', 'infy-ai-image-seo-optimizer' ); ?></strong> &mdash;
                <?php esc_html_e( 'Complete the setup wizard to configure your API keys and start optimizing images.', 'infy-ai-image-seo-optimizer' ); ?>
                <a href="<?php echo esc_url( $wizard_url ); ?>">
                    <?php esc_html_e( 'Start Setup', 'infy-ai-image-seo-optimizer' ); ?>
                </a>
            </p>
        </div>
        <?php
        // Add wizard notice dismiss script to footer
        add_action( 'admin_footer', function() {
            $nonce = wp_create_nonce( 'infyp_dismiss_wizard_notice' );
            $script = 'jQuery(document).ready(function($) {'
                . '$(".infyp-wizard-notice").on("click", ".notice-dismiss", function() {'
                . '$.post(ajaxurl, {'
                . 'action: "infyp_dismiss_wizard_notice",'
                . 'nonce: "' . $nonce . '"'
                . '});'
                . '});'
                . '});';
            wp_print_inline_script_tag( $script );
        } );
        ?>
        <?php
    }

    /**
     * AJAX handler: Dismiss wizard notice
     *
     * Permanently dismisses the setup wizard admin notice for this site.
     * Users can still access the wizard directly via the settings page.
     *
     * Security:
     * - Verifies 'infyp_dismiss_wizard_notice' nonce
     * - Requires 'manage_options' capability
     *
     * @since 1.0.0
     *
     * @return void Sends JSON response and terminates.
     */
    public function ajax_dismiss_wizard_notice() {
        // Verify nonce
        if ( ! check_ajax_referer( 'infyp_dismiss_wizard_notice', 'nonce', false ) ) {
            wp_send_json_error( [ 'message' => __( 'Security check failed.', 'infy-ai-image-seo-optimizer' ) ] );
        }

        // Check permissions
        if ( ! current_user_can( 'manage_options' ) ) {
            wp_send_json_error( [ 'message' => __( 'Insufficient permissions.', 'infy-ai-image-seo-optimizer' ) ] );
        }

        update_option( 'infyp_wizard_notice_dismissed', true );
        wp_send_json_success();
    }

    /**
     * Remove IE conditional comments from registered scripts and styles
     *
     * WordPress 6.9 deprecated the 'conditional' data key for scripts/styles
     * as IE is no longer supported. This method removes any such conditionals
     * added by themes or plugins to prevent deprecation warnings on the wizard page.
     *
     * @since 1.0.0
     *
     * @return void
     */
    public function remove_ie_conditionals() {
        global $wp_scripts, $wp_styles;

        // Remove conditional data from scripts
        if ( $wp_scripts instanceof \WP_Scripts ) {
            foreach ( $wp_scripts->registered as $handle => $script ) {
                if ( ! empty( $script->extra['conditional'] ) ) {
                    unset( $wp_scripts->registered[ $handle ]->extra['conditional'] );
                }
            }
        }

        // Remove conditional data from styles
        if ( $wp_styles instanceof \WP_Styles ) {
            foreach ( $wp_styles->registered as $handle => $style ) {
                if ( ! empty( $style->extra['conditional'] ) ) {
                    unset( $wp_styles->registered[ $handle ]->extra['conditional'] );
                }
            }
        }
    }
}
