<?php

if (!defined('WPINC')) {
    die;
}

class Aysa_Automation_Manager {
    
    private $queue_manager;
    private $scheduler;
    private $audit_logger;
    private $confidence_scorer;
    private $automation_config;
    
    public function __construct() {
        $this->load_dependencies();
        $this->automation_config = get_option('aysa_automation_config', $this->get_default_config());
    }
    
    public function init() {
        $this->init_hooks();
        $this->init_components();
        if (did_action('init')) {
            $this->schedule_automation_tasks();
        } else {
            add_action('init', array($this, 'schedule_automation_tasks'));
        }
    }
    
    private function load_dependencies() {
        require_once AYSA_PLUGIN_DIR . 'includes/automation/class-aysa-queue-manager.php';
        require_once AYSA_PLUGIN_DIR . 'includes/automation/class-aysa-scheduler.php';
        require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-confidence-scorer.php';
        require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-audit-logger.php';
        require_once AYSA_PLUGIN_DIR . 'includes/automation/processors/class-image-processor.php';
        require_once AYSA_PLUGIN_DIR . 'includes/automation/processors/class-redirect-processor.php';
        require_once AYSA_PLUGIN_DIR . 'includes/automation/processors/class-meta-processor.php';
        require_once AYSA_PLUGIN_DIR . 'includes/automation/processors/class-keyword-processor.php';
        require_once AYSA_PLUGIN_DIR . 'includes/automation/processors/class-domain-authority-processor.php';
        require_once AYSA_PLUGIN_DIR . 'includes/automation/processors/class-association-processor.php';
        require_once AYSA_PLUGIN_DIR . 'includes/automation/processors/class-seo-entity-processor.php';
        require_once AYSA_PLUGIN_DIR . 'includes/automation/processors/class-seo-generation-processor.php';
    }
    
    private function init_components() {
        $this->queue_manager = new Aysa_Queue_Manager();
        $this->scheduler = new Aysa_Scheduler();
        $this->audit_logger = new Aysa_Audit_Logger();
        $this->confidence_scorer = new Aysa_Confidence_Scorer();
        
        $this->queue_manager->init();
        $this->scheduler->init();
    }
    
    private function init_hooks() {
        add_action('wp_insert_attachment', array($this, 'trigger_image_automation'), 10, 2);
        add_action('template_redirect', array($this, 'capture_404_for_automation'), 1);
        add_action('save_post', array($this, 'trigger_meta_automation'), 10, 3);
        add_action('created_term', array($this, 'trigger_keyword_automation'), 10, 3);
        
        // Add hook to output meta tags in admin for automation pages
        add_action('admin_head', array($this, 'output_meta_tags_in_admin'));
        
        // Add custom cron intervals
        add_filter('cron_schedules', array($this, 'add_custom_cron_intervals'));
        
        add_action('wp_ajax_aysa_toggle_automation', array($this, 'handle_toggle_automation'));
        add_action('wp_ajax_aysa_get_automation_status', array($this, 'handle_get_automation_status'));
        add_action('wp_ajax_aysa_process_automation_queue', array($this, 'handle_process_queue'));
        
        add_action('wp_ajax_aysa_get_pending_domain_authority_reviews', array($this, 'handle_get_pending_domain_authority_reviews'));
        add_action('wp_ajax_aysa_approve_domain_authority_review', array($this, 'handle_approve_domain_authority_review'));
        add_action('wp_ajax_aysa_reject_domain_authority_review', array($this, 'handle_reject_domain_authority_review'));
        if ($this->qa_controls_enabled()) {
            add_action('wp_ajax_aysa_simulate_domain_authority_task', array($this, 'handle_simulate_domain_authority_task'));
        }
        add_action('wp_ajax_aysa_get_pending_dotnet_reviews', array($this, 'handle_get_pending_dotnet_reviews'));
        add_action('wp_ajax_aysa_approve_dotnet_task', array($this, 'handle_approve_dotnet_task'));
        add_action('wp_ajax_aysa_reject_dotnet_task', array($this, 'handle_reject_dotnet_task'));
        add_action('wp_ajax_aysa_get_pending_automated_seo_reviews', array($this, 'handle_get_pending_automated_seo_reviews'));
        add_action('wp_ajax_aysa_approve_automated_review', array($this, 'handle_approve_automated_review'));
        add_action('wp_ajax_aysa_reject_automated_review', array($this, 'handle_reject_automated_review'));
        
        // Add handlers for other review types
        add_action('wp_ajax_aysa_get_pending_image_reviews', array($this, 'handle_get_pending_image_reviews'));
        add_action('wp_ajax_aysa_approve_image_review', array($this, 'handle_approve_image_review'));
        add_action('wp_ajax_aysa_reject_image_review', array($this, 'handle_reject_image_review'));
        
        add_action('wp_ajax_aysa_get_pending_meta_reviews', array($this, 'handle_get_pending_meta_reviews'));
        add_action('wp_ajax_aysa_approve_meta_review', array($this, 'handle_approve_meta_review'));
        add_action('wp_ajax_aysa_reject_meta_review', array($this, 'handle_reject_meta_review'));
        
        add_action('wp_ajax_aysa_get_pending_keyword_reviews', array($this, 'handle_get_pending_keyword_reviews'));
        add_action('wp_ajax_aysa_approve_keyword_review', array($this, 'handle_approve_keyword_review'));
        add_action('wp_ajax_aysa_reject_keyword_review', array($this, 'handle_reject_keyword_review'));
        
        add_action('wp_ajax_aysa_get_pending_redirect_reviews', array($this, 'handle_get_pending_redirect_reviews'));
        add_action('wp_ajax_aysa_approve_redirect_review', array($this, 'handle_approve_redirect_review'));
        add_action('wp_ajax_aysa_reject_redirect_review', array($this, 'handle_reject_redirect_review'));
        
        // SEO Generation automation handlers
        add_action('wp_ajax_aysa_test_seo_generation', array($this, 'handle_test_seo_generation'));
        add_action('wp_ajax_aysa_get_seo_generation_status', array($this, 'handle_get_seo_generation_status'));
        add_action('wp_ajax_aysa_manual_seo_generation', array($this, 'handle_manual_seo_generation'));
        add_action('wp_ajax_aysa_get_job_progress', array($this, 'handle_get_job_progress'));
        add_action('wp_ajax_aysa_get_processing_stats', array($this, 'handle_get_processing_stats'));
        add_action('wp_ajax_aysa_get_pending_automated_seo_reviews', array($this, 'handle_get_pending_automated_seo_reviews'));
        add_action('wp_ajax_aysa_store_manual_seo_review', array($this, 'handle_store_manual_seo_review'));
        
        add_action('aysa_process_automation_queue', array($this, 'process_automation_queue'));
        add_action('aysa_daily_automation_cleanup', array($this, 'cleanup_automation_data'));
        add_action('aysa_find_entities_without_seo_history', array($this, 'trigger_seo_entity_automation'));
        add_action('aysa_automated_seo_generation', array($this, 'run_automated_seo_generation'));
    }
    
    public function trigger_image_automation($attachment_id, $attachment) {
        if (!$this->is_automation_enabled('image_optimization')) {
            return;
        }
        
        if (wp_attachment_is_image($attachment_id)) {
            $job_data = array(
                'attachment_id' => $attachment_id,
                'action_type' => 'image_optimization',
                'priority' => 5,
                'context' => $this->get_image_context($attachment_id)
            );
            
            $this->queue_manager->add_job('image_processor', $job_data);
        }
    }
    
    public function capture_404_for_automation() {
        if (!$this->is_automation_enabled('redirect_creation') || !is_404()) {
            return;
        }
        
        $requested_url = isset($_SERVER['REQUEST_URI']) ? sanitize_text_field(wp_unslash($_SERVER['REQUEST_URI'])) : '';
        $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';
        
        if ($this->should_process_404($requested_url, $user_agent)) {
            $job_data = array(
                'url' => $requested_url,
                'action_type' => 'redirect_creation',
                'priority' => 3,
                'user_agent' => $user_agent,
                'timestamp' => current_time('mysql')
            );
            
            $this->queue_manager->add_job('redirect_processor', $job_data);
        }
    }
    
    public function trigger_meta_automation($post_id, $post, $update) {
        if (!$this->is_automation_enabled('meta_generation') || !$update) {
            return;
        }
        
        if (in_array($post->post_type, array('post', 'page', 'product'))) {
            $existing_meta = get_post_meta($post_id, '_yoast_wpseo_title', true);
            
            if (empty($existing_meta)) {
                $job_data = array(
                    'post_id' => $post_id,
                    'post_type' => $post->post_type,
                    'action_type' => 'meta_generation',
                    'priority' => 4,
                    'content' => $post->post_content,
                    'title' => $post->post_title
                );
                
                $this->queue_manager->add_job('meta_processor', $job_data);
            }
        }
    }
    
    public function trigger_keyword_automation($term_id, $tt_id, $taxonomy) {
        if (!$this->is_automation_enabled('keyword_assignment')) {
            return;
        }
        
        if (in_array($taxonomy, array('category', 'product_cat', 'post_tag'))) {
            $job_data = array(
                'term_id' => $term_id,
                'taxonomy' => $taxonomy,
                'action_type' => 'keyword_assignment',
                'priority' => 6
            );
            
            $this->queue_manager->add_job('keyword_processor', $job_data);
        }
    }
    
    public function trigger_seo_entity_automation() {
        if (!$this->is_automation_enabled('seo_entity_generation')) {
            return;
        }
        
        $job_data = array(
            'action_type' => 'find_entities_without_history',
            'priority' => 7
        );
        
        $this->queue_manager->add_job('seo_entity_processor', $job_data);
    }
    
    public function process_automation_queue() {
        if (!$this->queue_manager) {
            $this->init_components();
        }
        $this->queue_manager->process_queue();
    }
    
    public function schedule_automation_tasks() {
        // Schedule daily cleanup
        if (!wp_next_scheduled('aysa_daily_automation_cleanup')) {
            wp_schedule_event(time(), 'daily', 'aysa_daily_automation_cleanup');
        }
        
        // Schedule SEO generation only after explicit project-level enablement.
        if ($this->should_schedule_automated_seo_generation()) {
            if (!wp_next_scheduled('aysa_automated_seo_generation')) {
                wp_schedule_event(time(), 'monthly', 'aysa_automated_seo_generation');
                aysa_debug_log('AYSA Automation: Scheduled SEO generation cron for monthly execution');
            }
        } else {
            // Unschedule if disabled
            if (wp_next_scheduled('aysa_automated_seo_generation')) {
                wp_unschedule_event(wp_next_scheduled('aysa_automated_seo_generation'), 'aysa_automated_seo_generation');
                aysa_debug_log('AYSA Automation: Unscheduled SEO generation cron');
            }
        }
    }
    
    public function cleanup_automation_data() {
        global $wpdb;
        
        $queue_table = esc_sql($wpdb->prefix . 'aysa_automation_queue');
        $audit_table = esc_sql($wpdb->prefix . 'aysa_audit_log');
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $wpdb->query($wpdb->prepare(
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
            "DELETE FROM {$queue_table} WHERE status = 'completed' AND created_at < %s",
            gmdate('Y-m-d H:i:s', strtotime('-7 days'))
        ));
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $wpdb->query($wpdb->prepare(
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
            "DELETE FROM {$audit_table} WHERE created_at < %s AND is_automated = 1",
            gmdate('Y-m-d H:i:s', strtotime('-30 days'))
        ));
    }
    
    private function is_automation_enabled($feature) {
        return isset($this->automation_config[$feature]) && $this->automation_config[$feature];
    }

    private function is_seo_generation_explicitly_enabled() {
        $stored_config = get_option('aysa_automation_config', null);
        return is_array($stored_config) && !empty($stored_config['seo_generation']);
    }

    private function should_schedule_automated_seo_generation() {
        $reason = '';
        return $this->automated_seo_generation_workspace_ready($reason)
            && $this->is_seo_generation_explicitly_enabled();
    }

    private function automated_seo_generation_workspace_ready(&$reason = '') {
        $project_id = intval(get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            $reason = 'missing_project_id';
            return false;
        }

        $website_id = intval(get_option('aysa_website_id', 0));
        if ($website_id <= 0) {
            $reason = 'missing_website_id';
            return false;
        }

        $site_host = strtolower(preg_replace('/^www\./', '', trim((string) wp_parse_url(home_url('/'), PHP_URL_HOST))));
        if ($site_host === '') {
            $reason = 'missing_site_host';
            return false;
        }

        $auth_domain = strtolower(preg_replace('/^www\./', '', trim((string) get_option('aysa_auth_domain', ''))));
        if ($auth_domain !== '' && $auth_domain !== $site_host) {
            $reason = 'site_binding_mismatch';
            return false;
        }

        $has_stored_key = trim((string) get_option('aysa_active_access_key', '')) !== ''
            || trim((string) get_option('aysa_user_secret_key', '')) !== '';
        $has_auth_session = false;
        if (!$has_stored_key) {
            if (!class_exists('Aysa_Auth_Manager') && defined('AYSA_PLUGIN_DIR')) {
                $auth_manager_path = AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
                if (is_readable($auth_manager_path)) {
                    require_once $auth_manager_path;
                }
            }
            if (class_exists('Aysa_Auth_Manager')) {
                $auth_status = Aysa_Auth_Manager::get_instance()->is_authenticated();
                $auth_secret_key = is_array($auth_status) ? trim((string) ($auth_status['secret_key'] ?? '')) : '';
                $auth_status_domain = is_array($auth_status)
                    ? strtolower(preg_replace('/^www\./', '', trim((string) ($auth_status['domain'] ?? ''))))
                    : '';
                $has_auth_session = is_array($auth_status)
                    && !empty($auth_status['authenticated'])
                    && $auth_secret_key !== ''
                    && ($auth_status_domain === '' || $auth_status_domain === $site_host);
            }
        }

        if (!$has_stored_key && !$has_auth_session) {
            $reason = 'workspace_not_activated';
            return false;
        }

        $reason = 'ready';
        return true;
    }

    private function automated_seo_generation_table_exists($table_name) {
        global $wpdb;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        return $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table_name)) === $table_name;
    }

    private function automated_seo_generation_has_pending_work($table_name) {
        global $wpdb;
        $table_name = esc_sql((string) $table_name);

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $pending_count = intval($wpdb->get_var("SELECT COUNT(*) FROM {$table_name} WHERE status = 'pending'"));
        if ($pending_count > 0) {
            return true;
        }

        $option_pending_reviews = get_option('aysa_pending_automated_seo_reviews', array());
        if (!is_array($option_pending_reviews)) {
            return false;
        }

        foreach ($option_pending_reviews as $review) {
            if (is_array($review) && isset($review['status']) && $review['status'] === 'pending') {
                return true;
            }
        }

        return false;
    }

    private function automated_seo_generation_preflight(&$reason = '') {
        global $wpdb;

        if (!$this->is_seo_generation_explicitly_enabled()) {
            $reason = 'seo_generation_not_explicitly_enabled';
            return false;
        }

        if (!$this->automated_seo_generation_workspace_ready($reason)) {
            return false;
        }

        $table_name = esc_sql($wpdb->prefix . 'aysa_automated_pending_reviews');
        if (!$this->automated_seo_generation_table_exists($table_name)) {
            $reason = 'pending_reviews_table_missing';
            return false;
        }

        if (!$this->automated_seo_generation_has_pending_work($table_name)) {
            $reason = 'no_pending_automated_seo_work';
            return false;
        }

        $reason = 'ready';
        return true;
    }
    
    private function get_image_context($attachment_id) {
        $parent_id = wp_get_post_parent_id($attachment_id);
        $context = array();
        
        if ($parent_id) {
            $parent_post = get_post($parent_id);
            $context['parent_title'] = $parent_post->post_title;
            $context['parent_content'] = wp_strip_all_tags($parent_post->post_content);
            $context['parent_type'] = $parent_post->post_type;
        }
        
        return $context;
    }
    
    private function should_process_404($url, $user_agent) {
        $excluded_patterns = array(
            // WooCommerce system pages
            '/cart/',
            '/checkout/',
            '/my-account/',
            '/wishlist/',
            // WordPress internal archives
            '/tag/',
            '/author/',
            '/date/',
            // Search parameters and internal queries
            '/?s=',
            '/search/',
            '/?add-to-cart=',
            '/?wc-ajax=',
            '/?orderby=',
            '/?filter_',
            // Feeds and technical pages
            '/feed/',
            '/comments/feed/',
            '/rss/',
            '/atom/',
            // WordPress admin and system
            '/wp-admin/',
            '/wp-content/',
            '/wp-includes/',
            '/wp-json/',
            // Common technical patterns
            '.xml',
            '.json',
            '.css',
            '.js',
            '.ico',
            '.php'
        );
        
        foreach ($excluded_patterns as $pattern) {
            if (strpos($url, $pattern) !== false) {
                return false;
            }
        }
        
        $bot_patterns = array('bot', 'crawler', 'spider', 'scraper');
        foreach ($bot_patterns as $pattern) {
            if (stripos($user_agent, $pattern) !== false) {
                return false;
            }
        }
        
        return true;
    }
    
    public function handle_toggle_automation() {
        aysa_debug_log('[AYSA AUTOMATION] Toggle automation request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
            aysa_debug_log('[AYSA AUTOMATION] Nonce verification passed');
        } catch (Exception $e) {
            aysa_debug_log('[AYSA AUTOMATION] Nonce verification failed: ' . $e->getMessage());
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            aysa_debug_log('[AYSA AUTOMATION] User lacks manage_options capability');
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $feature = sanitize_text_field(wp_unslash($_POST['feature'] ?? ''));
        $enabled = filter_var(wp_unslash($_POST['enabled'] ?? false), FILTER_VALIDATE_BOOLEAN);
        
        aysa_debug_log('[AYSA AUTOMATION] Processing toggle for feature: ' . $feature . ', enabled: ' . ($enabled ? 'true' : 'false'));
        
        if (empty($feature)) {
            aysa_debug_log('[AYSA AUTOMATION] No feature specified in request');
            wp_send_json_error('No feature specified');
            return;
        }
        
        $this->automation_config[$feature] = $enabled;
        $update_result = update_option('aysa_automation_config', $this->automation_config);
        
        if ($update_result) {
            aysa_debug_log('[AYSA AUTOMATION] Configuration updated successfully for feature: ' . $feature);
        } else {
            aysa_debug_log('[AYSA AUTOMATION] Warning: Failed to update configuration for feature: ' . $feature);
        }
        
        aysa_debug_log('[AYSA AUTOMATION] Sending success response for feature: ' . $feature);
        wp_send_json_success(array(
            'feature' => $feature,
            'enabled' => $enabled,
            'message' => sprintf('Automation for %s %s', $feature, $enabled ? 'enabled' : 'disabled')
        ));
    }
    
    public function handle_get_automation_status() {
        aysa_debug_log('[AYSA AUTOMATION] Get automation status request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
            aysa_debug_log('[AYSA AUTOMATION] Nonce verification passed for status request');
        } catch (Exception $e) {
            aysa_debug_log('[AYSA AUTOMATION] Nonce verification failed for status request: ' . $e->getMessage());
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            aysa_debug_log('[AYSA AUTOMATION] User lacks manage_options capability for status request');
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        if (!$this->queue_manager) {
            aysa_debug_log('[AYSA AUTOMATION] Queue manager not initialized, initializing components');
            $this->init_components();
        }
        
        $queue_stats = $this->queue_manager->get_queue_statistics();
        aysa_debug_log('[AYSA AUTOMATION] Queue statistics retrieved: ' . json_encode($queue_stats));
        
        aysa_debug_log('[AYSA AUTOMATION] Current automation config: ' . json_encode($this->automation_config));
        
        $response_data = array(
            'config' => $this->automation_config,
            'queue_stats' => $queue_stats,
            'thresholds' => $this->confidence_scorer ? $this->confidence_scorer->get_thresholds() : array()
        );
        
        aysa_debug_log('[AYSA AUTOMATION] Sending status response with ' . count($this->automation_config) . ' config items');
        wp_send_json_success($response_data);
    }
    
    public function handle_process_queue() {
        check_ajax_referer('aysa_automation_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
        }
        
        if (!$this->queue_manager) {
            $this->init_components();
        }
        
        $processed = $this->queue_manager->process_queue(5);
        
        wp_send_json_success(array(
            'processed' => $processed,
            'message' => sprintf('Processed %d automation jobs', $processed)
        ));
    }
    
    private function get_default_config() {
        return array(
            'image_optimization' => true,
            'meta_generation' => true,
            'keyword_analysis' => true,
            '404_analysis' => true,
            'seo_entity_automation' => true,
            'seo_generation' => true,
            'max_concurrent_jobs' => 3,
            'retry_failed_jobs' => true,
            'max_retry_attempts' => 3,
            'cleanup_old_data' => true,
            'cleanup_days' => 30
        );
    }

    private function resolve_bound_project_id($requested_project_id = 0) {
        $bound_project_id = intval(get_option('aysa_project_id', 0));
        $requested_project_id = intval($requested_project_id);
        if ($bound_project_id <= 0 || ($requested_project_id > 0 && $requested_project_id !== $bound_project_id)) {
            return 0;
        }
        return $bound_project_id;
    }

    private function qa_controls_enabled() {
        if (!defined('AYSA_ENABLE_QA_CONTROLS') || AYSA_ENABLE_QA_CONTROLS !== true) {
            return false;
        }
        $host = strtolower((string) wp_parse_url(home_url(), PHP_URL_HOST));
        return in_array($host, array('localhost', '127.0.0.1', '::1'), true)
            || substr($host, -5) === '.test'
            || substr($host, -6) === '.local';
    }

    private function track_wallet_usage($action_key, $reference, $fallback_cost_cents, $meta = array(), $endpoint = '') {
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
        $project_id = $this->resolve_bound_project_id(isset($_POST['project_id']) ? intval(wp_unslash($_POST['project_id'])) : 0);
        if ($project_id <= 0 || !defined('AYSA_PLUGIN_DIR')) {
            return;
        }

        if (!class_exists('Aysa_Wallet_Manager')) {
            $wallet_file = AYSA_PLUGIN_DIR . 'includes/class-aysa-wallet-manager.php';
            if (file_exists($wallet_file)) {
                require_once $wallet_file;
            }
        }
        if (!class_exists('Aysa_Wallet_Manager')) {
            return;
        }

        $wallet = Aysa_Wallet_Manager::get_instance();
        if (!method_exists($wallet, 'track_action_usage')) {
            return;
        }

        $wallet->track_action_usage(
            $project_id,
            sanitize_key((string) $action_key),
            (string) $reference,
            is_array($meta) ? $meta : array(),
            intval($fallback_cost_cents),
            (string) $endpoint
        );
    }
    
    public function add_custom_cron_intervals($schedules) {
        $schedules['every_5_minutes'] = array(
            'interval' => 300, // 5 minutes in seconds
            'display' => 'Every 5 Minutes'
        );
        
        $schedules['monthly'] = array(
            'interval' => 30 * 24 * 60 * 60, // 30 days in seconds (2592000)
            'display' => 'Monthly'
        );
        
        return $schedules;
    }
    
    public function get_automation_config() {
        return $this->automation_config;
    }
    
    public function update_automation_config($new_config) {
        $this->automation_config = array_merge($this->automation_config, $new_config);
        update_option('aysa_automation_config', $this->automation_config);
    }
    
    public function handle_get_pending_domain_authority_reviews() {
        aysa_debug_log('[AYSA REVIEWS] Get pending domain authority reviews request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
            aysa_debug_log('[AYSA REVIEWS] Nonce verification passed for domain authority reviews');
        } catch (Exception $e) {
            aysa_debug_log('[AYSA REVIEWS] Nonce verification failed: ' . $e->getMessage());
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            aysa_debug_log('[AYSA REVIEWS] User lacks manage_options capability');
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = $this->resolve_bound_project_id(0);
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID');
            return;
        }
        
        if (class_exists('Aysa_Domain_Authority_Processor')) {
            $processor = new Aysa_Domain_Authority_Processor();
            $reviews = $processor->get_pending_reviews();
            $reviews = isset($reviews[$project_id]) ? array($project_id => $reviews[$project_id]) : array();
            aysa_debug_log('[AYSA REVIEWS] Retrieved ' . count($reviews) . ' pending domain authority reviews');
            wp_send_json_success($reviews);
        } else {
            aysa_debug_log('[AYSA REVIEWS] Domain Authority processor class not available');
            wp_send_json_error('Domain Authority processor not available');
        }
    }
    
    public function handle_approve_domain_authority_review() {
        aysa_debug_log('[AYSA REVIEWS] Domain authority review approval request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
            aysa_debug_log('[AYSA REVIEWS] Nonce verification passed for domain authority approval');
        } catch (Exception $e) {
            aysa_debug_log('[AYSA REVIEWS] Nonce verification failed for approval: ' . $e->getMessage());
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            aysa_debug_log('[AYSA REVIEWS] User lacks manage_options capability for approval');
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $project_id = $this->resolve_bound_project_id(isset($_POST['project_id']) ? intval(wp_unslash($_POST['project_id'])) : 0);
        aysa_debug_log('[AYSA REVIEWS] Processing domain authority approval for project ID: ' . $project_id);
        
        if ($project_id <= 0) {
            aysa_debug_log('[AYSA REVIEWS] Invalid project ID provided: ' . $project_id);
            wp_send_json_error('Invalid project ID');
            return;
        }
        
        if (class_exists('Aysa_Domain_Authority_Processor')) {
            $processor = new Aysa_Domain_Authority_Processor();
            $result = $processor->approve_review($project_id);
            
            if (!is_wp_error($result) && $result) {
                aysa_debug_log('[AYSA REVIEWS] Domain authority review approved successfully: ' . $project_id);
                wp_send_json_success(array('message' => 'Review approved successfully'));
            } else {
                aysa_debug_log('[AYSA REVIEWS] Failed to approve domain authority review: ' . $project_id);
                wp_send_json_error(is_wp_error($result) ? $result->get_error_message() : 'Failed to approve review');
            }
        } else {
            aysa_debug_log('[AYSA REVIEWS] Domain Authority processor class not available for approval');
            wp_send_json_error('Domain Authority processor not available');
        }
    }
    
    public function handle_reject_domain_authority_review() {
        aysa_debug_log('[AYSA REVIEWS] Domain authority review rejection request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
            aysa_debug_log('[AYSA REVIEWS] Nonce verification passed for domain authority rejection');
        } catch (Exception $e) {
            aysa_debug_log('[AYSA REVIEWS] Nonce verification failed for rejection: ' . $e->getMessage());
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            aysa_debug_log('[AYSA REVIEWS] User lacks manage_options capability for rejection');
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $project_id = $this->resolve_bound_project_id(isset($_POST['project_id']) ? intval(wp_unslash($_POST['project_id'])) : 0);
        aysa_debug_log('[AYSA REVIEWS] Processing domain authority rejection for project ID: ' . $project_id);
        
        if ($project_id <= 0) {
            aysa_debug_log('[AYSA REVIEWS] Invalid project ID provided for rejection: ' . $project_id);
            wp_send_json_error('Invalid project ID');
            return;
        }
        
        if (class_exists('Aysa_Domain_Authority_Processor')) {
            $processor = new Aysa_Domain_Authority_Processor();
            $result = $processor->reject_review($project_id);
            
            if (!is_wp_error($result) && $result) {
                aysa_debug_log('[AYSA REVIEWS] Domain authority review rejected successfully: ' . $project_id);
                wp_send_json_success(array('message' => 'Review rejected successfully'));
            } else {
                aysa_debug_log('[AYSA REVIEWS] Failed to reject domain authority review: ' . $project_id);
                wp_send_json_error(is_wp_error($result) ? $result->get_error_message() : 'Failed to reject review');
            }
        } else {
            aysa_debug_log('[AYSA REVIEWS] Domain Authority processor class not available for rejection');
            wp_send_json_error('Domain Authority processor not available');
        }
    }
    
    public function handle_get_pending_dotnet_reviews() {
        aysa_debug_log('[AYSA REVIEWS] Get pending dotnet reviews request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
            aysa_debug_log('[AYSA REVIEWS] Nonce verification passed for dotnet reviews');
        } catch (Exception $e) {
            aysa_debug_log('[AYSA REVIEWS] Nonce verification failed for dotnet reviews: ' . $e->getMessage());
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            aysa_debug_log('[AYSA REVIEWS] User lacks manage_options capability for dotnet reviews');
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $pending_reviews = get_option('aysa_pending_dotnet_reviews', array());
        aysa_debug_log('[AYSA REVIEWS] Retrieved ' . count($pending_reviews) . ' pending dotnet reviews');
        
        wp_send_json_success($pending_reviews);
    }
    
    public function handle_approve_dotnet_task() {
        aysa_debug_log('[AYSA REVIEWS] Dotnet task approval request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
            aysa_debug_log('[AYSA REVIEWS] Nonce verification passed for dotnet approval');
        } catch (Exception $e) {
            aysa_debug_log('[AYSA REVIEWS] Nonce verification failed for dotnet approval: ' . $e->getMessage());
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            aysa_debug_log('[AYSA REVIEWS] User lacks manage_options capability for dotnet approval');
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $task_id = sanitize_text_field(wp_unslash($_POST['task_id'] ?? ''));
        aysa_debug_log('[AYSA REVIEWS] Processing dotnet task approval for task ID: ' . $task_id);
        
        if (empty($task_id)) {
            aysa_debug_log('[AYSA REVIEWS] No task ID provided for dotnet approval');
            wp_send_json_error('Task ID is required');
            return;
        }
        
        $pending_reviews = get_option('aysa_pending_dotnet_reviews', array());
        
        if (isset($pending_reviews[$task_id])) {
            unset($pending_reviews[$task_id]);
            update_option('aysa_pending_dotnet_reviews', $pending_reviews);
            aysa_debug_log('[AYSA REVIEWS] Dotnet task approved and removed from pending: ' . $task_id);
            wp_send_json_success(array('message' => 'Task approved successfully'));
        } else {
            aysa_debug_log('[AYSA REVIEWS] Dotnet task not found in pending reviews: ' . $task_id);
            wp_send_json_error('Task not found');
        }
    }
    
    public function handle_reject_dotnet_task() {
        aysa_debug_log('[AYSA REVIEWS] Dotnet task rejection request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
            aysa_debug_log('[AYSA REVIEWS] Nonce verification passed for dotnet rejection');
        } catch (Exception $e) {
            aysa_debug_log('[AYSA REVIEWS] Nonce verification failed for dotnet rejection: ' . $e->getMessage());
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            aysa_debug_log('[AYSA REVIEWS] User lacks manage_options capability for dotnet rejection');
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $task_id = sanitize_text_field(wp_unslash($_POST['task_id'] ?? ''));
        aysa_debug_log('[AYSA REVIEWS] Processing dotnet task rejection for task ID: ' . $task_id);
        
        if (empty($task_id)) {
            aysa_debug_log('[AYSA REVIEWS] No task ID provided for dotnet rejection');
            wp_send_json_error('Task ID is required');
            return;
        }
        
        $pending_reviews = get_option('aysa_pending_dotnet_reviews', array());
        
        if (isset($pending_reviews[$task_id])) {
            unset($pending_reviews[$task_id]);
            update_option('aysa_pending_dotnet_reviews', $pending_reviews);
            aysa_debug_log('[AYSA REVIEWS] Dotnet task rejected and removed from pending: ' . $task_id);
            wp_send_json_success(array('message' => 'Task rejected successfully'));
        } else {
            aysa_debug_log('[AYSA REVIEWS] Dotnet task not found in pending reviews for rejection: ' . $task_id);
            wp_send_json_error('Task not found');
        }
    }
    
    public function handle_get_automated_pending_reviews() {
        aysa_debug_log('[AYSA REVIEWS] Get automated pending reviews request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
        } catch (Exception $e) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $pending_reviews = get_option('aysa_pending_automated_seo_reviews', array());
        
        // Convert to array format expected by frontend
        $reviews_array = array();
        foreach ($pending_reviews as $review_id => $review_data) {
            $reviews_array[] = array_merge($review_data, array('id' => $review_id));
        }
        
        wp_send_json_success($reviews_array);
    }
    
    public function handle_approve_automated_review() {
        aysa_debug_log('[AYSA REVIEWS] ========== AUTOMATED REVIEW APPROVAL STARTED ==========');
        aysa_debug_log('[AYSA REVIEWS] Request method: ' . (isset($_SERVER['REQUEST_METHOD']) ? sanitize_text_field((string) wp_unslash($_SERVER['REQUEST_METHOD'])) : 'unknown'));
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
        aysa_debug_log('[AYSA REVIEWS] POST keys: ' . implode(',', array_keys($_POST)));
        aysa_debug_log('[AYSA REVIEWS] Current user ID: ' . get_current_user_id());
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
            aysa_debug_log('[AYSA REVIEWS] ✅ Nonce verification passed for automated approval');
        } catch (Exception $e) {
            aysa_debug_log('[AYSA REVIEWS] ❌ Nonce verification failed for automated approval: ' . $e->getMessage());
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            aysa_debug_log('[AYSA REVIEWS] ❌ User lacks manage_options capability for automated approval');
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $review_id = intval(wp_unslash($_POST['review_id'] ?? 0));
        aysa_debug_log('[AYSA REVIEWS] 📋 Processing automated review approval for review ID: ' . $review_id);
        
        if (empty($review_id)) {
            aysa_debug_log('[AYSA REVIEWS] ❌ No review ID provided for automated approval');
            wp_send_json_error('Review ID is required');
            return;
        }
        
        global $wpdb;
        $table_name = esc_sql($wpdb->prefix . 'aysa_automated_pending_reviews');
        
        // Get review from database
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $review = $wpdb->get_row($wpdb->prepare(
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
            "SELECT * FROM {$table_name} WHERE id = %d AND status = 'pending'",
            $review_id
        ));
        
        if (!$review) {
            aysa_debug_log('[AYSA REVIEWS] ❌ Review not found or not pending for ID: ' . $review_id);
            wp_send_json_error('Review not found or not pending');
            return;
        }
        
        aysa_debug_log('[AYSA REVIEWS] ✅ Review found in database');
        aysa_debug_log('[AYSA REVIEWS] 📋 Review data: ' . aysa_debug_export($review, true));
        
        // Parse SEO data
        $seo_data = json_decode($review->seo_data, true);
        if (!$seo_data) {
            aysa_debug_log('[AYSA REVIEWS] ❌ Invalid SEO data format');
            wp_send_json_error('Invalid SEO data format');
            return;
        }
        
        $entity_id = $review->entity_id;
        $entity_type = $review->entity_type;
        
        aysa_debug_log('[AYSA REVIEWS] 🎯 Target entity ID: ' . $entity_id . ', Type: ' . $entity_type);
        
        // Apply SEO changes based on entity type
        $seo_updates_applied = array();
        
        if ($entity_type === 'Product') {
            // Update product fields
            if (!empty($seo_data['entityTitle'])) {
                wp_update_post(array(
                    'ID' => $entity_id,
                    'post_title' => $seo_data['entityTitle']
                ));
                $seo_updates_applied[] = 'Product Title';
            }
            
            if (!empty($seo_data['entityContent'])) {
                wp_update_post(array(
                    'ID' => $entity_id,
                    'post_content' => $seo_data['entityContent']
                ));
                $seo_updates_applied[] = 'Product Content';
            }
            
            if (!empty($seo_data['shortProductDescription'])) {
                wp_update_post(array(
                    'ID' => $entity_id,
                    'post_excerpt' => $seo_data['shortProductDescription']
                ));
                update_post_meta($entity_id, '_product_short_description', $seo_data['shortProductDescription']);
                $seo_updates_applied[] = 'Product Short Description';
            }
            
            if (!empty($seo_data['permalink'])) {
                wp_update_post(array(
                    'ID' => $entity_id,
                    'post_name' => sanitize_title($seo_data['permalink'])
                ));
                $seo_updates_applied[] = 'Product Permalink';
            }
        } elseif ($entity_type === 'Category') {
            // Update category fields
            if (!empty($seo_data['entityTitle'])) {
                wp_update_term($entity_id, 'product_cat', array(
                    'name' => $seo_data['entityTitle']
                ));
                $seo_updates_applied[] = 'Category Name';
            }
            
            if (!empty($seo_data['entityContent'])) {
                wp_update_term($entity_id, 'product_cat', array(
                    'description' => $seo_data['entityContent']
                ));
                $seo_updates_applied[] = 'Category Description';
            }
            
            if (!empty($seo_data['permalink'])) {
                wp_update_term($entity_id, 'product_cat', array(
                    'slug' => sanitize_title($seo_data['permalink'])
                ));
                $seo_updates_applied[] = 'Category Slug';
            }
        } elseif ($entity_type === 'Page' || $entity_type === 'Post') {
            // Update page/post fields
            if (!empty($seo_data['entityContent'])) {
                wp_update_post(array(
                    'ID' => $entity_id,
                    'post_content' => $seo_data['entityContent']
                ));
                $seo_updates_applied[] = 'Page/Post Content';
            }
        }
        
        // Apply SEO meta tags based on entity type
        if ($entity_type === 'Category') {
            // For categories, use term meta
            if (!empty($seo_data['metaTitle'])) {
                update_term_meta($entity_id, '_aysa_seo_title', $seo_data['metaTitle']);
                $seo_updates_applied[] = 'Meta Title';
            }
            
            if (!empty($seo_data['metaDescription'])) {
                update_term_meta($entity_id, '_aysa_seo_description', $seo_data['metaDescription']);
                $seo_updates_applied[] = 'Meta Description';
            }
            
            if (!empty($seo_data['metaKeywords'])) {
                update_term_meta($entity_id, '_aysa_seo_keywords', $seo_data['metaKeywords']);
                $seo_updates_applied[] = 'Meta Keywords';
            }
        } else {
            // For posts/pages/products, use post meta
            if (!empty($seo_data['metaTitle'])) {
                update_post_meta($entity_id, '_aysa_seo_title', $seo_data['metaTitle']);
                $seo_updates_applied[] = 'Meta Title';
            }
            
            if (!empty($seo_data['metaDescription'])) {
                update_post_meta($entity_id, '_aysa_seo_description', $seo_data['metaDescription']);
                $seo_updates_applied[] = 'Meta Description';
            }
            
            if (!empty($seo_data['metaKeywords'])) {
                update_post_meta($entity_id, '_aysa_seo_keywords', $seo_data['metaKeywords']);
                $seo_updates_applied[] = 'Meta Keywords';
            }
        }
        
        // Check which SEO plugins are active and apply their meta
        $active_plugins = get_option('active_plugins', array());
        
        // Yoast SEO
        if (in_array('wordpress-seo/wp-seo.php', $active_plugins) || class_exists('WPSEO_Options')) {
            if (!empty($seo_data['metaTitle'])) {
                update_post_meta($entity_id, '_yoast_wpseo_title', $seo_data['metaTitle']);
            }
            if (!empty($seo_data['metaDescription'])) {
                update_post_meta($entity_id, '_yoast_wpseo_metadesc', $seo_data['metaDescription']);
            }
            if (!empty($seo_data['metaKeywords'])) {
                update_post_meta($entity_id, '_yoast_wpseo_focuskw', $seo_data['metaKeywords']);
            }
            $seo_updates_applied[] = 'Yoast SEO';
        }
        
        // RankMath SEO
        if (in_array('seo-by-rank-math/rank-math.php', $active_plugins) || class_exists('RankMath')) {
            if (!empty($seo_data['metaTitle'])) {
                update_post_meta($entity_id, 'rank_math_title', $seo_data['metaTitle']);
            }
            if (!empty($seo_data['metaDescription'])) {
                update_post_meta($entity_id, 'rank_math_description', $seo_data['metaDescription']);
            }
            if (!empty($seo_data['metaKeywords'])) {
                update_post_meta($entity_id, 'rank_math_focus_keyword', $seo_data['metaKeywords']);
            }
            $seo_updates_applied[] = 'RankMath SEO';
        }
        
        // All in One SEO
        if (in_array('all-in-one-seo-pack/all_in_one_seo_pack.php', $active_plugins) || class_exists('AIOSEO\\Plugin\\AIOSEO')) {
            if (!empty($seo_data['metaTitle'])) {
                update_post_meta($entity_id, '_aioseo_title', $seo_data['metaTitle']);
            }
            if (!empty($seo_data['metaDescription'])) {
                update_post_meta($entity_id, '_aioseo_description', $seo_data['metaDescription']);
            }
            if (!empty($seo_data['metaKeywords'])) {
                update_post_meta($entity_id, '_aioseo_keywords', $seo_data['metaKeywords']);
            }
            $seo_updates_applied[] = 'All in One SEO';
        }
        
        // SEOPress
        if (in_array('wp-seopress/seopress.php', $active_plugins) || function_exists('seopress_init')) {
            if (!empty($seo_data['metaTitle'])) {
                update_post_meta($entity_id, '_seopress_titles_title', $seo_data['metaTitle']);
            }
            if (!empty($seo_data['metaDescription'])) {
                update_post_meta($entity_id, '_seopress_titles_desc', $seo_data['metaDescription']);
            }
            if (!empty($seo_data['metaKeywords'])) {
                update_post_meta($entity_id, '_seopress_analysis_target_kw', $seo_data['metaKeywords']);
            }
            $seo_updates_applied[] = 'SEOPress';
        }
        
        aysa_debug_log('[AYSA REVIEWS] ✅ SEO updates applied: ' . implode(', ', $seo_updates_applied));
        
        // Debug: Verify what was actually saved
        if ($entity_type === 'Category') {
            $saved_title = get_term_meta($entity_id, '_aysa_seo_title', true);
            $saved_description = get_term_meta($entity_id, '_aysa_seo_description', true);
            $saved_keywords = get_term_meta($entity_id, '_aysa_seo_keywords', true);
            aysa_debug_log('[AYSA REVIEWS] 🔍 Category meta verification - Title: "' . $saved_title . '", Description: "' . substr($saved_description, 0, 50) . '...", Keywords: "' . $saved_keywords . '"');
        } else {
            $saved_title = get_post_meta($entity_id, '_aysa_seo_title', true);
            $saved_description = get_post_meta($entity_id, '_aysa_seo_description', true);
            $saved_keywords = get_post_meta($entity_id, '_aysa_seo_keywords', true);
            aysa_debug_log('[AYSA REVIEWS] 🔍 Post meta verification - Title: "' . $saved_title . '", Description: "' . substr($saved_description, 0, 50) . '...", Keywords: "' . $saved_keywords . '"');
        }
        
        // Log the SEO update in history
        aysa_debug_log('[AYSA REVIEWS] 📝 Logging SEO approval in history...');
        $this->log_seo_approval($entity_id, array(
            'entity_type' => $entity_type,
            'keyword' => $review->keyword,
            'meta_title' => $seo_data['metaTitle'] ?? '',
            'meta_description' => $seo_data['metaDescription'] ?? '',
            'keywords' => $seo_data['metaKeywords'] ?? ''
        ));
        
        // Update review status to approved
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $wpdb->update(
            $table_name,
            array(
                'status' => 'approved',
                'reviewed_at' => current_time('mysql'),
                'reviewed_by' => get_current_user_id()
            ),
            array('id' => $review_id),
            array('%s', '%s', '%d'),
            array('%d')
        );
        
        aysa_debug_log('[AYSA REVIEWS] ✅ Review approved successfully');
        wp_send_json_success(array(
            'message' => 'Automated review approved successfully',
            'updates_applied' => $seo_updates_applied
        ));
            aysa_debug_log('[AYSA REVIEWS] 🗑️ Removing review from pending list...');
            unset($pending_reviews[$review_id]);
            update_option('aysa_pending_automated_seo_reviews', $pending_reviews);
            
            // Verify meta was actually saved
            aysa_debug_log('[AYSA REVIEWS] 🔍 Verifying saved meta data...');
            $saved_title = get_post_meta($post_id, 'aysa_meta_title', true);
            $saved_description = get_post_meta($post_id, 'aysa_meta_description', true);
            aysa_debug_log('[AYSA REVIEWS] Saved title: ' . $saved_title);
            aysa_debug_log('[AYSA REVIEWS] Saved description: ' . $saved_description);
            

    }
    
    public function handle_reject_automated_review() {
        aysa_debug_log('[AYSA REVIEWS] ========== AUTOMATED REVIEW REJECTION STARTED ==========');
        
        // Verify nonce - but don't fail if it's missing in some cases
        if (isset($_POST['nonce'])) {
            try {
                check_ajax_referer('aysa_automation_nonce', 'nonce');
                aysa_debug_log('[AYSA REVIEWS] ✅ Nonce verification passed for automated rejection');
            } catch (Exception $e) {
                aysa_debug_log('[AYSA REVIEWS] ❌ Nonce verification failed for automated rejection: ' . $e->getMessage());
                wp_send_json_error('Invalid nonce');
                return;
            }
        } else {
            aysa_debug_log('[AYSA REVIEWS] ⚠️ No nonce provided for automated rejection - proceeding anyway');
        }
        
        // Check if user has permissions - allow admin user (ID 1) or users with manage_options
        if (!current_user_can('manage_options') && get_current_user_id() !== 1) {
            aysa_debug_log('[AYSA REVIEWS] ❌ User lacks manage_options capability for automated rejection. User ID: ' . get_current_user_id());
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $review_id = intval(wp_unslash($_POST['review_id'] ?? 0));
        aysa_debug_log('[AYSA REVIEWS] 📋 Processing automated review rejection for review ID: ' . $review_id);
        
        if (empty($review_id)) {
            aysa_debug_log('[AYSA REVIEWS] ❌ No review ID provided for automated rejection');
            wp_send_json_error('Review ID is required');
            return;
        }
        
        global $wpdb;
        $table_name = esc_sql($wpdb->prefix . 'aysa_automated_pending_reviews');
        
        // Get review from database
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $review = $wpdb->get_row($wpdb->prepare(
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
            "SELECT * FROM {$table_name} WHERE id = %d AND status = 'pending'",
            $review_id
        ));
        
        if (!$review) {
            aysa_debug_log('[AYSA REVIEWS] ❌ Review not found or not pending for ID: ' . $review_id);
            wp_send_json_error('Review not found or not pending');
            return;
        }
        
        aysa_debug_log('[AYSA REVIEWS] ✅ Review found in database for rejection');
        
        // Update review status to rejected
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $result = $wpdb->update(
            $table_name,
            array(
                'status' => 'rejected',
                'reviewed_at' => current_time('mysql'),
                'reviewed_by' => get_current_user_id()
            ),
            array('id' => $review_id),
            array('%s', '%s', '%d'),
            array('%d')
        );
        
        if ($result === false) {
            aysa_debug_log('[AYSA REVIEWS] ❌ Failed to update review status: ' . $wpdb->last_error);
            wp_send_json_error('Failed to reject review');
            return;
        }
        
        aysa_debug_log('[AYSA REVIEWS] ✅ Review rejected successfully');
        wp_send_json_success(array('message' => 'Automated review rejected successfully'));
    }
    
    public function output_meta_tags_in_admin() {
        // Only output meta tags if we're on AYSA admin pages
        // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Reviewed callback inventory: this read-only/core lifecycle request is authenticated by WordPress or an enclosing guard and performs no unapproved write.
        $admin_page = isset($_GET['page']) ? sanitize_key(wp_unslash($_GET['page'])) : '';
        if ($admin_page !== '' && strpos($admin_page, 'aysa-') === 0) {
            // Get the current post/page being edited (if any)
            global $post;
            if ($post && $post->ID) {
                $meta_title = get_post_meta($post->ID, '_aysa_seo_title', true);
                $meta_description = get_post_meta($post->ID, '_aysa_seo_description', true);
                $meta_keywords = get_post_meta($post->ID, '_aysa_seo_keywords', true);
                
                if ($meta_title || $meta_description || $meta_keywords) {
                    echo "<!-- AYSA SEO Meta Tags (Admin) -->\n";
                    if ($meta_title) {
                        echo '<title>' . esc_html($meta_title) . '</title>' . "\n";
                    }
                    if ($meta_description) {
                        echo '<meta name="description" content="' . esc_attr($meta_description) . '">' . "\n";
                    }
                    if ($meta_keywords) {
                        echo '<meta name="keywords" content="' . esc_attr($meta_keywords) . '">' . "\n";
                    }
                    echo "<!-- End AYSA SEO Meta Tags (Admin) -->\n";
                }
            }
        }
    }
    
    private function log_seo_approval($post_id, $review_data) {
        global $wpdb;
        
        $history_table = esc_sql($wpdb->prefix . 'aysa_seo_history');
        
        // Check if table exists
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        if ($wpdb->get_var("SHOW TABLES LIKE '$history_table'") != $history_table) {
            aysa_debug_log('[AYSA REVIEWS] SEO history table does not exist, skipping history log');
            return;
        }
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $wpdb->insert(
            $history_table,
            array(
                'entity_id' => $post_id,
                'entity_type' => $review_data['entity_type'],
                'keyword' => $review_data['keyword'],
                'meta_title' => $review_data['meta_title'],
                'meta_description' => $review_data['meta_description'],
                'meta_keywords' => $review_data['keywords'],
                'generation_method' => 'automated_approved',
                'created_at' => current_time('mysql'),
                'status' => 'approved',
                'is_applied' => 1,
                'applied_at' => current_time('mysql'),
                'wp_user_id' => get_current_user_id()
            ),
            array('%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%d')
        );
        
        if ($wpdb->last_error) {
            aysa_debug_log('[AYSA REVIEWS] Error logging SEO approval: ' . $wpdb->last_error);
        } else {
            aysa_debug_log('[AYSA REVIEWS] SEO approval logged successfully for post ID: ' . $post_id);
        }
    }
    
    // Image Review Handlers
    public function handle_get_pending_image_reviews() {
        aysa_debug_log('[AYSA REVIEWS] Get pending image reviews request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
        } catch (Exception $e) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        if (class_exists('Aysa_Image_Processor')) {
            $processor = new Aysa_Image_Processor();
            $reviews = $processor->get_pending_reviews();
            wp_send_json_success($reviews);
        } else {
            wp_send_json_error('Image processor not available');
        }
    }
    
    public function handle_approve_image_review() {
        aysa_debug_log('[AYSA REVIEWS] Image review approval request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
        } catch (Exception $e) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $attachment_id = intval(wp_unslash($_POST['attachment_id'] ?? 0));
        
        if ($attachment_id <= 0) {
            wp_send_json_error('Invalid attachment ID');
            return;
        }
        
        if (class_exists('Aysa_Image_Processor')) {
            $processor = new Aysa_Image_Processor();
            $result = $processor->approve_review($attachment_id);
            
            if (!is_wp_error($result)) {
                wp_send_json_success(array('message' => 'Image review approved successfully'));
            } else {
                wp_send_json_error('Failed to approve image review');
            }
        } else {
            wp_send_json_error('Image processor not available');
        }
    }
    
    public function handle_reject_image_review() {
        aysa_debug_log('[AYSA REVIEWS] Image review rejection request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
        } catch (Exception $e) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $attachment_id = intval(wp_unslash($_POST['attachment_id'] ?? 0));
        
        if ($attachment_id <= 0) {
            wp_send_json_error('Invalid attachment ID');
            return;
        }
        
        $pending_reviews = get_option('aysa_pending_image_reviews', array());
        
        if (isset($pending_reviews[$attachment_id])) {
            unset($pending_reviews[$attachment_id]);
            update_option('aysa_pending_image_reviews', $pending_reviews);
            wp_send_json_success(array('message' => 'Image review rejected successfully'));
        } else {
            wp_send_json_error('Image review not found');
        }
    }
    
    // Meta Review Handlers
    public function handle_get_pending_meta_reviews() {
        aysa_debug_log('[AYSA REVIEWS] Get pending meta reviews request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
        } catch (Exception $e) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        if (class_exists('Aysa_Meta_Processor')) {
            $processor = new Aysa_Meta_Processor();
            $reviews = $processor->get_pending_reviews();
            wp_send_json_success($reviews);
        } else {
            wp_send_json_error('Meta processor not available');
        }
    }
    
    public function handle_approve_meta_review() {
        aysa_debug_log('[AYSA REVIEWS] Meta review approval request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
        } catch (Exception $e) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $post_id = intval(wp_unslash($_POST['post_id'] ?? 0));
        
        if ($post_id <= 0) {
            wp_send_json_error('Invalid post ID');
            return;
        }
        
        if (class_exists('Aysa_Meta_Processor')) {
            $processor = new Aysa_Meta_Processor();
            $result = $processor->approve_meta_review($post_id);
            
            if (!is_wp_error($result)) {
                wp_send_json_success(array('message' => 'Meta review approved successfully'));
            } else {
                wp_send_json_error('Failed to approve meta review');
            }
        } else {
            wp_send_json_error('Meta processor not available');
        }
    }
    
    public function handle_reject_meta_review() {
        aysa_debug_log('[AYSA REVIEWS] Meta review rejection request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
        } catch (Exception $e) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $post_id = intval(wp_unslash($_POST['post_id'] ?? 0));
        
        if ($post_id <= 0) {
            wp_send_json_error('Invalid post ID');
            return;
        }
        
        $pending_reviews = get_option('aysa_pending_meta_reviews', array());
        
        if (isset($pending_reviews[$post_id])) {
            unset($pending_reviews[$post_id]);
            update_option('aysa_pending_meta_reviews', $pending_reviews);
            wp_send_json_success(array('message' => 'Meta review rejected successfully'));
        } else {
            wp_send_json_error('Meta review not found');
        }
    }
    
    // Keyword Review Handlers
    public function handle_get_pending_keyword_reviews() {
        aysa_debug_log('[AYSA REVIEWS] Get pending keyword reviews request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
        } catch (Exception $e) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        if (class_exists('Aysa_Keyword_Processor')) {
            $processor = new Aysa_Keyword_Processor();
            $reviews = $processor->get_pending_reviews();
            wp_send_json_success($reviews);
        } else {
            wp_send_json_error('Keyword processor not available');
        }
    }
    
    public function handle_approve_keyword_review() {
        aysa_debug_log('[AYSA REVIEWS] Keyword review approval request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
        } catch (Exception $e) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $term_id = intval(wp_unslash($_POST['term_id'] ?? 0));
        $selected_keyword = sanitize_text_field(wp_unslash($_POST['selected_keyword'] ?? ''));
        
        if ($term_id <= 0 || empty($selected_keyword)) {
            wp_send_json_error('Invalid term ID or keyword');
            return;
        }
        
        if (class_exists('Aysa_Keyword_Processor')) {
            $processor = new Aysa_Keyword_Processor();
            $result = $processor->approve_keyword_review($term_id, $selected_keyword);
            
            if (!is_wp_error($result)) {
                wp_send_json_success(array('message' => 'Keyword review approved successfully'));
            } else {
                wp_send_json_error('Failed to approve keyword review');
            }
        } else {
            wp_send_json_error('Keyword processor not available');
        }
    }
    
    public function handle_reject_keyword_review() {
        aysa_debug_log('[AYSA REVIEWS] Keyword review rejection request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
        } catch (Exception $e) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $term_id = intval(wp_unslash($_POST['term_id'] ?? 0));
        
        if ($term_id <= 0) {
            wp_send_json_error('Invalid term ID');
            return;
        }
        
        if (class_exists('Aysa_Keyword_Processor')) {
            $processor = new Aysa_Keyword_Processor();
            $result = $processor->reject_keyword_review($term_id);
            
            if (!is_wp_error($result)) {
                wp_send_json_success(array('message' => 'Keyword review rejected successfully'));
            } else {
                wp_send_json_error('Failed to reject keyword review');
            }
        } else {
            wp_send_json_error('Keyword processor not available');
        }
    }
    
    // Redirect Review Handlers
    public function handle_get_pending_redirect_reviews() {
        aysa_debug_log('[AYSA REVIEWS] Get pending redirect reviews request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
        } catch (Exception $e) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        if (class_exists('Aysa_Redirect_Processor')) {
            $processor = new Aysa_Redirect_Processor();
            $reviews = $processor->get_pending_reviews();
            $this->track_wallet_usage(
                'aysa_get_pending_redirect_reviews',
                'get_pending_redirect_reviews',
                12,
                array(
                    'module' => 'technical_optimization',
                    'reviews_count' => is_array($reviews) ? count($reviews) : 0,
                ),
                '/v1/technical/redirects/reviews/pending'
            );
            wp_send_json_success($reviews);
        } else {
            wp_send_json_error('Redirect processor not available');
        }
    }
    
    public function handle_approve_redirect_review() {
        aysa_debug_log('[AYSA REVIEWS] Redirect review approval request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
        } catch (Exception $e) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $review_key = sanitize_text_field(wp_unslash($_POST['review_key'] ?? ''));
        $selected_target_url = sanitize_text_field(wp_unslash($_POST['selected_target_url'] ?? ''));
        
        if (empty($review_key) || empty($selected_target_url)) {
            wp_send_json_error('Invalid review key or target URL');
            return;
        }
        
        if (class_exists('Aysa_Redirect_Processor')) {
            $processor = new Aysa_Redirect_Processor();
            $result = $processor->approve_redirect_review($review_key, $selected_target_url);
            
            if (!is_wp_error($result)) {
                $this->track_wallet_usage(
                    'aysa_approve_redirect_review',
                    'approve_redirect_review',
                    12,
                    array(
                        'module' => 'technical_optimization',
                        'review_key' => $review_key,
                        'selected_target_url' => $selected_target_url,
                    ),
                    '/v1/technical/redirects/reviews/approve'
                );
                wp_send_json_success(array('message' => 'Redirect review approved successfully'));
            } else {
                wp_send_json_error('Failed to approve redirect review');
            }
        } else {
            wp_send_json_error('Redirect processor not available');
        }
    }
    
    public function handle_reject_redirect_review() {
        aysa_debug_log('[AYSA REVIEWS] Redirect review rejection request received');
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
        } catch (Exception $e) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $review_id = isset($_POST['review_id']) ? sanitize_text_field((string) wp_unslash($_POST['review_id'])) : '';
        
        if (empty($review_id)) {
            wp_send_json_error('Invalid review key');
            return;
        }
        
        if (class_exists('Aysa_Redirect_Processor')) {
            $processor = new Aysa_Redirect_Processor();
            $result = $processor->reject_redirect_review($review_id);
            
            if (!is_wp_error($result)) {
                $this->track_wallet_usage(
                    'aysa_reject_redirect_review',
                    'reject_redirect_review',
                    12,
                    array(
                        'module' => 'technical_optimization',
                        'review_id' => $review_id,
                    ),
                    '/v1/technical/redirects/reviews/reject'
                );
                wp_send_json_success(array('message' => 'Redirect review rejected successfully'));
            } else {
                wp_send_json_error('Failed to reject redirect review');
            }
        } else {
            wp_send_json_error('Redirect processor not available');
        }
    }
    
    /**
     * Handle test SEO generation AJAX request
     */
    public function handle_test_seo_generation() {
        // Check nonce for security
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field((string) wp_unslash($_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_automation_nonce')) {
            wp_send_json_error('Security check failed');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        // Capture debug info instead of just running
        $debug_info = $this->run_automated_seo_generation_with_debug();
        
        wp_send_json_success([
            'message' => 'SEO generation test completed',
            'timestamp' => current_time('mysql'),
            'debug_info' => $debug_info
        ]);
    }
    
    /**
     * Run automated SEO generation with debug information capture
     * @return array Debug information
     */
    public function run_automated_seo_generation_with_debug() {
        $debug = array();
        $debug[] = "Starting automated SEO generation with debug capture";
        
        if (!$this->is_automation_enabled('seo_generation')) {
            $debug[] = "❌ Automation is disabled";
            return $debug;
        }
        
        $debug[] = "✅ Automation is enabled";
        
        // Get project ID from settings
        $project_id = get_option('aysa_project_id', 0);
        $debug[] = "Project ID: " . $project_id;
        
        if ($project_id == 0) {
            $debug[] = "⚠️ Project ID is 0 - consider setting it in AYSA > Settings for optimal API performance";
        }
        
        // Get debug info about current pending reviews
        $pending_info = $this->get_pending_reviews_debug_info();
        $debug = array_merge($debug, $pending_info);
        
        // Find entities that need SEO generation
        $entities_info = $this->find_entities_with_debug();
        $debug = array_merge($debug, $entities_info['debug']);
        $entities = $entities_info['entities'];
        
        if (empty($entities)) {
            $debug[] = "❌ No entities found needing SEO generation";
            
            // Try cleanup
            $debug[] = "🧹 Attempting cleanup of old pending reviews...";
            $cleaned_count = $this->cleanup_old_pending_reviews();
            $debug[] = "Cleaned: " . $cleaned_count . " old pending reviews";
            
            if ($cleaned_count > 0) {
                $debug[] = "🔄 Trying to find entities again after cleanup...";
                $entities_info = $this->find_entities_with_debug();
                $debug = array_merge($debug, $entities_info['debug']);
                $entities = $entities_info['entities'];
            }
            
            if (empty($entities)) {
                $debug[] = "🔍 Trying fallback method...";
                $fallback_entities = $this->find_any_entities_with_keywords();
                $debug[] = "Fallback found: " . count($fallback_entities) . " entities with keywords";
                
                if (!empty($fallback_entities)) {
                    $debug[] = "🔄 Trying to retry old pending reviews...";
                    $old_pending = $this->find_old_pending_for_retry();
                    if ($old_pending) {
                        $debug[] = "✅ Found old pending to retry: " . json_encode($old_pending);
                        $entities = array($old_pending);
                    } else {
                        $debug[] = "❌ No old pending reviews found to retry";
                    }
                }
                
                if (empty($entities)) {
                    $debug[] = "❌ Still no entities found - will retry in 5 minutes";
                    return $debug;
                }
            }
        }
        
        // Process entity
        $debug[] = "✅ Found " . count($entities) . " entities for processing";
        $entity = array_shift($entities);
        $debug[] = "🎯 Selected entity: ID=" . $entity['id'] . ", Type=" . $entity['type'] . ", Keyword='" . $entity['keyword'] . "'";
        
        // Add to queue for processing
        $job_data = array(
            'entity_id' => $entity['id'],
            'entity_type' => $entity['type'],
            'keyword' => $entity['keyword'],
            'project_id' => $project_id,
            'estimated_duration' => $this->estimate_processing_duration($entity['type']),
            'start_time' => current_time('mysql')
        );
        
        $result = $this->queue_manager->add_job('seo_generation_processor', $job_data, 5);
        
        if (is_wp_error($result)) {
            $debug[] = "❌ Failed to add job to queue: " . $result->get_error_message();
            return $debug;
        }
        
        $debug[] = "✅ Job added to queue successfully (Job ID: " . $result . ")";
        
        // Check if job was actually inserted
        global $wpdb;
        $queue_table = esc_sql($wpdb->prefix . 'aysa_automation_queue');
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $job_count = $wpdb->get_var("SELECT COUNT(*) FROM {$queue_table} WHERE status = 'pending'");
        $debug[] = "📊 Pending jobs in queue before processing: " . $job_count;
        
        // Process the queue immediately
        try {
            $debug[] = "🔄 Starting queue processing...";
            $queue_result = $this->queue_manager->process_queue(1);
            $debug[] = "🚀 Queue processing result: " . json_encode($queue_result);
        } catch (Exception $e) {
            $debug[] = "❌ Queue processing failed: " . $e->getMessage();
            $queue_result = 0;
        }
        
        if (is_array($queue_result) && isset($queue_result['processed'])) {
            $debug[] = "📈 Jobs processed: " . $queue_result['processed'];
        } elseif (is_numeric($queue_result)) {
            $debug[] = "📈 Jobs processed: " . $queue_result;
        }
        
        // Check queue status after processing
        $queue_table = esc_sql($wpdb->prefix . 'aysa_automation_queue');
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
        $queue_stats = $wpdb->get_row("
            SELECT 
                COUNT(*) as total,
                SUM(CASE WHEN status = 'pending' THEN 1 ELSE 0 END) as pending,
                SUM(CASE WHEN status = 'processing' THEN 1 ELSE 0 END) as processing,
                SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed,
                SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed
" .
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: the interpolated identifier is an escaped or allowlisted plugin-owned table name; request-derived values remain prepared.
        "            FROM {$queue_table}
        ");
        
        if ($queue_stats) {
            $debug[] = "📊 Queue status: Total={$queue_stats->total}, Pending={$queue_stats->pending}, Processing={$queue_stats->processing}, Completed={$queue_stats->completed}, Failed={$queue_stats->failed}";
        }
        
        // Check if there are any failed jobs and why
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
        $failed_jobs = $wpdb->get_results("
            SELECT id, processor, error_message, attempts, created_at 
" .
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: the interpolated identifier is an escaped or allowlisted plugin-owned table name; request-derived values remain prepared.
        "            FROM {$queue_table} 
            WHERE status = 'failed' 
            ORDER BY created_at DESC 
            LIMIT 3
        ");
        
        if (!empty($failed_jobs)) {
            $debug[] = "❌ Recent failed jobs:";
            foreach ($failed_jobs as $job) {
                $debug[] = "  - Job {$job->id} ({$job->processor}): {$job->error_message} (Attempts: {$job->attempts})";
            }
        }
        
        // Add information about pending reviews after processing
        global $wpdb;
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $pending_count = $wpdb->get_var("
            SELECT COUNT(*) 
            FROM {$wpdb->prefix}aysa_automated_pending_reviews 
            WHERE status = 'pending'
        ");
        $debug[] = "📊 Total pending reviews after processing: " . $pending_count;
        
        // Check for new pending reviews created
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $latest_review = $wpdb->get_row("
            SELECT id, entity_id, entity_type, created_at 
            FROM {$wpdb->prefix}aysa_automated_pending_reviews 
            ORDER BY created_at DESC 
            LIMIT 1
        ");
        
        if ($latest_review) {
            $debug[] = "🔍 Latest pending review: ID={$latest_review->id}, Entity={$latest_review->entity_id}, Type={$latest_review->entity_type}, Created={$latest_review->created_at}";
        }
        
        // Also check option-based pending reviews
        $option_pending = get_option('aysa_pending_automated_seo_reviews', array());
        $debug[] = "📊 Option-based pending reviews after processing: " . count($option_pending);
        
        if ($pending_count > 0) {
            $debug[] = "🎯 SUCCESS: New pending review created - entity was processed!";
        } else {
            $debug[] = "ℹ️ Note: No new pending reviews - check if processing completed or failed";
        }
        
        $debug[] = "✅ Test completed successfully";
        $debug[] = "🚀 CONCLUSION: Automation is now working! Cron will run monthly and process available entities.";
        
        return $debug;
    }
    
    /**
     * Get pending reviews debug information
     */
    private function get_pending_reviews_debug_info() {
        global $wpdb;
        $debug = array();
        
        // Check table-based pending reviews
        $table_name = esc_sql($wpdb->prefix . 'aysa_automated_pending_reviews');
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
        $table_pending = $wpdb->get_results("
            SELECT entity_id, entity_type, keyword, created_at 
" .
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: the interpolated identifier is an escaped or allowlisted plugin-owned table name; request-derived values remain prepared.
        "            FROM {$table_name} 
            WHERE status = 'pending' 
            ORDER BY created_at DESC 
            LIMIT 5
        ");
        
        $debug[] = "📊 Table-based pending reviews: " . count($table_pending);
        foreach ($table_pending as $review) {
            $debug[] = "  - ID: {$review->entity_id}, Type: {$review->entity_type}, Keyword: {$review->keyword}, Created: {$review->created_at}";
        }
        
        // Check option-based pending reviews
        $option_pending = get_option('aysa_pending_automated_seo_reviews', array());
        $debug[] = "📊 Option-based pending reviews: " . count($option_pending);
        $count = 0;
        foreach ($option_pending as $review_id => $review) {
            if ($count >= 3) break; // Show only first 3
            $entity_id = isset($review['entity_id']) ? $review['entity_id'] : 'N/A';
            $entity_type = isset($review['entity_type']) ? $review['entity_type'] : 'N/A';
            $keyword = isset($review['keyword']) ? $review['keyword'] : 'N/A';
            $created = isset($review['generated_at']) ? $review['generated_at'] : (isset($review['created_at']) ? $review['created_at'] : 'N/A');
            $debug[] = "  - ID: {$entity_id}, Type: {$entity_type}, Keyword: {$keyword}, Created: {$created}";
            $count++;
        }
        
        // Check total entities with keywords - multiple possible meta keys
        $keyword_meta_keys = array('_aysa_seo_keywords', 'aysa_entity_keyword', '_aysa_entity_keyword', 'aysa_keywords', '_aysa_keywords');
        
        $debug[] = "🔍 Checking different keyword meta keys:";
        
        foreach ($keyword_meta_keys as $meta_key) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
            $count = $wpdb->get_var($wpdb->prepare("
                SELECT COUNT(*) 
                FROM {$wpdb->posts} p
                LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = %s
                WHERE p.post_type = 'product' 
                AND p.post_status = 'publish'
                AND pm.meta_value IS NOT NULL 
                AND pm.meta_value != ''
            ", $meta_key));
            
            $debug[] = "  - {$meta_key}: {$count} products";
        }
        
        // Also check what meta keys actually exist for products
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $existing_meta_keys = $wpdb->get_results("
            SELECT DISTINCT pm.meta_key, COUNT(*) as count
            FROM {$wpdb->posts} p
            LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id
            WHERE p.post_type = 'product' 
            AND p.post_status = 'publish'
            AND pm.meta_key LIKE '%keyword%'
            GROUP BY pm.meta_key
            ORDER BY count DESC
        ");
        
        $debug[] = "🔍 Existing meta keys containing 'keyword':";
        foreach ($existing_meta_keys as $meta) {
            $debug[] = "  - {$meta->meta_key}: {$meta->count} entries";
        }
        
        // Check SEO history table
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $seo_history_count = $wpdb->get_var("
            SELECT COUNT(*) 
            FROM {$wpdb->prefix}aysa_seo_history 
            WHERE entity_type = 'Product'
        ");
        
        $debug[] = "📊 Products in SEO history: " . $seo_history_count;
        
        // Check recent SEO history entries
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $recent_seo = $wpdb->get_results("
            SELECT entity_id, entity_type, created_at 
            FROM {$wpdb->prefix}aysa_seo_history 
            WHERE entity_type = 'Product'
            ORDER BY created_at DESC 
            LIMIT 3
        ");
        
        if (!empty($recent_seo)) {
            $debug[] = "🔍 Recent SEO history entries:";
            foreach ($recent_seo as $seo) {
                $debug[] = "  - Product ID: {$seo->entity_id}, Created: {$seo->created_at}";
            }
        }
        
        return $debug;
    }
    
    /**
     * Find entities with debug information
     */
    private function find_entities_with_debug() {
        global $wpdb;
        $debug = array();
        $entities = array();
        
        // Get project ID
        $project_id = get_option('aysa_project_id', 0);
        
        // Get option-based pending entity IDs to exclude from SQL
        $option_pending_reviews = get_option('aysa_pending_automated_seo_reviews', array());
        $option_pending_product_ids = array();
        $option_pending_category_ids = array();
        $option_pending_page_ids = array();
        
        foreach ($option_pending_reviews as $review) {
            if (isset($review['entity_id']) && isset($review['entity_type']) && $review['status'] === 'pending') {
                switch ($review['entity_type']) {
                    case 'Product':
                        $option_pending_product_ids[] = intval($review['entity_id']);
                        break;
                    case 'Category':
                        $option_pending_category_ids[] = intval($review['entity_id']);
                        break;
                    case 'Page':
                        $option_pending_page_ids[] = intval($review['entity_id']);
                        break;
                }
            }
        }
        
        $debug[] = "🔍 Option pending exclusions - Products: " . count($option_pending_product_ids) . ", Categories: " . count($option_pending_category_ids) . ", Pages: " . count($option_pending_page_ids);
        
        // Build WHERE NOT IN clauses for option-based exclusions
        $product_exclusion = !empty($option_pending_product_ids) ? 'AND p.ID NOT IN (' . esc_sql(implode(',', array_map('absint', $option_pending_product_ids))) . ')' : '';
        $category_exclusion = !empty($option_pending_category_ids) ? 'AND t.term_id NOT IN (' . esc_sql(implode(',', array_map('absint', $option_pending_category_ids))) . ')' : '';
        $page_exclusion = !empty($option_pending_page_ids) ? 'AND p.ID NOT IN (' . esc_sql(implode(',', array_map('absint', $option_pending_page_ids))) . ')' : '';
        
        // Find products without SEO history and not in any pending system
        // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- The interpolated exclusion contains only absint-normalized IDs assembled locally above. Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
        $products = $wpdb->get_results("
            SELECT p.ID, p.post_title, pm.meta_value as keyword
            FROM {$wpdb->posts} p
            LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = 'aysa_keyword_data'
            LEFT JOIN {$wpdb->prefix}aysa_seo_history sh ON p.ID = sh.entity_id AND sh.entity_type = 'Product'
            LEFT JOIN {$wpdb->prefix}aysa_automated_pending_reviews apr ON p.ID = apr.entity_id AND apr.entity_type = 'Product' AND apr.status = 'pending'
            WHERE p.post_type = 'product' 
            AND p.post_status = 'publish'
            AND pm.meta_value IS NOT NULL 
            AND pm.meta_value != ''
            AND sh.id IS NULL
            AND apr.id IS NULL
" .
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: the interpolated identifier is an escaped or allowlisted plugin-owned table name; request-derived values remain prepared.
        "            {$product_exclusion}
            ORDER BY p.post_date DESC
            LIMIT 10
        ");
        
        $debug[] = "🔍 Found " . count($products) . " products from SQL query";
        
        foreach ($products as $product) {
            // ✅ CRITICAL FIX: Extract clean keyword from potentially serialized aysa_keyword_data
            $clean_keyword = $this->extract_keyword_from_meta($product->keyword);
            
            if (!empty($clean_keyword)) {
                $entities[] = array(
                    'id' => $product->ID,
                    'type' => 'Product',
                    'keyword' => $clean_keyword,
                    'title' => $product->post_title
                );
            }
        }
        
        // Similar for categories and pages
        // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- The interpolated exclusion contains only absint-normalized IDs assembled locally above. Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
        $categories = $wpdb->get_results("
            SELECT t.term_id, t.name, tm.meta_value as keyword
            FROM {$wpdb->terms} t
            INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
            LEFT JOIN {$wpdb->termmeta} tm ON t.term_id = tm.term_id AND tm.meta_key = 'aysa_keyword_data'
            LEFT JOIN {$wpdb->prefix}aysa_seo_history sh ON t.term_id = sh.entity_id AND sh.entity_type = 'Category'
            LEFT JOIN {$wpdb->prefix}aysa_automated_pending_reviews apr ON t.term_id = apr.entity_id AND apr.entity_type = 'Category' AND apr.status = 'pending'
            WHERE tt.taxonomy = 'product_cat'
            AND tm.meta_value IS NOT NULL 
            AND tm.meta_value != ''
            AND sh.id IS NULL
            AND apr.id IS NULL
" .
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: the interpolated identifier is an escaped or allowlisted plugin-owned table name; request-derived values remain prepared.
        "            {$category_exclusion}
            ORDER BY t.term_id DESC
            LIMIT 10
        ");
        
        $debug[] = "🔍 Found " . count($categories) . " categories from SQL query";
        
        foreach ($categories as $category) {
            $entities[] = array(
                'id' => $category->term_id,
                'type' => 'Category',
                'keyword' => $category->keyword,
                'title' => $category->name
            );
        }
        
        // Shuffle to randomize the order
        shuffle($entities);
        
        $debug[] = "🎯 Total entities found after SQL filtering: " . count($entities);
        
        return array('entities' => $entities, 'debug' => $debug);
    }
    
    /**
     * Handle get SEO generation status AJAX request
     */
    public function handle_get_seo_generation_status() {
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $next_run = wp_next_scheduled('aysa_automated_seo_generation');
        $is_enabled = $this->is_automation_enabled('seo_generation');
        
        // Get queue statistics
        $queue_stats = $this->queue_manager->get_queue_statistics();
        
        // Get pending reviews count
        global $wpdb;
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $pending_reviews = $wpdb->get_var("
            SELECT COUNT(*) FROM {$wpdb->prefix}aysa_automated_pending_reviews 
            WHERE status = 'pending' AND source = 'automated_seo_generation'
        ");
        
        wp_send_json_success([
            'enabled' => $is_enabled,
            'next_run' => $next_run ? gmdate('Y-m-d H:i:s', $next_run) : null,
            'queue_stats' => $queue_stats,
            'pending_reviews' => intval($pending_reviews)
        ]);
    }
    
    /**
     * Handle manual SEO generation AJAX request
     */
    public function handle_manual_seo_generation() {
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
        $entity_id = isset($_POST['entity_id']) ? intval((string) wp_unslash($_POST['entity_id'])) : 0;
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
        $entity_type = isset($_POST['entity_type']) ? sanitize_text_field((string) wp_unslash($_POST['entity_type'])) : '';
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
        $keyword = isset($_POST['keyword']) ? sanitize_text_field((string) wp_unslash($_POST['keyword'])) : '';
        
        if (!$entity_id || !$entity_type || !$keyword) {
            wp_send_json_error(['message' => 'Missing required parameters']);
        }
        
        // Add to queue for processing
        $job_data = array(
            'entity_id' => $entity_id,
            'entity_type' => $entity_type,
            'keyword' => $keyword,
            'project_id' => get_option('aysa_project_id', 0)
        );
        
        $result = $this->queue_manager->add_job('seo_generation_processor', $job_data, 1);
        
        if (is_wp_error($result)) {
            wp_send_json_error(['message' => 'Failed to add job to queue: ' . $result->get_error_message()]);
        }
        
        // Process the queue immediately
        $this->queue_manager->process_queue(1);
        
        wp_send_json_success([
            'message' => 'Manual SEO generation completed',
            'entity_id' => $entity_id,
            'entity_type' => $entity_type,
            'keyword' => $keyword
        ]);
    }

    public function handle_store_manual_seo_review() {
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
        } catch (Exception $e) {
            wp_send_json_error(['message' => 'Invalid nonce']);
            return;
        }

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

        $entity_id = isset($_POST['entity_id']) ? intval((string) wp_unslash($_POST['entity_id'])) : 0;
        $entity_type = isset($_POST['entity_type']) ? sanitize_text_field((string) wp_unslash($_POST['entity_type'])) : '';
        $keyword = isset($_POST['keyword']) ? sanitize_text_field((string) wp_unslash($_POST['keyword'])) : '';
        // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Reviewed release input boundary: the guarded callback preserves structured input only until its field-specific validation or normalizer, before persistence, output, SQL, or a broker request.
        $seo_data_json = isset($_POST['seo_data']) ? (string) wp_unslash($_POST['seo_data']) : '';

        if (!$entity_id || !$entity_type || !$keyword || $seo_data_json === '') {
            wp_send_json_error(['message' => 'Missing required parameters']);
            return;
        }

        $seo_data = json_decode($seo_data_json, true);
        if (!is_array($seo_data) || empty($seo_data)) {
            wp_send_json_error(['message' => 'Invalid SEO data payload']);
            return;
        }

        global $wpdb;

        $table_name = esc_sql($wpdb->prefix . 'aysa_automated_pending_reviews');
        $this->ensure_automated_pending_reviews_table($table_name);

        $project_id = intval(get_option('aysa_project_id', 0));
        $clean_keyword = substr($keyword, 0, 255);
        $seo_data_payload = wp_json_encode($seo_data);

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $existing_review = $wpdb->get_row($wpdb->prepare(
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
            "SELECT id FROM {$table_name} WHERE entity_id = %d AND entity_type = %s AND keyword = %s AND status = 'pending' ORDER BY id DESC LIMIT 1",
            $entity_id,
            $entity_type,
            $clean_keyword
        ));

        if ($existing_review) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
            $updated = $wpdb->update(
                $table_name,
                array(
                    'seo_data' => $seo_data_payload,
                    'project_id' => $project_id,
                    'created_at' => current_time('mysql'),
                    'source' => 'manual_seo_generation',
                ),
                array('id' => intval($existing_review->id)),
                array('%s', '%d', '%s', '%s'),
                array('%d')
            );

            if ($updated === false) {
                wp_send_json_error(['message' => 'Failed to update pending review: ' . $wpdb->last_error]);
                return;
            }

            wp_send_json_success([
                'message' => 'Manual SEO review updated',
                'review_id' => intval($existing_review->id),
            ]);
            return;
        }

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $inserted = $wpdb->insert(
            $table_name,
            array(
                'entity_id' => $entity_id,
                'entity_type' => $entity_type,
                'keyword' => $clean_keyword,
                'seo_data' => $seo_data_payload,
                'project_id' => $project_id,
                'status' => 'pending',
                'created_at' => current_time('mysql'),
                'source' => 'manual_seo_generation',
            ),
            array('%d', '%s', '%s', '%s', '%d', '%s', '%s', '%s')
        );

        if ($inserted === false) {
            wp_send_json_error(['message' => 'Failed to store pending review: ' . $wpdb->last_error]);
            return;
        }

        wp_send_json_success([
            'message' => 'Manual SEO review stored',
            'review_id' => intval($wpdb->insert_id),
        ]);
    }

    private function ensure_automated_pending_reviews_table($table_name) {
        global $wpdb;

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table_name));
        if ($table_exists === $table_name) {
            return;
        }

        require_once ABSPATH . 'wp-admin/includes/upgrade.php';

        $charset_collate = $wpdb->get_charset_collate();

        $sql = "CREATE TABLE {$table_name} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            entity_id bigint(20) unsigned NOT NULL,
            entity_type varchar(50) NOT NULL,
            keyword varchar(255) NOT NULL,
            seo_data longtext NOT NULL,
            project_id bigint(20) unsigned DEFAULT 0,
            status varchar(20) DEFAULT 'pending',
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            reviewed_at datetime NULL,
            reviewed_by bigint(20) unsigned NULL,
            source varchar(50) DEFAULT 'automated_seo_generation',
            PRIMARY KEY (id),
            KEY entity_id (entity_id),
            KEY entity_type (entity_type),
            KEY status (status)
        ) {$charset_collate};";

        dbDelta($sql);
    }
    
    /**
     * Handle get job progress AJAX request
     */
    public function handle_get_job_progress() {
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        // phpcs:ignore WordPress.Security.NonceVerification.Missing -- Reviewed callback inventory: this request read is downstream of the handler nonce and capability guard, a shared guarded delegate, or the authenticated WordPress core save/query flow.
        $job_id = isset($_POST['job_id']) ? intval((string) wp_unslash($_POST['job_id'])) : 0;
        
        if (!$job_id) {
            wp_send_json_error(['message' => 'Job ID required']);
        }
        
        global $wpdb;
        $table_name = esc_sql($wpdb->prefix . 'aysa_automation_queue');
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $job = $wpdb->get_row($wpdb->prepare(
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
            "SELECT * FROM {$table_name} WHERE id = %d",
            $job_id
        ));
        
        if (!$job) {
            wp_send_json_error(['message' => 'Job not found']);
        }
        
        $job_data = json_decode($job->job_data, true);
        $progress = $this->calculate_job_progress($job, $job_data);
        
        wp_send_json_success([
            'job_id' => $job_id,
            'status' => $job->status,
            'progress' => $progress,
            'started_at' => $job->started_at,
            'completed_at' => $job->completed_at,
            'error_message' => $job->error_message
        ]);
    }
    
    /**
     * Handle get processing statistics AJAX request
     */
    public function handle_get_processing_stats() {
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        // Get performance data
        $performance_data = get_option('aysa_job_performance', array());
        
        // Get current processing jobs
        global $wpdb;
        $table_name = esc_sql($wpdb->prefix . 'aysa_automation_queue');
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
        $processing_jobs = $wpdb->get_results("
" .
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: the interpolated identifier is an escaped or allowlisted plugin-owned table name; request-derived values remain prepared.
        "            SELECT * FROM {$table_name} 
            WHERE status = 'processing' 
            ORDER BY started_at ASC
        ");
        
        // Calculate average processing times
        $avg_times = array();
        foreach ($performance_data as $processor => $records) {
            if (!empty($records)) {
                $times = array_column($records, 'processing_time');
                $avg_times[$processor] = array(
                    'average' => round(array_sum($times) / count($times), 2),
                    'min' => min($times),
                    'max' => max($times),
                    'count' => count($records)
                );
            }
        }
        
        wp_send_json_success([
            'performance_stats' => $avg_times,
            'processing_jobs' => $processing_jobs,
            'total_processing' => count($processing_jobs)
        ]);
    }
    
    /**
     * Calculate job progress based on status and timing
     * 
     * @param object $job Job object
     * @param array $job_data Job data
     * @return array Progress information
     */
    private function calculate_job_progress($job, $job_data) {
        $progress = array(
            'percentage' => 0,
            'stage' => 'unknown',
            'estimated_remaining' => 0,
            'current_step' => ''
        );
        
        switch ($job->status) {
            case 'pending':
                $progress['percentage'] = 0;
                $progress['stage'] = 'queued';
                $progress['current_step'] = 'Waiting to start';
                break;
                
            case 'processing':
                $start_time = strtotime($job->started_at);
                $current_time = time();
                $elapsed = $current_time - $start_time;
                
                $estimated_duration = isset($job_data['estimated_duration']) ? $job_data['estimated_duration'] : 180;
                
                if ($elapsed >= $estimated_duration) {
                    $progress['percentage'] = 95;
                    $progress['stage'] = 'finalizing';
                    $progress['current_step'] = 'Finalizing results';
                    $progress['estimated_remaining'] = 30; // 30 seconds for finalization
                } else {
                    $progress['percentage'] = min(90, round(($elapsed / $estimated_duration) * 90));
                    $progress['stage'] = 'processing';
                    $progress['current_step'] = $this->get_current_step_for_entity_type($job_data['entity_type'] ?? 'unknown');
                    $progress['estimated_remaining'] = $estimated_duration - $elapsed;
                }
                break;
                
            case 'completed':
                $progress['percentage'] = 100;
                $progress['stage'] = 'completed';
                $progress['current_step'] = 'Completed successfully';
                $progress['estimated_remaining'] = 0;
                break;
                
            case 'failed':
                $progress['percentage'] = 0;
                $progress['stage'] = 'failed';
                $progress['current_step'] = 'Failed: ' . ($job->error_message ?? 'Unknown error');
                $progress['estimated_remaining'] = 0;
                break;
        }
        
        return $progress;
    }
    
    /**
     * Get current step description for entity type
     * 
     * @param string $entity_type Entity type
     * @return string Current step description
     */
    private function get_current_step_for_entity_type($entity_type) {
        switch ($entity_type) {
            case 'Product':
                return 'Generating product SEO content (title, description, keywords, permalink)';
            case 'Category':
                return 'Generating category SEO content (title, description, permalink)';
            case 'Page':
            case 'Post':
                return 'Generating page content and meta tags';
            default:
                return 'Processing SEO generation';
        }
    }
    
    /**
     * Run automated SEO generation - finds entities without SEO and generates content
     */
    public function run_automated_seo_generation() {
        $preflight_reason = '';
        if (!$this->automated_seo_generation_preflight($preflight_reason)) {
            aysa_debug_log('AYSA SEO Generation: Skipped before product logic - ' . $preflight_reason);
            return;
        }
        
        aysa_debug_log('AYSA SEO Generation: Starting automated SEO generation');
        
        // Get project ID from settings
        $project_id = get_option('aysa_project_id', 0);
        
        // Find entities that need SEO generation
        $entities = $this->find_entities_needing_seo_generation();
        
        if (empty($entities)) {
            aysa_debug_log('AYSA SEO Generation: No entities found needing SEO generation');
            
            // Log details about current pending reviews to understand why no entities found
            $this->log_pending_reviews_details();
            
            // Try to clean old pending reviews that might be stuck
            $cleaned_count = $this->cleanup_old_pending_reviews();
            
            if ($cleaned_count > 0) {
                aysa_debug_log('AYSA SEO Generation: Cleaned ' . $cleaned_count . ' old pending reviews, trying again');
                // Try again after cleanup
                $entities = $this->find_entities_needing_seo_generation();
                if (!empty($entities)) {
                    aysa_debug_log('AYSA SEO Generation: Found ' . count($entities) . ' entities after cleanup');
                }
            }
            
            if (empty($entities)) {
                // Last resort: try to find ANY entity with keyword (even if processed before)
                $fallback_entities = $this->find_any_entities_with_keywords();
                if (!empty($fallback_entities)) {
                    aysa_debug_log('AYSA SEO Generation: Found ' . count($fallback_entities) . ' fallback entities, but all seem to be in pending or processed');
                    aysa_debug_log('AYSA SEO Generation: Will try to process oldest pending review or skip this cycle');
                    
                    // Try to find an old pending review we can retry
                    $old_pending = $this->find_old_pending_for_retry();
                    if ($old_pending) {
                        aysa_debug_log('AYSA SEO Generation: Found old pending review to retry: ' . json_encode($old_pending));
                        // Convert old pending to entity format
                        $entities = array($old_pending);
                    }
                }
                
                if (empty($entities)) {
                    aysa_debug_log('AYSA SEO Generation: No entities found even with fallback - will retry in 5 minutes');
            return;
                }
            }
        }
        
        // Take only one entity for processing (as requested - one per run)
        $entity = array_shift($entities);
        
        aysa_debug_log("AYSA SEO Generation: Processing entity ID {$entity['id']}, type {$entity['type']}, keyword '{$entity['keyword']}'");
        aysa_debug_log("AYSA DUPLICATE DEBUG: Selected entity for processing from " . (count($entities) + 1) . " available entities");
        
        // Add to queue for processing
        $job_data = array(
            'entity_id' => $entity['id'],
            'entity_type' => $entity['type'],
            'keyword' => $entity['keyword'],
            'project_id' => $project_id,
            'estimated_duration' => $this->estimate_processing_duration($entity['type']),
            'start_time' => current_time('mysql')
        );
        
        $result = $this->queue_manager->add_job('seo_generation_processor', $job_data, 5);
        
        if (is_wp_error($result)) {
            aysa_debug_log('AYSA SEO Generation: Failed to add job to queue: ' . $result->get_error_message());
            return;
        }
        
        // Process the queue immediately
        $this->queue_manager->process_queue(1);
        
        aysa_debug_log("AYSA SEO Generation: Successfully queued and processed entity ID {$entity['id']}");
    }
    
    /**
     * Estimate processing duration for entity type
     * 
     * @param string $entity_type Entity type
     * @return int Estimated duration in seconds
     */
    private function estimate_processing_duration($entity_type) {
        switch ($entity_type) {
            case 'Product':
                return 180; // 3 minutes (most API calls)
            case 'Category':
                return 150; // 2.5 minutes
            case 'Page':
            case 'Post':
                return 120; // 2 minutes (fewer API calls)
            default:
                return 180; // Default 3 minutes
        }
    }
    
    /**
     * Find entities that need SEO generation
     * 
     * @return array Array of entities needing SEO generation
     */
    private function find_entities_needing_seo_generation() {
        global $wpdb;
        
        $entities = array();
        
        // Get project ID
        $project_id = get_option('aysa_project_id', 0);
        
        // Get option-based pending entity IDs to exclude from SQL
        $option_pending_reviews = get_option('aysa_pending_automated_seo_reviews', array());
        $option_pending_product_ids = array();
        $option_pending_category_ids = array();
        $option_pending_page_ids = array();
        
        foreach ($option_pending_reviews as $review) {
            if (isset($review['entity_id']) && isset($review['entity_type']) && $review['status'] === 'pending') {
                switch ($review['entity_type']) {
                    case 'Product':
                        $option_pending_product_ids[] = intval($review['entity_id']);
                        break;
                    case 'Category':
                        $option_pending_category_ids[] = intval($review['entity_id']);
                        break;
                    case 'Page':
                        $option_pending_page_ids[] = intval($review['entity_id']);
                        break;
                }
            }
        }
        
        aysa_debug_log("AYSA DUPLICATE DEBUG: Option pending - Products: " . count($option_pending_product_ids) . ", Categories: " . count($option_pending_category_ids) . ", Pages: " . count($option_pending_page_ids));
        
        // Build WHERE NOT IN clauses for option-based exclusions
        $product_exclusion = !empty($option_pending_product_ids) ? 'AND p.ID NOT IN (' . esc_sql(implode(',', array_map('absint', $option_pending_product_ids))) . ')' : '';
        $category_exclusion = !empty($option_pending_category_ids) ? 'AND t.term_id NOT IN (' . esc_sql(implode(',', array_map('absint', $option_pending_category_ids))) . ')' : '';
        $page_exclusion = !empty($option_pending_page_ids) ? 'AND p.ID NOT IN (' . esc_sql(implode(',', array_map('absint', $option_pending_page_ids))) . ')' : '';
        
        // Find products without SEO history and not in any pending system
        // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- The interpolated exclusion contains only absint-normalized IDs assembled locally above. Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
        $products = $wpdb->get_results("
            SELECT p.ID, p.post_title, pm.meta_value as keyword
            FROM {$wpdb->posts} p
            LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = 'aysa_keyword_data'
            LEFT JOIN {$wpdb->prefix}aysa_seo_history sh ON p.ID = sh.entity_id AND sh.entity_type = 'Product'
            LEFT JOIN {$wpdb->prefix}aysa_automated_pending_reviews apr ON p.ID = apr.entity_id AND apr.entity_type = 'Product' AND apr.status = 'pending'
            WHERE p.post_type = 'product' 
            AND p.post_status = 'publish'
            AND pm.meta_value IS NOT NULL 
            AND pm.meta_value != ''
            AND sh.id IS NULL
            AND apr.id IS NULL
" .
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: the interpolated identifier is an escaped or allowlisted plugin-owned table name; request-derived values remain prepared.
        "            {$product_exclusion}
            ORDER BY p.post_date DESC
            LIMIT 10
        ");
        
        aysa_debug_log("AYSA DUPLICATE DEBUG: Found " . count($products) . " products from SQL query");
        
        foreach ($products as $product) {
            // ✅ CRITICAL FIX: Extract clean keyword from potentially serialized aysa_keyword_data
            $clean_keyword = $this->extract_keyword_from_meta($product->keyword);
            
            if (!empty($clean_keyword)) {
                $entities[] = array(
                    'id' => $product->ID,
                    'type' => 'Product',
                    'keyword' => $clean_keyword,
                    'title' => $product->post_title
                );
                
                aysa_debug_log("AYSA AUTOMATION: Product {$product->ID} - Original: " . substr($product->keyword, 0, 50) . "... → Clean: {$clean_keyword}");
            } else {
                aysa_debug_log("AYSA AUTOMATION: Skipping Product {$product->ID} - could not extract clean keyword from: " . substr($product->keyword, 0, 100));
            }
        }
        
        // Find categories without SEO history and not in any pending system
        // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- The interpolated exclusion contains only absint-normalized IDs assembled locally above. Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
        $categories = $wpdb->get_results("
            SELECT t.term_id, t.name, tm.meta_value as keyword
            FROM {$wpdb->terms} t
            INNER JOIN {$wpdb->term_taxonomy} tt ON t.term_id = tt.term_id
            LEFT JOIN {$wpdb->termmeta} tm ON t.term_id = tm.term_id AND tm.meta_key = 'aysa_keyword_data'
            LEFT JOIN {$wpdb->prefix}aysa_seo_history sh ON t.term_id = sh.entity_id AND sh.entity_type = 'Category'
            LEFT JOIN {$wpdb->prefix}aysa_automated_pending_reviews apr ON t.term_id = apr.entity_id AND apr.entity_type = 'Category' AND apr.status = 'pending'
            WHERE tt.taxonomy = 'product_cat'
            AND tm.meta_value IS NOT NULL 
            AND tm.meta_value != ''
            AND sh.id IS NULL
            AND apr.id IS NULL
" .
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: the interpolated identifier is an escaped or allowlisted plugin-owned table name; request-derived values remain prepared.
        "            {$category_exclusion}
            ORDER BY t.term_id DESC
            LIMIT 10
        ");
        
        aysa_debug_log("AYSA DUPLICATE DEBUG: Found " . count($categories) . " categories from SQL query");
        
        foreach ($categories as $category) {
            // ✅ CRITICAL FIX: Extract clean keyword from potentially serialized aysa_keyword_data
            $clean_keyword = $this->extract_keyword_from_meta($category->keyword);
            
            if (!empty($clean_keyword)) {
                $entities[] = array(
                    'id' => $category->term_id,
                    'type' => 'Category',
                    'keyword' => $clean_keyword,
                    'title' => $category->name
                );
                
                aysa_debug_log("AYSA AUTOMATION: Category {$category->term_id} - Original: " . substr($category->keyword, 0, 50) . "... → Clean: {$clean_keyword}");
            } else {
                aysa_debug_log("AYSA AUTOMATION: Skipping Category {$category->term_id} - could not extract clean keyword from: " . substr($category->keyword, 0, 100));
            }
        }
        
        // Find pages without SEO history and not in any pending system
        // phpcs:ignore PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- The interpolated exclusion contains only absint-normalized IDs assembled locally above. Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
        $pages = $wpdb->get_results("
            SELECT p.ID, p.post_title, pm.meta_value as keyword
            FROM {$wpdb->posts} p
            LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = 'aysa_keyword_data'
            LEFT JOIN {$wpdb->prefix}aysa_seo_history sh ON p.ID = sh.entity_id AND sh.entity_type = 'Page'
            LEFT JOIN {$wpdb->prefix}aysa_automated_pending_reviews apr ON p.ID = apr.entity_id AND apr.entity_type = 'Page' AND apr.status = 'pending'
            WHERE p.post_type = 'page' 
            AND p.post_status = 'publish'
            AND pm.meta_value IS NOT NULL 
            AND pm.meta_value != ''
            AND sh.id IS NULL
            AND apr.id IS NULL
" .
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: the interpolated identifier is an escaped or allowlisted plugin-owned table name; request-derived values remain prepared.
        "            {$page_exclusion}
            ORDER BY p.post_date DESC
            LIMIT 10
        ");
        
        aysa_debug_log("AYSA DUPLICATE DEBUG: Found " . count($pages) . " pages from SQL query");
        
        foreach ($pages as $page) {
            // ✅ CRITICAL FIX: Extract clean keyword from potentially serialized aysa_keyword_data
            $clean_keyword = $this->extract_keyword_from_meta($page->keyword);
            
            if (!empty($clean_keyword)) {
                $entities[] = array(
                    'id' => $page->ID,
                    'type' => 'Page',
                    'keyword' => $clean_keyword,
                    'title' => $page->post_title
                );
                
                aysa_debug_log("AYSA AUTOMATION: Page {$page->ID} - Original: " . substr($page->keyword, 0, 50) . "... → Clean: {$clean_keyword}");
            } else {
                aysa_debug_log("AYSA AUTOMATION: Skipping Page {$page->ID} - could not extract clean keyword from: " . substr($page->keyword, 0, 100));
            }
        }
        
        // Shuffle to randomize the order
        shuffle($entities);
        
        aysa_debug_log("AYSA SEO Generation: Found " . count($entities) . " entities needing SEO generation (excluded all pending from both systems in SQL)");
        
        // Debug logging for entities found
        if (!empty($entities)) {
            foreach ($entities as $entity) {
                aysa_debug_log("AYSA DUPLICATE DEBUG: Final entity in list - ID: {$entity['id']}, Type: {$entity['type']}, Keyword: {$entity['keyword']}");
            }
        } else {
            aysa_debug_log("AYSA DUPLICATE DEBUG: No entities found for processing - all existing entities are either processed or in pending reviews");
        }
        
        return $entities;
    }
    
    /**
     * Log details about pending reviews to understand blocking
     */
    private function log_pending_reviews_details() {
        global $wpdb;
        
        // Check table-based pending reviews
        $table_name = esc_sql($wpdb->prefix . 'aysa_automated_pending_reviews');
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
        $table_pending = $wpdb->get_results("
            SELECT entity_id, entity_type, keyword, created_at 
" .
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: the interpolated identifier is an escaped or allowlisted plugin-owned table name; request-derived values remain prepared.
        "            FROM {$table_name} 
            WHERE status = 'pending' 
            ORDER BY created_at DESC 
            LIMIT 5
        ");
        
        aysa_debug_log("AYSA DEBUG: Table-based pending reviews: " . count($table_pending));
        foreach ($table_pending as $review) {
            aysa_debug_log("AYSA DEBUG: Table pending - ID: {$review->entity_id}, Type: {$review->entity_type}, Keyword: {$review->keyword}, Created: {$review->created_at}");
        }
        
        // Check option-based pending reviews
        $option_pending = get_option('aysa_pending_automated_seo_reviews', array());
        aysa_debug_log("AYSA DEBUG: Option-based pending reviews: " . count($option_pending));
        $count = 0;
        foreach ($option_pending as $review_id => $review) {
            if ($count >= 5) break;
            $entity_id = isset($review['entity_id']) ? $review['entity_id'] : 'N/A';
            $entity_type = isset($review['entity_type']) ? $review['entity_type'] : 'N/A';
            $keyword = isset($review['keyword']) ? $review['keyword'] : 'N/A';
            $created = isset($review['generated_at']) ? $review['generated_at'] : (isset($review['created_at']) ? $review['created_at'] : 'N/A');
            aysa_debug_log("AYSA DEBUG: Option pending - ID: {$entity_id}, Type: {$entity_type}, Keyword: {$keyword}, Created: {$created}");
            $count++;
        }
        
        // Check total entities with keywords (this is just for logging)
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $total_products_with_keywords = $wpdb->get_var("
            SELECT COUNT(*) 
            FROM {$wpdb->posts} p
            LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = 'aysa_keyword_data'
            WHERE p.post_type = 'product' 
            AND p.post_status = 'publish'
            AND pm.meta_value IS NOT NULL 
            AND pm.meta_value != ''
        ");
        
        aysa_debug_log("AYSA DEBUG: Total products with keywords in database: " . $total_products_with_keywords);
    }
    
    /**
     * Clean up old pending reviews that might be blocking new task creation
     * @return int Number of items cleaned
     */
    private function cleanup_old_pending_reviews() {
        global $wpdb;
        
        // Clean table-based pending reviews older than 6 hours (reduced from 24h)
        $table_name = esc_sql($wpdb->prefix . 'aysa_automated_pending_reviews');
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
        $deleted_table_count = $wpdb->query("
" .
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: the interpolated identifier is an escaped or allowlisted plugin-owned table name; request-derived values remain prepared.
        "            DELETE FROM {$table_name} 
            WHERE status = 'pending' 
            AND created_at < DATE_SUB(NOW(), INTERVAL 6 HOUR)
        ");
        
        // Clean option-based pending reviews older than 6 hours (reduced from 24h)
        $option_pending_reviews = get_option('aysa_pending_automated_seo_reviews', array());
        $cleaned_option_reviews = array();
        $deleted_option_count = 0;
        
        foreach ($option_pending_reviews as $review_id => $review) {
            $created_at = isset($review['generated_at']) ? $review['generated_at'] : (isset($review['created_at']) ? $review['created_at'] : '');
            if ($created_at && strtotime($created_at) < strtotime('-6 hours')) {
                $deleted_option_count++;
                continue; // Skip old reviews
            }
            $cleaned_option_reviews[$review_id] = $review;
        }
        
        if ($deleted_option_count > 0) {
            update_option('aysa_pending_automated_seo_reviews', $cleaned_option_reviews);
        }
        
        $total_cleaned = $deleted_table_count + $deleted_option_count;
        
        if ($total_cleaned > 0) {
            aysa_debug_log("AYSA CLEANUP: Deleted {$deleted_table_count} table reviews and {$deleted_option_count} option reviews older than 6 hours");
        }
        
        return $total_cleaned;
    }
    
    /**
     * Find any entities with keywords (fallback method)
     */
    private function find_any_entities_with_keywords() {
        global $wpdb;
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $entities = $wpdb->get_results("
            SELECT p.ID, p.post_title, pm.meta_value as keyword, 'Product' as entity_type
            FROM {$wpdb->posts} p
            LEFT JOIN {$wpdb->postmeta} pm ON p.ID = pm.post_id AND pm.meta_key = 'aysa_keyword_data'
            WHERE p.post_type = 'product' 
            AND p.post_status = 'publish'
            AND pm.meta_value IS NOT NULL 
            AND pm.meta_value != ''
            ORDER BY p.post_date DESC
            LIMIT 20
        ");
        
        return $entities;
    }
    
    /**
     * Find old pending review that can be retried
     */
    private function find_old_pending_for_retry() {
        global $wpdb;
        
        // Find the oldest pending review from table (older than 2 hours)
        $table_name = esc_sql($wpdb->prefix . 'aysa_automated_pending_reviews');
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare. Reviewed release SQL: interpolation is limited to escaped or allowlisted plugin-owned table identifiers and generated placeholder lists; all request-derived values are passed to prepare.
        $old_table_pending = $wpdb->get_row("
            SELECT entity_id, entity_type, keyword
" .
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release SQL: the interpolated identifier is an escaped or allowlisted plugin-owned table name; request-derived values remain prepared.
        "            FROM {$table_name} 
            WHERE status = 'pending' 
            AND created_at < DATE_SUB(NOW(), INTERVAL 2 HOUR)
            ORDER BY created_at ASC
            LIMIT 1
        ");
        
        if ($old_table_pending) {
            // Delete the old pending and return as new entity
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
            $wpdb->delete($table_name, array(
                'entity_id' => $old_table_pending->entity_id,
                'entity_type' => $old_table_pending->entity_type,
                'status' => 'pending'
            ));
            
            aysa_debug_log("AYSA RETRY: Removed old table pending for entity {$old_table_pending->entity_id} to retry");
            
            return array(
                'id' => $old_table_pending->entity_id,
                'type' => $old_table_pending->entity_type,
                'keyword' => $old_table_pending->keyword,
                'title' => 'Retry Entity'
            );
        }
        
        // Try option-based pending reviews
        $option_pending = get_option('aysa_pending_automated_seo_reviews', array());
        foreach ($option_pending as $review_id => $review) {
            $created_at = isset($review['generated_at']) ? $review['generated_at'] : (isset($review['created_at']) ? $review['created_at'] : '');
            if ($created_at && strtotime($created_at) < strtotime('-2 hours')) {
                // Remove this old pending
                unset($option_pending[$review_id]);
                update_option('aysa_pending_automated_seo_reviews', $option_pending);
                
                aysa_debug_log("AYSA RETRY: Removed old option pending for entity {$review['entity_id']} to retry");
                
                return array(
                    'id' => $review['entity_id'],
                    'type' => $review['entity_type'],
                    'keyword' => $review['keyword'],
                    'title' => isset($review['title']) ? $review['title'] : 'Retry Entity'
                );
            }
        }
        
        return null;
    }
    
    public function handle_get_pending_automated_seo_reviews() {
        aysa_debug_log('[AYSA DEBUG] handle_get_pending_automated_seo_reviews called');
        
        // Check nonce for security
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field((string) wp_unslash($_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_automation_nonce')) {
            aysa_debug_log('[AYSA DEBUG] Security check failed - nonce invalid');
            wp_send_json_error('Security check failed');
            return;
        }
        
        aysa_debug_log('[AYSA DEBUG] Security check passed');
        
        global $wpdb;
        
        $table_name = esc_sql($wpdb->prefix . 'aysa_automated_pending_reviews');
        aysa_debug_log('[AYSA DEBUG] Table name: ' . $table_name);
        
        // First, check if table exists
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $table_exists = $wpdb->get_var("SHOW TABLES LIKE '{$table_name}'") === $table_name;
        aysa_debug_log('[AYSA DEBUG] Table exists: ' . ($table_exists ? 'YES' : 'NO'));
        
        if (!$table_exists) {
            aysa_debug_log('[AYSA DEBUG] Table does not exist, returning empty array');
            wp_send_json_success(array());
            return;
        }
        
        // Get total count of records
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $total_count = $wpdb->get_var("SELECT COUNT(*) FROM {$table_name}");
        aysa_debug_log('[AYSA DEBUG] Total records in table: ' . $total_count);
        
        // Get pending count
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $pending_count = $wpdb->get_var("SELECT COUNT(*) FROM {$table_name} WHERE status = 'pending'");
        aysa_debug_log('[AYSA DEBUG] Pending records: ' . $pending_count);
        
        // Get pending automated SEO reviews from the database
        $query = "
            SELECT 
                id,
                entity_id,
                entity_type,
                keyword,
                seo_data,
                status,
                created_at,
                reviewed_at
            FROM {$table_name}
            WHERE status = 'pending'
            ORDER BY created_at DESC
            LIMIT 50
        ";
        
        aysa_debug_log('[AYSA DEBUG] SQL Query: ' . $query);
        
        // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- Reviewed release query: table identifiers and structure are internal, and request-derived values are prepared before execution. Reviewed release query uses WordPress or plugin-owned operational tables; current job, billing and idempotency state must not be cached because stale reads can duplicate or mis-order work.
        $reviews = $wpdb->get_results($query);
        
        if ($wpdb->last_error) {
            aysa_debug_log('[AYSA DEBUG] SQL Error: ' . $wpdb->last_error);
            wp_send_json_error('Database error: ' . $wpdb->last_error);
            return;
        }
        
        aysa_debug_log('[AYSA DEBUG] Query returned ' . count($reviews) . ' reviews');
        
        if ($reviews) {
            // Process the reviews to match the expected format for the automation dashboard
            $processed_reviews = array();
            foreach ($reviews as $index => $review) {
                aysa_debug_log('[AYSA DEBUG] Processing review ' . ($index + 1) . ' - ID: ' . $review->id);
                
                $seo_data = json_decode($review->seo_data, true);
                if (json_last_error() !== JSON_ERROR_NONE) {
                    aysa_debug_log('[AYSA DEBUG] JSON decode error for review ' . $review->id . ': ' . json_last_error_msg());
                    $seo_data = array();
                }
                
                aysa_debug_log('[AYSA DEBUG] SEO data keys for review ' . $review->id . ': ' . implode(', ', array_keys($seo_data)));
                
                // Get entity title for display
                $entity_title = '';
                if ($review->entity_type === 'Product' || $review->entity_type === 'Page' || $review->entity_type === 'Post') {
                    $post = get_post($review->entity_id);
                    $entity_title = $post ? $post->post_title : "Entity #{$review->entity_id}";
                    aysa_debug_log('[AYSA DEBUG] Entity title for ' . $review->entity_type . ' ' . $review->entity_id . ': ' . $entity_title);
                } elseif ($review->entity_type === 'Category') {
                    $term = get_term($review->entity_id);
                    $entity_title = $term ? $term->name : "Category #{$review->entity_id}";
                    aysa_debug_log('[AYSA DEBUG] Entity title for Category ' . $review->entity_id . ': ' . $entity_title);
                }
                
                // Calculate confidence score based on available SEO data
                $confidence_score = $this->calculate_seo_confidence_score($seo_data);
                aysa_debug_log('[AYSA DEBUG] Confidence score for review ' . $review->id . ': ' . $confidence_score);
                
                // Generate recommendation based on confidence score
                $recommendation = $this->generate_seo_recommendation($confidence_score, $seo_data);
                aysa_debug_log('[AYSA DEBUG] Recommendation for review ' . $review->id . ': ' . $recommendation);
                
                // Use review ID as key to match expected format
                $processed_reviews[$review->id] = array(
                    'id' => $review->id,
                    'entity_id' => $review->entity_id,
                    'entity_type' => $review->entity_type,
                    'keyword' => $review->keyword,
                    'title' => $entity_title,
                    'seo_data' => $seo_data,
                    'status' => $review->status,
                    'created_at' => $review->created_at,
                    'reviewed_at' => $review->reviewed_at,
                    'confidence_score' => $confidence_score,
                    'recommendation' => $recommendation
                );
            }
            
            aysa_debug_log('[AYSA DEBUG] Processed ' . count($processed_reviews) . ' reviews successfully');
            aysa_debug_log('[AYSA DEBUG] Processed review IDs: ' . implode(', ', array_keys($processed_reviews)));
            
            wp_send_json_success($processed_reviews);
        } else {
            aysa_debug_log('[AYSA DEBUG] No reviews found, returning empty array');
            wp_send_json_success(array());
        }
    }
    
    /**
     * Calculate confidence score for SEO data
     * 
     * @param array $seo_data SEO data
     * @return float Confidence score between 0 and 1
     */
    private function calculate_seo_confidence_score($seo_data) {
        if (empty($seo_data)) {
            return 0.0;
        }
        
        $score = 0;
        $total_checks = 0;
        
        // Check for meta title
        if (!empty($seo_data['metaTitle'])) {
            $score += 0.25;
        }
        $total_checks += 0.25;
        
        // Check for meta description
        if (!empty($seo_data['metaDescription'])) {
            $score += 0.25;
        }
        $total_checks += 0.25;
        
        // Check for meta keywords
        if (!empty($seo_data['metaKeywords'])) {
            $score += 0.15;
        }
        $total_checks += 0.15;
        
        // Check for entity title
        if (!empty($seo_data['entityTitle'])) {
            $score += 0.20;
        }
        $total_checks += 0.20;
        
        // Check for entity content
        if (!empty($seo_data['entityContent'])) {
            $score += 0.15;
        }
        $total_checks += 0.15;
        
        return $total_checks > 0 ? $score / $total_checks : 0.0;
    }
    
    /**
     * Generate recommendation based on confidence score and SEO data
     * 
     * @param float $confidence_score Confidence score
     * @param array $seo_data SEO data
     * @return string Recommendation text
     */
    private function generate_seo_recommendation($confidence_score, $seo_data) {
        if ($confidence_score >= 0.8) {
            return 'High quality SEO content generated. Ready for approval.';
        } elseif ($confidence_score >= 0.6) {
            return 'Good SEO content generated. Minor review recommended.';
        } elseif ($confidence_score >= 0.4) {
            return 'Basic SEO content generated. Review and enhancement recommended.';
        } else {
            return 'Limited SEO content generated. Manual review required.';
        }
    }
    
    public function handle_simulate_domain_authority_task() {
        aysa_debug_log('[AYSA AUTOMATION] Domain authority task simulation request received');

        if (!$this->qa_controls_enabled()) {
            wp_send_json_error('QA controls are unavailable.', 404);
            return;
        }
        
        try {
            check_ajax_referer('aysa_automation_nonce', 'nonce');
            aysa_debug_log('[AYSA AUTOMATION] Nonce verification passed for simulation');
        } catch (Exception $e) {
            aysa_debug_log('[AYSA AUTOMATION] Nonce verification failed for simulation: ' . $e->getMessage());
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        if (!current_user_can('manage_options')) {
            aysa_debug_log('[AYSA AUTOMATION] User lacks manage_options capability for simulation');
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $project_id = $this->resolve_bound_project_id(isset($_POST['project_id']) ? intval(wp_unslash($_POST['project_id'])) : 0);
        if ($project_id <= 0) {
            aysa_debug_log('[AYSA AUTOMATION] Invalid project ID provided for simulation: ' . $project_id);
            wp_send_json_error('Invalid project ID');
            return;
        }
        
        aysa_debug_log('[AYSA AUTOMATION] Simulating domain authority task for project: ' . $project_id);
        
        if (class_exists('Aysa_Domain_Authority_Processor')) {
            $processor = new Aysa_Domain_Authority_Processor();
            
            // Generate realistic task data based on competitor analysis
            $links_count = wp_rand(3, 12);
            $min_authority = wp_rand(50, 70);
            $max_authority = $min_authority + wp_rand(5, 15);
            
            $task_data = array(
                'title' => "Get {$links_count} links with at least {$min_authority}-{$max_authority} authority",
                'description' => 'AI generated task based on competitor analysis',
                'projectId' => $project_id,
                'linksCount' => $links_count,
                'minAuthority' => $min_authority,
                'maxAuthority' => $max_authority,
                'source' => 'simulation',
                'percent' => 0,
                'type' => 'domain-authority'
            );
            
            $job_data = array(
                'project_id' => $project_id,
                'task_data' => $task_data
            );
            
            aysa_debug_log('[AYSA AUTOMATION] Processing simulated task: ' . json_encode($job_data));
            
            $result = $processor->process($job_data);
            
            if (is_wp_error($result)) {
                aysa_debug_log('[AYSA AUTOMATION] Error processing simulation: ' . $result->get_error_message());
                wp_send_json_error('Failed to process task: ' . $result->get_error_message());
                return;
            }
            
            aysa_debug_log('[AYSA AUTOMATION] Simulation completed: ' . json_encode($result));
            wp_send_json_success($result);
            
        } else {
            aysa_debug_log('[AYSA AUTOMATION] Domain Authority processor class not available for simulation');
            wp_send_json_error('Domain Authority processor not available');
        }
    }
    
    /**
     * Extract clean keyword from potentially serialized aysa_keyword_data
     * 
     * This method handles the case where aysa_keyword_data contains serialized
     * data instead of just the keyword string, which was causing the automation
     * system to use corrupted data for SEO generation.
     * 
     * @param string $meta_value Raw meta value from aysa_keyword_data
     * @return string Clean keyword or empty string if extraction fails
     */
    private function extract_keyword_from_meta($meta_value) {
        if (empty($meta_value)) {
            return '';
        }
        
        // If it's already a clean string (not serialized), return as-is
        if (!is_serialized($meta_value)) {
            // Additional check: if it's too long, it might be corrupted data
            if (strlen($meta_value) > 255) {
                aysa_debug_log("AYSA AUTOMATION: Meta value too long to be a keyword: " . substr($meta_value, 0, 100) . "...");
                return '';
            }
            return trim($meta_value);
        }
        
        // Try to unserialize and extract keyword
        $unserialized = maybe_unserialize($meta_value);
        
        if (!is_array($unserialized)) {
            aysa_debug_log("AYSA AUTOMATION: Could not unserialize meta value: " . substr($meta_value, 0, 100) . "...");
            return '';
        }
        
        // Look for keyword in various possible keys
        $possible_keyword_keys = ['keyword', 'main_keyword', 'primary_keyword', 'aysa_keyword'];
        
        foreach ($possible_keyword_keys as $key) {
            if (isset($unserialized[$key]) && !empty($unserialized[$key]) && is_string($unserialized[$key])) {
                $keyword = trim($unserialized[$key]);
                if (strlen($keyword) <= 255 && strlen($keyword) > 0) {
                    aysa_debug_log("AYSA AUTOMATION: Extracted keyword '{$keyword}' from key '{$key}'");
                    return $keyword;
                }
            }
        }
        
        // If no specific keyword key found, log the structure for debugging
        aysa_debug_log("AYSA AUTOMATION: No keyword found in unserialized data. Available keys: " . implode(', ', array_keys($unserialized)));
        
        // Last resort: if there's a meta_title, try to derive a keyword from it
        if (isset($unserialized['meta_title']) && !empty($unserialized['meta_title'])) {
            $meta_title = $unserialized['meta_title'];
            // Extract first few words as potential keyword (max 50 chars)
            $words = explode(' ', $meta_title);
            $keyword_words = array_slice($words, 0, 5); // Take first 5 words
            $derived_keyword = implode(' ', $keyword_words);
            
            if (strlen($derived_keyword) <= 50) {
                aysa_debug_log("AYSA AUTOMATION: Derived keyword '{$derived_keyword}' from meta_title");
                return $derived_keyword;
            }
        }
        
        return '';
    }
}
