<?php
if (!defined('ABSPATH')) {
    exit;
}


// Function to display the typing text section (global version)
if (!function_exists('aysa_display_typing_text_section_global')) {
    function aysa_display_typing_text_section_global($text_to_type, $element_id_suffix) {
        ob_start();
        ?>
        <div class="aysa-chat-container aysa-wizard-intro-text aysa-global-intro-text">
            <div class="aysa-chat-message aysa-ai-message">
                <p id="aysa-dynamic-text-<?php echo esc_attr($element_id_suffix); ?>"></p>
            </div>
        </div>
        <script type="text/javascript">
            document.addEventListener('DOMContentLoaded', function() {
                const uniqueSuffixGlobal = '<?php echo esc_js($element_id_suffix); ?>';
                const targetElementIdGlobal = 'aysa-dynamic-text-' + uniqueSuffixGlobal;
                // console.log('[Aysa Typing Effect DEBUG Global] Trying to initialize for suffix: ' + uniqueSuffixGlobal + ', Target ID: ' + targetElementIdGlobal);

                const textElementGlobal = document.getElementById(targetElementIdGlobal);
                const textToTypeGlobal = <?php echo json_encode($text_to_type); ?>;
                let currentIndexGlobal = 0;
                const typingSpeedGlobal = 30; // Milliseconds per character

                // console.log('[Aysa Typing Effect DEBUG Global ' + uniqueSuffixGlobal + '] Target Element:', textElementGlobal);
                // console.log('[Aysa Typing Effect DEBUG Global ' + uniqueSuffixGlobal + '] Text to type:', textToTypeGlobal);

                function typeWriterGlobal() {
                    if (textElementGlobal && currentIndexGlobal < textToTypeGlobal.length) {
                        textElementGlobal.innerHTML += textToTypeGlobal.charAt(currentIndexGlobal);
                        currentIndexGlobal++;
                        setTimeout(typeWriterGlobal, typingSpeedGlobal);
                    } else if (textElementGlobal) {
                        textElementGlobal.classList.add('typing-done');
                        // console.log('[Aysa Typing Effect DEBUG Global ' + uniqueSuffixGlobal + '] Typing complete.');
                    } else {
                        // console.error('[Aysa Typing Effect DEBUG Global ' + uniqueSuffixGlobal + '] Target element became null during typing.');
                    }
                }

                if (textElementGlobal) {
                    textElementGlobal.innerHTML = ''; // Clear initial content
                    // console.log('[Aysa Typing Effect DEBUG Global ' + uniqueSuffixGlobal + '] Starting typewriter.');
                    typeWriterGlobal();
                } else {
                    // console.error('[Aysa Typing Effect DEBUG Global ' + uniqueSuffixGlobal + '] Target element NOT FOUND at initialization for ID: ' + targetElementIdGlobal);
                }
            });
        </script>
        <?php
        return ob_get_clean();
    }
}

class Aysa_Admin {
    private $plugin_name;
    private $version;
    public $credits_component;
    private $setup_gate_notice = array();

    public function __construct($plugin_name, $version) {
        $this->plugin_name = $plugin_name;
        $this->version = $version;
        
        // Initialize credits component with demo user ID (0)
        $this->credits_component = new Aysa_Credits_Component(function_exists('get_current_user_id') ? get_current_user_id() : 0);
        
        // Register logout handler
        add_action('wp_ajax_aysa_logout', array($this, 'handle_logout'));
        
        // Register wizard data handler
        add_action('wp_ajax_aysa_save_wizard_data', array($this, 'handle_save_wizard_data'));
        add_action('wp_ajax_aysa_setup_save_tone_of_voice', array($this, 'handle_setup_save_tone_of_voice'));
        add_action('wp_ajax_aysa_setup_api_proxy', array($this, 'handle_setup_api_proxy'));
        add_action('wp_ajax_aysa_setup_general_autofill', array($this, 'handle_setup_general_autofill'));
        add_action('wp_ajax_aysa_setup_conversation', array($this, 'handle_setup_conversation'));
        add_action('wp_ajax_aysa_adjustments_conversation', array($this, 'handle_adjustments_conversation'));
        add_action('wp_ajax_aysa_project_jobs_status', array($this, 'handle_project_jobs_status'));
        add_action('wp_ajax_aysa_conversation_async_start', array($this, 'handle_conversation_async_start'));
        add_action('wp_ajax_aysa_conversation_async_status', array($this, 'handle_conversation_async_status'));
        add_action('wp_ajax_aysa_keyword_research_run_status', array($this, 'handle_keyword_research_run_status'));
        add_action('wp_ajax_aysa_keyword_research_projection_sync', array($this, 'handle_keyword_research_projection_sync'));
        add_action('wp_ajax_nopriv_aysa_keyword_research_projection_sync', array($this, 'handle_keyword_research_projection_sync'));
        add_action('wp_ajax_aysa_technical_site_audit_projection_sync', array($this, 'handle_technical_site_audit_projection_sync'));
        add_action('wp_ajax_nopriv_aysa_technical_site_audit_projection_sync', array($this, 'handle_technical_site_audit_projection_sync'));
        add_action('wp_ajax_aysa_technical_pagespeed_projection_sync', array($this, 'handle_technical_pagespeed_projection_sync'));
        add_action('wp_ajax_nopriv_aysa_technical_pagespeed_projection_sync', array($this, 'handle_technical_pagespeed_projection_sync'));
        add_action('wp_ajax_aysa_technical_schema_projection_sync', array($this, 'handle_technical_schema_projection_sync'));
        add_action('wp_ajax_nopriv_aysa_technical_schema_projection_sync', array($this, 'handle_technical_schema_projection_sync'));
        add_action('wp_ajax_aysa_technical_redirects_projection_sync', array($this, 'handle_technical_redirects_projection_sync'));
        add_action('wp_ajax_nopriv_aysa_technical_redirects_projection_sync', array($this, 'handle_technical_redirects_projection_sync'));
        add_action('wp_ajax_aysa_technical_sitemap_robots_projection_sync', array($this, 'handle_technical_sitemap_robots_projection_sync'));
        add_action('wp_ajax_nopriv_aysa_technical_sitemap_robots_projection_sync', array($this, 'handle_technical_sitemap_robots_projection_sync'));
        add_action('wp_ajax_aysa_pagespeed_report_email', array($this, 'handle_pagespeed_report_email'));
        add_action('wp_ajax_aysa_adjustments_generation_notifications', array($this, 'handle_adjustments_generation_notifications'));
        add_action('wp_ajax_aysa_setup_notifications', array($this, 'handle_setup_notifications'));
        add_action('wp_ajax_aysa_workspace_state_save', array($this, 'handle_workspace_state_save'));
        add_action('wp_ajax_aysa_insight_banner_dismiss', array($this, 'handle_insight_banner_dismiss'));
        add_action('aysa_adjustments_generate_created_entity', array($this, 'handle_adjustments_generate_created_entity'), 10, 1);
        add_action('wp_ajax_aysa_setup_reset_chat_test', array($this, 'handle_setup_reset_chat_test'));
        add_action('wp_ajax_aysa_apprentice_reset_test', array($this, 'handle_apprentice_reset_test'));
        add_action('wp_ajax_aysa_agent8_factory_reset_dry_run', array($this, 'handle_agent8_factory_reset_dry_run'));
        add_action('wp_ajax_aysa_agent8_factory_reset_execute', array($this, 'handle_agent8_factory_reset_execute'));
        add_action('wp_ajax_aysa_access_key_get_test', array($this, 'handle_access_key_get_test'));
        add_action('wp_ajax_aysa_access_key_verify', array($this, 'handle_access_key_verify'));
        add_action('wp_ajax_aysa_activation_conversation', array($this, 'handle_activation_conversation'));
        add_action('wp_ajax_aysa_activation_reset_test', array($this, 'handle_activation_reset_test'));
        add_action('wp_ajax_aysa_agent8_conversation', array($this, 'handle_agent8_conversation'));
        add_action('wp_ajax_aysa_agent9_conversation', array($this, 'handle_agent9_conversation'));
        add_action('wp_ajax_aysa_agent8v3_message', array($this, 'handle_agent8v3_message'));
        add_action('wp_ajax_aysa_agent8v3_action', array($this, 'handle_agent8v3_action'));
        add_action('wp_ajax_aysa_agent8v3_modal', array($this, 'handle_agent8v3_modal'));
        add_action('wp_ajax_aysa_agent8v3_approval', array($this, 'handle_agent8v3_approval'));
        add_action('wp_ajax_aysa_agent8v3_state', array($this, 'handle_agent8v3_state'));
        add_action('wp_ajax_aysa_agent8v3_reset', array($this, 'handle_agent8v3_reset'));
        add_action('wp_ajax_aysa_agent8v3_qa_run', array($this, 'handle_agent8v3_qa_run'));
        add_action('wp_ajax_aysa_agent8v3_qa_clear', array($this, 'handle_agent8v3_qa_clear'));
        add_action('wp_ajax_aysa_agent8v3_qa_export', array($this, 'handle_agent8v3_qa_export'));
        add_action('wp_ajax_aysa_agent8v3_qa_log', array($this, 'handle_agent8v3_qa_log'));
        add_action('wp_ajax_aysa_agent8v3_qa_legacy_sync', array($this, 'handle_agent8v3_qa_legacy_sync'));
        add_action('wp_ajax_aysa_export_pagespeed_pdf', array($this, 'handle_export_pagespeed_pdf'));
        add_action('wp_ajax_aysa_export_pagespeed_pdf_status', array($this, 'handle_export_pagespeed_pdf_status'));
        add_action('admin_init', array($this, 'redirect_deprecated_agent_pages'), 0);
        add_action('admin_init', array($this, 'enforce_access_key_for_admin_pages'), 1);
        add_action('admin_init', array($this, 'enforce_setup_gate_for_admin_pages'), 2);
        add_action('admin_init', array($this, 'maybe_cleanup_retired_agent7_runtime'), 3);
        add_action('admin_notices', array($this, 'render_setup_gate_notice'));
        add_action('admin_notices', array($this, 'render_notifications_notice'));
        add_action('init', array($this, 'enforce_access_key_for_aysa_ajax'), 1);
        add_action('current_screen', array($this, 'maybe_disable_wc_admin_preload_on_aysa_pages'), 1);
        
        // Register WooCommerce categories handler
        add_action('wp_ajax_get_woocommerce_categories', array($this, 'get_woocommerce_categories'));
        
        // Register .htaccess redirects handler
        add_action('wp_ajax_aysa_add_htaccess_redirect', array($this, 'handle_add_htaccess_redirect'));
        add_action('wp_ajax_aysa_get_local_redirects', array($this, 'handle_get_local_redirects'));
        add_action('wp_ajax_aysa_delete_local_redirect', array($this, 'handle_delete_local_redirect'));
        
        // Register .htaccess gone URL handler
        add_action('wp_ajax_aysa_add_htaccess_gone', array($this, 'handle_add_htaccess_gone'));
        
        // Register .htaccess remove redirect handler
        add_action('wp_ajax_aysa_remove_htaccess_redirect', array($this, 'handle_remove_htaccess_redirect'));
        
        // Keep destructive cleanup tooling disabled in live/client runtime unless explicitly enabled.
        if ($this->aysa_allow_destructive_tools()) {
            add_action('wp_ajax_aysa_cleanup_suggested_keywords', array($this, 'handle_cleanup_suggested_keywords'));
            add_action('wp_ajax_aysa_cleanup_get_associations', array($this, 'handle_cleanup_get_associations'));
            add_action('wp_ajax_aysa_cleanup_associations_batch', array($this, 'handle_cleanup_associations_batch'));
            add_action('wp_ajax_aysa_cleanup_wordpress_meta', array($this, 'handle_cleanup_wordpress_meta'));
            add_action('wp_ajax_aysa_cleanup_generated_titles', array($this, 'handle_cleanup_generated_titles'));
            add_action('wp_ajax_aysa_cleanup_content_strategies', array($this, 'handle_cleanup_content_strategies'));
            add_action('wp_ajax_aysa_cleanup_local_project_data', array($this, 'handle_cleanup_local_project_data'));
        }
        
        // Register .htaccess remove gone URL handler
        add_action('wp_ajax_aysa_remove_htaccess_gone', array($this, 'handle_remove_htaccess_gone'));
        
        // Register AJAX handler for redirect suggestions
        add_action('wp_ajax_aysa_get_redirect_suggestions', array($this, 'ajax_get_redirect_suggestions'));
        
        // Register AJAX handler for settings
        add_action('wp_ajax_aysa_save_settings', array($this, 'handle_save_settings'));
        add_action('wp_ajax_aysa_notifications_fetch', array($this, 'handle_notifications_fetch'));
        add_action('wp_ajax_aysa_agent8_notifications_fetch', array($this, 'handle_notifications_fetch'));
        add_action('wp_ajax_aysa_notifications_mark_read', array($this, 'handle_notifications_mark_read'));
        add_action('wp_ajax_aysa_notifications_mark_all_read', array($this, 'handle_notifications_mark_all_read'));
        
        // Register AJAX handlers for automation QA framework
        add_action('wp_ajax_aysa_get_feature_stats', array($this, 'handle_get_feature_stats'));
        add_action('wp_ajax_aysa_check_acceptance_rate', array($this, 'handle_check_acceptance_rate'));
        add_action('wp_ajax_aysa_save_automation_setting', array($this, 'handle_save_automation_setting'));
        add_action('wp_ajax_aysa_save_qa_settings', array($this, 'handle_save_qa_settings'));
        add_action('wp_ajax_aysa_submit_feedback', array($this, 'handle_submit_feedback'));
        
        // Register AJAX handler for social media content generation
        add_action('wp_ajax_aysa_generate_social_content', array($this, 'handle_generate_social_content'));
        
        // Register automation AJAX handlers
        add_action('wp_ajax_aysa_get_scheduled_tasks', array($this, 'handle_get_scheduled_tasks'));
        add_action('wp_ajax_aysa_schedule_domain_authority_task', array($this, 'handle_schedule_domain_authority_task'));
        add_action('wp_ajax_aysa_schedule_association_task', array($this, 'handle_schedule_association_task'));
        add_action('wp_ajax_aysa_add_task_to_queue', array($this, 'handle_add_task_to_queue'));
        add_action('wp_ajax_aysa_add_pending_review', array($this, 'handle_add_pending_review'));
        
        // Add AJAX hook for loading automation content in Adjustment & Improvement tab
        add_action('wp_ajax_aysa_load_automation_content', array($this, 'handle_load_automation_content'));
        
        // Register status update handler
        add_action('wp_ajax_aysa_update_status', array($this, 'handle_update_status'));
        
        // Register AJAX actions for orders
        add_action('wp_ajax_aysa_get_link_orders', array($this, 'handle_get_link_orders'));
        add_action('wp_ajax_aysa_get_order_details', array($this, 'handle_get_order_details'));
        add_action('wp_ajax_aysa_retry_link_order', array($this, 'handle_retry_link_order'));
        
        // Register AJAX action for WordToLink API URL
        add_action('wp_ajax_aysa_get_wordtolink_api_url', array($this, 'handle_get_wordtolink_api_url'));
        
        // Register AJAX actions for Adverlink off-site suite
        add_action('wp_ajax_aysa_adverlink_list_portals', array($this, 'handle_adverlink_list_portals'));
        add_action('wp_ajax_aysa_adverlink_backlink_audit', array($this, 'handle_adverlink_backlink_audit'));
        add_action('wp_ajax_aysa_adverlink_credit_balance', array($this, 'handle_adverlink_credit_balance'));
        add_action('wp_ajax_aysa_adverlink_create_order', array($this, 'handle_adverlink_create_order'));
        add_action('wp_ajax_aysa_adverlink_track_order', array($this, 'handle_adverlink_track_order'));
        add_action('wp_ajax_aysa_adverlink_get_orders', array($this, 'handle_adverlink_get_orders'));
        add_action('wp_ajax_aysa_adverlink_marketplace_diagnostics', array($this, 'handle_adverlink_marketplace_diagnostics'));
        add_action('wp_ajax_aysa_adverlink_exchange_list_portals', array($this, 'handle_adverlink_exchange_list_portals'));
        add_action('wp_ajax_aysa_adverlink_exchange_create_request', array($this, 'handle_adverlink_exchange_create_request'));
        add_action('wp_ajax_aysa_adverlink_exchange_list_requests', array($this, 'handle_adverlink_exchange_list_requests'));
        add_action('wp_ajax_aysa_adverlink_exchange_get_request', array($this, 'handle_adverlink_exchange_get_request'));
        add_action('wp_ajax_aysa_adverlink_exchange_respond_request', array($this, 'handle_adverlink_exchange_respond_request'));
        add_action('wp_ajax_aysa_adverlink_exchange_list_activation_candidates', array($this, 'handle_adverlink_exchange_list_activation_candidates'));
        add_action('wp_ajax_aysa_adverlink_exchange_activate_link', array($this, 'handle_adverlink_exchange_activate_link'));
        add_action('wp_ajax_aysa_adverlink_exchange_submit_publication', array($this, 'handle_adverlink_exchange_submit_publication'));
        add_action('wp_ajax_aysa_adverlink_exchange_refresh_verification', array($this, 'handle_adverlink_exchange_refresh_verification'));
        add_action('wp_ajax_aysa_adverlink_exchange_change_state', array($this, 'handle_adverlink_exchange_change_state'));
        add_action('wp_ajax_aysa_adverlink_save_article_draft', array($this, 'handle_adverlink_save_article_draft'));
        add_action('wp_ajax_aysa_adverlink_get_article_draft', array($this, 'handle_adverlink_get_article_draft'));
        add_action('wp_ajax_aysa_adverlink_create_publisher_article_draft', array($this, 'handle_adverlink_create_publisher_article_draft'));
        add_action('wp_ajax_aysa_adverlink_list_publisher_article_drafts', array($this, 'handle_adverlink_list_publisher_article_drafts'));
        add_action('wp_ajax_aysa_adverlink_get_publisher_article_draft', array($this, 'handle_adverlink_get_publisher_article_draft'));
        add_action('wp_ajax_aysa_adverlink_update_publisher_article_draft', array($this, 'handle_adverlink_update_publisher_article_draft'));
        add_action('wp_ajax_aysa_adverlink_approve_publisher_article_draft', array($this, 'handle_adverlink_approve_publisher_article_draft'));
        add_action('wp_ajax_aysa_adverlink_search_link_targets', array($this, 'handle_adverlink_search_link_targets'));
        add_action('wp_ajax_aysa_adverlink_get_link_issues', array($this, 'handle_adverlink_get_link_issues'));
        add_action('wp_ajax_aysa_adverlink_get_backlink_audit', array($this, 'handle_adverlink_get_backlink_audit'));
        add_action('wp_ajax_aysa_competitor_backlinks_gap_insights', array($this, 'handle_competitor_backlinks_gap_insights'));
        add_action('wp_ajax_aysa_competitor_backlinks_get_cached', array($this, 'handle_competitor_backlinks_get_cached'));
        add_action('wp_ajax_aysa_monitoring_get_associations', array($this, 'handle_monitoring_get_associations'));
        add_action('wp_ajax_aysa_onpage_get_associations', array($this, 'handle_onpage_get_associations'));
        add_action('wp_ajax_aysa_onpage_workspace', array($this, 'handle_onpage_workspace'));
        add_action('wp_ajax_aysa_onpage_detail', array($this, 'handle_onpage_detail'));
        add_action('wp_ajax_aysa_onpage_start_run', array($this, 'handle_onpage_start_run'));
        add_action('wp_ajax_aysa_onpage_run_status', array($this, 'handle_onpage_run_status'));
        add_action('wp_ajax_aysa_onpage_entity_action', array($this, 'handle_onpage_entity_action_v2'));
        add_action('wp_ajax_aysa_onpage_batch_action', array($this, 'handle_onpage_batch_action_v2'));
        add_action('wp_ajax_aysa_monitoring_get_project_id', array($this, 'handle_monitoring_get_project_id'));
        add_action('wp_ajax_aysa_get_project_by_domain_broker', array($this, 'handle_get_project_by_domain_broker'));
        add_action('wp_ajax_aysa_monitoring_get_context', array($this, 'handle_monitoring_get_context'));
        add_action('wp_ajax_aysa_monitoring_rank_plan', array($this, 'handle_monitoring_rank_plan'));
        add_action('wp_ajax_aysa_monitoring_keywords_overview', array($this, 'handle_monitoring_keywords_overview'));
        add_action('wp_ajax_aysa_monitoring_keywords_async_start', array($this, 'handle_monitoring_keywords_async_start'));
        add_action('wp_ajax_aysa_monitoring_keywords_async_status', array($this, 'handle_monitoring_keywords_async_status'));
        add_action('wp_ajax_aysa_monitoring_rank_latest_from_db', array($this, 'handle_monitoring_rank_latest_from_db'));
        add_action('wp_ajax_aysa_monitoring_rank_refresh_due', array($this, 'handle_monitoring_rank_refresh_due'));
        add_action('wp_ajax_aysa_monitoring_rank_refresh_start_background', array($this, 'handle_monitoring_rank_refresh_start_background'));
        add_action('wp_ajax_aysa_monitoring_rank_refresh_status', array($this, 'handle_monitoring_rank_refresh_status'));
        add_action('wp_ajax_aysa_monitoring_conversation', array($this, 'handle_monitoring_conversation'));
        add_action('wp_ajax_aysa_monitoring_get_ai_overview', array($this, 'handle_monitoring_get_ai_overview'));
        add_action('wp_ajax_aysa_monitoring_refresh_ai_snapshot', array($this, 'handle_monitoring_refresh_ai_snapshot'));
        add_action('wp_ajax_aysa_monitoring_start_ai_refresh', array($this, 'handle_monitoring_start_ai_refresh'));
        add_action('wp_ajax_aysa_monitoring_get_ai_refresh_status', array($this, 'handle_monitoring_get_ai_refresh_status'));
        add_action('wp_ajax_aysa_monitoring_get_ai_snapshot_history', array($this, 'handle_monitoring_get_ai_snapshot_history'));
        add_action('wp_ajax_aysa_monitoring_get_ai_snapshot_detail', array($this, 'handle_monitoring_get_ai_snapshot_detail'));
        add_action('wp_ajax_aysa_monitoring_ai_async_start', array($this, 'handle_monitoring_ai_async_start'));
        add_action('wp_ajax_aysa_monitoring_ai_async_status', array($this, 'handle_monitoring_ai_async_status'));
        add_action('wp_ajax_aysa_monitoring_save_ai_query_pack', array($this, 'handle_monitoring_save_ai_query_pack'));
        add_action('wp_ajax_aysa_research_async_start', array($this, 'handle_research_async_start'));
        add_action('wp_ajax_aysa_research_async_status', array($this, 'handle_research_async_status'));
        add_action('wp_ajax_aysa_offpage_async_start', array($this, 'handle_offpage_async_start'));
        add_action('wp_ajax_aysa_offpage_async_status', array($this, 'handle_offpage_async_status'));
        add_action('wp_ajax_aysa_sitemap_competition_get_overview', array($this, 'handle_sitemap_competition_get_overview'));
        add_action('wp_ajax_aysa_sitemap_competition_start_scan', array($this, 'handle_sitemap_competition_start_scan'));
        add_action('wp_ajax_aysa_sitemap_competition_get_status', array($this, 'handle_sitemap_competition_get_status'));
        add_action('wp_ajax_aysa_sitemap_competition_get_detail', array($this, 'handle_sitemap_competition_get_detail'));
        add_action('wp_ajax_aysa_sitemap_competition_get_history', array($this, 'handle_sitemap_competition_get_history'));
        add_action('aysa_research_async_event', array($this, 'run_research_async_event'), 10, 1);
        add_action('aysa_offpage_async_event', array($this, 'run_offpage_async_event'), 10, 1);
        add_action('aysa_conversation_async_event', array($this, 'run_conversation_async_event'), 10, 1);
        add_action('aysa_sitemap_competition_scan_event', array($this, 'run_sitemap_competition_scan_event'), 10, 1);
        add_action('aysa_monitoring_ai_refresh_event', array($this, 'run_monitoring_ai_refresh_event'), 10, 1);
        add_action('aysa_monitoring_ai_async_event', array($this, 'run_monitoring_ai_async_event'), 10, 1);
        add_action('aysa_monitoring_keywords_async_event', array($this, 'run_monitoring_keywords_async_event'), 10, 1);
        add_action('aysa_monitoring_rank_refresh_background_event', array($this, 'handle_monitoring_rank_refresh_background_event'), 10, 1);
        add_action('aysa_monitoring_rank_refresh_cron_tick', array($this, 'handle_monitoring_rank_refresh_background_event'), 10, 1);
        add_action('aysa_technical_site_audit_poll_event', array($this, 'handle_technical_site_audit_poll_event'), 10, 1);
        add_filter('cron_schedules', array($this, 'register_aysa_cron_schedules'));
        add_action('admin_init', array($this, 'aysa_monitoring_history_maybe_create_table'));
        add_action('admin_init', array($this, 'aysa_monitoring_ai_history_maybe_create_table'));
        add_action('admin_init', array($this, 'aysa_sitemap_competition_maybe_create_tables'));
        add_action('init', array($this, 'schedule_adverlink_link_issues_cron'));
        add_action('aysa_adverlink_sync_link_issues_event', array($this, 'handle_adverlink_cron_link_issues_sync'));
        add_action('init', array($this, 'schedule_adverlink_subscription_renewal_cron'));
        add_action('aysa_adverlink_process_subscription_renewals_event', array($this, 'handle_adverlink_cron_subscription_renewals'));
        add_action('init', array($this, 'schedule_adverlink_exchange_verification_cron'));
        add_action('aysa_adverlink_exchange_verify_event', array($this, 'handle_adverlink_cron_exchange_verification'));
        
        // Register AJAX action for link recheck
        add_action('wp_ajax_aysa_recheck_link', array($this, 'handle_recheck_link'));
        
        // Register AJAX action for cancel WordToLink
        add_action('wp_ajax_aysa_cancel_wordtolink', array($this, 'handle_cancel_wordtolink'));
        
        // Register AJAX action for cached suggested keywords
        add_action('wp_ajax_aysa_get_suggested_keywords_cached', array($this, 'handle_get_suggested_keywords_cached'));
        add_action('wp_ajax_aysa_keyword_discovery_get_associations', array($this, 'handle_keyword_discovery_get_associations'));
        add_action('wp_ajax_aysa_keyword_discovery_upsert_association', array($this, 'handle_keyword_discovery_upsert_association'));
        add_action('wp_ajax_aysa_keyword_discovery_get_keywords_and_competitors', array($this, 'handle_keyword_discovery_get_keywords_and_competitors'));
        add_action('wp_ajax_aysa_keyword_discovery_local_keywords', array($this, 'handle_keyword_discovery_local_keywords'));
        add_action('wp_ajax_aysa_keyword_discovery_generate_strategy', array($this, 'handle_keyword_discovery_generate_strategy'));
        add_action('wp_ajax_aysa_keyword_discovery_get_strategy', array($this, 'handle_keyword_discovery_get_strategy'));
        add_action('wp_ajax_aysa_keyword_discovery_refresh_start_background', array($this, 'handle_keyword_discovery_refresh_start_background'));
        add_action('wp_ajax_aysa_keyword_discovery_refresh_status', array($this, 'handle_keyword_discovery_refresh_status'));
        add_action('wp_ajax_aysa_keyword_discovery_apply_staged_to_local', array($this, 'handle_keyword_discovery_apply_staged_to_local'));
        add_action('wp_ajax_aysa_keyword_discovery_keyword_suggestions_refresh', array($this, 'handle_keyword_discovery_keyword_suggestions_refresh'));
        add_action('wp_ajax_aysa_keyword_discovery_intent_batch', array($this, 'handle_keyword_discovery_intent_batch'));
        add_action('wp_ajax_aysa_keyword_discovery_v2_gsc_raw', array($this, 'handle_keyword_discovery_v2_gsc_raw'));
        add_action('wp_ajax_aysa_keyword_discovery_v2_dataforseo_raw', array($this, 'handle_keyword_discovery_v2_dataforseo_raw'));
        add_action('wp_ajax_aysa_keyword_discovery_v2_competitor_gap_raw', array($this, 'handle_keyword_discovery_v2_competitor_gap_raw'));
        add_action('wp_ajax_aysa_keyword_mapping_v1_workspace', array($this, 'handle_keyword_mapping_v1_workspace'));
        add_action('wp_ajax_aysa_keyword_mapping_v1_save_selection', array($this, 'handle_keyword_mapping_v1_save_selection'));
        add_action('wp_ajax_aysa_missing_pages_v1_workspace', array($this, 'handle_missing_pages_v1_workspace'));
        add_action('wp_ajax_aysa_content_strategy_v1_workspace', array($this, 'handle_content_strategy_v1_workspace'));
        add_action('wp_ajax_aysa_competitor_gap_keywords_gap', array($this, 'handle_competitor_gap_keywords_gap'));
        add_action('wp_ajax_aysa_competitor_gap_add_to_keyword_research', array($this, 'handle_competitor_gap_add_to_keyword_research'));
        add_action('wp_ajax_aysa_competitor_identification_seed_competitors', array($this, 'handle_competitor_identification_seed_competitors'));
        add_action('wp_ajax_aysa_competitor_backlinks_setup_competitors', array($this, 'handle_competitor_backlinks_setup_competitors'));
        add_action('wp_ajax_aysa_content_strategy_generate', array($this, 'handle_content_strategy_generate'));
        add_action('wp_ajax_aysa_missing_pages_send_to_plan', array($this, 'handle_missing_pages_send_to_plan'));
        add_action('wp_ajax_aysa_missing_page_generate_meta', array($this, 'handle_missing_page_generate_meta'));
        add_action('wp_ajax_aysa_missing_pages_refactor_titles', array($this, 'handle_missing_pages_refactor_titles'));
        add_action('wp_ajax_aysa_technical_site_audit_start', array($this, 'handle_technical_site_audit_start'));
        add_action('wp_ajax_aysa_technical_site_audit_status', array($this, 'handle_technical_site_audit_status'));
        add_action('wp_ajax_aysa_technical_site_audit_results', array($this, 'handle_technical_site_audit_results'));
        add_action('admin_init', array($this, 'aysa_sf_site_audit_versions_maybe_create_table'));
        add_action('wp_ajax_aysa_technical_pagespeed_summary', array($this, 'handle_technical_pagespeed_summary'));
        add_action('wp_ajax_aysa_technical_sitemap_robots_audit', array($this, 'handle_technical_sitemap_robots_audit'));
        add_action('wp_ajax_aysa_technical_robots_save', array($this, 'handle_technical_robots_save'));
        add_action('wp_ajax_aysa_technical_robots_restore_default', array($this, 'handle_technical_robots_restore_default'));
        add_action('wp_ajax_aysa_technical_sitemap_generate', array($this, 'handle_technical_sitemap_generate'));
        add_action('wp_ajax_aysa_technical_schema_snapshot', array($this, 'handle_technical_schema_snapshot'));
        add_action('wp_ajax_aysa_technical_schema_refresh', array($this, 'handle_technical_schema_refresh'));
        add_action('wp_ajax_aysa_technical_schema_refresh_start', array($this, 'handle_technical_schema_refresh_start'));
        add_action('wp_ajax_aysa_technical_schema_refresh_status', array($this, 'handle_technical_schema_refresh_status'));
        add_action('wp_ajax_aysa_technical_schema_job_start', array($this, 'handle_technical_schema_job_start'));
        add_action('wp_ajax_aysa_technical_schema_job_status', array($this, 'handle_technical_schema_job_status'));
        add_action('wp_ajax_aysa_technical_schema_job_cancel', array($this, 'handle_technical_schema_job_cancel'));
        add_action('wp_ajax_aysa_technical_schema_rewrite_start', array($this, 'handle_technical_schema_rewrite_start'));
        add_action('wp_ajax_aysa_technical_schema_rewrite_status', array($this, 'handle_technical_schema_rewrite_status'));
        add_action('wp_ajax_aysa_schema_inventory_summary', array($this, 'handle_schema_inventory_summary'));
        add_action('wp_ajax_aysa_schema_inventory_list', array($this, 'handle_schema_inventory_list'));
        add_action('wp_ajax_aysa_schema_inventory_detail', array($this, 'handle_schema_inventory_detail'));
        add_action('wp_ajax_aysa_schema_inventory_auto_generate', array($this, 'handle_schema_inventory_auto_generate'));
        add_action('wp_ajax_aysa_schema_inventory_row_action', array($this, 'handle_schema_inventory_row_action'));
        add_action('wp_ajax_aysa_schema_inventory_row_update', array($this, 'handle_schema_inventory_row_update'));
        add_action('wp_ajax_aysa_schema_reconcile_start', array($this, 'handle_technical_schema_refresh_start'));
        add_action('wp_ajax_aysa_schema_reconcile_status', array($this, 'handle_technical_schema_refresh_status'));
        add_action('wp_ajax_aysa_internal_linking_workspace', array($this, 'handle_internal_linking_workspace'));
        add_action('wp_ajax_aysa_internal_linking_detail', array($this, 'handle_internal_linking_detail'));
        add_action('wp_ajax_aysa_internal_linking_start_run', array($this, 'handle_internal_linking_start_run'));
        add_action('wp_ajax_aysa_internal_linking_run_status', array($this, 'handle_internal_linking_run_status'));
        add_action('wp_ajax_aysa_internal_linking_row_action_v2', array($this, 'handle_internal_linking_row_action_v2'));
        add_action('aysa_process_schema_job_batch', array($this, 'process_technical_schema_job_event'), 10, 1);
        add_action('aysa_process_schema_rewrite_batch', array($this, 'process_technical_schema_rewrite_job_event'), 10, 1);
        add_action('aysa_schema_inventory_process_delta_event', array($this, 'handle_schema_inventory_delta_event'));
        add_action('aysa_schema_inventory_nightly_reconcile_event', array($this, 'handle_schema_inventory_nightly_reconcile_event'));
        add_action('init', array($this, 'aysa_schema_inventory_maybe_upgrade_table'));
        add_action('init', array($this, 'aysa_schema_inventory_ensure_scheduled_reconcile'));
        add_action('save_post', array($this, 'handle_schema_inventory_post_change'), 20, 3);
        add_action('transition_post_status', array($this, 'handle_schema_inventory_post_status_transition'), 20, 3);
        add_action('delete_post', array($this, 'handle_schema_inventory_post_delete'), 20, 1);
        add_action('created_term', array($this, 'handle_schema_inventory_term_change'), 20, 3);
        add_action('edited_term', array($this, 'handle_schema_inventory_term_change'), 20, 3);
        add_action('delete_term', array($this, 'handle_schema_inventory_term_delete'), 20, 5);
        add_action('template_redirect', array($this, 'maybe_serve_aysa_technical_sitemap'), 0);
        add_filter('robots_txt', array($this, 'filter_aysa_technical_robots_txt'), 20, 2);
        add_filter('wp_sitemaps_enabled', '__return_false', 100);
        add_filter('wpseo_enable_xml_sitemap', '__return_false', 100);
        add_filter('rank_math/sitemap/enabled', '__return_false', 100);
        add_action('aysa_keyword_discovery_refresh_background_event', array($this, 'handle_keyword_discovery_refresh_background_event'), 10, 1);
        add_action('admin_init', array($this, 'aysa_keyword_discovery_maybe_create_table'));
        add_filter('admin_body_class', array($this, 'add_admin_body_class'));
        
        // Register AJAX action for cumulative Research summary (all tabs)
        add_action('wp_ajax_aysa_get_research_summary', array($this, 'handle_get_research_summary'));
        
        // Register all the hooks related to the admin area functionality
        $this->register_hooks();
    }

    private function aysa_allow_destructive_tools() {
        return defined('AYSA_ALLOW_DESTRUCTIVE_TOOLS') && AYSA_ALLOW_DESTRUCTIVE_TOOLS;
    }

    private function aysa_guard_destructive_tool_request() {
        if ($this->aysa_allow_destructive_tools()) {
            return true;
        }

        wp_send_json_error('This maintenance tool is disabled in live mode.');
        return false;
    }

    public function add_admin_body_class($classes) {
        if ($this->is_aysa_admin_request()) {
            $classes .= ' aysa-admin-ui';
        }
        return $classes;
    }

    private function is_aysa_admin_request() {
        if (!is_admin()) {
            return false;
        }
        $page = isset($_GET['page']) ? sanitize_key((string) $_GET['page']) : '';
        if ($page !== '' && strpos($page, 'aysa') === 0) {
            return true;
        }
        $screen = function_exists('get_current_screen') ? get_current_screen() : null;
        if ($screen && isset($screen->id) && is_string($screen->id) && strpos($screen->id, 'aysa') !== false) {
            return true;
        }
        return false;
    }

    private function aysa_workspace_capability() {
        return 'read';
    }

    private function aysa_current_user_can_workspace() {
        return is_user_logged_in() && current_user_can($this->aysa_workspace_capability());
    }

    private function aysa_supported_research_tabs() {
        return array(
            'keyword-discovery',
            'competitor-gap',
            'competitor-identification',
            'competitor-backlinks',
            'missing-pages',
            'content-strategy',
            'sitemap-competition',
        );
    }

    private function aysa_normalize_research_tab($tab) {
        $tab = sanitize_key((string) $tab);
        if (in_array($tab, $this->aysa_supported_research_tabs(), true)) {
            return $tab;
        }

        return 'keyword-discovery';
    }

    private function aysa_last_research_tab_meta_key() {
        return 'aysa_last_research_tab';
    }

    private function aysa_last_keyword_discovery_subtab_meta_key() {
        return 'aysa_last_keyword_discovery_subtab';
    }

    private function aysa_setup_reset_forced_option_key() {
        return 'aysa_setup_reset_forced';
    }

    private function aysa_onboarding_run_token_option_key($project_id) {
        return 'aysa_onboarding_run_token_' . intval($project_id);
    }

    private function aysa_generate_onboarding_run_token() {
        try {
            return bin2hex(random_bytes(16));
        } catch (Exception $e) {
            return wp_generate_password(32, false, false);
        }
    }

    private function aysa_get_or_create_onboarding_run_token($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return '';
        }

        $token = trim((string) get_option($this->aysa_onboarding_run_token_option_key($project_id), ''));
        if ($token !== '') {
            return $token;
        }

        $token = $this->aysa_generate_onboarding_run_token();
        update_option($this->aysa_onboarding_run_token_option_key($project_id), $token, false);
        return $token;
    }

    private function aysa_rotate_onboarding_run_token($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return '';
        }

        $token = $this->aysa_generate_onboarding_run_token();
        update_option($this->aysa_onboarding_run_token_option_key($project_id), $token, false);
        return $token;
    }

    private function aysa_mark_setup_reset_forced() {
        update_option($this->aysa_setup_reset_forced_option_key(), '1', false);
    }

    private function aysa_clear_setup_reset_forced() {
        delete_option($this->aysa_setup_reset_forced_option_key());
    }

    private function aysa_is_setup_reset_forced() {
        return get_option($this->aysa_setup_reset_forced_option_key(), '') === '1';
    }

    private function aysa_get_last_research_tab() {
        $user_id = get_current_user_id();
        if ($user_id <= 0) {
            return 'keyword-discovery';
        }

        $saved = get_user_meta($user_id, $this->aysa_last_research_tab_meta_key(), true);
        return $this->aysa_normalize_research_tab($saved);
    }

    private function aysa_save_last_research_tab($tab) {
        $user_id = get_current_user_id();
        if ($user_id <= 0) {
            return '';
        }

        $normalized = $this->aysa_normalize_research_tab($tab);
        update_user_meta($user_id, $this->aysa_last_research_tab_meta_key(), $normalized);
        return $normalized;
    }

    private function aysa_supported_keyword_discovery_subtabs() {
        return array(
            'dashboard',
            'gsc_v2',
            'dataforseo_v2',
            'competitor_gap_v2',
            'gsc_auto_discovery',
            'keyword_suggestions',
            'mapped-keywords',
            'unmapped-keywords',
            'deleted-keywords',
            'banned-keywords',
        );
    }

    private function aysa_normalize_keyword_discovery_subtab($subtab) {
        $subtab = sanitize_key((string) $subtab);
        if (in_array($subtab, $this->aysa_supported_keyword_discovery_subtabs(), true)) {
            return $subtab;
        }

        return 'gsc_v2';
    }

    private function aysa_get_last_keyword_discovery_subtab() {
        $user_id = get_current_user_id();
        if ($user_id <= 0) {
            return 'gsc_v2';
        }

        $saved = get_user_meta($user_id, $this->aysa_last_keyword_discovery_subtab_meta_key(), true);
        return $this->aysa_normalize_keyword_discovery_subtab($saved);
    }

    private function aysa_save_last_keyword_discovery_subtab($subtab) {
        $user_id = get_current_user_id();
        if ($user_id <= 0) {
            return '';
        }

        $normalized = $this->aysa_normalize_keyword_discovery_subtab($subtab);
        update_user_meta($user_id, $this->aysa_last_keyword_discovery_subtab_meta_key(), $normalized);
        return $normalized;
    }

    private function aysa_last_research_tab_url() {
        return admin_url('admin.php?page=aysa-research&tab=' . rawurlencode($this->aysa_get_last_research_tab()));
    }

    public function maybe_disable_wc_admin_preload_on_aysa_pages() {
        if (!$this->is_aysa_admin_request()) {
            return;
        }

        // Prevent WooCommerce Admin REST preload from exhausting memory on AYSA screens.
        add_filter('woocommerce_component_settings_preload_endpoints', '__return_empty_array', 9999);
        add_filter('woocommerce_admin_preload_options', '__return_empty_array', 9999);
        add_filter('woocommerce_admin_preload_settings', '__return_empty_array', 9999);

        if (function_exists('wc_get_container') && class_exists('\\Automattic\\WooCommerce\\Blocks\\Assets\\AssetDataRegistry')) {
            try {
                $registry = wc_get_container()->get('\\Automattic\\WooCommerce\\Blocks\\Assets\\AssetDataRegistry');
                if ($registry && method_exists($registry, 'enqueue_asset_data')) {
                    remove_action('admin_print_footer_scripts', array($registry, 'enqueue_asset_data'), 1);
                }
            } catch (Throwable $e) {
                // Ignore container resolution errors in non-standard environments.
            }
        }
    }

    public function enqueue_styles() {
        $page = isset($_GET['page']) ? sanitize_key((string) $_GET['page']) : '';
        $tab = isset($_GET['tab']) ? sanitize_key((string) $_GET['tab']) : '';

        $base_css_path = AYSA_PLUGIN_DIR . 'assets/css/aysa.css';
        $base_css_version = file_exists($base_css_path)
            ? (string) filemtime($base_css_path)
            : $this->version;
        wp_enqueue_style(
            $this->plugin_name,
            AYSA_PLUGIN_URL . 'assets/css/aysa.css',
            array(),
            $base_css_version,
            'all'
        );

        // Enqueue keyword discovery specific CSS only on that page
        $screen = get_current_screen();

        if (strpos((string) $page, 'aysa') === 0) {
            $debug_flag = (defined('AYSA_DEBUG') && AYSA_DEBUG) ? 'true' : 'false';
            $debug_js = "window.AYSA_DEBUG = {$debug_flag};\n" .
                "if (!window.AYSA_DEBUG && window.console) {\n" .
                "  window.console.log = function(){};\n" .
                "  window.console.debug = function(){};\n" .
                "  window.console.info = function(){};\n" .
                "}\n";
            wp_add_inline_script($this->plugin_name, $debug_js, 'before');
        }
        
        if (($page === 'aysa-research' && $tab === 'keyword-discovery') ||
            ($page === 'aysa-research' && empty($tab) && isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'], 'keyword-discovery') !== false)) {
            $keyword_discovery_css_path = AYSA_PLUGIN_DIR . 'assets/css/keyword-discovery.css';
            $keyword_discovery_css_version = file_exists($keyword_discovery_css_path)
                ? (string) filemtime($keyword_discovery_css_path)
                : $this->version;
            
            wp_enqueue_style(
                $this->plugin_name . '-keyword-discovery',
                AYSA_PLUGIN_URL . 'assets/css/keyword-discovery.css',
                array(),
                $keyword_discovery_css_version,
                'all'
            );
        }

        if ($page === 'aysa-research') {
            $research_workspace_css_path = AYSA_PLUGIN_DIR . 'assets/css/research-workspace.css';
            $research_workspace_css_version = file_exists($research_workspace_css_path)
                ? (string) filemtime($research_workspace_css_path)
                : $this->version;

            wp_enqueue_style(
                $this->plugin_name . '-research-workspace',
                AYSA_PLUGIN_URL . 'assets/css/research-workspace.css',
                array($this->plugin_name),
                $research_workspace_css_version,
                'all'
            );
        }
        
        // Enqueue automation dashboard CSS
        if ($page === 'aysa-automation') {
            wp_enqueue_style(
                $this->plugin_name . '-automation',
                plugin_dir_url(__FILE__) . 'css/aysa-admin.css',
                array(),
                $this->version,
                'all'
            );
        }
        
        // Enqueue tab styles for settings page
        if ($page === 'aysa-settings') {
            wp_enqueue_style(
                $this->plugin_name . '-tabs',
                plugin_dir_url(__FILE__) . 'css/aysa-tabs.css',
                array(),
                $this->version,
                'all'
            );
        }

        if ($page === 'aysa-agent8-v3-lab') {
            $agent8v3_css_path = AYSA_PLUGIN_DIR . 'admin/css/aysa-agent8-v3-lab.css';
            $agent8v3_css_version = file_exists($agent8v3_css_path)
                ? (string) filemtime($agent8v3_css_path)
                : $this->version;
            wp_enqueue_style(
                $this->plugin_name . '-agent8-v3-lab',
                AYSA_PLUGIN_URL . 'admin/css/aysa-agent8-v3-lab.css',
                array(),
                $agent8v3_css_version,
                'all'
            );
        }

        wp_enqueue_style('dashicons');

        $offpage_pages = array(
            'aysa-off-page-optimization',
            'aysa-off-page-tools',
        );
        if (in_array($page, $offpage_pages, true)) {
            wp_enqueue_style(
                'font-awesome-6',
                'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css',
                array(),
                '6.4.0'
            );
            wp_enqueue_style(
                'bootstrap-5',
                'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css',
                array(),
                '5.3.0'
            );
        }

        // Enqueue UI core at the end so it can normalize legacy section styles.
        $ui_core_handle = $this->plugin_name . '-ui-core';
        $ui_core_deps = array($this->plugin_name);
        $ui_core_path = AYSA_PLUGIN_DIR . 'assets/css/aysa-ui-core.css';
        $ui_core_version = file_exists($ui_core_path)
            ? (string) filemtime($ui_core_path)
            : $this->version;
        wp_enqueue_style(
            $ui_core_handle,
            AYSA_PLUGIN_URL . 'assets/css/aysa-ui-core.css',
            $ui_core_deps,
            $ui_core_version,
            'all'
        );

        $legacy_research_tabs = array(
            'keyword-discovery',
            'competitor-gap',
            'competitor-identification',
            'competitor-backlinks',
            'missing-pages',
            'content-strategy',
            'sitemap-competition',
        );
        if ($page === 'aysa-research' && in_array($tab, $legacy_research_tabs, true)) {
            $legacy_reset_path = AYSA_PLUGIN_DIR . 'assets/css/aysa-legacy-admin-reset.css';
            $legacy_reset_version = file_exists($legacy_reset_path)
                ? (string) filemtime($legacy_reset_path)
                : $this->version;

            wp_enqueue_style(
                $this->plugin_name . '-legacy-admin-reset',
                AYSA_PLUGIN_URL . 'assets/css/aysa-legacy-admin-reset.css',
                array($ui_core_handle),
                $legacy_reset_version,
                'all'
            );
        }
        
        // Add custom CSS for highlighting SEO Profile menu item
        $custom_css = '
            #adminmenu .wp-submenu a[href*="aysa-setup-seo-profile"] {
                background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%) !important;
                color: white !important;
                font-weight: bold !important;
                border-radius: 4px !important;
                margin: 2px 0 !important;
                box-shadow: 0 2px 4px rgba(76, 175, 80, 0.3) !important;
                position: relative !important;
            }
            #adminmenu .wp-submenu a[href*="aysa-setup-seo-profile"]:hover {
                background: linear-gradient(135deg, #45a049 0%, #4CAF50 100%) !important;
                color: white !important;
                transform: translateX(2px) !important;
                transition: all 0.2s ease !important;
            }
            #adminmenu .wp-submenu a[href*="aysa-setup-seo-profile"]:before {
                content: "" !important;
                position: absolute !important;
                left: -8px !important;
                top: 50% !important;
                transform: translateY(-50%) !important;
                width: 4px !important;
                height: 20px !important;
                background: #FFD700 !important;
                border-radius: 2px !important;
            }
        ';
        wp_add_inline_style($this->plugin_name, $custom_css);
    }

    public function enqueue_scripts() {
        /**
         * This function is provided for demonstration purposes only.
         *
         * An instance of this class should be passed to the run() function
         * defined in Aysa_Loader as all of the hooks are defined
         * in that particular class.
         *
         * The Aysa_Loader will then create the relationship
         * between the defined hooks and the functions defined in this
         * class.
         */

        $screen = function_exists('get_current_screen') ? get_current_screen() : null;
        $page = isset($_GET['page']) ? sanitize_key((string) $_GET['page']) : '';
        $is_aysa_page = (strpos($page, 'aysa') === 0)
            || ($screen && isset($screen->id) && is_string($screen->id) && strpos($screen->id, 'aysa') !== false);

        $is_supported_editor = false;
        if ($screen && isset($screen->base) && is_string($screen->base)) {
            if ($screen->base === 'post' && isset($screen->post_type)) {
                $is_supported_editor = in_array((string) $screen->post_type, array('post', 'page', 'product'), true);
            } elseif ($screen->base === 'term') {
                $taxonomy = isset($_GET['taxonomy']) ? sanitize_key((string) $_GET['taxonomy']) : '';
                $is_supported_editor = in_array($taxonomy, array('category', 'product_cat'), true);
            }
        }

        if (!$is_aysa_page && !$is_supported_editor) {
            return;
        }

        // Get project ID from WordPress options (no hardcoded default)
        $project_id = intval(get_option('aysa_project_id', 0));

        // Main plugin script
        wp_enqueue_script(
            $this->plugin_name,
            AYSA_PLUGIN_URL . 'assets/js/aysa.js',
            array('jquery'),
            $this->version,
            false
        );
        
        // API service script
        wp_enqueue_script(
            $this->plugin_name . '-api',
            AYSA_PLUGIN_URL . 'assets/js/aysa-api.js',
            array('jquery', $this->plugin_name),
            $this->version . '.1',
            false
        );
        
        // Credit tracker script - monitors API calls and updates credits in real-time
        wp_enqueue_script(
            $this->plugin_name . '-credit-tracker',
            AYSA_PLUGIN_URL . 'assets/js/aysa-credit-tracker.js',
            array('jquery', $this->plugin_name . '-api'),
            $this->version,
            false
        );
        
        // Service type helper script for VAT calculations and country/region handling
        wp_enqueue_script(
            $this->plugin_name . '-service-type-helper',
            AYSA_PLUGIN_URL . 'admin/js/aysa-service-type-helper.js',
            array('jquery'),
            $this->version,
            false
        );

        $page = isset($_GET['page']) ? sanitize_key((string) $_GET['page']) : '';
        $offpage_pages = array(
            'aysa-off-page-optimization',
            'aysa-off-page-tools',
        );
        if (in_array($page, $offpage_pages, true)) {
            wp_enqueue_script(
                'bootstrap-5',
                'https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js',
                array('jquery'),
                '5.3.0',
                true
            );
        }
        
        wp_enqueue_script(
            'aysa-domain-authority-scheduler',
            AYSA_PLUGIN_URL . 'admin/js/aysa-domain-authority-scheduler.js',
            array('jquery'),
            $this->version,
            true
        );

        $automation_config = get_option('aysa_automation_config', array());
        $automation_config['ajaxurl'] = admin_url('admin-ajax.php');
        $automation_config['nonce'] = wp_create_nonce('aysa_automation_nonce');
        wp_localize_script(
            'aysa-domain-authority-scheduler',
            'aysaAutomationConfig',
            $automation_config
        );

        $api_js_config = Aysa_API_Config::get_js_config();

        wp_localize_script(
            $this->plugin_name,
            'aysa_params',
            array(
                'api_base_url' => AYSA_API_BASE_URL,
                'ajax_url' => admin_url('admin-ajax.php'),
                'nonce' => wp_create_nonce('aysa_nonce'),
                'auth_token' => get_option('aysa_auth_token', ''),
                'project_id' => $project_id,
                'automation_config' => get_option('aysa_automation_config', array()),
                'api_config' => $api_js_config
            )
        );

        // Always expose API config for assets/js/aysa-api.js across all admin screens.
        wp_localize_script(
            $this->plugin_name . '-api',
            'aysaApiConfig',
            $api_js_config
        );

        
        // Add the WordPress AJAX URL and nonce to our script
        wp_localize_script(
            $this->plugin_name,
            'aysa_ajax',
            array(
                'ajax_url' => admin_url('admin-ajax.php'),
                'nonce'    => wp_create_nonce('aysa_nonce'),
                'project_id' => $project_id,
                'api_config' => $api_js_config
            )
        );

        wp_localize_script(
            $this->plugin_name . '-credit-tracker',
            'aysaCreditTrackerParams',
            array(
                'ajax_url' => admin_url('admin-ajax.php'),
                'nonce' => wp_create_nonce('aysa_nonce'),
                'project_id' => $project_id,
            )
        );
        
        // Add link purchase nonce for AJAX requests
        wp_add_inline_script(
            $this->plugin_name,
            'var aysaLinkPurchaseNonce = "' . wp_create_nonce('aysa_link_purchase_nonce') . '";',
            'before'
        );
        
        // Check if we're on specific AYSA admin pages to add page-specific parameters
        $page = isset($_GET['page']) ? sanitize_key((string) $_GET['page']) : '';
        $tab = isset($_GET['tab']) ? sanitize_key((string) $_GET['tab']) : '';
        
        // Define common parameters that all AYSA pages need
        $common_params = array(
            'api_base_url' => AYSA_API_BASE_URL,
            'ajax_url' => admin_url('admin-ajax.php'),
            'admin_url' => admin_url(),
            'nonce' => wp_create_nonce('aysa_metabox_nonce'),
            'ajax_nonce' => wp_create_nonce('aysa-ajax-nonce'),
            'generic_nonce' => wp_create_nonce('aysa_nonce'),
            'credit_analytics_nonce' => wp_create_nonce('aysa_credit_analytics_nonce'),
            'auth_token' => get_option('aysa_auth_token', ''),
            'project_id' => $project_id,
            'site_url' => site_url(),
            'home_url' => home_url(),
            'api_config' => $api_js_config
        );

        if ($page === 'aysa-agent8-v3-lab') {
            $agent8v3_js_path = AYSA_PLUGIN_DIR . 'admin/js/aysa-agent8-v3-lab.js';
            $agent8v3_js_version = file_exists($agent8v3_js_path)
                ? (string) filemtime($agent8v3_js_path)
                : $this->version;
            $agent8v3_project_id = $project_id > 0 ? 'lab_' . $project_id : 'lab_test';
            wp_enqueue_script(
                $this->plugin_name . '-agent8-v3-lab',
                AYSA_PLUGIN_URL . 'admin/js/aysa-agent8-v3-lab.js',
                array('jquery'),
                $agent8v3_js_version,
                true
            );
            wp_localize_script(
                $this->plugin_name . '-agent8-v3-lab',
                'aysaAgent8V3Lab',
                array(
                    'ajax_url' => admin_url('admin-ajax.php'),
                    'nonce' => wp_create_nonce('aysa_nonce'),
                    'project_id' => $agent8v3_project_id,
                    'site_host' => (string) wp_parse_url(home_url('/'), PHP_URL_HOST),
                    'qa_enabled' => $this->aysa_agent8v3_qa_enabled(),
                )
            );
        }
        
        // Enqueue Content Strategy script when on content-strategy tab
        if ($page === 'aysa-research' && $tab === 'content-strategy') {
            wp_enqueue_script(
                'aysa-content-strategy',
                AYSA_PLUGIN_URL . 'admin/js/aysa-content-strategy.js',
                array('jquery'),
                $this->version,
                true
            );
            
            // Add nonce for Content Strategy
            wp_add_inline_script(
                'aysa-content-strategy',
                'var aysaContentStrategyNonce = "' . wp_create_nonce('aysa_content_strategy_nonce') . '";',
                'before'
            );
            
            aysa_debug_log('[AYSA] Content Strategy script enqueued');
        }
        
        // Add page-specific parameters and scripts
        if ($page === 'aysa-on-page-optimization') {
            $aysa_actions_version = @filemtime(AYSA_PLUGIN_DIR . 'admin/js/aysa-actions.js') ?: $this->version;

            // Enqueue aysa-actions script for On-Page Optimization page
            wp_enqueue_script(
                'aysa-actions',
                AYSA_PLUGIN_URL . 'admin/js/aysa-actions.js',
                array('jquery'),
                $aysa_actions_version,
                true
            );
            
            // Localize aysa-actions script
            wp_localize_script(
                'aysa-actions',
                'aysaParams',
                array(
                    'ajax_url' => admin_url('admin-ajax.php'),
                    'admin_url' => admin_url(),
                    'nonce' => wp_create_nonce('aysa-ajax-nonce'),
                    'api_base_url' => AYSA_API_BASE_URL,
                    'auth_token' => get_option('aysa_auth_token', ''),
                    'site_url' => get_site_url(),
                    'api_config' => Aysa_API_Config::get_js_config()
                )
            );
            
            // Enqueue SEO history script for On-Page Optimization page
            wp_enqueue_script(
                'aysa-seo-history',
                AYSA_PLUGIN_URL . 'assets/js/aysa-seo-history.js',
                array('jquery'),
                $this->version,
                true
            );
            
            // Localize SEO history script with AJAX data
            wp_localize_script(
                'aysa-seo-history',
                'aysaSeoHistoryAjax',
                array(
                    'ajax_url' => admin_url('admin-ajax.php'),
                    'nonce' => wp_create_nonce('aysa_seo_history_nonce'),
                    'user_id' => get_current_user_id(),
                    'user_name' => wp_get_current_user()->display_name,
                    'site_url' => get_site_url()
                )
            );
            
            $aysa_generator_core_version = @filemtime(AYSA_PLUGIN_DIR . 'assets/js/aysa-seo-generator-core.js') ?: $this->version;

            // Enqueue aysa-seo-generator-core script with aysa-actions dependency
            wp_enqueue_script(
                'aysa-seo-generator-core',
                AYSA_PLUGIN_URL . 'assets/js/aysa-seo-generator-core.js',
                array('jquery', 'aysa-actions'),
                $aysa_generator_core_version,
                true
            );
            
            // Enqueue script for On-Page Optimization page
            $on_page_js_version = $this->version;
            $on_page_js_file = plugin_dir_path(__FILE__) . 'js/aysa-on-page-optimization-v2.js';
            if (file_exists($on_page_js_file)) {
                $on_page_js_version = (string) filemtime($on_page_js_file);
            }
            wp_enqueue_script(
                $this->plugin_name . '-on-page-optimization',
                AYSA_PLUGIN_URL . 'admin/js/aysa-on-page-optimization-v2.js',
                array('jquery'),
                $on_page_js_version,
                true
            );
            
            // Pass specific parameters for on-page optimization scripts
            wp_localize_script(
                $this->plugin_name . '-on-page-optimization',
                'aysa_params',
                array_merge($common_params, array(
                    'wc_product_taxonomy' => 'product_cat',
                    'wc_product_post_type' => 'product'
                ))
            );
        }

        // Ensure WordPress visual editor assets are available for Off-Page article modal
        if ($page === 'aysa-off-page-optimization' && function_exists('wp_enqueue_editor')) {
            wp_enqueue_editor();
        }
        
        // Add parameters for monitoring analytics page
        if ($page === 'aysa-monitoring-analytics') {
            wp_localize_script(
                $this->plugin_name,
                'aysaParams',
                $common_params
            );
        }
        
        // Add parameters for technical optimization page
        if ($page === 'aysa-technical-optimization') {
            // Schema Generator script - DISABLED (using simple modal approach)
            // wp_enqueue_script(
            //     'aysa-schema-generator',
            //     plugin_dir_url(__FILE__) . 'js/aysa-schema-generator.js',
            //     array('jquery', $this->plugin_name),
            //     $this->version,
            //     true
            // );
            
            // Schema Frontend Generator - new implementation
            wp_enqueue_script(
                'aysa-schema-frontend',
                plugin_dir_url(__FILE__) . 'js/aysa-schema-frontend.js',
                array('jquery'),
                $this->version,
                true
            );
            
            // Get company information from SEO profile
            // Try multiple option keys for company data
            $company_name = get_option('aysa_company_name', 
                               get_option('company_name', 
                                 get_bloginfo('name')));
            $company_description = get_option('aysa_company_description', 
                                     get_option('company_description',
                                       get_bloginfo('description')));
            
            // Localize script for AJAX
            wp_localize_script(
                'aysa-schema-frontend',
                'aysaSchemaAjax',
                array(
                    'ajaxurl' => admin_url('admin-ajax.php'),
                    'nonce' => wp_create_nonce('aysa_schema_nonce'),
                    'siteUrl' => home_url(),
                    'siteName' => $company_name,
                    'companyName' => $company_name,
                    'companyDescription' => $company_description,
                    'logoUrl' => get_site_icon_url() ? get_site_icon_url() : (home_url() . '/wp-content/uploads/logo.png')
                )
            );
            
            // Schema CSS removed - using inline styles for simple modal
            
            // Internal Linking script for technical optimization
            wp_enqueue_script(
                'aysa-internal-linking',
                plugin_dir_url(__FILE__) . 'js/aysa-internal-linking.js',
                array('jquery', $this->plugin_name),
                file_exists(plugin_dir_path(__FILE__) . 'js/aysa-internal-linking.js') ? filemtime(plugin_dir_path(__FILE__) . 'js/aysa-internal-linking.js') : $this->version,
                true
            );
            
            // Add nonce for internal linking AJAX calls
            wp_localize_script(
                'aysa-internal-linking',
                'aysaInternalLinking',
                array(
                    'ajaxUrl' => admin_url('admin-ajax.php'),
                    'nonce' => wp_create_nonce('aysa_internal_linking_nonce')
                )
            );
            
            wp_localize_script(
                $this->plugin_name,
                'aysaParams',
                $common_params
            );
        }
        
        // Add parameters for settings page, automation page, and adjustment improvement page (for automation tab)
        if ($page === 'aysa-settings' || $page === 'aysa-automation' || $page === 'aysa-adjustment-improvement') {
            // Enqueue tab functionality for settings page
            if ($page === 'aysa-settings') {
                wp_enqueue_script(
                    'aysa-tabs',
                    plugin_dir_url(__FILE__) . 'js/aysa-tabs.js',
                    array('jquery'),
                    $this->version,
                    true
                );
            }
            
            // Enqueue automation controls script for settings and automation pages
            wp_enqueue_script(
                'aysa-automation-controls',
                plugin_dir_url(__FILE__) . 'js/aysa-automation-controls.js',
                array('jquery', $this->plugin_name),
                $this->version,
                false
            );
            
            // Internal Linking script for technical optimization
            wp_enqueue_script(
                'aysa-internal-linking',
                plugin_dir_url(__FILE__) . 'js/aysa-internal-linking.js',
                array('jquery', $this->plugin_name),
                file_exists(plugin_dir_path(__FILE__) . 'js/aysa-internal-linking.js') ? filemtime(plugin_dir_path(__FILE__) . 'js/aysa-internal-linking.js') : $this->version,
                true
            );
            
            wp_localize_script(
                $this->plugin_name,
                'aysaParams',
                $common_params
            );
            
            wp_localize_script(
                'aysa-automation-controls',
                'aysaAutomation',
                array(
                    'nonce' => wp_create_nonce('aysa_automation_nonce'),
                    'ajax_url' => admin_url('admin-ajax.php')
                )
            );
        }
        
        // Add parameters for all other AYSA pages
        if ($page === 'aysa' || strpos($page, 'aysa-') === 0) {
            // Some third-party admin pointers (e.g. Elementor) can run on AYSA pages.
            // Ensure the WP pointer script is available to avoid runtime errors.
            wp_enqueue_script('wp-pointer');
            wp_enqueue_style('wp-pointer');

            wp_localize_script(
                $this->plugin_name,
                'aysaParams',
                $common_params
            );
        }

        // Script to hide the 'Welcome to aysa' submenu item
        $custom_script = "
            document.addEventListener('DOMContentLoaded', function() {
                var menuLinks = document.querySelectorAll('#adminmenu a[href=\"admin.php?page=aysa-welcome\"]');
                menuLinks.forEach(function(link) {
                    var parentLi = link.closest('li');
                    if (parentLi) {
                        parentLi.style.display = 'none';
                    }
                });
            });
        ";
        wp_add_inline_script($this->plugin_name, $custom_script);
    }

    public function add_plugin_admin_menu() {
        $key_validation = null;
        $has_valid_access_key = $this->aysa_is_access_key_valid_globally($key_validation);

        $setup_completed = $this->aysa_is_setup_profile_completed_effectively();
        $apprentice_mode = $this->aysa_is_apprentice_mode_enabled();
        $show_novice_only_menu = $apprentice_mode && $setup_completed;

        $root_callback = array($this, 'display_agent8_page');

        add_menu_page(
            'AYSA AI SEO - Apprentice',
            'AYSA AI SEO',
            $this->aysa_workspace_capability(),
            'aysa',
            $root_callback,
            'dashicons-speedometer',
            6
        );

        add_submenu_page(
            'aysa',
            'Agent 8',
            'Agent 8',
            $this->aysa_workspace_capability(),
            'aysa-agent8',
            array($this, 'display_agent8_page')
        );

        add_submenu_page(
            'aysa',
            'Agent 9',
            'Agent 9',
            $this->aysa_workspace_capability(),
            'aysa-agent9',
            array($this, 'display_agent9_page')
        );

        add_submenu_page(
            'aysa',
            'Agent8 V3 Lab',
            'Agent8 V3 Lab',
            $this->aysa_workspace_capability(),
            'aysa-agent8-v3-lab',
            array($this, 'display_agent8_v3_lab_page')
        );

        // Keep critical direct routes registered even when access-key validation is temporarily unavailable.
        add_submenu_page(
            null,
            'Technical Optimization',
            'Technical Optimization',
            $this->aysa_workspace_capability(),
            'aysa-technical-optimization',
            array($this, 'display_technical_optimization_page')
        );

        if (!$has_valid_access_key) {
            add_submenu_page(
                null,
                'Activation Key',
                'Activation Key',
                $this->aysa_workspace_capability(),
                'aysa-activation-key',
                array($this, 'display_activation_page')
            );
            remove_submenu_page('aysa', 'aysa');
            return;
        }

        add_submenu_page(
            'aysa',
            'Legacy',
            'Legacy',
            $this->aysa_workspace_capability(),
            'aysa-legacy',
            array($this, 'display_legacy_page')
        );

        // Keep direct routes registered, but expose them through Legacy only.
        add_submenu_page(
            null,
            'Setup SEO Profile',
            'Setup SEO Profile',
            $this->aysa_workspace_capability(),
            'aysa-setup-seo-profile',
            array($this, 'display_setup_seo_profile_page')
        );

        // Keep activation route registered for direct access and QA even after a valid key exists.
        add_submenu_page(
            null,
            'Activation Key',
            'Activation Key',
            $this->aysa_workspace_capability(),
            'aysa-activation-key',
            array($this, 'display_activation_page')
        );

        if ($show_novice_only_menu) {
            add_submenu_page(
                null,
                'Activation Key',
                'Activation Key',
                $this->aysa_workspace_capability(),
                'aysa-activation-key',
                array($this, 'display_activation_page')
            );
        } else {
            add_submenu_page(
                null,
                'Dashboard',
                'Dashboard',
                $this->aysa_workspace_capability(),
                'aysa',
                array($this, 'display_dashboard_page')
            );
        }

        add_submenu_page(
            null,
            'Research & Analysis',
            'Research & Analysis',
            $this->aysa_workspace_capability(),
            'aysa-research',
            array($this, 'display_plugin_admin_page')
        );
        
        add_submenu_page(
            null,
            'On-Page Optimization',
            'On-Page Optimization',
            $this->aysa_workspace_capability(),
            'aysa-on-page-optimization',
            array($this, 'display_on_page_optimization_page')
        );
        
        add_submenu_page(
            null,
            'Off-Page Optimization',
            'Off-Page Optimization',
            $this->aysa_workspace_capability(),
            'aysa-off-page-optimization',
            array($this, 'display_off_page_optimization_page')
        );

        add_submenu_page(
            null,
            'Monitoring & Analytics',
            'Monitoring & Analytics',
            $this->aysa_workspace_capability(),
            'aysa-monitoring-analytics',
            array($this, 'display_monitoring_analytics_page')
        );
        
        /*
        add_submenu_page(
            'aysa',
            'Omnichannel SEO',
            '⟲ Omnichannel SEO',
            'manage_options',
            'aysa-omnichannel-seo',
            array($this, 'display_omnichannel_seo_page')
        );
        */
        
        // SEO Profile Setup moved to top of menu

        // Add Welcome to Aysa page - Keep it registered for direct access
        add_submenu_page(
            null, // Parent slug
            'Welcome to aysa', // Page title
            'Welcome to aysa', // Menu title (will be hidden by CSS/JS)
            $this->aysa_workspace_capability(),
            'aysa-welcome', // Menu slug
            array($this, 'display_welcome_aysa_page') // Function to display page
        );

        add_submenu_page(
            null,
            'Settings',
            'Settings',
            $this->aysa_workspace_capability(),
            'aysa-settings',
            array($this, 'display_settings_page')
        );

        remove_submenu_page('aysa', 'aysa');

        // Keep all plugin areas visible while we stabilize the orchestrator architecture.
        
        // Automation moved to Adjustment & Improvement as a tab
        /*
        add_submenu_page(
            'aysa',
            'Automation',
            'Automation',
            'manage_options',
            'aysa-automation',
            array($this, 'display_automation_page')
        );
        */


    }

    private function aysa_hide_submenus_until_setup_complete() {
        add_action('admin_head', function () {
            echo '<style>
                #adminmenu .wp-submenu a[href*="page=aysa-research"],
                #adminmenu .wp-submenu a[href*="page=aysa-technical-optimization"],
                #adminmenu .wp-submenu a[href*="page=aysa-on-page-optimization"],
                #adminmenu .wp-submenu a[href*="page=aysa-off-page-optimization"],
                #adminmenu .wp-submenu a[href*="page=aysa-monitoring-analytics"],
                #adminmenu .wp-submenu a[href*="page=aysa-adjustment-improvement"],
                #adminmenu .wp-submenu a[href*="page=aysa-settings"],
                #adminmenu .wp-submenu a[href*="page=aysa-welcome"],
                #adminmenu .wp-submenu a[href="admin.php?page=aysa"] {
                    display: none !important;
                }
            </style>';
        }, 1000);
    }

    private function aysa_is_apprentice_mode_enabled() {
        $raw = get_option('aysa_apprentice_mode', '1');
        return !in_array((string) $raw, array('0', 'false', 'off'), true);
    }

    /**
     * Display the dashboard page with login
     */
    public function display_dashboard_page() {
        $auth_manager = Aysa_Auth_Manager::get_instance(); $auth_status = $auth_manager->is_authenticated(); $is_logged_in = $auth_status['authenticated'];
        if (!$is_logged_in) {
            $aysa_section_title = 'Dashboard';
            include AYSA_PLUGIN_DIR . 'admin/partials/aysa-login-required.php';
            return;
        }
        if ($this->aysa_is_apprentice_mode_enabled() && $this->aysa_is_setup_profile_completed_effectively()) {
            $this->display_agent8_page();
            return;
        }
        $user_id = $auth_status['aysa_user_id'];
        $aysa_user_id = $user_id;
        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-dashboard.php';
    }

    // Check authentication before displaying any admin page
    private function check_authentication($unused = true) {
        $auth_manager = Aysa_Auth_Manager::get_instance(); $auth_status = $auth_manager->is_authenticated(); $is_logged_in = $auth_status['authenticated'];
        if (!$is_logged_in) {
            // Nu mai includ direct pagina de login aici
            return false;
        }
        $validation = null;
        if (!$this->aysa_is_access_key_valid_globally($validation)) {
            return false;
        }
        return true;
    }

    public function enforce_access_key_for_admin_pages() {
        if (!is_admin() || wp_doing_ajax()) {
            return;
        }
        if (!$this->aysa_current_user_can_workspace()) {
            return;
        }
        $page = isset($_GET['page']) ? sanitize_key((string) $_GET['page']) : '';
        if ($page === '' || strpos($page, 'aysa') !== 0) {
            return;
        }

        $validation = null;
        if ($this->aysa_is_access_key_valid_globally($validation)) {
            return;
        }

        // Allow the conversational onboarding/agent pages without a valid key.
        if ($page === 'aysa-activation-key' || $page === 'aysa-ai-agent' || $page === 'aysa-agent8' || $page === 'aysa-agent9' || $page === 'aysa-agent8-v3-lab' || $page === 'aysa') {
            return;
        }

        $msg = is_array($validation) && !empty($validation['message'])
            ? (string) $validation['message']
            : 'Access key is required.';
        wp_die(
            '<h1>AYSA Access Blocked</h1><p>' . esc_html($msg) . '</p><p>Configure a valid access key first.</p>',
            'AYSA Access Blocked',
            array('response' => 403)
        );
    }

    public function redirect_deprecated_agent_pages() {
        if (!is_admin() || wp_doing_ajax()) {
            return;
        }
        if (!$this->aysa_current_user_can_workspace()) {
            return;
        }

        $page = isset($_GET['page']) ? sanitize_key((string) $_GET['page']) : '';
        if (!in_array($page, array('aysa-ai-agent', 'aysa-agent7'), true)) {
            return;
        }

        wp_safe_redirect(admin_url('admin.php?page=aysa-agent8'));
        exit;
    }

    public function enforce_setup_gate_for_admin_pages() {
        if (!is_admin() || wp_doing_ajax()) {
            return;
        }
        if (!$this->aysa_current_user_can_workspace()) {
            return;
        }

        $page = isset($_GET['page']) ? sanitize_key((string) $_GET['page']) : '';
        if ($page === '' || strpos($page, 'aysa') !== 0) {
            return;
        }

        $validation = null;
        if (!$this->aysa_is_access_key_valid_globally($validation)) {
            // Access-key gate handles this case and will block with 403.
            return;
        }

        if ($page === 'aysa-activation-key' || $page === 'aysa-agent8' || $page === 'aysa-agent9') {
            return;
        }

        $setup_reason = array();
        $setup_completed = $this->aysa_is_setup_profile_completed_effectively($setup_reason);
        $apprentice_mode = $this->aysa_is_apprentice_mode_enabled();
        if (!$setup_completed && $apprentice_mode) {
            if ($page === 'aysa') {
                wp_safe_redirect(admin_url('admin.php?page=aysa-adjustment-improvement'));
                exit;
            }
            return;
        }
        if (!$setup_completed && $page !== 'aysa-setup-seo-profile') {
            $reason_code = isset($setup_reason['code']) ? sanitize_key((string) $setup_reason['code']) : '';
            if ($reason_code === 'google_not_connected') {
                $this->setup_gate_notice = array(
                    'type' => 'warning',
                    'message' => isset($setup_reason['message']) ? (string) $setup_reason['message'] : __('Google connection is not completed. Some features may be limited until it is connected.', 'aysa'),
                    'step' => 'integrations',
                );
                return;
            }
            $target_step = isset($setup_reason['step']) ? sanitize_key((string) $setup_reason['step']) : 'general';
            if ($target_step === '') {
                $target_step = 'general';
            }
            $target_url = add_query_arg(
                array(
                    'page' => 'aysa-setup-seo-profile',
                    'step' => $target_step,
                    'aysa_setup_error' => isset($setup_reason['code']) ? sanitize_key((string) $setup_reason['code']) : 'setup_incomplete',
                ),
                admin_url('admin.php')
            );
            wp_safe_redirect($target_url);
            exit;
        }

        if ($setup_completed && $page === 'aysa') {
            $target = $apprentice_mode
                ? admin_url('admin.php?page=aysa-adjustment-improvement')
                : $this->aysa_last_research_tab_url();
            wp_safe_redirect($target);
            exit;
        }
    }

    public function render_setup_gate_notice() {
        if (!is_admin() || !$this->aysa_current_user_can_workspace()) {
            return;
        }
        $page = isset($_GET['page']) ? sanitize_key((string) $_GET['page']) : '';
        if ($page === '' || strpos($page, 'aysa') !== 0) {
            return;
        }

        if (empty($this->setup_gate_notice) || !is_array($this->setup_gate_notice)) {
            $setup_reason = array();
            $setup_completed = $this->aysa_is_setup_profile_completed_effectively($setup_reason);
            $reason_code = isset($setup_reason['code']) ? sanitize_key((string) $setup_reason['code']) : '';
            if ($setup_completed || $reason_code !== 'google_not_connected') {
                return;
            }
            $this->setup_gate_notice = array(
                'type' => 'warning',
                'message' => isset($setup_reason['message']) ? (string) $setup_reason['message'] : __('Google connection is not completed. Some features may be limited until it is connected.', 'aysa'),
                'step' => 'integrations',
            );
        }

        $type = isset($this->setup_gate_notice['type']) ? sanitize_html_class((string) $this->setup_gate_notice['type']) : 'warning';
        $message = isset($this->setup_gate_notice['message']) ? (string) $this->setup_gate_notice['message'] : '';
        $step = isset($this->setup_gate_notice['step']) ? sanitize_key((string) $this->setup_gate_notice['step']) : 'integrations';
        $setup_url = add_query_arg(
            array(
                'page' => 'aysa-setup-seo-profile',
                'step' => $step,
            ),
            admin_url('admin.php')
        );
        if ($message === '') {
            return;
        }
        echo '<div class="notice notice-' . esc_attr($type) . ' is-dismissible"><p><strong>' . esc_html__('AYSA Setup:', 'aysa') . '</strong> ' . esc_html($message) . ' <a href="' . esc_url($setup_url) . '">' . esc_html__('Open Integrations', 'aysa') . '</a>.</p></div>';
    }

    public function render_notifications_notice() {
        if (!$this->is_aysa_admin_request()) {
            return;
        }

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

        $notice = $this->get_cached_notification_notice();
        if (!is_array($notice) || empty($notice['title'])) {
            return;
        }

        $type = isset($notice['severity']) ? sanitize_html_class((string) $notice['severity']) : 'info';
        $type_class = 'notice-info';
        if ($type === 'warning') {
            $type_class = 'notice-warning';
        } elseif ($type === 'error' || $type === 'critical') {
            $type_class = 'notice-error';
        } elseif ($type === 'success') {
            $type_class = 'notice-success';
        }

        $title = esc_html((string) $notice['title']);
        $body = esc_html((string) ($notice['body'] ?? ''));
        $action_url = !empty($notice['action_url']) ? esc_url((string) $notice['action_url']) : admin_url('admin.php?page=aysa-settings&tab=notifications');
        ?>
        <div class="notice <?php echo esc_attr($type_class); ?>">
            <p><strong><?php echo $title; ?></strong></p>
            <?php if ($body !== '') : ?>
                <p><?php echo $body; ?></p>
            <?php endif; ?>
            <p><a class="button button-secondary" href="<?php echo $action_url; ?>">Open AYSA notifications</a></p>
        </div>
        <?php
    }

    private function get_cached_notification_notice() {
        $current_user_id = get_current_user_id();
        if ($current_user_id <= 0) {
            return null;
        }

        $cache_key = 'aysa_notification_notice_' . $current_user_id;
        $cached = get_transient($cache_key);
        if ($cached !== false) {
            return is_array($cached) ? $cached : null;
        }

        $result = $this->make_app_aysa_notifications_request('/bridge/plugin-notifications');
        $notice = null;
        if (!empty($result['ok']) && !empty($result['notifications']) && is_array($result['notifications'])) {
            foreach ($result['notifications'] as $notification) {
                if (!is_array($notification)) {
                    continue;
                }

                $type = isset($notification['type']) ? (string) $notification['type'] : '';
                if (!in_array($type, array('credits_low', 'subscription_expiring'), true)) {
                    continue;
                }

                $notice = array(
                    'title' => (string) ($notification['title'] ?? 'AYSA update'),
                    'body' => (string) ($notification['body'] ?? ''),
                    'severity' => (string) ($notification['severity'] ?? 'warning'),
                    'action_url' => (string) ($notification['action_url'] ?? ''),
                );
                break;
            }
        }

        set_transient($cache_key, $notice ?: array(), 120);

        return $notice;
    }

    private function clear_cached_notification_notice() {
        $current_user_id = get_current_user_id();
        if ($current_user_id > 0) {
            delete_transient('aysa_notification_notice_' . $current_user_id);
        }
    }

    public function enforce_access_key_for_aysa_ajax() {
        if (!wp_doing_ajax()) {
            return;
        }
        $action = isset($_REQUEST['action']) ? sanitize_key((string) $_REQUEST['action']) : '';
        if ($action === '' || strpos($action, 'aysa_') !== 0) {
            return;
        }

        if ($action === 'aysa_agent8_conversation') {
            $mode = isset($_REQUEST['mode']) ? sanitize_key((string) $_REQUEST['mode']) : 'bootstrap';
            if (in_array($mode, array('bootstrap', 'message'), true)) {
                return;
            }
        }
        if (strpos($action, 'aysa_agent8v3_') === 0) {
            return;
        }

        $allow = array(
            'aysa_access_key_get_test',
            'aysa_access_key_verify',
            'aysa_activation_conversation',
            'aysa_activation_reset_test',
            'aysa_apprentice_reset_test',
            'aysa_agent8_factory_reset_dry_run',
            'aysa_agent8_factory_reset_execute',
            'aysa_agent8_notifications_fetch',
            'aysa_keyword_research_projection_sync',
            'aysa_technical_site_audit_projection_sync',
            'aysa_technical_pagespeed_projection_sync',
            'aysa_technical_schema_projection_sync',
            'aysa_technical_redirects_projection_sync',
            'aysa_technical_sitemap_robots_projection_sync',
            'aysa_get_auth_status',
            'aysa_notifications_fetch',
            'aysa_login',
            'aysa_verify_secret_key',
            'aysa_get_secret_key',
            'aysa_logout',
        );
        if (in_array($action, $allow, true)) {
            return;
        }

        $validation = null;
        if ($this->aysa_is_access_key_valid_globally($validation)) {
            return;
        }

        $msg = is_array($validation) && !empty($validation['message'])
            ? (string) $validation['message']
            : 'Access key is required.';
        wp_send_json_error(array(
            'message' => $msg,
            'code' => 'access_key_blocked',
            'validation' => $validation,
        ), 403);
    }

    public function display_plugin_admin_page() {
        
        if (!$this->check_authentication()) {
            $aysa_section_title = 'Research & Analysis';
            include AYSA_PLUGIN_DIR . 'admin/partials/aysa-login-required.php';
            return;
        }
        echo '<div class="aysa-page-container">';

        $requested_tab = isset($_GET['tab']) ? sanitize_text_field($_GET['tab']) : '';
        $current_tab = $requested_tab !== '' ? $requested_tab : $this->aysa_get_last_research_tab();
        $current_tab = $this->aysa_normalize_research_tab($current_tab);
        $this->aysa_save_last_research_tab($current_tab);
        switch ($current_tab) {
            case 'keyword-discovery':
                include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-keyword-discovery.php';
                break;
            case 'competitor-gap':
                include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-competitor-gap.php';
                break;
            case 'competitor-identification':
                include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-competitor-identification.php';
                break;
            case 'competitor-backlinks':
                include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-competitor-backlinks.php';
                break;
            case 'missing-pages':
                include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-missing-pages-v2.php';
                break;
            case 'content-strategy':
                include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-content-strategy.php';
                break;
            case 'sitemap-competition':
                include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-sitemap-competition.php';
                break;
            case 'google-local-seo':
                // Tab removed
                include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-admin-display.php';
                break;
            default:
                include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-admin-display.php';
                break;
        }
        echo '</div>'; // Close the container
    }
    
    public function display_technical_optimization_page() {
        if (!$this->check_authentication()) {
            $aysa_section_title = 'Technical Optimization';
            include AYSA_PLUGIN_DIR . 'admin/partials/aysa-login-required.php';
            return;
        }
        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-technical-optimization.php';
    }
    
    public function display_on_page_optimization_page() {
        if (!$this->check_authentication()) {
            $aysa_section_title = 'On-Page Optimization';
            include AYSA_PLUGIN_DIR . 'admin/partials/aysa-login-required.php';
            return;
        }

        $aysa_section_title = 'On-Page Optimization';
        $aysa_user_id = get_current_user_id();
        $aysa_user_credits = get_option('aysa_user_credits', 0);

        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-on-page-optimization-v2.php';
    }    
    public function display_off_page_optimization_page() {
        if (!$this->check_authentication()) {
            $aysa_section_title = 'Off-Page Optimization';
            include AYSA_PLUGIN_DIR . 'admin/partials/aysa-login-required.php';
            return;
        }
        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-off-page-optimization.php';
    }

    private function aysa_conversation_async_supported_tasks() {
        return array('setup', 'adjustments', 'activation', 'monitoring', 'agent8');
    }

    private function aysa_project_jobs_state_key($project_id) {
        return 'aysa_project_jobs_state_' . intval($project_id);
    }

    private function aysa_project_jobs_get_state($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array(
                'project_id' => 0,
                'active_job_ids' => array(),
                'queue' => array(),
                'jobs' => array(),
                'recent' => array(),
                'updated_at' => 0,
            );
        }

        $raw = get_option($this->aysa_project_jobs_state_key($project_id), array());
        $state = is_array($raw) ? $raw : array();
        $state['project_id'] = $project_id;
        $state['active_job_ids'] = isset($state['active_job_ids']) && is_array($state['active_job_ids']) ? array_values($state['active_job_ids']) : array();
        $state['queue'] = isset($state['queue']) && is_array($state['queue']) ? array_values($state['queue']) : array();
        $state['jobs'] = isset($state['jobs']) && is_array($state['jobs']) ? $state['jobs'] : array();
        $state['recent'] = isset($state['recent']) && is_array($state['recent']) ? array_values($state['recent']) : array();
        $state['updated_at'] = intval($state['updated_at'] ?? 0);

        foreach ($state['active_job_ids'] as $idx => $job_id) {
            if (!isset($state['jobs'][$job_id]) || !is_array($state['jobs'][$job_id])) {
                unset($state['active_job_ids'][$idx]);
                continue;
            }
            $job_status = (string) ($state['jobs'][$job_id]['status'] ?? '');
            if (!in_array($job_status, array('running', 'delegated'), true)) {
                unset($state['active_job_ids'][$idx]);
            }
        }
        $state['active_job_ids'] = array_values(array_unique($state['active_job_ids']));

        foreach ($state['queue'] as $idx => $job_id) {
            if (!isset($state['jobs'][$job_id]) || !is_array($state['jobs'][$job_id])) {
                unset($state['queue'][$idx]);
                continue;
            }
            $job_status = (string) ($state['jobs'][$job_id]['status'] ?? '');
            if ($job_status !== 'queued') {
                unset($state['queue'][$idx]);
            }
        }
        $state['queue'] = array_values(array_unique($state['queue']));

        foreach ($state['recent'] as $idx => $job_id) {
            if (!isset($state['jobs'][$job_id]) || !is_array($state['jobs'][$job_id])) {
                unset($state['recent'][$idx]);
            }
        }
        $state['recent'] = array_values(array_unique($state['recent']));

        return $state;
    }

    private function aysa_project_jobs_save_state($project_id, $state) {
        $project_id = intval($project_id);
        if ($project_id <= 0 || !is_array($state)) {
            return;
        }
        $state['project_id'] = $project_id;
        $state['updated_at'] = time();
        update_option($this->aysa_project_jobs_state_key($project_id), $state, false);
    }

    private function aysa_project_jobs_resource_groups_intersect($left, $right) {
        $left = array_values(array_unique(array_filter(array_map('strval', is_array($left) ? $left : array()))));
        $right = array_values(array_unique(array_filter(array_map('strval', is_array($right) ? $right : array()))));
        if ($left === array() || $right === array()) {
            return false;
        }
        return array_values(array_intersect($left, $right)) !== array();
    }

    private function aysa_project_jobs_trim_recent($recent, $limit = 10) {
        $recent = array_values(array_unique(array_filter(is_array($recent) ? $recent : array(), function($job_id) {
            return is_string($job_id) && $job_id !== '';
        })));
        if (count($recent) > $limit) {
            $recent = array_slice($recent, 0, $limit);
        }
        return $recent;
    }

    private function aysa_project_jobs_metadata($module, $job_type, $overrides = array()) {
        $module = sanitize_key((string) $module);
        $job_type = sanitize_key((string) $job_type);
        $catalog = array(
            'research' => array(
                'keyword_discovery_refresh' => array(
                    'resource_groups' => array('research_context', 'gsc_fetch'),
                    'execution_family' => 'keyword_discovery',
                    'priority' => 60,
                    'billing_owner' => 'existing_executor',
                ),
                'competitor_gap' => array(
                    'resource_groups' => array('research_context', 'competitor_data'),
                    'execution_family' => 'research_async',
                    'priority' => 50,
                    'billing_owner' => 'existing_executor',
                ),
                'competitor_identification' => array(
                    'resource_groups' => array('research_context', 'competitor_data'),
                    'execution_family' => 'research_async',
                    'priority' => 45,
                    'billing_owner' => 'existing_executor',
                ),
                'keyword_strategy_generate' => array(
                    'resource_groups' => array('research_context', 'content_generation'),
                    'execution_family' => 'research_async',
                    'priority' => 55,
                    'billing_owner' => 'existing_executor',
                ),
                'sitemap_competition_scan' => array(
                    'resource_groups' => array('research_context', 'site_crawl', 'competitor_data'),
                    'execution_family' => 'sitemap_competition',
                    'priority' => 40,
                    'billing_owner' => 'existing_executor',
                ),
                'content_strategy_generation' => array(
                    'resource_groups' => array('research_context', 'content_generation'),
                    'execution_family' => 'content_strategy',
                    'priority' => 35,
                    'billing_owner' => 'existing_executor',
                ),
            ),
            'monitoring' => array(
                'rank_refresh' => array(
                    'resource_groups' => array('monitoring_refresh'),
                    'execution_family' => 'monitoring_rank_refresh',
                    'priority' => 30,
                    'billing_owner' => 'existing_executor',
                ),
                'ai_refresh' => array(
                    'resource_groups' => array('monitoring_refresh'),
                    'execution_family' => 'monitoring_ai_async',
                    'priority' => 30,
                    'billing_owner' => 'existing_executor',
                ),
                'keywords_overview_refresh' => array(
                    'resource_groups' => array('monitoring_refresh'),
                    'execution_family' => 'monitoring_keywords_async',
                    'priority' => 30,
                    'billing_owner' => 'existing_executor',
                ),
            ),
            'technical' => array(
                'site_audit' => array(
                    'resource_groups' => array('site_crawl', 'technical_snapshot'),
                    'execution_family' => 'technical_site_audit',
                    'priority' => 25,
                    'billing_owner' => 'existing_executor',
                ),
                'pagespeed_report' => array(
                    'resource_groups' => array('technical_snapshot'),
                    'execution_family' => 'pagespeed_report',
                    'priority' => 25,
                    'billing_owner' => 'existing_executor',
                ),
                'schema_job' => array(
                    'resource_groups' => array('technical_snapshot'),
                    'execution_family' => 'technical_schema',
                    'priority' => 25,
                    'billing_owner' => 'existing_executor',
                ),
            ),
            'onpage' => array(
                'meta_generation' => array(
                    'resource_groups' => array('onpage_write', 'content_generation'),
                    'execution_family' => 'metabox_async',
                    'priority' => 25,
                    'billing_owner' => 'existing_executor',
                ),
                'rewrite_generation' => array(
                    'resource_groups' => array('onpage_write', 'content_generation'),
                    'execution_family' => 'metabox_async',
                    'priority' => 25,
                    'billing_owner' => 'existing_executor',
                ),
                'page_update_apply' => array(
                    'resource_groups' => array('onpage_write'),
                    'execution_family' => 'onpage_write',
                    'priority' => 25,
                    'billing_owner' => 'existing_executor',
                ),
            ),
            'offpage' => array(
                'gbp_refresh' => array(
                    'resource_groups' => array('offpage_gbp'),
                    'execution_family' => 'gbp_jobs',
                    'priority' => 25,
                    'billing_owner' => 'existing_executor',
                ),
                'exchange_action' => array(
                    'resource_groups' => array('offpage_exchange'),
                    'execution_family' => 'offpage_async',
                    'priority' => 25,
                    'billing_owner' => 'existing_executor',
                ),
                'draft_approve' => array(
                    'resource_groups' => array('offpage_exchange'),
                    'execution_family' => 'offpage_async',
                    'priority' => 25,
                    'billing_owner' => 'existing_executor',
                ),
            ),
            'conversation' => array(
                'conversation_async' => array(
                    'resource_groups' => array('conversation_state'),
                    'execution_family' => 'conversation_async',
                    'priority' => 20,
                    'billing_owner' => 'existing_executor',
                ),
            ),
        );
        $base = $catalog[$module][$job_type] ?? array(
            'resource_groups' => array($module !== '' ? $module : 'generic'),
            'execution_family' => 'custom',
            'priority' => 10,
            'billing_owner' => 'existing_executor',
        );
        if (is_array($overrides) && $overrides !== array()) {
            $base = array_merge($base, $overrides);
        }
        $base['resource_groups'] = array_values(array_unique(array_filter(array_map('sanitize_key', (array) ($base['resource_groups'] ?? array())))));
        $base['execution_family'] = sanitize_key((string) ($base['execution_family'] ?? 'custom'));
        $base['priority'] = intval($base['priority'] ?? 10);
        $base['billing_owner'] = sanitize_key((string) ($base['billing_owner'] ?? 'existing_executor'));
        return $base;
    }

    private function aysa_project_jobs_make_worker_ref($execution_family, $job_type, $run_id) {
        $execution_family = sanitize_key((string) $execution_family);
        $job_type = sanitize_key((string) $job_type);
        $run_id = trim((string) $run_id);
        if ($execution_family === '' || $job_type === '' || $run_id === '') {
            return '';
        }
        return $execution_family . ':' . $job_type . ':' . $run_id;
    }

    private function aysa_project_jobs_build_envelope($project_id, $module, $job_type, array $metadata = array(), array $payload = array()) {
        $project_id = intval($project_id);
        $module = sanitize_key((string) $module);
        $job_type = sanitize_key((string) $job_type);
        $resolved = $this->aysa_project_jobs_metadata($module, $job_type, $metadata);
        $dedupe_key = trim((string) ($metadata['dedupe_key'] ?? ''));
        if ($dedupe_key === '') {
            $dedupe_key = md5(wp_json_encode(array(
                'module' => $module,
                'job_type' => $job_type,
                'payload' => $payload,
            )));
        }
        return array(
            'job_id' => uniqid('pjob_', true),
            'project_id' => $project_id,
            'module' => $module,
            'job_type' => $job_type,
            'resource_groups' => $resolved['resource_groups'],
            'execution_family' => (string) $resolved['execution_family'],
            'status' => 'queued',
            'priority' => intval($resolved['priority']),
            'dedupe_key' => $dedupe_key,
            'depends_on_job_ids' => array_values(array_unique(array_filter((array) ($metadata['depends_on_job_ids'] ?? array())))),
            'billing_owner' => (string) $resolved['billing_owner'],
            'input_summary' => isset($metadata['input_summary']) && is_array($metadata['input_summary']) ? $metadata['input_summary'] : array(),
            'worker_ref' => '',
            'worker_status' => '',
            'result_summary' => null,
            'result_payload' => null,
            'error_summary' => '',
            'blocked_by' => array(),
            'executor_payload' => $payload,
            'created_at' => time(),
            'started_at' => 0,
            'updated_at' => time(),
            'finished_at' => 0,
        );
    }

    private function aysa_project_jobs_find_duplicate_job(array $state, $module, $job_type, $dedupe_key) {
        foreach ((array) ($state['jobs'] ?? array()) as $job_id => $job) {
            if (!is_array($job)) {
                continue;
            }
            if ((string) ($job['module'] ?? '') !== (string) $module || (string) ($job['job_type'] ?? '') !== (string) $job_type) {
                continue;
            }
            if ((string) ($job['dedupe_key'] ?? '') !== (string) $dedupe_key) {
                continue;
            }
            $status = (string) ($job['status'] ?? '');
            if (in_array($status, array('queued', 'running', 'delegated'), true)) {
                return $job;
            }
        }
        return null;
    }

    private function aysa_project_jobs_get_blocking_job_ids(array $state, array $job) {
        $blocking = array();
        foreach ((array) ($state['active_job_ids'] ?? array()) as $active_job_id) {
            $active = $state['jobs'][$active_job_id] ?? null;
            if (!is_array($active)) {
                continue;
            }
            if (!$this->aysa_project_jobs_resource_groups_intersect($job['resource_groups'] ?? array(), $active['resource_groups'] ?? array())) {
                continue;
            }
            $blocking[] = (string) $active_job_id;
        }
        return array_values(array_unique($blocking));
    }

    private function aysa_project_jobs_start_enqueued_job(&$state, &$job) {
        $start = $this->aysa_project_jobs_delegate_start($job);
        if (is_wp_error($start)) {
            $job['status'] = 'failed';
            $job['error_summary'] = $start->get_error_message();
            $job['updated_at'] = time();
            $job['finished_at'] = time();
            return $job;
        }

        $job['started_at'] = intval($start['started_at'] ?? time());
        $job['updated_at'] = time();
        $job['worker_ref'] = (string) ($start['worker_ref'] ?? '');
        $job['worker_status'] = (string) ($start['worker_status'] ?? '');
        $job['result_summary'] = $start['result_summary'] ?? null;
        $job['result_payload'] = $start['result'] ?? null;
        $job['error_summary'] = (string) ($start['error_summary'] ?? '');
        $job['blocked_by'] = array();

        $resolved_status = sanitize_key((string) ($start['status'] ?? 'running'));
        if ($resolved_status === 'completed') {
            $job['status'] = 'completed';
            $job['finished_at'] = time();
            $state['recent'] = $this->aysa_project_jobs_trim_recent(array_merge(array($job['job_id']), (array) ($state['recent'] ?? array())));
        } elseif ($resolved_status === 'failed') {
            $job['status'] = 'failed';
            $job['finished_at'] = time();
            $state['recent'] = $this->aysa_project_jobs_trim_recent(array_merge(array($job['job_id']), (array) ($state['recent'] ?? array())));
        } else {
            $job['status'] = 'running';
            if (!in_array($job['job_id'], $state['active_job_ids'], true)) {
                $state['active_job_ids'][] = $job['job_id'];
            }
        }

        return $job;
    }

    private function aysa_project_jobs_delegate_start(array $job) {
        $project_id = intval($job['project_id'] ?? 0);
        $job_type = (string) ($job['job_type'] ?? '');
        $execution_family = (string) ($job['execution_family'] ?? '');
        $payload = isset($job['executor_payload']) && is_array($job['executor_payload']) ? $job['executor_payload'] : array();

        if ($project_id <= 0 || $job_type === '' || $execution_family === '') {
            return new WP_Error('invalid_project_job', 'Invalid project job envelope.');
        }

        switch ($execution_family) {
            case 'keyword_discovery':
                $apply_to_wp = !empty($payload['apply_to_wp']);
                $refresh_kind = (string) ($payload['refresh_kind'] ?? 'gsc_auto');
                $start = $this->aysa_keyword_discovery_start_background_refresh($project_id, $apply_to_wp, $refresh_kind);
                if (is_wp_error($start)) {
                    return $start;
                }
                $status = isset($start['status']) && is_array($start['status']) ? $start['status'] : array();
                $run_id = (string) ($status['run_id'] ?? '');
                return array(
                    'status' => in_array((string) ($status['status'] ?? ''), array('running', 'queued'), true) ? 'running' : ((string) ($status['status'] ?? '') ?: 'running'),
                    'worker_ref' => $this->aysa_project_jobs_make_worker_ref($execution_family, $job_type, $run_id),
                    'worker_status' => (string) ($status['status'] ?? ''),
                    'result_summary' => array(
                        'already_running' => !empty($start['already_running']),
                        'pending_item' => isset($start['pending_item']) && is_array($start['pending_item']) ? $start['pending_item'] : array(),
                    ),
                    'started_at' => intval($status['started_at'] ?? time()),
                );
            case 'research_async':
                $task = (string) ($payload['task'] ?? $job_type);
                $request = isset($payload['request']) && is_array($payload['request']) ? $payload['request'] : array();
                return $this->aysa_research_async_schedule_request($task, $request);
            case 'sitemap_competition':
                $start = $this->aysa_sitemap_competition_start_queued_scan($payload);
                if (is_wp_error($start)) {
                    return $start;
                }
                return array(
                    'status' => 'running',
                    'worker_ref' => $this->aysa_project_jobs_make_worker_ref($execution_family, $job_type, (string) intval($start['local_run_id'] ?? 0)),
                    'worker_status' => 'queued',
                    'result_summary' => array(
                        'local_run_id' => intval($start['local_run_id'] ?? 0),
                        'queued' => !empty($start['queued']),
                    ),
                    'started_at' => time(),
                );
            case 'monitoring_keywords_async':
                $request = isset($payload['request']) && is_array($payload['request']) ? $payload['request'] : array();
                return $this->aysa_monitoring_keywords_async_schedule_request($request);
            case 'monitoring_rank_refresh':
                return $this->aysa_monitoring_rank_refresh_start_queued($payload);
            case 'monitoring_ai_async':
                return $this->aysa_monitoring_ai_refresh_start_queued($payload);
            case 'technical_site_audit':
                return $this->aysa_technical_site_audit_start_queued($payload);
            case 'pagespeed_report':
                return $this->aysa_pagespeed_report_start_queued($payload);
            case 'technical_schema':
                return $this->aysa_technical_schema_start_queued_job($payload);
            default:
                return new WP_Error('unsupported_execution_family', 'Unsupported execution family `' . $execution_family . '`.');
        }
    }

    private function aysa_project_jobs_dispatch_next_eligible(&$state) {
        if (!is_array($state) || empty($state['queue']) || empty($state['jobs'])) {
            return;
        }

        $new_queue = array();
        foreach ((array) $state['queue'] as $job_id) {
            $job = $state['jobs'][$job_id] ?? null;
            if (!is_array($job) || (string) ($job['status'] ?? '') !== 'queued') {
                continue;
            }

            $depends_on = array_values(array_filter((array) ($job['depends_on_job_ids'] ?? array())));
            $dependency_block = false;
            foreach ($depends_on as $dep_job_id) {
                $dep = $state['jobs'][$dep_job_id] ?? null;
                if (!is_array($dep) || !in_array((string) ($dep['status'] ?? ''), array('completed'), true)) {
                    $dependency_block = true;
                    break;
                }
            }
            if ($dependency_block) {
                $new_queue[] = $job_id;
                continue;
            }

            $blocking = $this->aysa_project_jobs_get_blocking_job_ids($state, $job);
            if ($blocking !== array()) {
                $job['blocked_by'] = $blocking;
                $job['updated_at'] = time();
                $state['jobs'][$job_id] = $job;
                $new_queue[] = $job_id;
                continue;
            }

            $job['blocked_by'] = array();
            $this->aysa_project_jobs_start_enqueued_job($state, $job);
            $state['jobs'][$job_id] = $job;
        }

        $state['queue'] = array_values($new_queue);
    }

    private function aysa_project_jobs_finalize($project_id, $worker_ref, $status, $result_summary = null, $error_summary = '') {
        $project_id = intval($project_id);
        $worker_ref = trim((string) $worker_ref);
        $status = sanitize_key((string) $status);
        if ($project_id <= 0 || $worker_ref === '' || !in_array($status, array('completed', 'failed'), true)) {
            return;
        }

        $state = $this->aysa_project_jobs_get_state($project_id);
        if (empty($state['jobs'])) {
            return;
        }

        $target_job_id = '';
        foreach ((array) $state['jobs'] as $job_id => $job) {
            if (!is_array($job)) {
                continue;
            }
            if ((string) ($job['worker_ref'] ?? '') === $worker_ref && in_array((string) ($job['status'] ?? ''), array('running', 'delegated', 'queued'), true)) {
                $target_job_id = (string) $job_id;
                break;
            }
        }
        if ($target_job_id === '') {
            return;
        }

        $job = $state['jobs'][$target_job_id];
        $job['status'] = $status;
        $job['worker_status'] = $status;
        $job['result_summary'] = $result_summary;
        $job['error_summary'] = $status === 'failed' ? (string) $error_summary : '';
        $job['updated_at'] = time();
        $job['finished_at'] = time();
        $state['jobs'][$target_job_id] = $job;

        $state['active_job_ids'] = array_values(array_filter((array) ($state['active_job_ids'] ?? array()), function($job_id) use ($target_job_id) {
            return (string) $job_id !== (string) $target_job_id;
        }));
        $state['recent'] = $this->aysa_project_jobs_trim_recent(array_merge(array($target_job_id), (array) ($state['recent'] ?? array())));
        $this->aysa_project_jobs_dispatch_next_eligible($state);
        $this->aysa_project_jobs_save_state($project_id, $state);
    }

    private function aysa_project_jobs_to_rack(array $state) {
        $jobs = isset($state['jobs']) && is_array($state['jobs']) ? $state['jobs'] : array();
        $summarize = function($job) {
            if (!is_array($job)) {
                return array();
            }
            return array(
                'job_id' => (string) ($job['job_id'] ?? ''),
                'module' => (string) ($job['module'] ?? ''),
                'job_type' => (string) ($job['job_type'] ?? ''),
                'status' => (string) ($job['status'] ?? ''),
                'worker_ref' => (string) ($job['worker_ref'] ?? ''),
                'worker_status' => (string) ($job['worker_status'] ?? ''),
                'resource_groups' => array_values((array) ($job['resource_groups'] ?? array())),
                'blocked_by' => array_values((array) ($job['blocked_by'] ?? array())),
                'created_at' => intval($job['created_at'] ?? 0),
                'started_at' => intval($job['started_at'] ?? 0),
                'updated_at' => intval($job['updated_at'] ?? 0),
                'finished_at' => intval($job['finished_at'] ?? 0),
            );
        };

        $running = array();
        foreach ((array) ($state['active_job_ids'] ?? array()) as $job_id) {
            if (isset($jobs[$job_id]) && is_array($jobs[$job_id])) {
                $running[] = $summarize($jobs[$job_id]);
            }
        }

        $queued = array();
        foreach ((array) ($state['queue'] ?? array()) as $job_id) {
            if (isset($jobs[$job_id]) && is_array($jobs[$job_id])) {
                $queued[] = $summarize($jobs[$job_id]);
            }
        }

        $completed = array();
        $failed = array();
        foreach ((array) ($state['recent'] ?? array()) as $job_id) {
            if (!isset($jobs[$job_id]) || !is_array($jobs[$job_id])) {
                continue;
            }
            $row = $summarize($jobs[$job_id]);
            if (($row['status'] ?? '') === 'completed') {
                $completed[] = $row;
            } elseif (($row['status'] ?? '') === 'failed') {
                $failed[] = $row;
            }
        }

        return array(
            'project_id' => intval($state['project_id'] ?? 0),
            'running_now' => $running,
            'queued_jobs' => $queued,
            'queued_next' => $queued !== array() ? $queued[0] : array(),
            'recently_completed' => $completed,
            'recently_failed' => $failed,
            'updated_at' => intval($state['updated_at'] ?? 0),
        );
    }

    private function aysa_project_jobs_find_latest_job($project_id, $module, $job_type, $statuses = array()) {
        $state = $this->aysa_project_jobs_get_state($project_id);
        $jobs = isset($state['jobs']) && is_array($state['jobs']) ? $state['jobs'] : array();
        $statuses = array_values(array_filter(array_map('strval', is_array($statuses) ? $statuses : array())));
        $best = null;
        foreach ($jobs as $job) {
            if (!is_array($job)) {
                continue;
            }
            if ((string) ($job['module'] ?? '') !== (string) $module || (string) ($job['job_type'] ?? '') !== (string) $job_type) {
                continue;
            }
            if ($statuses !== array() && !in_array((string) ($job['status'] ?? ''), $statuses, true)) {
                continue;
            }
            if ($best === null || intval($job['updated_at'] ?? 0) > intval($best['updated_at'] ?? 0)) {
                $best = $job;
            }
        }
        return is_array($best) ? $best : null;
    }

    private function aysa_project_jobs_enqueue($project_id, $module, $job_type, array $payload = array(), array $metadata = array()) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return new WP_Error('invalid_project_id', 'Invalid project id.');
        }

        $state = $this->aysa_project_jobs_get_state($project_id);
        $job = $this->aysa_project_jobs_build_envelope($project_id, $module, $job_type, $metadata, $payload);
        $duplicate = $this->aysa_project_jobs_find_duplicate_job($state, $job['module'], $job['job_type'], $job['dedupe_key']);
        if (is_array($duplicate)) {
            return array(
                'job' => $duplicate,
                'deduped' => true,
                'rack' => $this->aysa_project_jobs_to_rack($state),
            );
        }

        $blocking = $this->aysa_project_jobs_get_blocking_job_ids($state, $job);
        if ($blocking !== array()) {
            $job['blocked_by'] = $blocking;
            $state['jobs'][$job['job_id']] = $job;
            $state['queue'][] = $job['job_id'];
            $state['queue'] = array_values(array_unique($state['queue']));
            $this->aysa_project_jobs_save_state($project_id, $state);
            return array(
                'job' => $job,
                'deduped' => false,
                'queued' => true,
                'rack' => $this->aysa_project_jobs_to_rack($state),
            );
        }

        $state['jobs'][$job['job_id']] = $job;
        $this->aysa_project_jobs_start_enqueued_job($state, $job);
        $state['jobs'][$job['job_id']] = $job;
        $this->aysa_project_jobs_save_state($project_id, $state);

        return array(
            'job' => $job,
            'deduped' => false,
            'queued' => false,
            'rack' => $this->aysa_project_jobs_to_rack($state),
        );
    }

    public function handle_project_jobs_status() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }
        $state = $this->aysa_project_jobs_get_state($project_id);
        wp_send_json_success($this->aysa_project_jobs_to_rack($state));
    }

    private function aysa_conversation_async_status_key($task, $project_id, $wp_user_id, $site_host) {
        $host_hash = md5(strtolower(trim((string) $site_host)));
        return 'aysa_conversation_async_' . sanitize_key((string) $task) . '_' . intval($project_id) . '_' . intval($wp_user_id) . '_' . $host_hash;
    }

    private function aysa_conversation_async_get_status($task, $project_id, $wp_user_id, $site_host) {
        $raw = get_option($this->aysa_conversation_async_status_key($task, $project_id, $wp_user_id, $site_host), array());
        return is_array($raw) ? $raw : array();
    }

    private function aysa_conversation_async_save_status($task, $project_id, $wp_user_id, $site_host, $status) {
        update_option(
            $this->aysa_conversation_async_status_key($task, $project_id, $wp_user_id, $site_host),
            is_array($status) ? $status : array(),
            false
        );
    }

    private function aysa_conversation_async_output_status($status) {
        if (!is_array($status)) {
            return array(
                'status' => 'idle',
                'result' => null,
                'error' => '',
            );
        }
        $clean = $status;
        unset($clean['_request']);
        return $this->aysa_agent8_bridge_compact_payload_if_needed($clean, array(
            'task' => (string) ($clean['task'] ?? ''),
            'run_id' => (string) ($clean['run_id'] ?? ''),
            'project_id' => intval($clean['project_id'] ?? 0),
            'conversation_id' => '',
            'source' => 'async_status_output',
        ));
    }

    private function aysa_agent8_bridge_log($message, $context = array()) {
        $safe_context = array();
        if (is_array($context)) {
            foreach (array('task', 'run_id', 'project_id', 'conversation_id', 'source', 'error') as $key) {
                if (isset($context[$key]) && $context[$key] !== '') {
                    $safe_context[$key] = is_scalar($context[$key]) ? (string) $context[$key] : wp_json_encode($context[$key]);
                }
            }
        }
        $suffix = !empty($safe_context) ? ' ' . wp_json_encode($safe_context) : '';
        if (function_exists('aysa_debug_log')) {
            aysa_debug_log('[AYSA AGENT8 BRIDGE] ' . (string) $message . $suffix);
        }
    }

    private function aysa_agent8_bridge_is_post_research_response($response_result) {
        if (!is_array($response_result)) {
            return false;
        }
        $phase = sanitize_key((string) ($response_result['phase'] ?? ''));
        $current_step = sanitize_key((string) ($response_result['current_step'] ?? ''));
        $module = sanitize_key((string) ($response_result['module'] ?? ''));
        $tab = sanitize_key((string) ($response_result['tab'] ?? ''));
        $onboarding = isset($response_result['state']['context']['onboarding']) && is_array($response_result['state']['context']['onboarding'])
            ? $response_result['state']['context']['onboarding']
            : array();
        $onboarding_phase = sanitize_key((string) ($onboarding['phase_key'] ?? ''));
        $onboarding_module = sanitize_key((string) ($onboarding['module'] ?? ''));
        $onboarding_tab = sanitize_key((string) ($onboarding['tab'] ?? ''));

        return $phase === 'post_research_workspace'
            || $current_step === 'post_research_workspace'
            || $onboarding_phase === 'post_research_workspace'
            || ($module === 'technical_seo' && $tab === 'site_audit')
            || ($onboarding_module === 'technical_seo' && $onboarding_tab === 'site_audit');
    }

    private function aysa_agent8_bridge_post_research_actions() {
        return array(
            array('type' => 'action', 'action_key' => 'show_missing_pages', 'label' => 'Open Missing Pages'),
            array('type' => 'action', 'action_key' => 'show_keyword_mapping', 'label' => 'Open Keyword Mapping'),
            array('type' => 'action', 'action_key' => 'show_planned_content', 'label' => 'Open Content Plan'),
            array('type' => 'action', 'action_key' => 'show_found_keywords', 'label' => 'Review Keywords'),
            array('type' => 'action', 'action_key' => 'show_research_competitors', 'label' => 'Refine Competitors'),
            array('type' => 'action', 'action_key' => 'refresh_technical_optimization', 'label' => 'Start Technical Audit'),
            array('type' => 'action', 'action_key' => 'show_technical_overview', 'label' => 'View technical overview'),
            array('type' => 'action', 'action_key' => 'show_technical_site_audit', 'label' => 'Open Site Audit'),
        );
    }

    private function aysa_agent8_bridge_post_research_revisit_actions() {
        return array(
            array('type' => 'action', 'action_key' => 'show_found_keywords', 'label' => 'Review Keywords'),
            array('type' => 'action', 'action_key' => 'show_research_competitors', 'label' => 'Review Competitors'),
            array('type' => 'action', 'action_key' => 'show_keyword_mapping', 'label' => 'Review Keyword Mapping'),
            array('type' => 'action', 'action_key' => 'show_missing_pages', 'label' => 'Review Missing Pages'),
            array('type' => 'action', 'action_key' => 'show_planned_content', 'label' => 'Review Content Plan'),
            array('type' => 'action', 'action_key' => 'show_publishing_calendar', 'label' => 'Publishing Calendar'),
        );
    }

    private function aysa_agent8_bridge_primary_guidance_actions($response_result) {
        if (!is_array($response_result)) {
            return array();
        }
        $action_result = isset($response_result['action_result']) && is_array($response_result['action_result'])
            ? $response_result['action_result']
            : array();
        $projection = isset($action_result['projection']) && is_array($action_result['projection'])
            ? $action_result['projection']
            : array();
        $type = sanitize_key((string) ($projection['type'] ?? ''));

        if ($type === 'technical_overview' || $type === 'technical_site_audit') {
            return array(
                array('type' => 'action', 'action_key' => 'show_onpage_optimization', 'label' => 'Open On-Page SEO', 'style' => 'primary'),
            );
        }

        if ($type !== 'onpage_optimization') {
            return array();
        }

        $summary = isset($action_result['summary']) && is_array($action_result['summary'])
            ? $action_result['summary']
            : array();
        $next_action = '';
        $label = '';
        if ((int) ($summary['keyword_mapping_needed'] ?? 0) > 0) {
            $next_action = 'show_onpage_keyword_association';
            $label = 'Review pages without keyword mapping';
        } elseif ((int) ($summary['keyword_validation_ready'] ?? 0) > 0) {
            $next_action = 'show_onpage_keyword_validation';
            $label = 'Validate page keywords';
        } elseif ((int) ($summary['meta_ready'] ?? 0) > 0) {
            $next_action = 'show_onpage_meta_generation';
            $label = 'Review missing or weak metadata';
        } elseif ((int) ($summary['rewrite_recommended'] ?? 0) > 0 || (int) ($summary['rewrite_review'] ?? 0) > 0) {
            $next_action = 'show_onpage_rewrite_generation';
            $label = 'Review pages that may need rewrites';
        } elseif ((int) ($summary['apply_queue'] ?? 0) > 0) {
            $next_action = 'show_onpage_apply_queue';
            $label = 'Review the apply queue';
        }

        return $next_action !== '' ? array(
            array('type' => 'action', 'action_key' => $next_action, 'label' => $label, 'style' => 'primary'),
        ) : array();
    }

    private function aysa_agent8_bridge_compact_post_research_response($response_result) {
        if (!is_array($response_result)) {
            return $response_result;
        }

        $messages = isset($response_result['messages']) && is_array($response_result['messages'])
            ? array_slice($response_result['messages'], -3)
            : array();
        $assistant_message = trim((string) ($response_result['assistant_message'] ?? ''));
        if ($assistant_message === '') {
            $assistant_message = 'Research is complete. Start by reviewing Missing Pages, then Keyword Mapping, then Content Plan. Technical SEO is available next.';
        }

        $state = isset($response_result['state']) && is_array($response_result['state']) ? $response_result['state'] : array();
        $meta = isset($state['meta']) && is_array($state['meta']) ? $state['meta'] : array();
        $context = isset($state['context']) && is_array($state['context']) ? $state['context'] : array();
        $onboarding = isset($context['onboarding']) && is_array($context['onboarding']) ? $context['onboarding'] : array();
        $onboarding['phase_key'] = 'post_research_workspace';
        $onboarding['module'] = 'technical_seo';
        $onboarding['tab'] = 'site_audit';
        $onboarding['next_step'] = 'review_or_execute';

        $compact_context = array(
            'onboarding' => $onboarding,
        );
        if (isset($context['post_research_artifact']) && is_array($context['post_research_artifact'])) {
            $compact_context['post_research_artifact'] = $this->aysa_agent8_transport_reduce_value($context['post_research_artifact'], 0, 12);
        }
        if (isset($context['latest_keyword_research_run']) && is_array($context['latest_keyword_research_run'])) {
            $compact_context['latest_keyword_research_run'] = $this->aysa_agent8_transport_reduce_value($context['latest_keyword_research_run'], 0, 12);
        }
        if (isset($context['research_review']) && is_array($context['research_review'])) {
            $compact_context['research_review'] = $context['research_review'];
        }

        $raw_action_result = isset($response_result['action_result']) && is_array($response_result['action_result'])
            ? $response_result['action_result']
            : array();
        $raw_ui_command = isset($raw_action_result['ui_command']) && is_array($raw_action_result['ui_command'])
            ? $raw_action_result['ui_command']
            : array();
        $is_found_keywords_modal = (($raw_action_result['action_key'] ?? '') === 'show_found_keywords')
            || (
                ($raw_ui_command['type'] ?? '') === 'open_keyword_review'
                && ($raw_ui_command['review_type'] ?? '') === 'found_keywords'
                && isset($raw_ui_command['pagination'])
            );
        $is_keyword_mapping_modal = (($raw_action_result['action_key'] ?? '') === 'show_keyword_mapping')
            || (
                ($raw_ui_command['type'] ?? '') === 'open_keyword_review'
                && ($raw_ui_command['review_type'] ?? '') === 'keyword_mapping'
                && isset($raw_ui_command['pagination'])
            );
        $is_missing_pages_modal = (($raw_action_result['action_key'] ?? '') === 'show_missing_pages')
            || (
                ($raw_ui_command['type'] ?? '') === 'open_missing_pages_review'
                && isset($raw_ui_command['pagination'])
            );
        $is_planned_content_modal = (($raw_action_result['action_key'] ?? '') === 'show_planned_content')
            || (
                ($raw_ui_command['type'] ?? '') === 'open_content_strategy_review'
                && ($raw_ui_command['review_type'] ?? '') === 'planned_content'
                && isset($raw_ui_command['pagination'])
            );
        $is_site_audit_review = (($raw_action_result['action_key'] ?? '') === 'show_technical_site_audit')
            || (($raw_ui_command['type'] ?? '') === 'open_technical_site_audit_review');
        $is_site_audit_bundle_review = in_array(($raw_action_result['action_key'] ?? ''), array(
            'prepare_metadata_remediation',
            'prepare_response_code_review',
            'prepare_indexability_review',
            'prepare_internal_linking_plan',
            'prepare_image_alt_text',
            'apply_metadata_remediation',
            'verify_metadata_remediation',
            'apply_response_code_review',
            'verify_response_code_review',
            'apply_indexability_review',
            'verify_indexability_review',
            'apply_internal_linking_plan',
            'verify_internal_linking_plan',
            'apply_image_alt_text',
            'verify_image_alt_text',
            'ignore_site_audit_action_bundle',
        ), true)
            || (($raw_ui_command['type'] ?? '') === 'open_technical_site_audit_bundle_review');
        $action_result = !empty($raw_action_result)
            ? (($is_site_audit_review || $is_site_audit_bundle_review) ? $this->aysa_agent8_transport_reduce_site_audit_action_result($raw_action_result) : (($is_found_keywords_modal || $is_keyword_mapping_modal || $is_missing_pages_modal || $is_planned_content_modal) ? $raw_action_result : $this->aysa_agent8_transport_reduce_value($raw_action_result, 0, 12)))
            : array('ok' => true, 'projection' => array('type' => 'post_research_workspace'));

        $response_result['state'] = array(
            'meta' => $meta,
            'context' => $compact_context,
        );
        $response_result['phase'] = 'post_research_workspace';
        $response_result['current_step'] = 'post_research_workspace';
        $response_result['module'] = 'technical_seo';
        $response_result['tab'] = 'site_audit';
        $response_result['messages'] = $messages;
        $response_result['assistant_message'] = $assistant_message;
        $primary_guidance_actions = $this->aysa_agent8_bridge_primary_guidance_actions($response_result);
        $api_suggested_actions = isset($response_result['suggested_actions']) && is_array($response_result['suggested_actions'])
            ? $response_result['suggested_actions']
            : array();
        $response_result['suggested_actions'] = !empty($primary_guidance_actions)
            ? $primary_guidance_actions
            : (!empty($api_suggested_actions) ? $this->aysa_agent8_transport_reduce_value($api_suggested_actions, 0, 6) : array());
        if (!empty($primary_guidance_actions) || !empty($api_suggested_actions)) {
            $api_persistent_actions = isset($response_result['persistent_actions']) && is_array($response_result['persistent_actions'])
                ? $response_result['persistent_actions']
                : array();
            $response_result['persistent_actions'] = !empty($api_persistent_actions)
                ? $this->aysa_agent8_transport_reduce_value($api_persistent_actions, 0, 6)
                : array();
        }
        $response_result['approval_request'] = null;
        $response_result['needs_approval'] = false;
        $response_result['action_result'] = $action_result;

        return $response_result;
    }

    private function aysa_agent8_bridge_compact_payload_if_needed($payload, $context = array(), $force = false) {
        if (!is_array($payload)) {
            return $payload;
        }

        if (!$force) {
            $encoded = wp_json_encode($payload);
            if (is_string($encoded) && strlen($encoded) <= 1200000) {
                return $payload;
            }
        }

        if (isset($payload['result']['result']) && is_array($payload['result']['result']) && $this->aysa_agent8_bridge_is_post_research_response($payload['result']['result'])) {
            $payload['result']['result'] = $this->aysa_agent8_bridge_compact_post_research_response($payload['result']['result']);
            $this->aysa_agent8_bridge_log('Compacted async post-research bridge payload.', $context);
            return $payload;
        }

        if (isset($payload['result']) && is_array($payload['result']) && $this->aysa_agent8_bridge_is_post_research_response($payload['result'])) {
            $payload['result'] = $this->aysa_agent8_bridge_compact_post_research_response($payload['result']);
            $this->aysa_agent8_bridge_log('Compacted sync post-research bridge payload.', $context);
            return $payload;
        }

        if ($force) {
            $payload = $this->aysa_agent8_transport_reduce_value($payload, 0, 20);
            $this->aysa_agent8_bridge_log('Reduced non-post-research Agent8 bridge payload.', $context);
        }

        return $payload;
    }

    private function aysa_agent8_bridge_send_json_success($payload, $context = array()) {
        $payload = $this->aysa_agent8_bridge_compact_payload_if_needed($payload, $context);
        $encoded = wp_json_encode(array('success' => true, 'data' => $payload));
        if (!is_string($encoded)) {
            $payload = $this->aysa_agent8_bridge_compact_payload_if_needed($payload, $context, true);
            $encoded = wp_json_encode(array('success' => true, 'data' => $payload));
        }
        if (!is_string($encoded)) {
            $this->aysa_agent8_bridge_log('Agent8 bridge JSON encoding failed.', $context);
            wp_send_json_error(array(
                'message' => 'Agent8 bridge could not encode the response. Please retry.',
                'code' => 'agent8_bridge_json_encode_failed',
            ));
            return;
        }

        wp_send_json_success($payload);
    }

    private function aysa_conversation_async_fail_status($task, $project_id, $wp_user_id, $site_host, $status, $message) {
        if (!is_array($status)) {
            $status = array();
        }
        $status['status'] = 'failed';
        $status['result'] = null;
        $status['error'] = (string) $message;
        $status['updated_at'] = time();
        $status['finished_at'] = time();
        unset($status['_request']);
        $this->aysa_conversation_async_save_status($task, $project_id, $wp_user_id, $site_host, $status);
        return $status;
    }

    private function aysa_conversation_async_maybe_recover_stale($task, $project_id, $wp_user_id, $site_host, $status) {
        if (!is_array($status)) {
            return array();
        }

        $state = (string) ($status['status'] ?? 'idle');
        if (!in_array($state, array('queued', 'running'), true)) {
            return $status;
        }

        $updated_at = intval($status['updated_at'] ?? 0);
        $stale_after = $state === 'running' ? 300 : 20;
        if ($updated_at > 0 && (time() - $updated_at) <= $stale_after) {
            return $status;
        }

        $request = isset($status['_request']) && is_array($status['_request']) ? $status['_request'] : array();
        if ($request === array()) {
            return $this->aysa_conversation_async_fail_status(
                $task,
                $project_id,
                $wp_user_id,
                $site_host,
                $status,
                'Conversation job metadata is missing. Please retry.'
            );
        }

        $attempts = intval($status['attempts'] ?? 0);
        $recovery_count = intval($status['recovery_count'] ?? 0);
        $started_at = intval($status['started_at'] ?? $updated_at);
        $age = $started_at > 0 ? (time() - $started_at) : 0;
        $max_age = $state === 'running' ? 900 : 180;
        if ($recovery_count >= 3 || $age > $max_age) {
            return $this->aysa_conversation_async_fail_status(
                $task,
                $project_id,
                $wp_user_id,
                $site_host,
                $status,
                'Conversation job stalled and was stopped. Please retry.'
            );
        }

        $status['status'] = 'queued';
        $status['updated_at'] = time();
        $status['last_recovered_at'] = time();
        $status['recovery_count'] = $recovery_count + 1;
        $status['error'] = '';
        $this->aysa_conversation_async_save_status($task, $project_id, $wp_user_id, $site_host, $status);

        $this->aysa_conversation_async_schedule_event(array(
            'task' => $task,
            'project_id' => $project_id,
            'wp_user_id' => $wp_user_id,
            'site_host' => $site_host,
            'run_id' => (string) ($status['run_id'] ?? ''),
            'request_hash' => (string) ($status['request_hash'] ?? ''),
            'request' => $request,
        ), 1);

        return $this->aysa_conversation_async_get_status($task, $project_id, $wp_user_id, $site_host);
    }

    private function aysa_conversation_async_schedule_event($payload, $delay_seconds = 1) {
        if (!is_array($payload)) {
            return;
        }
        $timestamp = time() + max(1, intval($delay_seconds));
        wp_schedule_single_event($timestamp, 'aysa_conversation_async_event', array($payload));
        if (function_exists('spawn_cron')) {
            spawn_cron();
        }
    }

    private function aysa_conversation_async_validate_start_request() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            return new WP_Error('invalid_nonce', 'Security check failed.');
        }
        if (!current_user_can('manage_options')) {
            return new WP_Error('insufficient_permissions', 'Insufficient permissions.');
        }
        return true;
    }

    private function aysa_conversation_async_prepare_request($task, $input) {
        $task = sanitize_key((string) $task);
        $wp_user_id = get_current_user_id();
        $project_id = intval($input['project_id'] ?? get_option('aysa_project_id', 0));
        $site_host = isset($input['site_host']) ? sanitize_text_field(wp_unslash((string) $input['site_host'])) : '';
        $conversation_language = $this->aysa_agent8_normalize_conversation_language(
            isset($input['conversation_language']) ? wp_unslash((string) $input['conversation_language']) : '',
            'en'
        );
        $request = array(
            'task' => $task,
            'project_id' => $project_id,
            'wp_user_id' => $wp_user_id,
            'site_host' => $site_host,
            'conversation_language' => $conversation_language,
            'input' => $input,
        );

        if ($task === 'setup') {
            $request['mode'] = isset($input['mode']) ? sanitize_key(wp_unslash((string) $input['mode'])) : 'bootstrap';
            $request['current_step'] = isset($input['current_step']) ? sanitize_key(wp_unslash((string) $input['current_step'])) : 'integrations';
            $request['user_message'] = isset($input['user_message']) ? sanitize_textarea_field(wp_unslash((string) $input['user_message'])) : '';
            $request['request_hash'] = md5(wp_json_encode(array(
                'task' => $task,
                'project_id' => $project_id,
                'site_host' => $site_host,
                'mode' => $request['mode'],
                'current_step' => $request['current_step'],
                'conversation_language' => $conversation_language,
                'user_message' => $request['user_message'],
                'reset_session' => !empty($input['reset_session']),
                'skip_google' => !empty($input['skip_google']),
            )));
            return $request;
        }

        if ($task === 'adjustments') {
            $request['mode'] = isset($input['mode']) ? sanitize_key(wp_unslash((string) $input['mode'])) : 'bootstrap';
            $request['user_message'] = isset($input['user_message']) ? sanitize_textarea_field(wp_unslash((string) $input['user_message'])) : '';
            $request['request_hash'] = md5(wp_json_encode(array(
                'task' => $task,
                'project_id' => $project_id,
                'site_host' => $site_host,
                'mode' => $request['mode'],
                'conversation_language' => $conversation_language,
                'user_message' => $request['user_message'],
                'reset_session' => !empty($input['reset_session']),
                'skip_google' => !empty($input['skip_google']),
                'forced_execution_action' => isset($input['forced_execution_action']) ? sanitize_text_field(wp_unslash((string) $input['forced_execution_action'])) : '',
            )));
            return $request;
        }

        if ($task === 'monitoring') {
            $request['mode'] = isset($input['mode']) ? sanitize_key(wp_unslash((string) $input['mode'])) : 'bootstrap';
            $request['user_message'] = isset($input['user_message']) ? sanitize_textarea_field(wp_unslash((string) $input['user_message'])) : '';
            $request['request_hash'] = md5(wp_json_encode(array(
                'task' => $task,
                'project_id' => $project_id,
                'site_host' => $site_host,
                'mode' => $request['mode'],
                'conversation_language' => $conversation_language,
                'user_message' => $request['user_message'],
                'reset_session' => !empty($input['reset_session']),
                'client_state' => isset($input['client_state']) ? wp_unslash((string) $input['client_state']) : '',
            )));
            return $request;
        }

        if ($task === 'activation') {
            $request['user_message'] = isset($input['user_message']) ? sanitize_textarea_field(wp_unslash((string) $input['user_message'])) : '';
            $request['request_hash'] = md5(wp_json_encode(array(
                'task' => $task,
                'site_host' => $site_host,
                'conversation_language' => $conversation_language,
                'user_message' => $request['user_message'],
            )));
            return $request;
        }

        if ($task === 'agent8') {
            $resolved_project_id = $this->aysa_resolve_technical_project_id($project_id, $site_host);
            if ($resolved_project_id > 0 && $resolved_project_id !== $project_id) {
                aysa_debug_log('[AYSA AGENT8 ASYNC] Normalized async project id from ' . $project_id . ' to ' . $resolved_project_id . ' for host ' . $site_host);
                $project_id = $resolved_project_id;
                $request['project_id'] = $project_id;
            }
            $request['mode'] = isset($input['mode']) ? sanitize_key(wp_unslash((string) $input['mode'])) : 'bootstrap';
            $request['user_message'] = isset($input['user_message']) ? sanitize_textarea_field(wp_unslash((string) $input['user_message'])) : '';
            $request['conversation_id'] = isset($input['conversation_id']) ? sanitize_text_field(wp_unslash((string) $input['conversation_id'])) : '';
            $request['request_epoch'] = max(0, intval($input['request_epoch'] ?? 0));
            $request['action_key'] = isset($input['action_key']) ? sanitize_key(wp_unslash((string) $input['action_key'])) : '';
            $request['approved'] = !empty($input['approved']);
            if (isset($input['action_payload'])) {
                $raw_action_payload = wp_unslash($input['action_payload']);
                if (is_string($raw_action_payload) && $raw_action_payload !== '') {
                    $decoded_action_payload = json_decode($raw_action_payload, true);
                    if (is_array($decoded_action_payload)) {
                        $request['action_payload'] = $decoded_action_payload;
                    }
                } elseif (is_array($raw_action_payload)) {
                    $request['action_payload'] = $raw_action_payload;
                }
            }
            $request['request_hash'] = md5(wp_json_encode(array(
                'task' => $task,
                'project_id' => $project_id,
                'site_host' => $site_host,
                'mode' => $request['mode'],
                'conversation_language' => $conversation_language,
                'request_epoch' => $request['request_epoch'],
                'user_message' => $request['user_message'],
                'conversation_id' => $request['conversation_id'],
                'action_key' => $request['action_key'],
                'approved' => $request['approved'] ? 1 : 0,
                'action_payload' => isset($input['action_payload']) ? wp_unslash((string) $input['action_payload']) : '',
            )));
            return $request;
        }

        return new WP_Error('unsupported_task', 'Unsupported conversation async task.');
    }

    public function handle_conversation_async_start() {
        $validation = $this->aysa_conversation_async_validate_start_request();
        if (is_wp_error($validation)) {
            wp_send_json_error(array('message' => $validation->get_error_message()));
            return;
        }

        $task = sanitize_key((string) ($_POST['task'] ?? ''));
        if (!in_array($task, $this->aysa_conversation_async_supported_tasks(), true)) {
            wp_send_json_error(array('message' => 'Unsupported conversation task.'));
            return;
        }

        $request = $this->aysa_conversation_async_prepare_request($task, $_POST);
        if (is_wp_error($request)) {
            wp_send_json_error(array('message' => $request->get_error_message(), 'code' => $request->get_error_code()));
            return;
        }

        $project_id = intval($request['project_id'] ?? 0);
        $wp_user_id = intval($request['wp_user_id'] ?? get_current_user_id());
        $site_host = (string) ($request['site_host'] ?? '');
        $request_hash = (string) ($request['request_hash'] ?? '');
        $existing = $this->aysa_conversation_async_get_status($task, $project_id, $wp_user_id, $site_host);
        if (
            is_array($existing) &&
            !empty($existing['run_id']) &&
            in_array((string) ($existing['status'] ?? ''), array('queued', 'running'), true) &&
            (string) ($existing['request_hash'] ?? '') === $request_hash
        ) {
            wp_send_json_success($this->aysa_conversation_async_output_status($existing));
            return;
        }

        $run_id = uniqid('conv_', true);
        $status = array(
            'task' => $task,
            'project_id' => $project_id,
            'wp_user_id' => $wp_user_id,
            'site_host' => $site_host,
            'run_id' => $run_id,
            'request_hash' => $request_hash,
            'request_epoch' => max(0, intval($request['request_epoch'] ?? 0)),
            'status' => 'queued',
            'result' => null,
            'error' => '',
            'started_at' => time(),
            'updated_at' => time(),
            'finished_at' => 0,
            'attempts' => 0,
            'recovery_count' => 0,
            'last_recovered_at' => 0,
            'billing_applied' => false,
            '_request' => $request,
        );
        $this->aysa_conversation_async_save_status($task, $project_id, $wp_user_id, $site_host, $status);
        $this->aysa_conversation_async_schedule_event(array(
            'task' => $task,
            'project_id' => $project_id,
            'wp_user_id' => $wp_user_id,
            'site_host' => $site_host,
            'run_id' => $run_id,
            'request_hash' => $request_hash,
            'request' => $request,
        ), 1);

        wp_send_json_success($this->aysa_conversation_async_output_status($status));
    }

    public function handle_conversation_async_status() {
        $validation = $this->aysa_conversation_async_validate_start_request();
        if (is_wp_error($validation)) {
            wp_send_json_error(array('message' => $validation->get_error_message()));
            return;
        }

        $task = sanitize_key((string) ($_POST['task'] ?? ''));
        if (!in_array($task, $this->aysa_conversation_async_supported_tasks(), true)) {
            wp_send_json_error(array('message' => 'Unsupported conversation task.'));
            return;
        }

        $request = $this->aysa_conversation_async_prepare_request($task, $_POST);
        if (is_wp_error($request)) {
            wp_send_json_error(array('message' => $request->get_error_message(), 'code' => $request->get_error_code()));
            return;
        }

        $project_id = intval($request['project_id'] ?? 0);
        $wp_user_id = intval($request['wp_user_id'] ?? get_current_user_id());
        $site_host = (string) ($request['site_host'] ?? '');
        $status = $this->aysa_conversation_async_get_status($task, $project_id, $wp_user_id, $site_host);
        if (empty($status)) {
            $this->aysa_agent8_bridge_send_json_success(array(
                'task' => $task,
                'project_id' => $project_id,
                'status' => 'idle',
                'result' => null,
                'error' => '',
            ), array(
                'task' => $task,
                'run_id' => '',
                'project_id' => $project_id,
                'conversation_id' => (string) ($request['conversation_id'] ?? ''),
                'source' => 'async_status_idle',
            ));
            return;
        }

        $requested_run_id = (string) ($_POST['run_id'] ?? '');
        if ($requested_run_id !== '' && (string) ($status['run_id'] ?? '') !== $requested_run_id) {
            $this->aysa_agent8_bridge_send_json_success(array(
                'task' => $task,
                'project_id' => $project_id,
                'status' => 'idle',
                'result' => null,
                'error' => '',
            ), array(
                'task' => $task,
                'run_id' => $requested_run_id,
                'project_id' => $project_id,
                'conversation_id' => (string) ($request['conversation_id'] ?? ''),
                'source' => 'async_status_run_mismatch',
            ));
            return;
        }

        if ($task === 'agent8') {
            $requested_epoch = max(0, intval($request['request_epoch'] ?? 0));
            $status_epoch = max(0, intval($status['request_epoch'] ?? 0));
            if ($requested_epoch > 0 && $status_epoch > 0 && $requested_epoch !== $status_epoch) {
                $this->aysa_agent8_bridge_send_json_success(array(
                    'task' => $task,
                    'project_id' => $project_id,
                    'status' => 'idle',
                    'result' => null,
                    'error' => '',
                ), array(
                    'task' => $task,
                    'run_id' => (string) ($status['run_id'] ?? ''),
                    'project_id' => $project_id,
                    'conversation_id' => (string) ($request['conversation_id'] ?? ''),
                    'source' => 'async_status_epoch_mismatch',
                ));
                return;
            }
        }

        $status = $this->aysa_conversation_async_maybe_recover_stale($task, $project_id, $wp_user_id, $site_host, $status);

        $this->aysa_agent8_bridge_send_json_success($this->aysa_conversation_async_output_status($status), array(
            'task' => $task,
            'run_id' => (string) ($status['run_id'] ?? ''),
            'project_id' => $project_id,
            'conversation_id' => (string) ($request['conversation_id'] ?? ''),
            'source' => 'async_status',
        ));
    }

    public function run_conversation_async_event($payload) {
        if (!is_array($payload)) {
            return;
        }

        $task = sanitize_key((string) ($payload['task'] ?? ''));
        $project_id = $this->aysa_resolve_technical_project_id(intval($payload['project_id'] ?? 0));
        $wp_user_id = intval($payload['wp_user_id'] ?? 0);
        $site_host = (string) ($payload['site_host'] ?? '');
        $run_id = (string) ($payload['run_id'] ?? '');
        $request_hash = (string) ($payload['request_hash'] ?? '');
        $request = isset($payload['request']) && is_array($payload['request']) ? $payload['request'] : array();
        if ($run_id === '' || $request_hash === '' || !in_array($task, $this->aysa_conversation_async_supported_tasks(), true)) {
            return;
        }

        if ($wp_user_id > 0 && function_exists('wp_set_current_user')) {
            wp_set_current_user($wp_user_id);
        }

        $status = $this->aysa_conversation_async_get_status($task, $project_id, $wp_user_id, $site_host);
        if (
            empty($status) ||
            (string) ($status['run_id'] ?? '') !== $run_id ||
            (string) ($status['request_hash'] ?? '') !== $request_hash
        ) {
            return;
        }

        $status['status'] = 'running';
        $status['updated_at'] = time();
        $status['attempts'] = intval($status['attempts'] ?? 0) + 1;
        $status['worker_started_at'] = time();
        $status['error'] = '';
        $this->aysa_conversation_async_save_status($task, $project_id, $wp_user_id, $site_host, $status);

        try {
            $result = $this->aysa_conversation_async_execute($task, $request);
        } catch (Throwable $e) {
            $this->aysa_agent8_bridge_log('Agent8 async execution threw.', array(
                'task' => $task,
                'run_id' => $run_id,
                'project_id' => $project_id,
                'conversation_id' => (string) ($request['conversation_id'] ?? ''),
                'source' => 'async_event',
                'error' => $e->getMessage(),
            ));
            $result = new WP_Error('agent8_bridge_exception', 'Agent8 bridge execution failed: ' . $e->getMessage());
        }
        $status['updated_at'] = time();
        $status['finished_at'] = time();
        unset($status['_request']);

        if (is_wp_error($result)) {
            $status['status'] = 'failed';
            $status['result'] = null;
            $status['error'] = $result->get_error_message();
        } else {
            $status['status'] = 'completed';
            $status['result'] = $this->aysa_agent8_bridge_compact_payload_if_needed($result, array(
                'task' => $task,
                'run_id' => $run_id,
                'project_id' => $project_id,
                'conversation_id' => (string) ($request['conversation_id'] ?? ''),
                'source' => 'async_event_result',
            ));
            $status['error'] = '';
            $status['billing_applied'] = true;
        }

        $this->aysa_conversation_async_save_status($task, $project_id, $wp_user_id, $site_host, $status);
    }

    private function aysa_conversation_async_execute($task, $request) {
        switch ($task) {
            case 'setup':
                return $this->aysa_execute_setup_conversation($request);
            case 'adjustments':
                return $this->aysa_execute_adjustments_conversation($request);
            case 'activation':
                return $this->aysa_execute_activation_conversation($request);
            case 'monitoring':
                return $this->aysa_execute_monitoring_conversation($request);
            case 'agent8':
                return $this->aysa_execute_agent8_conversation($request);
        }

        return new WP_Error('unsupported_task', 'Unsupported conversation async task.');
    }

    private function aysa_execute_setup_conversation($input) {
        if (!$this->check_authentication(false)) {
            return new WP_Error('auth_required', 'Authentication required.');
        }

        $request_started_at = microtime(true);
        $key_validation = $this->aysa_validate_test_access_key(
            $this->aysa_get_effective_access_key(isset($input['access_key']) ? sanitize_text_field(wp_unslash((string) $input['access_key'])) : ''),
            isset($input['site_host']) ? sanitize_text_field(wp_unslash((string) $input['site_host'])) : ''
        );
        if (empty($key_validation['valid'])) {
            return new WP_Error('access_key_invalid', (string) ($key_validation['message'] ?? 'Access key is invalid.'));
        }

        $mode = isset($input['mode']) ? sanitize_key(wp_unslash((string) $input['mode'])) : 'bootstrap';
        $current_step = isset($input['current_step']) ? sanitize_key(wp_unslash((string) $input['current_step'])) : 'integrations';
        $conversation_language = $this->aysa_agent8_normalize_conversation_language(
            isset($input['conversation_language']) ? wp_unslash((string) $input['conversation_language']) : '',
            'en'
        );
        $project_id = isset($input['project_id']) ? intval(wp_unslash((string) $input['project_id'])) : intval(get_option('aysa_project_id', 0));
        $user_message = isset($input['user_message']) ? sanitize_textarea_field(wp_unslash((string) $input['user_message'])) : '';
        if ($project_id <= 0) {
            return new WP_Error('missing_project', 'Missing project ID.');
        }

        $auth_manager = Aysa_Auth_Manager::get_instance();
        $auth_status = $auth_manager->is_authenticated();
        $user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : 0;
        if ($user_id <= 0) {
            return new WP_Error('invalid_auth_context', 'Invalid authentication context.');
        }

        $onboarding_run_token = $this->aysa_get_or_create_onboarding_run_token($project_id);
        $setup_reset_forced = $this->aysa_is_setup_reset_forced();
        $local_setup_state = $setup_reset_forced ? array() : $this->aysa_get_local_setup_state_for_user($user_id, $project_id);
        $general = isset($local_setup_state['general']) && is_array($local_setup_state['general']) ? $local_setup_state['general'] : array();
        $website = isset($general['website']) ? $this->aysa_setup_normalize_url((string) $general['website']) : '';
        if ($website === '') {
            $website = $this->aysa_setup_normalize_url(home_url('/'));
        }

        $google_connected = false;
        $google_email = '';
        $google_gsc_count = 0;
        $google_ga4_count = 0;
        if (class_exists('Aysa_Google_OAuth_Manager')) {
            $oauth_manager = Aysa_Google_OAuth_Manager::get_instance();
            if (is_object($oauth_manager) && method_exists($oauth_manager, 'get_status_for_project')) {
                $oauth_status = $oauth_manager->get_status_for_project($project_id);
                if (!is_wp_error($oauth_status) && is_array($oauth_status)) {
                    $google_connected = !empty($oauth_status['connected']);
                    $google_email = isset($oauth_status['email']) ? sanitize_email((string) $oauth_status['email']) : '';
                    $google_gsc_count = isset($oauth_status['gsc_sites_count']) ? intval($oauth_status['gsc_sites_count']) : 0;
                    $google_ga4_count = isset($oauth_status['ga4_properties_count']) ? intval($oauth_status['ga4_properties_count']) : 0;
                }
            }
        }

        $endpoint = '/v1/conversation/setup/bootstrap';
        $payload = array(
            'project_id' => $project_id,
            'current_step' => $current_step,
            'reset_session' => !empty($input['reset_session']),
            'skip_google' => !empty($input['skip_google']),
            'conversation_language' => $conversation_language,
            'site_host' => isset($input['site_host']) ? sanitize_text_field(wp_unslash((string) $input['site_host'])) : '',
            'website' => $website,
            'onboarding_run_token' => $onboarding_run_token,
            'wallet_snapshot' => $this->aysa_build_agent_wallet_snapshot($project_id),
            'local_setup_state' => $local_setup_state,
            'ui_context' => array(
                'conversation_language' => $conversation_language,
                'available_languages' => array('en', 'ro', 'bg'),
                'integrations' => array(
                    'terms_url' => 'https://aysa.ai/terms/',
                    'privacy_url' => 'https://aysa.ai/privacy/',
                    'google_connect_available' => true,
                    'google_connect_label' => 'Connect Google',
                    'google_connected' => $google_connected,
                    'google_skipped' => !empty($input['skip_google']),
                    'google_email' => $google_email,
                    'google_gsc_count' => $google_gsc_count,
                    'google_ga4_count' => $google_ga4_count,
                ),
            ),
        );

        if ($mode === 'message') {
            $endpoint = '/v1/conversation/setup/message';
            $payload['user_message'] = $user_message;
        }

        if ($this->aysa_should_queue_setup_competition_suggestion($mode, $current_step, $user_message)) {
            $plugin_url = admin_url('admin.php?page=aysa-agent8');
            $payload['async_notification'] = array(
                'channel' => 'setup_seo_profile',
                'event_key' => 'competition_seed_research_ready',
                'ui_action' => 'resume_setup_onboarding',
                'cta_label' => $conversation_language === 'ro' ? 'Vezi sugestiile' : ($conversation_language === 'bg' ? 'Виж предложенията' : 'See the suggestions'),
                'action_url' => $plugin_url,
                'email_to' => $this->get_notification_target_email(),
                'conversation_language' => $conversation_language,
                'run_token' => $onboarding_run_token,
            );

            $this->aysa_broker_request_async('POST', $endpoint, $payload, 1);

            $assistant_message = $this->aysa_setup_competition_async_ack($conversation_language);
            $email_target = trim((string) ($payload['async_notification']['email_to'] ?? ''));
            return array(
                'result' => array(
                    'ok' => true,
                    'assistant_message' => $assistant_message,
                    'phase' => 'onboarding_competition',
                    'current_step' => 'competition',
                    'next_step' => 'competition',
                    'step_substate' => 'regenerating_proposal',
                    'onboarding_run_token' => $onboarding_run_token,
                    'turn_type' => 'provide_seed_keywords',
                    'intent_family' => 'does_not_know',
                    'assistant_goal' => 'answer_and_reanchor',
                    'next_action' => 'await_async_competition_proposal',
                    'confidence' => 1,
                    'needs_clarification' => false,
                    'reanchor_target' => 'competition',
                    'async_task' => array(
                        'type' => 'competition_seed_research',
                        'status' => 'running',
                        'channel' => 'setup_seo_profile',
                        'event_key' => 'competition_seed_research_ready',
                    ),
                    'notification_policy' => array(
                        'in_app' => true,
                        'email' => $email_target !== '',
                        'channel' => 'setup_seo_profile',
                        'event_key' => 'competition_seed_research_ready',
                        'action_url' => $plugin_url,
                    ),
                    'ready_to_advance' => false,
                    'actions' => array(
                        'executed' => array(),
                        'should_advance' => false,
                    ),
                    'messages' => array(
                        array('role' => 'user', 'message' => $user_message),
                        array('role' => 'assistant', 'message' => $assistant_message),
                    ),
                    'suggested_actions' => array(),
                    'state' => array(
                        'steps' => is_array($local_setup_state) ? $local_setup_state : array(),
                        'meta' => array(
                            'phase' => 'onboarding_competition',
                            'current_step' => 'competition',
                            'next_step' => 'competition',
                            'step_substate' => 'regenerating_proposal',
                            'onboarding_run_token' => $onboarding_run_token,
                            'competition_research_status' => 'running',
                            'intent_family' => 'does_not_know',
                            'assistant_goal' => 'answer_and_reanchor',
                            'last_resolved_action' => 'await_async_competition_proposal',
                            'last_reanchor_target' => 'competition',
                            'turn_confidence' => 1,
                            'needs_clarification' => false,
                        ),
                    ),
                ),
                'access' => $key_validation,
                'queued' => true,
            );
        }

        $broker_started_at = microtime(true);
        $result = $this->aysa_broker_request('POST', $endpoint, $payload, 75);
        $broker_elapsed_ms = (int) round((microtime(true) - $broker_started_at) * 1000);
        if (is_wp_error($result)) {
            return $result;
        }
        if (!is_array($result) || empty($result['ok'])) {
            return new WP_Error('setup_conversation_failed', isset($result['error']) ? (string) $result['error'] : 'Setup conversation request failed.');
        }

        if ($mode === 'message') {
            $this->aysa_wallet_bill_provider_usage(
                $project_id,
                $endpoint,
                isset($result['usage']) && is_array($result['usage']) ? $result['usage'] : array(),
                'setup_conversation_message',
                array(
                    'module' => 'setup_conversation',
                    'source' => 'chat_message',
                )
            );
        }

        if (!empty($result['wp_updates']) && is_array($result['wp_updates'])) {
            $this->aysa_apply_setup_conversation_updates($user_id, $result['wp_updates'], $project_id);
            $updated_local_setup_state = $this->aysa_get_local_setup_state_for_user($user_id, $project_id);
            $result_state = isset($result['state']) && is_array($result['state']) ? $result['state'] : array();
            $result_meta = isset($result_state['meta']) && is_array($result_state['meta']) ? $result_state['meta'] : array();
            $result_steps = isset($result_state['steps']) && is_array($result_state['steps']) ? $result_state['steps'] : array();
            $google_skipped = !empty($result_steps['integrations']['google_skip']) || !empty($result_meta['google_optional_skipped']);
            $this->aysa_sync_setup_state_to_client_rag(
                $project_id,
                $conversation_language,
                $updated_local_setup_state,
                $google_connected,
                $google_skipped,
                $result_meta
            );
        }

        if (defined('WP_DEBUG') && WP_DEBUG) {
            error_log(sprintf(
                'AYSA setup conversation timings: broker=%dms total=%dms mode=%s project=%d',
                $broker_elapsed_ms,
                (int) round((microtime(true) - $request_started_at) * 1000),
                (string) $mode,
                (int) $project_id
            ));
        }

        return array(
            'result' => $result,
            'access' => $key_validation,
        );
    }

    private function aysa_execute_adjustments_conversation($input) {
        if (!$this->check_authentication(false)) {
            return new WP_Error('auth_required', 'Authentication required.');
        }

        $key_validation = $this->aysa_validate_test_access_key(
            $this->aysa_get_effective_access_key(isset($input['access_key']) ? sanitize_text_field(wp_unslash((string) $input['access_key'])) : ''),
            isset($input['site_host']) ? sanitize_text_field(wp_unslash((string) $input['site_host'])) : ''
        );
        if (empty($key_validation['valid'])) {
            return new WP_Error('access_key_invalid', (string) ($key_validation['message'] ?? 'Access key is invalid.'));
        }

        $mode = isset($input['mode']) ? sanitize_key(wp_unslash((string) $input['mode'])) : 'bootstrap';
        $conversation_language = isset($input['conversation_language']) ? sanitize_key(wp_unslash((string) $input['conversation_language'])) : 'en';
        if (!in_array($conversation_language, array('en', 'ro', 'bg'), true)) {
            $conversation_language = 'en';
        }
        $project_id = isset($input['project_id']) ? intval(wp_unslash((string) $input['project_id'])) : intval(get_option('aysa_project_id', 0));
        $user_message = isset($input['user_message']) ? sanitize_textarea_field(wp_unslash((string) $input['user_message'])) : '';

        if ($project_id <= 0) {
            return new WP_Error('missing_project', 'Missing project ID.');
        }

        $auth_manager = Aysa_Auth_Manager::get_instance();
        $auth_status = $auth_manager->is_authenticated();
        $user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : 0;
        if ($user_id <= 0) {
            return new WP_Error('invalid_auth_context', 'Invalid authentication context.');
        }

        $setup_reset_forced = $this->aysa_is_setup_reset_forced();
        $local_setup_state = $setup_reset_forced ? array() : $this->aysa_get_local_setup_state_for_user($user_id, $project_id);
        $general = isset($local_setup_state['general']) && is_array($local_setup_state['general']) ? $local_setup_state['general'] : array();
        $website = isset($general['website']) ? $this->aysa_setup_normalize_url((string) $general['website']) : '';
        if ($website === '') {
            $website = $this->aysa_setup_normalize_url(home_url('/'));
        }

        $google_connected = false;
        $google_email = '';
        $google_gsc_count = 0;
        $google_ga4_count = 0;
        if (class_exists('Aysa_Google_OAuth_Manager')) {
            $oauth_manager = Aysa_Google_OAuth_Manager::get_instance();
            if (is_object($oauth_manager) && method_exists($oauth_manager, 'get_status_for_project')) {
                $oauth_status = $oauth_manager->get_status_for_project($project_id);
                if (!is_wp_error($oauth_status) && is_array($oauth_status)) {
                    $google_connected = !empty($oauth_status['connected']);
                    $google_email = isset($oauth_status['email']) ? sanitize_email((string) $oauth_status['email']) : '';
                    $google_gsc_count = isset($oauth_status['gsc_sites_count']) ? intval($oauth_status['gsc_sites_count']) : 0;
                    $google_ga4_count = isset($oauth_status['ga4_properties_count']) ? intval($oauth_status['ga4_properties_count']) : 0;
                }
            }
        }

        $research_snapshot = $setup_reset_forced ? array() : $this->aysa_adjustments_get_research_snapshot($project_id, $user_id);
        $research_snapshot_for_ai = $setup_reset_forced ? array() : $this->aysa_adjustments_reduce_research_snapshot_for_ai($research_snapshot);
        $client_state = array();
        if (!$setup_reset_forced && isset($input['client_state'])) {
            $decoded_client_state = json_decode(wp_unslash((string) $input['client_state']), true);
            if (is_array($decoded_client_state)) {
                $client_state = $decoded_client_state;
            }
        }
        $client_local_setup_state = isset($client_state['local_setup_state']) && is_array($client_state['local_setup_state'])
            ? $client_state['local_setup_state']
            : array();
        if ($client_local_setup_state !== array()) {
            $local_setup_state = $this->aysa_merge_local_setup_state_fallback($local_setup_state, $client_local_setup_state);
        }
        $forced_execution_action = isset($input['forced_execution_action']) ? sanitize_text_field(wp_unslash((string) $input['forced_execution_action'])) : '';

        if ($mode === 'message' && $forced_execution_action !== '') {
            $result = array(
                'ok' => true,
                'state' => is_array($client_state) ? $client_state : array(),
                'messages' => array(),
            );
            $execution_meta = $this->aysa_adjustments_execute_research_action($forced_execution_action, $project_id, $user_id, $conversation_language);
            $research_snapshot = $this->aysa_adjustments_get_research_snapshot($project_id, $user_id);
            $assistant_message = !empty($execution_meta['assistant_message']) ? (string) $execution_meta['assistant_message'] : '';
            if ($user_message !== '') {
                $result['messages'][] = array('role' => 'user', 'message' => $user_message);
            }
            if ($assistant_message !== '') {
                $result['messages'][] = array('role' => 'assistant', 'message' => $assistant_message);
                $result['assistant_message'] = $assistant_message;
            }
            if (!empty($execution_meta['suggested_actions']) && is_array($execution_meta['suggested_actions'])) {
                $result['suggested_actions'] = $execution_meta['suggested_actions'];
            }
            if (!empty($execution_meta['ui_action'])) {
                $result['ui_action'] = (string) $execution_meta['ui_action'];
            }
            if (!empty($execution_meta['payment_link'])) {
                $result['payment_link'] = (string) $execution_meta['payment_link'];
            }
            if (!isset($result['state']['context']) || !is_array($result['state']['context'])) {
                $result['state']['context'] = array();
            }
            if (!isset($result['state']['meta']) || !is_array($result['state']['meta'])) {
                $result['state']['meta'] = array();
            }
            $result['state']['context']['research_snapshot'] = $research_snapshot;
            if (!empty($execution_meta['module'])) {
                $result['state']['meta']['active_research_module'] = (string) $execution_meta['module'];
            }
            if (!empty($execution_meta['state_meta']) && is_array($execution_meta['state_meta'])) {
                foreach ($execution_meta['state_meta'] as $meta_key => $meta_value) {
                    if (!is_string($meta_key) || $meta_key === '') {
                        continue;
                    }
                    $result['state']['meta'][$meta_key] = $meta_value;
                }
            }
            $result['research_snapshot'] = $research_snapshot;
            $result['workspace_artifact'] = $this->aysa_adjustments_build_research_workspace_artifact($research_snapshot, $conversation_language);

            return array(
                'result' => $result,
                'access' => $key_validation,
            );
        }

        $endpoint = '/v1/conversation/adjustments/bootstrap';
        $payload = array(
            'project_id' => $project_id,
            'aysa_user_id' => $user_id,
            'conversation_language' => $conversation_language,
            'reset_session' => !empty($input['reset_session']),
            'skip_google' => !empty($input['skip_google']),
            'site_host' => isset($input['site_host']) ? sanitize_text_field(wp_unslash((string) $input['site_host'])) : '',
            'website' => $website,
            'wallet_snapshot' => $this->aysa_build_agent_wallet_snapshot($project_id),
            'local_setup_state' => $local_setup_state,
            'research_snapshot' => $research_snapshot_for_ai,
            'ui_context' => array(
                'conversation_language' => $conversation_language,
                'available_languages' => array('en', 'ro', 'bg'),
                'phase' => 'agent_orchestration',
                'integrations' => array(
                    'terms_url' => 'https://aysa.ai/terms/',
                    'privacy_url' => 'https://aysa.ai/privacy/',
                    'google_connect_available' => true,
                    'google_connect_label' => 'Connect Google',
                    'google_connected' => $google_connected,
                    'google_skipped' => !empty($input['skip_google']),
                    'google_email' => $google_email,
                    'google_gsc_count' => $google_gsc_count,
                    'google_ga4_count' => $google_ga4_count,
                ),
            ),
            'state' => $client_state,
        );
        if ($mode === 'message') {
            $endpoint = '/v1/conversation/adjustments/message';
            $payload['user_message'] = $user_message;
        }

        $result = $this->aysa_broker_request('POST', $endpoint, $payload, 75);
        if (is_wp_error($result)) {
            return $result;
        }
        if (!is_array($result) || empty($result['ok'])) {
            return new WP_Error('adjustments_conversation_failed', isset($result['error']) ? (string) $result['error'] : 'Adjustments conversation request failed.');
        }

        if ($mode === 'message') {
            $this->aysa_wallet_bill_provider_usage(
                $project_id,
                $endpoint,
                isset($result['usage']) && is_array($result['usage']) ? $result['usage'] : array(),
                'adjustments_conversation_message',
                array(
                    'module' => 'adjustments_conversation',
                    'source' => 'chat_message',
                )
            );
        }

        $execution_action = (string) ($result['execution_action'] ?? '');
        if ($execution_action !== '') {
            $execution_meta = $this->aysa_adjustments_execute_research_action($execution_action, $project_id, $user_id, $conversation_language);
            $research_snapshot = $this->aysa_adjustments_get_research_snapshot($project_id, $user_id);
            if (!empty($execution_meta['assistant_message'])) {
                $result['assistant_message'] = $execution_meta['assistant_message'];
            }
            if (!empty($execution_meta['suggested_actions']) && is_array($execution_meta['suggested_actions'])) {
                $result['suggested_actions'] = $execution_meta['suggested_actions'];
            }
            if (!empty($execution_meta['ui_action'])) {
                $result['ui_action'] = (string) $execution_meta['ui_action'];
            }
            if (!empty($execution_meta['payment_link'])) {
                $result['payment_link'] = (string) $execution_meta['payment_link'];
            }
            if (!isset($result['state']) || !is_array($result['state'])) {
                $result['state'] = array();
            }
            if (!isset($result['state']['context']) || !is_array($result['state']['context'])) {
                $result['state']['context'] = array();
            }
            if (!isset($result['state']['meta']) || !is_array($result['state']['meta'])) {
                $result['state']['meta'] = array();
            }
            $result['state']['context']['research_snapshot'] = $research_snapshot;
            if (!empty($execution_meta['module'])) {
                $result['state']['meta']['active_research_module'] = (string) $execution_meta['module'];
            }
            if (!empty($execution_meta['state_meta']) && is_array($execution_meta['state_meta'])) {
                foreach ($execution_meta['state_meta'] as $meta_key => $meta_value) {
                    if (!is_string($meta_key) || $meta_key === '') {
                        continue;
                    }
                    $result['state']['meta'][$meta_key] = $meta_value;
                }
            }
            if ($execution_action === 'keyword_research_sync' && empty($result['state']['meta']['keyword_walkthrough_stage'])) {
                $result['state']['meta']['keyword_walkthrough_stage'] = 'ranking';
            }
        } else {
            $research_snapshot = $this->aysa_adjustments_get_research_snapshot($project_id, $user_id);
            if (isset($result['state']) && is_array($result['state'])) {
                if (!isset($result['state']['context']) || !is_array($result['state']['context'])) {
                    $result['state']['context'] = array();
                }
                $result['state']['context']['research_snapshot'] = $research_snapshot;
            }
        }
        $result['research_snapshot'] = $research_snapshot;
        $result['workspace_artifact'] = $this->aysa_adjustments_build_research_workspace_artifact($research_snapshot, $conversation_language);

        if (isset($result['state']['context']['local_setup_state']) && is_array($result['state']['context']['local_setup_state'])) {
            $this->aysa_persist_local_setup_state_snapshot($user_id, $result['state']['context']['local_setup_state'], $project_id);
        }

        return array(
            'result' => $result,
            'access' => $key_validation,
        );
    }

    private function aysa_execute_monitoring_conversation($input) {
        if (!$this->check_authentication(false)) {
            return new WP_Error('auth_required', 'Authentication required.');
        }

        $key_validation = $this->aysa_validate_test_access_key(
            $this->aysa_get_effective_access_key(isset($input['access_key']) ? sanitize_text_field(wp_unslash((string) $input['access_key'])) : ''),
            isset($input['site_host']) ? sanitize_text_field(wp_unslash((string) $input['site_host'])) : ''
        );
        if (empty($key_validation['valid'])) {
            return new WP_Error('access_key_invalid', (string) ($key_validation['message'] ?? 'Access key is invalid.'));
        }

        $mode = isset($input['mode']) ? sanitize_key(wp_unslash((string) $input['mode'])) : 'bootstrap';
        $conversation_language = isset($input['conversation_language']) ? sanitize_key(wp_unslash((string) $input['conversation_language'])) : 'en';
        if (!in_array($conversation_language, array('en', 'ro', 'bg'), true)) {
            $conversation_language = 'en';
        }
        $project_id = isset($input['project_id']) ? intval(wp_unslash((string) $input['project_id'])) : intval(get_option('aysa_project_id', 0));
        $user_message = isset($input['user_message']) ? sanitize_textarea_field(wp_unslash((string) $input['user_message'])) : '';
        if ($project_id <= 0) {
            return new WP_Error('missing_project', 'Missing project ID.');
        }

        $auth_manager = Aysa_Auth_Manager::get_instance();
        $auth_status = $auth_manager->is_authenticated();
        $user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : 0;
        if ($user_id <= 0) {
            return new WP_Error('invalid_auth_context', 'Invalid authentication context.');
        }

        $client_state = array();
        if (isset($input['client_state'])) {
            $decoded_client_state = json_decode(wp_unslash((string) $input['client_state']), true);
            if (is_array($decoded_client_state)) {
                $client_state = $decoded_client_state;
            }
        }

        $local_setup_state = $this->aysa_get_local_setup_state_for_user($user_id, $project_id);
        $general = isset($local_setup_state['general']) && is_array($local_setup_state['general']) ? $local_setup_state['general'] : array();
        $website = isset($general['website']) ? $this->aysa_setup_normalize_url((string) $general['website']) : '';
        if ($website === '') {
            $website = $this->aysa_setup_normalize_url(home_url('/'));
        }

        $monitoring_state = array(
            'tracked_count' => intval($client_state['tracked_count'] ?? 0),
            'refreshed_count' => intval($client_state['refreshed_count'] ?? 0),
            'stale_count' => intval($client_state['stale_count'] ?? 0),
            'stale_preview' => isset($client_state['stale_preview']) && is_array($client_state['stale_preview']) ? $client_state['stale_preview'] : array(),
            'job_status' => sanitize_text_field((string) ($client_state['job_status'] ?? 'idle')),
            'job_processed' => intval($client_state['job_processed'] ?? 0),
            'job_total' => intval($client_state['job_total'] ?? 0),
            'wallet_balance' => isset($client_state['wallet_balance']) ? intval($client_state['wallet_balance']) : null,
            'wallet_spendable' => isset($client_state['wallet_spendable']) ? intval($client_state['wallet_spendable']) : null,
            'wallet_reserved_links' => isset($client_state['wallet_reserved_links']) ? intval($client_state['wallet_reserved_links']) : null,
        );

        $endpoint = '/v1/conversation/monitoring/bootstrap';
        $payload = array(
            'project_id' => $project_id,
            'conversation_language' => $conversation_language,
            'reset_session' => !empty($input['reset_session']),
            'site_host' => isset($input['site_host']) ? sanitize_text_field(wp_unslash((string) $input['site_host'])) : '',
            'website' => $website,
            'local_setup_state' => $local_setup_state,
            'ui_context' => array(
                'conversation_language' => $conversation_language,
                'available_languages' => array('en', 'ro', 'bg'),
                'page' => 'monitoring_analytics',
            ),
            'state' => array(
                'monitoring' => $monitoring_state,
            ),
        );
        if ($mode === 'message') {
            $endpoint = '/v1/conversation/monitoring/message';
            $payload['user_message'] = $user_message;
        }

        $result = $this->aysa_broker_request('POST', $endpoint, $payload, 45);
        if (is_wp_error($result)) {
            return $result;
        }
        if (!is_array($result) || empty($result['ok'])) {
            return new WP_Error('monitoring_conversation_failed', isset($result['error']) ? (string) $result['error'] : 'Monitoring conversation request failed.');
        }

        if ($mode === 'message') {
            $this->aysa_wallet_bill_provider_usage(
                $project_id,
                $endpoint,
                isset($result['usage']) && is_array($result['usage']) ? $result['usage'] : array(),
                'monitoring_conversation_message',
                array(
                    'module' => 'monitoring_conversation',
                    'source' => 'chat_message',
                )
            );
        }

        return array(
            'result' => $result,
            'access' => $key_validation,
        );
    }

    private function aysa_execute_activation_conversation($input) {
        $conversation_language = isset($input['conversation_language']) ? sanitize_key(wp_unslash((string) $input['conversation_language'])) : 'en';
        if (!in_array($conversation_language, array('en', 'ro', 'bg'), true)) {
            $conversation_language = 'en';
        }
        $user_message = isset($input['user_message']) ? sanitize_textarea_field(wp_unslash((string) $input['user_message'])) : '';
        $site_host = isset($input['site_host']) ? sanitize_text_field(wp_unslash((string) $input['site_host'])) : '';

        $access_key_candidate = trim($user_message);
        if ($access_key_candidate !== '') {
            $validation = $this->aysa_validate_test_access_key($access_key_candidate, $site_host);
            if (!empty($validation['valid'])) {
                update_option($this->aysa_test_access_key_option_name(), $access_key_candidate, false);

                if (!empty($validation['project_id'])) {
                    update_option('aysa_project_id', intval($validation['project_id']), false);
                }
                if (!empty($validation['website_id'])) {
                    update_option('aysa_website_id', intval($validation['website_id']), false);
                }

                $auth_bootstrap = null;
                if (!class_exists('Aysa_Auth_Manager')) {
                    require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
                }
                if (class_exists('Aysa_Auth_Manager')) {
                    $auth_manager = Aysa_Auth_Manager::get_instance();
                    $auth_bootstrap = $auth_manager->verify_and_store_secret_key($access_key_candidate, $site_host);
                    if (is_array($auth_bootstrap) && !empty($auth_bootstrap['success'])) {
                        update_option('aysa_user_secret_key', $access_key_candidate, false);
                        update_option('aysa_user_id', intval($auth_bootstrap['aysa_user_id'] ?? 0), false);
                        update_option('aysa_auth_domain', $site_host !== '' ? $site_host : wp_parse_url(home_url(), PHP_URL_HOST), false);
                    }
                }

                if (!empty($validation['plan_code'])) {
                    update_option('aysa_wallet_default_plan', sanitize_key((string) $validation['plan_code']), false);
                    if (class_exists('Aysa_Wallet_Manager')) {
                        $project_id = intval(get_option('aysa_project_id', 0));
                        if ($project_id > 0) {
                            Aysa_Wallet_Manager::get_instance()->apply_plan_to_project(
                                $project_id,
                                sanitize_key((string) $validation['plan_code']),
                                false
                            );
                        }
                    }
                }

                if (is_array($auth_bootstrap) && empty($auth_bootstrap['success'])) {
                    $validation['auth_bootstrap_warning'] = (string) ($auth_bootstrap['error'] ?? 'Authentication bootstrap failed after access key verification.');
                }

                $success_message = match ($conversation_language) {
                    'ro' => 'Cheia a fost validată. Mulțumim. Activez pluginul și continuăm automat.',
                    'bg' => 'Ключът е валидиран. Благодаря. Активирам плъгина и продължаваме автоматично.',
                    default => 'The key has been validated. Thank you. I am activating the plugin and we will continue automatically.',
                };

                return array(
                    'ok' => true,
                    'assistant_message' => $success_message,
                    'activation_verified' => true,
                    'redirect_url' => admin_url('admin.php?page=aysa'),
                    'validation' => $validation,
                );
            }
        }

        $result = $this->aysa_broker_request('POST', '/v1/conversation/activation/message', array(
            'conversation_language' => $conversation_language,
            'user_message' => $user_message,
            'site_host' => $site_host,
            'activation_url' => 'https://app.aysa.ai/websites',
        ), 45);

        if (is_wp_error($result)) {
            return $result;
        }
        if (!is_array($result) || empty($result['ok'])) {
            return new WP_Error('activation_conversation_failed', isset($result['error']) ? (string) $result['error'] : 'Activation conversation request failed.');
        }

        return $result;
    }

    private function aysa_execute_agent8_conversation($input) {
        $request_started_at = microtime(true);
        $bundle_build_ms = 0.0;
        $broker_request_ms = 0.0;
        $local_persistence_ms = 0.0;
        $payload_size_bytes = 0;
        $execution_path = isset($input['_agent8_execution_path'])
            ? sanitize_key(wp_unslash((string) $input['_agent8_execution_path']))
            : 'async';
        if (!in_array($execution_path, array('sync', 'async'), true)) {
            $execution_path = 'async';
        }

        $mode = isset($input['mode']) ? sanitize_key(wp_unslash((string) $input['mode'])) : 'bootstrap';
        $requested_mode = $mode;
        $conversation_language = $this->aysa_agent8_normalize_conversation_language(
            isset($input['conversation_language']) ? wp_unslash((string) $input['conversation_language']) : '',
            'en'
        );

        $wp_project_id = intval(get_option('aysa_project_id', 0));
        $posted_project_id = isset($input['project_id']) ? intval(wp_unslash((string) $input['project_id'])) : 0;
        $site_host = isset($input['site_host']) ? sanitize_text_field(wp_unslash((string) $input['site_host'])) : '';
        $user_message = isset($input['user_message']) ? sanitize_textarea_field(wp_unslash((string) $input['user_message'])) : '';
        $client_state = array();
        if (isset($input['client_state'])) {
            $decoded_client_state = json_decode(wp_unslash((string) $input['client_state']), true);
            if (is_array($decoded_client_state)) {
                $client_state = $decoded_client_state;
            }
        }
        $wp_user_id = intval($input['wp_user_id'] ?? get_current_user_id());
        $auth_manager = class_exists('Aysa_Auth_Manager') ? Aysa_Auth_Manager::get_instance() : null;
        $auth_status = is_object($auth_manager) && method_exists($auth_manager, 'is_authenticated')
            ? $auth_manager->is_authenticated()
            : array();
        $setup_user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : 0;
        if ($setup_user_id <= 0) {
            $setup_user_id = intval(get_option('aysa_user_id', 0));
        }
        if ($setup_user_id <= 0) {
            $setup_user_id = intval($this->get_user_id());
        }
        if ($setup_user_id <= 0) {
            $setup_user_id = intval($this->aysa_get_authenticated_external_user_id());
        }

        $key_validation = null;
        $has_valid_access_key = $this->aysa_is_access_key_valid_globally($key_validation);
        $canonical_project_id = !empty($has_valid_access_key)
            ? $this->aysa_resolve_agent8_canonical_project_id($posted_project_id > 0 ? $posted_project_id : $wp_project_id, $site_host, is_array($key_validation) ? $key_validation : array())
            : 0;
        $agent_project_id = !empty($has_valid_access_key)
            ? ($canonical_project_id > 0 ? $canonical_project_id : ($posted_project_id > 0 ? $posted_project_id : $wp_project_id))
            : 0;
        $saved_session = $this->aysa_agent8_load_session($wp_user_id, $site_host, $conversation_language);
        $request_epoch = max(0, intval($input['request_epoch'] ?? 0));
        $saved_request_epoch = max(0, intval($saved_session['request_epoch'] ?? 0));
        if ($request_epoch > $saved_request_epoch) {
            $saved_session['request_epoch'] = $request_epoch;
            update_user_meta($wp_user_id, $this->aysa_agent8_storage_key($wp_user_id, $site_host), $saved_session);
        }
        $wallet_snapshot = !empty($has_valid_access_key)
            ? $this->aysa_build_agent_wallet_snapshot($agent_project_id)
            : array();

        if ($mode === 'reset') {
            $this->aysa_agent8_clear_session($wp_user_id, $site_host);
            $client_state = array();
            $saved_session = array();
            $mode = 'bootstrap';
            $input['force_fresh_bootstrap'] = true;
            $input['conversation_id'] = '';
        }

        if ($mode === 'bootstrap') {
            $client_state = array();
        }

        $website = $this->aysa_setup_normalize_url(home_url('/'));
        $payload = array(
            'project_id' => $agent_project_id,
            'canonical_project_id' => $agent_project_id,
            'wp_project_id' => $wp_project_id,
            'conversation_language' => $conversation_language,
            'site_host' => $site_host,
            'website' => $website,
            'wp_user_id' => $wp_user_id,
            'aysa_user_id' => $setup_user_id,
            'return_url' => admin_url('admin.php?page=aysa-agent8'),
            'agent8_url' => admin_url('admin.php?page=aysa-agent8'),
            'email_to' => $this->get_notification_target_email(),
            'access_key_validation' => array(
                'valid' => !empty($has_valid_access_key),
                'project_id' => intval($key_validation['project_id'] ?? 0),
                'website_id' => intval($key_validation['website_id'] ?? 0),
                'canonical_project_id' => intval($canonical_project_id),
                'plan_code' => (string) ($key_validation['plan_code'] ?? ''),
            ),
        );
        if (!empty($input['force_fresh_bootstrap'])) {
            $payload['force_fresh_bootstrap'] = true;
        }
        $force_fresh_bootstrap = !empty($input['force_fresh_bootstrap']);
        $saved_conversation_id = $force_fresh_bootstrap ? '' : (isset($saved_session['conversation_id']) ? trim((string) $saved_session['conversation_id']) : '');
        $posted_conversation_id = $force_fresh_bootstrap ? '' : (isset($input['conversation_id']) ? sanitize_text_field(wp_unslash((string) $input['conversation_id'])) : '');
        $conversation_id = $posted_conversation_id !== '' ? $posted_conversation_id : $saved_conversation_id;
        if ($conversation_id !== '') {
            $payload['conversation_id'] = $conversation_id;
        }
        if ($mode === 'message') {
            $payload['user_message'] = $user_message;
            if ($client_state !== array()) {
                $payload['client_state'] = $client_state;
            }
        } elseif ($mode === 'action' || $mode === 'approval') {
            $payload['action_key'] = isset($input['action_key']) ? sanitize_key(wp_unslash((string) $input['action_key'])) : '';
            if ($mode === 'approval') {
                $payload['approved'] = !empty($input['approved']);
            }
            if (isset($input['action_payload'])) {
                $raw_action_payload = wp_unslash($input['action_payload']);
                if (is_string($raw_action_payload) && $raw_action_payload !== '') {
                    $decoded_action_payload = json_decode($raw_action_payload, true);
                    if (is_array($decoded_action_payload)) {
                        $payload['action_payload'] = $decoded_action_payload;
                    }
                } elseif (is_array($raw_action_payload)) {
                    $payload['action_payload'] = $raw_action_payload;
                }
            }
        }

        if ($mode !== 'bootstrap' && !empty($has_valid_access_key)) {
            $action_key = isset($payload['action_key']) ? (string) $payload['action_key'] : '';
            $bundle_started_at = microtime(true);
            $payload = array_merge($payload, $this->aysa_agent8_runtime_bundle_for_request($wp_project_id, $mode, $user_message, $action_key));
            $bundle_build_ms = (microtime(true) - $bundle_started_at) * 1000;
        }
        $payload_size_bytes = strlen((string) wp_json_encode($payload));

        $bundle_action_key = isset($payload['action_key']) ? sanitize_key((string) $payload['action_key']) : '';
        if ($mode === 'action' && $this->aysa_adjustments_is_site_audit_bundle_action_key($bundle_action_key)) {
            $approval_request = $this->aysa_adjustments_build_site_audit_bundle_approval_request(
                $bundle_action_key,
                isset($payload['action_payload']) && is_array($payload['action_payload']) ? $payload['action_payload'] : array()
            );
            if (is_array($approval_request)) {
                if ($wp_project_id > 0) {
                    $this->aysa_adjustments_update_site_audit_action_bundle_state($wp_project_id, $bundle_action_key, array(
                        'approval_state' => 'suggested',
                        'last_sent_to_agent8_at' => current_time('mysql'),
                    ));
                }

                $response_result = array(
                    'ok' => true,
                    'state' => isset($saved_session['state']) && is_array($saved_session['state']) ? $saved_session['state'] : array(),
                    'conversation_id' => $conversation_id,
                    'canonical_project_id' => intval($agent_project_id),
                    'wp_project_id' => intval($wp_project_id),
                    'phase' => 'conversation',
                    'current_step' => 'technical_site_audit_bundle_approval',
                    'owner' => 'conversation',
                    'capability_level' => 'Conversational',
                    'agent_version' => '0.7.9',
                    'messages' => array(
                        array(
                            'role' => 'assistant',
                            'message' => 'Am pregătit bundle-ul tehnic și aștept acordul tău explicit înainte să rulez pregătirea în Agent8.',
                        ),
                    ),
                    'assistant_message' => 'Am pregătit bundle-ul tehnic și aștept acordul tău explicit înainte să rulez pregătirea în Agent8.',
                    'suggested_actions' => array(),
                    'approval_request' => $approval_request,
                    'proposed_action' => null,
                    'action_result' => null,
                    'billing' => null,
                    'needs_approval' => true,
                    'form_projection' => null,
                    'activity_feed' => array(),
                    'contract_status' => '',
                    'contract_issue_code' => '',
                    'contract_issue_details' => array(),
                );

                $response_result = $this->aysa_agent8_attach_plugin_timings(
                    $response_result,
                    $execution_path,
                    $bundle_build_ms,
                    $broker_request_ms,
                    $local_persistence_ms,
                    $request_started_at,
                    $payload_size_bytes
                );
                $this->aysa_agent8_save_session($wp_user_id, $site_host, $conversation_language, $response_result, $request_epoch);
                return array(
                    'result' => $response_result,
                    'wallet_snapshot' => $wallet_snapshot,
                );
            }
        }

        if ($mode === 'approval' && $this->aysa_adjustments_is_site_audit_bundle_action_key($bundle_action_key)) {
            if (empty($payload['approved'])) {
                if ($wp_project_id > 0) {
                    $this->aysa_adjustments_update_site_audit_action_bundle_state($wp_project_id, $bundle_action_key, array(
                        'approval_state' => 'dismissed',
                        'dismissed_at' => current_time('mysql'),
                    ));
                }
                $response_result = array(
                    'ok' => true,
                    'state' => isset($saved_session['state']) && is_array($saved_session['state']) ? $saved_session['state'] : array(),
                    'conversation_id' => $conversation_id,
                    'canonical_project_id' => intval($agent_project_id),
                    'wp_project_id' => intval($wp_project_id),
                    'phase' => 'conversation',
                    'current_step' => 'technical_site_audit',
                    'owner' => 'conversation',
                    'capability_level' => 'Conversational',
                    'agent_version' => '0.7.9',
                    'messages' => array(
                        array(
                            'role' => 'assistant',
                            'message' => 'Am oprit bundle-ul tehnic pentru moment. Îl poți relansa oricând din Site Audit sau Agent8.',
                        ),
                    ),
                    'assistant_message' => 'Am oprit bundle-ul tehnic pentru moment. Îl poți relansa oricând din Site Audit sau Agent8.',
                    'suggested_actions' => array(),
                    'approval_request' => null,
                    'proposed_action' => null,
                    'action_result' => null,
                    'billing' => null,
                    'needs_approval' => false,
                    'form_projection' => null,
                    'activity_feed' => array(),
                    'contract_status' => '',
                    'contract_issue_code' => '',
                    'contract_issue_details' => array(),
                );

                $response_result = $this->aysa_agent8_attach_plugin_timings(
                    $response_result,
                    $execution_path,
                    $bundle_build_ms,
                    $broker_request_ms,
                    $local_persistence_ms,
                    $request_started_at,
                    $payload_size_bytes
                );
                $this->aysa_agent8_save_session($wp_user_id, $site_host, $conversation_language, $response_result, $request_epoch);
                return array(
                    'result' => $response_result,
                    'wallet_snapshot' => $wallet_snapshot,
                );
            }

            $mode = 'action';
        }

        $endpoint = match ($mode) {
            'bootstrap' => '/v1/agent8/bootstrap',
            'approval' => '/v1/agent8/approval',
            'action' => '/v1/agent8/action',
            default => '/v1/agent8/message',
        };
        $broker_started_at = microtime(true);
        $result = $this->aysa_broker_request('POST', $endpoint, $payload, 75);
        $broker_request_ms = (microtime(true) - $broker_started_at) * 1000;
        if (is_wp_error($result)) {
            return $result;
        }
        if (!is_array($result) || empty($result['ok'])) {
            return new WP_Error('agent8_request_failed', isset($result['error']) ? (string) $result['error'] : 'Agent 8 request failed.');
        }

        $local_persistence_started_at = microtime(true);

        if (
            isset($result['action_result']['activation_validation'])
            && is_array($result['action_result']['activation_validation'])
            && !empty($result['action_result']['activation_validation']['valid'])
            && !empty($result['action_result']['raw_access_key'])
        ) {
            $this->aysa_store_verified_access_key(
                $result['action_result']['activation_validation'],
                (string) $result['action_result']['raw_access_key'],
                $site_host
            );
            if ($setup_user_id > 0 || $wp_project_id > 0) {
                $this->aysa_reset_local_setup_state($setup_user_id, $wp_project_id);
            }
        }

        if (
            $setup_user_id > 0
            && isset($result['state']['context']['local_setup_state'])
            && is_array($result['state']['context']['local_setup_state'])
        ) {
            $this->aysa_persist_local_setup_state_snapshot($setup_user_id, $result['state']['context']['local_setup_state'], $wp_project_id);
        }

        $canonical_project_state_patch = isset($result['canonical_project_state_patch']) && is_array($result['canonical_project_state_patch'])
            ? $result['canonical_project_state_patch']
            : array();
        if ($canonical_project_state_patch !== array() && $wp_project_id > 0) {
            $result['canonical_project_state_persistence'] = $this->aysa_agent8_persist_canonical_project_state_patch($wp_project_id, $canonical_project_state_patch);
        }

        $executed_action_key = isset($payload['action_key']) ? sanitize_key((string) $payload['action_key']) : '';
        if ($wp_project_id > 0 && $this->aysa_adjustments_is_site_audit_bundle_action_key($executed_action_key)) {
            if ($requested_mode === 'action' && isset($result['approval_request']) && is_array($result['approval_request'])) {
                $this->aysa_adjustments_update_site_audit_action_bundle_state($wp_project_id, $executed_action_key, array(
                    'approval_state' => 'suggested',
                    'last_sent_to_agent8_at' => current_time('mysql'),
                ));
            } elseif ($requested_mode === 'approval') {
                if (empty($payload['approved'])) {
                    $this->aysa_adjustments_update_site_audit_action_bundle_state($wp_project_id, $executed_action_key, array(
                        'approval_state' => 'dismissed',
                        'dismissed_at' => current_time('mysql'),
                    ));
                } else {
                    $bundle_changes = array(
                        'approval_state' => 'approved',
                        'approved_at' => current_time('mysql'),
                    );
                    $action_result = isset($result['action_result']) && is_array($result['action_result']) ? $result['action_result'] : array();
                    $action_summary = isset($action_result['summary']) && is_array($action_result['summary']) ? $action_result['summary'] : array();
                    $action_execution = isset($action_result['workspace_snapshot']['execution']) && is_array($action_result['workspace_snapshot']['execution'])
                        ? $action_result['workspace_snapshot']['execution']
                        : array();
                    $prepared_summary = '';
                    if (!empty($action_summary)) {
                        $summary_map = $action_summary;
                        $prepared_summary = sanitize_text_field((string) ($summary_map['next_step'] ?? $summary_map['summary'] ?? $summary_map['bundle'] ?? ''));
                    }
                    if ($prepared_summary === '' && isset($result['summary']) && is_array($result['summary'])) {
                        $summary_map = $result['summary'];
                        $prepared_summary = sanitize_text_field((string) ($summary_map['next_step'] ?? $summary_map['summary'] ?? $summary_map['bundle'] ?? ''));
                    }
                    if (!empty($result['ok']) || !empty($result['action_result']['ok'])) {
                        $bundle_changes['approval_state'] = 'prepared';
                        $bundle_changes['last_prepared_at'] = current_time('mysql');
                        if (!empty($action_summary['execution_state'])) {
                            $bundle_changes['execution_state'] = sanitize_key((string) $action_summary['execution_state']);
                        } elseif (!empty($action_execution['execution_state'])) {
                            $bundle_changes['execution_state'] = sanitize_key((string) $action_execution['execution_state']);
                        }
                    if (!empty($action_summary['execution_package_id'])) {
                        $bundle_changes['execution_package_id'] = sanitize_text_field((string) $action_summary['execution_package_id']);
                    } elseif (!empty($action_execution['execution_package_id'])) {
                        $bundle_changes['execution_package_id'] = sanitize_text_field((string) $action_execution['execution_package_id']);
                    }
                    if ($executed_action_key === 'prepare_metadata_remediation' && isset($action_execution['metadata_execution_package']) && is_array($action_execution['metadata_execution_package'])) {
                        $bundle_changes['metadata_execution_package'] = $action_execution['metadata_execution_package'];
                    }
                    if ($executed_action_key === 'prepare_indexability_review' && isset($action_execution['canonical_execution_package']) && is_array($action_execution['canonical_execution_package'])) {
                        $bundle_changes['canonical_execution_package'] = $action_execution['canonical_execution_package'];
                    }
                    if ($executed_action_key === 'prepare_image_alt_text' && isset($action_execution['image_alt_execution_package']) && is_array($action_execution['image_alt_execution_package'])) {
                        $bundle_changes['image_alt_execution_package'] = $action_execution['image_alt_execution_package'];
                    }
                    if (isset($action_execution['prepare_result']) && is_array($action_execution['prepare_result'])) {
                        $bundle_changes['last_prepare_result'] = $action_execution['prepare_result'];
                    }
                        if ($prepared_summary !== '') {
                            $bundle_changes['last_prepared_summary'] = $prepared_summary;
                        }
                    }
                    $this->aysa_adjustments_update_site_audit_action_bundle_state($wp_project_id, $executed_action_key, $bundle_changes);
                }
            }
        }

        $response_result = array(
            'ok' => true,
            'state' => isset($result['state']) && is_array($result['state']) ? $result['state'] : array(),
            'conversation_id' => (string) ($result['conversation_id'] ?? ''),
            'canonical_project_id' => intval($result['canonical_project_id'] ?? $agent_project_id),
            'wp_project_id' => intval($wp_project_id),
            'phase' => (string) ($result['phase'] ?? 'conversation'),
            'current_step' => (string) ($result['current_step'] ?? 'conversation'),
            'module' => sanitize_key((string) ($result['module'] ?? '')),
            'tab' => sanitize_key((string) ($result['tab'] ?? '')),
            'owner' => 'conversation',
            'capability_level' => 'Conversational',
            'agent_version' => (string) ($result['agent_version'] ?? ''),
            'messages' => isset($result['messages']) && is_array($result['messages'])
                ? $result['messages']
                : $this->aysa_agent8_messages_from_state(isset($result['state']) && is_array($result['state']) ? $result['state'] : array()),
            'assistant_message' => (string) ($result['assistant_message'] ?? ''),
            'suggested_actions' => isset($result['suggested_actions']) && is_array($result['suggested_actions'])
                ? array_values($result['suggested_actions'])
                : array(),
            'persistent_actions' => isset($result['persistent_actions']) && is_array($result['persistent_actions'])
                ? array_values($result['persistent_actions'])
                : array(),
            'approval_request' => isset($result['approval_request']) && is_array($result['approval_request']) ? $result['approval_request'] : null,
            'proposed_action' => isset($result['proposed_action']) && is_array($result['proposed_action']) ? $result['proposed_action'] : null,
            'action_result' => isset($result['action_result']) && is_array($result['action_result']) ? $result['action_result'] : null,
            'billing' => isset($result['billing']) && is_array($result['billing']) ? $result['billing'] : null,
            'needs_approval' => false,
            'form_projection' => null,
            'activity_feed' => isset($result['action_result']['activity_feed']) && is_array($result['action_result']['activity_feed'])
                ? $result['action_result']['activity_feed']
                : array(),
            'canonical_project_state_patch' => array(),
            'canonical_project_state_persistence' => isset($result['canonical_project_state_persistence']) && is_array($result['canonical_project_state_persistence'])
                ? $result['canonical_project_state_persistence']
                : null,
            'product_map' => isset($result['product_map']) && is_array($result['product_map'])
                ? array_values($result['product_map'])
                : array(),
            'timings' => isset($result['timings']) && is_array($result['timings'])
                ? $result['timings']
                : array(),
            'contract_status' => sanitize_key((string) ($result['contract_status'] ?? '')),
            'contract_issue_code' => sanitize_key((string) ($result['contract_issue_code'] ?? '')),
            'contract_issue_details' => isset($result['contract_issue_details']) && is_array($result['contract_issue_details'])
                ? $result['contract_issue_details']
                : array(),
        );

        $local_mutations = isset($result['action_result']['local_mutations']) && is_array($result['action_result']['local_mutations'])
            ? $result['action_result']['local_mutations']
            : array();
        if (!empty($local_mutations)) {
            $response_result['local_mutation_result'] = $this->aysa_agent8_apply_local_mutations($wp_project_id, $local_mutations);
            $response_result = $this->aysa_agent8_refresh_action_result_after_local_mutations($response_result, $wp_project_id);
            if ($this->aysa_agent8_local_mutation_skips_billing($response_result['local_mutation_result'])) {
                $skip_reason = sanitize_key((string) ($response_result['local_mutation_result']['reason'] ?? $response_result['local_mutation_result']['code'] ?? 'already_done'));
                $skip_billing = array(
                    'status' => 'skipped',
                    'estimated_credits_cents' => 0,
                    'actual_credits_cents' => 0,
                    'reason' => $skip_reason,
                    'entries' => array(),
                );
                $result['billing'] = $skip_billing;
                if (isset($result['action_result']) && is_array($result['action_result'])) {
                    $result['action_result']['billing'] = $skip_billing;
                }
                if (isset($response_result['action_result']) && is_array($response_result['action_result'])) {
                    $response_result['action_result']['billing'] = $skip_billing;
                }
            } else {
                $local_billing = $this->aysa_agent8_bill_successful_local_mutation($wp_project_id, $result['action_result'] ?? array(), $response_result['local_mutation_result']);
                if ($local_billing !== array()) {
                    $result['billing'] = $local_billing;
                    if (isset($result['action_result']) && is_array($result['action_result'])) {
                        $result['action_result']['billing'] = $local_billing;
                    }
                    if (isset($response_result['action_result']) && is_array($response_result['action_result'])) {
                        $response_result['action_result']['billing'] = $local_billing;
                    }
                }
            }
        }

        $contract_validation = $this->aysa_agent8_validate_contract_response($response_result);
        if (!empty($contract_validation['status'])) {
            $response_result['contract_status'] = (string) $contract_validation['status'];
        }
        if (!empty($contract_validation['issue_code'])) {
            $response_result['contract_issue_code'] = (string) $contract_validation['issue_code'];
        }
        if (!empty($contract_validation['details']) && is_array($contract_validation['details'])) {
            $response_result['contract_issue_details'] = $contract_validation['details'];
        }
        if (($response_result['contract_status'] ?? '') === 'invalid') {
            $this->aysa_agent8_log_contract_issue($response_result);
            $response_result['suggested_actions'] = array();
            $response_result['approval_request'] = null;
        }

        $local_persistence_ms = (microtime(true) - $local_persistence_started_at) * 1000;
        $response_result = $this->aysa_agent8_attach_plugin_timings(
            $response_result,
            $execution_path,
            $bundle_build_ms,
            $broker_request_ms,
            $local_persistence_ms,
            $request_started_at,
            $payload_size_bytes
        );
        $response_result = $this->aysa_agent8_bridge_compact_payload_if_needed($response_result, array(
            'task' => 'agent8',
            'run_id' => '',
            'project_id' => $wp_project_id,
            'conversation_id' => (string) ($response_result['conversation_id'] ?? ''),
            'source' => 'execute_agent8_response',
        ));
        $this->aysa_agent8_save_session($wp_user_id, $site_host, $conversation_language, $response_result, $request_epoch);

        $resolved_wallet_project_id = intval($result['canonical_project_id'] ?? $agent_project_id);
        $resolved_binding_valid = !empty($has_valid_access_key)
            || !empty($result['action_result']['activation_validation']['valid'])
            || $resolved_wallet_project_id > 0;
        if ($resolved_binding_valid) {
            $wallet_snapshot = $this->aysa_build_agent_wallet_snapshot($resolved_wallet_project_id > 0 ? $resolved_wallet_project_id : $agent_project_id);
            $wallet_snapshot = $this->aysa_agent8_apply_billing_usage_hint($wallet_snapshot, isset($result['action_result']) && is_array($result['action_result']) ? $result['action_result'] : array());
            $wallet_snapshot = $this->aysa_agent8_apply_top_level_billing_usage_hint($wallet_snapshot, $result);
        } else {
            $wallet_snapshot = array();
        }

        return $this->aysa_agent8_bridge_compact_payload_if_needed(array(
            'result' => $response_result,
            'wallet_snapshot' => $wallet_snapshot,
        ), array(
            'task' => 'agent8',
            'run_id' => '',
            'project_id' => $wp_project_id,
            'conversation_id' => (string) ($response_result['conversation_id'] ?? ''),
            'source' => 'execute_agent8_return',
        ));
    }

    private function aysa_execute_agent9_conversation($input) {
        $mode = isset($input['mode']) ? sanitize_key(wp_unslash((string) $input['mode'])) : 'bootstrap';
        $conversation_language = $this->aysa_agent8_normalize_conversation_language(
            isset($input['conversation_language']) ? wp_unslash((string) $input['conversation_language']) : '',
            'en'
        );

        $wp_project_id = intval(get_option('aysa_project_id', 0));
        $posted_project_id = isset($input['project_id']) ? intval(wp_unslash((string) $input['project_id'])) : 0;
        $site_host = isset($input['site_host']) ? sanitize_text_field(wp_unslash((string) $input['site_host'])) : '';
        $user_message = isset($input['user_message']) ? sanitize_textarea_field(wp_unslash((string) $input['user_message'])) : '';
        $wp_user_id = intval($input['wp_user_id'] ?? get_current_user_id());

        $auth_manager = class_exists('Aysa_Auth_Manager') ? Aysa_Auth_Manager::get_instance() : null;
        $auth_status = is_object($auth_manager) && method_exists($auth_manager, 'is_authenticated')
            ? $auth_manager->is_authenticated()
            : array();
        $setup_user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : 0;
        if ($setup_user_id <= 0) {
            $setup_user_id = intval(get_option('aysa_user_id', 0));
        }
        if ($setup_user_id <= 0) {
            $setup_user_id = intval($this->get_user_id());
        }
        if ($setup_user_id <= 0) {
            $setup_user_id = intval($this->aysa_get_authenticated_external_user_id());
        }

        $saved_session = $this->aysa_agent9_load_session($wp_user_id, $site_host, $conversation_language);
        if ($mode === 'reset') {
            $this->aysa_agent9_clear_session($wp_user_id, $site_host);
            $saved_session = array();
            $mode = 'bootstrap';
            $input['conversation_id'] = '';
        }

        $website = $this->aysa_setup_normalize_url(home_url('/'));
        $agent_project_id = $posted_project_id > 0 ? $posted_project_id : $wp_project_id;
        $payload = array(
            'project_id' => $agent_project_id,
            'wp_project_id' => $wp_project_id,
            'conversation_language' => $conversation_language,
            'site_host' => $site_host,
            'website' => $website,
            'wp_user_id' => $wp_user_id,
            'aysa_user_id' => $setup_user_id,
            'migration_mode' => true,
        );

        $saved_conversation_id = isset($saved_session['conversation_id']) ? trim((string) $saved_session['conversation_id']) : '';
        $posted_conversation_id = isset($input['conversation_id']) ? sanitize_text_field(wp_unslash((string) $input['conversation_id'])) : '';
        $conversation_id = $posted_conversation_id !== '' ? $posted_conversation_id : $saved_conversation_id;
        if ($conversation_id !== '') {
            $payload['conversation_id'] = $conversation_id;
        }

        if ($setup_user_id > 0) {
            $payload['local_setup_state'] = $this->aysa_get_local_setup_state_for_user($setup_user_id, $wp_project_id);
        }

        if ($mode === 'message') {
            $payload['user_message'] = $user_message;
        } elseif ($mode === 'action' || $mode === 'approval') {
            $payload['action_key'] = isset($input['action_key']) ? sanitize_key(wp_unslash((string) $input['action_key'])) : '';
            if ($mode === 'approval') {
                $payload['approved'] = !empty($input['approved']);
            }
            if (isset($input['action_payload'])) {
                $raw_action_payload = wp_unslash($input['action_payload']);
                if (is_string($raw_action_payload) && $raw_action_payload !== '') {
                    $decoded_action_payload = json_decode($raw_action_payload, true);
                    if (is_array($decoded_action_payload)) {
                        $payload['action_payload'] = $decoded_action_payload;
                    }
                } elseif (is_array($raw_action_payload)) {
                    $payload['action_payload'] = $raw_action_payload;
                }
            }
        }

        $endpoint = match ($mode) {
            'bootstrap' => '/v1/agent9/bootstrap',
            'approval' => '/v1/agent9/approval',
            'action' => '/v1/agent9/action',
            default => '/v1/agent9/message',
        };
        $result = $this->aysa_broker_request('POST', $endpoint, $payload, 45);
        if (is_wp_error($result)) {
            return $result;
        }
        if (!is_array($result) || empty($result['ok'])) {
            return new WP_Error('agent9_request_failed', isset($result['error']) ? (string) $result['error'] : 'Agent 9 request failed.');
        }

        $this->aysa_agent9_save_session($wp_user_id, $site_host, $conversation_language, $result);

        return array(
            'result' => $result,
            'wallet_snapshot' => array(),
        );
    }

    private function aysa_agent8_attach_plugin_timings($response_result, $execution_path, $bundle_build_ms, $broker_request_ms, $local_persistence_ms, $request_started_at, $payload_size_bytes = 0) {
        if (!is_array($response_result)) {
            $response_result = array();
        }

        $existing_timings = isset($response_result['timings']) && is_array($response_result['timings'])
            ? $response_result['timings']
            : array();
        $existing_timings['plugin'] = array(
            'bundle_build_ms' => round(max(0, (float) $bundle_build_ms), 2),
            'broker_request_ms' => round(max(0, (float) $broker_request_ms), 2),
            'local_persistence_ms' => round(max(0, (float) $local_persistence_ms), 2),
            'payload_size_bytes' => max(0, intval($payload_size_bytes)),
            'total_plugin_ms' => round(max(0, (microtime(true) - (float) $request_started_at) * 1000), 2),
        );
        $response_result['timings'] = $existing_timings;
        $response_result['execution_path'] = in_array((string) $execution_path, array('sync', 'async'), true)
            ? (string) $execution_path
            : 'async';

        return $response_result;
    }

    public function display_monitoring_analytics_page() {
        if (!$this->check_authentication()) {
            $aysa_section_title = 'Monitoring & Analytics';
            include AYSA_PLUGIN_DIR . 'admin/partials/aysa-login-required.php';
            return;
        }
        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-monitoring-analytics.php';
    }
    
    public function display_agent8_page() {
        if (!$this->aysa_current_user_can_workspace()) {
            $aysa_section_title = 'Agent 8';
            include AYSA_PLUGIN_DIR . 'admin/partials/aysa-login-required.php';
            return;
        }

        $project_id = intval(get_option('aysa_project_id', 0));
        $site_host = (string) wp_parse_url(home_url('/'), PHP_URL_HOST);
        $site_host = is_string($site_host) ? strtolower($site_host) : '';
        $key_validation = null;
        $activation_valid = $this->aysa_is_access_key_valid_globally($key_validation);
        $aysa_agent8_canonical_project_id = !empty($activation_valid)
            ? $this->aysa_resolve_agent8_canonical_project_id($project_id, $site_host, is_array($key_validation) ? $key_validation : array())
            : 0;
        $aysa_agent8_wallet_snapshot = !empty($activation_valid)
            ? $this->aysa_build_agent_wallet_snapshot($aysa_agent8_canonical_project_id > 0 ? $aysa_agent8_canonical_project_id : $project_id)
            : array();
        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-agent8.php';
    }

    public function display_agent9_page() {
        if (!$this->aysa_current_user_can_workspace()) {
            $aysa_section_title = 'Agent 9';
            include AYSA_PLUGIN_DIR . 'admin/partials/aysa-login-required.php';
            return;
        }

        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-agent9.php';
    }

    public function display_agent8_v3_lab_page() {
        if (!$this->aysa_current_user_can_workspace()) {
            $aysa_section_title = 'Agent8 V3 Lab';
            include AYSA_PLUGIN_DIR . 'admin/partials/aysa-login-required.php';
            return;
        }

        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-agent8-v3-lab.php';
    }

    public function display_legacy_page() {
        if (!$this->check_authentication()) {
            $aysa_section_title = 'Legacy';
            include AYSA_PLUGIN_DIR . 'admin/partials/aysa-login-required.php';
            return;
        }

        $links = [
            ['label' => 'Setup SEO Profile', 'url' => admin_url('admin.php?page=aysa-setup-seo-profile')],
            ['label' => 'Dashboard', 'url' => admin_url('admin.php?page=aysa')],
            ['label' => 'Research & Analysis', 'url' => admin_url('admin.php?page=aysa-research')],
            ['label' => 'Technical Optimization', 'url' => admin_url('admin.php?page=aysa-technical-optimization')],
            ['label' => 'On-Page Optimization', 'url' => admin_url('admin.php?page=aysa-on-page-optimization')],
            ['label' => 'Off-Page Optimization', 'url' => admin_url('admin.php?page=aysa-off-page-optimization')],
            ['label' => 'Monitoring & Analytics', 'url' => admin_url('admin.php?page=aysa-monitoring-analytics')],
            ['label' => 'Settings', 'url' => admin_url('admin.php?page=aysa-settings')],
            ['label' => 'Welcome to aysa', 'url' => admin_url('admin.php?page=aysa-welcome')],
        ];
        ?>
        <div class="wrap">
            <h1>Legacy</h1>
            <p>All existing AYSA plugin areas remain available here while the main product surface moves into AYSA AI Agent.</p>
            <div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;max-width:1100px;margin-top:20px;">
                <?php foreach ($links as $link): ?>
                    <a href="<?php echo esc_url($link['url']); ?>" style="display:block;padding:16px 18px;background:#fff;border:1px solid #dcdcde;border-radius:10px;text-decoration:none;color:#1d2327;font-weight:600;">
                        <?php echo esc_html($link['label']); ?>
                    </a>
                <?php endforeach; ?>
            </div>
        </div>
        <?php
    }
    
    public function display_setup_seo_profile_page() {
        if (!$this->check_authentication()) {
            $aysa_section_title = 'Setup SEO Profile';
            include AYSA_PLUGIN_DIR . 'admin/partials/aysa-login-required.php';
            return;
        }
        $setup_completion_reason = array();
        $setup_completed_effectively = $this->aysa_is_setup_profile_completed_effectively($setup_completion_reason);
        if ($this->aysa_is_apprentice_mode_enabled() && $setup_completed_effectively) {
            $this->display_agent8_page();
            return;
        }
        $force_edit_setup = !empty($_GET['edit_setup']) ? true : false;
        if ($setup_completed_effectively && !$force_edit_setup) {
            wp_safe_redirect($this->aysa_last_research_tab_url());
            exit;
        }
        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-setup-wizard.php';
    }
    
    public function display_omnichannel_seo_page() {
        if (!$this->check_authentication()) {
            $aysa_section_title = 'Omnichannel SEO';
            include AYSA_PLUGIN_DIR . 'admin/partials/aysa-login-required.php';
            return;
        }
        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-omnichannel-seo.php';
    }
    
    public function display_settings_page() {
        if (!$this->check_authentication()) {
            $aysa_section_title = 'Settings';
            include AYSA_PLUGIN_DIR . 'admin/partials/aysa-login-required.php';
            return;
        }
        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-settings.php';
    }

    public function display_activation_page() {
        if (!$this->aysa_current_user_can_workspace()) {
            wp_die(esc_html__('Access denied. You do not have permission to access AYSA.', 'aysa-seo'));
        }
        $nonce = wp_create_nonce('aysa_nonce');
        $logo_url = 'https://aysa.ai/assets/aysa-ai-logo.png';
        $activation_language = 'en';
        $activation_copy = array(
            'ro' => array(
                'title' => 'AYSA AI SEO',
                'subtitle' => 'Activare plugin',
                'language' => 'Limbă',
                'intro' => 'Salut si multumim pentru interes. Ca sa putem continua, am nevoie de cheia secreta asociata website-ului curent.',
                'instructions' => 'Gasesti cheia in <a href="https://app.aysa.ai/websites" target="_blank" rel="noopener noreferrer">app.aysa.ai/websites</a>, in pagina website-ului asociat acestui domeniu. Cauta Secret Key, apoi da copy si lipeste cheia aici in chat.',
                'reminder' => 'Pana cand cheia este valida, ramanem la acest pas.',
                'placeholder' => 'Lipește aici cheia secretă...',
                'send' => 'Trimite cheia',
                'hint' => 'Nu consumam credite pana cand cheia nu este validata.',
                'where_title' => 'De unde iei cheia',
                'where_body' => 'Deschide <a href="https://app.aysa.ai/websites" target="_blank" rel="noopener noreferrer">app.aysa.ai/websites</a>, alege website-ul curent si copiaza Secret Key din pagina lui.',
                'continue_title' => 'Ce facem acum',
                'continue_body' => 'Lipeste cheia secreta in chat. Daca nu este corecta, iti voi cere din nou cheia corecta.',
                'offer_intro' => 'AYSA te ajuta sa pornesti SEO mai simplu: conectezi website-ul, activezi pluginul, apoi agentul te ghideaza si executa pasii importanti pentru SEO.',
                'offer_pricing' => 'Planurile pornesc de la Starter €29/luna, apoi Growth €69/luna si Scale €99/luna. Ai si o perioada de trial de 14 zile, cu €0 today.',
                'offer_benefit' => 'Pluginul te ajuta cu setup, conectare, monitorizare, prioritizarea paginilor importante si urmatorii pasi SEO, fara sa trebuiasca sa cauti singur prin toate sectiunile.',
                'offer_cta' => 'Cand esti gata, lipeste cheia secreta aici in chat ca sa poti incepe sa-l folosesti.',
                'empty' => 'Am nevoie de cheia secreta ca sa pot continua. Te rog copiaza Secret Key din app.aysa.ai si lipeste-o aici.',
                'invalid_prefix' => 'Cheia nu a fost validata.',
                'invalid_suffix' => 'Te rog verifica Secret Key-ul din app.aysa.ai, din zona Websites a website-ului curent, apoi lipeste-l din nou aici.',
                'success' => 'Cheia a fost validata. Multumim. Activez pluginul si continuam automat.',
                'redirect' => 'Redirectionez catre AYSA AI SEO...',
            ),
            'bg' => array(
                'title' => 'AYSA AI SEO',
                'subtitle' => 'Активиране на плъгина',
                'language' => 'Език',
                'intro' => 'Здравей и благодарим за интереса. За да продължим, ми трябва секретният ключ за текущия сайт.',
                'instructions' => 'Ще намериш ключа в <a href="https://app.aysa.ai/websites" target="_blank" rel="noopener noreferrer">app.aysa.ai/websites</a>, в страницата на сайта за този домейн. Намери Secret Key, копирай го и го постави тук в чата.',
                'reminder' => 'Докато ключът не е валиден, оставаме на тази стъпка.',
                'placeholder' => 'Постави секретния ключ тук...',
                'send' => 'Изпрати ключа',
                'hint' => 'Не се използват кредити, докато ключът не бъде валидиран.',
                'where_title' => 'Откъде да вземеш ключа',
                'where_body' => 'Отвори <a href="https://app.aysa.ai/websites" target="_blank" rel="noopener noreferrer">app.aysa.ai/websites</a>, избери текущия сайт и копирай Secret Key от страницата му.',
                'continue_title' => 'Какво следва',
                'continue_body' => 'Постави секретния ключ в чата. Ако не е правилен, ще поискам правилния ключ отново.',
                'offer_intro' => 'AYSA ти помага да започнеш SEO по-лесно: свързваш сайта, активираш плъгина, а после агентът те води и изпълнява важните SEO стъпки.',
                'offer_pricing' => 'Плановете започват от Starter €29/месец, след това Growth €69/месец и Scale €99/месец. Имаш и 14-дневен trial с €0 today.',
                'offer_benefit' => 'Плъгинът помага със setup, свързване, мониторинг, приоритизиране на важните страници и следващите SEO стъпки, без да търсиш сам през всички секции.',
                'offer_cta' => 'Когато си готов, постави секретния ключ тук в чата, за да започнеш да го използваш.',
                'empty' => 'Нужен ми е секретният ключ, за да продължа. Моля, копирай Secret Key от app.aysa.ai и го постави тук.',
                'invalid_prefix' => 'Ключът не беше валидиран.',
                'invalid_suffix' => 'Моля, провери Secret Key в app.aysa.ai, в секцията Websites за текущия сайт, и го постави отново тук.',
                'success' => 'Ключът е валидиран. Благодаря. Активирам плъгина и продължаваме автоматично.',
                'redirect' => 'Пренасочвам към AYSA AI SEO...',
            ),
            'en' => array(
                'title' => 'AYSA AI SEO',
                'subtitle' => 'Plugin activation',
                'intro' => 'Hello and thank you for your interest. To continue, I need the secret key associated with the current website.',
                'instructions' => 'You can find it in <a href="https://app.aysa.ai/websites" target="_blank" rel="noopener noreferrer">app.aysa.ai/websites</a>, inside the page for the website linked to this domain. Look for Secret Key, copy it, and paste it here in the chat.',
                'reminder' => 'Until the key is valid, we stay on this step.',
                'placeholder' => 'Paste the secret key here...',
                'send' => 'Send key',
                'hint' => 'No credits are consumed until the key is validated.',
                'language' => 'Language',
                'where_title' => 'Where to get the key',
                'where_body' => 'Open <a href="https://app.aysa.ai/websites" target="_blank" rel="noopener noreferrer">app.aysa.ai/websites</a>, select the current website, and copy the Secret Key from its page.',
                'continue_title' => 'What happens next',
                'continue_body' => 'Paste the secret key in the chat. If it is not correct, I will ask for the correct key again.',
                'offer_intro' => 'AYSA helps you start SEO in a simpler way: you connect the website, activate the plugin, and then the agent guides you and executes the important SEO steps.',
                'offer_pricing' => 'Plans start with Starter at €29/month, then Growth at €69/month and Scale at €99/month. You also get a 14-day trial with €0 today.',
                'offer_benefit' => 'The plugin helps with setup, connection, monitoring, prioritizing important pages, and the next SEO steps, without forcing you to search through every section yourself.',
                'offer_cta' => 'When you are ready, paste the secret key here in the chat so you can start using it.',
                'empty' => 'I need the secret key before I can continue. Please copy the Secret Key from app.aysa.ai and paste it here.',
                'invalid_prefix' => 'The key was not validated.',
                'invalid_suffix' => 'Please check the Secret Key in app.aysa.ai, in the Websites area for the current website, and paste it here again.',
                'success' => 'The key has been validated. Thank you. I am activating the plugin and we will continue automatically.',
                'redirect' => 'Redirecting to AYSA AI SEO...',
            ),
        );
        ?>
        <div class="wrap aysa-activation-agent-wrap">
            <style>
                .aysa-activation-agent-wrap{max-width:none;margin:18px 24px 0 0}
                .aysa-activation-agent-shell{width:min(1480px,calc(100vw - 200px));background:#fff;border:1px solid #e2e8f0;border-radius:32px;padding:20px 24px 24px;box-shadow:none}
                .aysa-activation-brand{display:flex;align-items:center;justify-content:space-between;gap:16px;margin-bottom:12px}
                .aysa-activation-brand img{display:block;height:42px;width:auto}
                .aysa-activation-brand-meta{display:flex;flex-direction:column;gap:4px}
                .aysa-activation-brand-meta .brand-kicker{font-size:12px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#1faa4b}
                .aysa-activation-agent-head{display:flex;justify-content:space-between;align-items:flex-start;gap:20px;margin-bottom:14px}
                .aysa-activation-agent-head h1{margin:0 0 6px;font-size:28px;line-height:1.05}
                .aysa-activation-agent-head p{margin:0;color:#5b6b84;font-size:15px}
                .aysa-activation-agent-toolbar{display:flex;align-items:center;gap:14px}
                .aysa-activation-agent-pill{display:inline-flex;align-items:center;border-radius:999px;padding:10px 16px;background:#eefbf2;border:1px solid #b6e6c2;color:#15803d;font-weight:700}
                .aysa-activation-language{display:flex;align-items:center;gap:8px;color:#475569;font-size:14px;font-weight:600}
                .aysa-activation-language select{border:1px solid #cbd5e1;border-radius:12px;padding:8px 12px;font-size:14px;background:#fff}
                .aysa-activation-agent-grid{display:grid;grid-template-columns:minmax(0,2.5fr) minmax(300px,.85fr);gap:22px;align-items:start}
                .aysa-activation-chat{border:1px solid #e7edf5;border-radius:26px;background:#fbfdff;display:flex;flex-direction:column;overflow:hidden}
                .aysa-activation-chat-log{display:flex;flex-direction:column;gap:12px;flex:1;min-height:180px;max-height:280px;overflow-y:auto;padding:22px 22px 10px}
                .aysa-activation-bubble{max-width:82%;padding:16px 18px;border-radius:18px;font-size:15px;line-height:1.65;white-space:pre-wrap}
                .aysa-activation-bubble.assistant{align-self:flex-start;background:#f4f7fb;border:1px solid #dde6f3;color:#14233b}
                .aysa-activation-bubble.user{align-self:flex-end;background:#1d4ed8;color:#fff}
                .aysa-activation-bubble.system-ok{align-self:flex-start;background:#effaf1;border:1px solid #b7e2c1;color:#166534}
                .aysa-activation-bubble.typing{display:inline-flex;align-items:center;gap:7px;background:#f4f7fb;border:1px solid #dde6f3;color:#64748b}
                .aysa-activation-bubble.typing span{display:block;width:8px;height:8px;border-radius:999px;background:#9fb0c8;animation:aysaActivationPulse 1.1s infinite ease-in-out}
                .aysa-activation-bubble.typing span:nth-child(2){animation-delay:.15s}
                .aysa-activation-bubble.typing span:nth-child(3){animation-delay:.3s}
                .aysa-activation-composer-wrap{padding:16px 22px 18px;border-top:1px solid #e7edf5;background:#fff;position:relative}
                .aysa-activation-composer{display:flex;gap:12px}
                .aysa-activation-composer textarea{flex:1;min-height:78px;border-radius:20px;border:1px solid #cbd5e1;padding:15px 17px;font-size:15px;line-height:1.55;resize:vertical;box-shadow:0 0 0 0 rgba(31,170,75,0)}
                .aysa-activation-composer-wrap.is-attention textarea{border-color:#1faa4b;box-shadow:0 0 0 4px rgba(31,170,75,.12);animation:aysaActivationGlow 1.5s ease-in-out infinite}
                .aysa-activation-composer button{align-self:flex-end;border:0;border-radius:16px;background:#1faa4b;color:#fff;padding:14px 20px;font-weight:700;cursor:pointer}
                .aysa-activation-composer button[disabled]{opacity:.6;cursor:default}
                .aysa-activation-note{margin-top:10px;color:#64748b;font-size:13px}
                .aysa-activation-sidecard{background:#fff;border:1px solid #e2e8f0;border-radius:24px;padding:24px}
                .aysa-activation-sidecard h3{margin:0 0 10px;font-size:18px}
                .aysa-activation-sidecard p{margin:0;color:#516277;line-height:1.65}
                .aysa-activation-sidecard + .aysa-activation-sidecard{margin-top:18px}
                @keyframes aysaActivationPulse{0%,80%,100%{transform:scale(.8);opacity:.45}40%{transform:scale(1);opacity:1}}
                @keyframes aysaActivationGlow{0%,100%{box-shadow:0 0 0 0 rgba(31,170,75,.12)}50%{box-shadow:0 0 0 6px rgba(31,170,75,.18)}}
                @media (max-width: 1280px){
                    .aysa-activation-agent-shell{width:calc(100vw - 190px)}
                    .aysa-activation-agent-grid{grid-template-columns:1fr}
                    .aysa-activation-chat-log{min-height:180px;max-height:260px}
                }
                @media (max-width: 960px){
                    .aysa-activation-agent-wrap{margin-right:12px}
                    .aysa-activation-agent-grid{grid-template-columns:1fr}
                    .aysa-activation-agent-shell{width:100%;padding:22px}
                    .aysa-activation-agent-head{flex-direction:column}
                    .aysa-activation-brand{margin-bottom:14px}
                    .aysa-activation-bubble{max-width:100%}
                    .aysa-activation-chat-log{min-height:160px;max-height:240px;padding:18px 18px 10px}
                    .aysa-activation-composer-wrap{padding:18px 22px 20px}
                }
            </style>
            <div class="aysa-activation-agent-shell">
                <div class="aysa-activation-brand">
                    <img src="<?php echo esc_url($logo_url); ?>" alt="AYSA AI SEO">
                    <div class="aysa-activation-brand-meta">
                        <span class="brand-kicker">AI SEO Workspace</span>
                    </div>
                </div>
                <div class="aysa-activation-agent-head">
                    <div>
                        <h1><?php echo esc_html($activation_copy[$activation_language]['title']); ?></h1>
                        <p><?php echo esc_html($activation_copy[$activation_language]['subtitle']); ?></p>
                    </div>
                    <div class="aysa-activation-agent-toolbar">
                        <label class="aysa-activation-language" for="aysa-activation-language">
                            <span id="aysa-activation-language-label"><?php echo esc_html($activation_copy[$activation_language]['language']); ?></span>
                            <select id="aysa-activation-language">
                                <option value="en" selected>English</option>
                                <option value="ro">Romanian</option>
                                <option value="bg">Bulgarian</option>
                            </select>
                        </label>
                        <span class="aysa-activation-agent-pill" id="aysa-activation-hint"><?php echo esc_html($activation_copy[$activation_language]['hint']); ?></span>
                    </div>
                </div>
                <div class="aysa-activation-agent-grid">
                    <section class="aysa-activation-chat">
                        <div class="aysa-activation-chat-log" id="aysa-activation-chat-log"></div>
                        <div class="aysa-activation-composer-wrap">
                            <form class="aysa-activation-composer" id="aysa-activation-chat-form">
                                <textarea id="aysa-activation-chat-input" autocomplete="off" placeholder="<?php echo esc_attr($activation_copy[$activation_language]['placeholder']); ?>"></textarea>
                                <button type="submit" id="aysa-activation-chat-send"><?php echo esc_html($activation_copy[$activation_language]['send']); ?></button>
                            </form>
                            <div class="aysa-activation-note" id="aysa-activation-status"><?php echo esc_html($activation_copy[$activation_language]['reminder']); ?></div>
                        </div>
                    </section>
                    <aside>
                        <div class="aysa-activation-sidecard" id="aysa-activation-sidecard-where">
                            <h3><?php echo esc_html($activation_copy[$activation_language]['where_title']); ?></h3>
                            <p><?php echo esc_html($activation_copy[$activation_language]['where_body']); ?></p>
                        </div>
                        <div class="aysa-activation-sidecard" id="aysa-activation-sidecard-next">
                            <h3><?php echo esc_html($activation_copy[$activation_language]['continue_title']); ?></h3>
                            <p><?php echo esc_html($activation_copy[$activation_language]['continue_body']); ?></p>
                        </div>
                    </aside>
                </div>
            </div>
        </div>
        <script>
        jQuery(function($){
            const ajaxUrl = <?php echo wp_json_encode(admin_url('admin-ajax.php')); ?>;
            const nonce = <?php echo wp_json_encode($nonce); ?>;
            const host = window.location.hostname || '';
            const copy = <?php echo wp_json_encode($activation_copy); ?>;
            const $log = $('#aysa-activation-chat-log');
            const $input = $('#aysa-activation-chat-input');
            const $send = $('#aysa-activation-chat-send');
            const $status = $('#aysa-activation-status');
            const $language = $('#aysa-activation-language');
            const $languageLabel = $('#aysa-activation-language-label');
            const $hint = $('#aysa-activation-hint');
            const $whereCard = $('#aysa-activation-sidecard-where');
            const $nextCard = $('#aysa-activation-sidecard-next');
            const $composerWrap = $('.aysa-activation-composer-wrap');
            let currentLanguage = 'en';
            let introTimeouts = [];
            let hasExplainedOffer = false;
            let activationRunId = '';
            let activationPollTimer = null;

            function escapeHtml(text) {
                return $('<div>').text(text || '').html();
            }

            function t() {
                return copy[currentLanguage] || copy.en;
            }

            function appendBubble(role, text) {
                const cls = role === 'user' ? 'user' : (role === 'system-ok' ? 'system-ok' : 'assistant');
                $log.append('<div class="aysa-activation-bubble ' + cls + '">' + escapeHtml(text) + '</div>');
                $log.scrollTop($log.prop('scrollHeight'));
            }

            function appendBubbleHtml(role, html) {
                const cls = role === 'user' ? 'user' : (role === 'system-ok' ? 'system-ok' : 'assistant');
                $log.append('<div class="aysa-activation-bubble ' + cls + '">' + html + '</div>');
                $log.scrollTop($log.prop('scrollHeight'));
            }

            function appendTypingBubble() {
                $log.append('<div class="aysa-activation-bubble typing" id="aysa-activation-typing"><span></span><span></span><span></span></div>');
                $log.scrollTop($log.prop('scrollHeight'));
            }

            function clearIntroTimers() {
                introTimeouts.forEach(function(id){ clearTimeout(id); });
                introTimeouts = [];
                $('#aysa-activation-typing').remove();
            }

            function appendIntro() {
                clearIntroTimers();
                appendTypingBubble();
                introTimeouts.push(setTimeout(function(){
                    $('#aysa-activation-typing').remove();
                    appendBubble('assistant', t().intro);
                    $composerWrap.addClass('is-attention');
                    $input.trigger('focus');
                }, 650));
                introTimeouts.push(setTimeout(function(){
                    appendBubbleHtml('assistant', t().instructions);
                }, 1150));
            }

            function renderStaticCopy() {
                document.title = t().title + ' ‹ Florarie Bragadiru - Florarie Online Bucuresti — WordPress';
                $('.aysa-activation-agent-head h1').text(t().title);
                $('.aysa-activation-agent-head p').text(t().subtitle);
                $languageLabel.text(t().language || 'Language');
                $hint.text(t().hint);
                $input.attr('placeholder', t().placeholder);
                $send.text(t().send);
                $status.text(t().reminder);
                $whereCard.find('h3').text(t().where_title);
                $whereCard.find('p').html(t().where_body);
                $nextCard.find('h3').text(t().continue_title);
                $nextCard.find('p').text(t().continue_body);
            }

            function setBusy(isBusy) {
                $send.prop('disabled', isBusy);
                $input.prop('disabled', isBusy);
            }

            function stopActivationPolling() {
                if (activationPollTimer) {
                    clearTimeout(activationPollTimer);
                    activationPollTimer = null;
                }
            }

            function handleActivationConversationResult(result) {
                const payload = result && result.result ? result.result : result;
                if (!payload || typeof payload !== 'object') {
                    appendBubble('assistant', t().invalid_prefix + ' ' + t().invalid_suffix);
                    $status.text(t().reminder);
                    $composerWrap.addClass('is-attention');
                    return;
                }
                const assistantMessage = String(payload.assistant_message || '').trim();
                if (assistantMessage) {
                    if (payload.activation_verified) {
                        appendBubble('system-ok', '✓ ' + assistantMessage);
                    } else {
                        appendBubble('assistant', assistantMessage);
                    }
                }
                if (payload.activation_verified) {
                    $status.text(t().redirect);
                    setTimeout(function(){
                        window.location.href = payload.redirect_url || <?php echo wp_json_encode(admin_url('admin.php?page=aysa')); ?>;
                    }, 900);
                    return;
                }
                $status.text(t().reminder);
                $composerWrap.addClass('is-attention');
            }

            function pollActivationConversationStatus(userMessage) {
                stopActivationPolling();
                activationPollTimer = setTimeout(function tick() {
                    $.ajax({
                        url: ajaxUrl,
                        type: 'POST',
                        dataType: 'json',
                        data: {
                            action: 'aysa_conversation_async_status',
                            task: 'activation',
                            nonce: nonce,
                            site_host: host,
                            conversation_language: currentLanguage,
                            user_message: userMessage || '',
                            run_id: activationRunId
                        },
                        success: function(resp) {
                            const data = resp && resp.success && resp.data ? resp.data : null;
                            const state = data && data.status ? String(data.status) : 'failed';
                            if (state === 'queued' || state === 'running') {
                                $status.text(state === 'queued' ? 'Queued...' : 'Running...');
                                activationPollTimer = setTimeout(tick, 1200);
                                return;
                            }
                            activationRunId = '';
                            if (state === 'completed' && data.result) {
                                handleActivationConversationResult(data.result);
                                return;
                            }
                            appendBubble('assistant', (data && data.error) ? String(data.error) : (t().invalid_prefix + ' ' + t().invalid_suffix));
                            $status.text(t().reminder);
                            $composerWrap.addClass('is-attention');
                        },
                        error: function() {
                            activationRunId = '';
                            appendBubble('assistant', t().invalid_prefix + ' ' + t().invalid_suffix);
                            $status.text(t().reminder);
                            $composerWrap.addClass('is-attention');
                        },
                        complete: function() {
                            if (!activationRunId) {
                                setBusy(false);
                                $input.trigger('focus');
                            }
                        }
                    });
                }, 1200);
            }

            renderStaticCopy();
            appendIntro();

            $language.on('change', function() {
                currentLanguage = $(this).val() || 'en';
                renderStaticCopy();
                if ($log.find('.aysa-activation-bubble.user').length === 0) {
                    $log.empty();
                    appendIntro();
                }
            });

            $input.on('keydown', function(e){
                if (e.key === 'Enter' && !e.shiftKey) {
                    e.preventDefault();
                    $('#aysa-activation-chat-form').trigger('submit');
                }
            });

            $('#aysa-activation-chat-form').on('submit', function(e){
                e.preventDefault();
                const key = ($input.val() || '').trim();
                if (!key) {
                    if (!hasExplainedOffer) {
                        appendBubble('assistant', t().offer_intro);
                        appendBubble('assistant', t().offer_pricing);
                        appendBubble('assistant', t().offer_benefit);
                        appendBubble('assistant', t().offer_cta);
                        hasExplainedOffer = true;
                    } else {
                        appendBubble('assistant', t().empty);
                    }
                    $composerWrap.addClass('is-attention');
                    $status.text(t().reminder);
                    return;
                }

                appendBubble('user', key);
                $composerWrap.removeClass('is-attention');
                $input.val('');
                setBusy(true);
                $status.text(t().reminder);

                $.ajax({
                    url: ajaxUrl,
                    type: 'POST',
                    dataType: 'json',
                    data: {
                        action: 'aysa_conversation_async_start',
                        task: 'activation',
                        nonce: nonce,
                        site_host: host,
                        conversation_language: currentLanguage,
                        user_message: key
                    },
                    success: function(resp) {
                        if (!resp || !resp.success || !resp.data) {
                            appendBubble('assistant', (resp && resp.data && resp.data.message) ? String(resp.data.message) : (t().invalid_prefix + ' ' + t().invalid_suffix));
                            $status.text(t().reminder);
                            $composerWrap.addClass('is-attention');
                            return;
                        }
                        activationRunId = String(resp.data.run_id || '');
                        const state = resp.data.status ? String(resp.data.status) : '';
                        if (state === 'completed' && resp.data.result) {
                            activationRunId = '';
                            handleActivationConversationResult(resp.data.result);
                            setBusy(false);
                            $input.trigger('focus');
                            return;
                        }
                        $status.text(state === 'queued' ? 'Queued...' : 'Running...');
                        pollActivationConversationStatus(key);
                    },
                    error: function() {
                        appendBubble('assistant', t().invalid_prefix + ' ' + t().invalid_suffix);
                        $status.text(t().reminder);
                        $composerWrap.addClass('is-attention');
                        setBusy(false);
                        $input.trigger('focus');
                    }
                });
            });
        });
        </script>
        <?php
    }
    
    public function display_automation_page() {
        if (!$this->check_authentication()) {
            $aysa_section_title = 'Automation';
            include AYSA_PLUGIN_DIR . 'admin/partials/aysa-login-required.php';
            return;
        }
        include_once AYSA_PLUGIN_DIR . 'admin/partials/automation/aysa-automation-dashboard.php';
    }
    
    // AJAX handler to load automation content for the tab in Adjustment & Improvement
    public function handle_load_automation_content() {
        // Verify nonce
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_automation_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        // Check authentication
        if (!$this->check_authentication()) {
            wp_send_json_error('Authentication required');
            return;
        }
        
        // Capture the automation dashboard content
        ob_start();
        include_once AYSA_PLUGIN_DIR . 'admin/partials/automation/aysa-automation-dashboard.php';
        $content = ob_get_clean();
        
        // Remove the wrap div and h1 title since we're embedding this
        $content = preg_replace('/<div class="wrap">.*?<h1>.*?<\/h1>/s', '', $content);
        $content = preg_replace('/<\/div>\s*$/', '', $content);
        
        wp_send_json_success($content);
    }
    
    // Function to display the Welcome to Aysa page
    public function display_welcome_aysa_page() {
        if (!$this->check_authentication()) {
            return;
        }
        include_once AYSA_PLUGIN_DIR . 'admin/partials/aysa-welcome-page.php';
    }


    /**
     * Get the authenticated user's ID
     * @return int|null User ID if authenticated, null otherwise
     */
    private function get_user_id() {
        if (isset($_COOKIE['aysa_user_id'])) {
            return (int) $_COOKIE['aysa_user_id'];
        }
        return null;
    }

    /**
     * Handle logout action
     */
    public function handle_logout() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }

        if (!is_user_logged_in()) {
            wp_send_json_error(array('message' => 'Authentication required.'));
            return;
        }

        // Clear cookies
        setcookie('aysa_auth', '', time() - 3600, '/');
        setcookie('aysa_user_id', '', time() - 3600, '/');
        
        wp_send_json_success(array('message' => 'Logged out successfully'));
        exit;
    }

    /**
     * Handle saving wizard data from AJAX requests
     */
    public function handle_save_wizard_data() {
        $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (empty($nonce) || (!wp_verify_nonce($nonce, 'aysa_wizard_nonce') && !wp_verify_nonce($nonce, 'aysa_nonce'))) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions'));
            return;
        }
        
        // Check if user is authenticated
        if (!$this->check_authentication(false)) {
            wp_send_json_error(array('message' => 'Authentication required'));
            return;
        }

        $auth_manager = Aysa_Auth_Manager::get_instance();
        $auth_status = $auth_manager->is_authenticated();
        
        // Get the current step and data
        $step = isset($_POST['step']) ? sanitize_text_field(wp_unslash((string) $_POST['step'])) : '';
        if (empty($step)) {
            wp_send_json_error(array('message' => 'Step is required'));
            return;
        }
        
        // Remove action and step from the data
        $data = wp_unslash($_POST);
        unset($data['action']);
        unset($data['step']);
        
        // Sanitize the data
        $sanitized_data = $this->sanitize_wizard_data($data);
        
        // Save the data for this step
        $user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : 0;
        if ($user_id <= 0) {
            wp_send_json_error(array('message' => 'Invalid authentication context'));
            return;
        }
        $option_name = 'aysa_wizard_' . $step . '_' . $user_id;
        update_option($option_name, $sanitized_data);
        
        // Return success
        wp_send_json_success(array('message' => 'Data saved successfully'));
    }

    /**
     * Save tone of voice from Setup SEO Profile via authenticated server-side API proxy.
     */
    public function handle_setup_save_tone_of_voice() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }

        if (!$this->aysa_current_user_can_workspace()) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }

        $key_validation = $this->aysa_validate_test_access_key(
            $this->aysa_get_effective_access_key(isset($_POST['access_key']) ? sanitize_text_field(wp_unslash($_POST['access_key'])) : ''),
            isset($_POST['site_host']) ? sanitize_text_field(wp_unslash($_POST['site_host'])) : ''
        );
        if (empty($key_validation['valid'])) {
            wp_send_json_error(array(
                'message' => (string) ($key_validation['message'] ?? 'Access key is invalid.'),
                'code' => 'access_key_invalid',
            ));
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : intval(get_option('aysa_project_id', 0));
        $selected_tone = isset($_POST['selected_tone']) ? sanitize_text_field(wp_unslash($_POST['selected_tone'])) : '';

        if ($project_id <= 0) {
            wp_send_json_error(array('message' => 'Missing project ID.'));
            return;
        }
        if ($selected_tone === '') {
            wp_send_json_error(array('message' => 'Missing tone selection.'));
            return;
        }

        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }

        try {
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $existing = $auth_manager->make_authenticated_request('api/ToneOfVoice/ByProject/' . $project_id, 'GET');

            $existing_id = 0;
            if (is_array($existing)) {
                if (!empty($existing['id'])) {
                    $existing_id = intval($existing['id']);
                } elseif (isset($existing['data']) && is_array($existing['data']) && !empty($existing['data']['id'])) {
                    $existing_id = intval($existing['data']['id']);
                }
            }

            $operation = 'create';
            $ok = false;
            if ($existing_id > 0) {
                $operation = 'update';
                $ok = $auth_manager->make_authenticated_request(
                    'api/ToneOfVoice/UpdateTone/' . $existing_id,
                    'PATCH',
                    array('selectedTone' => $selected_tone)
                );
            }

            if (!$ok) {
                $operation = 'create';
                $ok = $auth_manager->make_authenticated_request(
                    'api/ToneOfVoice',
                    'POST',
                    array(
                        'projectId' => $project_id,
                        'selectedTone' => $selected_tone,
                    )
                );
            }

            if (!$ok) {
                wp_send_json_error(array('message' => 'Could not save tone of voice via API.'));
                return;
            }

            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_setup_save_tone_of_voice',
                'setup_save_tone_of_voice',
                array(
                    'module' => 'setup_seo_profile',
                    'operation' => $operation,
                ),
                2,
                'api/ToneOfVoice'
            );

            wp_send_json_success(array(
                'message' => 'Tone of voice saved successfully.',
                'operation' => $operation,
                'access' => $key_validation,
            ));
        } catch (Exception $e) {
            wp_send_json_error(array('message' => 'Tone of voice save failed: ' . $e->getMessage()));
        }
    }

    /**
     * Generic setup wizard API proxy to keep browser calls inside WordPress/auth context.
     */
    public function handle_setup_api_proxy() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }

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

        if (!$this->check_authentication(false)) {
            wp_send_json_success(array(
                'ok' => true,
                'notifications' => array(),
                'pre_activation' => true,
            ));
            return;
        }

        $key_validation = $this->aysa_validate_test_access_key(
            $this->aysa_get_effective_access_key(isset($_POST['access_key']) ? sanitize_text_field(wp_unslash($_POST['access_key'])) : ''),
            isset($_POST['site_host']) ? sanitize_text_field(wp_unslash($_POST['site_host'])) : ''
        );
        if (empty($key_validation['valid'])) {
            wp_send_json_error(array(
                'message' => (string) ($key_validation['message'] ?? 'Access key is invalid.'),
                'code' => 'access_key_invalid',
            ));
            return;
        }

        $endpoint_raw = isset($_POST['endpoint']) ? (string) wp_unslash($_POST['endpoint']) : '';
        $method = strtoupper(isset($_POST['method']) ? sanitize_text_field(wp_unslash($_POST['method'])) : 'GET');
        $payload_raw = isset($_POST['payload']) ? wp_unslash($_POST['payload']) : '';

        if ($endpoint_raw === '') {
            wp_send_json_error(array('message' => 'Missing endpoint.'));
            return;
        }
        if (!in_array($method, array('GET', 'POST', 'PUT', 'PATCH', 'DELETE'), true)) {
            wp_send_json_error(array('message' => 'Invalid method.'));
            return;
        }

        $endpoint = trim($endpoint_raw);
        if (preg_match('#^https?://#i', $endpoint)) {
            $parts = wp_parse_url($endpoint);
            $path = isset($parts['path']) ? (string) $parts['path'] : '';
            $query = isset($parts['query']) ? (string) $parts['query'] : '';
            $endpoint = ltrim($path, '/');
            if ($query !== '') {
                $endpoint .= '?' . $query;
            }
        }
        $endpoint = ltrim($endpoint, '/');
        if (strpos($endpoint, 'api/') !== 0) {
            wp_send_json_error(array('message' => 'Blocked endpoint.'));
            return;
        }

        $payload = array();
        if ($payload_raw !== '') {
            if (is_array($payload_raw)) {
                $payload = $payload_raw;
            } elseif (is_string($payload_raw)) {
                $decoded = json_decode($payload_raw, true);
                if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
                    $payload = $decoded;
                } else {
                    $payload = array();
                    parse_str($payload_raw, $payload);
                    if (!is_array($payload)) {
                        $payload = array();
                    }
                }
            }
        }

        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }

        try {
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $response = $auth_manager->make_authenticated_request($endpoint, $method, $payload);
            if ($response === false) {
                wp_send_json_error(array('message' => 'API proxy request failed.'));
                return;
            }
            wp_send_json_success(array(
                'result' => $response,
                'access' => $key_validation,
            ));
        } catch (Exception $e) {
            wp_send_json_error(array('message' => 'API proxy exception: ' . $e->getMessage()));
        }
    }

    public function handle_setup_general_autofill() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }
        if (!$this->check_authentication(false)) {
            wp_send_json_success(array(
                'ok' => true,
                'notifications' => array(),
                'pre_activation' => true,
            ));
            return;
        }

        $key_validation = $this->aysa_validate_test_access_key(
            $this->aysa_get_effective_access_key(isset($_POST['access_key']) ? sanitize_text_field(wp_unslash($_POST['access_key'])) : ''),
            isset($_POST['site_host']) ? sanitize_text_field(wp_unslash($_POST['site_host'])) : ''
        );
        if (empty($key_validation['valid'])) {
            wp_send_json_error(array(
                'message' => (string) ($key_validation['message'] ?? 'Access key is invalid.'),
                'code' => 'access_key_invalid',
            ));
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : intval(get_option('aysa_project_id', 0));
        $force_generate_raw = isset($_POST['force_generate']) ? strtolower(trim((string) wp_unslash($_POST['force_generate']))) : '0';
        $force_generate = in_array($force_generate_raw, array('1', 'true', 'yes'), true);

        $requested_website = isset($_POST['website']) ? sanitize_text_field(wp_unslash($_POST['website'])) : home_url('/');
        $website = $this->aysa_setup_normalize_url($requested_website);
        if ($website === '') {
            wp_send_json_error(array('message' => 'Invalid website URL.'));
            return;
        }

        $auth_manager = Aysa_Auth_Manager::get_instance();
        $auth_status = $auth_manager->is_authenticated();
        $user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : 0;
        $general_option_name = ($user_id > 0) ? ('aysa_wizard_general_' . $user_id) : '';

        if (!$force_generate && $general_option_name !== '') {
            $saved = get_option($general_option_name, array());
            if (is_array($saved) && !empty($saved)) {
                $saved_data = array(
                    'id' => 0,
                    'companyName' => isset($saved['company_name']) ? (string) $saved['company_name'] : '',
                    'brand' => isset($saved['brand_name']) ? (string) $saved['brand_name'] : '',
                    'companyDescription' => isset($saved['company_description']) ? (string) $saved['company_description'] : '',
                    'supportPhoneNumber' => isset($saved['support_phone']) ? (string) $saved['support_phone'] : '',
                    'supportEmailAddress' => isset($saved['support_email']) ? (string) $saved['support_email'] : '',
                    'websiteUrl' => isset($saved['website']) ? (string) $saved['website'] : $website,
                    'websiteType' => isset($saved['website_type']) ? (string) $saved['website_type'] : '',
                    'shippingOptions' => isset($saved['shipping']) ? (string) $saved['shipping'] : '',
                    'paymentMethods' => isset($saved['payment_method']) ? (string) $saved['payment_method'] : '',
                    'industry' => isset($saved['industry']) ? (string) $saved['industry'] : '',
                    'businessInformation' => isset($saved['company_info']) ? (string) $saved['company_info'] : '',
                    'projectId' => $project_id,
                    'requestedWebsiteUrl' => $website,
                    'source' => 'setup_general_autofill_saved_wizard',
                    '_debug' => array(
                        'mode' => 'saved_wizard_data',
                        'force_generate' => false,
                    ),
                );

                $saved_value_count = 0;
                foreach (array('companyName', 'brand', 'companyDescription', 'supportPhoneNumber', 'supportEmailAddress', 'websiteUrl', 'websiteType', 'shippingOptions', 'paymentMethods', 'industry', 'businessInformation') as $key) {
                    if (trim((string) $saved_data[$key]) !== '') {
                        $saved_value_count++;
                    }
                }

                $contact_present = (
                    trim((string) $saved_data['supportPhoneNumber']) !== ''
                    || trim((string) $saved_data['supportEmailAddress']) !== ''
                );
                $commercial_present = (
                    trim((string) $saved_data['shippingOptions']) !== ''
                    && trim((string) $saved_data['paymentMethods']) !== ''
                );
                $profile_present = (
                    trim((string) $saved_data['companyDescription']) !== ''
                    || trim((string) $saved_data['businessInformation']) !== ''
                );

                $has_minimum_saved_profile = (
                    $saved_value_count >= 8
                    && trim((string) $saved_data['companyName']) !== ''
                    && trim((string) $saved_data['brand']) !== ''
                    && trim((string) $saved_data['websiteUrl']) !== ''
                    && trim((string) $saved_data['websiteType']) !== ''
                    && trim((string) $saved_data['industry']) !== ''
                    && $contact_present
                    && $commercial_present
                    && $profile_present
                );

                if ($has_minimum_saved_profile) {
                    wp_send_json_success($saved_data);
                    return;
                }
            }
        }

        // Temporary local-dev override: local hosts are crawled using the live domain.
        $website_for_autofill = $website;
        $website_host = strtolower((string) wp_parse_url($website_for_autofill, PHP_URL_HOST));
        if ($this->aysa_setup_is_local_host($website_host)) {
            $website_for_autofill = 'https://florariebragadiru.ro';
        }

        $data = array();
        $broker_payload = array(
            'project_id' => $project_id,
            'website' => $website_for_autofill,
            'requested_website' => $website,
            'site_host' => isset($_POST['site_host']) ? sanitize_text_field(wp_unslash($_POST['site_host'])) : '',
        );
        $api_resp = $this->aysa_broker_request('POST', 'v1/setup/general/autofill', $broker_payload, 45);
        $broker_debug = array(
            'ok' => false,
            'error_code' => '',
            'error_message' => '',
        );
        if (is_wp_error($api_resp)) {
            $broker_debug['error_code'] = (string) $api_resp->get_error_code();
            $broker_debug['error_message'] = (string) $api_resp->get_error_message();
        }
        if (!is_wp_error($api_resp) && is_array($api_resp) && !empty($api_resp['ok']) && isset($api_resp['data']) && is_array($api_resp['data'])) {
            $broker_debug['ok'] = true;
            $data = $api_resp['data'];
        }

        if (empty($data)) {
            $data = $this->aysa_setup_extract_general_info($website);
            $data['source'] = 'setup_general_autofill_local_fallback';
        }

        $resolved_website_type = isset($data['websiteType']) ? sanitize_key((string) $data['websiteType']) : '';
        $resolved_payment_methods = isset($data['paymentMethods']) ? trim((string) $data['paymentMethods']) : '';
        if ($resolved_payment_methods === '' && $resolved_website_type === 'ecommerce') {
            $data['paymentMethods'] = 'Card, Bank transfer, Cash on delivery';
            $data['_normalization'] = array(
                'payment_methods' => 'defaulted_for_ecommerce',
            );
        }

        $data['projectId'] = $project_id;
        $data['requestedWebsiteUrl'] = $website;
        $data['websiteUrl'] = isset($data['websiteUrl']) && is_string($data['websiteUrl']) && $data['websiteUrl'] !== ''
            ? $data['websiteUrl']
            : $website_for_autofill;
        $data['_debug'] = array(
            'broker_endpoint' => 'v1/setup/general/autofill',
            'requested_website' => $website,
            'website_used_for_autofill' => $website_for_autofill,
            'broker' => $broker_debug,
        );
        $cfg_probe = $this->aysa_get_broker_config();
        if (!is_wp_error($cfg_probe)) {
            $data['_debug']['broker_base_url'] = isset($cfg_probe['base_url']) ? (string) $cfg_probe['base_url'] : '';
            $data['_debug']['broker_key_id'] = isset($cfg_probe['key_id']) ? (string) $cfg_probe['key_id'] : '';
        }

        if ($general_option_name !== '' && is_array($data)) {
            $persist = array(
                'company_name' => isset($data['companyName']) ? sanitize_text_field((string) $data['companyName']) : '',
                'brand_name' => isset($data['brand']) ? sanitize_text_field((string) $data['brand']) : '',
                'company_description' => isset($data['companyDescription']) ? sanitize_textarea_field((string) $data['companyDescription']) : '',
                'support_phone' => isset($data['supportPhoneNumber']) ? sanitize_text_field((string) $data['supportPhoneNumber']) : '',
                'support_email' => isset($data['supportEmailAddress']) ? sanitize_email((string) $data['supportEmailAddress']) : '',
                'website' => isset($data['websiteUrl']) ? esc_url_raw((string) $data['websiteUrl']) : '',
                'website_type' => isset($data['websiteType']) ? sanitize_text_field((string) $data['websiteType']) : '',
                'shipping' => isset($data['shippingOptions']) ? sanitize_text_field((string) $data['shippingOptions']) : '',
                'payment_method' => isset($data['paymentMethods']) ? sanitize_text_field((string) $data['paymentMethods']) : '',
                'industry' => isset($data['industry']) ? sanitize_text_field((string) $data['industry']) : '',
                'company_info' => isset($data['businessInformation']) ? sanitize_textarea_field((string) $data['businessInformation']) : '',
            );
            update_option($general_option_name, $persist, false);
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_setup_general_autofill',
            'setup_general_autofill',
            array(
                'module' => 'setup_seo_profile',
                'source' => isset($data['source']) ? sanitize_text_field((string) $data['source']) : '',
                'force_generate' => $force_generate ? 1 : 0,
                'broker_ok' => !empty($broker_debug['ok']) ? 1 : 0,
                'website' => $website_for_autofill,
            ),
            55,
            'v1/setup/general/autofill'
        );

        wp_send_json_success($data);
    }

    public function handle_setup_conversation() {
        $request_started_at = microtime(true);
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }
        if (!$this->check_authentication(false)) {
            wp_send_json_error(array('message' => 'Authentication required.'));
            return;
        }

        $key_validation = $this->aysa_validate_test_access_key(
            $this->aysa_get_effective_access_key(isset($_POST['access_key']) ? sanitize_text_field(wp_unslash($_POST['access_key'])) : ''),
            isset($_POST['site_host']) ? sanitize_text_field(wp_unslash($_POST['site_host'])) : ''
        );
        if (empty($key_validation['valid'])) {
            wp_send_json_error(array(
                'message' => (string) ($key_validation['message'] ?? 'Access key is invalid.'),
                'code' => 'access_key_invalid',
            ));
            return;
        }

        $mode = isset($_POST['mode']) ? sanitize_key(wp_unslash((string) $_POST['mode'])) : 'bootstrap';
        $requested_mode = $mode;
        $current_step = isset($_POST['current_step']) ? sanitize_key(wp_unslash((string) $_POST['current_step'])) : 'integrations';
        $conversation_language = isset($_POST['conversation_language']) ? sanitize_key(wp_unslash((string) $_POST['conversation_language'])) : 'en';
        if (!in_array($conversation_language, array('en', 'ro', 'bg'), true)) {
            $conversation_language = 'en';
        }
        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : intval(get_option('aysa_project_id', 0));
        $user_message = isset($_POST['user_message']) ? sanitize_textarea_field(wp_unslash((string) $_POST['user_message'])) : '';

        if ($project_id <= 0) {
            wp_send_json_error(array('message' => 'Missing project ID.'));
            return;
        }

        $auth_manager = Aysa_Auth_Manager::get_instance();
        $auth_status = $auth_manager->is_authenticated();
        $user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : 0;
        if ($user_id <= 0) {
            wp_send_json_error(array('message' => 'Invalid authentication context.'));
            return;
        }

        $onboarding_run_token = $this->aysa_get_or_create_onboarding_run_token($project_id);

        $setup_reset_forced = $this->aysa_is_setup_reset_forced();
        $local_setup_state = $setup_reset_forced ? array() : $this->aysa_get_local_setup_state_for_user($user_id, $project_id);
        $general = isset($local_setup_state['general']) && is_array($local_setup_state['general']) ? $local_setup_state['general'] : array();
        $website = isset($general['website']) ? $this->aysa_setup_normalize_url((string) $general['website']) : '';
        if ($website === '') {
            $website = $this->aysa_setup_normalize_url(home_url('/'));
        }

        $google_connected = false;
        $google_email = '';
        $google_gsc_count = 0;
        $google_ga4_count = 0;
        if (class_exists('Aysa_Google_OAuth_Manager')) {
            $oauth_manager = Aysa_Google_OAuth_Manager::get_instance();
            if (is_object($oauth_manager) && method_exists($oauth_manager, 'get_status_for_project')) {
                $oauth_status = $oauth_manager->get_status_for_project($project_id);
                if (!is_wp_error($oauth_status) && is_array($oauth_status)) {
                    $google_connected = !empty($oauth_status['connected']);
                    $google_email = isset($oauth_status['email']) ? sanitize_email((string) $oauth_status['email']) : '';
                    $google_gsc_count = isset($oauth_status['gsc_sites_count']) ? intval($oauth_status['gsc_sites_count']) : 0;
                    $google_ga4_count = isset($oauth_status['ga4_properties_count']) ? intval($oauth_status['ga4_properties_count']) : 0;
                }
            }
        }
        $endpoint = '/v1/conversation/setup/bootstrap';
        $payload = array(
            'project_id' => $project_id,
            'current_step' => $current_step,
            'reset_session' => !empty($_POST['reset_session']),
            'skip_google' => !empty($_POST['skip_google']),
            'conversation_language' => $conversation_language,
            'site_host' => isset($_POST['site_host']) ? sanitize_text_field(wp_unslash($_POST['site_host'])) : '',
            'website' => $website,
            'onboarding_run_token' => $onboarding_run_token,
            'wallet_snapshot' => $this->aysa_build_agent_wallet_snapshot($project_id),
            'local_setup_state' => $local_setup_state,
            'ui_context' => array(
                'conversation_language' => $conversation_language,
                'available_languages' => array('en', 'ro', 'bg'),
                'integrations' => array(
                    'terms_url' => 'https://aysa.ai/terms/',
                    'privacy_url' => 'https://aysa.ai/privacy/',
                    'google_connect_available' => true,
                    'google_connect_label' => 'Connect Google',
                    'google_connected' => $google_connected,
                    'google_skipped' => !empty($_POST['skip_google']),
                    'google_email' => $google_email,
                    'google_gsc_count' => $google_gsc_count,
                    'google_ga4_count' => $google_ga4_count,
                ),
            ),
        );

        if ($mode === 'message') {
            $endpoint = '/v1/conversation/setup/message';
            $payload['user_message'] = $user_message;
        }

        if ($this->aysa_should_queue_setup_competition_suggestion($mode, $current_step, $user_message)) {
            $plugin_url = admin_url('admin.php?page=aysa-agent8');
            $payload['async_notification'] = array(
                'channel' => 'setup_seo_profile',
                'event_key' => 'competition_seed_research_ready',
                'ui_action' => 'resume_setup_onboarding',
                'cta_label' => $conversation_language === 'ro' ? 'Vezi sugestiile' : ($conversation_language === 'bg' ? 'Виж предложенията' : 'See the suggestions'),
                'action_url' => $plugin_url,
                'email_to' => $this->get_notification_target_email(),
                'conversation_language' => $conversation_language,
                'run_token' => $onboarding_run_token,
            );

            register_shutdown_function(function() use ($endpoint, $payload) {
                $this->aysa_broker_request_async('POST', $endpoint, $payload, 1);
            });

            $assistant_message = $this->aysa_setup_competition_async_ack($conversation_language);
            $email_target = trim((string) ($payload['async_notification']['email_to'] ?? ''));
            wp_send_json_success(array(
                'result' => array(
                    'ok' => true,
                    'assistant_message' => $assistant_message,
                    'phase' => 'onboarding_competition',
                    'current_step' => 'competition',
                    'next_step' => 'competition',
                    'step_substate' => 'regenerating_proposal',
                    'onboarding_run_token' => $onboarding_run_token,
                    'turn_type' => 'provide_seed_keywords',
                    'intent_family' => 'does_not_know',
                    'assistant_goal' => 'answer_and_reanchor',
                    'next_action' => 'await_async_competition_proposal',
                    'confidence' => 1,
                    'needs_clarification' => false,
                    'reanchor_target' => 'competition',
                    'async_task' => array(
                        'type' => 'competition_seed_research',
                        'status' => 'running',
                        'channel' => 'setup_seo_profile',
                        'event_key' => 'competition_seed_research_ready',
                    ),
                    'notification_policy' => array(
                        'in_app' => true,
                        'email' => $email_target !== '',
                        'channel' => 'setup_seo_profile',
                        'event_key' => 'competition_seed_research_ready',
                        'action_url' => $plugin_url,
                    ),
                    'ready_to_advance' => false,
                    'actions' => array(
                        'executed' => array(),
                        'should_advance' => false,
                    ),
                    'messages' => array(
                        array('role' => 'user', 'message' => $user_message),
                        array('role' => 'assistant', 'message' => $assistant_message),
                    ),
                    'suggested_actions' => array(),
                    'state' => array(
                        'steps' => is_array($local_setup_state) ? $local_setup_state : array(),
                        'meta' => array(
                            'phase' => 'onboarding_competition',
                            'current_step' => 'competition',
                            'next_step' => 'competition',
                            'step_substate' => 'regenerating_proposal',
                            'onboarding_run_token' => $onboarding_run_token,
                            'competition_research_status' => 'running',
                            'intent_family' => 'does_not_know',
                            'assistant_goal' => 'answer_and_reanchor',
                            'last_resolved_action' => 'await_async_competition_proposal',
                            'last_reanchor_target' => 'competition',
                            'turn_confidence' => 1,
                            'needs_clarification' => false,
                        ),
                    ),
                ),
                'access' => $key_validation,
                'queued' => true,
            ));
            return;
        }

        $broker_started_at = microtime(true);
        $result = $this->aysa_broker_request('POST', $endpoint, $payload, 75);
        $broker_elapsed_ms = (int) round((microtime(true) - $broker_started_at) * 1000);
        if (is_wp_error($result)) {
            wp_send_json_error(array('message' => $result->get_error_message()));
            return;
        }
        if (!is_array($result) || empty($result['ok'])) {
            wp_send_json_error(array(
                'message' => isset($result['error']) ? (string) $result['error'] : 'Setup conversation request failed.',
                'result' => $result,
            ));
            return;
        }

        $self = $this;
        register_shutdown_function(static function () use (
            $self,
            $mode,
            $project_id,
            $endpoint,
            $result,
            $user_id,
            $conversation_language,
            $google_connected,
            $broker_elapsed_ms,
            $request_started_at
        ) {
            if ($mode === 'message') {
                $self->aysa_wallet_bill_provider_usage(
                    $project_id,
                    $endpoint,
                    isset($result['usage']) && is_array($result['usage']) ? $result['usage'] : array(),
                    'setup_conversation_message',
                    array(
                        'module' => 'setup_conversation',
                        'source' => 'chat_message',
                    )
                );
            }

            if (!empty($result['wp_updates']) && is_array($result['wp_updates'])) {
                $self->aysa_apply_setup_conversation_updates($user_id, $result['wp_updates'], $project_id);
                $updated_local_setup_state = $self->aysa_get_local_setup_state_for_user($user_id, $project_id);
                $result_state = isset($result['state']) && is_array($result['state']) ? $result['state'] : array();
                $result_meta = isset($result_state['meta']) && is_array($result_state['meta']) ? $result_state['meta'] : array();
                $result_steps = isset($result_state['steps']) && is_array($result_state['steps']) ? $result_state['steps'] : array();
                $google_skipped = !empty($result_steps['integrations']['google_skip'])
                    || !empty($result_meta['google_optional_skipped']);

                $self->aysa_sync_setup_state_to_client_rag(
                    $project_id,
                    $conversation_language,
                    $updated_local_setup_state,
                    $google_connected,
                    $google_skipped,
                    $result_meta
                );
            }

            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log(sprintf(
                    'AYSA setup conversation timings: broker=%dms total=%dms mode=%s project=%d',
                    $broker_elapsed_ms,
                    (int) round((microtime(true) - $request_started_at) * 1000),
                    (string) $mode,
                    (int) $project_id
                ));
            }
        });

        wp_send_json_success(array(
            'result' => $result,
            'access' => $key_validation,
        ));
    }

    public function handle_adjustments_conversation() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }
        if (!$this->check_authentication(false)) {
            wp_send_json_error(array('message' => 'Authentication required.'));
            return;
        }

        $key_validation = $this->aysa_validate_test_access_key(
            $this->aysa_get_effective_access_key(isset($_POST['access_key']) ? sanitize_text_field(wp_unslash((string) $_POST['access_key'])) : ''),
            isset($_POST['site_host']) ? sanitize_text_field(wp_unslash($_POST['site_host'])) : ''
        );
        if (empty($key_validation['valid'])) {
            wp_send_json_error(array(
                'message' => (string) ($key_validation['message'] ?? 'Access key is invalid.'),
                'code' => 'access_key_invalid',
            ));
            return;
        }

        $mode = isset($_POST['mode']) ? sanitize_key(wp_unslash((string) $_POST['mode'])) : 'bootstrap';
        $conversation_language = isset($_POST['conversation_language']) ? sanitize_key(wp_unslash((string) $_POST['conversation_language'])) : 'en';
        if (!in_array($conversation_language, array('en', 'ro', 'bg'), true)) {
            $conversation_language = 'en';
        }
        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : intval(get_option('aysa_project_id', 0));
        $user_message = isset($_POST['user_message']) ? sanitize_textarea_field(wp_unslash((string) $_POST['user_message'])) : '';

        if ($project_id <= 0) {
            wp_send_json_error(array('message' => 'Missing project ID.'));
            return;
        }

        $auth_manager = Aysa_Auth_Manager::get_instance();
        $auth_status = $auth_manager->is_authenticated();
        $user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : 0;
        if ($user_id <= 0) {
            wp_send_json_error(array('message' => 'Invalid authentication context.'));
            return;
        }

        $setup_reset_forced = $this->aysa_is_setup_reset_forced();
        $local_setup_state = $setup_reset_forced ? array() : $this->aysa_get_local_setup_state_for_user($user_id, $project_id);
        $general = isset($local_setup_state['general']) && is_array($local_setup_state['general']) ? $local_setup_state['general'] : array();
        $website = isset($general['website']) ? $this->aysa_setup_normalize_url((string) $general['website']) : '';
        if ($website === '') {
            $website = $this->aysa_setup_normalize_url(home_url('/'));
        }
        $google_connected = false;
        $google_email = '';
        $google_gsc_count = 0;
        $google_ga4_count = 0;
        if (class_exists('Aysa_Google_OAuth_Manager')) {
            $oauth_manager = Aysa_Google_OAuth_Manager::get_instance();
            if (is_object($oauth_manager) && method_exists($oauth_manager, 'get_status_for_project')) {
                $oauth_status = $oauth_manager->get_status_for_project($project_id);
                if (!is_wp_error($oauth_status) && is_array($oauth_status)) {
                    $google_connected = !empty($oauth_status['connected']);
                    $google_email = isset($oauth_status['email']) ? sanitize_email((string) $oauth_status['email']) : '';
                    $google_gsc_count = isset($oauth_status['gsc_sites_count']) ? intval($oauth_status['gsc_sites_count']) : 0;
                    $google_ga4_count = isset($oauth_status['ga4_properties_count']) ? intval($oauth_status['ga4_properties_count']) : 0;
                }
            }
        }
        $research_snapshot = $setup_reset_forced ? array() : $this->aysa_adjustments_get_research_snapshot($project_id, $user_id);
        $research_snapshot_for_ai = $setup_reset_forced ? array() : $this->aysa_adjustments_reduce_research_snapshot_for_ai($research_snapshot);
        $client_state = array();
        if (!$setup_reset_forced && isset($_POST['client_state'])) {
            $decoded_client_state = json_decode(wp_unslash((string) $_POST['client_state']), true);
            if (is_array($decoded_client_state)) {
                $client_state = $decoded_client_state;
            }
        }
        $client_local_setup_state = isset($client_state['local_setup_state']) && is_array($client_state['local_setup_state'])
            ? $client_state['local_setup_state']
            : array();
        if ($client_local_setup_state !== array()) {
            $local_setup_state = $this->aysa_merge_local_setup_state_fallback($local_setup_state, $client_local_setup_state);
        }
        $forced_execution_action = isset($_POST['forced_execution_action']) ? sanitize_text_field(wp_unslash((string) $_POST['forced_execution_action'])) : '';

        if ($mode === 'message' && $forced_execution_action !== '') {
            $result = array(
                'ok' => true,
                'state' => is_array($client_state) ? $client_state : array(),
                'messages' => array(),
            );
            $execution_meta = $this->aysa_adjustments_execute_research_action($forced_execution_action, $project_id, $user_id, $conversation_language);
            $research_snapshot = $this->aysa_adjustments_get_research_snapshot($project_id, $user_id);
            $assistant_message = !empty($execution_meta['assistant_message'])
                ? (string) $execution_meta['assistant_message']
                : '';
            if ($user_message !== '') {
                $result['messages'][] = array('role' => 'user', 'message' => $user_message);
            }
            if ($assistant_message !== '') {
                $result['messages'][] = array('role' => 'assistant', 'message' => $assistant_message);
                $result['assistant_message'] = $assistant_message;
            }
            if (!empty($execution_meta['suggested_actions']) && is_array($execution_meta['suggested_actions'])) {
                $result['suggested_actions'] = $execution_meta['suggested_actions'];
            }
            if (!empty($execution_meta['ui_action'])) {
                $result['ui_action'] = (string) $execution_meta['ui_action'];
            }
            if (!empty($execution_meta['payment_link'])) {
                $result['payment_link'] = (string) $execution_meta['payment_link'];
            }
            if (!isset($result['state']['context']) || !is_array($result['state']['context'])) {
                $result['state']['context'] = array();
            }
            if (!isset($result['state']['meta']) || !is_array($result['state']['meta'])) {
                $result['state']['meta'] = array();
            }
            $result['state']['context']['research_snapshot'] = $research_snapshot;
            if (!empty($execution_meta['module'])) {
                $result['state']['meta']['active_research_module'] = (string) $execution_meta['module'];
            }
            if (!empty($execution_meta['state_meta']) && is_array($execution_meta['state_meta'])) {
                foreach ($execution_meta['state_meta'] as $meta_key => $meta_value) {
                    if (!is_string($meta_key) || $meta_key === '') {
                        continue;
                    }
                    $result['state']['meta'][$meta_key] = $meta_value;
                }
            }
            $result['research_snapshot'] = $research_snapshot;
            $result['workspace_artifact'] = $this->aysa_adjustments_build_research_workspace_artifact($research_snapshot, $conversation_language);

            wp_send_json_success(array(
                'result' => $result,
                'access' => $key_validation,
            ));
            return;
        }

        $endpoint = '/v1/conversation/adjustments/bootstrap';
        $payload = array(
            'project_id' => $project_id,
            'aysa_user_id' => $user_id,
            'conversation_language' => $conversation_language,
            'reset_session' => !empty($_POST['reset_session']),
            'skip_google' => !empty($_POST['skip_google']),
            'site_host' => isset($_POST['site_host']) ? sanitize_text_field(wp_unslash((string) $_POST['site_host'])) : '',
            'website' => $website,
            'wallet_snapshot' => $this->aysa_build_agent_wallet_snapshot($project_id),
            'local_setup_state' => $local_setup_state,
            'research_snapshot' => $research_snapshot_for_ai,
            'ui_context' => array(
                'conversation_language' => $conversation_language,
                'available_languages' => array('en', 'ro', 'bg'),
                'phase' => 'agent_orchestration',
                'integrations' => array(
                    'terms_url' => 'https://aysa.ai/terms/',
                    'privacy_url' => 'https://aysa.ai/privacy/',
                    'google_connect_available' => true,
                    'google_connect_label' => 'Connect Google',
                    'google_connected' => $google_connected,
                    'google_skipped' => !empty($_POST['skip_google']),
                    'google_email' => $google_email,
                    'google_gsc_count' => $google_gsc_count,
                    'google_ga4_count' => $google_ga4_count,
                ),
            ),
            'state' => $client_state,
        );

        if ($mode === 'message') {
            $endpoint = '/v1/conversation/adjustments/message';
            $payload['user_message'] = $user_message;
        }

        $result = $this->aysa_broker_request('POST', $endpoint, $payload, 75);
        if (is_wp_error($result)) {
            wp_send_json_error(array('message' => $result->get_error_message()));
            return;
        }
        if (!is_array($result) || empty($result['ok'])) {
            wp_send_json_error(array(
                'message' => isset($result['error']) ? (string) $result['error'] : 'Adjustments conversation request failed.',
                'result' => $result,
            ));
            return;
        }

        if ($mode === 'message') {
            $this->aysa_wallet_bill_provider_usage(
                $project_id,
                $endpoint,
                isset($result['usage']) && is_array($result['usage']) ? $result['usage'] : array(),
                'adjustments_conversation_message',
                array(
                    'module' => 'adjustments_conversation',
                    'source' => 'chat_message',
                )
            );
        }

        $execution_action = (string) ($result['execution_action'] ?? '');
        if ($execution_action !== '') {
            $execution_meta = $this->aysa_adjustments_execute_research_action($execution_action, $project_id, $user_id, $conversation_language);
            $research_snapshot = $this->aysa_adjustments_get_research_snapshot($project_id, $user_id);
            if (!empty($execution_meta['assistant_message'])) {
                $result['assistant_message'] = $execution_meta['assistant_message'];
            }
            if (!empty($execution_meta['suggested_actions']) && is_array($execution_meta['suggested_actions'])) {
                $result['suggested_actions'] = $execution_meta['suggested_actions'];
            }
            if (!empty($execution_meta['ui_action'])) {
                $result['ui_action'] = (string) $execution_meta['ui_action'];
            }
            if (!empty($execution_meta['payment_link'])) {
                $result['payment_link'] = (string) $execution_meta['payment_link'];
            }
            if (!isset($result['state']) || !is_array($result['state'])) {
                $result['state'] = array();
            }
            if (!isset($result['state']['context']) || !is_array($result['state']['context'])) {
                $result['state']['context'] = array();
            }
            if (!isset($result['state']['meta']) || !is_array($result['state']['meta'])) {
                $result['state']['meta'] = array();
            }
            $result['state']['context']['research_snapshot'] = $research_snapshot;
            if (!empty($execution_meta['module'])) {
                $result['state']['meta']['active_research_module'] = (string) $execution_meta['module'];
            }
            if (!empty($execution_meta['state_meta']) && is_array($execution_meta['state_meta'])) {
                foreach ($execution_meta['state_meta'] as $meta_key => $meta_value) {
                    if (!is_string($meta_key) || $meta_key === '') {
                        continue;
                    }
                    $result['state']['meta'][$meta_key] = $meta_value;
                }
            }
            if ($execution_action === 'keyword_research_sync' && empty($result['state']['meta']['keyword_walkthrough_stage'])) {
                $result['state']['meta']['keyword_walkthrough_stage'] = 'ranking';
            }
        } else {
            $research_snapshot = $this->aysa_adjustments_get_research_snapshot($project_id, $user_id);
            if (isset($result['state']) && is_array($result['state'])) {
                if (!isset($result['state']['context']) || !is_array($result['state']['context'])) {
                    $result['state']['context'] = array();
                }
                $result['state']['context']['research_snapshot'] = $research_snapshot;
            }
        }
        $result['research_snapshot'] = $research_snapshot;
        $result['workspace_artifact'] = $this->aysa_adjustments_build_research_workspace_artifact($research_snapshot, $conversation_language);

        if (isset($result['state']['context']['local_setup_state']) && is_array($result['state']['context']['local_setup_state'])) {
            $this->aysa_persist_local_setup_state_snapshot($user_id, $result['state']['context']['local_setup_state'], $project_id);
        }

        wp_send_json_success(array(
            'result' => $result,
            'access' => $key_validation,
        ));
    }

    public function handle_keyword_research_run_status() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }
        if (!$this->check_authentication(false)) {
            wp_send_json_error(array('message' => 'Authentication required.'));
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : intval(get_option('aysa_project_id', 0));
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        if ($project_id <= 0) {
            wp_send_json_error(array('message' => 'Missing project ID.'));
            return;
        }

        $run_id = isset($_POST['run_id']) ? intval(wp_unslash((string) $_POST['run_id'])) : 0;
        $after_event_id = isset($_POST['after_event_id']) ? intval(wp_unslash((string) $_POST['after_event_id'])) : 0;
        $job_type = isset($_POST['job_type']) ? sanitize_key(wp_unslash((string) $_POST['job_type'])) : '';
        $status_endpoint = isset($_POST['status_endpoint']) ? sanitize_text_field(wp_unslash((string) $_POST['status_endpoint'])) : '/v1/research-runs/keyword/status';
        if (($status_endpoint === '' || $status_endpoint === '/v1/research-runs/keyword/status') && $job_type !== '' && $job_type !== 'keyword_research') {
            if (strpos($job_type, 'business_profile') !== false) {
                $status_endpoint = '/v1/setup/general/autofill/status';
            } elseif (strpos($job_type, 'site_audit') !== false) {
                $status_endpoint = '/v1/research-runs/technical-site-audit/status';
            } elseif (strpos($job_type, 'pagespeed') !== false) {
                $status_endpoint = '/v1/research-runs/technical-pagespeed/status';
            } elseif (strpos($job_type, 'schema') !== false) {
                $status_endpoint = '/v1/research-runs/technical-schema/status';
            } elseif (strpos($job_type, 'redirect') !== false) {
                $status_endpoint = '/v1/research-runs/technical-redirects/status';
            } elseif (strpos($job_type, 'sitemap') !== false || strpos($job_type, 'robots') !== false) {
                $status_endpoint = '/v1/research-runs/technical-sitemap-robots/status';
            } elseif (strpos($job_type, 'internal_link') !== false) {
                $status_endpoint = '/v1/internal-link-runs/status';
            } elseif (strpos($job_type, 'onpage') !== false) {
                $status_endpoint = '/v1/onpage-runs/status';
            }
            $status_endpoint_by_job = array(
                'business_profile_autofill' => '/v1/setup/general/autofill/status',
                'technical_site_audit' => '/v1/research-runs/technical-site-audit/status',
                'technical_pagespeed' => '/v1/research-runs/technical-pagespeed/status',
                'technical_schema' => '/v1/research-runs/technical-schema/status',
                'technical_redirects' => '/v1/research-runs/technical-redirects/status',
                'technical_sitemap_robots' => '/v1/research-runs/technical-sitemap-robots/status',
                'technical_optimization' => '/v1/research-runs/technical-optimization/status',
                'internal_linking' => '/v1/internal-link-runs/status',
                'onpage' => '/v1/onpage-runs/status',
            );
            if (isset($status_endpoint_by_job[$job_type])) {
                $status_endpoint = $status_endpoint_by_job[$job_type];
            }
        }
        $allowed_status_endpoints = array(
            '/v1/setup/general/autofill/status',
            '/v1/research-runs/keyword/status',
            '/v1/research-runs/technical-site-audit/status',
            '/v1/research-runs/technical-pagespeed/status',
            '/v1/research-runs/technical-schema/status',
            '/v1/research-runs/technical-redirects/status',
            '/v1/research-runs/technical-sitemap-robots/status',
            '/v1/research-runs/technical-optimization/status',
            '/v1/internal-link-runs/status',
            '/v1/onpage-runs/status',
        );
        if (!in_array($status_endpoint, $allowed_status_endpoints, true)) {
            wp_send_json_error(array('message' => 'Unsupported async status endpoint.'));
            return;
        }

        $result = $this->aysa_broker_request('POST', $status_endpoint, array(
            'project_id' => $project_id,
            'run_id' => $run_id,
            'after_event_id' => $after_event_id,
        ), 30);
        if (is_wp_error($result)) {
            wp_send_json_error(array('message' => $result->get_error_message()));
            return;
        }

        $projection = isset($result['projection']) && is_array($result['projection']) ? $result['projection'] : array();
        if ($status_endpoint === '/v1/research-runs/keyword/status' && !empty($projection) && !empty($result['run']['id'])) {
            $result['local_projection_sync'] = $this->aysa_keyword_research_apply_local_projection(
                $project_id,
                intval($result['run']['id']),
                $projection
            );
        }

        $wallet_snapshot = $this->aysa_build_agent_wallet_snapshot($project_id);
        if (isset($result['billing']) && is_array($result['billing'])) {
            $wallet_snapshot = $this->aysa_agent8_apply_top_level_billing_usage_hint($wallet_snapshot, $result);
        }

        wp_send_json_success(array(
            'status' => $result,
            'wallet_snapshot' => $wallet_snapshot,
        ));
    }

    public function handle_keyword_research_projection_sync() {
        $secret = $this->get_app_aysa_internal_secret();
        if ($secret === '') {
            wp_send_json_error(array('message' => 'Internal secret unavailable.'), 500);
            return;
        }

        $provided = $this->aysa_get_bearer_authorization_token();
        if ($provided === '' || !hash_equals($secret, $provided)) {
            wp_send_json_error(array('message' => 'Unauthorized'), 401);
            return;
        }

        $raw = file_get_contents('php://input');
        $payload = json_decode(is_string($raw) ? $raw : '', true);
        if (!is_array($payload)) {
            wp_send_json_error(array('message' => 'Invalid JSON payload'), 400);
            return;
        }

        $project_id = $this->aysa_resolve_technical_project_id(intval($payload['project_id'] ?? 0));
        $run_id = intval($payload['run_id'] ?? 0);
        $projection = isset($payload['projection']) && is_array($payload['projection']) ? $payload['projection'] : array();
        if ($project_id <= 0 || $run_id <= 0 || $projection === array()) {
            wp_send_json_error(array('message' => 'Invalid projection sync payload'), 422);
            return;
        }

        $sync = $this->aysa_keyword_research_apply_local_projection($project_id, $run_id, $projection);
        wp_send_json_success(array(
            'local_projection_sync' => $sync,
        ));
    }

    public function handle_technical_site_audit_projection_sync() {
        $secret = $this->get_app_aysa_internal_secret();
        if ($secret === '') {
            wp_send_json_error(array('message' => 'Internal secret unavailable.'), 500);
            return;
        }

        $provided = $this->aysa_get_bearer_authorization_token();
        if ($provided === '' || !hash_equals($secret, $provided)) {
            wp_send_json_error(array('message' => 'Unauthorized'), 401);
            return;
        }

        $raw = file_get_contents('php://input');
        $payload = json_decode(is_string($raw) ? $raw : '', true);
        if (!is_array($payload)) {
            wp_send_json_error(array('message' => 'Invalid JSON payload'), 400);
            return;
        }

        $project_id = $this->aysa_resolve_technical_project_id(intval($payload['project_id'] ?? 0));
        $operation = sanitize_key((string) ($payload['operation'] ?? ''));
        if ($project_id <= 0) {
            wp_send_json_error(array('message' => 'Invalid project id'), 422);
            return;
        }

        if ($operation === 'reset_state') {
            $sync = $this->aysa_technical_site_audit_reset_local_projection($project_id);
            wp_send_json_success(array(
                'local_projection_sync' => $sync,
            ));
            return;
        }

        if ($operation === 'update_issue_lifecycle') {
            $issue_id = sanitize_text_field((string) ($payload['issue_id'] ?? ''));
            $lifecycle_state = sanitize_key((string) ($payload['lifecycle_state'] ?? ''));
            $sync = $this->aysa_technical_site_audit_update_issue_lifecycle($project_id, $issue_id, $lifecycle_state);
            if (empty($sync['applied'])) {
                wp_send_json_error(array(
                    'local_projection_sync' => $sync,
                ), 422);
                return;
            }
            wp_send_json_success(array(
                'local_projection_sync' => $sync,
                'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
            ));
            return;
        }

        if ($operation === 'site_audit_package_adapter_context') {
            $action_key = sanitize_key((string) ($payload['action_key'] ?? ''));
            wp_send_json_success(array(
                'adapter_context' => $this->aysa_technical_site_audit_build_package_adapter_context($project_id, $action_key, $payload),
                'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
            ));
            return;
        }

        if ($operation === 'site_audit_action_lifecycle') {
            $action_key = sanitize_key((string) ($payload['action_key'] ?? ''));
            $stage = sanitize_key((string) ($payload['stage'] ?? ''));
            $sync = $this->aysa_technical_site_audit_update_action_lifecycle($project_id, $action_key, $stage, $payload);
            if (empty($sync['applied'])) {
                wp_send_json_error(array(
                    'local_projection_sync' => $sync,
                    'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
                ), 422);
                return;
            }
            wp_send_json_success(array(
                'local_projection_sync' => $sync,
                'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
            ));
            return;
        }

        if ($operation === 'site_audit_metadata_remediation_dispatch') {
            $sync = $this->aysa_technical_site_audit_dispatch_metadata_remediation($project_id, $payload);
            if (empty($sync['applied'])) {
                wp_send_json_error(array(
                    'local_projection_sync' => $sync,
                    'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
                ), 422);
                return;
            }
            wp_send_json_success(array(
                'local_projection_sync' => $sync,
                'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
            ));
            return;
        }

        if ($operation === 'site_audit_metadata_remediation_apply') {
            $sync = $this->aysa_technical_site_audit_apply_metadata_remediation($project_id, $payload);
            if (empty($sync['applied'])) {
                wp_send_json_error(array(
                    'local_projection_sync' => $sync,
                    'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
                ), 422);
                return;
            }
            wp_send_json_success(array(
                'local_projection_sync' => $sync,
                'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
            ));
            return;
        }

        if ($operation === 'site_audit_image_alt_generation_dispatch') {
            $sync = $this->aysa_technical_site_audit_dispatch_image_alt_generation($project_id, $payload);
            if (empty($sync['applied'])) {
                wp_send_json_error(array(
                    'local_projection_sync' => $sync,
                    'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
                ), 422);
                return;
            }
            wp_send_json_success(array(
                'local_projection_sync' => $sync,
                'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
            ));
            return;
        }

        if ($operation === 'site_audit_canonical_generation_dispatch') {
            $sync = $this->aysa_technical_site_audit_dispatch_canonical_generation($project_id, $payload);
            if (empty($sync['applied'])) {
                wp_send_json_error(array(
                    'local_projection_sync' => $sync,
                    'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
                ), 422);
                return;
            }
            wp_send_json_success(array(
                'local_projection_sync' => $sync,
                'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
            ));
            return;
        }

        if ($operation === 'site_audit_communication_event') {
            $event = $this->aysa_technical_site_audit_create_external_communication_event($project_id, $payload);
            if (empty($event['ok'])) {
                wp_send_json_error(array('communication_event' => $event), 422);
                return;
            }
            wp_send_json_success(array(
                'communication_event' => $event,
                'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
            ));
            return;
        }

        if ($operation === 'site_audit_shared_action_dispatch') {
            $sync = $this->aysa_technical_site_audit_dispatch_shared_action($project_id, $payload);
            if (empty($sync['applied'])) {
                wp_send_json_error(array(
                    'local_projection_sync' => $sync,
                    'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
                ), 422);
                return;
            }
            wp_send_json_success(array(
                'local_projection_sync' => $sync,
                'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
            ));
            return;
        }

        if (in_array($operation, array('site_audit_redirects_apply', 'site_audit_canonical_apply', 'site_audit_internal_linking_apply', 'site_audit_image_alt_apply'), true)) {
            $sync = $this->aysa_technical_site_audit_apply_shared_action($project_id, $payload);
            if (empty($sync['applied'])) {
                wp_send_json_error(array(
                    'local_projection_sync' => $sync,
                    'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
                ), 422);
                return;
            }
            wp_send_json_success(array(
                'local_projection_sync' => $sync,
                'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
            ));
            return;
        }

        $run_id = intval($payload['run_id'] ?? 0);
        $projection = isset($payload['projection']) && is_array($payload['projection']) ? $payload['projection'] : array();
        if ($run_id <= 0 || $projection === array()) {
            wp_send_json_error(array('message' => 'Invalid projection sync payload'), 422);
            return;
        }

        $sync = $this->aysa_technical_site_audit_apply_local_projection($project_id, $run_id, $projection);
        wp_send_json_success(array(
            'local_projection_sync' => $sync,
        ));
    }

    public function handle_technical_pagespeed_projection_sync() {
        $secret = $this->get_app_aysa_internal_secret();
        if ($secret === '') {
            wp_send_json_error(array('message' => 'Internal secret unavailable.'), 500);
            return;
        }

        $provided = $this->aysa_get_bearer_authorization_token();
        if ($provided === '' || !hash_equals($secret, $provided)) {
            wp_send_json_error(array('message' => 'Unauthorized'), 401);
            return;
        }

        $raw = file_get_contents('php://input');
        $payload = json_decode(is_string($raw) ? $raw : '', true);
        if (!is_array($payload)) {
            wp_send_json_error(array('message' => 'Invalid JSON payload'), 400);
            return;
        }

        $project_id = $this->aysa_resolve_technical_project_id(intval($payload['project_id'] ?? 0));
        $operation = sanitize_key((string) ($payload['operation'] ?? ''));
        if ($project_id <= 0) {
            wp_send_json_error(array('message' => 'Invalid project id'), 422);
            return;
        }

        if ($operation === 'reset_state') {
            $sync = $this->aysa_technical_pagespeed_reset_local_projection($project_id);
            wp_send_json_success(array(
                'local_projection_sync' => $sync,
            ));
            return;
        }

        $run_id = intval($payload['run_id'] ?? 0);
        $projection = isset($payload['projection']) && is_array($payload['projection']) ? $payload['projection'] : array();
        if ($run_id <= 0 || $projection === array()) {
            wp_send_json_error(array('message' => 'Invalid projection sync payload'), 422);
            return;
        }

        $sync = $this->aysa_technical_pagespeed_apply_local_projection($project_id, $run_id, $projection);
        wp_send_json_success(array(
            'local_projection_sync' => $sync,
        ));
    }

    public function handle_technical_schema_projection_sync() {
        $secret = $this->get_app_aysa_internal_secret();
        if ($secret === '') {
            wp_send_json_error(array('message' => 'Internal secret unavailable.'), 500);
            return;
        }

        $provided = $this->aysa_get_bearer_authorization_token();
        if ($provided === '' || !hash_equals($secret, $provided)) {
            wp_send_json_error(array('message' => 'Unauthorized'), 401);
            return;
        }

        $raw = file_get_contents('php://input');
        $payload = json_decode(is_string($raw) ? $raw : '', true);
        if (!is_array($payload)) {
            wp_send_json_error(array('message' => 'Invalid JSON payload'), 400);
            return;
        }

        $project_id = $this->aysa_resolve_technical_project_id(intval($payload['project_id'] ?? 0));
        $operation = sanitize_key((string) ($payload['operation'] ?? ''));
        if ($project_id <= 0) {
            wp_send_json_error(array('message' => 'Invalid project id'), 422);
            return;
        }

        if ($operation === 'reset_state') {
            $sync = $this->aysa_technical_schema_reset_local_projection($project_id);
            wp_send_json_success(array(
                'local_projection_sync' => $sync,
            ));
            return;
        }

        if ($operation === 'start_refresh') {
            $this->aysa_adjustments_delete_technical_schema_snapshot_cache($project_id);
            $running_job = $this->aysa_technical_schema_get_current_refresh_job($project_id);
            if ($running_job && in_array((string) ($running_job['status'] ?? ''), array('queued', 'running'), true)) {
                wp_send_json_success(array(
                    'job' => $this->aysa_technical_schema_get_job_response($running_job),
                    'started' => false,
                    'already_running' => true,
                ));
                return;
            }

            $items = array();
            foreach ((array) $this->aysa_adjustments_collect_schema_scope_pages() as $page_data) {
                if (!is_array($page_data)) {
                    continue;
                }
                $url = $this->aysa_adjustments_normalize_schema_url((string) ($page_data['url'] ?? ''));
                if ($url === '') {
                    continue;
                }
                $page_data['url'] = $url;
                $items[$url] = $page_data;
            }
            $items = array_values($items);
            if (empty($items)) {
                wp_send_json_error(array('message' => 'No schema scope pages were found for snapshot refresh.'), 422);
                return;
            }

            $job = $this->aysa_technical_schema_create_job($project_id, 'snapshot_refresh', 'sitewide', $items, 'snapshot_refresh');
            set_transient($this->aysa_technical_schema_refresh_job_pointer_key($project_id), (string) $job['job_id'], 12 * HOUR_IN_SECONDS);
            $summary_state = $this->aysa_adjustments_get_cached_or_idle_technical_schema_summary($project_id);
            $summary_state['status'] = 'rebuilding';
            $summary_state['needs_refresh'] = false;
            set_transient(
                $this->aysa_adjustments_get_schema_cache_key($project_id, 'summary'),
                $summary_state,
                HOUR_IN_SECONDS
            );
            $this->aysa_technical_schema_schedule_job((string) $job['job_id'], 1);

            wp_send_json_success(array(
                'job' => $this->aysa_technical_schema_get_job_response((array) $job),
                'started' => true,
            ));
            return;
        }

        if ($operation === 'poll_refresh') {
            $job_id = isset($payload['job_id']) ? sanitize_text_field((string) $payload['job_id']) : '';
            $job = $job_id !== '' ? $this->aysa_technical_schema_read_job($job_id) : $this->aysa_technical_schema_get_current_refresh_job($project_id);

            if (!$job || intval($job['project_id'] ?? 0) !== $project_id) {
                wp_send_json_error(array('message' => 'Schema refresh job not found.'), 404);
                return;
            }

            if ((string) ($job['status'] ?? '') === 'queued') {
                $job = $this->aysa_technical_schema_process_job((string) ($job['job_id'] ?? ''), 4);
            } elseif ((string) ($job['status'] ?? '') === 'running' && intval($job['lock_until'] ?? 0) <= time()) {
                $job = $this->aysa_technical_schema_process_job((string) ($job['job_id'] ?? ''), 4);
            }

            wp_send_json_success(array(
                'job' => $this->aysa_technical_schema_get_job_response((array) $job),
            ));
            return;
        }

        if ($operation === 'final_snapshot') {
            $snapshot = $this->aysa_adjustments_get_technical_schema_snapshot($project_id, false);
            $markup = $this->aysa_adjustments_get_technical_schema_markup_snapshot($project_id);
            wp_send_json_success(array(
                'snapshot' => $snapshot,
                'markup' => $markup,
            ));
            return;
        }

        if ($operation === 'refresh_snapshot') {
            $snapshot = $this->aysa_adjustments_get_cached_or_idle_technical_schema_snapshot($project_id);
            $markup = $this->aysa_adjustments_get_technical_schema_markup_snapshot($project_id);
            $state = get_option($this->aysa_technical_schema_state_key($project_id), array());
            if (!is_array($state)) {
                $state = array();
            }
            $snapshot_has_data = !empty($snapshot['has_data']) || !empty($snapshot['coverage_rows']);
            $state = array_merge($state, array(
                'project_id' => $project_id,
                'updated_at' => current_time('mysql'),
                'updated_at_unix' => time(),
                'run_id' => intval($state['run_id'] ?? 0),
                'status' => $snapshot_has_data ? 'completed' : sanitize_key((string) ($snapshot['status'] ?? ($state['status'] ?? 'idle'))),
                'source' => 'worker_local_snapshot_cached',
                'orchestration' => 'worker_managed',
            ));
            update_option($this->aysa_technical_schema_state_key($project_id), $state, false);
            wp_send_json_success(array(
                'snapshot' => $snapshot,
                'markup' => $markup,
            ));
            return;
        }

        $run_id = intval($payload['run_id'] ?? 0);
        $projection = isset($payload['projection']) && is_array($payload['projection']) ? $payload['projection'] : array();
        if ($run_id <= 0 || $projection === array()) {
            wp_send_json_error(array('message' => 'Invalid projection sync payload'), 422);
            return;
        }

        $sync = $this->aysa_technical_schema_apply_local_projection($project_id, $run_id, $projection);
        wp_send_json_success(array(
            'local_projection_sync' => $sync,
        ));
    }

    public function handle_technical_redirects_projection_sync() {
        $secret = $this->get_app_aysa_internal_secret();
        if ($secret === '') {
            wp_send_json_error(array('message' => 'Internal secret unavailable.'), 500);
            return;
        }

        $provided = $this->aysa_get_bearer_authorization_token();
        if ($provided === '' || !hash_equals($secret, $provided)) {
            wp_send_json_error(array('message' => 'Unauthorized'), 401);
            return;
        }

        $raw = file_get_contents('php://input');
        $payload = json_decode(is_string($raw) ? $raw : '', true);
        if (!is_array($payload)) {
            wp_send_json_error(array('message' => 'Invalid JSON payload'), 400);
            return;
        }

        $project_id = intval($payload['project_id'] ?? 0);
        $operation = sanitize_key((string) ($payload['operation'] ?? ''));
        if ($project_id <= 0) {
            wp_send_json_error(array('message' => 'Invalid project id'), 422);
            return;
        }

        if ($operation === 'reset_state') {
            $sync = $this->aysa_technical_redirects_reset_local_projection($project_id);
            wp_send_json_success(array(
                'local_projection_sync' => $sync,
            ));
            return;
        }

        if ($operation === 'refresh_snapshot') {
            $snapshot = $this->aysa_adjustments_get_technical_errors_snapshot($project_id);
            $state = get_option($this->aysa_technical_redirects_state_key($project_id), array());
            if (!is_array($state)) {
                $state = array();
            }
            $state = array_merge($state, array(
                'project_id' => $project_id,
                'updated_at' => current_time('mysql'),
                'updated_at_unix' => time(),
                'run_id' => intval($state['run_id'] ?? 0),
                'status' => 'completed',
                'source' => 'worker_local_snapshot',
                'orchestration' => 'worker_managed',
                'snapshot' => $snapshot,
            ));
            update_option($this->aysa_technical_redirects_state_key($project_id), $state, false);
            wp_send_json_success(array(
                'snapshot' => $snapshot,
            ));
            return;
        }

        $run_id = intval($payload['run_id'] ?? 0);
        $projection = isset($payload['projection']) && is_array($payload['projection']) ? $payload['projection'] : array();
        if ($run_id <= 0 || $projection === array()) {
            wp_send_json_error(array('message' => 'Invalid projection sync payload'), 422);
            return;
        }

        $sync = $this->aysa_technical_redirects_apply_local_projection($project_id, $run_id, $projection);
        wp_send_json_success(array(
            'local_projection_sync' => $sync,
        ));
    }

    public function handle_technical_sitemap_robots_projection_sync() {
        $secret = $this->get_app_aysa_internal_secret();
        if ($secret === '') {
            wp_send_json_error(array('message' => 'Internal secret unavailable.'), 500);
            return;
        }

        $provided = $this->aysa_get_bearer_authorization_token();
        if ($provided === '' || !hash_equals($secret, $provided)) {
            wp_send_json_error(array('message' => 'Unauthorized'), 401);
            return;
        }

        $raw = file_get_contents('php://input');
        $payload = json_decode(is_string($raw) ? $raw : '', true);
        if (!is_array($payload)) {
            wp_send_json_error(array('message' => 'Invalid JSON payload'), 400);
            return;
        }

        $project_id = intval($payload['project_id'] ?? 0);
        $operation = sanitize_key((string) ($payload['operation'] ?? ''));
        if ($project_id <= 0) {
            wp_send_json_error(array('message' => 'Invalid project id'), 422);
            return;
        }

        if ($operation === 'reset_state') {
            $sync = $this->aysa_technical_sitemap_robots_reset_local_projection($project_id);
            wp_send_json_success(array(
                'local_projection_sync' => $sync,
            ));
            return;
        }

        if ($operation === 'refresh_snapshot') {
            $target_url = isset($payload['target_url']) ? esc_url_raw((string) $payload['target_url']) : '';
            if ($target_url === '') {
                $target_url = $this->aysa_technical_resolve_target_url();
            }
            $audit = $this->aysa_technical_build_sitemap_robots_audit($target_url);
            $state = get_option($this->aysa_technical_sitemap_robots_state_key($project_id), array());
            if (!is_array($state)) {
                $state = array();
            }
            $state = array_merge($state, array(
                'project_id' => $project_id,
                'updated_at' => current_time('mysql'),
                'updated_at_unix' => time(),
                'data' => $audit,
                'target_url' => $target_url,
                'source' => 'worker_local_snapshot',
                'orchestration' => 'worker_managed',
            ));
            update_option($this->aysa_technical_sitemap_robots_state_key($project_id), $state, false);
            wp_send_json_success(array(
                'snapshot' => $this->aysa_adjustments_get_technical_sitemap_robots_snapshot($project_id),
            ));
            return;
        }

        $run_id = intval($payload['run_id'] ?? 0);
        $projection = isset($payload['projection']) && is_array($payload['projection']) ? $payload['projection'] : array();
        if ($run_id <= 0 || $projection === array()) {
            wp_send_json_error(array('message' => 'Invalid projection sync payload'), 422);
            return;
        }

        $sync = $this->aysa_technical_sitemap_robots_apply_local_projection($project_id, $run_id, $projection);
        wp_send_json_success(array(
            'local_projection_sync' => $sync,
        ));
    }

    private function aysa_get_bearer_authorization_token() {
        $header = '';
        if (!empty($_SERVER['HTTP_AUTHORIZATION'])) {
            $header = (string) $_SERVER['HTTP_AUTHORIZATION'];
        } elseif (!empty($_SERVER['Authorization'])) {
            $header = (string) $_SERVER['Authorization'];
        } elseif (function_exists('apache_request_headers')) {
            $headers = apache_request_headers();
            if (is_array($headers)) {
                foreach ($headers as $key => $value) {
                    if (strtolower((string) $key) === 'authorization') {
                        $header = (string) $value;
                        break;
                    }
                }
            }
        }

        if ($header === '' || stripos($header, 'Bearer ') !== 0) {
            return '';
        }

        return trim(substr($header, 7));
    }

    private function aysa_keyword_research_projection_applied_run_option_key($project_id) {
        return 'aysa_keyword_research_projection_applied_run_' . intval($project_id);
    }

    private function aysa_keyword_research_projection_last_content_row_count_option_key($project_id) {
        return 'aysa_keyword_research_projection_last_content_row_count_' . intval($project_id);
    }

    private function aysa_keyword_research_projection_last_calendar_row_count_option_key($project_id) {
        return 'aysa_keyword_research_projection_last_calendar_row_count_' . intval($project_id);
    }

    private function aysa_keyword_research_projection_last_applied_at_option_key($project_id) {
        return 'aysa_keyword_research_projection_last_applied_at_' . intval($project_id);
    }

    private function aysa_technical_site_audit_apply_local_projection($project_id, $run_id, $projection) {
        $project_id = $this->aysa_resolve_technical_project_id(intval($project_id));
        $run_id = intval($run_id);
        if ($project_id <= 0 || $run_id <= 0 || !is_array($projection)) {
            return array(
                'applied' => false,
                'reason' => 'invalid_projection_payload',
            );
        }

        $state_key = $this->aysa_technical_site_audit_state_key($project_id);
        $existing = get_option($state_key, array());
        if (!is_array($existing)) {
            $existing = array();
        }

        $summary = isset($projection['summary']) && is_array($projection['summary']) ? $projection['summary'] : array();
        $run = isset($projection['run']) && is_array($projection['run']) ? $projection['run'] : array();
        $snapshot = isset($projection['site_audit_snapshot']) && is_array($projection['site_audit_snapshot'])
            ? $projection['site_audit_snapshot']
            : array();
        $files = isset($projection['result_files']) && is_array($projection['result_files'])
            ? array_values($projection['result_files'])
            : array();
        $overview = !empty($snapshot['overview']) && is_array($snapshot['overview']) ? $snapshot['overview'] : array();
        $pages_crawled = intval($overview['pages_crawled'] ?? ($summary['pages_crawled'] ?? 0));
        $csv_file_count = 0;
        foreach ($files as $file) {
            if (!is_array($file)) {
                continue;
            }
            $filename = strtolower((string) ($file['filename'] ?? ''));
            $type = strtolower((string) ($file['type'] ?? ''));
            if ($type === 'csv' || substr($filename, -4) === '.csv') {
                $csv_file_count++;
            }
        }
        $is_valid_crawl = $pages_crawled > 0 && $csv_file_count > 0;
        $incoming_status = sanitize_key((string) ($run['status'] ?? ($summary['status'] ?? 'completed')));
        if (!$is_valid_crawl && in_array($incoming_status, array('completed', 'complete', 'success', 'succeeded', 'ready'), true)) {
            $incoming_status = 'invalid';
        }

        $state = array_merge($existing, array(
            'project_id' => $project_id,
            'run_id' => $run_id,
            'last_applied_run_id' => $run_id,
            'status' => $incoming_status,
            'valid_crawl' => $is_valid_crawl,
            'invalid_reason' => $is_valid_crawl ? '' : 'empty_or_missing_crawl_artifacts',
            'job_id' => sanitize_text_field((string) ($projection['job_id'] ?? ($existing['job_id'] ?? ''))),
            'target_url' => esc_url_raw((string) ($summary['target_url'] ?? ($existing['target_url'] ?? ''))),
            'started_at' => sanitize_text_field((string) ($existing['started_at'] ?? current_time('mysql'))),
            'updated_at' => current_time('mysql'),
            'provider' => 'aysa_site_audit',
            'source' => 'worker_projection_sync',
            'orchestration' => 'worker_managed',
            'current_task_key' => sanitize_key((string) ($run['current_task_key'] ?? '')),
            'current_milestone' => sanitize_text_field((string) ($run['current_milestone'] ?? '')),
            'audit_snapshot' => $snapshot,
            'result_files' => $files,
            'latest_output_dir' => sanitize_text_field((string) ($projection['latest_output_dir'] ?? ($existing['latest_output_dir'] ?? ''))),
            'last_applied_at' => current_time('mysql'),
        ));

        $version_id = 0;
        if (!empty($snapshot) || !empty($files)) {
            $version_id = intval($this->aysa_sf_site_audit_persist_version($project_id, $state));
            if ($version_id > 0) {
                $latest_version = $this->aysa_sf_site_audit_get_latest_version_row($project_id);
                if (is_array($latest_version) && !empty($latest_version)) {
                    $state['version_id'] = intval($latest_version['id'] ?? 0);
                    $state['version_number'] = intval($latest_version['version_number'] ?? 0);
                }
            }
        }

        update_option($state_key, $state, false);

        return array(
            'applied' => true,
            'run_id' => $run_id,
            'status' => (string) ($state['status'] ?? 'completed'),
            'valid_crawl' => !empty($state['valid_crawl']),
            'job_id' => (string) ($state['job_id'] ?? ''),
            'snapshot_version_id' => $version_id,
            'result_file_count' => count($files),
        );
    }

    private function aysa_technical_site_audit_reset_local_projection($project_id) {
        $project_id = $this->aysa_resolve_technical_project_id(intval($project_id));
        if ($project_id <= 0) {
            return array(
                'applied' => false,
                'reason' => 'invalid_project_id',
            );
        }

        update_option($this->aysa_technical_site_audit_state_key($project_id), array(
            'project_id' => $project_id,
            'run_id' => 0,
            'last_applied_run_id' => 0,
            'job_id' => '',
            'status' => 'idle',
            'source' => 'manual_reset',
            'orchestration' => 'worker_managed',
            'updated_at' => current_time('mysql'),
        ), false);

        return array(
            'applied' => true,
            'status' => 'idle',
        );
    }

    private function aysa_technical_pagespeed_apply_local_projection($project_id, $run_id, $projection) {
        $project_id = intval($project_id);
        $run_id = intval($run_id);
        if ($project_id <= 0 || $run_id <= 0 || !is_array($projection)) {
            return array(
                'applied' => false,
                'reason' => 'invalid_projection_payload',
            );
        }

        $state_key = $this->aysa_technical_pagespeed_state_key($project_id);
        $existing = get_option($state_key, array());
        if (!is_array($existing)) {
            $existing = array();
        }

        $summary = isset($projection['summary']) && is_array($projection['summary']) ? $projection['summary'] : array();
        $run = isset($projection['run']) && is_array($projection['run']) ? $projection['run'] : array();
        $snapshot = isset($projection['pagespeed_snapshot']) && is_array($projection['pagespeed_snapshot'])
            ? $projection['pagespeed_snapshot']
            : array();
        $raw = isset($snapshot['raw']) && is_array($snapshot['raw']) ? $snapshot['raw'] : array();

        $state = array_merge($existing, array(
            'project_id' => $project_id,
            'run_id' => $run_id,
            'last_applied_run_id' => $run_id,
            'status' => sanitize_key((string) ($run['status'] ?? ($summary['status'] ?? 'completed'))),
            'target_url' => esc_url_raw((string) ($summary['target_url'] ?? ($snapshot['url'] ?? ($existing['target_url'] ?? '')))),
            'started_at' => sanitize_text_field((string) ($existing['started_at'] ?? current_time('mysql'))),
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'provider' => 'google_pagespeed_api',
            'source' => 'worker_projection_sync',
            'orchestration' => 'worker_managed',
            'data' => $raw,
        ));

        update_option($state_key, $state, false);

        return array(
            'applied' => true,
            'run_id' => $run_id,
            'status' => (string) ($state['status'] ?? 'completed'),
            'overall_score' => intval($summary['overall_score'] ?? 0),
        );
    }

    private function aysa_technical_pagespeed_reset_local_projection($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array(
                'applied' => false,
                'reason' => 'invalid_project_id',
            );
        }

        update_option($this->aysa_technical_pagespeed_state_key($project_id), array(
            'project_id' => $project_id,
            'run_id' => 0,
            'last_applied_run_id' => 0,
            'status' => 'idle',
            'target_url' => '',
            'source' => 'manual_reset',
            'orchestration' => 'worker_managed',
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'data' => array(),
        ), false);

        return array(
            'applied' => true,
            'status' => 'idle',
        );
    }

    private function aysa_technical_schema_apply_local_projection($project_id, $run_id, $projection) {
        $project_id = intval($project_id);
        $run_id = intval($run_id);
        if ($project_id <= 0 || $run_id <= 0 || !is_array($projection)) {
            return array(
                'applied' => false,
                'reason' => 'invalid_projection_payload',
            );
        }

        $state_key = $this->aysa_technical_schema_state_key($project_id);
        $existing = get_option($state_key, array());
        if (!is_array($existing)) {
            $existing = array();
        }

        $summary = isset($projection['summary']) && is_array($projection['summary']) ? $projection['summary'] : array();
        $run = isset($projection['run']) && is_array($projection['run']) ? $projection['run'] : array();
        $snapshot = isset($projection['schema_snapshot']) && is_array($projection['schema_snapshot'])
            ? $projection['schema_snapshot']
            : array();
        $markup = isset($projection['schema_markup_snapshot']) && is_array($projection['schema_markup_snapshot'])
            ? $projection['schema_markup_snapshot']
            : array();

        if ($snapshot !== array()) {
            $this->aysa_adjustments_write_technical_schema_snapshot_cache($project_id, $snapshot);
        }

        $state = array_merge($existing, array(
            'project_id' => $project_id,
            'run_id' => $run_id,
            'last_applied_run_id' => $run_id,
            'status' => sanitize_key((string) ($run['status'] ?? ($summary['status'] ?? 'completed'))),
            'started_at' => sanitize_text_field((string) ($existing['started_at'] ?? current_time('mysql'))),
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'provider' => 'wp_local_snapshot',
            'source' => 'worker_projection_sync',
            'orchestration' => 'worker_managed',
            'schema_markup_snapshot' => $markup,
        ));

        update_option($state_key, $state, false);

        return array(
            'applied' => true,
            'run_id' => $run_id,
            'status' => (string) ($state['status'] ?? 'completed'),
            'total_pages' => intval($summary['total_pages'] ?? 0),
        );
    }

    private function aysa_technical_schema_reset_local_projection($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array(
                'applied' => false,
                'reason' => 'invalid_project_id',
            );
        }

        update_option($this->aysa_technical_schema_state_key($project_id), array(
            'project_id' => $project_id,
            'run_id' => 0,
            'last_applied_run_id' => 0,
            'status' => 'idle',
            'source' => 'manual_reset',
            'orchestration' => 'worker_managed',
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'schema_markup_snapshot' => array(),
        ), false);
        $this->aysa_adjustments_delete_technical_schema_snapshot_cache($project_id);

        return array(
            'applied' => true,
            'status' => 'idle',
        );
    }

    private function aysa_technical_redirects_apply_local_projection($project_id, $run_id, $projection) {
        $project_id = intval($project_id);
        $run_id = intval($run_id);
        if ($project_id <= 0 || $run_id <= 0 || !is_array($projection)) {
            return array(
                'applied' => false,
                'reason' => 'invalid_projection_payload',
            );
        }

        $state_key = $this->aysa_technical_redirects_state_key($project_id);
        $existing = get_option($state_key, array());
        if (!is_array($existing)) {
            $existing = array();
        }

        $summary = isset($projection['summary']) && is_array($projection['summary']) ? $projection['summary'] : array();
        $run = isset($projection['run']) && is_array($projection['run']) ? $projection['run'] : array();
        $snapshot = isset($projection['technical_errors_snapshot']) && is_array($projection['technical_errors_snapshot'])
            ? $projection['technical_errors_snapshot']
            : array();

        $state = array_merge($existing, array(
            'project_id' => $project_id,
            'run_id' => $run_id,
            'last_applied_run_id' => $run_id,
            'status' => sanitize_key((string) ($run['status'] ?? ($summary['status'] ?? 'completed'))),
            'started_at' => sanitize_text_field((string) ($existing['started_at'] ?? current_time('mysql'))),
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'provider' => 'wp_local_snapshot',
            'source' => 'worker_projection_sync',
            'orchestration' => 'worker_managed',
            'snapshot' => $snapshot,
        ));

        update_option($state_key, $state, false);

        return array(
            'applied' => true,
            'run_id' => $run_id,
            'status' => (string) ($state['status'] ?? 'completed'),
            'total_404' => intval($summary['total_404'] ?? 0),
        );
    }

    private function aysa_technical_redirects_reset_local_projection($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array(
                'applied' => false,
                'reason' => 'invalid_project_id',
            );
        }

        update_option($this->aysa_technical_redirects_state_key($project_id), array(
            'project_id' => $project_id,
            'run_id' => 0,
            'last_applied_run_id' => 0,
            'status' => 'idle',
            'source' => 'manual_reset',
            'orchestration' => 'worker_managed',
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'snapshot' => array(),
        ), false);

        return array(
            'applied' => true,
            'status' => 'idle',
        );
    }

    private function aysa_technical_sitemap_robots_apply_local_projection($project_id, $run_id, $projection) {
        $project_id = intval($project_id);
        $run_id = intval($run_id);
        if ($project_id <= 0 || $run_id <= 0 || !is_array($projection)) {
            return array(
                'applied' => false,
                'reason' => 'invalid_projection_payload',
            );
        }

        $state_key = $this->aysa_technical_sitemap_robots_state_key($project_id);
        $existing = get_option($state_key, array());
        if (!is_array($existing)) {
            $existing = array();
        }

        $summary = isset($projection['summary']) && is_array($projection['summary']) ? $projection['summary'] : array();
        $run = isset($projection['run']) && is_array($projection['run']) ? $projection['run'] : array();
        $snapshot = isset($projection['sitemap_robots_snapshot']) && is_array($projection['sitemap_robots_snapshot'])
            ? $projection['sitemap_robots_snapshot']
            : array();
        $target_url = esc_url_raw((string) ($summary['target_url'] ?? ($snapshot['target_url'] ?? ($existing['target_url'] ?? ''))));
        $raw_data = isset($existing['data']) && is_array($existing['data']) ? $existing['data'] : array();
        if ($snapshot !== array() && $raw_data === array()) {
            $raw_data = $this->aysa_technical_sitemap_robots_snapshot_to_raw_data($snapshot, $target_url);
        }

        $state = array_merge($existing, array(
            'project_id' => $project_id,
            'run_id' => $run_id,
            'last_applied_run_id' => $run_id,
            'status' => sanitize_key((string) ($run['status'] ?? ($summary['status'] ?? 'completed'))),
            'target_url' => $target_url,
            'started_at' => sanitize_text_field((string) ($existing['started_at'] ?? current_time('mysql'))),
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'provider' => 'wp_local_snapshot',
            'source' => 'worker_projection_sync',
            'orchestration' => 'worker_managed',
            'data' => $raw_data,
        ));

        update_option($state_key, $state, false);

        return array(
            'applied' => true,
            'run_id' => $run_id,
            'status' => (string) ($state['status'] ?? 'completed'),
            'checks' => intval($summary['checks'] ?? 0),
        );
    }

    private function aysa_technical_sitemap_robots_reset_local_projection($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array(
                'applied' => false,
                'reason' => 'invalid_project_id',
            );
        }

        update_option($this->aysa_technical_sitemap_robots_state_key($project_id), array(
            'project_id' => $project_id,
            'run_id' => 0,
            'last_applied_run_id' => 0,
            'status' => 'idle',
            'target_url' => '',
            'source' => 'manual_reset',
            'orchestration' => 'worker_managed',
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'data' => array(),
        ), false);

        return array(
            'applied' => true,
            'status' => 'idle',
        );
    }

    private function aysa_technical_sitemap_robots_snapshot_to_raw_data($snapshot, $target_url = '') {
        $snapshot = is_array($snapshot) ? $snapshot : array();
        return array(
            'target_url' => $target_url !== '' ? $target_url : esc_url_raw((string) ($snapshot['target_url'] ?? '')),
            'primary_sitemap' => esc_url_raw((string) ($snapshot['primary_sitemap'] ?? '')),
            'summary' => array(
                'errors' => intval($snapshot['errors'] ?? 0),
                'warnings' => intval($snapshot['warnings'] ?? 0),
                'passes' => intval($snapshot['passes'] ?? 0),
                'robots_health' => sanitize_text_field((string) ($snapshot['robots_health'] ?? '')),
                'sitemap_health' => sanitize_text_field((string) ($snapshot['sitemap_health'] ?? '')),
            ),
            'checks' => isset($snapshot['rows']) && is_array($snapshot['rows']) ? array_values($snapshot['rows']) : array(),
            'sitemaps' => isset($snapshot['sitemaps']) && is_array($snapshot['sitemaps']) ? array_values($snapshot['sitemaps']) : array(),
            'robots' => array(
                'source_label' => isset($snapshot['robots_rows'][0]['value']) ? sanitize_text_field((string) $snapshot['robots_rows'][0]['value']) : '',
                'content' => isset($snapshot['robots_rows'][2]['value']) ? sanitize_textarea_field((string) $snapshot['robots_rows'][2]['value']) : '',
            ),
        );
    }

    private function aysa_keyword_research_apply_local_projection($project_id, $run_id, $projection) {
        $project_id = intval($project_id);
        $run_id = intval($run_id);
        if ($project_id <= 0 || $run_id <= 0 || !is_array($projection)) {
            return array(
                'applied' => false,
                'reason' => 'invalid_projection_payload',
            );
        }

        $last_applied_run = intval(get_option($this->aysa_keyword_research_projection_applied_run_option_key($project_id), 0));
        $requires_reapply = $last_applied_run === $run_id
            && $this->aysa_keyword_research_projection_requires_reapply($project_id, $run_id, $projection);
        if ($last_applied_run > $run_id || ($last_applied_run === $run_id && !$requires_reapply)) {
            return array(
                'applied' => false,
                'already_applied' => true,
                'run_id' => $last_applied_run,
            );
        }

        $keyword_snapshot = isset($projection['keyword_snapshot']) && is_array($projection['keyword_snapshot'])
            ? $projection['keyword_snapshot']
            : array();
        $keywords = isset($keyword_snapshot['keywords']) && is_array($keyword_snapshot['keywords'])
            ? $keyword_snapshot['keywords']
            : array();
        $persisted_keywords = $this->aysa_keyword_discovery_persist_keywords($project_id, $keywords);
        update_option($this->aysa_keyword_discovery_snapshot_key($project_id), array(
            'run_id' => (string) ($keyword_snapshot['run_id'] ?? $run_id),
            'project_id' => $project_id,
            'fetched_at' => intval($keyword_snapshot['fetched_at'] ?? time()),
            'count' => intval($keyword_snapshot['count'] ?? count($keywords)),
            'keywords' => $keywords,
        ), false);
        $this->aysa_keyword_discovery_save_status($project_id, array(
            'run_id' => (string) $run_id,
            'refresh_kind' => 'gsc_auto',
            'status' => 'completed',
            'total' => intval($keyword_snapshot['count'] ?? count($keywords)),
            'processed' => intval($keyword_snapshot['count'] ?? count($keywords)),
            'staged' => 0,
            'inserted' => intval($persisted_keywords['inserted'] ?? 0),
            'updated' => intval($persisted_keywords['updated'] ?? 0),
            'apply_to_wp' => 1,
            'last_error' => '',
            'metrics_notice' => '',
            'notification_sent' => '0',
            'started_at' => time(),
            'updated_at' => time(),
            'finished_at' => time(),
        ));

        $association_rows = $this->aysa_keyword_research_projection_build_local_associations(
            $project_id,
            isset($projection['local_associations']) && is_array($projection['local_associations'])
                ? $projection['local_associations']
                : array()
        );
        if (!empty($association_rows)) {
            $this->save_keyword_discovery_local_associations($project_id, $association_rows);
        }

        if (isset($projection['competitor_identification']) && is_array($projection['competitor_identification'])) {
            update_option($this->aysa_competitor_identification_cache_key($project_id), $projection['competitor_identification'], false);
        }
        if (isset($projection['competitor_gap']) && is_array($projection['competitor_gap'])) {
            update_option($this->aysa_competitor_gap_cache_key($project_id), $projection['competitor_gap'], false);
        }
        if (isset($projection['backlink_summary']) && is_array($projection['backlink_summary'])) {
            update_option('aysa_backlink_summary_' . $project_id, $projection['backlink_summary'], false);
        }

        $content_strategy_result = $this->aysa_keyword_research_projection_apply_content_strategy_rows(
            $project_id,
            $run_id,
            isset($projection['content_strategy_rows']) && is_array($projection['content_strategy_rows'])
                ? $projection['content_strategy_rows']
                : array()
        );

        update_option($this->aysa_keyword_research_projection_applied_run_option_key($project_id), $run_id, false);
        update_option(
            $this->aysa_keyword_research_projection_last_content_row_count_option_key($project_id),
            intval($content_strategy_result['saved'] ?? 0),
            false
        );
        update_option(
            $this->aysa_keyword_research_projection_last_calendar_row_count_option_key($project_id),
            count(isset($projection['publishing_calendar_rows']) && is_array($projection['publishing_calendar_rows']) ? $projection['publishing_calendar_rows'] : array()),
            false
        );
        update_option(
            $this->aysa_keyword_research_projection_last_applied_at_option_key($project_id),
            current_time('mysql'),
            false
        );

        return array(
            'applied' => true,
            'run_id' => $run_id,
            'keywords_total' => intval($keyword_snapshot['count'] ?? count($keywords)),
            'associations_total' => count($association_rows),
            'content_strategy_saved' => intval($content_strategy_result['saved'] ?? 0),
            'calendar_rows' => count(isset($projection['publishing_calendar_rows']) && is_array($projection['publishing_calendar_rows']) ? $projection['publishing_calendar_rows'] : array()),
            'reapplied' => $requires_reapply,
        );
    }

    private function aysa_keyword_research_projection_requires_reapply($project_id, $run_id, $projection) {
        global $wpdb;

        $project_id = intval($project_id);
        $run_id = intval($run_id);
        if ($project_id <= 0 || $run_id <= 0 || !is_array($projection)) {
            return false;
        }

        $association_rows = isset($projection['local_associations']) && is_array($projection['local_associations'])
            ? $projection['local_associations']
            : array();
        if ($association_rows !== array()) {
            $projected_associations = $this->aysa_keyword_research_projection_build_local_associations($project_id, $association_rows);
            $existing_associations = $this->get_keyword_discovery_local_associations($project_id);
            if (count($projected_associations) !== count($existing_associations)) {
                return true;
            }
            $projected_signature = array_map(static function ($row) {
                return implode('|', array(
                    (string) ($row['keyword'] ?? ''),
                    (string) ($row['associatedOn'] ?? ''),
                    (string) ($row['idIntern'] ?? ''),
                    (string) intval($row['isMain'] ?? 0),
                    (string) ($row['secondKeyword'] ?? ''),
                ));
            }, $projected_associations);
            $existing_signature = array_map(static function ($row) {
                return implode('|', array(
                    (string) ($row['keyword'] ?? ''),
                    (string) ($row['associatedOn'] ?? ''),
                    (string) ($row['idIntern'] ?? ''),
                    (string) intval($row['isMain'] ?? 0),
                    (string) ($row['secondKeyword'] ?? ''),
                ));
            }, $existing_associations);
            sort($projected_signature);
            sort($existing_signature);
            if ($projected_signature !== $existing_signature) {
                return true;
            }
        }

        $content_rows = isset($projection['content_strategy_rows']) && is_array($projection['content_strategy_rows'])
            ? $projection['content_strategy_rows']
            : array();
        if ($content_rows === array()) {
            return false;
        }

        $table = $wpdb->prefix . 'aysa_content_strategies';
        $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($exists !== $table) {
            return false;
        }

        $plan_batch = 'keyword_research_run_' . $run_id;
        $existing_count = intval($wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*)
             FROM {$table}
             WHERE project_id = %d
               AND plan_batch = %s
               AND COALESCE(automation_status, '') <> 'deleted'
               AND duplicate_of_strategy_id IS NULL
               AND superseded_by_strategy_id IS NULL",
            $project_id,
            $plan_batch
        )));

        if ($existing_count === 0) {
            return true;
        }

        if ($existing_count !== count($content_rows)) {
            return true;
        }

        $projected_types = array();
        foreach ($content_rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $type = sanitize_key((string) ($row['strategy_type'] ?? $row['strategy_module'] ?? 'keyword_research'));
            if ($type !== '') {
                $projected_types[$type] = true;
            }
        }
        $projected_type_keys = array_keys($projected_types);
        sort($projected_type_keys);

        $existing_types = $wpdb->get_col($wpdb->prepare(
            "SELECT DISTINCT strategy_type
             FROM {$table}
             WHERE project_id = %d
               AND plan_batch = %s
               AND COALESCE(automation_status, '') <> 'deleted'
               AND duplicate_of_strategy_id IS NULL
               AND superseded_by_strategy_id IS NULL",
            $project_id,
            $plan_batch
        ));
        $existing_type_keys = array_values(array_filter(array_map('sanitize_key', is_array($existing_types) ? $existing_types : array())));
        sort($existing_type_keys);

        return $projected_type_keys !== $existing_type_keys;
    }

    private function aysa_keyword_research_projection_build_local_associations($project_id, $rows) {
        $project_id = intval($project_id);
        if ($project_id <= 0 || !is_array($rows)) {
            return array();
        }

        $normalized = array();
        $safe_secondaries_by_entity = array();
        $secondary_count_by_entity = array();
        $safe_primary_by_entity = $this->aysa_keyword_mapping_execution_choose_primaries($rows);
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['keyword'] ?? ''));
            $target_url = trim((string) ($row['targetUrl'] ?? $row['target_url'] ?? ''));
            if ($keyword === '' || $target_url === '') {
                continue;
            }

            $target = $this->aysa_keyword_research_projection_resolve_target_by_url($target_url);
            if (!is_array($target) || empty($target['associated_on']) || intval($target['id_intern'] ?? 0) <= 0) {
                continue;
            }

            $associated_on = (string) $target['associated_on'];
            $id_intern = intval($target['id_intern']);
            $value = trim((string) ($target['value'] ?? ($row['entityLabel'] ?? $target_url)));
            if ($value === '') {
                $value = $target_url;
            }

            $entity_key = trim((string) ($row['entityKey'] ?? $row['entity_key'] ?? $target_url));
            if ($entity_key === '') {
                $entity_key = $target_url;
            }
            $raw_is_main = !empty($row['isMain']) || !empty($row['is_main']);
            $safe_primary = isset($safe_primary_by_entity[$entity_key]) && is_array($safe_primary_by_entity[$entity_key])
                ? $safe_primary_by_entity[$entity_key]
                : array();
            $safe_primary_keyword = trim((string) ($safe_primary['keyword'] ?? ''));
            $is_main = $safe_primary_keyword !== ''
                ? strcasecmp($keyword, $safe_primary_keyword) === 0
                : $raw_is_main;
            if (!$is_main) {
                if (!$this->aysa_keyword_mapping_execution_guard_allows_secondary($keyword, $target_url, $value, $associated_on)) {
                    continue;
                }
                $limit = $this->aysa_keyword_mapping_execution_secondary_limit($target_url, $associated_on);
                $current_count = intval($secondary_count_by_entity[$entity_key] ?? 0);
                if ($current_count >= $limit) {
                    continue;
                }
                $secondary_count_by_entity[$entity_key] = $current_count + 1;
                if (!isset($safe_secondaries_by_entity[$entity_key])) {
                    $safe_secondaries_by_entity[$entity_key] = array();
                }
                $safe_secondaries_by_entity[$entity_key][] = $keyword;
            }

            $normalized[] = array(
                'id' => $this->build_keyword_discovery_stable_local_id($keyword, $associated_on, $id_intern),
                'projectId' => $project_id,
                'keyword' => $keyword,
                'associatedOn' => $associated_on,
                'idIntern' => $id_intern,
                'value' => $value,
                'desiredPosition' => null,
                'source' => 'local_fallback',
                'isMain' => $is_main ? 1 : 0,
                'secondKeyword' => trim((string) ($row['secondKeyword'] ?? $row['second_keyword'] ?? '')),
                'secondaryKeywords' => array(),
                'entityKey' => $entity_key,
                'mappingSource' => trim((string) ($row['mapping_source'] ?? $row['mappingSource'] ?? '')),
                'confidenceScore' => isset($row['confidence_score']) && is_numeric($row['confidence_score']) ? floatval($row['confidence_score']) : (isset($row['score']) && is_numeric($row['score']) ? floatval($row['score']) : null),
                'intentType' => trim((string) ($row['intent_type'] ?? $row['intentType'] ?? '')),
                'targetUrl' => $target_url,
                'executionPrimaryKeyword' => $safe_primary_keyword,
                'executionPrimarySource' => trim((string) ($safe_primary['source'] ?? 'raw_mapping_primary')),
                'executionPrimaryConfidence' => isset($safe_primary['confidence']) && is_numeric($safe_primary['confidence']) ? floatval($safe_primary['confidence']) : null,
                'executionPrimaryReason' => trim((string) ($safe_primary['reason'] ?? '')),
                'rawIsMain' => $raw_is_main ? 1 : 0,
                'updatedAt' => current_time('mysql'),
            );
        }

        foreach ($normalized as &$association) {
            if (empty($association['isMain'])) {
                continue;
            }
            $entity_key = trim((string) ($association['entityKey'] ?? ''));
            $association['secondaryKeywords'] = isset($safe_secondaries_by_entity[$entity_key])
                ? array_values($safe_secondaries_by_entity[$entity_key])
                : array();
        }
        unset($association);

        return $normalized;
    }

    private function aysa_keyword_mapping_execution_choose_primaries($rows) {
        $groups = array();
        foreach ((array) $rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['keyword'] ?? ''));
            $target_url = trim((string) ($row['targetUrl'] ?? $row['target_url'] ?? ''));
            if ($keyword === '' || $target_url === '') {
                continue;
            }
            $entity_key = trim((string) ($row['entityKey'] ?? $row['entity_key'] ?? $target_url));
            if ($entity_key === '') {
                $entity_key = $target_url;
            }
            if (!isset($groups[$entity_key])) {
                $groups[$entity_key] = array(
                    'target_url' => $target_url,
                    'label' => trim((string) ($row['entityLabel'] ?? $target_url)),
                    'rows' => array(),
                );
            }
            $groups[$entity_key]['rows'][] = $row;
        }

        $chosen = array();
        foreach ($groups as $entity_key => $group) {
            $best = null;
            $best_score = -9999;
            foreach ((array) ($group['rows'] ?? array()) as $row) {
                $score = $this->aysa_keyword_mapping_execution_primary_candidate_score(
                    $row,
                    (string) ($group['target_url'] ?? ''),
                    (string) ($group['label'] ?? '')
                );
                if ($score === null) {
                    continue;
                }
                if ($best === null || $score > $best_score) {
                    $best = $row;
                    $best_score = $score;
                }
            }
            if ($best === null) {
                foreach ((array) ($group['rows'] ?? array()) as $row) {
                    if (!empty($row['isMain']) || !empty($row['is_main'])) {
                        $best = $row;
                        $best_score = isset($row['confidence_score']) && is_numeric($row['confidence_score'])
                            ? floatval($row['confidence_score'])
                            : (isset($row['score']) && is_numeric($row['score']) ? floatval($row['score']) : 0);
                        break;
                    }
                }
            }
            if ($best !== null) {
                $chosen[$entity_key] = array(
                    'keyword' => trim((string) ($best['keyword'] ?? '')),
                    'source' => trim((string) ($best['mapping_source'] ?? $best['mappingSource'] ?? '')),
                    'confidence' => round(floatval($best_score), 4),
                    'reason' => $this->aysa_keyword_mapping_execution_primary_reason($best, (string) ($group['target_url'] ?? ''), (string) ($group['label'] ?? '')),
                );
            }
        }

        return $chosen;
    }

    private function aysa_keyword_mapping_execution_primary_candidate_score($row, $target_url, $entity_label) {
        $keyword = trim((string) ($row['keyword'] ?? ''));
        if ($keyword === '') {
            return null;
        }
        $keyword_norm = $this->aysa_keyword_mapping_execution_normalize_text($keyword);
        $label_norm = $this->aysa_keyword_mapping_execution_normalize_text($entity_label . ' ' . $target_url);
        if ($this->aysa_keyword_mapping_execution_is_noise_keyword($keyword_norm)) {
            return null;
        }

        $score = isset($row['confidence_score']) && is_numeric($row['confidence_score'])
            ? floatval($row['confidence_score'])
            : (isset($row['score']) && is_numeric($row['score']) ? floatval($row['score']) : 0);
        if (!empty($row['isMain']) || !empty($row['is_main'])) {
            $score += 0.04;
        }
        if (trim((string) ($row['mapping_source'] ?? $row['mappingSource'] ?? '')) === 'gsc') {
            $score += 0.08;
        }

        if ($this->aysa_keyword_mapping_execution_is_homepage_url($target_url)) {
            if (!$this->aysa_keyword_mapping_execution_has_commercial_intent($keyword_norm)) {
                return null;
            }
            if (preg_match('/\bbragadiru\b/u', $keyword_norm)) {
                $score += 0.45;
            }
            if (preg_match('/\bilfov\b/u', $keyword_norm)) {
                $score += 0.25;
            }
            if (preg_match('/\bbucuresti\b/u', $keyword_norm) && !preg_match('/\b(bragadiru|ilfov)\b/u', $keyword_norm)) {
                $score -= 0.18;
            }
            if (preg_match('/\b(buchet|buchete|bonsai|bujori|trandafiri|lalele|frezii|ghiveci|ursuleti|primavara|martie|nastere)\b/u', $keyword_norm)) {
                $score -= 0.5;
            }
            return $score;
        }

        if ($this->aysa_keyword_mapping_execution_is_product_target($target_url, '', $entity_label)) {
            if ($this->aysa_keyword_mapping_execution_has_informational_intent($keyword_norm)) {
                return null;
            }
            if ($this->aysa_keyword_mapping_execution_has_quantity_mismatch($keyword_norm, $label_norm)) {
                return null;
            }
            if ($this->aysa_keyword_mapping_execution_is_broad_category_keyword($keyword_norm, $label_norm)) {
                $score -= 0.45;
            }
            preg_match_all('/\b\d+\b/u', $label_norm, $label_numbers);
            foreach (array_unique($label_numbers[0] ?? array()) as $number) {
                if (preg_match('/\b' . preg_quote((string) $number, '/') . '\b/u', $keyword_norm)) {
                    $score += 0.45;
                }
            }
        }

        return $score;
    }

    private function aysa_keyword_mapping_execution_primary_reason($row, $target_url, $entity_label) {
        $keyword_norm = $this->aysa_keyword_mapping_execution_normalize_text((string) ($row['keyword'] ?? ''));
        $label_norm = $this->aysa_keyword_mapping_execution_normalize_text($entity_label . ' ' . $target_url);
        if ($this->aysa_keyword_mapping_execution_is_homepage_url($target_url) && preg_match('/\b(bragadiru|ilfov)\b/u', $keyword_norm)) {
            return 'preferred_local_homepage_intent';
        }
        if ($this->aysa_keyword_mapping_execution_is_product_target($target_url, '', $entity_label)) {
            preg_match_all('/\b\d+\b/u', $label_norm, $label_numbers);
            foreach (array_unique($label_numbers[0] ?? array()) as $number) {
                if (preg_match('/\b' . preg_quote((string) $number, '/') . '\b/u', $keyword_norm)) {
                    return 'preferred_product_specific_quantity_match';
                }
            }
            return 'preferred_product_specific_fit';
        }
        return 'preferred_highest_safe_score';
    }

    private function aysa_keyword_mapping_execution_guard_allows_secondary($keyword, $target_url, $entity_label, $associated_on) {
        $keyword_norm = $this->aysa_keyword_mapping_execution_normalize_text($keyword);
        $label_norm = $this->aysa_keyword_mapping_execution_normalize_text($entity_label . ' ' . $target_url);
        if ($keyword_norm === '') {
            return false;
        }
        if ($this->aysa_keyword_mapping_execution_is_noise_keyword($keyword_norm)) {
            return false;
        }
        if ($this->aysa_keyword_mapping_execution_is_homepage_url($target_url)) {
            return $this->aysa_keyword_mapping_execution_is_homepage_safe_secondary($keyword_norm);
        }
        if ($this->aysa_keyword_mapping_execution_is_product_target($target_url, $associated_on, $entity_label)) {
            if ($this->aysa_keyword_mapping_execution_has_informational_intent($keyword_norm)) {
                return false;
            }
            if ($this->aysa_keyword_mapping_execution_has_quantity_mismatch($keyword_norm, $label_norm)) {
                return false;
            }
            if ($this->aysa_keyword_mapping_execution_is_broad_category_keyword($keyword_norm, $label_norm)) {
                return false;
            }
        }

        return true;
    }

    private function aysa_keyword_mapping_execution_secondary_limit($target_url, $associated_on) {
        if ($this->aysa_keyword_mapping_execution_is_homepage_url($target_url)) {
            return 20;
        }
        if ($this->aysa_keyword_mapping_execution_is_product_target($target_url, $associated_on, '')) {
            return 8;
        }
        return 20;
    }

    private function aysa_keyword_mapping_execution_normalize_text($value) {
        $value = remove_accents(strtolower(trim((string) $value)));
        $value = preg_replace('/[^a-z0-9\s]+/u', ' ', $value);
        $value = preg_replace('/\s+/', ' ', is_string($value) ? $value : '');
        return trim((string) $value);
    }

    private function aysa_keyword_mapping_execution_is_homepage_url($url) {
        $path = trim((string) wp_parse_url((string) $url, PHP_URL_PATH), '/');
        return $path === '';
    }

    private function aysa_keyword_mapping_execution_is_product_target($target_url, $associated_on, $entity_label) {
        $target_url = strtolower((string) $target_url);
        $entity_label = strtolower((string) $entity_label);
        return strpos($target_url, '/p/') !== false
            || strpos($target_url, '/product/') !== false
            || strpos($entity_label, 'pret') !== false
            || strpos($entity_label, 'buchet') !== false;
    }

    private function aysa_keyword_mapping_execution_is_noise_keyword($keyword_norm) {
        if (preg_match('/\b(near me|interflora|teleflora|amazon|emag|etsy|flora ri|florarir|florariw|floratie|floraria trandafirul)\b/u', $keyword_norm)) {
            return true;
        }
        if (strpos($keyword_norm, '_') !== false) {
            return true;
        }
        if (preg_match('/\b(uk|usa|nursery near me)\b/u', $keyword_norm)) {
            return true;
        }
        return false;
    }

    private function aysa_keyword_mapping_execution_is_homepage_safe_secondary($keyword_norm) {
        if (!$this->aysa_keyword_mapping_execution_has_commercial_intent($keyword_norm)) {
            return false;
        }
        if (!preg_match('/\b(bragadiru|ilfov|bucuresti|online|livrare|comanda|florarie|flori)\b/u', $keyword_norm)) {
            return false;
        }
        if (preg_match('/\b(buchet|buchete|bonsai|bujori|trandafiri|lalele|frezii|ghiveci|ursuleti|plus|sampanie|primavara|martie|indragostitilor|nastere)\b/u', $keyword_norm)) {
            return false;
        }
        return true;
    }

    private function aysa_keyword_mapping_execution_has_commercial_intent($keyword_norm) {
        return (bool) preg_match('/\b(florarie|flori|buchet|buchete|comanda|livrare|online|pret|cumpara|aranjamente|coroane)\b/u', $keyword_norm);
    }

    private function aysa_keyword_mapping_execution_has_informational_intent($keyword_norm) {
        return (bool) preg_match('/\b(cum|ce|cand|unde|idei|ghid|sfaturi|semnifica|inseamna|blog)\b/u', $keyword_norm);
    }

    private function aysa_keyword_mapping_execution_has_quantity_mismatch($keyword_norm, $label_norm) {
        preg_match_all('/\b\d+\b/u', $keyword_norm, $keyword_numbers);
        preg_match_all('/\b\d+\b/u', $label_norm, $label_numbers);
        $keyword_numbers = array_values(array_unique($keyword_numbers[0] ?? array()));
        $label_numbers = array_values(array_unique($label_numbers[0] ?? array()));
        if ($keyword_numbers === array() || $label_numbers === array()) {
            return false;
        }
        foreach ($keyword_numbers as $number) {
            if (!in_array($number, $label_numbers, true)) {
                return true;
            }
        }
        return false;
    }

    private function aysa_keyword_mapping_execution_is_broad_category_keyword($keyword_norm, $label_norm) {
        if (preg_match('/\b(buchete de flori|buchet trandafiri|flori trandafiri|flori online|florarie|flori bucuresti)\b/u', $keyword_norm)) {
            if (preg_match('/\b(3|5|7|9|11|13|15|18|19|21|25|27|30|31|51|101|rosii|mov|roz|mare)\b/u', $label_norm)) {
                return true;
            }
        }
        return false;
    }

    private function aysa_keyword_research_projection_resolve_target_by_url($url) {
        $url = esc_url_raw((string) $url);
        if ($url === '') {
            return null;
        }

        $post_id = url_to_postid($url);
        if ($post_id > 0) {
            $title = get_the_title($post_id);
            return array(
                'associated_on' => 'post',
                'id_intern' => $post_id,
                'value' => is_string($title) && $title !== '' ? $title : $url,
            );
        }

        $path = trim((string) wp_parse_url($url, PHP_URL_PATH), '/');
        if ($path !== '') {
            $slug = basename($path);
            $taxonomies = get_taxonomies(array('public' => true), 'names');
            foreach ((array) $taxonomies as $taxonomy) {
                $term = get_term_by('slug', $slug, $taxonomy);
                if (!$term || is_wp_error($term)) {
                    continue;
                }
                $term_link = get_term_link($term);
                if (is_string($term_link) && untrailingslashit($term_link) === untrailingslashit($url)) {
                    return array(
                        'associated_on' => 'term',
                        'id_intern' => intval($term->term_id),
                        'value' => (string) $term->name,
                    );
                }
            }
        }

        return array(
            'associated_on' => 'url',
            'id_intern' => intval(sprintf('%u', crc32(untrailingslashit($url)))),
            'value' => $url,
        );
    }

    private function aysa_keyword_mapping_ensure_local_associations($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array(
                'ok' => false,
                'reason' => 'invalid_project_id',
            );
        }

        $existing = $this->get_keyword_discovery_local_associations($project_id);
        if (!empty($existing)) {
            $workspace = $this->aysa_keyword_v2_cache_get($project_id, 'keyword_mapping_v2');
            if (!is_array($workspace) || empty($workspace['clusters']) || !is_array($workspace['clusters'])) {
                return array(
                    'ok' => true,
                    'source' => 'local_option',
                    'count' => count($existing),
                );
            }

            $mapped_urls = array();
            foreach ((array) $workspace['clusters'] as $cluster) {
                if (!is_array($cluster)) {
                    continue;
                }
                $mapping_status = $this->aysa_keyword_mapping_onpage_status($cluster);
                if ($mapping_status === '') {
                    continue;
                }
                $target_url = $this->aysa_keyword_mapping_extract_cluster_target_url($cluster);
                $keywords = $this->aysa_keyword_mapping_cluster_keyword_candidates($cluster);
                $normalized_url = $this->aysa_keyword_mapping_normalize_url($target_url);
                if ($normalized_url !== '') {
                    $mapped_urls[$normalized_url] = true;
                    foreach ($keywords as $keyword) {
                        $keyword_value = trim((string) ($keyword['keyword'] ?? ''));
                        if ($keyword_value !== '') {
                            $mapped_urls[$normalized_url . '|' . strtolower($keyword_value)] = true;
                        }
                    }
                }
            }

            $existing_urls = array();
            foreach ((array) $existing as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $target_url = esc_url_raw((string) ($row['target_url'] ?? $row['link'] ?? ''));
                $normalized_url = $this->aysa_keyword_mapping_normalize_url($target_url);
                if ($normalized_url !== '') {
                    $existing_urls[$normalized_url] = true;
                    $keyword = trim((string) ($row['keyword'] ?? ''));
                    if ($keyword !== '') {
                        $existing_urls[$normalized_url . '|' . strtolower($keyword)] = true;
                    }
                }
            }

            if (!array_diff_key($mapped_urls, $existing_urls)) {
                return array(
                    'ok' => true,
                    'source' => 'local_option',
                    'count' => count($existing),
                );
            }
        }

        $workspace = $this->aysa_keyword_v2_cache_get($project_id, 'keyword_mapping_v2');
        if (!is_array($workspace) || empty($workspace['clusters']) || !is_array($workspace['clusters'])) {
            $payload = array(
                'project_id' => $project_id,
                'raw_context' => $this->aysa_collect_research_context_bundle($project_id),
                'force_refresh' => 0,
            );
            $result = $this->aysa_broker_request('POST', '/v1/research/workspaces/keyword-mapping', $payload, 240);
            if (is_wp_error($result)) {
                return array(
                    'ok' => false,
                    'reason' => 'workspace_fetch_failed',
                    'error' => $result->get_error_message(),
                );
            }
            if (is_array($result) && isset($result['data']) && is_array($result['data'])) {
                $result = $result['data'];
            }
            if (!is_array($result) || empty($result['ok'])) {
                return array(
                    'ok' => false,
                    'reason' => 'workspace_invalid',
                );
            }
            $workspace = $this->aysa_keyword_v2_finalize_snapshot_contract($project_id, 'keyword_mapping_v2', $result, 'clusters');
            $this->aysa_keyword_v2_cache_set($project_id, 'keyword_mapping_v2', $workspace);
        }

        return $this->aysa_keyword_mapping_sync_local_associations_from_workspace($project_id, $workspace);
    }

    private function aysa_keyword_mapping_sync_local_associations_from_workspace($project_id, $workspace) {
        $project_id = intval($project_id);
        $workspace = is_array($workspace) ? $workspace : array();
        $clusters = isset($workspace['clusters']) && is_array($workspace['clusters']) ? $workspace['clusters'] : array();
        if ($project_id <= 0 || empty($clusters)) {
            return array(
                'ok' => false,
                'reason' => 'workspace_missing',
            );
        }

        $association_rows = array();
        $keyword_rows = array();
        $seen = array();

        foreach ($clusters as $cluster) {
            if (!is_array($cluster)) {
                continue;
            }

            $selection_state = sanitize_key((string) ($cluster['selection_state'] ?? 'review'));
            if ($selection_state === 'excluded') {
                continue;
            }

            $mapping_status = $this->aysa_keyword_mapping_onpage_status($cluster);
            if ($mapping_status === '') {
                continue;
            }

            $target_url = $this->aysa_keyword_mapping_extract_cluster_target_url($cluster);
            if ($target_url === '') {
                continue;
            }

            $target = $this->aysa_keyword_mapping_resolve_local_association_target_by_url($target_url);
            if (!is_array($target) || empty($target['associated_on']) || intval($target['id_intern'] ?? 0) <= 0) {
                continue;
            }

            $keyword_candidates = $this->aysa_keyword_mapping_cluster_keyword_candidates($cluster, $selection_state);

            foreach ($keyword_candidates as $candidate) {
                $keyword_row = isset($candidate['row']) && is_array($candidate['row']) ? $candidate['row'] : array();
                $keyword = trim((string) ($keyword_row['keyword'] ?? ''));
                if ($keyword === '') {
                    continue;
                }
                $keyword_state = sanitize_key((string) ($keyword_row['selection_state'] ?? $selection_state));
                if ($keyword_state === 'excluded') {
                    continue;
                }

                $assoc_key = strtolower($keyword) . '|' . strtolower((string) $target['associated_on']) . '|' . intval($target['id_intern']);
                if (isset($seen[$assoc_key])) {
                    continue;
                }
                $seen[$assoc_key] = true;

                $association_rows[] = array(
                    'id' => $this->build_keyword_discovery_stable_local_id($keyword, (string) $target['associated_on'], intval($target['id_intern'])),
                    'projectId' => $project_id,
                    'keyword' => $keyword,
                    'associatedOn' => (string) $target['associated_on'],
                    'idIntern' => intval($target['id_intern']),
                    'value' => (string) ($target['value'] ?? $target_url),
                    'desiredPosition' => null,
                    'source' => 'keyword_mapping_v2',
                    'updatedAt' => current_time('mysql'),
                    'link' => $target_url,
                    'target_url' => $target_url,
                    'isMain' => intval($candidate['is_main'] ?? 0),
                );

                $keyword_rows[] = array(
                    'keyword' => $keyword,
                    'target_url' => $target_url,
                    'url' => $target_url,
                    'link' => $target_url,
                    'source' => 'association_seed',
                );
            }
        }

        if (empty($association_rows)) {
            return array(
                'ok' => false,
                'reason' => 'no_mapped_keyword_associations',
            );
        }

        $this->save_keyword_discovery_local_associations($project_id, $association_rows);
        global $wpdb;
        $table = $wpdb->prefix . 'aysa_keyword_discovery';
        $table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($table_exists === $table) {
            $now = current_time('mysql');
            foreach ($keyword_rows as $row) {
                $keyword = trim((string) ($row['keyword'] ?? ''));
                $target_url = esc_url_raw((string) ($row['target_url'] ?? ''));
                if ($keyword === '' || $target_url === '') {
                    continue;
                }
                $existing_id = intval($wpdb->get_var($wpdb->prepare(
                    "SELECT id FROM {$table} WHERE project_id = %d AND keyword = %s LIMIT 1",
                    $project_id,
                    $keyword
                )));
                $data = array(
                    'project_id' => $project_id,
                    'keyword' => $keyword,
                    'target_url' => $target_url,
                    'source' => 'association_seed',
                    'updated_at' => $now,
                );
                if ($existing_id > 0) {
                    $wpdb->update($table, $data, array('id' => $existing_id));
                } else {
                    $data['created_at'] = $now;
                    $wpdb->insert($table, $data);
                }
            }
        }

        return array(
            'ok' => true,
            'source' => 'keyword_mapping_v2',
            'count' => count($association_rows),
        );
    }

    private function aysa_keyword_mapping_extract_cluster_target_url($cluster) {
        $cluster = is_array($cluster) ? $cluster : array();
        $direct_url = esc_url_raw((string) ($cluster['target_url'] ?? ''));
        if ($direct_url !== '') {
            return $direct_url;
        }
        foreach (array('matched_page', 'selected_page') as $page_key) {
            $page = isset($cluster[$page_key]) && is_array($cluster[$page_key]) ? $cluster[$page_key] : array();
            foreach (array('url', 'permalink', 'target_url') as $url_key) {
                $url = esc_url_raw((string) ($page[$url_key] ?? ''));
                if ($url !== '') {
                    return $url;
                }
            }
        }

        $evidence = isset($cluster['mapping_evidence']) && is_array($cluster['mapping_evidence']) ? $cluster['mapping_evidence'] : array();
        $page = $evidence['page'] ?? '';
        if (is_array($page)) {
            foreach (array('url', 'permalink', 'target_url') as $url_key) {
                $url = esc_url_raw((string) ($page[$url_key] ?? ''));
                if ($url !== '') {
                    return $url;
                }
            }
        } else {
            $url = esc_url_raw((string) $page);
            if ($url !== '') {
                return $url;
            }
        }

        return esc_url_raw((string) ($cluster['gsc_selected_page_url'] ?? ''));
    }

    private function aysa_keyword_mapping_resolve_local_association_target_by_url($url) {
        $url = esc_url_raw((string) $url);
        if ($url === '') {
            return null;
        }

        $post_id = url_to_postid($url);
        if ($post_id > 0) {
            $post = get_post($post_id);
            if ($post instanceof WP_Post) {
                return array(
                    'associated_on' => $post->post_type === 'product' ? 'product' : ($post->post_type === 'post' ? 'post' : 'page'),
                    'id_intern' => $post_id,
                    'value' => get_the_title($post_id) ?: $url,
                );
            }
        }

        $path = trim((string) wp_parse_url($url, PHP_URL_PATH), '/');
        if ($path === '') {
            return null;
        }

        $post_types = get_post_types(array('public' => true), 'names');
        foreach (array($path, basename($path)) as $candidate_path) {
            $post = get_page_by_path($candidate_path, OBJECT, array_values((array) $post_types));
            if ($post instanceof WP_Post) {
                return array(
                    'associated_on' => $post->post_type === 'product' ? 'product' : ($post->post_type === 'post' ? 'post' : 'page'),
                    'id_intern' => intval($post->ID),
                    'value' => get_the_title($post->ID) ?: $url,
                );
            }
        }

        $slug = basename($path);
        $taxonomies = get_taxonomies(array('public' => true), 'names');
        foreach ((array) $taxonomies as $taxonomy) {
            $term = get_term_by('slug', $slug, $taxonomy);
            if (!$term || is_wp_error($term)) {
                continue;
            }
            $term_link = get_term_link($term);
            if (is_string($term_link) && $this->aysa_keyword_mapping_normalize_url($term_link) === $this->aysa_keyword_mapping_normalize_url($url)) {
                return array(
                    'associated_on' => 'category',
                    'id_intern' => intval($term->term_id),
                    'value' => (string) $term->name,
                );
            }
        }

        return null;
    }

    private function aysa_keyword_mapping_onpage_status($cluster) {
        $cluster = is_array($cluster) ? $cluster : array();
        $selection_state = sanitize_key((string) ($cluster['selection_state'] ?? 'review'));
        if ($selection_state === 'excluded') {
            return '';
        }
        $mapping_status = sanitize_key((string) ($cluster['mapping_status'] ?? ''));
        if (in_array($mapping_status, array('mapped', 'improve_existing_page'), true)) {
            return 'mapped';
        }
        if ($mapping_status === 'review') {
            return 'review';
        }
        return '';
    }

    private function aysa_keyword_mapping_cluster_keyword_candidates($cluster, $selection_state = 'review') {
        $cluster = is_array($cluster) ? $cluster : array();
        $selection_state = sanitize_key((string) $selection_state);
        $candidates = array();
        $seen = array();
        $add_keyword = static function ($value, $is_main) use (&$candidates, &$seen, $selection_state) {
            if (is_array($value)) {
                $value = $value['keyword'] ?? $value['query'] ?? $value['label'] ?? '';
            }
            $keyword = trim((string) $value);
            if ($keyword === '') {
                return;
            }
            $key = strtolower($keyword);
            if (isset($seen[$key])) {
                return;
            }
            $seen[$key] = true;
            $candidates[] = array(
                'row' => array(
                    'keyword' => $keyword,
                    'selection_state' => $selection_state,
                ),
                'keyword' => $keyword,
                'is_main' => $is_main ? 1 : 0,
            );
        };

        $add_keyword($cluster['primary_keyword'] ?? '', true);
        $add_keyword($cluster['primary_keyword_label'] ?? '', true);
        foreach (array('secondary_keywords', 'secondary_keyword_labels', 'member_keywords', 'keywords', 'supporting_keywords') as $key) {
            foreach ((array) ($cluster[$key] ?? array()) as $keyword) {
                $add_keyword($keyword, false);
            }
        }
        if (empty($candidates)) {
            $add_keyword($cluster['label'] ?? '', true);
        }
        return $candidates;
    }

    private function aysa_keyword_mapping_normalize_url($url) {
        $url = esc_url_raw((string) $url);
        if ($url === '') {
            return '';
        }
        $parts = wp_parse_url($url);
        if (!is_array($parts) || empty($parts['host'])) {
            return strtolower(untrailingslashit($url));
        }
        $path = isset($parts['path']) ? '/' . trim((string) $parts['path'], '/') : '';
        return strtolower((string) $parts['host'] . untrailingslashit($path));
    }

    private function aysa_keyword_research_projection_apply_content_strategy_rows($project_id, $run_id, $rows) {
        global $wpdb;

        $project_id = intval($project_id);
        $run_id = intval($run_id);
        if ($project_id <= 0 || $run_id <= 0 || !is_array($rows)) {
            return array('saved' => 0);
        }

        $table = $wpdb->prefix . 'aysa_content_strategies';
        $table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($table_exists !== $table) {
            return array('saved' => 0, 'reason' => 'content_strategy_table_missing');
        }

        $plan_batch = 'keyword_research_run_' . $run_id;
        $wpdb->delete($table, array(
            'project_id' => $project_id,
            'plan_batch' => $plan_batch,
        ), array('%d', '%s'));

        $saved = 0;
        $inserted_ids = array();
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $title = sanitize_text_field((string) ($row['title'] ?? ''));
            if ($title === '') {
                continue;
            }

            $wpdb->insert($table, array(
                'project_id' => $project_id,
                'title' => $title,
                'keywords' => is_string($row['keywords'] ?? null) ? (string) $row['keywords'] : wp_json_encode($row['keywords'] ?? array()),
                'intent' => sanitize_text_field((string) ($row['intent'] ?? 'I')),
                'associated_page' => esc_url_raw((string) ($row['associated_page'] ?? '')),
                'strategy_type' => sanitize_key((string) ($row['strategy_type'] ?? $row['strategy_module'] ?? 'keyword_research')),
                'canonical_topic' => !empty($row['canonical_topic']) ? sanitize_text_field((string) $row['canonical_topic']) : null,
                'content_fingerprint' => !empty($row['content_fingerprint']) ? preg_replace('/[^a-f0-9]/', '', strtolower((string) $row['content_fingerprint'])) : null,
                'recommended_page_type' => sanitize_text_field((string) ($row['recommended_page_type'] ?? 'article')),
                'content_type' => sanitize_text_field((string) ($row['content_type'] ?? 'post')),
                'intent_layer' => !empty($row['intent_layer']) ? sanitize_key((string) $row['intent_layer']) : null,
                'journey_stage' => !empty($row['journey_stage']) ? sanitize_key((string) $row['journey_stage']) : null,
                'persona_key' => !empty($row['persona_key']) ? sanitize_key((string) $row['persona_key']) : null,
                'locality_key' => !empty($row['locality_key']) ? sanitize_key((string) $row['locality_key']) : null,
                'seasonality_key' => !empty($row['seasonality_key']) ? sanitize_key((string) $row['seasonality_key']) : null,
                'content_goal' => !empty($row['content_goal']) ? sanitize_key((string) $row['content_goal']) : null,
                'execution_stage' => sanitize_text_field((string) ($row['execution_stage'] ?? 'planned')),
                'status' => sanitize_text_field((string) ($row['status'] ?? 'proposed')),
                'duplicate_of_strategy_id' => !empty($row['duplicate_of_strategy_id']) ? intval($row['duplicate_of_strategy_id']) : null,
                'superseded_by_strategy_id' => !empty($row['superseded_by_strategy_id']) ? intval($row['superseded_by_strategy_id']) : null,
                'post_id' => null,
                'scheduled_for' => !empty($row['scheduled_for']) ? sanitize_text_field((string) $row['scheduled_for']) : null,
                'generated_at' => !empty($row['generated_at']) ? sanitize_text_field((string) $row['generated_at']) : null,
                'automation_status' => isset($row['automation_status']) ? sanitize_text_field((string) $row['automation_status']) : null,
                'automation_attempts' => intval($row['automation_attempts'] ?? 0),
                'plan_batch' => $plan_batch,
                'last_error' => isset($row['last_error']) ? sanitize_text_field((string) $row['last_error']) : null,
                'automation_updated_at' => !empty($row['automation_updated_at']) ? sanitize_text_field((string) $row['automation_updated_at']) : current_time('mysql'),
            ));
            if (!empty($wpdb->insert_id)) {
                $saved++;
                $inserted_ids[] = intval($wpdb->insert_id);
            }
        }

        if ($inserted_ids !== array()) {
            $this->aysa_keyword_research_reconcile_content_strategy_duplicates($project_id, $plan_batch, $inserted_ids);
        }

        return array('saved' => $saved);
    }

    private function aysa_keyword_research_reconcile_content_strategy_duplicates($project_id, $plan_batch, array $inserted_ids) {
        global $wpdb;

        $project_id = intval($project_id);
        if ($project_id <= 0 || $inserted_ids === array()) {
            return;
        }

        $table = $wpdb->prefix . 'aysa_content_strategies';
        foreach ($inserted_ids as $inserted_id) {
            $inserted_id = intval($inserted_id);
            if ($inserted_id <= 0) {
                continue;
            }

            $current = $wpdb->get_row($wpdb->prepare(
                "SELECT *
                 FROM {$table}
                 WHERE id = %d
                   AND project_id = %d",
                $inserted_id,
                $project_id
            ), ARRAY_A);
            if (!is_array($current)) {
                continue;
            }

            if (!empty($current['duplicate_of_strategy_id']) || !empty($current['superseded_by_strategy_id'])) {
                continue;
            }

            $canonical_topic = sanitize_text_field((string) ($current['canonical_topic'] ?? ''));
            $content_fingerprint = sanitize_text_field((string) ($current['content_fingerprint'] ?? ''));
            $associated_page = untrailingslashit((string) ($current['associated_page'] ?? ''));
            $intent_layer = sanitize_key((string) ($current['intent_layer'] ?? ''));
            $journey_stage = sanitize_key((string) ($current['journey_stage'] ?? ''));
            $content_goal = sanitize_key((string) ($current['content_goal'] ?? ''));

            $candidates = $wpdb->get_results($wpdb->prepare(
                "SELECT *
                 FROM {$table}
                 WHERE project_id = %d
                   AND id <> %d
                   AND COALESCE(automation_status, '') <> 'deleted'
                   AND duplicate_of_strategy_id IS NULL
                   AND superseded_by_strategy_id IS NULL",
                $project_id,
                $inserted_id
            ), ARRAY_A);

            $exact = array();
            $near = array();
            foreach ((array) $candidates as $candidate) {
                if (!is_array($candidate)) {
                    continue;
                }
                if ($content_fingerprint !== '' && (string) ($candidate['content_fingerprint'] ?? '') === $content_fingerprint) {
                    $exact[] = $candidate;
                    continue;
                }
                if (
                    $canonical_topic !== ''
                    && (string) ($candidate['canonical_topic'] ?? '') === $canonical_topic
                    && sanitize_key((string) ($candidate['intent_layer'] ?? '')) === $intent_layer
                    && sanitize_key((string) ($candidate['journey_stage'] ?? '')) === $journey_stage
                    && sanitize_key((string) ($candidate['content_goal'] ?? '')) === $content_goal
                    && untrailingslashit((string) ($candidate['associated_page'] ?? '')) === $associated_page
                ) {
                    $near[] = $candidate;
                }
            }

            if ($exact !== array()) {
                $exact[] = $current;
                $canonical = $this->aysa_keyword_research_pick_canonical_strategy_row($exact, $plan_batch);
                $canonical_id = intval($canonical['id'] ?? 0);
                foreach ($exact as $row) {
                    $row_id = intval($row['id'] ?? 0);
                    if ($row_id <= 0 || $row_id === $canonical_id) {
                        continue;
                    }
                    $wpdb->update(
                        $table,
                        array(
                            'status' => 'duplicate',
                            'duplicate_of_strategy_id' => $canonical_id,
                            'automation_updated_at' => current_time('mysql'),
                        ),
                        array('id' => $row_id),
                        array('%s', '%d', '%s'),
                        array('%d')
                    );
                }
                if ($canonical_id !== $inserted_id) {
                    continue;
                }
            }

            if ($near !== array()) {
                $near[] = $current;
                $canonical = $this->aysa_keyword_research_pick_canonical_strategy_row($near, $plan_batch);
                $canonical_id = intval($canonical['id'] ?? 0);
                foreach ($near as $row) {
                    $row_id = intval($row['id'] ?? 0);
                    if ($row_id <= 0 || $row_id === $canonical_id) {
                        continue;
                    }
                    $wpdb->update(
                        $table,
                        array(
                            'status' => 'superseded',
                            'superseded_by_strategy_id' => $canonical_id,
                            'automation_updated_at' => current_time('mysql'),
                        ),
                        array('id' => $row_id),
                        array('%s', '%d', '%s'),
                        array('%d')
                    );
                }
            }
        }
    }

    private function aysa_keyword_research_pick_canonical_strategy_row(array $rows, $current_plan_batch) {
        $best = null;
        $best_score = null;
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $score = $this->aysa_keyword_research_content_strategy_rank($row, $current_plan_batch);
            if ($best === null || $score > $best_score) {
                $best = $row;
                $best_score = $score;
            }
        }
        return is_array($best) ? $best : array();
    }

    private function aysa_keyword_research_content_strategy_rank(array $row, $current_plan_batch) {
        $score = 0;
        $status = strtolower((string) ($row['status'] ?? ''));
        $automation_status = strtolower((string) ($row['automation_status'] ?? ''));
        $plan_batch = (string) ($row['plan_batch'] ?? '');
        $post_id = intval($row['post_id'] ?? 0);
        $scheduled_for = trim((string) ($row['scheduled_for'] ?? ''));

        if ($post_id > 0 || $status === 'generated' || $automation_status === 'generated') {
            $score += 1000;
        } elseif ($status === 'scheduled' || $scheduled_for !== '' || in_array($automation_status, array('processing', 'queued', 'retry'), true)) {
            $score += 500;
        } elseif ($plan_batch === (string) $current_plan_batch) {
            $score += 250;
        } else {
            $score += 100;
        }

        $score += intval($row['id'] ?? 0);
        return $score;
    }

    public function handle_monitoring_conversation() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }
        if (!$this->check_authentication(false)) {
            wp_send_json_error(array('message' => 'Authentication required.'));
            return;
        }

        $key_validation = $this->aysa_validate_test_access_key(
            $this->aysa_get_effective_access_key(isset($_POST['access_key']) ? sanitize_text_field(wp_unslash((string) $_POST['access_key'])) : ''),
            isset($_POST['site_host']) ? sanitize_text_field(wp_unslash((string) $_POST['site_host'])) : ''
        );
        if (empty($key_validation['valid'])) {
            wp_send_json_error(array(
                'message' => (string) ($key_validation['message'] ?? 'Access key is invalid.'),
                'code' => 'access_key_invalid',
            ));
            return;
        }

        $mode = isset($_POST['mode']) ? sanitize_key(wp_unslash((string) $_POST['mode'])) : 'bootstrap';
        $conversation_language = isset($_POST['conversation_language']) ? sanitize_key(wp_unslash((string) $_POST['conversation_language'])) : 'en';
        if (!in_array($conversation_language, array('en', 'ro', 'bg'), true)) {
            $conversation_language = 'en';
        }
        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : intval(get_option('aysa_project_id', 0));
        $user_message = isset($_POST['user_message']) ? sanitize_textarea_field(wp_unslash((string) $_POST['user_message'])) : '';
        if ($project_id <= 0) {
            wp_send_json_error(array('message' => 'Missing project ID.'));
            return;
        }

        $auth_manager = Aysa_Auth_Manager::get_instance();
        $auth_status = $auth_manager->is_authenticated();
        $user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : 0;
        if ($user_id <= 0) {
            wp_send_json_error(array('message' => 'Invalid authentication context.'));
            return;
        }

        $client_state = array();
        if (isset($_POST['client_state'])) {
            $decoded_client_state = json_decode(wp_unslash((string) $_POST['client_state']), true);
            if (is_array($decoded_client_state)) {
                $client_state = $decoded_client_state;
            }
        }

        $local_setup_state = $this->aysa_get_local_setup_state_for_user($user_id, $project_id);
        $general = isset($local_setup_state['general']) && is_array($local_setup_state['general']) ? $local_setup_state['general'] : array();
        $website = isset($general['website']) ? $this->aysa_setup_normalize_url((string) $general['website']) : '';
        if ($website === '') {
            $website = $this->aysa_setup_normalize_url(home_url('/'));
        }

        $monitoringState = array(
            'tracked_count' => intval($client_state['tracked_count'] ?? 0),
            'refreshed_count' => intval($client_state['refreshed_count'] ?? 0),
            'stale_count' => intval($client_state['stale_count'] ?? 0),
            'stale_preview' => isset($client_state['stale_preview']) && is_array($client_state['stale_preview']) ? $client_state['stale_preview'] : array(),
            'job_status' => sanitize_text_field((string) ($client_state['job_status'] ?? 'idle')),
            'job_processed' => intval($client_state['job_processed'] ?? 0),
            'job_total' => intval($client_state['job_total'] ?? 0),
            'wallet_balance' => isset($client_state['wallet_balance']) ? intval($client_state['wallet_balance']) : null,
            'wallet_spendable' => isset($client_state['wallet_spendable']) ? intval($client_state['wallet_spendable']) : null,
            'wallet_reserved_links' => isset($client_state['wallet_reserved_links']) ? intval($client_state['wallet_reserved_links']) : null,
        );

        $endpoint = '/v1/conversation/monitoring/bootstrap';
        $payload = array(
            'project_id' => $project_id,
            'conversation_language' => $conversation_language,
            'reset_session' => !empty($_POST['reset_session']),
            'site_host' => isset($_POST['site_host']) ? sanitize_text_field(wp_unslash((string) $_POST['site_host'])) : '',
            'website' => $website,
            'local_setup_state' => $local_setup_state,
            'ui_context' => array(
                'conversation_language' => $conversation_language,
                'available_languages' => array('en', 'ro', 'bg'),
                'page' => 'monitoring_analytics',
            ),
            'state' => array(
                'monitoring' => $monitoringState,
            ),
        );

        if ($mode === 'message') {
            $endpoint = '/v1/conversation/monitoring/message';
            $payload['user_message'] = $user_message;
        }

        $result = $this->aysa_broker_request('POST', $endpoint, $payload, 45);
        if (is_wp_error($result)) {
            wp_send_json_error(array('message' => $result->get_error_message()));
            return;
        }
        if (!is_array($result) || empty($result['ok'])) {
            wp_send_json_error(array(
                'message' => isset($result['error']) ? (string) $result['error'] : 'Monitoring conversation request failed.',
                'result' => $result,
            ));
            return;
        }

        wp_send_json_success(array(
            'result' => $result,
            'access' => $key_validation,
        ));
    }

    private function aysa_monitoring_build_conversation_bootstrap($project_id, $language, array $client_state) {
        $summary = $this->aysa_monitoring_conversation_summary($project_id, $client_state);
        $message = match ($language) {
            'ro' => 'Sunt conectat la Monitoring & Analytics. Urmăresc ' . $summary['tracked'] . ' keyworduri, dintre care ' . $summary['refreshed'] . ' sunt deja refreshate și ' . $summary['stale'] . ' încă așteaptă refresh. Spune-mi dacă vrei să pornesc refresh-ul, să-ți arăt keywordurile nereshate sau să verific statusul jobului.',
            'bg' => 'Свързан съм с Monitoring & Analytics. Следя ' . $summary['tracked'] . ' ключови думи, от които ' . $summary['refreshed'] . ' вече са обновени, а ' . $summary['stale'] . ' още чакат refresh. Кажи ми дали да стартирам refresh, да покажа неосвежените ключови думи или да проверя статуса на job-а.',
            default => 'I am connected to Monitoring & Analytics. I see ' . $summary['tracked'] . ' tracked keywords, with ' . $summary['refreshed'] . ' already refreshed and ' . $summary['stale'] . ' still waiting for refresh. Ask me to start a refresh, show stale keywords, or check the job status.',
        };

        return array(
            'assistant_message' => $message,
            'messages' => array(
                array('role' => 'assistant', 'message' => $message),
            ),
            'suggested_actions' => $this->aysa_monitoring_conversation_suggested_actions($language, $summary),
            'state' => array(
                'meta' => array(
                    'project_id' => $project_id,
                    'conversation_area' => 'monitoring_analytics',
                ),
            ),
        );
    }

    private function aysa_monitoring_build_conversation_reply($project_id, $language, $user_message, array $client_state) {
        $summary = $this->aysa_monitoring_conversation_summary($project_id, $client_state);
        $normalized = $this->aysa_monitoring_conversation_normalize($user_message);
        $execution_action = '';
        $ui_action = '';
        $message = '';

        if ($normalized === '' || $this->aysa_monitoring_conversation_has_any($normalized, array('overview', 'summary', 'rezumat', 'sumar', 'status general', 'general status'))) {
            $message = $this->aysa_monitoring_conversation_overview_message($language, $summary);
        } elseif ($this->aysa_monitoring_conversation_has_any($normalized, array('refresh', 'reimprosp', 'reîmprosp', 'actualizeaza', 'actualizează', 'update data', 'start refresh'))) {
            $execution_action = 'start_refresh';
            $message = match ($language) {
                'ro' => 'Pornesc refresh-ul în background pentru keywordurile urmărite. După ce pornește, îți actualizez și statusul.',
                'bg' => 'Стартирам refresh във фонов режим за следените ключови думи. След като тръгне, ще актуализирам и статуса.',
                default => 'I am starting the background refresh for tracked keywords now. After it starts, I will update the job status.',
            };
        } elseif ($this->aysa_monitoring_conversation_has_any($normalized, array('job', 'progress', 'status', 'running', 'merge refresh', 'refresh status', 'progres', 'merge jobul'))) {
            $execution_action = 'check_refresh_status';
            $message = match ($language) {
                'ro' => 'Verific acum statusul jobului de refresh și îți spun imediat dacă rulează, câte keyworduri a procesat și dacă are erori.',
                'bg' => 'Проверявам статуса на refresh job-а и веднага ще кажа дали върви, колко ключови думи е обработил и дали има грешки.',
                default => 'I am checking the refresh job status now and I will tell you if it is running, how many keywords it processed, and whether it has errors.',
            };
        } elseif ($this->aysa_monitoring_conversation_has_any($normalized, array('stale', 'not refreshed', 'unrefreshed', 'neresh', 'neactualizat', 'neactualizate', 'neactualizatele', 'nerefreshate'))) {
            $ui_action = 'open_monitoring_rows';
            $message = match ($language) {
                'ro' => 'Deschid tabelul cu keyworduri. În momentul ăsta ai ' . $summary['stale'] . ' keyworduri care nu sunt încă refreshate. Primele sunt: ' . $summary['stale_preview'] . '.',
                'bg' => 'Отварям таблицата с ключови думи. В момента имаш ' . $summary['stale'] . ' ключови думи, които още не са обновени. Първите са: ' . $summary['stale_preview'] . '.',
                default => 'I am opening the keyword table. Right now you have ' . $summary['stale'] . ' keywords that are not refreshed yet. The first ones are: ' . $summary['stale_preview'] . '.',
            };
        } elseif ($this->aysa_monitoring_conversation_has_any($normalized, array('credits', 'wallet', 'credite', 'sold', 'balance'))) {
            $execution_action = 'refresh_credits';
            $message = match ($language) {
                'ro' => 'Refac acum snapshotul de credite, ca să vezi soldul actual și partea rezervată pentru linkuri.',
                'bg' => 'Обновявам snapshot-а за кредити, за да видиш текущия баланс и резервираната част за линкове.',
                default => 'I am refreshing the credits snapshot now so you can see the current wallet balance and the portion reserved for links.',
            };
        } elseif ($this->aysa_monitoring_conversation_has_any($normalized, array('rows', 'keywords', 'keyworduri', 'cuvinte cheie', 'tracked keywords', 'tracked keyword'))) {
            $ui_action = 'open_monitoring_rows';
            $message = match ($language) {
                'ro' => 'Deschid tabelul de Monitoring. Ai ' . $summary['tracked'] . ' keyworduri urmărite și ' . $summary['refreshed'] . ' dintre ele au date locale deja persistate.',
                'bg' => 'Отварям таблицата Monitoring. Имаш ' . $summary['tracked'] . ' следени ключови думи и ' . $summary['refreshed'] . ' от тях вече имат локално запазени данни.',
                default => 'I am opening the Monitoring table. You have ' . $summary['tracked'] . ' tracked keywords and ' . $summary['refreshed'] . ' of them already have local persisted data.',
            };
        } else {
            $message = match ($language) {
                'ro' => 'Te pot ajuta aici cu monitoringul keywordurilor: să pornesc refresh-ul în background, să verific statusul jobului, să-ți arăt keywordurile nereshate sau să verific creditele pentru refresh.',
                'bg' => 'Тук мога да помогна с monitoring-а на ключовите думи: да стартирам refresh във фонов режим, да проверя статуса на job-а, да покажа неосвежените ключови думи или да проверя кредитите.',
                default => 'Here I can help with keyword monitoring: start the background refresh, check the job status, show stale keywords, or refresh the credits snapshot.',
            };
        }

        return array(
            'assistant_message' => $message,
            'messages' => array(
                array('role' => 'user', 'message' => $user_message),
                array('role' => 'assistant', 'message' => $message),
            ),
            'suggested_actions' => $this->aysa_monitoring_conversation_suggested_actions($language, $summary),
            'ui_action' => $ui_action,
            'execution_action' => $execution_action,
            'state' => array(
                'meta' => array(
                    'project_id' => $project_id,
                    'conversation_area' => 'monitoring_analytics',
                ),
            ),
        );
    }

    private function aysa_monitoring_conversation_summary($project_id, array $client_state) {
        $tracked = intval($client_state['tracked_count'] ?? 0);
        $refreshed = intval($client_state['refreshed_count'] ?? 0);
        $stale = intval($client_state['stale_count'] ?? max(0, $tracked - $refreshed));
        $job_status = sanitize_text_field((string) ($client_state['job_status'] ?? 'idle'));
        $job_processed = intval($client_state['job_processed'] ?? 0);
        $job_total = intval($client_state['job_total'] ?? 0);
        $rows_preview = isset($client_state['stale_preview']) && is_array($client_state['stale_preview']) ? $client_state['stale_preview'] : array();
        $preview = array();
        foreach ($rows_preview as $row) {
            $keyword = sanitize_text_field((string) ($row['keyword'] ?? ''));
            if ($keyword !== '') {
                $preview[] = $keyword;
            }
            if (count($preview) >= 3) {
                break;
            }
        }

        return array(
            'project_id' => intval($project_id),
            'tracked' => $tracked,
            'refreshed' => $refreshed,
            'stale' => $stale,
            'job_status' => $job_status,
            'job_processed' => $job_processed,
            'job_total' => $job_total,
            'stale_preview' => !empty($preview) ? implode(', ', $preview) : 'n/a',
        );
    }

    private function aysa_monitoring_conversation_overview_message($language, array $summary) {
        return match ($language) {
            'ro' => 'În monitoring ai ' . $summary['tracked'] . ' keyworduri urmărite. ' . $summary['refreshed'] . ' au date locale persistate, iar ' . $summary['stale'] . ' sunt încă stale. Jobul de refresh este acum `' . $summary['job_status'] . '` și a procesat ' . $summary['job_processed'] . ' din ' . $summary['job_total'] . '.',
            'bg' => 'В monitoring имаш ' . $summary['tracked'] . ' следени ключови думи. ' . $summary['refreshed'] . ' имат локално запазени данни, а ' . $summary['stale'] . ' още са stale. Refresh job-ът е `' . $summary['job_status'] . '` и е обработил ' . $summary['job_processed'] . ' от ' . $summary['job_total'] . '.',
            default => 'In Monitoring you have ' . $summary['tracked'] . ' tracked keywords. ' . $summary['refreshed'] . ' already have local persisted data, and ' . $summary['stale'] . ' are still stale. The refresh job is currently `' . $summary['job_status'] . '` and processed ' . $summary['job_processed'] . ' out of ' . $summary['job_total'] . '.',
        };
    }

    private function aysa_monitoring_conversation_suggested_actions($language, array $summary) {
        return array(
            array(
                'label' => match ($language) {
                    'ro' => 'Pornește refresh',
                    'bg' => 'Стартирай refresh',
                    default => 'Start refresh',
                },
                'execution_action' => 'start_refresh',
            ),
            array(
                'label' => match ($language) {
                    'ro' => 'Vezi keywordurile',
                    'bg' => 'Виж ключовите думи',
                    default => 'Show keywords',
                },
                'ui_action' => 'open_monitoring_rows',
            ),
            array(
                'label' => match ($language) {
                    'ro' => 'Status job',
                    'bg' => 'Статус на job',
                    default => 'Job status',
                },
                'execution_action' => 'check_refresh_status',
            ),
        );
    }

    private function aysa_monitoring_conversation_normalize($value) {
        $value = strtolower(trim((string) $value));
        $value = str_replace(array('ă', 'â', 'î', 'ș', 'ş', 'ț', 'ţ'), array('a', 'a', 'i', 's', 's', 't', 't'), $value);
        return preg_replace('/\s+/', ' ', $value);
    }

    private function aysa_monitoring_conversation_has_any($haystack, array $patterns) {
        foreach ($patterns as $pattern) {
            if ($pattern !== '' && strpos($haystack, (string) $pattern) !== false) {
                return true;
            }
        }
        return false;
    }

    public function handle_adjustments_generation_notifications() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }
        if (!$this->check_authentication(false)) {
            wp_send_json_error(array('message' => 'Authentication required.'));
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : intval(get_option('aysa_project_id', 0));
        $conversation_language = isset($_POST['conversation_language']) ? sanitize_key(wp_unslash((string) $_POST['conversation_language'])) : 'en';
        if (!in_array($conversation_language, array('en', 'ro', 'bg'), true)) {
            $conversation_language = 'en';
        }

        $this->aysa_adjustments_process_generation_queue_now($project_id, 3);

        wp_send_json_success(array(
            'notifications' => $this->aysa_adjustments_collect_generation_notifications($project_id, $conversation_language),
            'approvals' => $this->aysa_adjustments_collect_pending_approval_notifications($project_id, $conversation_language),
        ));
    }

    public function handle_setup_notifications() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }
        if (!$this->check_authentication(false)) {
            wp_send_json_error(array('message' => 'Authentication required.'));
            return;
        }

        $key_validation = $this->aysa_validate_test_access_key(
            $this->aysa_get_effective_access_key(isset($_POST['access_key']) ? sanitize_text_field(wp_unslash($_POST['access_key'])) : ''),
            isset($_POST['site_host']) ? sanitize_text_field(wp_unslash((string) $_POST['site_host'])) : ''
        );
        if (empty($key_validation['valid'])) {
            wp_send_json_error(array(
                'message' => (string) ($key_validation['message'] ?? 'Access key is invalid.'),
                'code' => 'access_key_invalid',
            ));
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : intval(get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error(array('message' => 'Missing project ID.'));
            return;
        }

        $conversation_language = isset($_POST['conversation_language']) ? sanitize_key(wp_unslash((string) $_POST['conversation_language'])) : 'en';
        if (!in_array($conversation_language, array('en', 'ro', 'bg'), true)) {
            $conversation_language = 'en';
        }

        $run_token = $this->aysa_get_or_create_onboarding_run_token($project_id);
        $payload = array(
            'project_id' => $project_id,
            'channel' => 'setup_seo_profile',
            'conversation_language' => $conversation_language,
            'mark_delivered' => !empty($_POST['mark_delivered']),
            'run_token' => $run_token,
            'discard_mismatched' => true,
        );
        if (!empty($_POST['notification_ids']) && is_array($_POST['notification_ids'])) {
            $payload['notification_ids'] = array_values(array_filter(array_map('intval', wp_unslash($_POST['notification_ids'])), static function ($id) {
                return $id > 0;
            }));
        }
        $result = $this->aysa_broker_request('POST', '/v1/onboarding/notifications/pull', $payload, 30);
        if (is_wp_error($result)) {
            wp_send_json_error(array('message' => $result->get_error_message()));
            return;
        }
        if (!is_array($result) || empty($result['ok'])) {
            wp_send_json_error(array(
                'message' => isset($result['error']) ? (string) $result['error'] : 'Failed to load onboarding notifications.',
            ));
            return;
        }

        if (!isset($result['notifications']) || !is_array($result['notifications'])) {
            $result['notifications'] = array();
        }
        $result['run_token'] = $run_token;
        $result['notification_count'] = count($result['notifications']);
        wp_send_json_success($result);
    }

    public function handle_workspace_state_save() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!$this->aysa_current_user_can_workspace()) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }

        $tab = isset($_POST['tab']) ? sanitize_text_field(wp_unslash((string) $_POST['tab'])) : '';
        $subtab = isset($_POST['subtab']) ? sanitize_text_field(wp_unslash((string) $_POST['subtab'])) : '';
        $normalized_tab = $this->aysa_normalize_research_tab($tab);
        $saved = array();

        if ($normalized_tab !== '') {
            $this->aysa_save_last_research_tab($normalized_tab);
            $saved['tab'] = $normalized_tab;
        }

        if ($normalized_tab === 'keyword-discovery' && $subtab !== '') {
            $saved_subtab = $this->aysa_save_last_keyword_discovery_subtab($subtab);
            if ($saved_subtab !== '') {
                $saved['subtab'] = $saved_subtab;
            }
        }

        wp_send_json_success(array(
            'saved' => $saved,
        ));
    }

    public function handle_adjustments_generate_created_entity($payload) {
        $payload = is_array($payload) ? $payload : array();
        $queue_key = sanitize_text_field((string) ($payload['queue_key'] ?? ''));
        $project_id = intval($payload['project_id'] ?? 0);
        if ($queue_key === '' || $project_id <= 0) {
            return;
        }

        $queue = $this->aysa_adjustments_get_generation_queue($project_id);
        if (!isset($queue[$queue_key]) || !is_array($queue[$queue_key])) {
            return;
        }

        $item = $queue[$queue_key];
        $item['status'] = 'processing';
        $item['updated_at'] = current_time('mysql');
        $queue[$queue_key] = $item;
        $this->aysa_adjustments_save_generation_queue($project_id, $queue);

        if (!class_exists('Aysa_Seo_Generation_Processor')) {
            $processor_file = plugin_dir_path(dirname(__FILE__)) . 'includes/automation/processors/class-seo-generation-processor.php';
            if (file_exists($processor_file)) {
                require_once $processor_file;
            }
        }

        if (!class_exists('Aysa_Seo_Generation_Processor')) {
            $item['status'] = 'failed';
            $item['error'] = 'SEO generation processor is not available.';
            $item['updated_at'] = current_time('mysql');
            $queue[$queue_key] = $item;
            $this->aysa_adjustments_save_generation_queue($project_id, $queue);
            return;
        }

        $processor = new Aysa_Seo_Generation_Processor();
        $result = $processor->process(array(
            'entity_id' => intval($item['entity_id'] ?? 0),
            'entity_type' => (string) ($item['entity_processor_type'] ?? ''),
            'keyword' => (string) ($item['keyword'] ?? ''),
            'project_id' => $project_id,
        ));

        $item['updated_at'] = current_time('mysql');
        if (is_wp_error($result)) {
            $item['status'] = 'failed';
            $item['error'] = $result->get_error_message();
        } else {
            $item['status'] = 'generated';
            $item['review_id'] = intval($result['review_id'] ?? 0);
            $item['result_message'] = sanitize_text_field((string) ($result['message'] ?? ''));
        }

        $queue[$queue_key] = $item;
        $this->aysa_adjustments_save_generation_queue($project_id, $queue);
    }

    private function aysa_adjustments_get_keyword_research_snapshot($project_id, $setup_user_id = 0) {
        $project_id = intval($project_id);
        $setup_user_id = intval($setup_user_id);

        $profile_user_id = ($setup_user_id > 0 ? $setup_user_id : get_current_user_id());
        $seed_data = $this->aysa_keyword_strategy_extract_seed_data($profile_user_id);
        $shared_user_id = intval(get_option('aysa_user_id', 0));
        if (
            (empty($seed_data['seed_keywords']) || empty($seed_data['competitors']))
            && $shared_user_id > 0
            && $shared_user_id !== $profile_user_id
        ) {
            $shared_seed_data = $this->aysa_keyword_strategy_extract_seed_data($shared_user_id);
            if (empty($seed_data['seed_keywords']) && !empty($shared_seed_data['seed_keywords']) && is_array($shared_seed_data['seed_keywords'])) {
                $seed_data['seed_keywords'] = array_values($shared_seed_data['seed_keywords']);
            }
            if (empty($seed_data['competitors']) && !empty($shared_seed_data['competitors']) && is_array($shared_seed_data['competitors'])) {
                $seed_data['competitors'] = array_values($shared_seed_data['competitors']);
            }
        }
        if ((empty($seed_data['seed_keywords']) || empty($seed_data['competitors'])) && $project_id > 0) {
            $project_competition = get_option('aysa_wizard_competition_project_' . $project_id, array());
            if (is_array($project_competition) && !empty($project_competition)) {
                $project_seed_keywords = array();
                $project_competitors = array();
                foreach ($project_competition as $key => $value) {
                    $value = trim((string) $value);
                    if ($value === '') {
                        continue;
                    }
                    if (preg_match('/^keyword\\d+$/', (string) $key)) {
                        $project_seed_keywords[] = $value;
                        continue;
                    }
                    if (preg_match('/^competitor\\d+$/', (string) $key)) {
                        $project_competitors[] = $value;
                    }
                }
                if (empty($seed_data['seed_keywords']) && !empty($project_seed_keywords)) {
                    $seed_data['seed_keywords'] = array_values(array_unique($project_seed_keywords));
                }
                if (empty($seed_data['competitors']) && !empty($project_competitors)) {
                    $seed_data['competitors'] = array_values(array_unique($project_competitors));
                }
            }
        }
        $seed_keywords = isset($seed_data['seed_keywords']) && is_array($seed_data['seed_keywords']) ? array_values($seed_data['seed_keywords']) : array();
        $status = $this->aysa_keyword_discovery_get_status($project_id);
        $snapshot = get_option($this->aysa_keyword_discovery_snapshot_key($project_id), array());

        $association_rows = $this->get_keyword_discovery_local_associations($project_id);
        $gsc_rows = $this->aysa_adjustments_get_local_keywords_by_sources($project_id, array('gsc', 'gsc_query', 'gsc_query_only'));
        $suggestion_rows = $this->aysa_keyword_discovery_get_local_keywords_payload($project_id, 'keyword_suggestions');
        $selected_gap_rows = $this->aysa_adjustments_get_local_keywords_by_sources($project_id, array('competitor_gap'));
        $unified_rows = $this->aysa_adjustments_build_unified_keyword_rows(
            $project_id,
            $gsc_rows,
            $suggestion_rows,
            isset($selected_gap_rows) && is_array($selected_gap_rows) ? $selected_gap_rows : array(),
            $association_rows
        );
        $gsc_count = count($gsc_rows);
        $suggestions_count = count($suggestion_rows);

        return array(
            'project_id' => $project_id,
            'seed_keywords' => $seed_keywords,
            'gsc_count' => $gsc_count,
            'suggestions_count' => $suggestions_count,
            'competitor_gap_selected_count' => count($selected_gap_rows),
            'unified_keywords_count' => count($unified_rows),
            'associations_count' => is_array($association_rows) ? count($association_rows) : 0,
            'gsc_rows' => is_array($gsc_rows) ? $gsc_rows : array(),
            'suggestion_rows' => is_array($suggestion_rows) ? $suggestion_rows : array(),
            'competitor_gap_rows' => is_array($selected_gap_rows) ? $selected_gap_rows : array(),
            'unified_rows' => $unified_rows,
            'association_rows' => is_array($association_rows) ? $association_rows : array(),
            'tracked_keywords_plan' => $this->aysa_get_keyword_tracking_plan($project_id),
            'refresh_status' => $status,
            'background_snapshot' => is_array($snapshot) ? array(
                'count' => intval($snapshot['count'] ?? 0),
                'fetched_at' => intval($snapshot['fetched_at'] ?? 0),
                'run_id' => (string) ($snapshot['run_id'] ?? ''),
            ) : array(),
            'has_existing_data' => ($gsc_count > 0 || $suggestions_count > 0),
        );
    }

    private function aysa_adjustments_get_recent_relevant_keywords_option_key($project_id) {
        return 'aysa_adjustments_recent_relevant_keywords_' . intval($project_id);
    }

    private function aysa_keyword_tracking_plan_option_key($project_id) {
        return 'aysa_keyword_tracking_plan_' . intval($project_id);
    }

    private function aysa_adjustments_pending_approvals_option_key($project_id) {
        return 'aysa_adjustments_pending_approvals_' . intval($project_id);
    }

    private function aysa_adjustments_get_pending_approvals($project_id) {
        $value = get_option($this->aysa_adjustments_pending_approvals_option_key($project_id), array());
        return is_array($value) ? array_values($value) : array();
    }

    private function aysa_adjustments_save_pending_approvals($project_id, $items) {
        update_option(
            $this->aysa_adjustments_pending_approvals_option_key($project_id),
            is_array($items) ? array_values($items) : array(),
            false
        );
    }

    private function aysa_adjustments_approval_action_url($ui_action) {
        $ui_action = sanitize_key((string) $ui_action);
        return match ($ui_action) {
            'open_content_strategy', 'open_artifact_content_strategy' => admin_url('admin.php?page=aysa-research&tab=content-strategy'),
            'open_competitor_gap' => admin_url('admin.php?page=aysa-research&tab=competitor-gap'),
            'open_competitor_backlinks' => admin_url('admin.php?page=aysa-research&tab=competitor-backlinks'),
            'open_missing_pages' => admin_url('admin.php?page=aysa-research&tab=missing-pages'),
            default => admin_url('admin.php?page=aysa-research&tab=keyword-discovery'),
        };
    }

    private function aysa_adjustments_pending_approval_email_recipients() {
        $recipients = array();

        $admin_email = sanitize_email((string) get_option('admin_email', ''));
        if ($admin_email !== '' && is_email($admin_email)) {
            $recipients[] = $admin_email;
        }

        $current_user = function_exists('wp_get_current_user') ? wp_get_current_user() : null;
        if ($current_user && !empty($current_user->user_email)) {
            $user_email = sanitize_email((string) $current_user->user_email);
            if ($user_email !== '' && is_email($user_email)) {
                $recipients[] = $user_email;
            }
        }

        return array_values(array_unique($recipients));
    }

    private function get_app_aysa_internal_secret() {
        if (defined('AYSA_WALLET_APP_INTERNAL_SECRET')) {
            return trim((string) AYSA_WALLET_APP_INTERNAL_SECRET);
        }

        $stored = trim((string) get_option('aysa_wallet_app_internal_secret', ''));
        if ($stored !== '') {
            return $stored;
        }

        return '';
    }

    private function get_remote_aysa_user_id_for_notifications() {
        if (class_exists('Aysa_Auth_Manager')) {
            try {
                $auth = Aysa_Auth_Manager::get_instance();
                $status = $auth->is_authenticated();
                $user_id = is_array($status) ? intval($status['aysa_user_id'] ?? 0) : 0;
                if ($user_id > 0) {
                    return $user_id;
                }
            } catch (Exception $e) {
                // Fall back to email-only addressing.
            }
        }

        return 0;
    }

    private function get_notification_target_email() {
        $current_user = function_exists('wp_get_current_user') ? wp_get_current_user() : null;
        if ($current_user && !empty($current_user->user_email)) {
            $user_email = sanitize_email((string) $current_user->user_email);
            if ($user_email !== '' && is_email($user_email)) {
                return $user_email;
            }
        }

        $admin_email = sanitize_email((string) get_option('admin_email', ''));
        if ($admin_email !== '' && is_email($admin_email)) {
            return $admin_email;
        }

        return '';
    }

    private function notify_app_aysa_conversation_event($event_key, $task_label, $action_url = '', $meta = array()) {
        $event_key = sanitize_key((string) $event_key);
        $task_label = sanitize_text_field((string) $task_label);
        $internal_secret = $this->get_app_aysa_internal_secret();
        if ($event_key === '' || $task_label === '' || $internal_secret === '') {
            return false;
        }

        $payload = array(
            'event_key' => $event_key,
            'task_label' => $task_label,
            'meta' => is_array($meta) ? $meta : array(),
        );

        $user_id = $this->get_remote_aysa_user_id_for_notifications();
        if ($user_id > 0) {
            $payload['user_id'] = $user_id;
        } else {
            $email = $this->get_notification_target_email();
            if ($email !== '') {
                $payload['email'] = $email;
            }
        }

        if (!empty($action_url)) {
            $payload['action_url'] = esc_url_raw((string) $action_url);
        }

        $response = wp_remote_post($this->get_app_aysa_base_url() . '/internal/notifications/conversation-event', array(
            'timeout' => 20,
            'headers' => array(
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
                'Authorization' => 'Bearer ' . $internal_secret,
            ),
            'body' => wp_json_encode($payload),
        ));

        if (is_wp_error($response)) {
            return false;
        }

        $code = intval(wp_remote_retrieve_response_code($response));
        $body = json_decode((string) wp_remote_retrieve_body($response), true);

        return $code >= 200 && $code < 300 && is_array($body) && !empty($body['ok']);
    }

    private function aysa_adjustments_notify_pending_approval_email($project_id, $approval_key, $title, $message, $ui_action) {
        $project_id = intval($project_id);
        $approval_key = sanitize_key((string) $approval_key);
        if ($project_id <= 0 || $approval_key === '') {
            return;
        }

        $recipients = $this->aysa_adjustments_pending_approval_email_recipients();
        if ($recipients === array()) {
            return;
        }

        $site_name = wp_specialchars_decode(get_bloginfo('name'), ENT_QUOTES);
        $subject = sprintf('[AYSA] Approval required: %s', trim((string) $title) !== '' ? trim((string) $title) : 'Next SEO step');
        $action_url = $this->aysa_adjustments_approval_action_url($ui_action);
        $body_lines = array(
            'AYSA has completed a research or planning step and is waiting for approval before continuing.',
            '',
            'Project ID: ' . $project_id,
            'Approval: ' . $approval_key,
            'Title: ' . trim((string) $title),
            '',
            trim((string) $message),
            '',
            'Open in WordPress: ' . $action_url,
            '',
            'Site: ' . $site_name,
        );
        $headers = array('Content-Type: text/plain; charset=UTF-8');

        foreach ($recipients as $recipient) {
            wp_mail($recipient, $subject, implode("\n", $body_lines), $headers);
        }
    }

    private function aysa_adjustments_add_pending_approval($project_id, $approval_key, $title, $message, $ui_action = 'open_keyword_discovery') {
        $project_id = intval($project_id);
        $approval_key = sanitize_key((string) $approval_key);
        if ($project_id <= 0 || $approval_key === '') {
            return;
        }

        $items = $this->aysa_adjustments_get_pending_approvals($project_id);
        foreach ($items as $item) {
            if (!is_array($item)) {
                continue;
            }
            if ((string) ($item['approval_key'] ?? '') === $approval_key && (string) ($item['status'] ?? 'pending') === 'pending') {
                return;
            }
        }

        $items[] = array(
            'approval_key' => $approval_key,
            'title' => trim((string) $title),
            'message' => trim((string) $message),
            'ui_action' => trim((string) $ui_action),
            'status' => 'pending',
            'notified' => '0',
            'created_at' => current_time('mysql'),
        );
        $this->aysa_adjustments_save_pending_approvals($project_id, $items);
        $this->aysa_adjustments_notify_pending_approval_email($project_id, $approval_key, $title, $message, $ui_action);
        $this->notify_app_aysa_conversation_event(
            'approval_required',
            trim((string) $title) !== '' ? (string) $title : 'AYSA approval required',
            $this->aysa_adjustments_approval_action_url($ui_action),
            array(
                'project_id' => $project_id,
                'approval_key' => $approval_key,
                'resource_type' => 'project',
                'resource_id' => (string) $project_id,
                'message' => trim((string) $message),
            )
        );
    }

    private function aysa_adjustments_collect_pending_approval_notifications($project_id, $language) {
        $items = $this->aysa_adjustments_get_pending_approvals($project_id);
        if ($items === array()) {
            return array();
        }

        $notifications = array();
        $updated = array();
        foreach ($items as $item) {
            if (!is_array($item)) {
                continue;
            }
            $status = (string) ($item['status'] ?? 'pending');
            if ($status !== 'pending') {
                $updated[] = $item;
                continue;
            }

            $notified = (string) ($item['notified'] ?? '0') === '1';
            if ($notified) {
                $updated[] = $item;
                continue;
            }

            $notifications[] = array(
                'approval_key' => (string) ($item['approval_key'] ?? ''),
                'title' => (string) ($item['title'] ?? ''),
                'message' => (string) ($item['message'] ?? ''),
                'ui_action' => (string) ($item['ui_action'] ?? 'open_keyword_discovery'),
            );
            $item['notified'] = '1';
            $updated[] = $item;
        }

        $this->aysa_adjustments_save_pending_approvals($project_id, $updated);
        return $notifications;
    }

    private function aysa_get_keyword_tracking_plan($project_id) {
        $value = get_option($this->aysa_keyword_tracking_plan_option_key($project_id), array());
        return is_array($value) ? $value : array();
    }

    private function aysa_save_keyword_tracking_plan($project_id, $payload) {
        update_option(
            $this->aysa_keyword_tracking_plan_option_key($project_id),
            is_array($payload) ? $payload : array(),
            false
        );
    }

    private function aysa_adjustments_get_recent_relevant_keywords($project_id) {
        $value = get_option($this->aysa_adjustments_get_recent_relevant_keywords_option_key($project_id), array());
        return is_array($value) ? $value : array();
    }

    private function aysa_adjustments_save_recent_relevant_keywords($project_id, $payload) {
        update_option(
            $this->aysa_adjustments_get_recent_relevant_keywords_option_key($project_id),
            is_array($payload) ? $payload : array(),
            false
        );
    }

    private function aysa_adjustments_normalize_keyword_lookup_key($keyword) {
        return strtolower(trim(remove_accents((string) $keyword)));
    }

    private function aysa_adjustments_get_local_keywords_by_sources($project_id, $sources) {
        global $wpdb;
        $table = $this->aysa_keyword_discovery_table();
        $project_id = intval($project_id);
        $sources = array_values(array_filter(array_map(function($source) {
            return strtolower(trim((string) $source));
        }, is_array($sources) ? $sources : array())));
        if ($project_id <= 0 || empty($sources)) {
            return array();
        }

        $placeholders = implode(', ', array_fill(0, count($sources), '%s'));
        $args = array_merge(array($project_id), $sources);
        $query = "SELECT keyword, search_volume, difficulty, intent, intent_probabilities, intent_source, intent_updated_at, competition, traffic, traffic_pct, position, cpc, target_url, item_types, remote_keyword_id, source, updated_at
            FROM {$table}
            WHERE project_id = %d
              AND source IN ({$placeholders})
            ORDER BY updated_at DESC, id DESC
            LIMIT 5000";
        $rows = $wpdb->get_results($wpdb->prepare($query, $args), ARRAY_A);
        if (!is_array($rows)) {
            return array();
        }

        $keywords = array();
        foreach ($rows as $row) {
            $item_types = $this->aysa_keyword_discovery_parse_item_types($row['item_types'] ?? array());
            $intent_probabilities = $this->aysa_keyword_discovery_normalize_intent_probabilities($row['intent_probabilities'] ?? array());
            $keywords[] = array(
                'keyword' => (string) ($row['keyword'] ?? ''),
                'searchVolume' => ($row['search_volume'] !== null ? intval($row['search_volume']) : 'N/A'),
                'difficulty' => ($row['difficulty'] !== null ? (float) $row['difficulty'] : 'N/A'),
                'intent' => ((string) ($row['intent'] ?? '') !== '' ? (string) $row['intent'] : 'N/A'),
                'intentProbabilities' => $intent_probabilities,
                'intentSource' => ((string) ($row['intent_source'] ?? '') !== '' ? (string) $row['intent_source'] : ''),
                'intentUpdatedAt' => ((string) ($row['intent_updated_at'] ?? '') !== '' ? (string) $row['intent_updated_at'] : ''),
                'competition' => ((string) ($row['competition'] ?? '') !== '' ? (string) $row['competition'] : 'N/A'),
                'traffic' => ($row['traffic'] !== null ? intval($row['traffic']) : 'N/A'),
                'trafficPct' => ($row['traffic_pct'] !== null ? floatval($row['traffic_pct']) : 'N/A'),
                'position' => ($row['position'] !== null ? floatval($row['position']) : 'N/A'),
                'cpc' => ($row['cpc'] !== null ? floatval($row['cpc']) : 'N/A'),
                'url' => ((string) ($row['target_url'] ?? '') !== '' ? (string) ($row['target_url']) : 'N/A'),
                'itemTypes' => $item_types,
                'id' => intval($row['remote_keyword_id'] ?? 0),
                'projectId' => $project_id,
                'source' => (string) ($row['source'] ?? 'suggested'),
                'updatedAt' => (string) ($row['updated_at'] ?? ''),
                'createdDate' => (string) ($row['updated_at'] ?? ''),
            );
        }

        return $keywords;
    }

    private function aysa_adjustments_build_unified_keyword_rows($project_id, $gsc_rows, $suggestion_rows, $selected_gap_rows, $association_rows = array()) {
        $project_id = intval($project_id);
        $association_rows = is_array($association_rows) ? $association_rows : array();
        $recent = $this->aysa_adjustments_get_recent_relevant_keywords($project_id);
        $recent_selected = isset($recent['selected_keywords']) && is_array($recent['selected_keywords']) ? $recent['selected_keywords'] : array();
        $selected_lookup = array_fill_keys(array_map(array($this, 'aysa_adjustments_normalize_keyword_lookup_key'), $recent_selected), true);

        $grouped_associations = $this->aysa_adjustments_group_associations_for_workspace($association_rows);
        $assoc_by_keyword = array();
        foreach ($grouped_associations as $group) {
            $keywords = isset($group['keywords']) && is_array($group['keywords']) ? $group['keywords'] : array();
            foreach ($keywords as $keyword) {
                $assoc_by_keyword[$this->aysa_adjustments_normalize_keyword_lookup_key($keyword)] = $group;
            }
        }

        $map = array();
        $add_row = function($row, $label) use (&$map, $assoc_by_keyword, $selected_lookup) {
            if (!is_array($row)) {
                return;
            }
            $keyword = trim((string) ($row['keyword'] ?? ''));
            if ($keyword === '') {
                return;
            }
            $norm = $this->aysa_adjustments_normalize_keyword_lookup_key($keyword);
            if ($norm === '') {
                return;
            }

            if (!isset($map[$norm])) {
                $map[$norm] = array(
                    'keyword' => $keyword,
                    'search_volume' => 0,
                    'difficulty' => '',
                    'position' => '',
                    'page' => '',
                    'group' => '',
                    'source_labels' => array(),
                    'source_keys' => array(),
                    'selected' => !empty($selected_lookup[$norm]) ? 'Selected' : 'Available',
                );
            }

            $entry = $map[$norm];
            $entry['source_labels'][$label] = true;
            $entry['source_keys'][strtolower($label)] = true;

            $volume = intval($row['search_volume'] ?? $row['searchVolume'] ?? 0);
            if ($volume > intval($entry['search_volume'])) {
                $entry['search_volume'] = $volume;
            }

            $difficulty = $row['difficulty'] ?? '';
            if ($entry['difficulty'] === '' && $difficulty !== '' && $difficulty !== 'N/A') {
                $entry['difficulty'] = (string) $difficulty;
            }

            $position = $row['position'] ?? '';
            if ($entry['position'] === '' && $position !== '' && $position !== 'N/A') {
                $entry['position'] = (string) $position;
            }

            $page = trim((string) ($row['target_url'] ?? $row['url'] ?? ''));
            if ($page !== '' && $page !== 'N/A' && $entry['page'] === '') {
                $entry['page'] = $page;
            }

            if (isset($assoc_by_keyword[$norm])) {
                $group = $assoc_by_keyword[$norm];
                $entry['group'] = (string) ($group['entity'] ?? $entry['group']);
                if ($entry['page'] === '' && !empty($group['page'])) {
                    $entry['page'] = (string) $group['page'];
                }
                if (!empty($selected_lookup[$norm])) {
                    $entry['selected'] = 'Selected';
                }
            }

            $map[$norm] = $entry;
        };

        foreach (is_array($gsc_rows) ? $gsc_rows : array() as $row) {
            $add_row($row, 'Google');
        }
        foreach (is_array($suggestion_rows) ? $suggestion_rows : array() as $row) {
            $add_row($row, 'Suggestions');
        }
        foreach (is_array($selected_gap_rows) ? $selected_gap_rows : array() as $row) {
            $add_row($row, 'Competitor Gap');
        }

        $rows = array_values(array_map(function($row) {
            $row['sources'] = implode(', ', array_keys($row['source_labels']));
            unset($row['source_labels'], $row['source_keys']);
            return $row;
        }, $map));

        usort($rows, function($left, $right) {
            $left_selected = (($left['selected'] ?? '') === 'Selected') ? 1 : 0;
            $right_selected = (($right['selected'] ?? '') === 'Selected') ? 1 : 0;
            if ($left_selected !== $right_selected) {
                return $right_selected <=> $left_selected;
            }
            return intval($right['search_volume'] ?? 0) <=> intval($left['search_volume'] ?? 0);
        });

        return $rows;
    }

    private function aysa_adjustments_group_associations_for_workspace($association_rows) {
        $association_rows = is_array($association_rows) ? $association_rows : array();
        $groups = array();
        foreach ($association_rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['keyword'] ?? ''));
            $associated_on = trim((string) ($row['associatedOn'] ?? $row['associated_on'] ?? ''));
            $id_intern = intval($row['idIntern'] ?? $row['id_intern'] ?? 0);
            $entity = trim((string) ($row['value'] ?? ''));
            if ($keyword === '' || $associated_on === '' || $id_intern <= 0 || $entity === '') {
                continue;
            }
            $key = strtolower($associated_on . ':' . $id_intern);
            if (!isset($groups[$key])) {
                $groups[$key] = array(
                    'type' => ucfirst($associated_on),
                    'entity' => $entity,
                    'page' => '',
                    'keywords' => array(),
                );
            }
            $groups[$key]['keywords'][$this->aysa_adjustments_normalize_keyword_lookup_key($keyword)] = $keyword;
        }

        foreach ($groups as $key => $group) {
            $parts = explode(':', $key, 2);
            $associated_on = (string) ($parts[0] ?? '');
            $id_intern = intval($parts[1] ?? 0);
            $groups[$key]['page'] = $this->aysa_monitoring_resolve_association_link($associated_on, $id_intern, $group['entity']);
            $groups[$key]['keywords'] = array_values($group['keywords']);
            sort($groups[$key]['keywords']);
        }

        return array_values($groups);
    }

    private function aysa_adjustments_get_research_snapshot($project_id, $setup_user_id = 0) {
        $keyword = $this->aysa_adjustments_get_keyword_research_snapshot($project_id, $setup_user_id);
        $monitoring_ranking = $this->aysa_adjustments_get_monitoring_ranking_snapshot($project_id);
        $competitor_gap = $this->aysa_adjustments_get_competitor_gap_snapshot($project_id);
        $competitor_identification = $this->aysa_adjustments_get_competitor_identification_snapshot($project_id);
        $competitor_backlinks = $this->aysa_adjustments_get_competitor_backlinks_snapshot($project_id);
        $offpage_backlink_audit = $this->aysa_adjustments_get_offpage_backlink_audit_snapshot($project_id);
        $offpage_link_building = $this->aysa_adjustments_get_offpage_link_building_snapshot($project_id);
        $missing_pages = $this->aysa_adjustments_get_missing_pages_snapshot($project_id);
        $content_strategy = $this->aysa_adjustments_get_content_strategy_snapshot($project_id);
        $onpage_optimization = $this->aysa_adjustments_get_onpage_optimization_snapshot($project_id);
        $technical_site_audit = $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id);
        $technical_sitemap_robots = $this->aysa_adjustments_get_technical_sitemap_robots_snapshot($project_id);
        $technical_pagespeed = $this->aysa_adjustments_get_technical_pagespeed_snapshot($project_id);
        $technical_mobile_optimization = $this->aysa_adjustments_get_technical_mobile_optimization_snapshot($project_id);
        $technical_404_errors = $this->aysa_adjustments_get_technical_404_errors_snapshot($project_id);
        $technical_301_redirects = $this->aysa_adjustments_get_technical_301_redirects_snapshot($project_id);
        $technical_410_gone = $this->aysa_adjustments_get_technical_410_gone_snapshot($project_id);
        $technical_errors = $this->aysa_adjustments_get_technical_errors_snapshot(
            $project_id,
            $technical_404_errors,
            $technical_301_redirects,
            $technical_410_gone
        );
        $technical_schema = $this->aysa_adjustments_get_cached_or_idle_technical_schema_snapshot($project_id);
        $technical_schema_markup = $this->aysa_adjustments_get_technical_schema_markup_snapshot($project_id);
        $technical_internal_linking = $this->aysa_adjustments_get_technical_internal_linking_snapshot($project_id);

        $keyword['modules'] = array(
            'keyword_research' => $keyword,
            'monitoring_ranking' => $monitoring_ranking,
            'competitor_gap' => $competitor_gap,
            'competitor_identification' => $competitor_identification,
            'competitor_backlinks' => $competitor_backlinks,
            'offpage_backlink_audit' => $offpage_backlink_audit,
            'offpage_link_building' => $offpage_link_building,
            'missing_pages' => $missing_pages,
            'content_strategy' => $content_strategy,
            'onpage_optimization' => $onpage_optimization,
            'technical_site_audit' => $technical_site_audit,
            'technical_sitemap_robots' => $technical_sitemap_robots,
            'technical_pagespeed' => $technical_pagespeed,
            'technical_mobile_optimization' => $technical_mobile_optimization,
            'technical_errors' => $technical_errors,
            'technical_404_errors' => $technical_404_errors,
            'technical_301_redirects' => $technical_301_redirects,
            'technical_410_gone' => $technical_410_gone,
            'technical_schema' => $technical_schema,
            'technical_schema_markup' => $technical_schema_markup,
            'technical_internal_linking' => $technical_internal_linking,
        );
        $keyword['overview'] = array(
            'keyword_terms' => intval($keyword['gsc_count'] ?? 0) + intval($keyword['suggestions_count'] ?? 0),
            'page_matches' => intval($keyword['associations_count'] ?? 0),
            'monitoring_keywords_tracked' => intval($monitoring_ranking['tracked_count'] ?? 0),
            'monitoring_keywords_refreshed' => intval($monitoring_ranking['refreshed_count'] ?? 0),
            'monitoring_keywords_stale' => intval($monitoring_ranking['stale_count'] ?? 0),
            'competitor_gap_keywords' => intval($competitor_gap['count'] ?? 0),
            'identified_competitors' => intval($competitor_identification['count'] ?? 0),
            'backlink_domains' => intval($competitor_backlinks['count'] ?? 0),
            'backlink_audit_items' => intval($offpage_backlink_audit['count'] ?? 0),
            'link_building_orders' => intval($offpage_link_building['orders_count'] ?? 0),
            'link_building_issues' => intval($offpage_link_building['issues_count'] ?? 0),
            'missing_pages' => intval($missing_pages['count'] ?? 0),
            'content_strategies' => intval($content_strategy['total'] ?? 0),
            'onpage_entities' => intval($onpage_optimization['count'] ?? 0),
            'onpage_optimized' => intval($onpage_optimization['optimized_count'] ?? 0),
            'technical_issues' => intval($technical_site_audit['count'] ?? 0),
            'technical_pages_crawled' => intval($technical_site_audit['pages_crawled'] ?? 0),
            'sitemap_robots_checks' => intval($technical_sitemap_robots['count'] ?? 0),
            'pagespeed_score_overall' => intval($technical_pagespeed['overall_score'] ?? 0),
            'mobile_optimization_score' => intval($technical_mobile_optimization['mobile_score'] ?? 0),
            'technical_errors_total' => intval($technical_errors['count'] ?? 0),
            'technical_errors_conflicts' => intval($technical_errors['summary']['conflicts'] ?? 0),
            'technical_404_errors' => intval($technical_404_errors['count'] ?? 0),
            'technical_301_redirects' => intval($technical_301_redirects['count'] ?? 0),
            'technical_410_gone' => intval($technical_410_gone['count'] ?? 0),
            'technical_schema_pages' => intval(($technical_schema['summary']['total_pages'] ?? 0) ?: ($technical_schema_markup['total_pages'] ?? 0)),
            'internal_linking_keywords' => intval($technical_internal_linking['count'] ?? 0),
            'internal_links_added' => intval($technical_internal_linking['total_links'] ?? 0),
        );
        $keyword['project_job_rack'] = $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id));
        $keyword['has_existing_data'] = !empty($keyword['has_existing_data'])
            || !empty($monitoring_ranking['has_data'])
            || !empty($competitor_gap['has_data'])
            || !empty($competitor_identification['has_data'])
            || !empty($competitor_backlinks['has_data'])
            || !empty($offpage_backlink_audit['has_data'])
            || !empty($offpage_link_building['has_data'])
            || !empty($missing_pages['has_data'])
            || !empty($content_strategy['has_data'])
            || !empty($onpage_optimization['has_data'])
            || !empty($technical_site_audit['has_data'])
            || !empty($technical_sitemap_robots['has_data'])
            || !empty($technical_pagespeed['has_data']);
        $keyword['has_existing_data'] = !empty($keyword['has_existing_data'])
            || !empty($technical_mobile_optimization['has_data'])
            || !empty($technical_errors['has_data'])
            || !empty($technical_404_errors['has_data'])
            || !empty($technical_301_redirects['has_data'])
            || !empty($technical_410_gone['has_data'])
            || !empty($technical_schema['has_data'])
            || !empty($technical_schema_markup['has_data'])
            || !empty($technical_internal_linking['has_data']);

        return $keyword;
    }

    private function aysa_adjustments_get_monitoring_ranking_snapshot($project_id) {
        $project_id = intval($project_id);
        $context = $this->aysa_monitoring_resolve_context('');
        $target_host = (string) ($context['target'] ?? '');
        $associations = $this->get_keyword_discovery_local_associations($project_id);
        $tracked_map = array();
        foreach ($associations as $assoc) {
            if (!is_array($assoc)) {
                continue;
            }
            $keyword = trim((string) ($assoc['keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }
            $assoc_on = (string) ($assoc['associatedOn'] ?? $assoc['associated_on'] ?? 'other');
            $id_intern = intval($assoc['idIntern'] ?? $assoc['id_intern'] ?? 0);
            $link = (string) ($assoc['link'] ?? '');
            $tracked_map[$keyword . '|' . $assoc_on . '|' . $id_intern] = array(
                'keyword' => $keyword,
                'associated_on' => $assoc_on,
                'id_intern' => $id_intern,
                'link' => $link,
            );
        }

        $rows = $this->aysa_adjustments_get_monitoring_latest_rows($project_id, $target_host);
        $rows_map = array();
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $key = trim((string) ($row['keyword'] ?? '')) . '|' . (string) ($row['associated_on'] ?? 'other') . '|' . intval($row['id_intern'] ?? 0);
            $rows_map[$key] = $row;
        }

        $stale_rows = array();
        foreach ($tracked_map as $key => $tracked_row) {
            if (!isset($rows_map[$key])) {
                $stale_rows[] = array(
                    'keyword' => (string) ($tracked_row['keyword'] ?? ''),
                    'associated_on' => (string) ($tracked_row['associated_on'] ?? ''),
                    'id_intern' => intval($tracked_row['id_intern'] ?? 0),
                    'link' => (string) ($tracked_row['link'] ?? ''),
                    'reason' => 'No local rank snapshot saved yet.',
                );
            }
        }

        $status = $this->aysa_monitoring_get_refresh_status($project_id);
        $status_rows = array(
            array('metric' => 'Tracked keywords', 'value' => count($tracked_map)),
            array('metric' => 'Keywords with local ranks', 'value' => count($rows)),
            array('metric' => 'Stale keywords', 'value' => count($stale_rows)),
            array('metric' => 'Refresh job status', 'value' => (string) ($status['status'] ?? 'idle')),
            array('metric' => 'Processed', 'value' => intval($status['processed'] ?? 0) . ' / ' . intval($status['total'] ?? 0)),
            array('metric' => 'Refreshed', 'value' => intval($status['refreshed'] ?? 0)),
            array('metric' => 'Failed', 'value' => intval($status['failed'] ?? 0)),
            array('metric' => 'Deferred', 'value' => intval($status['deferred'] ?? 0)),
        );
        $last_error = trim((string) ($status['last_error'] ?? ''));
        if ($last_error !== '') {
            $status_rows[] = array('metric' => 'Last error', 'value' => $last_error);
        }

        return array(
            'id' => 'monitoring_ranking',
            'count' => count($tracked_map),
            'tracked_count' => count($tracked_map),
            'refreshed_count' => count($rows),
            'stale_count' => count($stale_rows),
            'rows' => $rows,
            'status_rows' => $status_rows,
            'stale_rows' => $stale_rows,
            'job_status' => (string) ($status['status'] ?? 'idle'),
            'job_processed' => intval($status['processed'] ?? 0),
            'job_total' => intval($status['total'] ?? 0),
            'job_failed' => intval($status['failed'] ?? 0),
            'job_deferred' => intval($status['deferred'] ?? 0),
            'job_last_error' => $last_error,
            'target_host' => $target_host,
            'has_data' => !empty($tracked_map) || !empty($rows),
        );
    }

    private function aysa_adjustments_get_monitoring_latest_rows($project_id, $target_host) {
        global $wpdb;

        $project_id = intval($project_id);
        $target_host = (string) $target_host;
        $table = $this->aysa_monitoring_history_table();
        $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table));
        if ($table_exists !== $table) {
            return array();
        }

        $sql = "
            SELECT h.*
            FROM {$table} h
            INNER JOIN (
                SELECT MAX(id) AS max_id
                FROM {$table}
                WHERE project_id = %d
                  AND target_host = %s
                  AND (
                        (position IS NOT NULL AND position > 0)
                     OR (position_absolute IS NOT NULL AND position_absolute > 0)
                     OR (ctr IS NOT NULL AND ctr > 0)
                     OR (search_volume IS NOT NULL AND search_volume > 0)
                     OR (bounce_rate IS NOT NULL AND bounce_rate > 0)
                     OR (sessions IS NOT NULL AND sessions > 0)
                     OR (gsc_clicks IS NOT NULL AND gsc_clicks > 0)
                     OR (gsc_impressions IS NOT NULL AND gsc_impressions > 0)
                     OR (intent IS NOT NULL AND intent <> '')
                     OR (traffic IS NOT NULL AND traffic > 0)
                     OR (traffic_pct IS NOT NULL AND traffic_pct > 0)
                     OR (kd IS NOT NULL AND kd > 0)
                     OR (target_url IS NOT NULL AND target_url <> '')
                     OR (item_types IS NOT NULL AND item_types <> '')
                     OR (check_url IS NOT NULL AND check_url <> '')
                     OR (matched_item_type IS NOT NULL AND matched_item_type <> '')
                  )
                GROUP BY keyword, associated_on, id_intern
            ) latest ON latest.max_id = h.id
            ORDER BY h.keyword ASC
        ";
        $raw_rows = $wpdb->get_results($wpdb->prepare($sql, $project_id, $target_host), ARRAY_A);
        if (!is_array($raw_rows)) {
            return array();
        }

        $out = array();
        foreach ($raw_rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $latest_pos = is_null($row['position']) ? null : floatval($row['position']);
            $prev_pos = $wpdb->get_var($wpdb->prepare(
                "SELECT position
                 FROM {$table}
                 WHERE project_id = %d
                   AND target_host = %s
                   AND keyword = %s
                   AND associated_on = %s
                   AND id_intern = %d
                 ORDER BY id DESC
                 LIMIT 1 OFFSET 1",
                $project_id,
                $target_host,
                (string) $row['keyword'],
                (string) $row['associated_on'],
                intval($row['id_intern'])
            ));
            $prev_pos = is_null($prev_pos) ? null : floatval($prev_pos);
            $delta = null;
            if (!is_null($latest_pos) && !is_null($prev_pos) && $prev_pos > 0) {
                $delta = $prev_pos - $latest_pos;
            }

            $out[] = array(
                'keyword' => (string) ($row['keyword'] ?? ''),
                'associated_on' => (string) ($row['associated_on'] ?? ''),
                'id_intern' => intval($row['id_intern'] ?? 0),
                'link' => (string) ($row['target_url'] ?? ''),
                'position' => $latest_pos,
                'previous_position' => $prev_pos,
                'position_delta' => $delta,
                'search_volume' => is_null($row['search_volume']) ? null : intval($row['search_volume']),
                'intent' => (string) ($row['intent'] ?? ''),
                'traffic' => is_null($row['traffic']) ? null : intval($row['traffic']),
                'traffic_pct' => is_null($row['traffic_pct']) ? null : floatval($row['traffic_pct']),
                'ctr' => is_null($row['ctr']) ? null : floatval($row['ctr']),
                'last_checked_at' => (string) ($row['tracked_at'] ?? ''),
            );
        }

        return $out;
    }

    private function aysa_adjustments_get_monitoring_refresh_associations($project_id) {
        $project_id = intval($project_id);
        $rows = $this->get_keyword_discovery_local_associations($project_id);
        $normalized = array();
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }
            $normalized[] = array(
                'keyword' => $keyword,
                'associated_on' => (string) ($row['associatedOn'] ?? $row['associated_on'] ?? 'other'),
                'id_intern' => intval($row['idIntern'] ?? $row['id_intern'] ?? 0),
                'link' => (string) ($row['link'] ?? ''),
            );
        }
        return $this->aysa_monitoring_normalize_associations($normalized);
    }

    private function aysa_adjustments_start_monitoring_refresh($project_id) {
        $project_id = intval($project_id);
        $associations = $this->aysa_adjustments_get_monitoring_refresh_associations($project_id);
        if (empty($associations)) {
            return array(
                'ok' => false,
                'error' => 'No tracked keywords provided.',
            );
        }

        $existing_status = $this->aysa_monitoring_get_refresh_status($project_id);
        if (($existing_status['status'] ?? '') === 'running') {
            return array(
                'ok' => true,
                'already_running' => true,
                'status' => $existing_status,
            );
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_monitoring_rank_refresh_start_background',
            'monitoring_rank_refresh_start_background',
            array(
                'module' => 'monitoring_analytics',
                'source' => 'adjustments_conversation',
            ),
            120,
            '/v1/dataforseo/rank-tracking/refresh-start-background'
        );

        $context = $this->aysa_monitoring_resolve_context('');
        $run_id = uniqid('mon_', true);
        $now = time();
        $queue = array(
            'run_id' => $run_id,
            'project_id' => $project_id,
            'context' => $context,
            'associations' => array_values($associations),
            'cursor' => 0,
            'batch_size' => 10,
            'current_retry' => 0,
            'created_at' => $now,
        );
        update_option($this->aysa_monitoring_refresh_queue_key($project_id), $queue, false);

        $status = array(
            'run_id' => $run_id,
            'status' => 'running',
            'total' => count($associations),
            'processed' => 0,
            'refreshed' => 0,
            'failed' => 0,
            'deferred' => 0,
            'last_error' => '',
            'started_at' => $now,
            'updated_at' => $now,
            'finished_at' => 0,
        );
        $this->aysa_monitoring_save_refresh_status($project_id, $status);
        $this->aysa_monitoring_ensure_refresh_cron_tick($project_id);
        $this->aysa_monitoring_schedule_background_refresh($project_id, 1);

        return array(
            'ok' => true,
            'started' => true,
            'status' => $status,
        );
    }

    private function aysa_adjustments_get_offpage_link_building_snapshot($project_id) {
        $project_id = intval($project_id);
        $orders = $this->aysa_get_adverlink_orders($project_id);
        $issues = get_option($this->aysa_get_adverlink_link_issues_key($project_id), array());
        $issuesLastSync = get_option($this->aysa_get_adverlink_link_issues_sync_key($project_id), array());

        if (!is_array($orders)) {
            $orders = array();
        }
        if (!is_array($issues)) {
            $issues = array();
        }
        if (!is_array($issuesLastSync)) {
            $issuesLastSync = array();
        }

        $orderRows = array();
        $statusCounts = array(
            'pending' => 0,
            'completed' => 0,
            'issues' => 0,
        );
        $subscriptionCounts = array(
            'active' => 0,
            'due' => 0,
            'renewal_issues' => 0,
        );
        $nowTs = time();
        foreach ($orders as $order) {
            if (!is_array($order)) {
                continue;
            }
            $status = strtoupper(trim((string) ($order['status'] ?? 'created')));
            $portalDomain = sanitize_text_field((string) ($order['portal_domain'] ?? ''));
            $keyword = sanitize_text_field((string) ($order['keyword'] ?? ''));
            $targetUrl = esc_url_raw((string) ($order['target_url'] ?? ''));
            $serviceType = sanitize_text_field((string) ($order['service_type'] ?? ''));
            $orderRef = sanitize_text_field((string) ($order['order_ref'] ?? ''));
            $billingType = sanitize_text_field((string) ($order['billing_type'] ?? 'one_time'));
            $verification = isset($order['verification']) && is_array($order['verification']) ? $order['verification'] : array();
            $verificationStatus = '';
            if (!empty($verification)) {
                $isValid = isset($verification['is_valid']) ? (bool) $verification['is_valid'] : null;
                if ($isValid === true) {
                    $verificationStatus = 'valid';
                } elseif ($isValid === false) {
                    $verificationStatus = 'invalid';
                }
            }

            $bucket = 'pending';
            if (in_array($status, array('COMPLETED', 'PUBLISHED', 'LIVE'), true)) {
                $bucket = 'completed';
            } elseif (in_array($status, array('FAILED', 'REJECTED', 'CANCELLED'), true) || $verificationStatus === 'invalid') {
                $bucket = 'issues';
            }
            $statusCounts[$bucket]++;

            $subscription = isset($order['subscription']) && is_array($order['subscription']) ? $order['subscription'] : array();
            $subscriptionStatus = sanitize_text_field((string) ($subscription['status'] ?? ''));
            $nextRenewalAt = sanitize_text_field((string) ($subscription['next_renewal_at'] ?? ''));
            $renewalCount = intval($order['renewal_count'] ?? 0);
            $renewalError = sanitize_text_field((string) ($order['last_subscription_renewal_error'] ?? ''));
            $nextRenewalTs = $this->aysa_subscription_next_renewal_timestamp($order);
            if ($this->aysa_is_subscription_order($order)) {
                if (strtolower($subscriptionStatus) === 'active') {
                    $subscriptionCounts['active']++;
                }
                if ($nextRenewalTs > 0 && $nextRenewalTs <= $nowTs) {
                    $subscriptionCounts['due']++;
                }
                if ($renewalError !== '') {
                    $subscriptionCounts['renewal_issues']++;
                }
            }

            $orderRows[] = array(
                'order_ref' => $orderRef,
                'portal_domain' => $portalDomain,
                'service_type' => $serviceType,
                'billing_type' => $billingType,
                'keyword' => $keyword,
                'target_url' => $targetUrl,
                'status' => $status,
                'verification' => $verificationStatus,
                'subscription_status' => $subscriptionStatus,
                'next_renewal_at' => $nextRenewalAt,
                'renewal_count' => $renewalCount,
                'renewal_error' => $renewalError,
                'created_at' => sanitize_text_field((string) ($order['created_at'] ?? '')),
                'charged_credits_cents' => intval($order['charged_credits_cents'] ?? 0),
            );
        }

        usort($orderRows, static function(array $a, array $b): int {
            return strcmp((string) ($b['created_at'] ?? ''), (string) ($a['created_at'] ?? ''));
        });

        $issueRows = array();
        foreach ($issues as $item) {
            if (!is_array($item)) {
                continue;
            }
            $issueRows[] = array(
                'portal_domain' => sanitize_text_field((string) ($item['portal_domain'] ?? $item['domain'] ?? '')),
                'target_url' => esc_url_raw((string) ($item['target_url'] ?? '')),
                'live_url' => esc_url_raw((string) ($item['live_url'] ?? $item['url'] ?? '')),
                'issue_type' => sanitize_text_field((string) ($item['issue_type'] ?? '')),
                'status' => sanitize_text_field((string) ($item['status'] ?? '')),
                'message' => sanitize_text_field((string) ($item['message'] ?? $item['issue_message'] ?? '')),
                'order_ref' => sanitize_text_field((string) ($item['order_ref'] ?? '')),
                'updated_at' => sanitize_text_field((string) ($item['updated_at'] ?? '')),
            );
        }

        usort($issueRows, static function(array $a, array $b): int {
            return strcmp((string) ($b['updated_at'] ?? ''), (string) ($a['updated_at'] ?? ''));
        });

        $overviewRows = array(
            array('metric' => 'Orders tracked', 'value' => count($orderRows)),
            array('metric' => 'Pending / in progress', 'value' => intval($statusCounts['pending'])),
            array('metric' => 'Completed / live', 'value' => intval($statusCounts['completed'])),
            array('metric' => 'Orders with issues', 'value' => intval($statusCounts['issues'])),
            array('metric' => 'Link issues detected', 'value' => count($issueRows)),
            array('metric' => 'Monthly subscriptions active', 'value' => intval($subscriptionCounts['active'])),
            array('metric' => 'Monthly renewals due', 'value' => intval($subscriptionCounts['due'])),
            array('metric' => 'Monthly renewal issues', 'value' => intval($subscriptionCounts['renewal_issues'])),
        );

        return array(
            'id' => 'offpage_link_building',
            'count' => count($orderRows) + count($issueRows),
            'orders_count' => count($orderRows),
            'issues_count' => count($issueRows),
            'pending_orders' => intval($statusCounts['pending']),
            'completed_orders' => intval($statusCounts['completed']),
            'problem_orders' => intval($statusCounts['issues']),
            'subscriptions_active_count' => intval($subscriptionCounts['active']),
            'subscriptions_due_count' => intval($subscriptionCounts['due']),
            'subscriptions_issues_count' => intval($subscriptionCounts['renewal_issues']),
            'rows' => $orderRows,
            'orders_rows' => $orderRows,
            'issues_rows' => $issueRows,
            'overview_rows' => $overviewRows,
            'last_sync' => $issuesLastSync,
            'has_data' => !empty($orderRows) || !empty($issueRows),
        );
    }

    private function aysa_adjustments_get_onpage_optimization_snapshot($project_id) {
        $service = $this->aysa_onpage_workspace_service();
        if (!$service) {
            return array(
                'id' => 'onpage_optimization',
                'count' => 0,
                'rows' => array(),
                'optimized_count' => 0,
                'needs_work_count' => 0,
                'not_optimized_count' => 0,
                'top_types' => array(),
                'has_data' => false,
            );
        }

        $project_id = intval($project_id);
        $workspace = $service->build_workspace($project_id, array(
            'entity_group' => 'page',
            'page' => 1,
            'per_page' => 500,
            'refresh' => false,
        ));
        $summary = is_array($workspace['summary'] ?? null) ? $workspace['summary'] : array();

        $rows = array();
        $type_counts = array();
        foreach (array('page', 'article', 'ecommerce_category', 'product', 'other') as $group) {
            $group_workspace = $service->build_workspace($project_id, array(
                'entity_group' => $group,
                'page' => 1,
                'per_page' => 250,
            ));
            foreach ((array) ($group_workspace['rows'] ?? array()) as $row) {
                $group_label = match ((string) ($row['entity_group'] ?? 'other')) {
                    'page' => 'Page',
                    'article' => 'Article',
                    'ecommerce_category' => 'Ecommerce Category',
                    'product' => 'Product',
                    default => 'Other',
                };
                $type_counts[$group_label] = intval($type_counts[$group_label] ?? 0) + 1;
                $rows[] = array(
                    'entity_id' => intval($row['entity_id'] ?? 0),
                    'entity_kind' => (string) ($row['object_kind'] ?? ''),
                    'entity_processor_type' => $group_label,
                    'entity_type' => $group_label,
                    'entity' => (string) ($row['title'] ?? ''),
                    'main_keyword' => (string) ($row['main_keyword'] ?? ''),
                    'all_keywords' => (string) ($row['main_keyword'] ?? ''),
                    'optimized' => ((string) ($row['seo_state'] ?? '') === 'optimized') ? 'Optimized' : 'Not optimized',
                    'seo_score' => isset($row['seo_score']) && is_numeric($row['seo_score']) ? (string) intval($row['seo_score']) . '/100' : 'N/A',
                    'page' => (string) ($row['url'] ?? ''),
                    'edit_url' => '',
                );
            }
        }

        $top_types = array();
        foreach ($type_counts as $label => $count) {
            $top_types[] = array(
                'label' => (string) $label,
                'count' => intval($count),
            );
        }

        return array(
            'id' => 'onpage_optimization',
            'count' => intval($summary['all'] ?? count($rows)),
            'rows' => $rows,
            'optimized_count' => intval($summary['optimized'] ?? 0),
            'needs_work_count' => 0,
            'not_optimized_count' => intval($summary['not_optimized'] ?? 0),
            'top_types' => array_slice($top_types, 0, 5),
            'has_data' => !empty($rows),
        );
    }

    private function aysa_adjustments_get_onpage_entity_quality($kind, $entity_id) {
        if ($kind === 'term') {
            return array(
                'seo_score' => get_term_meta($entity_id, '_aysa_entity_seo_score_cache', true),
                'content_source' => get_term_meta($entity_id, '_aysa_entity_content_source_cache', true),
            );
        }

        return array(
            'seo_score' => get_post_meta($entity_id, '_aysa_entity_seo_score_cache', true),
            'content_source' => get_post_meta($entity_id, '_aysa_entity_content_source_cache', true),
        );
    }

    private function aysa_adjustments_get_onpage_optimized_status($score, $content_source) {
        $content_source = strtolower(trim((string) $content_source));
        $score = is_numeric($score) ? intval($score) : null;
        if ($score !== null && $score >= 80) {
            return 'Optimized';
        }
        if ($score !== null && $score > 0) {
            return 'Needs work';
        }
        if ($content_source === 'aysa_generated') {
            return 'Optimized';
        }
        return 'Not optimized';
    }

    private function aysa_adjustments_get_onpage_entity_type_label($kind, $entity_id) {
        if ($kind === 'term') {
            $term = get_term($entity_id);
            if ($term && !is_wp_error($term)) {
                return match ((string) ($term->taxonomy ?? '')) {
                    'product_cat' => 'Category',
                    'category' => 'Article Category',
                    default => 'Term',
                };
            }
            return 'Term';
        }

        return match ((string) get_post_type($entity_id)) {
            'product' => 'Product',
            'page' => 'Page',
            'post' => 'Article',
            default => 'Post',
        };
    }

    private function aysa_adjustments_get_onpage_entity_title($kind, $entity_id) {
        if ($kind === 'term') {
            $term = get_term($entity_id);
            return ($term && !is_wp_error($term)) ? (string) $term->name : ('Term #' . intval($entity_id));
        }

        $post = get_post($entity_id);
        if ($post instanceof WP_Post) {
            $title = trim((string) get_the_title($post));
            return $title !== '' ? $title : ('Post #' . intval($entity_id));
        }
        return 'Post #' . intval($entity_id);
    }

    private function aysa_adjustments_get_onpage_entity_processor_type($kind, $entity_id) {
        if ($kind === 'term') {
            $term = get_term($entity_id);
            if ($term && !is_wp_error($term)) {
                return match ((string) ($term->taxonomy ?? '')) {
                    'product_cat', 'category' => 'Category',
                    default => 'Category',
                };
            }
            return 'Category';
        }

        return match ((string) get_post_type($entity_id)) {
            'product' => 'Product',
            'page' => 'Page',
            'post' => 'Post',
            default => 'Post',
        };
    }

    private function aysa_adjustments_get_onpage_entity_edit_url($kind, $entity_id) {
        $entity_id = intval($entity_id);
        if ($entity_id <= 0) {
            return '';
        }

        if ($kind === 'term') {
            $term = get_term($entity_id);
            if (!$term || is_wp_error($term)) {
                return '';
            }
            $taxonomy = (string) ($term->taxonomy ?? '');
            if ($taxonomy === '') {
                return '';
            }
            return (string) admin_url('term.php?taxonomy=' . rawurlencode($taxonomy) . '&tag_ID=' . $entity_id);
        }

        return (string) get_edit_post_link($entity_id, 'raw');
    }

    private function aysa_adjustments_get_onpage_entity_url($kind, $entity_id) {
        if ($kind === 'term') {
            $link = get_term_link(intval($entity_id));
            return is_wp_error($link) ? '' : (string) $link;
        }
        $link = get_permalink(intval($entity_id));
        return is_string($link) ? $link : '';
    }

    private function aysa_adjustments_reduce_research_snapshot_for_ai($snapshot) {
        if (!is_array($snapshot)) {
            return array();
        }
        $reduced = $snapshot;
        foreach (array('gsc_rows', 'suggestion_rows', 'association_rows') as $bucket) {
            if (isset($reduced[$bucket]) && is_array($reduced[$bucket])) {
                $reduced[$bucket] = array_slice(array_values($reduced[$bucket]), 0, 20);
            }
        }
        if (isset($reduced['modules']) && is_array($reduced['modules'])) {
            foreach ($reduced['modules'] as $moduleId => $module) {
                if (!is_array($module)) {
                    continue;
                }
                if (isset($module['rows']) && is_array($module['rows'])) {
                    $reduced['modules'][$moduleId]['rows'] = array_slice(array_values($module['rows']), 0, 20);
                }
            }
        }
        return $reduced;
    }

    private function aysa_adjustments_get_competitor_gap_snapshot($project_id) {
        $cached = $this->aysa_competitor_gap_get_cached_payload($project_id);
        $data = is_array($cached) && isset($cached['data']) && is_array($cached['data']) ? $cached['data'] : array();
        $ranked_keywords = isset($data['rankedKeywords']) && is_array($data['rankedKeywords']) ? array_values($data['rankedKeywords']) : array();
        $search_volumes = isset($data['searchVolume']) && is_array($data['searchVolume']) ? $data['searchVolume'] : array();
        $keyword_metrics = isset($data['keywordMetrics']) && is_array($data['keywordMetrics']) ? $data['keywordMetrics'] : array();

        $rows = array();
        foreach ($ranked_keywords as $row) {
            $keyword = '';
            $search_volume = 0;
            $difficulty = 0;
            $intent = '';

            if (is_array($row)) {
                $keyword = sanitize_text_field((string) ($row['keyword'] ?? $row['query'] ?? ''));
                $search_volume = intval($row['searchVolume'] ?? $row['search_volume'] ?? 0);
                $difficulty = intval($row['difficulty'] ?? $row['keyword_difficulty'] ?? 0);
                $intent = sanitize_text_field((string) ($row['intent'] ?? ''));
            } else {
                $keyword = sanitize_text_field((string) $row);
            }

            if ($keyword === '') {
                continue;
            }

            if ($search_volume <= 0 && isset($search_volumes[$keyword])) {
                $search_volume = intval($search_volumes[$keyword]);
            }

            if (isset($keyword_metrics[$keyword]) && is_array($keyword_metrics[$keyword])) {
                $metric = $keyword_metrics[$keyword];
                if ($search_volume <= 0) {
                    $search_volume = intval($metric['search_volume'] ?? 0);
                }
                if ($difficulty <= 0) {
                    $difficulty = intval(round(floatval($metric['kd'] ?? $metric['difficulty'] ?? 0)));
                }
                if ($intent === '') {
                    $intent = sanitize_text_field((string) ($metric['intent'] ?? ($metric['search_intent_info']['main_intent'] ?? '')));
                }
            }

            $rows[] = array(
                'keyword' => $keyword,
                'search_volume' => $search_volume,
                'difficulty' => $difficulty,
                'intent' => $intent,
                'source' => 'competitor_gap',
            );
        }

        return array(
            'id' => 'competitor_gap',
            'count' => count($rows),
            'rows' => $rows,
            'cached_at' => intval($cached['cached_at'] ?? 0),
            'has_data' => !empty($rows),
        );
    }

    private function aysa_adjustments_get_competitor_identification_snapshot($project_id) {
        $cached = $this->aysa_competitor_identification_get_cached_payload($project_id);
        $data = is_array($cached) && isset($cached['data']) && is_array($cached['data']) ? $cached['data'] : array();
        $active_rows = isset($data['active_competitors']) && is_array($data['active_competitors']) ? array_values($data['active_competitors']) : array();
        $suggested_rows = isset($data['suggested_competitors']) && is_array($data['suggested_competitors']) ? array_values($data['suggested_competitors']) : array();
        $rows = array_merge($active_rows, $suggested_rows);
        $rows = array_map(function($row) {
            if (!is_array($row)) {
                return array();
            }
            return array(
                'domain' => sanitize_text_field((string) ($row['domain'] ?? $row['competitor'] ?? '')),
                'overlap_keywords' => intval($row['overlap_keywords'] ?? $row['keywords_overlap'] ?? 0),
                'keyword_hits' => intval($row['keyword_hits'] ?? 0),
                'avg_position' => isset($row['avg_position']) ? round(floatval($row['avg_position']), 2) : '',
                'competitor_status' => sanitize_key((string) ($row['competitor_status'] ?? 'suggested')),
                'status_reason' => sanitize_text_field((string) ($row['status_reason'] ?? '')),
                'source' => 'competitor_identification',
            );
        }, $rows);

        return array(
            'id' => 'competitor_identification',
            'count' => count($rows),
            'rows' => $rows,
            'cached_at' => intval($cached['cached_at'] ?? 0),
            'has_data' => !empty($rows),
            'active_count' => count($active_rows),
            'suggested_count' => count($suggested_rows),
        );
    }

    private function aysa_adjustments_get_competitor_backlinks_snapshot($project_id) {
        $summary = get_option('aysa_backlink_summary_' . intval($project_id), array());
        $items = is_array($summary) && isset($summary['items']) && is_array($summary['items']) ? array_values($summary['items']) : array();
        $home_host = $this->aysa_monitoring_extract_host((string) home_url('/'));
        $resolved_context = $this->aysa_monitoring_resolve_context($home_host !== '' ? $home_host : 'florariebragadiru.ro');
        $target_host = strtolower(trim((string) ($resolved_context['target'] ?? '')));
        if ($target_host !== '') {
            $target_host = $this->aysa_monitoring_extract_host($target_host);
        }
        if ($target_host === '') {
            $target_host = $home_host;
        }
        $target_host = strtolower(preg_replace('/^www\./', '', (string) $target_host));
        $rows = array_map(function($row) {
            if (!is_array($row)) {
                return array();
            }
            return array(
                'domain' => sanitize_text_field((string) ($row['domain'] ?? '')),
                'authority' => isset($row['authority'])
                    ? round(floatval($row['authority']), 2)
                    : (isset($row['avg_authority']) ? round(floatval($row['avg_authority']), 2) : ''),
                'backlinks' => intval($row['backlinks'] ?? $row['links'] ?? $row['total_backlinks'] ?? 0),
                'source' => 'competitor_backlinks',
            );
        }, $items);
        $rows = array_values(array_filter($rows, function($row) use ($target_host) {
            if (!is_array($row)) {
                return false;
            }
            $domain = strtolower(preg_replace('/^www\./', '', trim((string) ($row['domain'] ?? ''))));
            if ($domain === '') {
                return false;
            }
            return $target_host === '' || $domain !== $target_host;
        }));

        return array(
            'id' => 'competitor_backlinks',
            'count' => count($rows),
            'rows' => $rows,
            'cached_at' => strtotime((string) ($summary['generated_at'] ?? '')) ?: 0,
            'has_data' => !empty($rows),
        );
    }

    private function aysa_adjustments_get_offpage_backlink_audit_snapshot($project_id) {
        $project_id = intval($project_id);
        $snapshot = get_option($this->aysa_get_backlink_audit_key($project_id), array());
        $last_sync = get_option($this->aysa_get_backlink_audit_sync_key($project_id), array());
        $items = (is_array($snapshot) && isset($snapshot['items']) && is_array($snapshot['items'])) ? array_values($snapshot['items']) : array();

        $rows = array();
        $refDomains = array();
        $followCount = 0;
        $brokenCount = 0;
        $riskCounts = array(
            'low' => 0,
            'medium' => 0,
            'high' => 0,
        );

        foreach ($items as $item) {
            if (!is_array($item)) {
                continue;
            }
            $sourceUrl = esc_url_raw((string) ($item['source_url'] ?? $item['source'] ?? ''));
            $targetUrl = esc_url_raw((string) ($item['target_url'] ?? $item['target'] ?? ''));
            $anchorText = sanitize_text_field((string) ($item['anchor_text'] ?? $item['anchor'] ?? ''));
            $referringDomain = sanitize_text_field((string) ($item['referring_domain'] ?? $item['domain'] ?? ''));
            $follow = strtolower(trim((string) ($item['follow'] ?? $item['follow_attr'] ?? $item['link_attr'] ?? '')));
            $health = strtolower(trim((string) ($item['health'] ?? $item['status'] ?? '')));
            $risk = strtolower(trim((string) ($item['risk'] ?? $item['risk_level'] ?? '')));
            $issueType = sanitize_text_field((string) ($item['issue_type'] ?? ''));
            $issueMessage = sanitize_text_field((string) ($item['issue_message'] ?? $item['issue_summary'] ?? ''));
            $firstSeen = sanitize_text_field((string) ($item['first_seen'] ?? ''));
            $lastSeen = sanitize_text_field((string) ($item['last_seen'] ?? ''));

            if ($risk === '' && $health === 'broken') {
                $risk = 'high';
            }
            if (!isset($riskCounts[$risk])) {
                $risk = '';
            }

            if ($referringDomain !== '') {
                $refDomains[strtolower($referringDomain)] = true;
            }
            if ($follow === 'follow') {
                $followCount++;
            }
            if ($health === 'broken' || $issueType === 'broken' || str_contains(strtolower($issueMessage), 'broken')) {
                $brokenCount++;
            }
            if ($risk !== '') {
                $riskCounts[$risk]++;
            }

            $rows[] = array(
                'referring_domain' => $referringDomain,
                'source_url' => $sourceUrl,
                'target_url' => $targetUrl,
                'anchor_text' => $anchorText,
                'risk' => $risk !== '' ? ucfirst($risk) : '',
                'follow' => $follow,
                'health' => $health,
                'issue_type' => $issueType,
                'issue_message' => $issueMessage,
                'first_seen' => $firstSeen,
                'last_seen' => $lastSeen,
            );
        }

        usort($rows, static function(array $a, array $b): int {
            $riskWeight = static function(string $value): int {
                $value = strtolower(trim($value));
                return match ($value) {
                    'high' => 3,
                    'medium' => 2,
                    'low' => 1,
                    default => 0,
                };
            };
            $riskCmp = $riskWeight((string) ($b['risk'] ?? '')) <=> $riskWeight((string) ($a['risk'] ?? ''));
            if ($riskCmp !== 0) {
                return $riskCmp;
            }
            return strcasecmp((string) ($a['referring_domain'] ?? ''), (string) ($b['referring_domain'] ?? ''));
        });

        $count = count($rows);
        return array(
            'id' => 'offpage_backlink_audit',
            'count' => $count,
            'rows' => $rows,
            'referring_domains' => count($refDomains),
            'follow_ratio' => $count > 0 ? round(($followCount / $count) * 100, 2) : 0,
            'broken_count' => $brokenCount,
            'risk_low' => intval($riskCounts['low']),
            'risk_medium' => intval($riskCounts['medium']),
            'risk_high' => intval($riskCounts['high']),
            'provider' => sanitize_text_field((string) ($snapshot['provider'] ?? '')),
            'generated_at' => sanitize_text_field((string) ($snapshot['generated_at'] ?? '')),
            'last_sync' => is_array($last_sync) ? $last_sync : array(),
            'has_data' => !empty($rows),
        );
    }

    private function aysa_adjustments_get_missing_pages_snapshot($project_id) {
        global $wpdb;

        $rows = array();
        $keyword_table = $this->aysa_keyword_discovery_table();
        $keyword_table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $keyword_table));
        if ($keyword_table_exists === $keyword_table) {
            $keyword_rows = $wpdb->get_results($wpdb->prepare(
                "SELECT keyword, source, search_volume, difficulty, target_url
                 FROM {$keyword_table}
                 WHERE project_id = %d
                   AND (target_url IS NULL OR target_url = '')
                 ORDER BY search_volume DESC, keyword ASC",
                $project_id
            ), ARRAY_A);
            if (is_array($keyword_rows)) {
                foreach ($keyword_rows as $row) {
                    $keyword = sanitize_text_field((string) ($row['keyword'] ?? ''));
                    if ($keyword === '') {
                        continue;
                    }
                    $rows[] = array(
                        'keyword' => $keyword,
                        'source' => sanitize_text_field((string) ($row['source'] ?? 'missing_pages')),
                        'search_volume' => intval($row['search_volume'] ?? 0),
                        'difficulty' => intval($row['difficulty'] ?? 0),
                        'target' => '',
                    );
                }
            }
        }

        $strategy_table = $wpdb->prefix . 'aysa_content_strategies';
        $strategy_table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $strategy_table));
        if ($strategy_table_exists === $strategy_table) {
            $strategy_rows = $wpdb->get_results($wpdb->prepare(
                "SELECT title, keywords, intent, recommended_page_type
                 FROM {$strategy_table}
                 WHERE project_id = %d
                   AND strategy_type = 'missing_pages'
                   AND (associated_page IS NULL OR associated_page = '')",
                $project_id
            ), ARRAY_A);
            if (is_array($strategy_rows)) {
                foreach ($strategy_rows as $row) {
                    $keyword = $this->aysa_missing_page_extract_keyword((string) ($row['title'] ?? ''), (string) ($row['keywords'] ?? ''));
                    if ($keyword === '') {
                        continue;
                    }
                    $rows[] = array(
                        'keyword' => $keyword,
                        'source' => 'missing_pages_plan',
                        'search_volume' => 0,
                        'difficulty' => 0,
                        'target' => sanitize_text_field((string) ($row['recommended_page_type'] ?? 'Landing Page')),
                    );
                }
            }
        }

        return array(
            'id' => 'missing_pages',
            'count' => count($rows),
            'rows' => $rows,
            'has_data' => !empty($rows),
        );
    }

    private function aysa_adjustments_get_content_strategy_snapshot($project_id) {
        global $wpdb;
        $table = $wpdb->prefix . 'aysa_content_strategies';
        $table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($table_exists !== $table) {
            return array(
                'id' => 'content_strategy',
                'total' => 0,
                'generated' => 0,
                'published' => 0,
                'pending' => 0,
                'published_today_count' => 0,
                'published_today' => array(),
                'rows' => array(),
                'has_data' => false,
            );
        }

        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT id, title, strategy_type, status, associated_page, execution_stage, post_id
             FROM {$table}
             WHERE project_id = %d
               AND COALESCE(automation_status, '') <> 'deleted'
               AND duplicate_of_strategy_id IS NULL
               AND superseded_by_strategy_id IS NULL
             ORDER BY id DESC",
            $project_id
        ), ARRAY_A);
        $total = 0;
        $generated = 0;
        $published = 0;
        $published_today = array();
        $today_start = current_datetime()->setTime(0, 0, 0);
        $today_end = (clone $today_start)->modify('+1 day');
        $items = array();
        if (is_array($rows)) {
            foreach ($rows as $row) {
                $total++;
                $status = strtolower((string) ($row['status'] ?? ''));
                if ($status === 'generated') {
                    $generated++;
                }
                if (trim((string) ($row['associated_page'] ?? '')) !== '') {
                    $published++;
                }

                $post_id = isset($row['post_id']) ? intval($row['post_id']) : 0;
                if ($post_id > 0) {
                    $post = get_post($post_id);
                    if ($post && !is_wp_error($post) && get_post_status($post_id) === 'publish') {
                        $post_ts = strtotime((string) $post->post_date);
                        if ($post_ts && $post_ts >= $today_start->getTimestamp() && $post_ts < $today_end->getTimestamp()) {
                            $published_today[] = array(
                                'title' => sanitize_text_field(get_the_title($post_id)),
                                'url' => esc_url_raw((string) get_permalink($post_id)),
                                'time' => wp_date('H:i', $post_ts),
                                'post_id' => $post_id,
                            );
                        }
                    }
                }

                $items[] = array(
                    'title' => sanitize_text_field((string) ($row['title'] ?? '')),
                    'strategy_type' => sanitize_key((string) ($row['strategy_type'] ?? '')),
                    'status' => sanitize_text_field((string) ($row['status'] ?? '')),
                    'stage' => sanitize_text_field((string) ($row['execution_stage'] ?? '')),
                );
            }
        }

        return array(
            'id' => 'content_strategy',
            'total' => $total,
            'generated' => $generated,
            'published' => $published,
            'pending' => max(0, $total - $generated),
            'published_today_count' => count($published_today),
            'published_today' => array_slice($published_today, 0, 20),
            'rows' => $items,
            'has_data' => !empty($items),
        );
    }

    private function aysa_adjustments_get_technical_site_audit_snapshot($project_id) {
        $project_id = $this->aysa_resolve_technical_project_id(intval($project_id));
        $state = get_option($this->aysa_technical_site_audit_state_key($project_id), array());
        if (!is_array($state)) {
            $state = array();
        }
        if (empty($state)) {
            $latest_version = $this->aysa_sf_site_audit_get_latest_version_row($project_id);
            if (is_array($latest_version) && !empty($latest_version)) {
                $state = $this->aysa_sf_site_audit_build_state_from_version_row($latest_version);
            }
        }

        $audit = !empty($state['audit_snapshot']) && is_array($state['audit_snapshot']) ? $state['audit_snapshot'] : array();
        $overview = !empty($audit['overview']) && is_array($audit['overview']) ? $audit['overview'] : array();
        $issues = !empty($audit['issues']) && is_array($audit['issues']) ? array_values($audit['issues']) : array();
        $pages = !empty($audit['pages']) && is_array($audit['pages']) ? array_values($audit['pages']) : array();
        $statistics = !empty($audit['statistics']) && is_array($audit['statistics']) ? $audit['statistics'] : array();
        $files = !empty($state['result_files']) && is_array($state['result_files']) ? array_values($state['result_files']) : array();
        $history_count = $this->aysa_sf_site_audit_version_history_count($project_id);
        $pages_crawled = intval($overview['pages_crawled'] ?? 0);
        $csv_file_count = 0;
        foreach ($files as $file) {
            if (!is_array($file)) {
                continue;
            }
            $filename = strtolower((string) ($file['filename'] ?? ''));
            $type = strtolower((string) ($file['type'] ?? ''));
            if ($type === 'csv' || substr($filename, -4) === '.csv') {
                $csv_file_count++;
            }
        }
        $valid_crawl = $pages_crawled > 0 && $csv_file_count > 0;
        $snapshot_status = sanitize_key((string) ($state['status'] ?? 'idle'));
        if (!$valid_crawl && in_array($snapshot_status, array('completed', 'complete', 'success', 'succeeded', 'ready'), true)) {
            $snapshot_status = 'invalid';
        }
        $issue_lifecycle = $this->aysa_technical_site_audit_get_issue_lifecycle_map($project_id);

        $issue_rows = array();
        foreach ($issues as $row) {
            $normalized_issue = $this->aysa_adjustments_normalize_technical_site_audit_issue($row, $overview, $issue_lifecycle);
            if (!empty($normalized_issue)) {
                $issue_rows[] = $normalized_issue;
            }
        }

        $page_rows = array_map(function($row) {
            if (!is_array($row)) {
                return array();
            }
            return array(
                'url' => esc_url_raw((string) ($row['url'] ?? '')),
                'title' => sanitize_text_field((string) ($row['title'] ?? '')),
                'status_code' => intval($row['status_code'] ?? 0),
                'indexability' => sanitize_text_field((string) ($row['indexability'] ?? '')),
                'crawl_depth' => intval($row['crawl_depth'] ?? 0),
                'inlinks' => intval($row['inlinks'] ?? 0),
            );
        }, array_slice($pages, 0, 300));

        $file_rows = array_map(function($row) {
            if (!is_array($row)) {
                return array();
            }
            return array(
                'filename' => sanitize_text_field((string) ($row['filename'] ?? '')),
                'path' => sanitize_text_field((string) ($row['path'] ?? '')),
                'type' => sanitize_text_field((string) ($row['type'] ?? '')),
                'row_count' => intval($row['row_count'] ?? 0),
                'columns' => isset($row['columns']) && is_array($row['columns'])
                    ? array_values(array_map('sanitize_text_field', $row['columns']))
                    : array(),
                'column_count' => isset($row['columns']) && is_array($row['columns']) ? count($row['columns']) : 0,
                'full_rows_available' => !empty($row['full_rows_available']),
                'row_access_method' => sanitize_text_field((string) ($row['row_access_method'] ?? '')),
                'updated_at' => sanitize_text_field((string) ($row['updated_at'] ?? '')),
                'size_bytes' => intval($row['size_bytes'] ?? 0),
            );
        }, $files);

        $overview_rows = array(
            array(
                'metric' => 'Pages crawled',
                'value' => (string) intval($overview['pages_crawled'] ?? 0),
            ),
            array(
                'metric' => 'Audit mode',
                'value' => sanitize_text_field((string) ($state['mode'] ?? 'baseline_full')),
            ),
            array(
                'metric' => 'Site health',
                'value' => (string) intval($overview['site_health'] ?? 0) . '%',
            ),
            array(
                'metric' => 'Errors',
                'value' => (string) intval($overview['errors'] ?? 0),
            ),
            array(
                'metric' => 'Warnings',
                'value' => (string) intval($overview['warnings'] ?? 0),
            ),
            array(
                'metric' => 'Indexable pages',
                'value' => (string) intval($overview['indexable_pages'] ?? 0),
            ),
            array(
                'metric' => 'Non-indexable pages',
                'value' => (string) intval($overview['non_indexable_pages'] ?? 0),
            ),
            array(
                'metric' => 'Pages over depth 3',
                'value' => (string) intval($overview['deep_pages_over_3'] ?? 0),
            ),
            array(
                'metric' => 'Pages with <=1 inlink',
                'value' => (string) intval($overview['single_inlink_pages'] ?? 0),
            ),
        );

        $export_rows = array();
        $exports = !empty($statistics['exports']) && is_array($statistics['exports']) ? $statistics['exports'] : array();
        foreach ($exports as $name => $count) {
            $label = ucwords(str_replace(array('_', '-'), ' ', sanitize_text_field((string) $name)));
            $export_rows[] = array(
                'export' => $label,
                'rows' => (string) intval($count),
            );
        }
        $export_registry = !empty($audit['export_registry']) && is_array($audit['export_registry'])
            ? array_values($audit['export_registry'])
            : array();
        $export_registry_rows = array();
        foreach ($export_registry as $registry_row) {
            if (!is_array($registry_row)) {
                continue;
            }
            $columns = isset($registry_row['column_list']) && is_array($registry_row['column_list'])
                ? array_values(array_map('sanitize_text_field', $registry_row['column_list']))
                : array();
            $export_registry_rows[] = array(
                'audit_run_id' => sanitize_text_field((string) ($registry_row['audit_run_id'] ?? ($state['run_id'] ?? ''))),
                'export_key' => sanitize_key((string) ($registry_row['export_key'] ?? '')),
                'source_name' => sanitize_text_field((string) ($registry_row['source_name'] ?? '')),
                'filename' => sanitize_text_field((string) ($registry_row['filename'] ?? '')),
                'file_path' => sanitize_text_field((string) ($registry_row['file_path'] ?? '')),
                'row_count' => intval($registry_row['row_count'] ?? 0),
                'column_list' => $columns,
                'column_count' => intval($registry_row['column_count'] ?? count($columns)),
                'file_type' => sanitize_text_field((string) ($registry_row['file_type'] ?? '')),
                'extracted_status' => sanitize_key((string) ($registry_row['extracted_status'] ?? '')),
                'normalized_status' => sanitize_key((string) ($registry_row['normalized_status'] ?? '')),
                'usage_status' => sanitize_key((string) ($registry_row['usage_status'] ?? 'ignored')),
                'full_rows_available' => !empty($registry_row['full_rows_available']),
                'row_access_method' => sanitize_text_field((string) ($registry_row['row_access_method'] ?? '')),
                'size_bytes' => intval($registry_row['size_bytes'] ?? 0),
                'updated_at' => sanitize_text_field((string) ($registry_row['updated_at'] ?? '')),
            );
        }

        $action_bundles = $this->aysa_adjustments_build_technical_site_audit_action_bundles(
            $project_id,
            $state,
            $issue_rows,
            $file_rows
        );

        foreach ($issue_rows as $issue_index => $issue_row) {
            if (!is_array($issue_row)) {
                continue;
            }
            $issue_rows[$issue_index]['recommended_actions'] = $this->aysa_adjustments_match_site_audit_issue_actions(
                $issue_row,
                $action_bundles
            );
        }

        $page_issue_map = $this->aysa_adjustments_build_site_audit_page_issue_map($issue_rows);
        foreach ($page_rows as $page_index => $page_row) {
            if (!is_array($page_row)) {
                continue;
            }
            $page_url = esc_url_raw((string) ($page_row['url'] ?? ''));
            $page_issue_state = isset($page_issue_map[$page_url]) && is_array($page_issue_map[$page_url]) ? $page_issue_map[$page_url] : array();
            $page_rows[$page_index]['issues_count'] = intval($page_issue_state['issues_count'] ?? 0);
            $page_rows[$page_index]['issues_label'] = sanitize_text_field((string) ($page_issue_state['issues_label'] ?? '0 issues'));
            $page_rows[$page_index]['issue_priority'] = sanitize_text_field((string) ($page_issue_state['issue_priority'] ?? ''));
            $page_rows[$page_index]['top_issue_title'] = sanitize_text_field((string) ($page_issue_state['top_issue_title'] ?? ''));
            $page_rows[$page_index]['page_type'] = $this->aysa_adjustments_infer_site_audit_page_type($page_url);
        }

        $has_data = $valid_crawl && (!empty($audit) || !empty($file_rows));
        $conclusion = $this->aysa_adjustments_build_technical_site_audit_conclusion(
            $project_id,
            $state,
            $overview,
            $issue_rows,
            $page_rows,
            $file_rows,
            $statistics,
            $history_count,
            $action_bundles
        );

        return array(
            'id' => 'technical_site_audit',
            'status' => $snapshot_status,
            'valid_crawl' => $valid_crawl,
            'invalid_reason' => $valid_crawl ? '' : 'empty_or_missing_crawl_artifacts',
            'job_id' => sanitize_text_field((string) ($state['job_id'] ?? '')),
            'version_id' => intval($state['version_id'] ?? 0),
            'version_number' => intval($state['version_number'] ?? 0),
            'history_count' => $history_count,
            'target_url' => esc_url_raw((string) ($state['target_url'] ?? '')),
            'updated_at' => sanitize_text_field((string) ($state['updated_at'] ?? '')),
            'mode' => sanitize_text_field((string) ($state['mode'] ?? 'baseline_full')),
            'provider' => sanitize_text_field((string) ($state['provider'] ?? 'screamingfrog')),
            'pages_crawled' => $pages_crawled,
            'artifact_count' => count($file_rows),
            'csv_artifact_count' => $csv_file_count,
            'site_health' => intval($overview['site_health'] ?? 0),
            'errors' => intval($overview['errors'] ?? 0),
            'warnings' => intval($overview['warnings'] ?? 0),
            'count' => count($issue_rows),
            'overview_rows' => $overview_rows,
            'conclusion' => $conclusion,
            'action_bundles' => $action_bundles,
            'rows' => $issue_rows,
            'issue_groups' => $this->aysa_adjustments_build_site_audit_issue_groups($issue_rows),
            'severity_summary' => $this->aysa_adjustments_build_site_audit_severity_summary($issue_rows),
            'category_summary' => $this->aysa_adjustments_build_site_audit_category_summary($issue_rows),
            'pages' => $page_rows,
            'page_issue_map' => $page_issue_map,
            'files' => $file_rows,
            'exports' => $export_rows,
            'export_registry' => $export_registry_rows,
            'export_registry_total' => count($export_registry_rows),
            'export_rows_total' => array_sum(array_map(function($row) {
                return is_array($row) ? intval($row['row_count'] ?? 0) : 0;
            }, $export_registry_rows)),
            'has_data' => $has_data,
        );
    }

    private function aysa_adjustments_normalize_technical_site_audit_issue($row, $overview = array(), $issue_lifecycle = array()) {
        if (!is_array($row)) {
            return array();
        }

        $details = !empty($row['details']) && is_array($row['details']) ? $row['details'] : array();
        $sample_rows = !empty($details['sample_rows']) && is_array($details['sample_rows']) ? array_values($details['sample_rows']) : array();
        $sample_columns = !empty($details['sample_columns']) && is_array($details['sample_columns']) ? array_values($details['sample_columns']) : array();
        $examples = array();
        foreach (array_slice($sample_rows, 0, 5) as $sample_row) {
            if (!is_array($sample_row)) {
                continue;
            }
            $url = esc_url_raw((string) ($sample_row['url'] ?? $sample_row['Address'] ?? $sample_row['"Address"'] ?? $sample_row['﻿"Address"'] ?? ''));
            $title = sanitize_text_field((string) ($sample_row['title'] ?? $sample_row['Title'] ?? ''));
            if ($url === '' && $title === '') {
                continue;
            }
            $examples[] = array(
                'url' => $url,
                'title' => $title,
                'page_type' => $this->aysa_adjustments_infer_site_audit_page_type($url),
            );
        }

        $severity_raw = sanitize_text_field((string) ($row['severity'] ?? 'notice'));
        $title_raw = sanitize_text_field((string) ($row['title'] ?? $row['issue'] ?? ''));
        $description = sanitize_text_field((string) ($row['description'] ?? ''));
        $key = sanitize_key((string) ($row['key'] ?? ''));
        $affected_page_rows = !empty($row['affected_pages']) && is_array($row['affected_pages']) ? array_values($row['affected_pages']) : array();
        $affected_pages = intval($row['affected_pages_count'] ?? 0);
        if ($affected_pages <= 0 && empty($affected_page_rows)) {
            $affected_pages = intval($row['affected_pages'] ?? 0);
        } elseif ($affected_pages <= 0) {
            $affected_pages = count($affected_page_rows);
        }
        $category = $this->aysa_adjustments_detect_site_audit_issue_category($title_raw, $description, $key, $sample_columns);
        $priority = $this->aysa_adjustments_score_site_audit_issue_priority($category, $severity_raw, $title_raw, $description, $affected_pages, $examples, $overview);
        $page_type_hint = $this->aysa_adjustments_build_site_audit_issue_page_type_hint($examples);
        $issue_id = sanitize_key((string) ($row['issue_id'] ?? ''));
        if ($issue_id === '') {
            $issue_id = $this->aysa_technical_site_audit_issue_id($key, $title_raw, $category);
        }
        $lifecycle_row = isset($issue_lifecycle[$issue_id]) && is_array($issue_lifecycle[$issue_id]) ? $issue_lifecycle[$issue_id] : array();
        $lifecycle_state = sanitize_key((string) ($lifecycle_row['state'] ?? 'open'));
        if (!in_array($lifecycle_state, array('open', 'accepted', 'ignored', 'resolved', 'verified'), true)) {
            $lifecycle_state = 'open';
        }

        return array(
            'issue_id' => $issue_id,
            'audit_run_id' => sanitize_text_field((string) ($row['audit_run_id'] ?? '')),
            'project_id' => sanitize_text_field((string) ($row['project_id'] ?? '')),
            'source' => sanitize_text_field((string) ($row['source'] ?? 'screamingfrog')),
            'source_export' => sanitize_key((string) ($row['source_export'] ?? '')),
            'source_file' => sanitize_text_field((string) ($row['source_file'] ?? ($details['filename'] ?? ''))),
            'issue_type' => sanitize_key((string) ($row['issue_type'] ?? $key)),
            'lifecycle_state' => $lifecycle_state,
            'lifecycle_updated_at' => sanitize_text_field((string) ($lifecycle_row['updated_at'] ?? '')),
            'severity' => strtolower($severity_raw),
            'sf_severity' => sanitize_text_field((string) ($row['sf_severity'] ?? '')),
            'priority_score' => intval($row['priority_score'] ?? 0),
            'severity_raw' => $severity_raw,
            'priority_label' => $priority['label'],
            'priority_rank' => intval($priority['rank'] ?? 4),
            'title' => $title_raw,
            'title_plain' => $this->aysa_adjustments_build_site_audit_issue_title_plain($key, $category, $title_raw, $description, $examples),
            'impact_plain' => $this->aysa_adjustments_build_site_audit_issue_impact_plain($key, $category, $title_raw, $description, $examples),
            'first_action_plain' => $this->aysa_adjustments_build_site_audit_issue_action_plain($key, $category, $title_raw, $description, $examples),
            'affected_pages' => $affected_pages,
            'affected_pages_count' => $affected_pages,
            'affected_page_rows' => $this->aysa_adjustments_sanitize_site_audit_affected_rows($affected_page_rows),
            'affected_display' => $affected_pages > 0
                ? trim($affected_pages . ($page_type_hint !== '' ? ' pages · ' . $page_type_hint : ' pages'))
                : '0 pages',
            'category' => $category,
            'description' => $description,
            'root_cause' => sanitize_text_field((string) ($row['root_cause'] ?? '')),
            'explanation_plain' => sanitize_text_field((string) ($row['explanation_plain'] ?? '')),
            'explanation_technical' => sanitize_text_field((string) ($row['explanation_technical'] ?? '')),
            'impact' => sanitize_text_field((string) ($row['impact'] ?? '')),
            'recommended_action' => sanitize_text_field((string) ($row['recommended_action'] ?? '')),
            'action_type' => sanitize_key((string) ($row['action_type'] ?? '')),
            'auto_fix_possible' => !empty($row['auto_fix_possible']),
            'worker_action_key' => sanitize_key((string) ($row['worker_action_key'] ?? '')),
            'execution_target' => sanitize_key((string) ($row['execution_target'] ?? '')),
            'requires_approval' => !empty($row['requires_approval']),
            'verification_method' => sanitize_key((string) ($row['verification_method'] ?? '')),
            'key' => $key,
            'examples' => $examples,
            'details' => array(
                'filename' => sanitize_text_field((string) ($details['filename'] ?? '')),
                'sample_columns' => $sample_columns,
                'sample_rows' => $sample_rows,
                'full_rows_available' => !empty($details['full_rows_available']),
                'affected_rows_preserved' => intval($details['affected_rows_preserved'] ?? 0),
                'full_rows_count' => intval($details['full_rows_count'] ?? 0),
            ),
        );
    }

    private function aysa_adjustments_sanitize_site_audit_affected_rows($rows) {
        $rows = is_array($rows) ? array_values($rows) : array();
        $out = array();
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $evidence = !empty($row['evidence']) && is_array($row['evidence']) ? $row['evidence'] : array();
            $safe_evidence = array();
            foreach ($evidence as $key => $value) {
                $safe_evidence[sanitize_text_field((string) $key)] = sanitize_text_field((string) $value);
            }
            $out[] = array(
                'url' => esc_url_raw((string) ($row['url'] ?? '')),
                'page_title' => sanitize_text_field((string) ($row['page_title'] ?? '')),
                'status_code' => isset($row['status_code']) ? intval($row['status_code']) : null,
                'indexability' => sanitize_text_field((string) ($row['indexability'] ?? '')),
                'canonical_url' => esc_url_raw((string) ($row['canonical_url'] ?? '')),
                'crawl_depth' => sanitize_text_field((string) ($row['crawl_depth'] ?? '')),
                'inlinks' => sanitize_text_field((string) ($row['inlinks'] ?? '')),
                'outlinks' => sanitize_text_field((string) ($row['outlinks'] ?? '')),
                'evidence' => $safe_evidence,
            );
        }
        return $out;
    }

    private function aysa_adjustments_detect_site_audit_issue_category($title, $description, $key, $sample_columns = array()) {
        $haystack = strtolower(trim($title . ' ' . $description . ' ' . $key . ' ' . implode(' ', array_map('strval', $sample_columns))));

        if (strpos($haystack, '5xx') !== false || strpos($haystack, 'server error') !== false) {
            return 'server';
        }
        if (strpos($haystack, 'response code') !== false || strpos($haystack, 'http response') !== false || strpos($haystack, '4xx') !== false || strpos($haystack, 'client error') !== false || strpos($haystack, 'broken') !== false) {
            return 'response_codes';
        }
        if (strpos($haystack, 'meta description') !== false || strpos($haystack, 'page title') !== false || strpos($haystack, 'h1') !== false || strpos($haystack, 'h2') !== false) {
            return 'metadata';
        }
        if (strpos($haystack, 'canonical') !== false) {
            return 'canonical';
        }
        if (strpos($haystack, 'directive') !== false || strpos($haystack, 'noindex') !== false || strpos($haystack, 'non-indexable') !== false || strpos($haystack, 'indexable') !== false || strpos($haystack, 'robots') !== false) {
            return 'indexability';
        }
        if (strpos($haystack, 'inlink') !== false || strpos($haystack, 'internal link') !== false || strpos($haystack, 'orphan') !== false || strpos($haystack, 'depth') !== false || strpos($haystack, 'links') !== false) {
            return 'internal_linking';
        }
        if (strpos($haystack, 'image') !== false || strpos($haystack, 'alt') !== false) {
            return 'media';
        }
        if (strpos($haystack, 'structured data') !== false || strpos($haystack, 'javascript') !== false) {
            return 'technical';
        }

        return 'technical';
    }

    private function aysa_adjustments_infer_site_audit_page_type($url) {
        $url = trim((string) $url);
        if ($url === '') {
            return '';
        }
        $path = (string) parse_url($url, PHP_URL_PATH);
        if ($path === '' || $path === '/') {
            return 'homepage';
        }
        if (preg_match('/\.(css|js|png|jpe?g|gif|svg|webp|pdf|xml)$/i', $path)) {
            return 'asset';
        }
        if (preg_match('#/(blog|articol|articole|news|noutati)/#i', $path)) {
            return 'content';
        }
        if (preg_match('#/(produs|product|shop|categoria|categorie|category|buchete|aranjamente|flori)/#i', $path)) {
            return 'commercial';
        }
        $segments = array_values(array_filter(explode('/', trim($path, '/'))));
        if (count($segments) <= 1) {
            return 'static';
        }
        return 'page';
    }

    private function aysa_adjustments_build_site_audit_issue_page_type_hint($examples) {
        if (!is_array($examples) || empty($examples)) {
            return '';
        }
        $counts = array();
        foreach ($examples as $example) {
            $type = sanitize_key((string) ($example['page_type'] ?? ''));
            if ($type === '' || $type === 'page') {
                continue;
            }
            if (!isset($counts[$type])) {
                $counts[$type] = 0;
            }
            $counts[$type]++;
        }
        if (empty($counts)) {
            return '';
        }
        arsort($counts);
        $label = (string) key($counts);
        $map = array(
            'homepage' => 'homepage-heavy',
            'commercial' => 'mostly commercial URLs',
            'content' => 'mostly content URLs',
            'static' => 'mostly static URLs',
            'asset' => 'mostly technical assets',
        );
        return $map[$label] ?? ('mostly ' . $label . ' URLs');
    }

    private function aysa_adjustments_score_site_audit_issue_priority($category, $severity_raw, $title_raw, $description, $affected_pages, $examples, $overview = array()) {
        $severity = strtolower((string) $severity_raw);
        $haystack = strtolower(trim($title_raw . ' ' . $description));
        $rank = 4;

        if ($category === 'server' || strpos($haystack, '5xx') !== false) {
            $rank = 1;
        } elseif ($category === 'indexability' || $category === 'canonical') {
            $rank = 1;
        } elseif ($category === 'response_codes' && (strpos($haystack, '4xx') !== false || strpos($haystack, 'client error') !== false)) {
            $rank = 1;
        } elseif ($category === 'metadata') {
            $rank = 2;
        } elseif ($category === 'internal_linking') {
            $rank = 2;
        } elseif ($category === 'media') {
            $rank = 3;
        } elseif ($severity === 'error') {
            $rank = 2;
        } elseif ($severity === 'warning') {
            $rank = 3;
        }

        if ($this->aysa_adjustments_examples_include_commercial_urls($examples) && $rank > 1) {
            $rank--;
        }
        if ($this->aysa_adjustments_examples_are_assets_only($examples) && $rank < 4) {
            $rank++;
        }
        if ($affected_pages >= 100 && $rank > 1) {
            $rank--;
        }
        $rank = max(1, min(4, $rank));

        $labels = array(
            1 => 'Fix now',
            2 => 'High',
            3 => 'Medium',
            4 => 'Low',
        );
        return array(
            'rank' => $rank,
            'label' => $labels[$rank] ?? 'Low',
        );
    }

    private function aysa_adjustments_examples_include_commercial_urls($examples) {
        if (!is_array($examples)) {
            return false;
        }
        foreach ($examples as $example) {
            if (($example['page_type'] ?? '') === 'commercial' || ($example['page_type'] ?? '') === 'homepage') {
                return true;
            }
        }
        return false;
    }

    private function aysa_adjustments_examples_are_assets_only($examples) {
        if (!is_array($examples) || empty($examples)) {
            return false;
        }
        foreach ($examples as $example) {
            if (($example['page_type'] ?? '') !== 'asset') {
                return false;
            }
        }
        return true;
    }

    private function aysa_adjustments_site_audit_issue_copy_by_key($key) {
        $map = array(
            'client_error_inlinks' => array(
                'title' => 'Broken internal links point to 4xx URLs',
                'impact' => 'Users and crawlers hit broken destinations from internal links.',
                'action' => 'Fix the broken targets or remove the internal links pointing to them.',
            ),
            'response_codes_4xx' => array(
                'title' => 'Internal URLs return 4xx status codes',
                'impact' => 'Broken internal URLs waste crawl budget and create dead ends.',
                'action' => 'Review the broken URLs and repair the destination or the internal link source.',
            ),
            'response_codes_5xx' => array(
                'title' => 'Internal URLs return 5xx status codes',
                'impact' => 'Server failures block both Google and users from loading pages.',
                'action' => 'Fix the server or application errors before SEO cleanup.',
            ),
            'meta_description_missing' => array(
                'title' => 'Meta descriptions are missing',
                'impact' => 'Missing snippets can reduce click-through rate in search results.',
                'action' => 'Start with homepage and commercial templates, then fill the remaining important URLs.',
            ),
            'meta_description_too_long' => array(
                'title' => 'Meta descriptions are too long',
                'impact' => 'Long snippets can truncate and make search previews less useful.',
                'action' => 'Shorten the template pattern first, then clean up key pages.',
            ),
            'page_title_too_long' => array(
                'title' => 'Page titles are too long',
                'impact' => 'Overlong titles truncate in Google and usually signal template bloat.',
                'action' => 'Reduce the title pattern in shared templates before editing URLs one by one.',
            ),
            'page_title_duplicates' => array(
                'title' => 'Page titles are duplicated',
                'impact' => 'Duplicate titles weaken uniqueness and relevance signals across URLs.',
                'action' => 'Find the repeated title pattern and make titles unique for key pages.',
            ),
            'h1_missing' => array(
                'title' => 'Pages are missing an H1 heading',
                'impact' => 'Missing H1 headings weaken topical clarity for search engines and users.',
                'action' => 'Add one clear H1 on the affected page templates or archives.',
            ),
            'h1_multiple' => array(
                'title' => 'Pages have multiple H1 headings',
                'impact' => 'Multiple H1s usually point to template duplication or conflicting markup.',
                'action' => 'Keep a single primary H1 per page and remove duplicate heading output.',
            ),
            'h1_duplicate' => array(
                'title' => 'Pages repeat the same H1 twice',
                'impact' => 'Duplicate H1 output is a template-level markup problem.',
                'action' => 'Inspect the template and remove the repeated H1 block.',
            ),
            'h2_multiple' => array(
                'title' => 'Pages have many H2 headings',
                'impact' => 'Dense H2 output can indicate noisy templates or weak content structure.',
                'action' => 'Review the affected templates and keep only meaningful section headings.',
            ),
            'h2_too_long' => array(
                'title' => 'H2 headings are too long',
                'impact' => 'Very long H2 headings make content structure harder to scan.',
                'action' => 'Shorten repeated H2 patterns and clean up verbose section headings.',
            ),
            'canonicalised_inlinks' => array(
                'title' => 'Internal links point to non-canonical URLs',
                'impact' => 'Internal links reinforce URLs that are not the preferred canonical destination.',
                'action' => 'Update internal links to point directly to the canonical URLs.',
            ),
            'canonical_chains' => array(
                'title' => 'Canonical chains were found',
                'impact' => 'Canonical chains can confuse search engines about the preferred URL.',
                'action' => 'Flatten the chain so every page points directly to the final canonical URL.',
            ),
            'canonicals_missing' => array(
                'title' => 'Canonical tags are missing on indexable pages',
                'impact' => 'Missing canonicals make preferred URL selection less explicit.',
                'action' => 'Add self-referencing canonicals to the affected important pages.',
            ),
            'noindex_inlinks' => array(
                'title' => 'Noindex URLs still receive internal links',
                'impact' => 'Internal links keep pushing crawlers toward pages that should not index.',
                'action' => 'Remove or reduce links to noindex pages unless there is a product reason to keep them.',
            ),
            'directives_noindex' => array(
                'title' => 'URLs carry noindex directives',
                'impact' => 'Noindex rules can hide pages from Google if applied too broadly.',
                'action' => 'Verify whether each noindex URL is intentionally excluded from search.',
            ),
            'missing_inlinks_rows' => array(
                'title' => 'Many URLs have weak internal linking',
                'impact' => 'Weak inlinks make important pages harder to discover and support with authority.',
                'action' => 'Prioritize key commercial and static pages for stronger navigation and contextual links.',
            ),
            'orphan_urls' => array(
                'title' => 'Potential orphan URLs were found',
                'impact' => 'Orphan URLs are harder for users and crawlers to discover naturally.',
                'action' => 'Reconnect the valid orphan pages into categories, menus, or contextual links.',
            ),
            'redirect_chains_found' => array(
                'title' => 'Redirect chains were found',
                'impact' => 'Redirect chains slow down crawling and dilute signals across hops.',
                'action' => 'Replace chained redirects with one direct redirect to the final URL.',
            ),
            'images_over_size' => array(
                'title' => 'Images are too heavy',
                'impact' => 'Large images can slow important pages and affect UX.',
                'action' => 'Compress or resize the heavy images first on important pages.',
            ),
            'images_missing_alt' => array(
                'title' => 'Images are missing alt text',
                'impact' => 'Missing alt text weakens accessibility and image-search relevance.',
                'action' => 'Add descriptive alt text to the affected important images.',
            ),
        );
        $key = sanitize_key((string) $key);
        return isset($map[$key]) && is_array($map[$key]) ? $map[$key] : array();
    }

    private function aysa_adjustments_build_site_audit_issue_title_plain($key, $category, $title_raw, $description, $examples) {
        $copy = $this->aysa_adjustments_site_audit_issue_copy_by_key($key);
        if (!empty($copy['title'])) {
            return sanitize_text_field((string) $copy['title']);
        }
        $haystack = strtolower(trim($title_raw . ' ' . $description));

        if ($category === 'server' || strpos($haystack, '5xx') !== false) {
            return 'Server errors are blocking important pages';
        }
        if ($category === 'response_codes' && (strpos($haystack, 'response code') !== false || strpos($haystack, 'http response') !== false) && strpos($haystack, '4xx') === false) {
            return 'Response-code dataset needs review across crawled URLs';
        }
        if ($category === 'response_codes' && (strpos($haystack, '4xx') !== false || strpos($haystack, 'client error') !== false)) {
            return 'Broken internal URLs are sending users and crawlers to 4xx pages';
        }
        if ($category === 'metadata' && strpos($haystack, 'meta description') !== false) {
            return 'Meta description dataset needs review across crawled URLs';
        }
        if ($category === 'metadata' && strpos($haystack, 'title') !== false) {
            return 'Title tag dataset needs review across crawled URLs';
        }
        if ($category === 'metadata' && (strpos($haystack, 'h1') !== false || strpos($haystack, 'h2') !== false)) {
            return 'Heading dataset needs review across crawled URLs';
        }
        if ($category === 'canonical') {
            return 'Canonical dataset needs review across crawled URLs';
        }
        if ($category === 'indexability') {
            return 'Indexability dataset needs review across crawled URLs';
        }
        if ($category === 'internal_linking' && strpos($haystack, 'depth') !== false) {
            return 'Important pages are buried too deep in the site structure';
        }
        if ($category === 'internal_linking') {
            return 'Internal linking is too weak for some pages';
        }
        if ($category === 'media') {
            return 'Image optimization signals are incomplete';
        }

        return $title_raw !== '' ? $title_raw : 'Technical issue requires review';
    }

    private function aysa_adjustments_build_site_audit_issue_impact_plain($key, $category, $title_raw, $description, $examples) {
        $copy = $this->aysa_adjustments_site_audit_issue_copy_by_key($key);
        if (!empty($copy['impact'])) {
            return sanitize_text_field((string) $copy['impact']);
        }
        $haystack = strtolower(trim($title_raw . ' ' . $description));

        if ($category === 'server') {
            return 'Google and users may fail to load these pages, which can suppress visibility and conversions immediately.';
        }
        if ($category === 'response_codes' && (strpos($haystack, 'response code') !== false || strpos($haystack, 'http response') !== false) && strpos($haystack, '4xx') === false) {
            return 'This export family needs review because redirects, client errors, or header-level response issues can waste crawl budget and weaken site quality signals.';
        }
        if ($category === 'response_codes' && (strpos($haystack, '4xx') !== false || strpos($haystack, 'client error') !== false)) {
            return 'Broken internal targets waste crawl budget and create dead ends for users.';
        }
        if ($category === 'metadata' && strpos($haystack, 'meta description') !== false) {
            return 'The saved export needs review because weak or missing snippets can reduce click-through rate from Google even when pages rank.';
        }
        if ($category === 'metadata') {
            return 'This export family needs review because shared title, heading, or snippet patterns may be repeated across many URLs.';
        }
        if ($category === 'canonical' || $category === 'indexability') {
            return 'This export family needs review because search engines may crawl the wrong URLs or ignore pages that should be ranking.';
        }
        if ($category === 'internal_linking') {
            return 'Weak internal links make key pages harder to discover and harder to support with authority.';
        }
        if ($category === 'media') {
            return 'Missing image signals reduce accessibility and can limit image search visibility.';
        }

        return 'This can limit crawl quality or search visibility.';
    }

    private function aysa_adjustments_build_site_audit_issue_action_plain($key, $category, $title_raw, $description, $examples) {
        $copy = $this->aysa_adjustments_site_audit_issue_copy_by_key($key);
        if (!empty($copy['action'])) {
            return sanitize_text_field((string) $copy['action']);
        }
        $haystack = strtolower(trim($title_raw . ' ' . $description));

        if ($category === 'server') {
            return 'Check the affected URLs first and fix the server/application failures before any SEO cleanup.';
        }
        if ($category === 'response_codes' && (strpos($haystack, 'response code') !== false || strpos($haystack, 'http response') !== false) && strpos($haystack, '4xx') === false) {
            return 'Review the affected URLs for redirects, unexpected response codes, and template-generated technical noise.';
        }
        if ($category === 'response_codes' && (strpos($haystack, '4xx') !== false || strpos($haystack, 'client error') !== false)) {
            return 'Repair the broken destinations or remove the internal links that point to them.';
        }
        if ($category === 'metadata' && strpos($haystack, 'meta description') !== false) {
            return 'Open the saved URLs first, confirm the missing or weak snippets, then fix the shared meta template for the highest-value pages.';
        }
        if ($category === 'metadata') {
            return 'Review the saved URLs and template pattern first, then decide what should be fixed at template level versus page level.';
        }
        if ($category === 'canonical' || $category === 'indexability') {
            return 'Audit the canonical and indexing rules on the affected templates before content work.';
        }
        if ($category === 'internal_linking' && strpos($haystack, 'depth') !== false) {
            return 'Reduce click depth for important pages through navigation, category links, and contextual links.';
        }
        if ($category === 'internal_linking') {
            return 'Add stronger internal links from high-authority pages to the affected URLs.';
        }
        if ($category === 'media') {
            return 'Update image templates and review the affected image URLs first.';
        }

        return 'Open details and review the affected URLs first.';
    }

    private function aysa_adjustments_build_site_audit_issue_groups($issue_rows) {
        $issue_rows = is_array($issue_rows) ? array_values($issue_rows) : array();
        $groups = array(
            'error' => array('severity' => 'error', 'label' => 'Errors', 'issues' => array()),
            'warning' => array('severity' => 'warning', 'label' => 'Warnings', 'issues' => array()),
            'notice' => array('severity' => 'notice', 'label' => 'Notices', 'issues' => array()),
        );

        foreach ($issue_rows as $issue_row) {
            if (!is_array($issue_row)) {
                continue;
            }
            $severity = sanitize_key((string) ($issue_row['severity'] ?? 'notice'));
            if (!isset($groups[$severity])) {
                $severity = 'notice';
            }
            $groups[$severity]['issues'][] = $issue_row;
        }

        $out = array();
        foreach (array('error', 'warning', 'notice') as $severity_key) {
            $group = $groups[$severity_key];
            $group['count'] = count($group['issues']);
            $out[] = $group;
        }

        return $out;
    }

    private function aysa_adjustments_build_site_audit_severity_summary($issue_rows) {
        $issue_rows = is_array($issue_rows) ? array_values($issue_rows) : array();
        $summary = array(
            'all' => 0,
            'error' => 0,
            'warning' => 0,
            'notice' => 0,
        );
        foreach ($issue_rows as $issue_row) {
            if (!is_array($issue_row)) {
                continue;
            }
            $summary['all']++;
            $severity = sanitize_key((string) ($issue_row['severity'] ?? 'notice'));
            if (!isset($summary[$severity])) {
                $severity = 'notice';
            }
            $summary[$severity]++;
        }
        return $summary;
    }

    private function aysa_adjustments_build_site_audit_category_summary($issue_rows) {
        $issue_rows = is_array($issue_rows) ? array_values($issue_rows) : array();
        $label_map = array(
            'response_codes' => 'Crawlability',
            'server' => 'Crawlability',
            'canonical' => 'Canonicalization',
            'indexability' => 'Indexability',
            'metadata' => 'Meta Tags',
            'internal_linking' => 'Internal Linking',
            'media' => 'Images',
            'technical' => 'Technical',
        );

        $counts = array();
        foreach ($issue_rows as $issue_row) {
            if (!is_array($issue_row)) {
                continue;
            }
            $category = sanitize_key((string) ($issue_row['category'] ?? 'technical'));
            if ($category === '') {
                $category = 'technical';
            }
            if (!isset($counts[$category])) {
                $counts[$category] = 0;
            }
            $counts[$category]++;
        }

        arsort($counts);
        $out = array();
        foreach ($counts as $key => $count) {
            $out[] = array(
                'key' => $key,
                'label' => sanitize_text_field((string) ($label_map[$key] ?? ucwords(str_replace('_', ' ', $key)))),
                'count' => intval($count),
            );
        }

        return $out;
    }

    private function aysa_adjustments_build_site_audit_page_issue_map($issue_rows) {
        $issue_rows = is_array($issue_rows) ? array_values($issue_rows) : array();
        $map = array();

        foreach ($issue_rows as $issue_row) {
            if (!is_array($issue_row)) {
                continue;
            }
            $priority_rank = intval($issue_row['priority_rank'] ?? 99);
            $priority_label = sanitize_text_field((string) ($issue_row['priority_label'] ?? ''));
            $title_plain = sanitize_text_field((string) ($issue_row['title_plain'] ?? $issue_row['title'] ?? ''));
            $examples = !empty($issue_row['details']['sample_rows']) && is_array($issue_row['details']['sample_rows'])
                ? array_values($issue_row['details']['sample_rows'])
                : (is_array($issue_row['examples'] ?? null) ? array_values($issue_row['examples']) : array());

            foreach ($examples as $example) {
                if (!is_array($example)) {
                    continue;
                }
                $url = esc_url_raw((string) ($example['url'] ?? $example['Address'] ?? $example['"Address"'] ?? $example['﻿"Address"'] ?? ''));
                if ($url === '') {
                    continue;
                }
                if (!isset($map[$url])) {
                    $map[$url] = array(
                        'issues_count' => 0,
                        'issue_priority' => '',
                        'top_issue_title' => '',
                        'priority_rank' => 99,
                    );
                }
                $map[$url]['issues_count']++;
                if ($priority_rank < intval($map[$url]['priority_rank'])) {
                    $map[$url]['priority_rank'] = $priority_rank;
                    $map[$url]['issue_priority'] = $priority_label;
                    $map[$url]['top_issue_title'] = $title_plain;
                }
            }
        }

        foreach ($map as $url => $row) {
            $count = intval($row['issues_count'] ?? 0);
            $row['issues_label'] = $count === 1 ? '1 issue' : ($count . ' issues');
            unset($row['priority_rank']);
            $map[$url] = $row;
        }

        return $map;
    }

    private function aysa_adjustments_classify_site_audit_issue_subtype($issue_row) {
        if (!is_array($issue_row)) {
            return '';
        }

        $category = sanitize_key((string) ($issue_row['category'] ?? 'technical'));
        $haystack = strtolower(trim(
            sanitize_text_field((string) ($issue_row['title'] ?? '')) . ' ' .
            sanitize_text_field((string) ($issue_row['title_plain'] ?? '')) . ' ' .
            sanitize_text_field((string) ($issue_row['description'] ?? ''))
        ));

        if ($category === 'metadata') {
            if (strpos($haystack, 'meta description') !== false) {
                return 'missing_or_weak_meta_descriptions';
            }
            if (strpos($haystack, 'page title') !== false || strpos($haystack, 'title') !== false) {
                return 'title_tag_issues';
            }
            if (strpos($haystack, 'h1') !== false) {
                return 'h1_issues';
            }
            if (strpos($haystack, 'h2') !== false) {
                return 'h2_issues';
            }
            return 'metadata_issues';
        }

        if ($category === 'response_codes' || $category === 'server') {
            if (strpos($haystack, '5xx') !== false || strpos($haystack, 'server error') !== false) {
                return 'server_errors';
            }
            if (strpos($haystack, '4xx') !== false || strpos($haystack, 'client error') !== false || strpos($haystack, 'broken') !== false) {
                return 'broken_internal_urls';
            }
            if (strpos($haystack, 'redirect') !== false) {
                return 'redirect_issues';
            }
            return 'response_code_issues';
        }

        if ($category === 'canonical') {
            return 'canonical_issues';
        }
        if ($category === 'indexability') {
            return 'indexability_issues';
        }
        if ($category === 'internal_linking') {
            if (strpos($haystack, 'depth') !== false) {
                return 'crawl_depth_issues';
            }
            if (strpos($haystack, 'orphan') !== false) {
                return 'orphan_or_missing_inlinks';
            }
            return 'internal_linking_issues';
        }

        return 'technical_issues';
    }

    private function aysa_adjustments_site_audit_issue_subtype_label($subtype) {
        $labels = array(
            'missing_or_weak_meta_descriptions' => 'Meta description review dataset',
            'title_tag_issues' => 'Title tag review dataset',
            'h1_issues' => 'H1 review dataset',
            'h2_issues' => 'H2 review dataset',
            'metadata_issues' => 'Metadata review dataset',
            'server_errors' => 'Server error review dataset',
            'broken_internal_urls' => 'Broken URL review dataset',
            'redirect_issues' => 'Redirect review dataset',
            'response_code_issues' => 'Response-code review dataset',
            'canonical_issues' => 'Canonical review dataset',
            'indexability_issues' => 'Indexability review dataset',
            'crawl_depth_issues' => 'Crawl-depth review dataset',
            'orphan_or_missing_inlinks' => 'Weak-link review dataset',
            'internal_linking_issues' => 'Internal-linking review dataset',
            'technical_issues' => 'Technical review dataset',
        );
        $subtype = sanitize_key((string) $subtype);
        return sanitize_text_field((string) ($labels[$subtype] ?? ucwords(str_replace('_', ' ', $subtype))));
    }

    private function aysa_adjustments_build_site_audit_bundle_breakdown($related_issues) {
        $related_issues = is_array($related_issues) ? array_values($related_issues) : array();
        $counts = array();
        foreach ($related_issues as $issue_row) {
            if (!is_array($issue_row)) {
                continue;
            }
            $subtype = $this->aysa_adjustments_classify_site_audit_issue_subtype($issue_row);
            if ($subtype === '') {
                continue;
            }
            $count = intval($issue_row['affected_pages'] ?? 0);
            if (!isset($counts[$subtype]) || $count > intval($counts[$subtype]['affected_count'] ?? 0)) {
                $counts[$subtype] = array(
                    'key' => $subtype,
                    'label' => $this->aysa_adjustments_site_audit_issue_subtype_label($subtype),
                    'affected_count' => $count,
                    'title' => sanitize_text_field((string) ($issue_row['title_plain'] ?? $issue_row['title'] ?? '')),
                );
            }
        }

        usort($counts, function($left, $right) {
            $left_count = intval($left['affected_count'] ?? 0);
            $right_count = intval($right['affected_count'] ?? 0);
            if ($left_count === $right_count) {
                return 0;
            }
            return ($left_count > $right_count) ? -1 : 1;
        });

        return array_values(array_slice($counts, 0, 4));
    }

    private function aysa_adjustments_site_audit_action_bundle_catalog() {
        return array(
            'prepare_metadata_remediation' => array(
                'action_key' => 'prepare_metadata_remediation',
                'action_label' => 'Prepare metadata remediation',
                'bundle_type' => 'metadata',
                'categories' => array('metadata'),
                'source_files' => array('page_titles_all.csv', 'meta_description_all.csv', 'h1_all.csv', 'h2_all.csv'),
                'target_scope' => 'template_and_url_review',
                'next_step' => 'Review the affected templates and prepare the remediation shortlist for key URLs first.',
                'default_reason' => 'Page title, meta description, and heading issues are weakening relevance and click-through signals.',
            ),
            'prepare_response_code_review' => array(
                'action_key' => 'prepare_response_code_review',
                'action_label' => 'Prepare response-code review',
                'bundle_type' => 'response_codes',
                'categories' => array('server', 'response_codes'),
                'source_files' => array('response_codes_all.csv', 'redirect_chains.csv', 'redirects.csv', 'internal_client_error_(4xx)_inlinks.csv', 'all_http_response_headers.csv'),
                'target_scope' => 'crawl_cleanup_and_redirect_review',
                'next_step' => 'Validate the affected URLs, redirect patterns, and broken internal targets before the next crawl.',
                'default_reason' => 'Response-code issues can waste crawl budget and send users or crawlers to broken destinations.',
            ),
            'prepare_indexability_review' => array(
                'action_key' => 'prepare_indexability_review',
                'action_label' => 'Prepare indexability review',
                'bundle_type' => 'indexability',
                'categories' => array('canonical', 'indexability'),
                'source_files' => array('canonicals_all.csv', 'directives_all.csv', 'noindex_inlinks.csv', 'canonicalised_inlinks.csv'),
                'target_scope' => 'canonical_and_indexing_rules',
                'next_step' => 'Review canonical, noindex, and directive conflicts before changing content or linking.',
                'default_reason' => 'Canonical and indexing conflicts can push Google toward the wrong URLs.',
            ),
            'prepare_internal_linking_plan' => array(
                'action_key' => 'prepare_internal_linking_plan',
                'action_label' => 'Prepare internal-linking plan',
                'bundle_type' => 'internal_linking',
                'categories' => array('internal_linking'),
                'source_files' => array('all_inlinks.csv', 'all_outlinks.csv', 'missing_inlinks.csv', 'orphan_urls_inlinks.csv'),
                'target_scope' => 'linking_and_architecture_plan',
                'next_step' => 'Prepare the internal-linking shortlist for weak pages and navigation-level fixes.',
                'default_reason' => 'Weak internal linking makes important pages harder to discover and support with authority.',
            ),
            'prepare_image_alt_text' => array(
                'action_key' => 'prepare_image_alt_text',
                'action_label' => 'Prepare image alt-text remediation',
                'bundle_type' => 'media',
                'categories' => array('media'),
                'source_files' => array('images_missing_alt_text_inlinks.csv', 'images_over_x_kb_inlinks.csv'),
                'target_scope' => 'image_alt_text_and_media_review',
                'next_step' => 'Prepare image alt text and media cleanup candidates for affected image rows.',
                'default_reason' => 'Missing alt text and heavy image rows can weaken accessibility, image relevance, and performance.',
            ),
        );
    }

    private function aysa_adjustments_site_audit_bundle_category_to_action_key($category) {
        $category = sanitize_key((string) $category);
        return match ($category) {
            'metadata' => 'prepare_metadata_remediation',
            'server', 'response_codes' => 'prepare_response_code_review',
            'canonical', 'indexability' => 'prepare_indexability_review',
            'internal_linking' => 'prepare_internal_linking_plan',
            'media' => 'prepare_image_alt_text',
            default => '',
        };
    }

    private function aysa_adjustments_get_site_audit_action_bundle_state_map($state) {
        $state = is_array($state) ? $state : array();
        $bundles = isset($state['action_bundles']) && is_array($state['action_bundles']) ? $state['action_bundles'] : array();
        $normalized = array();
        foreach ($bundles as $action_key => $row) {
            $normalized_key = sanitize_key((string) $action_key);
            if ($normalized_key === '' || !is_array($row)) {
                continue;
            }
            $normalized[$normalized_key] = array(
                'approval_state' => sanitize_key((string) ($row['approval_state'] ?? 'suggested')),
                'execution_state' => sanitize_key((string) ($row['execution_state'] ?? 'open')),
                'execution_package_id' => sanitize_text_field((string) ($row['execution_package_id'] ?? '')),
                'metadata_execution_package' => is_array($row['metadata_execution_package'] ?? null) ? $row['metadata_execution_package'] : array(),
                'canonical_execution_package' => is_array($row['canonical_execution_package'] ?? null) ? $row['canonical_execution_package'] : array(),
                'image_alt_execution_package' => is_array($row['image_alt_execution_package'] ?? null) ? $row['image_alt_execution_package'] : array(),
                'last_prepare_result' => is_array($row['last_prepare_result'] ?? null) ? $row['last_prepare_result'] : array(),
                'last_apply_result' => is_array($row['last_apply_result'] ?? null) ? $row['last_apply_result'] : array(),
                'last_verify_result' => is_array($row['last_verify_result'] ?? null) ? $row['last_verify_result'] : array(),
                'last_error' => sanitize_text_field((string) ($row['last_error'] ?? '')),
                'last_prepared_summary' => sanitize_text_field((string) ($row['last_prepared_summary'] ?? '')),
                'last_prepared_at' => sanitize_text_field((string) ($row['last_prepared_at'] ?? '')),
                'last_applied_at' => sanitize_text_field((string) ($row['last_applied_at'] ?? '')),
                'last_verified_at' => sanitize_text_field((string) ($row['last_verified_at'] ?? '')),
                'approved_at' => sanitize_text_field((string) ($row['approved_at'] ?? '')),
                'dismissed_at' => sanitize_text_field((string) ($row['dismissed_at'] ?? '')),
                'last_sent_to_agent8_at' => sanitize_text_field((string) ($row['last_sent_to_agent8_at'] ?? '')),
            );
        }
        return $normalized;
    }

    private function aysa_adjustments_build_technical_site_audit_action_bundles($project_id, $state, $issues, $files) {
        $catalog = $this->aysa_adjustments_site_audit_action_bundle_catalog();
        $persisted_state = $this->aysa_adjustments_get_site_audit_action_bundle_state_map($state);
        $issues = is_array($issues) ? array_values($issues) : array();
        $files = is_array($files) ? array_values($files) : array();
        $bundles = array();

        foreach ($catalog as $action_key => $config) {
            $categories = isset($config['categories']) && is_array($config['categories']) ? array_values($config['categories']) : array();
            $related_issues = array_values(array_filter($issues, function($issue) use ($categories) {
                if (!is_array($issue)) {
                    return false;
                }
                return in_array(sanitize_key((string) ($issue['category'] ?? '')), $categories, true);
            }));
            if (empty($related_issues)) {
                continue;
            }

            usort($related_issues, function($left, $right) {
                $left_rank = intval($left['priority_rank'] ?? 99);
                $right_rank = intval($right['priority_rank'] ?? 99);
                if ($left_rank !== $right_rank) {
                    return ($left_rank < $right_rank) ? -1 : 1;
                }
                $left_count = intval($left['affected_pages'] ?? 0);
                $right_count = intval($right['affected_pages'] ?? 0);
                if ($left_count === $right_count) {
                    return 0;
                }
                return ($left_count > $right_count) ? -1 : 1;
            });

            $top_issue = $related_issues[0];
            $issue_breakdown = $this->aysa_adjustments_build_site_audit_bundle_breakdown($related_issues);
            $preview_urls = array();
            foreach ($related_issues as $issue) {
                foreach ((array) ($issue['examples'] ?? array()) as $example) {
                    $url = esc_url_raw((string) ($example['url'] ?? ''));
                    if ($url === '' || isset($preview_urls[$url])) {
                        continue;
                    }
                    $preview_urls[$url] = $url;
                    if (count($preview_urls) >= 5) {
                        break 2;
                    }
                }
            }

            $matched_source_files = array();
            foreach ((array) ($config['source_files'] ?? array()) as $expected_filename) {
                foreach ($files as $file_row) {
                    if (!is_array($file_row)) {
                        continue;
                    }
                    if (sanitize_text_field((string) ($file_row['filename'] ?? '')) === $expected_filename) {
                        $matched_source_files[] = $expected_filename;
                        break;
                    }
                }
            }

            $persisted = isset($persisted_state[$action_key]) && is_array($persisted_state[$action_key]) ? $persisted_state[$action_key] : array();
            $approval_state = sanitize_key((string) ($persisted['approval_state'] ?? 'suggested'));
            if (!in_array($approval_state, array('suggested', 'approved', 'preparing', 'prepared', 'applying', 'applied', 'failed', 'manual_needed', 'verifying', 'verification_pending', 'verified', 'dismissed'), true)) {
                $approval_state = 'suggested';
            }

            $bundles[] = array(
                'action_key' => sanitize_key((string) ($config['action_key'] ?? $action_key)),
                'action_label' => sanitize_text_field((string) ($config['action_label'] ?? $action_key)),
                'bundle_type' => sanitize_key((string) ($config['bundle_type'] ?? 'technical')),
                'priority_label' => sanitize_text_field((string) ($top_issue['priority_label'] ?? 'Medium')),
                'priority_rank' => intval($top_issue['priority_rank'] ?? 4),
                'reason' => sanitize_text_field((string) ($top_issue['impact_plain'] ?? $config['default_reason'] ?? 'This audit family needs preparation before execution.')),
                'issue_category' => sanitize_key((string) ($top_issue['category'] ?? 'technical')),
                'issue_title' => sanitize_text_field((string) ($top_issue['title_plain'] ?? $top_issue['title'] ?? 'Technical remediation bundle')),
                'target_scope' => sanitize_key((string) ($config['target_scope'] ?? 'review')),
                'affected_count' => max(array_map(static function($issue) {
                    return intval($issue['affected_pages'] ?? 0);
                }, $related_issues)),
                'affected_urls_preview' => array_values($preview_urls),
                'source_files' => array_values($matched_source_files),
                'next_step' => sanitize_text_field((string) ($config['next_step'] ?? 'Review the affected URLs and prepare the remediation plan.')),
                'issue_breakdown' => $issue_breakdown,
                'requires_approval' => true,
                'approval_state' => $approval_state,
                'execution_state' => sanitize_key((string) ($persisted['execution_state'] ?? 'open')),
                'execution_package_id' => sanitize_text_field((string) ($persisted['execution_package_id'] ?? '')),
                'metadata_execution_package' => is_array($persisted['metadata_execution_package'] ?? null) ? $persisted['metadata_execution_package'] : array(),
                'canonical_execution_package' => is_array($persisted['canonical_execution_package'] ?? null) ? $persisted['canonical_execution_package'] : array(),
                'image_alt_execution_package' => is_array($persisted['image_alt_execution_package'] ?? null) ? $persisted['image_alt_execution_package'] : array(),
                'last_prepare_result' => is_array($persisted['last_prepare_result'] ?? null) ? $persisted['last_prepare_result'] : array(),
                'last_apply_result' => is_array($persisted['last_apply_result'] ?? null) ? $persisted['last_apply_result'] : array(),
                'last_verify_result' => is_array($persisted['last_verify_result'] ?? null) ? $persisted['last_verify_result'] : array(),
                'last_error' => sanitize_text_field((string) ($persisted['last_error'] ?? '')),
                'last_prepared_summary' => sanitize_text_field((string) ($persisted['last_prepared_summary'] ?? '')),
                'last_prepared_at' => sanitize_text_field((string) ($persisted['last_prepared_at'] ?? '')),
                'last_applied_at' => sanitize_text_field((string) ($persisted['last_applied_at'] ?? '')),
                'last_verified_at' => sanitize_text_field((string) ($persisted['last_verified_at'] ?? '')),
                'approved_at' => sanitize_text_field((string) ($persisted['approved_at'] ?? '')),
                'dismissed_at' => sanitize_text_field((string) ($persisted['dismissed_at'] ?? '')),
                'last_sent_to_agent8_at' => sanitize_text_field((string) ($persisted['last_sent_to_agent8_at'] ?? '')),
            );
        }

        usort($bundles, function($left, $right) {
            $left_rank = intval($left['priority_rank'] ?? 99);
            $right_rank = intval($right['priority_rank'] ?? 99);
            if ($left_rank !== $right_rank) {
                return ($left_rank < $right_rank) ? -1 : 1;
            }
            $left_count = intval($left['affected_count'] ?? 0);
            $right_count = intval($right['affected_count'] ?? 0);
            if ($left_count === $right_count) {
                return 0;
            }
            return ($left_count > $right_count) ? -1 : 1;
        });

        return array_values($bundles);
    }

    private function aysa_adjustments_match_site_audit_issue_actions($issue_row, $action_bundles) {
        if (!is_array($issue_row) || !is_array($action_bundles)) {
            return array();
        }
        $action_key = $this->aysa_adjustments_site_audit_bundle_category_to_action_key((string) ($issue_row['category'] ?? ''));
        if ($action_key === '') {
            return array();
        }
        foreach ($action_bundles as $bundle) {
            if (!is_array($bundle)) {
                continue;
            }
            if (sanitize_key((string) ($bundle['action_key'] ?? '')) !== $action_key) {
                continue;
            }
            return array(
                array(
                    'action_key' => sanitize_key((string) ($bundle['action_key'] ?? '')),
                    'action_label' => sanitize_text_field((string) ($bundle['action_label'] ?? '')),
                    'bundle_type' => sanitize_key((string) ($bundle['bundle_type'] ?? '')),
                    'priority_label' => sanitize_text_field((string) ($bundle['priority_label'] ?? 'Medium')),
                    'approval_state' => sanitize_key((string) ($bundle['approval_state'] ?? 'suggested')),
                    'execution_state' => sanitize_key((string) ($bundle['execution_state'] ?? 'open')),
                ),
            );
        }
        return array();
    }

    private function aysa_adjustments_is_site_audit_bundle_action_key($action_key) {
        $catalog = $this->aysa_adjustments_site_audit_action_bundle_catalog();
        return isset($catalog[sanitize_key((string) $action_key)]);
    }

    private function aysa_adjustments_update_site_audit_action_bundle_state($project_id, $action_key, $changes = array()) {
        $project_id = intval($project_id);
        $action_key = sanitize_key((string) $action_key);
        if ($project_id <= 0 || $action_key === '' || !$this->aysa_adjustments_is_site_audit_bundle_action_key($action_key)) {
            return false;
        }

        $state = get_option($this->aysa_technical_site_audit_state_key($project_id), array());
        if (!is_array($state) || empty($state)) {
            $latest_version = $this->aysa_sf_site_audit_get_latest_version_row($project_id);
            if (is_array($latest_version) && !empty($latest_version)) {
                $state = $this->aysa_sf_site_audit_build_state_from_version_row($latest_version);
            }
        }
        if (!is_array($state) || empty($state)) {
            return false;
        }

        $action_bundles = isset($state['action_bundles']) && is_array($state['action_bundles']) ? $state['action_bundles'] : array();
        $bundle_state = isset($action_bundles[$action_key]) && is_array($action_bundles[$action_key]) ? $action_bundles[$action_key] : array();

        foreach ((array) $changes as $key => $value) {
            switch ((string) $key) {
                case 'approval_state':
                    $normalized = sanitize_key((string) $value);
                    if (in_array($normalized, array('suggested', 'approved', 'preparing', 'prepared', 'applying', 'applied', 'failed', 'manual_needed', 'verifying', 'verification_pending', 'verified', 'dismissed'), true)) {
                        $bundle_state['approval_state'] = $normalized;
                    }
                    break;
                case 'execution_state':
                    $normalized = sanitize_key((string) $value);
                    if (in_array($normalized, array('open', 'accepted', 'preparing', 'prepared', 'applying', 'applied', 'failed', 'manual_needed', 'verifying', 'verification_pending', 'verified', 'ignored'), true)) {
                        $bundle_state['execution_state'] = $normalized;
                    }
                    break;
                case 'execution_package_id':
                    $bundle_state['execution_package_id'] = sanitize_text_field((string) $value);
                    break;
                case 'metadata_execution_package':
                case 'image_alt_execution_package':
                case 'canonical_execution_package':
                case 'last_prepare_result':
                case 'last_apply_result':
                case 'last_verify_result':
                    $bundle_state[(string) $key] = is_array($value) ? $value : array();
                    break;
                case 'worker_run_id':
                case 'worker_task_id':
                    $bundle_state[(string) $key] = intval($value);
                    break;
                case 'last_error':
                    $bundle_state['last_error'] = sanitize_text_field((string) $value);
                    break;
                case 'last_prepared_summary':
                    $bundle_state['last_prepared_summary'] = sanitize_text_field((string) $value);
                    break;
                case 'last_prepared_at':
                case 'last_applied_at':
                case 'last_verified_at':
                case 'approved_at':
                case 'dismissed_at':
                case 'last_sent_to_agent8_at':
                    $bundle_state[(string) $key] = sanitize_text_field((string) $value);
                    break;
            }
        }

        if (empty($bundle_state['approval_state'])) {
            $bundle_state['approval_state'] = 'suggested';
        }

        $action_bundles[$action_key] = $bundle_state;
        $state['action_bundles'] = $action_bundles;
        $state['updated_at'] = current_time('mysql');
        update_option($this->aysa_technical_site_audit_state_key($project_id), $state, false);
        $this->aysa_sf_site_audit_persist_version($project_id, $state);

        return true;
    }

    private function aysa_technical_site_audit_build_metadata_execution_package($project_id, $snapshot, $bundle, $package_id) {
        $project_id = intval($project_id);
        $snapshot = is_array($snapshot) ? $snapshot : array();
        $bundle = is_array($bundle) ? $bundle : array();
        $package_id = sanitize_text_field((string) $package_id);
        $items = array();
        $skipped = array();
        $seen = array();
        $seen_proposals = array();
        $issues = isset($snapshot['rows']) && is_array($snapshot['rows']) ? array_values($snapshot['rows']) : array();

        foreach ($issues as $issue) {
            if (!is_array($issue) || sanitize_key((string) ($issue['category'] ?? '')) !== 'metadata') {
                continue;
            }
            $issue_type = sanitize_key((string) ($issue['issue_type'] ?? $issue['key'] ?? ''));
            if ($issue_type === '' || strpos($issue_type, 'h1') !== false || strpos($issue_type, 'h2') !== false) {
                continue;
            }
            $field = '';
            if (strpos($issue_type, 'meta_description') !== false) {
                $field = 'meta_description';
            } elseif (strpos($issue_type, 'page_title') !== false || strpos($issue_type, 'title') !== false) {
                $field = 'meta_title';
            }
            if ($field === '') {
                continue;
            }

            $rows = isset($issue['affected_page_rows']) && is_array($issue['affected_page_rows']) ? array_values($issue['affected_page_rows']) : array();
            if (empty($rows) && isset($issue['examples']) && is_array($issue['examples'])) {
                $rows = array_values($issue['examples']);
            }
            foreach ($rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $url = esc_url_raw((string) ($row['url'] ?? ''));
                if ($url === '') {
                    continue;
                }
                $post_id = url_to_postid($url);
                if ($post_id <= 0) {
                    $skipped[] = array('url' => $url, 'field' => $field, 'reason' => 'no_clear_wordpress_post_mapping', 'old_value' => '', 'proposed_value' => '');
                    continue;
                }
                $post = get_post($post_id);
                if (!$post || !in_array((string) $post->post_status, array('publish', 'draft', 'private'), true)) {
                    $skipped[] = array('url' => $url, 'field' => $field, 'post_id' => $post_id, 'reason' => 'post_not_editable', 'old_value' => '', 'proposed_value' => '');
                    continue;
                }
                $item_key = $post_id . ':' . $field;
                if (isset($seen[$item_key])) {
                    continue;
                }
                $seen[$item_key] = true;
                $current = $field === 'meta_title'
                    ? $this->aysa_technical_site_audit_current_post_meta_title($post_id, $post)
                    : $this->aysa_technical_site_audit_current_post_meta_description($post_id, $post);
                if (strpos($issue_type, 'missing') !== false) {
                    $current = '';
                }
                $proposed = $field === 'meta_title'
                    ? $this->aysa_technical_site_audit_proposed_meta_title($post)
                    : $this->aysa_technical_site_audit_proposed_meta_description($post);
                $quality = $this->aysa_technical_site_audit_metadata_candidate_quality($field, $current, $proposed, $issue_type, $post);
                $decision = (string) ($quality['decision'] ?? '');
                if ($decision === 'preserve') {
                    $skipped[] = array('url' => $url, 'field' => $field, 'post_id' => $post_id, 'reason' => 'already_optimized', 'old_value' => sanitize_text_field((string) $current), 'proposed_value' => sanitize_text_field((string) $proposed), 'quality_score' => $quality['quality_score'], 'candidate_quality' => $quality);
                    continue;
                }
                if ($decision === 'manual_review') {
                    $skipped[] = array('url' => $url, 'field' => $field, 'post_id' => $post_id, 'reason' => (string) ($quality['overwrite_reason'] ?? 'manual_review_needed'), 'old_value' => sanitize_text_field((string) $current), 'proposed_value' => sanitize_text_field((string) $proposed), 'quality_score' => $quality['quality_score'], 'candidate_quality' => $quality);
                    continue;
                }
                if ($quality['classification'] === 'already_optimized') {
                    $skipped[] = array('url' => $url, 'field' => $field, 'post_id' => $post_id, 'reason' => 'already_optimized', 'old_value' => sanitize_text_field((string) $current), 'proposed_value' => sanitize_text_field((string) $proposed), 'quality_score' => $quality['quality_score'], 'candidate_quality' => $quality);
                    continue;
                }
                $validation = $this->aysa_technical_site_audit_validate_metadata_proposal($field, $proposed);
                if (!$validation['ok']) {
                    $skipped[] = array('url' => $url, 'field' => $field, 'post_id' => $post_id, 'reason' => $validation['reason'], 'old_value' => sanitize_text_field((string) $current), 'proposed_value' => sanitize_text_field((string) $proposed), 'quality_score' => $quality['quality_score'], 'candidate_quality' => $quality);
                    continue;
                }
                $proposal_key = $field . ':' . strtolower($proposed);
                if (isset($seen_proposals[$proposal_key])) {
                    $skipped[] = array('url' => $url, 'field' => $field, 'post_id' => $post_id, 'reason' => 'duplicate_proposed_value_in_batch', 'old_value' => sanitize_text_field((string) $current), 'proposed_value' => sanitize_text_field((string) $proposed));
                    continue;
                }
                $seen_proposals[$proposal_key] = true;
                $items[] = array(
                    'url' => $url,
                    'post_id' => $post_id,
                    'entity_key' => 'post:' . $post_id,
                    'post_type' => sanitize_key((string) $post->post_type),
                    'field' => $field,
                    'issue_type' => $issue_type,
                    'issue_id' => sanitize_text_field((string) ($issue['issue_id'] ?? '')),
                    'old_value' => sanitize_text_field((string) $current),
                    'proposed_value' => sanitize_text_field((string) $proposed),
                    'confidence' => intval($quality['quality_score'] ?? 70),
                    'classification' => sanitize_key((string) ($quality['classification'] ?? 'safe_auto_apply')),
                    'overwrite_decision' => sanitize_key((string) ($quality['decision'] ?? 'rewrite')),
                    'overwrite_reason' => sanitize_text_field((string) ($quality['overwrite_reason'] ?? 'metadata_weak_or_audit_flagged')),
                    'quality_flags' => isset($quality['quality_flags']) && is_array($quality['quality_flags']) ? array_values($quality['quality_flags']) : array(),
                    'inclusion_reason' => sanitize_text_field((string) ($quality['inclusion_reason'] ?? 'metadata_issue_from_site_audit')),
                    'quality_notes' => isset($quality['notes']) && is_array($quality['notes']) ? array_values($quality['notes']) : array(),
                    'status' => 'ready_for_apply',
                );
            }
        }

        return array(
            'package_id' => $package_id,
            'project_id' => $project_id,
            'action_key' => 'prepare_metadata_remediation',
            'issue_family' => 'metadata',
            'bundle_type' => sanitize_key((string) ($bundle['bundle_type'] ?? 'metadata')),
            'created_at' => current_time('mysql'),
            'items' => array_values($items),
            'candidates' => array_values($items),
            'row_results' => array(),
            'skipped' => array_values($skipped),
            'items_count' => count($items),
            'skipped_count' => count($skipped),
            'total_candidates' => count($items) + count($skipped),
            'safe_auto_apply_count' => count($items),
            'manual_review_needed_count' => count($skipped),
            'no_valid_target_count' => 0,
        );
    }

    private function aysa_technical_site_audit_build_package_adapter_context($project_id, $action_key, $payload = array()) {
        $project_id = intval($project_id);
        $action_key = sanitize_key((string) $action_key);
        $snapshot = array();
        $payload = is_array($payload) ? $payload : array();
        $payload_snapshot = isset($payload['technical_site_audit']) && is_array($payload['technical_site_audit']) ? $payload['technical_site_audit'] : array();
        if (!empty($payload_snapshot)) {
            $snapshot = $payload_snapshot;
        }
        if (empty($snapshot)) {
            $snapshot = $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id);
        }
        $normalized_snapshot = json_decode(wp_json_encode($snapshot), true);
        if (is_array($normalized_snapshot)) {
            $snapshot = $normalized_snapshot;
        }
        $bundle = $this->aysa_technical_site_audit_find_action_bundle($snapshot, $action_key);
        $context = array(
            'source' => 'wordpress_adapter_context',
            'action_key' => $action_key,
        );

        if ($action_key === 'prepare_metadata_remediation') {
            $posts = array();
            $adapter_rows = array();
            foreach ((array) ($snapshot['rows'] ?? array()) as $issue) {
                $issue = is_object($issue) ? (array) $issue : $issue;
                if (!is_array($issue)) {
                    continue;
                }
                $affected = isset($issue['affected_page_rows']) && is_array($issue['affected_page_rows'])
                    ? $issue['affected_page_rows']
                    : array();
                if (empty($affected) && isset($issue['examples']) && is_array($issue['examples'])) {
                    $affected = $issue['examples'];
                }
                foreach ($affected as $row) {
                    $row = is_object($row) ? (array) $row : $row;
                    if (!is_array($row)) {
                        continue;
                    }
                    $row['site_audit_issue_type'] = sanitize_key((string) ($issue['issue_type'] ?? $issue['key'] ?? ''));
                    $row['site_audit_issue_id'] = sanitize_text_field((string) ($issue['issue_id'] ?? ''));
                    $row['site_audit_category'] = sanitize_key((string) ($issue['category'] ?? ''));
                    $adapter_rows[] = $row;
                }
            }
            foreach ($adapter_rows as $row) {
                $url = $this->aysa_technical_site_audit_row_url($row);
                if ($url === '' || isset($posts[$url])) {
                    continue;
                }
                $post_id = url_to_postid($url);
                if ($post_id <= 0) {
                    $posts[$url] = array('post_id' => 0, 'reason' => 'no_clear_wordpress_post_mapping');
                    continue;
                }
                $post = get_post($post_id);
                if (!$post) {
                    $posts[$url] = array('post_id' => $post_id, 'reason' => 'post_not_found');
                    continue;
                }
                $primary_keyword = $this->aysa_technical_site_audit_primary_keyword($post_id);
                $posts[$url] = array(
                    'post_id' => $post_id,
                    'post_type' => sanitize_key((string) $post->post_type),
                    'post_status' => sanitize_key((string) $post->post_status),
                    'post_title' => sanitize_text_field((string) $post->post_title),
                    'post_excerpt' => sanitize_textarea_field((string) $post->post_excerpt),
                    'post_content' => sanitize_textarea_field(mb_substr(wp_strip_all_tags((string) $post->post_content), 0, 3000)),
                    'current_meta_title' => $this->aysa_technical_site_audit_current_post_meta_title($post_id, $post),
                    'current_meta_description' => $this->aysa_technical_site_audit_current_post_meta_description($post_id, $post),
                    'primary_keyword' => $primary_keyword,
                    'secondary_keywords' => $this->aysa_technical_site_audit_secondary_keywords($post_id, $primary_keyword),
                    'site_name' => sanitize_text_field((string) get_bloginfo('name')),
                    'permalink' => esc_url_raw((string) get_permalink($post)),
                );
            }
            $context['metadata_posts'] = $posts;
        } elseif ($action_key === 'prepare_response_code_review') {
            $hints = array();
            $rows = $this->aysa_technical_site_audit_collect_bundle_rows($snapshot, $bundle);
            if (empty($rows)) {
                $technical_errors = $this->aysa_adjustments_get_technical_errors_snapshot($project_id);
                $rows = isset($technical_errors['rows']) && is_array($technical_errors['rows']) ? array_values($technical_errors['rows']) : array();
            }
            $context['redirect_rows'] = array_values($rows);
            foreach ($rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $source = $this->aysa_technical_site_audit_row_url($row);
                if ($source === '' || isset($hints[$source])) {
                    continue;
                }
                $hint = array();
                $path = (string) parse_url($source, PHP_URL_PATH);
                $query = (string) parse_url($source, PHP_URL_QUERY);
                if ($query !== '' && $path !== '') {
                    $target = home_url(untrailingslashit($path) . '/');
                    if ($this->aysa_technical_site_audit_redirect_target_is_safe($source, $target) && $this->aysa_technical_site_audit_url_looks_live($target)) {
                        $hint['query_stripped_live_url'] = esc_url_raw($target);
                        $hint['query_stripped_live_url_reason'] = 'same_path_without_query_is_live';
                    }
                }
                $slug = sanitize_title(basename(untrailingslashit($path)));
                if ($slug !== '') {
                    $post_types = array_values(get_post_types(array('public' => true), 'names'));
                    $post = get_page_by_path($slug, OBJECT, $post_types);
                    if ($post instanceof WP_Post && (string) $post->post_status === 'publish') {
                        $target = get_permalink($post);
                        if ($target && $this->aysa_technical_site_audit_redirect_target_is_safe($source, $target)) {
                            $hint['exact_slug_permalink'] = esc_url_raw((string) $target);
                            $hint['exact_slug_permalink_reason'] = 'exact_slug_matches_live_wordpress_permalink';
                        }
                    }
                }
                $hints[$source] = $hint;
            }
            $context['redirect_target_hints'] = $hints;
        } elseif ($action_key === 'prepare_internal_linking_plan') {
            $context['internal_linking_fallback_rows'] = $this->aysa_technical_site_audit_discover_internal_linking_rows_from_wp($project_id);
        } elseif ($action_key === 'prepare_image_alt_text') {
            $image_items = array();
            $rows = $this->aysa_technical_site_audit_collect_bundle_rows($snapshot, $bundle);
            foreach ((array) $rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $image_url = esc_url_raw((string) ($row['image_url'] ?? $row['src'] ?? $row['address'] ?? $row['url'] ?? ''));
                if ($image_url === '' || isset($image_items[$image_url])) {
                    continue;
                }
                $attachment_id = attachment_url_to_postid($image_url);
                $page_url = esc_url_raw((string) ($row['page_url'] ?? $row['page'] ?? $row['from'] ?? $row['source'] ?? ''));
                $page_id = $page_url !== '' ? url_to_postid($page_url) : 0;
                $page_post = $page_id > 0 ? get_post($page_id) : null;
                $primary_keyword = $page_id > 0 ? $this->aysa_technical_site_audit_primary_keyword($page_id) : '';
                $language_context = $this->aysa_technical_site_audit_resolve_image_alt_language($project_id, $page_id);
                $image_items[$image_url] = array(
                    'attachment_id' => intval($attachment_id),
                    'existing_alt' => $attachment_id > 0 ? sanitize_text_field((string) get_post_meta($attachment_id, '_wp_attachment_image_alt', true)) : '',
                    'page_url' => $page_url,
                    'page_title' => $page_post instanceof WP_Post ? sanitize_text_field((string) $page_post->post_title) : '',
                    'primary_keyword' => $primary_keyword,
                    'secondary_keywords' => $page_id > 0 ? $this->aysa_technical_site_audit_secondary_keywords($page_id, $primary_keyword) : array(),
                    'image_filename' => sanitize_text_field(basename((string) parse_url($image_url, PHP_URL_PATH))),
                    'language' => sanitize_text_field((string) ($language_context['language'] ?? 'ro')),
                    'language_source' => sanitize_key((string) ($language_context['source'] ?? 'site_locale_fallback')),
                );
            }
            if (empty($image_items)) {
                $image_items = $this->aysa_technical_site_audit_discover_image_alt_items_from_wp($project_id);
            }
            $context['image_alt_items'] = $image_items;
        } elseif ($action_key === 'prepare_indexability_review') {
            $posts = array();
            $rows = $this->aysa_technical_site_audit_collect_bundle_rows($snapshot, $bundle);
            foreach ((array) $rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $url = $this->aysa_technical_site_audit_row_url($row);
                if ($url === '' || isset($posts[$url])) {
                    continue;
                }
                $post_id = url_to_postid($url);
                if ($post_id <= 0) {
                    $posts[$url] = array('post_id' => 0, 'reason' => 'no_clear_wordpress_post_mapping');
                    continue;
                }
                $post = get_post($post_id);
                if (!$post instanceof WP_Post) {
                    $posts[$url] = array('post_id' => $post_id, 'reason' => 'post_not_found');
                    continue;
                }
                $posts[$url] = array(
                    'post_id' => $post_id,
                    'post_type' => sanitize_key((string) $post->post_type),
                    'post_status' => sanitize_key((string) $post->post_status),
                    'page_title' => sanitize_text_field((string) $post->post_title),
                    'permalink' => esc_url_raw((string) get_permalink($post)),
                    'current_canonical_url' => $this->aysa_technical_site_audit_current_post_canonical_url($post_id),
                    'canonical_provider' => $this->aysa_technical_site_audit_detect_canonical_provider($post_id),
                    'gsc_signals' => array(),
                );
            }
            $has_mapped_posts = false;
            foreach ($posts as $post_context) {
                if (is_array($post_context) && intval($post_context['post_id'] ?? 0) > 0) {
                    $has_mapped_posts = true;
                    break;
                }
            }
            if (!$has_mapped_posts) {
                $posts = $this->aysa_technical_site_audit_discover_canonical_posts_from_wp($project_id);
            }
            $context['canonical_posts'] = $posts;
        }

        return $context;
    }

    private function aysa_technical_site_audit_resolve_image_alt_language($project_id, $page_id = 0) {
        $project_id = intval($project_id);
        $page_id = intval($page_id);

        if ($page_id > 0) {
            $page_language = $this->aysa_technical_site_audit_normalize_language_code((string) get_post_meta($page_id, 'aysa_language_optimize', true), '');
            if ($page_language !== '') {
                return array(
                    'language' => $page_language,
                    'source' => 'page_language_optimize',
                );
            }
        }

        if ($project_id > 0) {
            $project_settings = get_option('aysa_wizard_seo-settings_project_' . $project_id, array());
            if (is_array($project_settings)) {
                $project_language = $this->aysa_technical_site_audit_normalize_language_code((string) ($project_settings['primary_language'] ?? ($project_settings['language_optimize'] ?? '')), '');
                if ($project_language !== '') {
                    return array(
                        'language' => $project_language,
                        'source' => 'setup_seo_profile_project',
                    );
                }
            }
        }

        $candidate_user_ids = array();
        $external_user_id = intval($this->aysa_get_authenticated_external_user_id());
        if ($external_user_id > 0) {
            $candidate_user_ids[] = $external_user_id;
        }
        $shared_user_id = intval(get_option('aysa_user_id', 0));
        if ($shared_user_id > 0) {
            $candidate_user_ids[] = $shared_user_id;
        }
        $current_user_id = function_exists('get_current_user_id') ? intval(get_current_user_id()) : 0;
        if ($current_user_id > 0) {
            $candidate_user_ids[] = $current_user_id;
        }

        foreach (array_values(array_unique(array_filter($candidate_user_ids))) as $user_id) {
            $seo_settings = get_option('aysa_wizard_seo-settings_' . intval($user_id), array());
            if (!is_array($seo_settings)) {
                continue;
            }
            $profile_language = $this->aysa_technical_site_audit_normalize_language_code((string) ($seo_settings['primary_language'] ?? ($seo_settings['language_optimize'] ?? '')), '');
            if ($profile_language !== '') {
                return array(
                    'language' => $profile_language,
                    'source' => 'setup_seo_profile',
                );
            }
        }

        $global_language = $this->aysa_technical_site_audit_normalize_language_code((string) get_option('aysa_language', get_locale()), 'ro');
        return array(
            'language' => $global_language,
            'source' => 'site_locale_fallback',
        );
    }

    private function aysa_technical_site_audit_normalize_language_code($value, $default = 'ro') {
        $value = strtolower(str_replace('_', '-', trim((string) $value)));
        if ($value === '') {
            return (string) $default;
        }
        $base = explode('-', $value);
        $base = is_array($base) && isset($base[0]) ? strtolower(trim((string) $base[0])) : $value;
        $map = array(
            'ro' => 'ro',
            'romanian' => 'ro',
            'romana' => 'ro',
            'română' => 'ro',
            'romanian language' => 'ro',
            'en' => 'en',
            'english' => 'en',
            'engleza' => 'en',
            'engleză' => 'en',
            'bg' => 'bg',
            'bulgarian' => 'bg',
            'bulgara' => 'bg',
            'bulgară' => 'bg',
            'de' => 'de',
            'german' => 'de',
            'germana' => 'de',
            'germană' => 'de',
            'fr' => 'fr',
            'french' => 'fr',
            'franceza' => 'fr',
            'franceză' => 'fr',
            'es' => 'es',
            'spanish' => 'es',
            'spaniola' => 'es',
            'spaniolă' => 'es',
            'it' => 'it',
            'italian' => 'it',
            'italiana' => 'it',
            'italiană' => 'it',
        );
        if (isset($map[$value])) {
            return $map[$value];
        }
        if (isset($map[$base])) {
            return $map[$base];
        }
        if (preg_match('/^[a-z]{2}$/', $base)) {
            return $base;
        }
        return (string) $default;
    }

    private function aysa_technical_site_audit_collect_metadata_adapter_rows($snapshot) {
        $snapshot = is_array($snapshot) ? $snapshot : array();
        $rows = array();
        foreach ((array) ($snapshot['rows'] ?? array()) as $issue) {
            if (is_object($issue)) {
                $issue = (array) $issue;
            }
            if (!is_array($issue)) {
                continue;
            }
            $issue_type = sanitize_key((string) ($issue['issue_type'] ?? $issue['key'] ?? ''));
            $affected = isset($issue['affected_page_rows']) && is_array($issue['affected_page_rows'])
                ? $issue['affected_page_rows']
                : array();
            if (empty($affected) && isset($issue['examples']) && is_array($issue['examples'])) {
                $affected = $issue['examples'];
            }
            foreach ($affected as $row) {
                if (is_object($row)) {
                    $row = (array) $row;
                }
                if (!is_array($row)) {
                    continue;
                }
                $row['site_audit_issue_type'] = $issue_type;
                $row['site_audit_issue_id'] = sanitize_text_field((string) ($issue['issue_id'] ?? ''));
                $row['site_audit_category'] = 'metadata';
                $rows[] = $row;
            }
        }
        return $rows;
    }

    private function aysa_technical_site_audit_build_redirect_execution_package($project_id, $snapshot, $bundle, $package_id) {
        $project_id = intval($project_id);
        $snapshot = is_array($snapshot) ? $snapshot : array();
        $bundle = is_array($bundle) ? $bundle : array();
        $package_id = sanitize_text_field((string) $package_id);
        $rows = $this->aysa_technical_site_audit_collect_bundle_rows($snapshot, $bundle);
        if (empty($rows)) {
            $technical_errors = $this->aysa_adjustments_get_technical_errors_snapshot($project_id);
            $rows = isset($technical_errors['rows']) && is_array($technical_errors['rows']) ? array_values($technical_errors['rows']) : array();
        }

        $candidates = array();
        $seen = array();
        $safe = 0;
        $manual = 0;
        $no_target = 0;
        foreach ((array) $rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $candidate = $this->aysa_technical_site_audit_build_redirect_candidate($row);
            $source = sanitize_text_field((string) ($candidate['source_url'] ?? ''));
            if ($source === '' || isset($seen[$source])) {
                continue;
            }
            $seen[$source] = true;
            if (($candidate['classification'] ?? '') === 'safe_auto_apply') {
                $safe++;
            } elseif (($candidate['classification'] ?? '') === 'manual_review_needed') {
                $manual++;
            } else {
                $no_target++;
            }
            $candidates[] = $candidate;
        }

        return array(
            'package_id' => $package_id,
            'project_id' => $project_id,
            'action_key' => 'prepare_response_code_review',
            'issue_family' => 'response_codes_redirects',
            'created_at' => current_time('mysql'),
            'source' => empty($rows) ? 'site_audit_empty' : 'site_audit_or_technical_redirect_rows',
            'candidates' => array_values($candidates),
            'row_results' => array(),
            'total_candidates' => count($candidates),
            'safe_auto_apply_count' => $safe,
            'manual_review_needed_count' => $manual,
            'no_valid_target_count' => $no_target,
        );
    }

    private function aysa_technical_site_audit_build_redirect_candidate($row) {
        $row = is_array($row) ? $row : array();
        $source = $this->aysa_technical_site_audit_row_url($row);
        if ($source === '') {
            $source = esc_url_raw((string) ($row['source_url'] ?? ''));
        }
        $status_code = 0;
        foreach (array('status_code', 'response_code', 'status', 'http_status') as $key) {
            $status_code = intval($row[$key] ?? 0);
            if ($status_code > 0) {
                break;
            }
        }
        $issue_type = sanitize_key((string) ($row['site_audit_issue_type'] ?? $row['issue_type'] ?? $row['key'] ?? $row['suggested_action'] ?? 'response_code_review'));
        if (!$this->aysa_technical_site_audit_redirect_source_is_safe($source)) {
            return array(
                'source_url' => esc_url_raw($source),
                'detected_issue_type' => $issue_type,
                'current_status_code' => $status_code,
                'proposed_target_url' => '',
                'target_resolution_method' => 'blocked_protected_source',
                'confidence' => 0,
                'classification' => 'no_valid_target',
                'safe_to_apply' => false,
                'manual_review_required' => true,
                'reason' => 'protected_wordpress_or_system_path_not_safe_for_redirect_auto_apply',
            );
        }
        $resolution = $this->aysa_technical_site_audit_resolve_redirect_target($source, $row);
        $target = esc_url_raw((string) ($resolution['target_url'] ?? ''));
        $confidence = intval($resolution['confidence'] ?? 0);
        $classification = 'no_valid_target';
        if ($target !== '' && $confidence >= 90) {
            $classification = 'safe_auto_apply';
        } elseif ($target !== '') {
            $classification = 'manual_review_needed';
        }

        return array(
            'source_url' => esc_url_raw($source),
            'detected_issue_type' => $issue_type,
            'current_status_code' => $status_code,
            'proposed_target_url' => $target,
            'target_resolution_method' => sanitize_key((string) ($resolution['method'] ?? 'none')),
            'confidence' => $confidence,
            'confidence_tier' => sanitize_key((string) ($resolution['confidence_tier'] ?? ($confidence >= 90 ? 'high' : ($confidence > 0 ? 'medium' : 'low')))),
            'classification' => $classification,
            'safe_to_apply' => $classification === 'safe_auto_apply',
            'manual_review_required' => $classification !== 'safe_auto_apply',
            'reason' => sanitize_text_field((string) ($resolution['reason'] ?? '')),
        );
    }

    private function aysa_technical_site_audit_resolve_redirect_target($source, $row) {
        $source = esc_url_raw((string) $source);
        $row = is_array($row) ? $row : array();
        foreach (array('suggested_target_url', 'target_url', 'redirect_url', 'destination', 'final_url', 'final_address', 'canonical_url') as $key) {
            $target = esc_url_raw((string) ($row[$key] ?? ''));
            if ($target !== '' && $this->aysa_technical_site_audit_redirect_target_is_safe($source, $target)) {
                return array('target_url' => $target, 'method' => $key, 'confidence' => 95, 'confidence_tier' => 'high', 'reason' => 'explicit_target_from_audit_or_redirect_module');
            }
        }

        $path = (string) parse_url($source, PHP_URL_PATH);
        $query = (string) parse_url($source, PHP_URL_QUERY);
        if ($query !== '' && $path !== '') {
            $target = home_url(untrailingslashit($path) . '/');
            if ($this->aysa_technical_site_audit_redirect_target_is_safe($source, $target) && $this->aysa_technical_site_audit_url_looks_live($target)) {
                return array('target_url' => esc_url_raw($target), 'method' => 'query_stripped_live_url', 'confidence' => 92, 'confidence_tier' => 'high', 'reason' => 'same_path_without_query_is_live');
            }
        }
        $slug = sanitize_title(basename(untrailingslashit($path)));
        if ($slug !== '') {
            $post_types = array_values(get_post_types(array('public' => true), 'names'));
            $post = get_page_by_path($slug, OBJECT, $post_types);
            if ($post instanceof WP_Post && (string) $post->post_status === 'publish') {
                $target = get_permalink($post);
                if ($target && $this->aysa_technical_site_audit_redirect_target_is_safe($source, $target)) {
                    return array('target_url' => esc_url_raw($target), 'method' => 'exact_slug_permalink_match', 'confidence' => 90, 'confidence_tier' => 'high', 'reason' => 'exact_slug_matches_live_wordpress_permalink');
                }
            }
        }

        return array('target_url' => '', 'method' => 'none', 'confidence' => 0, 'confidence_tier' => 'low', 'reason' => 'no_safe_redirect_target_found');
    }

    private function aysa_technical_site_audit_url_looks_live($url) {
        $response = wp_remote_head($url, array('timeout' => 4, 'redirection' => 2));
        if (is_wp_error($response)) {
            return false;
        }
        $code = intval(wp_remote_retrieve_response_code($response));
        return $code >= 200 && $code < 400;
    }

    private function aysa_technical_site_audit_redirect_target_is_safe($source, $target) {
        $source = esc_url_raw((string) $source);
        $target = esc_url_raw((string) $target);
        if ($source === '' || $target === '') {
            return false;
        }
        $source_path = untrailingslashit((string) parse_url($source, PHP_URL_PATH));
        $target_path = untrailingslashit((string) parse_url($target, PHP_URL_PATH));
        if ($source_path !== '' && $target_path !== '' && $source_path === $target_path) {
            return false;
        }
        $target_host = (string) parse_url($target, PHP_URL_HOST);
        $site_host = (string) parse_url(home_url('/'), PHP_URL_HOST);
        return $target_host === '' || $site_host === '' || strcasecmp($target_host, $site_host) === 0;
    }

    private function aysa_technical_site_audit_redirect_source_is_safe($source) {
        $path = strtolower((string) parse_url((string) $source, PHP_URL_PATH));
        if ($path === '') {
            return false;
        }
        foreach (array('/wp-admin', '/wp-content', '/wp-includes', '/xmlrpc.php', '/wp-login.php', '/wp-json', '/.well-known') as $blocked) {
            if (strpos($path, $blocked) === 0) {
                return false;
            }
        }
        if (preg_match('#/(admin|cgi-bin|vendor|system|modules|plugins|includes|uploads?)(/|$)#', $path)) {
            return false;
        }
        return true;
    }

    private function aysa_technical_site_audit_current_post_meta_title($post_id, $post) {
        foreach (array('_aysa_meta_title', '_aysa_seo_title', '_yoast_wpseo_title', 'rank_math_title') as $key) {
            $value = trim((string) get_post_meta($post_id, $key, true));
            if ($value !== '') {
                return $value;
            }
        }
        return sanitize_text_field((string) ($post->post_title ?? ''));
    }

    private function aysa_technical_site_audit_current_post_meta_description($post_id, $post) {
        foreach (array('_aysa_meta_description', '_aysa_seo_description', '_yoast_wpseo_metadesc', 'rank_math_description') as $key) {
            $value = trim((string) get_post_meta($post_id, $key, true));
            if ($value !== '') {
                return $value;
            }
        }
        $excerpt = trim(wp_strip_all_tags((string) ($post->post_excerpt ?? '')));
        if ($excerpt !== '') {
            return sanitize_text_field(wp_trim_words($excerpt, 24, ''));
        }
        return sanitize_text_field(wp_trim_words(wp_strip_all_tags((string) ($post->post_content ?? '')), 24, ''));
    }

    private function aysa_technical_site_audit_detect_canonical_provider($post_id) {
        $post_id = intval($post_id);
        if ($post_id <= 0) {
            return '';
        }
        if (trim((string) get_post_meta($post_id, '_yoast_wpseo_canonical', true)) !== '' || is_dir(WP_PLUGIN_DIR . '/wordpress-seo')) {
            return 'yoast';
        }
        if (trim((string) get_post_meta($post_id, 'rank_math_canonical_url', true)) !== '' || is_dir(WP_PLUGIN_DIR . '/seo-by-rank-math')) {
            return 'rank_math';
        }
        return 'custom_meta';
    }

    private function aysa_technical_site_audit_current_post_canonical_url($post_id) {
        $post_id = intval($post_id);
        foreach (array('_yoast_wpseo_canonical', 'rank_math_canonical_url', 'aysa_canonical_url') as $key) {
            $value = trim((string) get_post_meta($post_id, $key, true));
            if ($value !== '') {
                return esc_url_raw($value);
            }
        }
        return '';
    }

    private function aysa_technical_site_audit_update_post_canonical_url($post_id, $target_url, $provider = '') {
        $post_id = intval($post_id);
        $target_url = esc_url_raw((string) $target_url);
        $provider = sanitize_key((string) $provider);
        if ($post_id <= 0 || $target_url === '') {
            return array('applied' => false, 'reason' => 'canonical_update_context_required');
        }
        $meta_key = '_yoast_wpseo_canonical';
        if ($provider === 'rank_math') {
            $meta_key = 'rank_math_canonical_url';
        } elseif ($provider === 'custom_meta') {
            $meta_key = 'aysa_canonical_url';
        }
        update_post_meta($post_id, $meta_key, $target_url);
        return array(
            'applied' => true,
            'meta_key' => $meta_key,
            'target_url' => $target_url,
        );
    }

    private function aysa_technical_site_audit_proposed_meta_title($post) {
        $title = sanitize_text_field((string) ($post->post_title ?? ''));
        $site = sanitize_text_field((string) get_bloginfo('name'));
        $candidate = $title;
        if ($site !== '' && mb_strlen($title . ' | ' . $site) <= 60) {
            $candidate = $title . ' | ' . $site;
        }
        return sanitize_text_field(mb_substr($candidate, 0, 70));
    }

    private function aysa_technical_site_audit_proposed_meta_description($post) {
        $source = trim(wp_strip_all_tags((string) ($post->post_excerpt ?? '')));
        if ($source === '') {
            $source = trim(wp_strip_all_tags((string) ($post->post_content ?? '')));
        }
        $sentences = preg_split('/(?<=[.!?])\\s+/u', trim($source), -1, PREG_SPLIT_NO_EMPTY);
        foreach ((array) $sentences as $sentence) {
            $candidate = trim(preg_replace('/\\s+/', ' ', wp_strip_all_tags((string) $sentence)));
            $length = mb_strlen($candidate);
            if ($length >= 70 && $length <= 165 && preg_match('/[.!?]$/u', $candidate)) {
                return sanitize_text_field($candidate);
            }
        }
        $fallback = $this->aysa_technical_site_audit_trim_metadata_text($source, 155);
        return $fallback !== '' && mb_strlen($fallback) >= 70 ? sanitize_text_field(rtrim($fallback, " \t\n\r\0\x0B,;-:") . '.') : '';
    }

    private function aysa_technical_site_audit_metadata_candidate_quality($field, $current, $proposed, $issue_type, $post) {
        $field = sanitize_key((string) $field);
        if ($field === 'meta_description') {
            return $this->aysa_technical_site_audit_meta_description_quality($current, $proposed, $issue_type, $post);
        }
        $current = trim((string) $current);
        $proposed = trim((string) $proposed);
        $issue_type = sanitize_key((string) $issue_type);
        $notes = array();
        $min = $field === 'meta_title' ? 25 : 70;
        $max = $field === 'meta_title' ? 60 : 160;
        $current_len = mb_strlen($current);
        $proposed_len = mb_strlen($proposed);
        if ($current !== '' && $current_len >= $min && $current_len <= $max && stripos($issue_type, 'missing') === false && stripos($issue_type, 'too_long') === false && stripos($issue_type, 'duplicate') === false) {
            return array('classification' => 'already_optimized', 'quality_score' => 95, 'inclusion_reason' => 'current_metadata_already_within_quality_bounds', 'notes' => array('current_value_within_length_bounds'));
        }
        if ($current !== '' && $current === $proposed) {
            return array('classification' => 'already_optimized', 'quality_score' => 90, 'inclusion_reason' => 'proposal_matches_current_value', 'notes' => array('no_safe_improvement_over_current_value'));
        }
        if ($proposed_len < $min) {
            $notes[] = 'proposal_shorter_than_recommended';
        }
        if ($proposed_len > $max) {
            $notes[] = 'proposal_longer_than_recommended';
        }
        $title = trim((string) ($post->post_title ?? ''));
        if ($title !== '' && stripos($proposed, mb_substr($title, 0, min(18, mb_strlen($title)))) !== false) {
            $notes[] = 'aligned_with_page_title';
        }
        $score = 70;
        if ($proposed_len >= $min && $proposed_len <= $max) {
            $score += 15;
        }
        if (in_array('aligned_with_page_title', $notes, true)) {
            $score += 10;
        }
        if ($current === '') {
            $score += 5;
        }
        $score = min(100, $score);
        return array(
            'decision' => $score >= 80 ? 'rewrite' : 'manual_review',
            'overwrite_reason' => $current === '' ? 'metadata_missing' : 'metadata_weak_or_audit_flagged',
            'classification' => $score >= 80 ? 'safe_auto_apply' : 'manual_review_needed',
            'quality_score' => $score,
            'inclusion_reason' => $current === '' ? 'metadata_missing' : 'metadata_weak_or_audit_flagged',
            'quality_flags' => array(),
            'notes' => $notes,
        );
    }

    private function aysa_technical_site_audit_meta_description_quality($current, $proposed, $issue_type, $post) {
        $current = trim((string) $current);
        $issue_type = sanitize_key((string) $issue_type);
        $flags = array();
        $notes = array();
        $primary_keyword = trim((string) ($post->primary_keyword ?? $post->main_keyword ?? ''));
        $secondary_keywords = $this->aysa_technical_site_audit_keyword_list($post->secondary_keywords ?? array(), $primary_keyword);
        $current_len = mb_strlen($current);
        $has_keyword_context = $primary_keyword !== '' || !empty($secondary_keywords);

        if ($current === '') {
            return $this->aysa_technical_site_audit_rewrite_decision('missing_current_description', 98, array('missing'), array('current_description_missing'));
        }
        if ($current_len < 105) {
            return $this->aysa_technical_site_audit_rewrite_decision('current_description_too_short', 96, array('too_short'), array('current_length_below_hard_floor'));
        }
        if ($current_len > 175) {
            return $this->aysa_technical_site_audit_rewrite_decision('current_description_too_long', 96, array('too_long'), array('current_length_above_hard_ceiling'));
        }
        if (strpos($issue_type, 'duplicate') !== false) {
            return $this->aysa_technical_site_audit_rewrite_decision('duplicate_description_flagged_by_site_audit', 94, array('duplicate'), array('site_audit_duplicate_signal'));
        }

        if ($current_len >= 120 && $current_len <= 160) {
            $flags[] = 'length_in_preserve_band';
        } else {
            $flags[] = 'length_edge_band';
            $notes[] = 'current_length_outside_preferred_preserve_band';
        }

        if ($this->aysa_technical_site_audit_has_truncation_signals($current)) {
            return $this->aysa_technical_site_audit_rewrite_decision('current_description_truncated_or_incomplete', 95, array_merge($flags, array('truncated')), array_merge($notes, array('current_description_looks_incomplete')));
        }
        if ($this->aysa_technical_site_audit_has_generic_filler($current)) {
            return $this->aysa_technical_site_audit_rewrite_decision('current_description_generic_or_filler', 92, array_merge($flags, array('generic_filler')), array_merge($notes, array('generic_filler_phrase_detected')));
        }
        if ($this->aysa_technical_site_audit_is_low_relevance_to_page_intent($current, $post)) {
            return $this->aysa_technical_site_audit_rewrite_decision('current_description_low_relevance_to_page_intent', 90, array_merge($flags, array('low_relevance')), array_merge($notes, array('weak_overlap_with_page_intent_terms')));
        }

        if ($primary_keyword === '') {
            $notes[] = $has_keyword_context ? 'primary_keyword_context_missing' : 'keyword_context_missing';
            return $this->aysa_technical_site_audit_manual_review_decision('keyword_context_missing_for_preserve_check', 58, array_merge($flags, array('missing_primary_context')), $notes);
        }

        if ($this->aysa_technical_site_audit_text_contains_keyword($current, $primary_keyword)) {
            $flags[] = 'primary_keyword_present';
        } else {
            return $this->aysa_technical_site_audit_rewrite_decision('primary_keyword_missing_from_current_description', 93, array_merge($flags, array('missing_primary_keyword')), array_merge($notes, array('primary_keyword_not_found_in_current_description')));
        }

        if (empty($secondary_keywords)) {
            return $this->aysa_technical_site_audit_manual_review_decision('secondary_keyword_context_missing_for_preserve_check', 56, array_merge($flags, array('missing_secondary_context')), array_merge($notes, array('secondary_keywords_not_available')));
        }

        $matched_secondary = $this->aysa_technical_site_audit_matched_secondary_keywords($current, $secondary_keywords);
        if (empty($matched_secondary)) {
            return $this->aysa_technical_site_audit_rewrite_decision('secondary_keyword_missing_from_current_description', 84, array_merge($flags, array('missing_secondary_keyword')), array_merge($notes, array('no_meaningful_secondary_keyword_match_found')));
        }
        $flags[] = 'secondary_keyword_present';
        $notes[] = 'matched_secondary_keywords:' . implode('|', array_slice($matched_secondary, 0, 3));

        if (!in_array('length_in_preserve_band', $flags, true)) {
            return $this->aysa_technical_site_audit_manual_review_decision('current_description_only_borderline_length', 64, $flags, $notes);
        }

        return array(
            'decision' => 'preserve',
            'overwrite_reason' => 'existing_meta_description_passes_quality_gate',
            'classification' => 'already_optimized',
            'quality_score' => 96,
            'inclusion_reason' => 'existing_meta_description_passes_quality_gate',
            'quality_flags' => $flags,
            'notes' => $notes,
        );
    }

    private function aysa_technical_site_audit_rewrite_decision($reason, $confidence, $flags, $notes) {
        return array(
            'decision' => 'rewrite',
            'overwrite_reason' => sanitize_key((string) $reason),
            'classification' => 'safe_auto_apply',
            'quality_score' => max(0, min(100, intval($confidence))),
            'inclusion_reason' => 'current_meta_description_failed_quality_gate',
            'quality_flags' => array_values(array_unique(array_map('sanitize_key', (array) $flags))),
            'notes' => array_values((array) $notes),
        );
    }

    private function aysa_technical_site_audit_manual_review_decision($reason, $confidence, $flags, $notes) {
        return array(
            'decision' => 'manual_review',
            'overwrite_reason' => sanitize_key((string) $reason),
            'classification' => 'manual_review_needed',
            'quality_score' => max(0, min(100, intval($confidence))),
            'inclusion_reason' => 'current_meta_description_requires_manual_review',
            'quality_flags' => array_values(array_unique(array_map('sanitize_key', (array) $flags))),
            'notes' => array_values((array) $notes),
        );
    }

    private function aysa_technical_site_audit_primary_keyword($post_id) {
        $post_id = intval($post_id);
        foreach (array('aysa_main_keyword', '_yoast_wpseo_focuskw', 'keyword', 'main_keyword', 'primary_keyword', 'aysa_keyword') as $meta_key) {
            $value = trim((string) get_post_meta($post_id, $meta_key, true));
            if ($value !== '') {
                return sanitize_text_field($value);
            }
        }
        return '';
    }

    private function aysa_technical_site_audit_secondary_keywords($post_id, $primary_keyword = '') {
        $post_id = intval($post_id);
        foreach (array('aysa_secondary_keywords', 'second_keyword', '_aysa_ai_secondary_keywords', 'secondary_keywords') as $meta_key) {
            $value = get_post_meta($post_id, $meta_key, true);
            $keywords = $this->aysa_technical_site_audit_keyword_list($value, (string) $primary_keyword);
            if (!empty($keywords)) {
                return $keywords;
            }
        }
        return array();
    }

    private function aysa_technical_site_audit_keyword_list($value, $primary_keyword = '') {
        $keywords = array();
        if (is_string($value)) {
            $parts = preg_split('/[,|;]/u', $value, -1, PREG_SPLIT_NO_EMPTY);
            $keywords = is_array($parts) ? $parts : array();
        } elseif (is_array($value)) {
            $keywords = array_map('strval', $value);
        }
        $primary_norm = $this->aysa_technical_site_audit_normalize_keyword((string) $primary_keyword);
        $out = array();
        foreach ($keywords as $keyword) {
            $keyword = trim((string) $keyword);
            if ($keyword === '') {
                continue;
            }
            if ($primary_norm !== '' && $this->aysa_technical_site_audit_normalize_keyword($keyword) === $primary_norm) {
                continue;
            }
            $out[] = sanitize_text_field($keyword);
        }
        return array_values(array_unique($out));
    }

    private function aysa_technical_site_audit_has_truncation_signals($value) {
        $trimmed = trim((string) $value);
        if ($trimmed === '') {
            return false;
        }
        if (preg_match('/(\.\.\.|…)\s*$/u', $trimmed)) {
            return true;
        }
        if (preg_match('/\b(si|sau|cu|de|din|pentru|la|in|în|pe|despre|catre|către)\s*$/iu', $trimmed)) {
            return true;
        }
        return preg_match('/[,;:\-]\s*$/u', $trimmed) === 1;
    }

    private function aysa_technical_site_audit_has_generic_filler($value) {
        $normalized = $this->aysa_technical_site_audit_normalize_text((string) $value);
        foreach (array('afla mai multe', 'descopera mai multe', 'click aici', 'intra acum', 'vezi mai multe', 'citeste mai mult', 'cele mai bune produse', 'produse de calitate', 'servicii de calitate', 'solutii complete', 'oferim servicii profesionale', 'alegerea perfecta') as $phrase) {
            if (strpos($normalized, $this->aysa_technical_site_audit_normalize_text($phrase)) !== false) {
                return true;
            }
        }
        return false;
    }

    private function aysa_technical_site_audit_is_low_relevance_to_page_intent($description, $post) {
        $intent_tokens = $this->aysa_technical_site_audit_page_intent_tokens($post);
        if (empty($intent_tokens)) {
            return false;
        }
        $description_tokens = $this->aysa_technical_site_audit_tokenize_text((string) $description);
        return count(array_intersect($intent_tokens, $description_tokens)) < 2;
    }

    private function aysa_technical_site_audit_page_intent_tokens($post) {
        $pool = array();
        foreach (array('post_title', 'title', 'permalink', 'url') as $key) {
            $pool = array_merge($pool, $this->aysa_technical_site_audit_tokenize_text((string) ($post->$key ?? '')));
        }
        $stopwords = array('si', 'sau', 'sunt', 'este', 'din', 'pentru', 'care', 'cele', 'this', 'with', 'from', 'your', 'the', 'and', 'www', 'https', 'http', 'com', 'ro');
        return array_values(array_unique(array_filter($pool, static function($token) use ($stopwords) {
            return mb_strlen((string) $token) >= 4 && !in_array($token, $stopwords, true);
        })));
    }

    private function aysa_technical_site_audit_text_contains_keyword($text, $keyword) {
        $normalized_text = $this->aysa_technical_site_audit_normalize_text((string) $text);
        $normalized_keyword = $this->aysa_technical_site_audit_normalize_keyword((string) $keyword);
        if ($normalized_keyword === '') {
            return false;
        }
        if (strpos($normalized_text, $normalized_keyword) !== false) {
            return true;
        }
        $keyword_tokens = $this->aysa_technical_site_audit_tokenize_text($normalized_keyword);
        if (count($keyword_tokens) <= 1) {
            return false;
        }
        $text_tokens = $this->aysa_technical_site_audit_tokenize_text($normalized_text);
        return count(array_intersect($keyword_tokens, $text_tokens)) >= max(2, count($keyword_tokens) - 1);
    }

    private function aysa_technical_site_audit_matched_secondary_keywords($text, $secondary_keywords) {
        $matched = array();
        foreach ((array) $secondary_keywords as $keyword) {
            $normalized_keyword = $this->aysa_technical_site_audit_normalize_keyword((string) $keyword);
            if ($normalized_keyword === '') {
                continue;
            }
            if (mb_strlen($normalized_keyword) >= 4 && strpos($this->aysa_technical_site_audit_normalize_text((string) $text), $normalized_keyword) !== false) {
                $matched[] = (string) $keyword;
                continue;
            }
            $keyword_tokens = $this->aysa_technical_site_audit_tokenize_text($normalized_keyword);
            if (!empty($keyword_tokens) && count(array_intersect($keyword_tokens, $this->aysa_technical_site_audit_tokenize_text((string) $text))) >= 1) {
                $matched[] = (string) $keyword;
            }
        }
        return array_values(array_unique($matched));
    }

    private function aysa_technical_site_audit_normalize_keyword($value) {
        return trim($this->aysa_technical_site_audit_normalize_text((string) $value));
    }

    private function aysa_technical_site_audit_normalize_text($value) {
        $value = mb_strtolower(trim((string) $value));
        $value = preg_replace('/[^\p{L}\p{N}\s-]+/u', ' ', $value);
        return trim(preg_replace('/\s+/u', ' ', (string) $value));
    }

    private function aysa_technical_site_audit_tokenize_text($value) {
        $parts = preg_split('/[\s\-\/]+/u', $this->aysa_technical_site_audit_normalize_text((string) $value), -1, PREG_SPLIT_NO_EMPTY);
        if (!is_array($parts)) {
            return array();
        }
        return array_values(array_unique(array_filter($parts, static function($token) {
            return mb_strlen((string) $token) >= 3;
        })));
    }

    private function aysa_technical_site_audit_trim_metadata_text($value, $max_length) {
        $value = trim(preg_replace('/\s+/', ' ', wp_strip_all_tags((string) $value)));
        $max_length = intval($max_length);
        if ($max_length <= 0 || mb_strlen($value) <= $max_length) {
            return sanitize_text_field($value);
        }
        $trimmed = mb_substr($value, 0, $max_length);
        $last_space = mb_strrpos($trimmed, ' ');
        if ($last_space !== false && $last_space > 40) {
            $trimmed = mb_substr($trimmed, 0, $last_space);
        }
        return sanitize_text_field(rtrim($trimmed, " \t\n\r\0\x0B,;-:"));
    }

    private function aysa_technical_site_audit_validate_metadata_proposal($field, $value) {
        $field = sanitize_key((string) $field);
        $value = trim(preg_replace('/\s+/', ' ', (string) $value));
        $length = mb_strlen($value);
        if ($value === '') {
            return array('ok' => false, 'reason' => 'empty_proposed_value');
        }
        if (preg_match('/[<>{}\[\]]/', $value)) {
            return array('ok' => false, 'reason' => 'unsafe_characters_in_proposed_value');
        }
        $tokens = preg_split('/\s+/u', mb_strtolower($value), -1, PREG_SPLIT_NO_EMPTY);
        $unique_tokens = array_unique(array_filter($tokens, static function($token) {
            return mb_strlen((string) $token) > 2;
        }));
        if ($field === 'meta_title' && ($length < 15 || $length > 70)) {
            return array('ok' => false, 'reason' => 'title_length_outside_safe_bounds');
        }
        if ($field === 'meta_title' && count($unique_tokens) < 3) {
            return array('ok' => false, 'reason' => 'title_too_generic');
        }
        if ($field === 'meta_description' && ($length < 70 || $length > 165)) {
            return array('ok' => false, 'reason' => 'description_length_outside_safe_bounds');
        }
        if ($field === 'meta_description' && count($unique_tokens) < 8) {
            return array('ok' => false, 'reason' => 'description_too_generic');
        }
        if ($field === 'meta_description' && preg_match('/\\s+(de|de a|pentru|pentru a|la|cu|si|și)$/iu', $value)) {
            return array('ok' => false, 'reason' => 'description_ends_with_incomplete_phrase');
        }
        if ($field === 'meta_description' && !preg_match('/[.!?]$/u', $value)) {
            return array('ok' => false, 'reason' => 'description_not_a_complete_sentence');
        }
        return array('ok' => true, 'reason' => '');
    }

    private function aysa_technical_site_audit_build_onpage_metadata_projection_row($project_id, $item, $post, $worker_run_id) {
        $project_id = intval($project_id);
        $post_id = intval($item['post_id'] ?? 0);
        $field = sanitize_key((string) ($item['field'] ?? ''));
        $proposed = sanitize_text_field((string) ($item['proposed_value'] ?? ''));
        $current_title = $this->aysa_technical_site_audit_current_post_meta_title($post_id, $post);
        $current_description = $this->aysa_technical_site_audit_current_post_meta_description($post_id, $post);
        $meta_title = $field === 'meta_title' ? $proposed : $current_title;
        $meta_description = $field === 'meta_description' ? $proposed : $current_description;

        return array(
            'entity_key' => 'post:' . $post_id,
            'entity_id' => $post_id,
            'object_kind' => 'post',
            'post_type' => sanitize_key((string) ($post->post_type ?? 'post')),
            'taxonomy' => '',
            'url' => esc_url_raw((string) ($item['url'] ?? get_permalink($post_id))),
            'title' => sanitize_text_field((string) ($post->post_title ?? '')),
            'entity_group' => sanitize_key((string) ($post->post_type ?? 'post')),
            'publication_state' => sanitize_key((string) ($post->post_status ?? 'publish')),
            'mapping_state' => 'mapped',
            'seo_state' => 'not_optimized',
            'optimization_scope' => 'meta_only',
            'meta_state' => 'generated',
            'rewrite_state' => 'not_needed',
            'output_state' => 'review_ready',
            'main_keyword' => '',
            'confidence_score' => 0.82,
            'decision_reason' => 'Prepared by Site Audit metadata remediation and applied through the On-Page metadata workspace.',
            'next_step' => 'Review generated meta',
            'content_source' => 'site_audit_metadata_remediation',
            'meta_package_json' => array(
                'meta_title' => $meta_title,
                'meta_description' => $meta_description,
                'confidence_score' => 0.82,
                'source' => 'site_audit_metadata_remediation',
                'site_audit_issue_type' => sanitize_key((string) ($item['issue_type'] ?? '')),
                'site_audit_issue_id' => sanitize_text_field((string) ($item['issue_id'] ?? '')),
                'target_field' => $field,
            ),
            'worker_run_id' => intval($worker_run_id),
            'last_evaluated_at' => current_time('mysql'),
        );
    }

    private function aysa_technical_site_audit_write_post_metadata($post_id, $field, $value) {
        $post_id = intval($post_id);
        $field = sanitize_key((string) $field);
        $value = sanitize_text_field((string) $value);
        if ($post_id <= 0 || $value === '') {
            return false;
        }
        if ($field === 'meta_title') {
            update_post_meta($post_id, '_aysa_seo_title', $value);
            update_post_meta($post_id, '_aysa_meta_title', $value);
            if (function_exists('is_plugin_active') && is_plugin_active('wordpress-seo/wp-seo.php')) {
                update_post_meta($post_id, '_yoast_wpseo_title', $value);
            }
            if (function_exists('is_plugin_active') && is_plugin_active('seo-by-rank-math/rank-math.php')) {
                update_post_meta($post_id, 'rank_math_title', $value);
            }
            return true;
        }
        if ($field === 'meta_description') {
            update_post_meta($post_id, '_aysa_seo_description', $value);
            update_post_meta($post_id, '_aysa_meta_description', $value);
            if (function_exists('is_plugin_active') && is_plugin_active('wordpress-seo/wp-seo.php')) {
                update_post_meta($post_id, '_yoast_wpseo_metadesc', $value);
            }
            if (function_exists('is_plugin_active') && is_plugin_active('seo-by-rank-math/rank-math.php')) {
                update_post_meta($post_id, 'rank_math_description', $value);
            }
            return true;
        }
        return false;
    }

    private function aysa_technical_site_audit_dispatch_metadata_remediation($project_id, $payload = array()) {
        $project_id = intval($project_id);
        $payload = is_array($payload) ? $payload : array();
        $snapshot = $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id);
        $bundle = $this->aysa_technical_site_audit_find_action_bundle($snapshot, 'prepare_metadata_remediation');
        if (empty($bundle)) {
            return array('applied' => false, 'reason' => 'metadata_bundle_not_available');
        }
        $package_id = sanitize_text_field((string) ($bundle['execution_package_id'] ?? ''));
        if ($package_id === '') {
            $package_id = 'site_audit_' . $project_id . '_prepare_metadata_remediation_' . substr(md5(current_time('mysql')), 0, 10);
        }
        $package = !empty($bundle['metadata_execution_package']) && is_array($bundle['metadata_execution_package'])
            ? $bundle['metadata_execution_package']
            : $this->aysa_technical_site_audit_build_metadata_execution_package($project_id, $snapshot, $bundle, $package_id);
        $result = array(
            'result_type' => 'worker_dispatched',
            'worker_run_id' => intval($payload['worker_run_id'] ?? 0),
            'worker_task_id' => intval($payload['worker_task_id'] ?? 0),
            'automatic_site_mutation' => true,
            'message' => 'Metadata remediation was queued for the worker. Supported WordPress post metadata will be updated by the worker callback.',
            'package_items_count' => intval($package['items_count'] ?? 0),
            'package_skipped_count' => intval($package['skipped_count'] ?? 0),
        );
        $communication = $this->aysa_agent8_create_site_audit_communication_event(
            $project_id,
            'site_audit_metadata_apply_started',
            'Agent8 Site Audit: ' . $this->aysa_technical_site_audit_action_subject_label('prepare_metadata_remediation', $package) . ' started',
            sprintf(
                "Relax, I got this.\n\nI sent the Site Audit metadata package to the worker. I will apply only clearly mapped WordPress pages through the shared On-Page metadata path.\n\nScope: %d ready items, %d skipped before apply.",
                intval($package['items_count'] ?? 0),
                intval($package['skipped_count'] ?? 0)
            ),
            array(
                'action_key' => 'prepare_metadata_remediation',
                'worker_run_id' => intval($payload['worker_run_id'] ?? 0),
                'worker_task_id' => intval($payload['worker_task_id'] ?? 0),
                'package_id' => $package_id,
            )
        );
        $result['communication_event'] = $communication;
        $this->aysa_adjustments_update_site_audit_action_bundle_state($project_id, 'prepare_metadata_remediation', array(
            'approval_state' => 'applying',
            'execution_state' => 'applying',
            'execution_package_id' => $package_id,
            'metadata_execution_package' => $package,
            'last_apply_result' => $result,
            'worker_run_id' => intval($payload['worker_run_id'] ?? 0),
            'worker_task_id' => intval($payload['worker_task_id'] ?? 0),
            'last_error' => '',
        ));
        return array_merge(array(
            'applied' => true,
            'stage' => 'apply',
            'execution_state' => 'applying',
            'action_key' => 'prepare_metadata_remediation',
            'execution_package_id' => $package_id,
        ), $result);
    }

    private function aysa_technical_site_audit_dispatch_image_alt_generation($project_id, $payload = array()) {
        $project_id = intval($project_id);
        $payload = is_array($payload) ? $payload : array();
        $snapshot = $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id);
        $bundle = $this->aysa_technical_site_audit_find_action_bundle($snapshot, 'prepare_image_alt_text');
        if (empty($bundle)) {
            return array('applied' => false, 'reason' => 'image_alt_bundle_not_available');
        }
        $package = isset($payload['image_alt_execution_package']) && is_array($payload['image_alt_execution_package'])
            ? $payload['image_alt_execution_package']
            : array();
        $package_id = sanitize_text_field((string) ($bundle['execution_package_id'] ?? ($package['package_id'] ?? '')));
        if ($package_id === '') {
            $package_id = 'site_audit_' . $project_id . '_prepare_image_alt_text_' . substr(md5(current_time('mysql')), 0, 10);
        }
        $result = array(
            'result_type' => 'worker_dispatched',
            'worker_run_id' => intval($payload['worker_run_id'] ?? 0),
            'worker_task_id' => intval($payload['worker_task_id'] ?? 0),
            'automatic_site_mutation' => false,
            'message' => 'The worker is analyzing mapped images and will return a reviewed image alt package before anything is applied.',
            'seed_candidate_count' => intval($package['seed_candidate_count'] ?? count((array) ($package['candidates'] ?? array()))),
        );
        $result['communication_event'] = $this->aysa_agent8_create_site_audit_communication_event(
            $project_id,
            'site_audit_image_alt_generation_started',
            'Agent8 Site Audit: Image alt text started',
            "Relax, I got this. I’m analyzing your images.",
            array(
                'action_key' => 'prepare_image_alt_text',
                'worker_run_id' => intval($payload['worker_run_id'] ?? 0),
                'worker_task_id' => intval($payload['worker_task_id'] ?? 0),
                'package_id' => $package_id,
            ),
            'info'
        );
        $this->aysa_adjustments_update_site_audit_action_bundle_state($project_id, 'prepare_image_alt_text', array(
            'approval_state' => 'preparing',
            'execution_state' => 'preparing',
            'execution_package_id' => $package_id,
            'image_alt_execution_package' => $package,
            'last_prepare_result' => $result,
            'last_error' => '',
            'worker_run_id' => intval($payload['worker_run_id'] ?? 0),
            'worker_task_id' => intval($payload['worker_task_id'] ?? 0),
        ));
        return array_merge(array(
            'applied' => true,
            'stage' => 'prepare',
            'execution_state' => 'preparing',
            'action_key' => 'prepare_image_alt_text',
            'execution_package_id' => $package_id,
        ), $result);
    }

    private function aysa_technical_site_audit_dispatch_shared_action($project_id, $payload = array()) {
        $project_id = intval($project_id);
        $payload = is_array($payload) ? $payload : array();
        $action_key = sanitize_key((string) ($payload['action_key'] ?? ''));
        $operation = sanitize_key((string) ($payload['executor_operation'] ?? ''));
        if ($project_id <= 0 || $action_key === '' || $operation === '') {
            return array('applied' => false, 'reason' => 'shared_action_dispatch_context_required');
        }

        $snapshot = $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id);
        $bundle = $this->aysa_technical_site_audit_find_action_bundle($snapshot, $action_key);
        if (empty($bundle)) {
            return array('applied' => false, 'reason' => 'action_bundle_not_available');
        }

        $package_id = sanitize_text_field((string) ($bundle['execution_package_id'] ?? ''));
        if ($package_id === '') {
            $package_id = 'site_audit_' . $project_id . '_' . $action_key . '_' . substr(md5(current_time('mysql')), 0, 10);
        }
        $maturity = $this->aysa_technical_site_audit_action_maturity($action_key);
        $maturity_label = $this->aysa_technical_site_audit_action_maturity_label($maturity);
        $redirect_package = array();
        $canonical_package = array();
        $internal_linking_package = array();
        $image_alt_package = array();
        if ($action_key === 'prepare_response_code_review') {
            $redirect_package = isset($payload['redirect_execution_package']) && is_array($payload['redirect_execution_package'])
                ? $payload['redirect_execution_package']
                : $this->aysa_technical_site_audit_get_redirect_execution_package($project_id, $snapshot, $bundle, $package_id);
            $bundle['redirect_execution_package'] = $redirect_package;
        } elseif ($action_key === 'prepare_indexability_review') {
            $canonical_package = isset($payload['canonical_execution_package']) && is_array($payload['canonical_execution_package']) && !empty($payload['canonical_execution_package'])
                ? $payload['canonical_execution_package']
                : (isset($bundle['canonical_execution_package']) && is_array($bundle['canonical_execution_package']) ? $bundle['canonical_execution_package'] : array());
            $bundle['canonical_execution_package'] = $canonical_package;
        } elseif ($action_key === 'prepare_internal_linking_plan') {
            $internal_linking_package = isset($payload['internal_linking_execution_package']) && is_array($payload['internal_linking_execution_package'])
                ? $payload['internal_linking_execution_package']
                : array();
            $bundle['internal_linking_execution_package'] = $internal_linking_package;
        } elseif ($action_key === 'prepare_image_alt_text') {
            $image_alt_package = isset($payload['image_alt_execution_package']) && is_array($payload['image_alt_execution_package']) && !empty($payload['image_alt_execution_package'])
                ? $payload['image_alt_execution_package']
                : (isset($bundle['image_alt_execution_package']) && is_array($bundle['image_alt_execution_package']) ? $bundle['image_alt_execution_package'] : array());
            $bundle['image_alt_execution_package'] = $image_alt_package;
        }
        $result = array(
            'result_type' => 'worker_dispatched',
            'worker_run_id' => intval($payload['worker_run_id'] ?? 0),
            'worker_task_id' => intval($payload['worker_task_id'] ?? 0),
            'worker_task_group' => sanitize_key((string) ($payload['worker_task_group'] ?? '')),
            'executor_operation' => $operation,
            'execution_maturity' => $maturity,
            'execution_maturity_label' => $maturity_label,
            'automatic_site_mutation' => in_array($maturity, array('real_executor', 'shared_executor_with_limitations'), true),
            'message' => $maturity === 'adapter_manual_needed_only'
                ? 'This Site Audit package was sent to a worker-controlled adapter for manual-needed classification, not automatic remediation.'
                : 'This Site Audit package was sent to the mapped worker execution path.',
        );
        if (!empty($redirect_package)) {
            $result['redirect_execution_package'] = array(
                'package_id' => sanitize_text_field((string) ($redirect_package['package_id'] ?? $package_id)),
                'total_candidates' => intval($redirect_package['total_candidates'] ?? 0),
                'safe_auto_apply_count' => intval($redirect_package['safe_auto_apply_count'] ?? 0),
                'manual_review_needed_count' => intval($redirect_package['manual_review_needed_count'] ?? 0),
                'no_valid_target_count' => intval($redirect_package['no_valid_target_count'] ?? 0),
            );
        }
        if (!empty($canonical_package)) {
            $result['canonical_execution_package'] = array(
                'package_id' => sanitize_text_field((string) ($canonical_package['package_id'] ?? $package_id)),
                'total_candidates' => intval($canonical_package['total_candidates'] ?? 0),
                'self_canonical_count' => intval($canonical_package['self_canonical_count'] ?? 0),
                'rewrite_count' => intval($canonical_package['rewrite_count'] ?? 0),
                'manual_review_needed_count' => intval($canonical_package['manual_review_needed_count'] ?? 0),
            );
        }
        if (!empty($internal_linking_package)) {
            $result['internal_linking_execution_package'] = array(
                'package_id' => sanitize_text_field((string) ($internal_linking_package['package_id'] ?? $package_id)),
                'total_candidates' => intval($internal_linking_package['total_candidates'] ?? 0),
                'safe_auto_apply_count' => intval($internal_linking_package['safe_auto_apply_count'] ?? 0),
                'manual_review_needed_count' => intval($internal_linking_package['manual_review_needed_count'] ?? 0),
                'no_valid_source_count' => intval($internal_linking_package['no_valid_source_count'] ?? 0),
            );
        }
        if (!empty($image_alt_package)) {
            $result['image_alt_execution_package'] = array(
                'package_id' => sanitize_text_field((string) ($image_alt_package['package_id'] ?? $package_id)),
                'total_candidates' => intval($image_alt_package['total_candidates'] ?? 0),
                'safe_auto_apply_count' => intval($image_alt_package['safe_auto_apply_count'] ?? 0),
                'manual_review_needed_count' => intval($image_alt_package['manual_review_needed_count'] ?? 0),
                'decorative_ignore_count' => intval($image_alt_package['decorative_ignore_count'] ?? 0),
                'preserve_count' => intval($image_alt_package['preserve_count'] ?? 0),
            );
        }
        $external_event = isset($payload['communication_event']) && is_array($payload['communication_event']) ? $payload['communication_event'] : array();
        if (!empty($external_event)) {
            $external_event['payload'] = array_merge(isset($external_event['payload']) && is_array($external_event['payload']) ? $external_event['payload'] : array(), array(
                'action_key' => $action_key,
                'executor_operation' => $operation,
                'execution_maturity' => $maturity,
                'execution_maturity_label' => $maturity_label,
                'worker_run_id' => intval($payload['worker_run_id'] ?? 0),
                'worker_task_id' => intval($payload['worker_task_id'] ?? 0),
                'package_id' => $package_id,
            ));
            $result['communication_event'] = $this->aysa_technical_site_audit_create_external_communication_event($project_id, $external_event);
        } else {
            $result['communication_event'] = $this->aysa_agent8_create_site_audit_communication_event(
                $project_id,
                'site_audit_shared_action_started',
                'Agent8 Site Audit: ' . $this->aysa_technical_site_audit_action_subject_label($action_key) . ' started',
                $this->aysa_technical_site_audit_start_communication_message($action_key, $operation, $bundle, $maturity_label, $maturity),
                array(
                    'action_key' => $action_key,
                    'executor_operation' => $operation,
                    'execution_maturity' => $maturity,
                    'execution_maturity_label' => $maturity_label,
                    'worker_run_id' => intval($payload['worker_run_id'] ?? 0),
                    'worker_task_id' => intval($payload['worker_task_id'] ?? 0),
                    'package_id' => $package_id,
                )
            );
        }
        $state_changes = array(
            'approval_state' => 'applying',
            'execution_state' => 'applying',
            'execution_package_id' => $package_id,
            'last_apply_result' => $result,
            'worker_run_id' => intval($payload['worker_run_id'] ?? 0),
            'worker_task_id' => intval($payload['worker_task_id'] ?? 0),
            'last_error' => '',
        );
        if (!empty($canonical_package)) {
            $state_changes['canonical_execution_package'] = $canonical_package;
            $state_changes['last_prepare_result'] = array(
                'result_type' => 'canonical_execution_package_prepared',
                'canonical_execution_package' => $canonical_package,
                'total_candidates' => intval($canonical_package['total_candidates'] ?? 0),
                'self_canonical_count' => intval($canonical_package['self_canonical_count'] ?? 0),
                'rewrite_count' => intval($canonical_package['rewrite_count'] ?? 0),
                'manual_review_needed_count' => intval($canonical_package['manual_review_needed_count'] ?? 0),
                'automatic_site_mutation' => false,
            );
        }
        if (!empty($redirect_package)) {
            $state_changes['last_prepare_result'] = array(
                'result_type' => 'redirect_execution_package_prepared',
                'redirect_execution_package' => $redirect_package,
                'total_candidates' => intval($redirect_package['total_candidates'] ?? 0),
                'safe_auto_apply_count' => intval($redirect_package['safe_auto_apply_count'] ?? 0),
                'manual_review_needed_count' => intval($redirect_package['manual_review_needed_count'] ?? 0),
                'no_valid_target_count' => intval($redirect_package['no_valid_target_count'] ?? 0),
                'automatic_site_mutation' => false,
            );
        }
        if (!empty($internal_linking_package)) {
            $state_changes['last_prepare_result'] = array(
                'result_type' => 'internal_linking_execution_package_prepared',
                'internal_linking_execution_package' => $internal_linking_package,
                'total_candidates' => intval($internal_linking_package['total_candidates'] ?? 0),
                'safe_auto_apply_count' => intval($internal_linking_package['safe_auto_apply_count'] ?? 0),
                'manual_review_needed_count' => intval($internal_linking_package['manual_review_needed_count'] ?? 0),
                'no_valid_source_count' => intval($internal_linking_package['no_valid_source_count'] ?? 0),
                'automatic_site_mutation' => false,
            );
        }
        if (!empty($image_alt_package)) {
            $state_changes['image_alt_execution_package'] = $image_alt_package;
            $state_changes['last_prepare_result'] = array(
                'result_type' => 'image_alt_execution_package_prepared',
                'image_alt_execution_package' => $image_alt_package,
                'total_candidates' => intval($image_alt_package['total_candidates'] ?? 0),
                'safe_auto_apply_count' => intval($image_alt_package['safe_auto_apply_count'] ?? 0),
                'manual_review_needed_count' => intval($image_alt_package['manual_review_needed_count'] ?? 0),
                'decorative_ignore_count' => intval($image_alt_package['decorative_ignore_count'] ?? 0),
                'preserve_count' => intval($image_alt_package['preserve_count'] ?? 0),
                'automatic_site_mutation' => false,
            );
        }
        $this->aysa_adjustments_update_site_audit_action_bundle_state($project_id, $action_key, $state_changes);

        return array_merge(array(
            'applied' => true,
            'stage' => 'apply',
            'execution_state' => 'applying',
            'action_key' => $action_key,
            'execution_package_id' => $package_id,
        ), $result);
    }

    private function aysa_technical_site_audit_action_maturity($action_key) {
        $action_key = sanitize_key((string) $action_key);
        if ($action_key === 'prepare_metadata_remediation') {
            return 'real_executor';
        }
        if (in_array($action_key, array('prepare_response_code_review', 'prepare_internal_linking_plan'), true)) {
            return 'shared_executor_with_limitations';
        }
        if ($action_key === 'prepare_image_alt_text') {
            return 'shared_executor_with_limitations';
        }
        if ($action_key === 'prepare_indexability_review') {
            return 'shared_executor_with_limitations';
        }
        return 'not_really_implemented';
    }

    private function aysa_technical_site_audit_action_maturity_label($maturity) {
        $labels = array(
            'real_executor' => 'REAL EXECUTOR',
            'shared_executor_with_limitations' => 'SHARED EXECUTOR WITH LIMITATIONS',
            'adapter_manual_needed_only' => 'ADAPTER / MANUAL-NEEDED ONLY',
            'not_really_implemented' => 'NOT REALLY IMPLEMENTED',
        );
        return $labels[$maturity] ?? 'NOT REALLY IMPLEMENTED';
    }

    private function aysa_technical_site_audit_action_subject_label($action_key, $package = array()) {
        $action_key = sanitize_key((string) $action_key);
        if ($action_key === 'prepare_metadata_remediation') {
            $fields = array();
            foreach ((array) ($package['items'] ?? array()) as $item) {
                if (is_array($item)) {
                    $field = sanitize_key((string) ($item['field'] ?? ''));
                    if ($field !== '') {
                        $fields[$field] = true;
                    }
                }
            }
            if (isset($fields['meta_title']) && !isset($fields['meta_description'])) {
                return 'Page titles';
            }
            if (isset($fields['meta_description']) && !isset($fields['meta_title'])) {
                return 'Meta descriptions';
            }
            return 'Page titles & meta descriptions';
        }
        $labels = array(
            'prepare_response_code_review' => 'Response codes & redirects',
            'prepare_indexability_review' => 'Canonical & noindex',
            'prepare_internal_linking_plan' => 'Internal linking',
            'prepare_image_alt_text' => 'Image alt text & media',
        );
        return $labels[$action_key] ?? 'Site Audit action';
    }

    private function aysa_technical_site_audit_issue_family($action_key) {
        $map = array(
            'prepare_metadata_remediation' => 'metadata',
            'prepare_response_code_review' => 'response_codes_redirects',
            'prepare_internal_linking_plan' => 'internal_linking',
        );
        $action_key = sanitize_key((string) $action_key);
        return $map[$action_key] ?? sanitize_key((string) str_replace('prepare_', '', $action_key));
    }

    private function aysa_technical_site_audit_normalize_apply_result($action_key, $package_id, $result) {
        $result = is_array($result) ? $result : array();
        $rows = isset($result['results']) && is_array($result['results']) ? array_values($result['results']) : array();
        $already_present = 0;
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $status = sanitize_key((string) ($row['status'] ?? ''));
            $reason = sanitize_key((string) ($row['reason'] ?? ''));
            if ($status === 'already_present' || $reason === 'already_matches_proposal' || $reason === 'anchor_not_found_or_already_linked') {
                $already_present++;
            }
        }
        $executor_path = sanitize_text_field((string) ($result['executor_path'] ?? $result['shared_executor'] ?? $result['shared_metadata_path'] ?? ''));
        $manual_needed = intval($result['manual_needed_count'] ?? $result['manual_review_needed_count'] ?? 0);
        $normalized = array(
            'issue_family' => $this->aysa_technical_site_audit_issue_family($action_key),
            'package_id' => sanitize_text_field((string) $package_id),
            'execution_package_id' => sanitize_text_field((string) ($result['execution_package_id'] ?? $package_id)),
            'executor_path' => $executor_path,
            'applied_count' => intval($result['applied_count'] ?? 0),
            'skipped_count' => intval($result['skipped_count'] ?? 0),
            'failed_count' => intval($result['failed_count'] ?? 0),
            'manual_needed_count' => $manual_needed,
            'already_present_count' => $already_present,
            'verification_ready' => (intval($result['applied_count'] ?? 0) + $already_present) > 0,
            'per_item_results' => $rows,
            'row_results' => $rows,
        );
        return array_merge($result, $normalized);
    }

    private function aysa_technical_site_audit_normalize_verification_result($verify_result) {
        $verify_result = is_array($verify_result) ? $verify_result : array();
        $checked = intval($verify_result['checked_count'] ?? 0);
        $verified = intval($verify_result['verified_count'] ?? 0);
        $pending = max(0, $checked - $verified);
        return array_merge($verify_result, array(
            'verification_pending_count' => intval($verify_result['verification_pending_count'] ?? $pending),
            'failed_verification_count' => intval($verify_result['failed_verification_count'] ?? $pending),
            'evidence_summary' => sanitize_text_field((string) ($verify_result['evidence_summary'] ?? $verify_result['evidence'] ?? '')),
        ));
    }

    private function aysa_technical_site_audit_start_communication_message($action_key, $operation, $bundle, $maturity_label, $maturity) {
        $affected = intval($bundle['affected_count'] ?? 0);
        if ($maturity === 'adapter_manual_needed_only') {
            return sprintf(
                "I am checking this Site Audit package through a worker-controlled adapter.\n\nAction: %s\nMaturity: %s\nAffected items: %d\n\nThis is not a mature automatic executor yet. I will not present these items as fixed unless a supported shared executor actually changes them.",
                $action_key,
                $maturity_label,
                $affected
            );
        }
        if ($maturity === 'shared_executor_with_limitations') {
            $redirect_package = isset($bundle['redirect_execution_package']) && is_array($bundle['redirect_execution_package']) ? $bundle['redirect_execution_package'] : array();
            $internal_linking_package = isset($bundle['internal_linking_execution_package']) && is_array($bundle['internal_linking_execution_package']) ? $bundle['internal_linking_execution_package'] : array();
            $redirect_counts = '';
            if (!empty($redirect_package) && sanitize_key((string) ($bundle['action_key'] ?? '')) === 'prepare_response_code_review') {
                $redirect_counts = sprintf(
                    "\nRedirect candidates: %d total, %d safe auto-apply, %d manual review, %d without valid target.",
                    intval($redirect_package['total_candidates'] ?? 0),
                    intval($redirect_package['safe_auto_apply_count'] ?? 0),
                    intval($redirect_package['manual_review_needed_count'] ?? 0),
                    intval($redirect_package['no_valid_target_count'] ?? 0)
                );
            }
            if (!empty($internal_linking_package) && sanitize_key((string) ($bundle['action_key'] ?? '')) === 'prepare_internal_linking_plan') {
                $redirect_counts = sprintf(
                    "\nInternal linking candidates: %d total, %d safe auto-apply, %d manual review, %d without valid source.",
                    intval($internal_linking_package['total_candidates'] ?? 0),
                    intval($internal_linking_package['safe_auto_apply_count'] ?? 0),
                    intval($internal_linking_package['manual_review_needed_count'] ?? 0),
                    intval($internal_linking_package['no_valid_source_count'] ?? 0)
                );
            }
            return sprintf(
                "I am starting this Site Audit package through the mapped shared execution path.\n\nAction: %s\nExecutor: %s\nMaturity: %s\nAffected items: %d%s\n\nI will apply only rows that are safely supported by the shared path. Unsupported rows stay skipped or manual-needed.",
                $action_key,
                $operation,
                $maturity_label,
                $affected,
                $redirect_counts
            );
        }
        return sprintf(
            "I am starting this Site Audit package through the mature shared executor.\n\nAction: %s\nExecutor: %s\nMaturity: %s\nAffected items: %d.",
            $action_key,
            $operation,
            $maturity_label,
            $affected
        );
    }

    private function aysa_technical_site_audit_dispatch_canonical_generation($project_id, $payload = array()) {
        $project_id = intval($project_id);
        $payload = is_array($payload) ? $payload : array();
        $snapshot = $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id);
        $bundle = $this->aysa_technical_site_audit_find_action_bundle($snapshot, 'prepare_indexability_review');
        $package = isset($payload['canonical_execution_package']) && is_array($payload['canonical_execution_package'])
            ? $payload['canonical_execution_package']
            : array();
        $package_id = sanitize_text_field((string) ($package['package_id'] ?? ($bundle['execution_package_id'] ?? '')));
        if ($package_id === '') {
            $package_id = 'site_audit_' . $project_id . '_prepare_indexability_review_' . substr(md5(current_time('mysql')), 0, 10);
        }
        $this->aysa_agent8_create_site_audit_communication_event(
            $project_id,
            'site_audit_canonical_prepare_started',
            'Agent8 Site Audit: Canonical remediation started',
            "Relax, I got this. I’m analyzing canonical issues.",
            array(
                'action_key' => 'prepare_indexability_review',
                'package_id' => $package_id,
                'worker_run_id' => intval($payload['worker_run_id'] ?? 0),
                'worker_task_id' => intval($payload['worker_task_id'] ?? 0),
            ),
            'info'
        );
        $this->aysa_adjustments_update_site_audit_action_bundle_state($project_id, 'prepare_indexability_review', array(
            'approval_state' => 'preparing',
            'execution_state' => 'preparing',
            'execution_package_id' => $package_id,
            'canonical_execution_package' => $package,
            'last_error' => '',
        ));
        return array(
            'applied' => true,
            'stage' => 'prepare',
            'execution_state' => 'preparing',
            'action_key' => 'prepare_indexability_review',
            'execution_package_id' => $package_id,
        );
    }

    private function aysa_technical_site_audit_completion_communication_message($action_key, $maturity_label, $maturity, $applied_count, $skipped_count, $manual_needed_count, $failed_count, $result = array()) {
        $result = is_array($result) ? $result : array();
        $redirect_counts = '';
        if ($action_key === 'prepare_response_code_review') {
            $redirect_counts = sprintf(
                "\nRedirect candidates: %d total, %d safe auto-apply, %d manual review, %d without valid target.",
                intval($result['total_candidates'] ?? 0),
                intval($result['safe_auto_apply_count'] ?? 0),
                intval($result['manual_review_needed_count'] ?? 0),
                intval($result['no_valid_target_count'] ?? 0)
            );
        } elseif ($action_key === 'prepare_internal_linking_plan') {
            $redirect_counts = sprintf(
                "\nInternal linking candidates: %d total, %d safe auto-apply, %d manual review, %d without valid source.",
                intval($result['total_candidates'] ?? 0),
                intval($result['safe_auto_apply_count'] ?? 0),
                intval($result['manual_review_needed_count'] ?? 0),
                intval($result['no_valid_source_count'] ?? 0)
            );
        }
        if ($maturity === 'adapter_manual_needed_only' || $manual_needed_count > 0 || $applied_count <= 0) {
            return sprintf(
                "I checked the Site Audit package, but I did not treat it as automatic remediation.\n\nAction: %s\nMaturity: %s\nApplied: %d\nSkipped: %d\nManual review needed: %d\nFailed: %d%s\n\nNext: these items need manual review or a mature shared executor before they can be presented as fixed.",
                $action_key,
                $maturity_label,
                $applied_count,
                $skipped_count,
                $manual_needed_count,
                $failed_count,
                $redirect_counts
            );
        }
        return sprintf(
            "I finished the Site Audit package through the supported execution path.\n\nAction: %s\nMaturity: %s\nApplied: %d\nSkipped: %d\nManual review needed: %d\nFailed: %d%s\n\nNext: use Verify by audit delta only after there is evidence from a newer crawl or live readback.",
            $action_key,
            $maturity_label,
            $applied_count,
            $skipped_count,
            $manual_needed_count,
            $failed_count,
            $redirect_counts
        );
    }

    private function aysa_technical_site_audit_apply_shared_action($project_id, $payload = array()) {
        $project_id = intval($project_id);
        $payload = is_array($payload) ? $payload : array();
        $operation = sanitize_key((string) ($payload['operation'] ?? ''));
        $operation_map = array(
            'site_audit_redirects_apply' => 'prepare_response_code_review',
            'site_audit_canonical_apply' => 'prepare_indexability_review',
            'site_audit_internal_linking_apply' => 'prepare_internal_linking_plan',
            'site_audit_image_alt_apply' => 'prepare_image_alt_text',
        );
        $action_key = sanitize_key((string) ($payload['action_key'] ?? ($operation_map[$operation] ?? '')));
        if ($project_id <= 0 || $operation === '' || $action_key === '') {
            return array('applied' => false, 'reason' => 'shared_action_apply_context_required');
        }

        $snapshot = $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id);
        $bundle = $this->aysa_technical_site_audit_find_action_bundle($snapshot, $action_key);
        if (empty($bundle)) {
            return array('applied' => false, 'reason' => 'action_bundle_not_available');
        }
        $package_id = sanitize_text_field((string) ($bundle['execution_package_id'] ?? ''));
        if ($package_id === '') {
            $package_id = 'site_audit_' . $project_id . '_' . $action_key . '_' . substr(md5(current_time('mysql')), 0, 10);
        }
        $maturity = $this->aysa_technical_site_audit_action_maturity($action_key);
        $maturity_label = $this->aysa_technical_site_audit_action_maturity_label($maturity);

        $rows = $this->aysa_technical_site_audit_collect_bundle_rows($snapshot, $bundle);
        if ($operation === 'site_audit_redirects_apply') {
            $redirect_package = isset($payload['redirect_execution_package']) && is_array($payload['redirect_execution_package'])
                ? $payload['redirect_execution_package']
                : $this->aysa_technical_site_audit_get_redirect_execution_package($project_id, $snapshot, $bundle, $package_id);
            $result = $this->aysa_technical_site_audit_apply_redirect_package($project_id, $redirect_package);
        } elseif ($operation === 'site_audit_canonical_apply') {
            $canonical_package = isset($payload['canonical_execution_package']) && is_array($payload['canonical_execution_package'])
                ? $payload['canonical_execution_package']
                : $this->aysa_technical_site_audit_get_canonical_execution_package($bundle);
            $result = $this->aysa_technical_site_audit_apply_canonical_package($project_id, $canonical_package);
        } elseif ($operation === 'site_audit_internal_linking_apply') {
            $internal_linking_package = isset($payload['internal_linking_execution_package']) && is_array($payload['internal_linking_execution_package'])
                ? $payload['internal_linking_execution_package']
                : $this->aysa_technical_site_audit_get_internal_linking_execution_package($bundle);
            $result = $this->aysa_technical_site_audit_apply_internal_linking_package($project_id, $internal_linking_package);
        } elseif ($operation === 'site_audit_image_alt_apply') {
            $image_alt_package = isset($payload['image_alt_execution_package']) && is_array($payload['image_alt_execution_package']) && !empty($payload['image_alt_execution_package'])
                ? $payload['image_alt_execution_package']
                : $this->aysa_technical_site_audit_get_image_alt_execution_package($bundle);
            $result = $this->aysa_technical_site_audit_apply_image_alt_package($project_id, $image_alt_package);
        } else {
            return array('applied' => false, 'reason' => 'unsupported_shared_action_operation');
        }

        $applied_count = intval($result['applied_count'] ?? 0);
        $failed_count = intval($result['failed_count'] ?? 0);
        $manual_needed_count = intval($result['manual_needed_count'] ?? 0);
        $skipped_count = intval($result['skipped_count'] ?? 0);
        $state = $failed_count > 0 && $applied_count <= 0 && $manual_needed_count <= 0 ? 'failed' : ($applied_count > 0 ? 'applied' : 'manual_needed');
        $result = array_merge($result, array(
            'result_type' => sanitize_key($operation . '_worker_result'),
            'worker_run_id' => intval($payload['worker_run_id'] ?? 0),
            'worker_task_id' => intval($payload['worker_task_id'] ?? 0),
            'worker_task_group' => sanitize_key((string) ($payload['worker_task_group'] ?? '')),
            'executor_operation' => $operation,
            'execution_maturity' => $maturity,
            'execution_maturity_label' => $maturity_label,
            'automatic_site_mutation' => in_array($maturity, array('real_executor', 'shared_executor_with_limitations'), true) && $applied_count > 0,
            'execution_package_id' => $package_id,
        ));
        $result = $this->aysa_technical_site_audit_normalize_apply_result($action_key, $package_id, $result);
        if (empty($payload['suppress_plugin_communication'])) {
            $result['communication_event'] = $this->aysa_agent8_create_site_audit_communication_event(
                $project_id,
                $state === 'failed' ? 'site_audit_shared_action_failed' : 'site_audit_shared_action_completed',
                'Agent8 Site Audit: ' . $this->aysa_technical_site_audit_action_subject_label($action_key) . ($state === 'failed' ? ' failed' : ($applied_count > 0 ? ' completed' : ' needs review')),
                $this->aysa_technical_site_audit_completion_communication_message($action_key, $maturity_label, $maturity, $applied_count, $skipped_count, $manual_needed_count, $failed_count, $result),
                array(
                    'action_key' => $action_key,
                    'executor_operation' => $operation,
                    'execution_maturity' => $maturity,
                    'execution_maturity_label' => $maturity_label,
                    'package_id' => $package_id,
                    'applied_count' => $applied_count,
                    'skipped_count' => $skipped_count,
                    'manual_needed_count' => $manual_needed_count,
                    'failed_count' => $failed_count,
                ),
                $state === 'failed' || $manual_needed_count > 0 ? 'warning' : 'info'
            );
        } else {
            $result['communication_event'] = array('ok' => true, 'source' => 'suppressed_for_worker_shared_service');
        }
        $this->aysa_adjustments_update_site_audit_action_bundle_state($project_id, $action_key, array(
            'approval_state' => $state,
            'execution_state' => $state,
            'execution_package_id' => $package_id,
            'last_applied_at' => current_time('mysql'),
            'last_apply_result' => $result,
            'last_error' => $state === 'failed' ? sanitize_text_field((string) ($result['reason'] ?? 'site_audit_shared_action_failed')) : '',
        ));

        return array(
            'applied' => true,
            'stage' => 'apply',
            'execution_state' => $state,
            'action_key' => $action_key,
            'execution_package_id' => $package_id,
            'execution_maturity' => $maturity,
            'execution_maturity_label' => $maturity_label,
            'applied_count' => $applied_count,
            'skipped_count' => $skipped_count,
            'manual_needed_count' => $manual_needed_count,
            'failed_count' => $failed_count,
            'apply_result' => $result,
        );
    }

    private function aysa_technical_site_audit_collect_bundle_rows($snapshot, $bundle) {
        $categories = array();
        foreach ((array) ($bundle['issue_breakdown'] ?? array()) as $issue) {
            if (is_array($issue)) {
                $category = sanitize_key((string) ($issue['category'] ?? ''));
                if ($category !== '') {
                    $categories[$category] = true;
                }
            }
        }
        if (empty($categories)) {
            $category = sanitize_key((string) ($bundle['issue_category'] ?? ''));
            if ($category !== '') {
                $categories[$category] = true;
            }
        }
        $action_key = sanitize_key((string) ($bundle['action_key'] ?? ''));
        $fallback = array(
            'prepare_response_code_review' => array('response_codes', 'server'),
            'prepare_indexability_review' => array('canonical', 'indexability'),
            'prepare_internal_linking_plan' => array('internal_linking'),
            'prepare_image_alt_text' => array('media'),
        );
        foreach ((array) ($fallback[$action_key] ?? array()) as $category) {
            $categories[sanitize_key((string) $category)] = true;
        }
        $rows = array();
        foreach ((array) ($snapshot['rows'] ?? array()) as $issue) {
            if (!is_array($issue) || !isset($categories[sanitize_key((string) ($issue['category'] ?? ''))])) {
                continue;
            }
            $affected = isset($issue['affected_page_rows']) && is_array($issue['affected_page_rows']) ? $issue['affected_page_rows'] : (array) ($issue['examples'] ?? array());
            foreach ($affected as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $row['site_audit_issue_type'] = sanitize_key((string) ($issue['issue_type'] ?? $issue['key'] ?? ''));
                $row['site_audit_issue_id'] = sanitize_text_field((string) ($issue['issue_id'] ?? ''));
                $row['site_audit_category'] = sanitize_key((string) ($issue['category'] ?? ''));
                $rows[] = $row;
            }
        }
        return $rows;
    }

    private function aysa_technical_site_audit_row_url($row) {
        foreach (array('url', 'address', 'source_url', 'source', 'image_url', 'src') as $key) {
            $url = esc_url_raw((string) ($row[$key] ?? ''));
            if ($url !== '') {
                return $url;
            }
        }
        return '';
    }

    private function aysa_technical_site_audit_get_redirect_execution_package($project_id, $snapshot, $bundle, $package_id) {
        $bundle = is_array($bundle) ? $bundle : array();
        $prepared = isset($bundle['last_prepare_result']) && is_array($bundle['last_prepare_result']) ? $bundle['last_prepare_result'] : array();
        $package = isset($prepared['redirect_execution_package']) && is_array($prepared['redirect_execution_package']) ? $prepared['redirect_execution_package'] : array();
        if (!empty($package) && !empty($package['candidates'])) {
            return $package;
        }
        return $this->aysa_technical_site_audit_build_redirect_execution_package($project_id, $snapshot, $bundle, $package_id);
    }

    private function aysa_technical_site_audit_get_internal_linking_execution_package($bundle) {
        $bundle = is_array($bundle) ? $bundle : array();
        $prepared = isset($bundle['last_prepare_result']) && is_array($bundle['last_prepare_result']) ? $bundle['last_prepare_result'] : array();
        $package = isset($prepared['internal_linking_execution_package']) && is_array($prepared['internal_linking_execution_package']) ? $prepared['internal_linking_execution_package'] : array();
        return !empty($package) && !empty($package['candidates']) ? $package : array();
    }

    private function aysa_technical_site_audit_get_image_alt_execution_package($bundle) {
        $bundle = is_array($bundle) ? $bundle : array();
        if (isset($bundle['image_alt_execution_package']) && is_array($bundle['image_alt_execution_package']) && !empty($bundle['image_alt_execution_package'])) {
            return $bundle['image_alt_execution_package'];
        }
        $prepared = isset($bundle['last_prepare_result']) && is_array($bundle['last_prepare_result']) ? $bundle['last_prepare_result'] : array();
        $package = isset($prepared['image_alt_execution_package']) && is_array($prepared['image_alt_execution_package']) ? $prepared['image_alt_execution_package'] : array();
        return !empty($package) ? $package : array();
    }

    private function aysa_technical_site_audit_get_canonical_execution_package($bundle) {
        $bundle = is_array($bundle) ? $bundle : array();
        if (isset($bundle['canonical_execution_package']) && is_array($bundle['canonical_execution_package']) && !empty($bundle['canonical_execution_package'])) {
            return $bundle['canonical_execution_package'];
        }
        $prepared = isset($bundle['last_prepare_result']) && is_array($bundle['last_prepare_result']) ? $bundle['last_prepare_result'] : array();
        $package = isset($prepared['canonical_execution_package']) && is_array($prepared['canonical_execution_package']) ? $prepared['canonical_execution_package'] : array();
        return !empty($package) ? $package : array();
    }

    private function aysa_technical_site_audit_build_internal_linking_execution_package($project_id, $snapshot, $bundle, $package_id) {
        $project_id = intval($project_id);
        $snapshot = is_array($snapshot) ? $snapshot : array();
        $bundle = is_array($bundle) ? $bundle : array();
        $package_id = sanitize_text_field((string) $package_id);
        $rows = $this->aysa_technical_site_audit_collect_bundle_rows($snapshot, $bundle);
        if (empty($rows)) {
            $rows = $this->aysa_technical_site_audit_discover_internal_linking_rows_from_wp($project_id);
        }
        $candidates = array();
        $seen = array();
        $manual = 0;
        $safe = 0;
        foreach ((array) $rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $url = $this->aysa_technical_site_audit_row_url($row);
            if ($url === '' || isset($seen[$url])) {
                continue;
            }
            $seen[$url] = true;
            $anchor = sanitize_text_field((string) ($row['suggested_anchor_text'] ?? $row['anchor_text'] ?? $row['keyword'] ?? ''));
            $sources = isset($row['suggested_source_pages']) && is_array($row['suggested_source_pages']) ? array_values($row['suggested_source_pages']) : array();
            $is_safe = !empty($sources) && $anchor !== '';
            $is_safe ? $safe++ : $manual++;
            $candidates[] = array(
                'url' => esc_url_raw($url),
                'page_type' => sanitize_key((string) ($row['page_type'] ?? $row['content_type'] ?? '')),
                'current_inlinks_count' => intval($row['inlinks'] ?? $row['inlinks_count'] ?? 0),
                'missing_inlinks' => true,
                'orphan_status' => 'missing_or_low_inlinks',
                'suggested_source_pages' => $sources,
                'suggested_anchor_text' => $anchor,
                'target_keywords' => $anchor !== '' ? array($anchor) : array(),
                'confidence' => $is_safe ? 88 : 45,
                'classification' => $is_safe ? 'safe_auto_apply' : 'manual_review_needed',
                'safe_to_apply' => $is_safe,
                'manual_review_required' => !$is_safe,
                'reason' => $is_safe ? 'source_page_contains_anchor_for_target' : 'source_pages_required_for_scoped_internal_linking',
                'inclusion_reason' => sanitize_text_field((string) ($row['inclusion_reason'] ?? 'weak_internal_linking_from_site_audit')),
            );
        }
        return array(
            'package_id' => $package_id,
            'project_id' => $project_id,
            'action_key' => 'prepare_internal_linking_plan',
            'issue_family' => 'internal_linking',
            'created_at' => current_time('mysql'),
            'source' => empty($rows) ? 'site_audit_empty' : 'site_audit_internal_linking_rows',
            'candidates' => array_values($candidates),
            'row_results' => array(),
            'total_candidates' => count($candidates),
            'safe_auto_apply_count' => $safe,
            'manual_review_needed_count' => $manual,
            'no_valid_source_count' => 0,
            'no_valid_target_count' => 0,
        );
    }

    private function aysa_technical_site_audit_discover_internal_linking_rows_from_wp($project_id) {
        $rows = array();
        $targets = get_posts(array(
            'post_type' => array('page', 'post', 'product'),
            'post_status' => 'publish',
            'numberposts' => 12,
            'orderby' => 'modified',
            'order' => 'DESC',
        ));
        $sources = get_posts(array(
            'post_type' => array('page', 'post'),
            'post_status' => 'publish',
            'numberposts' => 20,
            'orderby' => 'modified',
            'order' => 'DESC',
        ));
        foreach ((array) $targets as $target) {
            if (!$target instanceof WP_Post) {
                continue;
            }
            $target_url = get_permalink($target);
            $anchor = sanitize_text_field((string) $target->post_title);
            if (!$target_url || $anchor === '') {
                continue;
            }
            $suggested = array();
            foreach ((array) $sources as $source) {
                if (!$source instanceof WP_Post || intval($source->ID) === intval($target->ID)) {
                    continue;
                }
                $content = wp_strip_all_tags((string) $source->post_content);
                if ($content !== '' && stripos($content, $anchor) !== false) {
                    $suggested[] = array('url' => esc_url_raw((string) get_permalink($source)), 'anchor_text' => $anchor);
                    break;
                }
            }
            $rows[] = array(
                'url' => esc_url_raw((string) $target_url),
                'page_type' => sanitize_key((string) $target->post_type),
                'inlinks_count' => 0,
                'suggested_anchor_text' => $anchor,
                'suggested_source_pages' => $suggested,
                'inclusion_reason' => empty($suggested) ? 'wp_fallback_target_without_safe_source' : 'wp_fallback_source_contains_target_anchor',
            );
        }
        return $rows;
    }

    private function aysa_technical_site_audit_discover_image_alt_items_from_wp($project_id) {
        $project_id = intval($project_id);
        $items = array();
        $posts = get_posts(array(
            'post_type' => array('page', 'post', 'product'),
            'post_status' => 'publish',
            'numberposts' => 24,
            'orderby' => 'modified',
            'order' => 'DESC',
        ));
        foreach ((array) $posts as $post) {
            if (!$post instanceof WP_Post) {
                continue;
            }
            $post_id = intval($post->ID);
            $page_url = get_permalink($post);
            if (!$page_url) {
                continue;
            }
            $primary_keyword = $this->aysa_technical_site_audit_primary_keyword($post_id);
            $secondary_keywords = $this->aysa_technical_site_audit_secondary_keywords($post_id, $primary_keyword);
            $attachment_ids = array();
            $thumbnail_id = intval(get_post_thumbnail_id($post_id));
            if ($thumbnail_id > 0) {
                $attachment_ids[] = $thumbnail_id;
            }
            if (preg_match_all('/wp-image-(\d+)/', (string) $post->post_content, $matches) && !empty($matches[1])) {
                foreach ((array) $matches[1] as $match_id) {
                    $attachment_ids[] = intval($match_id);
                }
            }
            $attachment_ids = array_values(array_unique(array_filter(array_map('intval', $attachment_ids))));
            foreach ($attachment_ids as $attachment_id) {
                if ($attachment_id <= 0) {
                    continue;
                }
                $image_url = wp_get_attachment_url($attachment_id);
                if (!$image_url) {
                    continue;
                }
                $image_url = esc_url_raw((string) $image_url);
                if ($image_url === '' || isset($items[$image_url])) {
                    continue;
                }
                $existing_alt = sanitize_text_field((string) get_post_meta($attachment_id, '_wp_attachment_image_alt', true));
                $items[$image_url] = array(
                    'attachment_id' => $attachment_id,
                    'existing_alt' => $existing_alt,
                    'page_url' => esc_url_raw((string) $page_url),
                    'page_title' => sanitize_text_field((string) $post->post_title),
                    'primary_keyword' => $primary_keyword,
                    'secondary_keywords' => $secondary_keywords,
                    'image_filename' => sanitize_text_field(basename((string) parse_url($image_url, PHP_URL_PATH))),
                    'source' => 'wp_featured_or_embedded_image_fallback',
                );
            }
        }
        return $items;
    }

    private function aysa_technical_site_audit_discover_canonical_posts_from_wp($project_id) {
        $posts = array();
        $items = get_posts(array(
            'post_type' => array('page', 'post', 'product'),
            'post_status' => 'publish',
            'numberposts' => 24,
            'orderby' => 'modified',
            'order' => 'DESC',
        ));
        foreach ((array) $items as $post) {
            if (!$post instanceof WP_Post) {
                continue;
            }
            $permalink = get_permalink($post);
            if (!$permalink) {
                continue;
            }
            $posts[esc_url_raw((string) $permalink)] = array(
                'post_id' => intval($post->ID),
                'post_type' => sanitize_key((string) $post->post_type),
                'post_status' => sanitize_key((string) $post->post_status),
                'page_title' => sanitize_text_field((string) $post->post_title),
                'permalink' => esc_url_raw((string) $permalink),
                'current_canonical_url' => $this->aysa_technical_site_audit_current_post_canonical_url($post->ID),
                'canonical_provider' => $this->aysa_technical_site_audit_detect_canonical_provider($post->ID),
                'gsc_signals' => array(),
                'source' => 'wp_canonical_mapping_fallback',
            );
        }
        return $posts;
    }

    private function aysa_technical_site_audit_apply_redirect_package($project_id, $package) {
        $project_id = intval($project_id);
        $package = is_array($package) ? $package : array();
        $results = array();
        $applied = 0;
        $skipped = 0;
        $failed = 0;
        $manual = 0;
        foreach ((array) ($package['candidates'] ?? array()) as $candidate) {
            if (!is_array($candidate)) {
                continue;
            }
            $source = esc_url_raw((string) ($candidate['source_url'] ?? ''));
            $target = esc_url_raw((string) ($candidate['proposed_target_url'] ?? ''));
            $classification = sanitize_key((string) ($candidate['classification'] ?? 'no_valid_target'));
            if ($classification !== 'safe_auto_apply') {
                $classification === 'manual_review_needed' ? $manual++ : $skipped++;
                $results[] = array_merge($candidate, array(
                    'status' => $classification === 'manual_review_needed' ? 'manual_needed' : 'skipped',
                    'reason' => sanitize_text_field((string) ($candidate['reason'] ?? 'redirect_target_not_safe_for_auto_apply')),
                ));
                continue;
            }
            if ($source === '' || $target === '') {
                $skipped++;
                $results[] = array_merge($candidate, array('status' => 'skipped', 'reason' => 'redirect_source_or_target_missing'));
                continue;
            }
            if (!$this->aysa_technical_site_audit_redirect_source_is_safe($source)) {
                $skipped++;
                $results[] = array_merge($candidate, array('status' => 'skipped', 'reason' => 'redirect_source_protected_path'));
                continue;
            }
            if (!$this->aysa_technical_site_audit_redirect_target_is_safe($source, $target)) {
                $skipped++;
                $results[] = array_merge($candidate, array('status' => 'skipped', 'reason' => 'redirect_target_not_safe_for_auto_apply'));
                continue;
            }
            if ($this->aysa_technical_site_audit_redirect_rule_exists($source, $target)) {
                $applied++;
                $results[] = array_merge($candidate, array('status' => 'already_present', 'shared_executor' => 'apply_technical_redirect_candidate'));
                continue;
            }
            $result = $this->aysa_agent8_apply_technical_redirect_candidate($project_id, array('source_url' => $source, 'target_url' => $target));
            if (!empty($result['applied'])) {
                $applied++;
                $results[] = array_merge($candidate, array('status' => 'success', 'shared_executor' => 'apply_technical_redirect_candidate'));
            } else {
                $failed++;
                $results[] = array_merge($candidate, array('status' => 'failed', 'reason' => sanitize_text_field((string) ($result['reason'] ?? 'redirect_apply_failed'))));
            }
        }
        return array(
            'shared_executor' => 'apply_technical_redirect_candidate',
            'executor_path' => 'Site Audit worker -> aysa_agent8_apply_technical_redirect_candidate -> aysa_add_htaccess_redirect_rule',
            'package_id' => sanitize_text_field((string) ($package['package_id'] ?? '')),
            'total_candidates' => intval($package['total_candidates'] ?? count((array) ($package['candidates'] ?? array()))),
            'safe_auto_apply_count' => intval($package['safe_auto_apply_count'] ?? 0),
            'manual_review_needed_count' => intval($package['manual_review_needed_count'] ?? 0),
            'no_valid_target_count' => intval($package['no_valid_target_count'] ?? 0),
            'applied_count' => $applied,
            'skipped_count' => $skipped,
            'failed_count' => $failed,
            'manual_needed_count' => $manual,
            'results' => $results,
        );
    }

    private function aysa_technical_site_audit_redirect_rule_exists($source, $target) {
        $source = $this->aysa_normalize_redirect_source($source);
        $target = $this->aysa_normalize_redirect_destination($target);
        if ($source === '' || $target === '') {
            return false;
        }
        $htaccess_file = ABSPATH . '.htaccess';
        if (!file_exists($htaccess_file)) {
            return false;
        }
        $content = file_get_contents($htaccess_file);
        if (!is_string($content)) {
            return false;
        }
        $rule = 'RedirectMatch 301 ^' . preg_quote($source, '/') . '$ ' . $target;
        return strpos($content, $rule) !== false;
    }

    private function aysa_technical_site_audit_apply_redirect_rows($project_id, $rows) {
        $results = array();
        $applied = 0;
        $skipped = 0;
        $failed = 0;
        foreach ((array) $rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $source = $this->aysa_technical_site_audit_row_url($row);
            $target = '';
            foreach (array('target_url', 'redirect_url', 'destination', 'final_url', 'canonical_url') as $key) {
                $target = esc_url_raw((string) ($row[$key] ?? ''));
                if ($target !== '') {
                    break;
                }
            }
            if ($source === '' || $target === '' || untrailingslashit($source) === untrailingslashit($target)) {
                $skipped++;
                $results[] = array('url' => $source, 'target_url' => $target, 'status' => 'skipped', 'reason' => 'safe_redirect_target_missing');
                continue;
            }
            $result = $this->aysa_agent8_apply_technical_redirect_candidate($project_id, array('source_url' => $source, 'target_url' => $target));
            if (!empty($result['applied'])) {
                $applied++;
                $results[] = array('url' => $source, 'target_url' => $target, 'status' => 'success', 'shared_executor' => 'apply_technical_redirect_candidate');
            } else {
                $failed++;
                $results[] = array('url' => $source, 'target_url' => $target, 'status' => 'failed', 'reason' => sanitize_text_field((string) ($result['reason'] ?? 'redirect_apply_failed')));
            }
        }
        return array('shared_executor' => 'apply_technical_redirect_candidate', 'applied_count' => $applied, 'skipped_count' => $skipped, 'failed_count' => $failed, 'manual_needed_count' => 0, 'results' => $results);
    }

    private function aysa_technical_site_audit_apply_indexability_rows($project_id, $rows) {
        $results = array();
        $applied = 0;
        $skipped = 0;
        $manual = 0;
        foreach ((array) $rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $url = $this->aysa_technical_site_audit_row_url($row);
            $issue_type = sanitize_key((string) ($row['site_audit_issue_type'] ?? ''));
            if ($url === '' || strpos($issue_type, 'canonicals_missing') === false) {
                $manual++;
                $results[] = array('url' => $url, 'status' => 'manual_needed', 'reason' => 'unsafe_indexability_or_canonical_change_requires_review');
                continue;
            }
            $post_id = url_to_postid($url);
            if ($post_id <= 0) {
                $skipped++;
                $results[] = array('url' => $url, 'status' => 'skipped', 'reason' => 'no_clear_wordpress_post_mapping');
                continue;
            }
            $canonical = get_permalink($post_id);
            if (!$canonical) {
                $skipped++;
                $results[] = array('url' => $url, 'post_id' => $post_id, 'status' => 'skipped', 'reason' => 'canonical_permalink_unavailable');
                continue;
            }
            $manual++;
            $results[] = array('url' => $url, 'post_id' => $post_id, 'canonical_url' => esc_url_raw($canonical), 'status' => 'manual_needed', 'reason' => 'indexability_canonical_executor_not_mature', 'shared_executor' => 'site_audit_indexability_canonical_adapter');
        }
        return array('shared_executor' => 'site_audit_indexability_canonical_adapter', 'applied_count' => $applied, 'skipped_count' => $skipped, 'failed_count' => 0, 'manual_needed_count' => $manual, 'results' => $results);
    }

    private function aysa_technical_site_audit_apply_canonical_package($project_id, $package) {
        $package = is_array($package) ? $package : array();
        $results = array();
        $applied = 0;
        $skipped = 0;
        $failed = 0;
        $manual = 0;
        foreach ((array) ($package['items'] ?? array()) as $row) {
            if (!is_array($row)) {
                continue;
            }
            $post_id = intval($row['post_id'] ?? 0);
            $page_url = esc_url_raw((string) ($row['page_url'] ?? ''));
            $target_url = esc_url_raw((string) ($row['recommended_target_url'] ?? $row['proposed_value'] ?? ''));
            $action = sanitize_key((string) ($row['recommended_action'] ?? 'manual_review'));
            $confidence = intval($row['confidence'] ?? 0);
            $provider = sanitize_key((string) ($row['canonical_provider'] ?? ''));
            if ($post_id <= 0 || $target_url === '') {
                $skipped++;
                $results[] = array('page_url' => $page_url, 'post_id' => $post_id, 'status' => 'skipped', 'reason' => 'canonical_target_or_post_missing');
                continue;
            }
            if (!in_array($action, array('self_canonical', 'rewrite'), true) || $confidence < 70) {
                $manual++;
                $results[] = array('page_url' => $page_url, 'post_id' => $post_id, 'status' => 'manual_needed', 'reason' => 'canonical_requires_manual_review');
                continue;
            }
            $update = $this->aysa_technical_site_audit_update_post_canonical_url($post_id, $target_url, $provider);
            if (!empty($update['applied'])) {
                $applied++;
                $results[] = array(
                    'page_url' => $page_url,
                    'post_id' => $post_id,
                    'status' => 'success',
                    'shared_executor' => 'site_audit_canonical_adapter',
                    'recommended_action' => $action,
                    'expected_canonical' => $target_url,
                    'meta_key' => sanitize_text_field((string) ($update['meta_key'] ?? '')),
                );
            } else {
                $failed++;
                $results[] = array('page_url' => $page_url, 'post_id' => $post_id, 'status' => 'failed', 'reason' => sanitize_text_field((string) ($update['reason'] ?? 'canonical_update_failed')));
            }
        }
        foreach ((array) ($package['skipped'] ?? array()) as $row) {
            if (!is_array($row)) {
                continue;
            }
            $decision = sanitize_key((string) ($row['decision'] ?? ($row['candidate_quality']['decision'] ?? 'manual_review')));
            if ($decision === 'preserve') {
                $skipped++;
                $results[] = array('page_url' => esc_url_raw((string) ($row['page_url'] ?? '')), 'status' => 'skipped', 'reason' => 'canonical_already_good');
            } else {
                $manual++;
                $results[] = array('page_url' => esc_url_raw((string) ($row['page_url'] ?? '')), 'status' => 'manual_needed', 'reason' => sanitize_key((string) ($row['reason'] ?? 'canonical_requires_manual_review')));
            }
        }
        return array(
            'shared_executor' => 'site_audit_canonical_adapter',
            'applied_count' => $applied,
            'skipped_count' => $skipped,
            'failed_count' => $failed,
            'manual_needed_count' => $manual,
            'results' => $results,
        );
    }

    private function aysa_technical_site_audit_apply_internal_linking_package($project_id, $package) {
        $package = is_array($package) ? $package : array();
        $candidates = isset($package['candidates']) && is_array($package['candidates']) ? $package['candidates'] : array();
        if (!class_exists('Aysa_Internal_Linking')) {
            return array('shared_executor' => 'Aysa_Internal_Linking::process_site_audit_scoped_targets', 'applied_count' => 0, 'skipped_count' => count($candidates), 'failed_count' => 0, 'manual_needed_count' => count($candidates), 'reason' => 'internal_linking_engine_unavailable', 'results' => array(), 'global_mode_used' => false);
        }
        $engine = new Aysa_Internal_Linking();
        if (!method_exists($engine, 'process_site_audit_scoped_targets')) {
            return array('shared_executor' => 'Aysa_Internal_Linking::process_site_audit_scoped_targets', 'applied_count' => 0, 'skipped_count' => count($candidates), 'failed_count' => 0, 'manual_needed_count' => count($candidates), 'reason' => 'scoped_internal_linking_apply_method_unavailable', 'results' => array(), 'global_mode_used' => false);
        }
        $result = $engine->process_site_audit_scoped_targets($package, false);
        $result = is_array($result) ? $result : array();
        return array_merge(array(
            'shared_executor' => 'Aysa_Internal_Linking::process_site_audit_scoped_targets',
            'applied_count' => 0,
            'skipped_count' => 0,
            'failed_count' => 0,
            'manual_needed_count' => 0,
            'results' => array(),
            'global_mode_used' => false,
            'reason' => '',
        ), $result);
    }

    private function aysa_technical_site_audit_apply_image_alt_package($project_id, $package) {
        $package = is_array($package) ? $package : array();
        $results = array();
        $applied = 0;
        $skipped = 0;
        $failed = 0;
        $manual = 0;
        foreach ((array) ($package['items'] ?? array()) as $row) {
            if (!is_array($row)) {
                continue;
            }
            $attachment_id = intval($row['attachment_id'] ?? 0);
            $url = esc_url_raw((string) ($row['image_url'] ?? ''));
            $decision = sanitize_key((string) ($row['decision'] ?? $row['overwrite_decision'] ?? 'rewrite'));
            $confidence = intval($row['confidence'] ?? 0);
            $proposed = sanitize_text_field((string) ($row['proposed_value'] ?? ''));
            if ($decision !== 'rewrite') {
                $manual++;
                $results[] = array_merge($row, array('status' => 'manual_needed', 'reason' => 'image_alt_not_marked_for_rewrite'));
                continue;
            }
            if ($attachment_id <= 0) {
                $skipped++;
                $results[] = array_merge($row, array('status' => 'skipped', 'reason' => 'attachment_mapping_missing'));
                continue;
            }
            if (!empty($row['is_decorative'])) {
                $manual++;
                $results[] = array_merge($row, array('status' => 'manual_needed', 'reason' => 'decorative_image_not_auto_written'));
                continue;
            }
            if ($confidence < 70) {
                $manual++;
                $results[] = array_merge($row, array('status' => 'manual_needed', 'reason' => 'image_alt_confidence_below_auto_apply_threshold'));
                continue;
            }
            if ($proposed === '') {
                $skipped++;
                $results[] = array_merge($row, array('status' => 'skipped', 'reason' => 'empty_proposed_alt'));
                continue;
            }
            $current = trim((string) get_post_meta($attachment_id, '_wp_attachment_image_alt', true));
            if ($current !== '' && $current === $proposed) {
                $applied++;
                $results[] = array_merge($row, array('status' => 'already_present', 'current_value' => $current));
                continue;
            }
            if ($current !== '' && $current !== trim((string) ($row['old_value'] ?? ''))) {
                $skipped++;
                $results[] = array_merge($row, array('status' => 'skipped', 'reason' => 'existing_alt_changed_since_prepare', 'current_value' => $current));
                continue;
            }
            $updated = update_post_meta($attachment_id, '_wp_attachment_image_alt', $proposed);
            if ($updated === false && trim((string) get_post_meta($attachment_id, '_wp_attachment_image_alt', true)) !== $proposed) {
                $failed++;
                $results[] = array_merge($row, array('status' => 'failed', 'reason' => 'attachment_alt_write_failed'));
            } else {
                $applied++;
                $results[] = array_merge($row, array('status' => 'success', 'current_value' => $proposed, 'shared_executor' => 'wordpress_media_alt_text_adapter'));
            }
        }
        foreach ((array) ($package['skipped'] ?? array()) as $row) {
            if (!is_array($row)) {
                continue;
            }
            $decision = sanitize_key((string) ($row['decision'] ?? ($row['candidate_quality']['decision'] ?? 'manual_review')));
            if (in_array($decision, array('manual_review', 'decorative_ignore'), true)) {
                $manual++;
                $results[] = array_merge($row, array('status' => 'manual_needed', 'reason' => sanitize_key((string) ($row['reason'] ?? 'image_alt_review_needed'))));
            } else {
                $skipped++;
                $results[] = array_merge($row, array('status' => 'skipped', 'reason' => sanitize_key((string) ($row['reason'] ?? 'image_alt_preserved'))));
            }
        }
        return array(
            'shared_executor' => 'wordpress_media_alt_text_adapter',
            'executor_path' => 'Site Audit worker -> image alt package -> _wp_attachment_image_alt',
            'package_id' => sanitize_text_field((string) ($package['package_id'] ?? '')),
            'total_candidates' => intval($package['total_candidates'] ?? 0),
            'safe_auto_apply_count' => intval($package['safe_auto_apply_count'] ?? 0),
            'manual_review_needed_count' => intval($package['manual_review_needed_count'] ?? 0),
            'decorative_ignore_count' => intval($package['decorative_ignore_count'] ?? 0),
            'preserve_count' => intval($package['preserve_count'] ?? 0),
            'applied_count' => $applied,
            'skipped_count' => $skipped,
            'failed_count' => $failed,
            'manual_needed_count' => $manual,
            'results' => $results
        );
    }

    private function aysa_technical_site_audit_find_action_bundle($snapshot, $action_key) {
        $bundles = isset($snapshot['conclusion']['recommended_actions']) && is_array($snapshot['conclusion']['recommended_actions'])
            ? $snapshot['conclusion']['recommended_actions']
            : array();
        foreach ($bundles as $bundle) {
            if (is_array($bundle) && sanitize_key((string) ($bundle['action_key'] ?? '')) === sanitize_key((string) $action_key)) {
                return $bundle;
            }
        }
        return array();
    }

    private function aysa_technical_site_audit_apply_metadata_remediation($project_id, $payload = array()) {
        $project_id = intval($project_id);
        $payload = is_array($payload) ? $payload : array();
        $snapshot = $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id);
        $bundle = $this->aysa_technical_site_audit_find_action_bundle($snapshot, 'prepare_metadata_remediation');
        if (empty($bundle)) {
            return array('applied' => false, 'reason' => 'metadata_bundle_not_available');
        }
        $package_id = sanitize_text_field((string) ($bundle['execution_package_id'] ?? ''));
        if ($package_id === '') {
            $package_id = 'site_audit_' . $project_id . '_prepare_metadata_remediation_' . substr(md5(current_time('mysql')), 0, 10);
        }
        $package = !empty($bundle['metadata_execution_package']) && is_array($bundle['metadata_execution_package'])
            ? $bundle['metadata_execution_package']
            : $this->aysa_technical_site_audit_build_metadata_execution_package($project_id, $snapshot, $bundle, $package_id);

        $results = array();
        $applied_count = 0;
        $skipped_count = 0;
        $failed_count = 0;
        $onpage_service = $this->aysa_onpage_workspace_service();
        if (!$onpage_service || !method_exists($onpage_service, 'apply_projection') || !method_exists($onpage_service, 'apply_meta_output')) {
            $this->aysa_agent8_create_site_audit_communication_event(
                $project_id,
                'site_audit_metadata_apply_failed',
                'Agent8 Site Audit: ' . $this->aysa_technical_site_audit_action_subject_label('prepare_metadata_remediation', $package) . ' blocked',
                "I tried to apply the Site Audit metadata package, but the shared On-Page metadata workspace is unavailable. I did not fall back to a local shortcut.",
                array('action_key' => 'prepare_metadata_remediation', 'reason' => 'onpage_workspace_service_unavailable'),
                'warning'
            );
            return array('applied' => false, 'reason' => 'onpage_workspace_service_unavailable');
        }
        foreach ((array) ($package['items'] ?? array()) as $item) {
            if (!is_array($item)) {
                continue;
            }
            $post_id = intval($item['post_id'] ?? 0);
            $field = sanitize_key((string) ($item['field'] ?? ''));
            $new_value = sanitize_text_field((string) ($item['proposed_value'] ?? ''));
            $old_value = sanitize_text_field((string) ($item['old_value'] ?? ''));
            $post = $post_id > 0 ? get_post($post_id) : null;
            $validation = $this->aysa_technical_site_audit_validate_metadata_proposal($field, $new_value);
            if ($post_id <= 0 || !$post || !$validation['ok']) {
                $skipped_count++;
                $results[] = array(
                    'url' => esc_url_raw((string) ($item['url'] ?? '')),
                    'post_id' => $post_id,
                    'field' => $field,
                    'old_value' => $old_value,
                    'new_value' => $new_value,
                    'status' => 'skipped',
                    'reason' => $post_id <= 0 ? 'missing_post_id' : (!$post ? 'post_not_found' : $validation['reason']),
                );
                continue;
            }
            if ($old_value === $new_value) {
                $skipped_count++;
                $results[] = array(
                    'url' => esc_url_raw((string) ($item['url'] ?? '')),
                    'post_id' => $post_id,
                    'field' => $field,
                    'old_value' => $old_value,
                    'new_value' => $new_value,
                    'status' => 'skipped',
                    'reason' => 'already_matches_proposal',
                );
                continue;
            }
            $entity_key = sanitize_text_field((string) ($item['entity_key'] ?? ('post:' . $post_id)));
            $projection_row = $this->aysa_technical_site_audit_build_onpage_metadata_projection_row(
                $project_id,
                $item,
                $post,
                intval($payload['worker_run_id'] ?? 0)
            );
            $projection = $onpage_service->apply_projection($project_id, intval($payload['worker_run_id'] ?? 0), array(
                'run_type' => 'technical_site_audit_metadata_remediation',
                'run_status' => 'completed',
                'current_task_key' => 'technical_site_audit_metadata_apply',
                'current_milestone' => 'Site Audit metadata remediation package synced to On-Page workspace',
                'inventory_rows' => array($projection_row),
                'summary' => array(
                    'source' => 'site_audit_metadata_remediation',
                    'package_id' => $package_id,
                    'target_field' => $field,
                ),
            ));
            $onpage_result = !empty($projection['ok'])
                ? $onpage_service->apply_meta_output($project_id, $entity_key)
                : array('ok' => false, 'error' => 'onpage_projection_sync_failed');
            if (!empty($onpage_result['ok'])) {
                $applied_count++;
                $results[] = array(
                    'url' => esc_url_raw((string) ($item['url'] ?? '')),
                    'post_id' => $post_id,
                    'entity_key' => $entity_key,
                    'field' => $field,
                    'old_value' => $old_value,
                    'new_value' => $new_value,
                    'status' => 'success',
                    'reason' => '',
                    'shared_metadata_path' => 'onpage_workspace_apply_meta_output',
                    'onpage_result' => $onpage_result,
                );
            } else {
                $failed_count++;
                $results[] = array(
                    'url' => esc_url_raw((string) ($item['url'] ?? '')),
                    'post_id' => $post_id,
                    'entity_key' => $entity_key,
                    'field' => $field,
                    'old_value' => $old_value,
                    'new_value' => $new_value,
                    'status' => 'failed',
                    'reason' => sanitize_text_field((string) ($onpage_result['error'] ?? 'onpage_metadata_apply_failed')),
                    'shared_metadata_path' => 'onpage_workspace_apply_meta_output',
                );
            }
        }
        $result = array(
            'result_type' => 'worker_applied_metadata_via_onpage_workspace',
            'worker_run_id' => intval($payload['worker_run_id'] ?? 0),
            'worker_task_id' => intval($payload['worker_task_id'] ?? 0),
            'automatic_site_mutation' => true,
            'shared_executor' => 'OnPageWorkspaceService::apply_meta_output',
            'executor_path' => 'Site Audit worker -> On-Page metadata workspace -> apply_meta_output',
            'shared_metadata_path' => 'onpage_workspace_apply_meta_output',
            'applied_count' => $applied_count,
            'skipped_count' => $skipped_count + intval($package['skipped_count'] ?? 0),
            'failed_count' => $failed_count,
            'manual_needed_count' => intval($package['skipped_count'] ?? 0),
            'results' => array_values($results),
            'package_skipped' => isset($package['skipped']) && is_array($package['skipped']) ? array_values($package['skipped']) : array(),
        );
        $result = $this->aysa_technical_site_audit_normalize_apply_result('prepare_metadata_remediation', $package_id, $result);
        $result['communication_event'] = $this->aysa_agent8_create_site_audit_communication_event(
            $project_id,
            $failed_count > 0 ? 'site_audit_metadata_apply_completed_with_warnings' : 'site_audit_metadata_apply_completed',
            'Agent8 Site Audit: ' . $this->aysa_technical_site_audit_action_subject_label('prepare_metadata_remediation', $package) . ' completed',
            sprintf(
                "I finished the metadata batch.\n\nApplied: %d pages\nSkipped: %d pages\nFailed: %d pages\n\nThe applied changes used the shared On-Page metadata path and were recorded per page. Skipped items need manual review or a missing shared mapping.",
                $applied_count,
                $skipped_count + intval($package['skipped_count'] ?? 0),
                $failed_count
            ),
            array(
                'action_key' => 'prepare_metadata_remediation',
                'package_id' => $package_id,
                'shared_metadata_path' => 'onpage_workspace_apply_meta_output',
                'applied_count' => $applied_count,
                'skipped_count' => $skipped_count + intval($package['skipped_count'] ?? 0),
                'failed_count' => $failed_count,
            ),
            $failed_count > 0 ? 'warning' : 'info'
        );
        $state = $failed_count > 0 && $applied_count <= 0 ? 'failed' : 'applied';
        $this->aysa_adjustments_update_site_audit_action_bundle_state($project_id, 'prepare_metadata_remediation', array(
            'approval_state' => $state,
            'execution_state' => $state,
            'execution_package_id' => $package_id,
            'metadata_execution_package' => $package,
            'last_applied_at' => current_time('mysql'),
            'last_apply_result' => $result,
            'last_error' => $state === 'failed' ? 'metadata_worker_apply_failed' : '',
        ));
        return array(
            'applied' => true,
            'stage' => 'apply',
            'execution_state' => $state,
            'action_key' => 'prepare_metadata_remediation',
            'execution_package_id' => $package_id,
            'applied_count' => $applied_count,
            'skipped_count' => $result['skipped_count'],
            'failed_count' => $failed_count,
            'apply_result' => $result,
        );
    }

    private function aysa_technical_site_audit_verify_metadata_remediation($project_id, $bundle, $payload = array()) {
        $project_id = intval($project_id);
        $bundle = is_array($bundle) ? $bundle : array();
        $apply_result = isset($bundle['last_apply_result']) && is_array($bundle['last_apply_result']) ? $bundle['last_apply_result'] : array();
        $verified = 0;
        $checked = 0;
        $rows = array();
        foreach ((array) ($apply_result['results'] ?? array()) as $row) {
            if (!is_array($row) || sanitize_key((string) ($row['status'] ?? '')) !== 'success') {
                continue;
            }
            $post_id = intval($row['post_id'] ?? 0);
            $field = sanitize_key((string) ($row['field'] ?? ''));
            $expected = sanitize_text_field((string) ($row['new_value'] ?? ''));
            if ($post_id <= 0 || $expected === '') {
                continue;
            }
            $checked++;
            $current = $field === 'meta_title'
                ? trim((string) get_post_meta($post_id, '_aysa_meta_title', true))
                : trim((string) get_post_meta($post_id, '_aysa_meta_description', true));
            $matches = $current === $expected;
            if ($matches) {
                $verified++;
            }
            $rows[] = array(
                'url' => esc_url_raw((string) ($row['url'] ?? '')),
                'post_id' => $post_id,
                'field' => $field,
                'expected_value' => $expected,
                'current_value' => sanitize_text_field($current),
                'verified' => $matches,
            );
        }
        $is_verified = $checked > 0 && $verified === $checked;
        $verify_result = $this->aysa_technical_site_audit_normalize_verification_result(array(
            'verification_method' => 'live_wordpress_metadata_readback',
            'checked_count' => $checked,
            'verified_count' => $verified,
            'verified' => $is_verified,
            'evidence' => $is_verified
                ? 'Live WordPress metadata readback matches all worker-applied metadata values.'
                : 'Live WordPress metadata readback does not yet confirm every applied metadata value.',
            'rows' => $rows,
        ));
        $verify_result['communication_event'] = $this->aysa_agent8_create_site_audit_communication_event(
            $project_id,
            $is_verified ? 'site_audit_metadata_verify_completed' : 'site_audit_metadata_verify_pending',
            $is_verified ? 'Agent8 Site Audit: ' . $this->aysa_technical_site_audit_action_subject_label('prepare_metadata_remediation', $bundle['metadata_execution_package'] ?? array()) . ' verified' : 'Agent8 Site Audit: ' . $this->aysa_technical_site_audit_action_subject_label('prepare_metadata_remediation', $bundle['metadata_execution_package'] ?? array()) . ' verification pending',
            $is_verified
                ? sprintf("I checked the live WordPress metadata and verified all %d applied metadata changes. The Site Audit metadata package is confirmed by live readback.", $checked)
                : sprintf("I checked the live WordPress metadata, but only %d of %d applied changes are confirmed. I will not mark this fully verified without evidence.", $verified, $checked),
            array(
                'action_key' => 'prepare_metadata_remediation',
                'checked_count' => $checked,
                'verified_count' => $verified,
                'verification_method' => 'live_wordpress_metadata_readback',
            ),
            $is_verified ? 'info' : 'warning'
        );
        $this->aysa_adjustments_update_site_audit_action_bundle_state($project_id, 'prepare_metadata_remediation', array(
            'execution_state' => $is_verified ? 'verified' : 'verification_pending',
            'approval_state' => $is_verified ? 'verified' : 'verification_pending',
            'last_verified_at' => current_time('mysql'),
            'last_verify_result' => $verify_result,
            'last_error' => '',
        ));
        return array(
            'applied' => true,
            'stage' => 'verify',
            'execution_state' => $is_verified ? 'verified' : 'verification_pending',
            'action_key' => 'prepare_metadata_remediation',
            'execution_package_id' => sanitize_text_field((string) ($bundle['execution_package_id'] ?? '')),
            'verification' => $verify_result,
        );
    }

    private function aysa_technical_site_audit_verify_response_code_review($project_id, $bundle, $payload = array()) {
        $project_id = intval($project_id);
        $bundle = is_array($bundle) ? $bundle : array();
        $apply_result = isset($bundle['last_apply_result']) && is_array($bundle['last_apply_result']) ? $bundle['last_apply_result'] : array();
        $checked = 0;
        $verified = 0;
        $rows = array();
        foreach ((array) ($apply_result['results'] ?? array()) as $row) {
            if (!is_array($row) || !in_array(sanitize_key((string) ($row['status'] ?? '')), array('success', 'already_present'), true)) {
                continue;
            }
            $source = esc_url_raw((string) ($row['source_url'] ?? ''));
            $target = esc_url_raw((string) ($row['proposed_target_url'] ?? ''));
            if ($source === '' || $target === '') {
                continue;
            }
            $checked++;
            $exists = $this->aysa_technical_site_audit_redirect_rule_exists($source, $target);
            if ($exists) {
                $verified++;
            }
            $rows[] = array(
                'source_url' => $source,
                'target_url' => $target,
                'verified' => $exists,
                'verification_method' => 'htaccess_redirect_rule_readback',
            );
        }
        $is_verified = $checked > 0 && $verified === $checked;
        $verify_result = $this->aysa_technical_site_audit_normalize_verification_result(array(
            'verification_method' => 'htaccess_redirect_rule_readback',
            'checked_count' => $checked,
            'verified_count' => $verified,
            'verified' => $is_verified,
            'evidence' => $is_verified
                ? 'The applied redirect rules are present in the shared redirect executor storage.'
                : 'No complete redirect readback evidence exists yet; not marking this package verified.',
            'rows' => $rows,
        ));
        if (empty($payload['suppress_plugin_communication'])) {
            $verify_result['communication_event'] = $this->aysa_agent8_create_site_audit_communication_event(
                $project_id,
                $is_verified ? 'site_audit_redirect_verify_completed' : 'site_audit_redirect_verify_pending',
                $is_verified ? 'Agent8 Site Audit: Response codes & redirects verified' : 'Agent8 Site Audit: Response codes & redirects verification pending',
                $is_verified
                    ? sprintf("I checked the redirect rules and verified all %d applied redirect changes. The Site Audit response-code package is confirmed by redirect readback.", $checked)
                    : sprintf("I checked the redirect rules, but only %d of %d applied redirects are confirmed. I will not mark this fully verified without evidence.", $verified, $checked),
                array(
                    'action_key' => 'prepare_response_code_review',
                    'checked_count' => $checked,
                    'verified_count' => $verified,
                    'verification_method' => 'htaccess_redirect_rule_readback',
                ),
                $is_verified ? 'info' : 'warning'
            );
        } else {
            $verify_result['communication_event'] = array('ok' => true, 'source' => 'suppressed_for_api_worker_shared_service');
        }
        $this->aysa_adjustments_update_site_audit_action_bundle_state($project_id, 'prepare_response_code_review', array(
            'execution_state' => $is_verified ? 'verified' : 'verification_pending',
            'approval_state' => $is_verified ? 'verified' : 'verification_pending',
            'last_verified_at' => current_time('mysql'),
            'last_verify_result' => $verify_result,
            'last_error' => '',
        ));
        return array(
            'applied' => true,
            'stage' => 'verify',
            'execution_state' => $is_verified ? 'verified' : 'verification_pending',
            'action_key' => 'prepare_response_code_review',
            'execution_package_id' => sanitize_text_field((string) ($bundle['execution_package_id'] ?? '')),
            'verification' => $verify_result,
        );
    }

    private function aysa_technical_site_audit_update_action_lifecycle($project_id, $action_key, $stage, $payload = array()) {
        $project_id = intval($project_id);
        $action_key = sanitize_key((string) $action_key);
        $stage = sanitize_key((string) $stage);
        $payload = is_array($payload) ? $payload : array();
        if ($project_id <= 0 || $action_key === '' || !$this->aysa_adjustments_is_site_audit_bundle_action_key($action_key)) {
            return array('applied' => false, 'reason' => 'invalid_action_context');
        }
        if (!in_array($stage, array('prepare', 'apply', 'verify', 'ignore'), true)) {
            return array('applied' => false, 'reason' => 'invalid_action_stage');
        }

        $snapshot = $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id);
        $bundles = isset($snapshot['conclusion']['recommended_actions']) && is_array($snapshot['conclusion']['recommended_actions'])
            ? $snapshot['conclusion']['recommended_actions']
            : array();
        $bundle = array();
        foreach ($bundles as $candidate) {
            if (is_array($candidate) && sanitize_key((string) ($candidate['action_key'] ?? '')) === $action_key) {
                $bundle = $candidate;
                break;
            }
        }
        if (empty($bundle)) {
            return array('applied' => false, 'reason' => 'action_bundle_not_available');
        }

        $now = current_time('mysql');
        $package_id = sanitize_text_field((string) ($bundle['execution_package_id'] ?? ''));
        if ($package_id === '') {
            $package_id = 'site_audit_' . $project_id . '_' . $action_key . '_' . substr(md5($now), 0, 10);
        }
        $affected_count = intval($bundle['affected_count'] ?? 0);
        $version_number = intval($snapshot['version_number'] ?? 0);
        $history_count = intval($snapshot['history_count'] ?? 0);

        if ($stage === 'prepare') {
            $package = array();
            if ($action_key === 'prepare_metadata_remediation') {
                $package = isset($payload['metadata_execution_package']) && is_array($payload['metadata_execution_package'])
                    ? $payload['metadata_execution_package']
                    : (isset($payload['execution_package']) && is_array($payload['execution_package'])
                        ? $payload['execution_package']
                        : $this->aysa_technical_site_audit_build_metadata_execution_package($project_id, $snapshot, $bundle, $package_id));
            } elseif ($action_key === 'prepare_response_code_review') {
                $package = isset($payload['redirect_execution_package']) && is_array($payload['redirect_execution_package'])
                    ? $payload['redirect_execution_package']
                    : (isset($payload['execution_package']) && is_array($payload['execution_package'])
                        ? $payload['execution_package']
                        : $this->aysa_technical_site_audit_build_redirect_execution_package($project_id, $snapshot, $bundle, $package_id));
            } elseif ($action_key === 'prepare_internal_linking_plan') {
                $package = isset($payload['internal_linking_execution_package']) && is_array($payload['internal_linking_execution_package'])
                    ? $payload['internal_linking_execution_package']
                    : (isset($payload['execution_package']) && is_array($payload['execution_package'])
                        ? $payload['execution_package']
                        : $this->aysa_technical_site_audit_build_internal_linking_execution_package($project_id, $snapshot, $bundle, $package_id));
            } elseif ($action_key === 'prepare_image_alt_text') {
                $package = isset($payload['image_alt_execution_package']) && is_array($payload['image_alt_execution_package'])
                    ? $payload['image_alt_execution_package']
                    : (isset($payload['execution_package']) && is_array($payload['execution_package'])
                        ? $payload['execution_package']
                        : array());
            } elseif ($action_key === 'prepare_indexability_review') {
                $package = isset($payload['canonical_execution_package']) && is_array($payload['canonical_execution_package'])
                    ? $payload['canonical_execution_package']
                    : (isset($payload['execution_package']) && is_array($payload['execution_package'])
                        ? $payload['execution_package']
                        : array());
            }
            if (!empty($package['package_id'])) {
                $package_id = sanitize_text_field((string) $package['package_id']);
            }
            $prepare_result = array();
            if ($action_key === 'prepare_metadata_remediation') {
                $prepare_result = array(
                    'result_type' => 'metadata_execution_package_prepared',
                    'issue_family' => 'metadata',
                    'execution_package' => $package,
                    'total_candidates' => intval($package['total_candidates'] ?? 0),
                    'safe_auto_apply_count' => intval($package['safe_auto_apply_count'] ?? 0),
                    'manual_review_needed_count' => intval($package['manual_review_needed_count'] ?? 0),
                    'no_valid_target_count' => intval($package['no_valid_target_count'] ?? 0),
                    'package_items_count' => intval($package['items_count'] ?? 0),
                    'package_skipped_count' => intval($package['skipped_count'] ?? 0),
                    'automatic_site_mutation' => false,
                );
            } elseif ($action_key === 'prepare_response_code_review') {
                $prepare_result = array(
                    'result_type' => 'redirect_execution_package_prepared',
                    'issue_family' => 'response_codes_redirects',
                    'execution_package' => $package,
                    'redirect_execution_package' => $package,
                    'total_candidates' => intval($package['total_candidates'] ?? 0),
                    'safe_auto_apply_count' => intval($package['safe_auto_apply_count'] ?? 0),
                    'manual_review_needed_count' => intval($package['manual_review_needed_count'] ?? 0),
                    'no_valid_target_count' => intval($package['no_valid_target_count'] ?? 0),
                    'automatic_site_mutation' => false,
                );
            } elseif ($action_key === 'prepare_internal_linking_plan') {
                $prepare_result = array(
                    'result_type' => 'internal_linking_execution_package_prepared',
                    'issue_family' => 'internal_linking',
                    'execution_package' => $package,
                    'internal_linking_execution_package' => $package,
                    'total_candidates' => intval($package['total_candidates'] ?? 0),
                    'safe_auto_apply_count' => intval($package['safe_auto_apply_count'] ?? 0),
                    'manual_review_needed_count' => intval($package['manual_review_needed_count'] ?? 0),
                    'no_valid_target_count' => intval($package['no_valid_target_count'] ?? 0),
                    'automatic_site_mutation' => false,
                );
            } elseif ($action_key === 'prepare_image_alt_text') {
                $prepare_result = array(
                    'result_type' => 'image_alt_execution_package_prepared',
                    'issue_family' => 'image_alt',
                    'execution_package' => $package,
                    'image_alt_execution_package' => $package,
                    'total_candidates' => intval($package['total_candidates'] ?? 0),
                    'safe_auto_apply_count' => intval($package['safe_auto_apply_count'] ?? 0),
                    'manual_review_needed_count' => intval($package['manual_review_needed_count'] ?? 0),
                    'decorative_ignore_count' => intval($package['decorative_ignore_count'] ?? 0),
                    'preserve_count' => intval($package['preserve_count'] ?? 0),
                    'automatic_site_mutation' => false,
                );
            } elseif ($action_key === 'prepare_indexability_review') {
                $prepare_result = array(
                    'result_type' => 'canonical_execution_package_prepared',
                    'issue_family' => 'canonical',
                    'execution_package' => $package,
                    'canonical_execution_package' => $package,
                    'total_candidates' => intval($package['total_candidates'] ?? 0),
                    'self_canonical_count' => intval($package['self_canonical_count'] ?? 0),
                    'rewrite_count' => intval($package['rewrite_count'] ?? 0),
                    'manual_review_needed_count' => intval($package['manual_review_needed_count'] ?? 0),
                    'keep_count' => intval($package['keep_count'] ?? 0),
                    'automatic_site_mutation' => false,
                );
            }
            $changes = array(
                'approval_state' => 'prepared',
                'execution_state' => 'prepared',
                'execution_package_id' => $package_id,
                'last_prepared_at' => $now,
                'last_prepared_summary' => sprintf(
                    'Prepared %s work package for %d affected rows from Site Audit version %d.',
                    sanitize_text_field((string) ($bundle['bundle_type'] ?? $action_key)),
                    $affected_count,
                    $version_number
                ),
                'last_prepare_result' => $prepare_result,
                'metadata_execution_package' => $action_key === 'prepare_metadata_remediation' ? $package : array(),
                'canonical_execution_package' => $action_key === 'prepare_indexability_review' ? $package : array(),
                'image_alt_execution_package' => $action_key === 'prepare_image_alt_text' ? $package : array(),
                'last_error' => '',
            );
            $this->aysa_adjustments_update_site_audit_action_bundle_state($project_id, $action_key, $changes);
            return array(
                'applied' => true,
                'stage' => 'prepare',
                'execution_state' => 'prepared',
                'action_key' => $action_key,
                'execution_package_id' => $package_id,
                'affected_count' => $affected_count,
                'prepare_result' => $changes['last_prepare_result'],
                'metadata_execution_package' => $action_key === 'prepare_metadata_remediation' ? $package : array(),
                'canonical_execution_package' => $action_key === 'prepare_indexability_review' ? $package : array(),
                'image_alt_execution_package' => $action_key === 'prepare_image_alt_text' ? $package : array(),
                'version_number' => $version_number,
                'requires_approval' => true,
            );
        }

        if ($stage === 'ignore') {
            $changes = array(
                'approval_state' => 'dismissed',
                'execution_state' => 'ignored',
                'last_error' => '',
            );
            $this->aysa_adjustments_update_site_audit_action_bundle_state($project_id, $action_key, $changes);
            return array(
                'applied' => true,
                'stage' => 'ignore',
                'execution_state' => 'ignored',
                'action_key' => $action_key,
                'execution_package_id' => $package_id,
                'affected_count' => $affected_count,
            );
        }

        if ($stage === 'apply') {
            if (!in_array($action_key, array('prepare_metadata_remediation', 'prepare_image_alt_text', 'prepare_indexability_review'), true)) {
                $missing_endpoint_map = array(
                    'prepare_response_code_review' => 'redirect_cleanup_worker_endpoint_missing',
                    'prepare_indexability_review' => 'indexability_canonical_worker_endpoint_missing',
                    'prepare_internal_linking_plan' => 'site_audit_internal_linking_worker_endpoint_missing',
                );
                $reason = sanitize_key((string) ($missing_endpoint_map[$action_key] ?? 'shared_worker_endpoint_missing'));
                $result = array(
                    'result_type' => 'missing_shared_executor',
                    'execution_boundary' => sanitize_text_field((string) ($bundle['target_scope'] ?? 'review')),
                    'automatic_site_mutation' => false,
                    'message' => 'This Site Audit action family does not yet have a shared worker-backed executor. Agent8 did not apply a local fallback.',
                    'missing_endpoint' => $reason,
                );
                $this->aysa_agent8_create_site_audit_communication_event(
                    $project_id,
                    'site_audit_action_blocked',
                    'Agent8 Site Audit: ' . $this->aysa_technical_site_audit_action_subject_label($action_key) . ' blocked',
                    sprintf(
                        "I did not apply this Site Audit package because there is no shared worker-backed executor for it yet.\n\nAction: %s\nReason: %s\n\nNo local fallback was used. The next step is to add or wire the correct shared endpoint before this can safely mutate the site.",
                        $action_key,
                        $reason
                    ),
                    array(
                        'action_key' => $action_key,
                        'package_id' => $package_id,
                        'missing_endpoint' => $reason,
                    ),
                    'warning'
                );
                $this->aysa_adjustments_update_site_audit_action_bundle_state($project_id, $action_key, array(
                    'approval_state' => 'failed',
                    'execution_state' => 'failed',
                    'execution_package_id' => $package_id,
                    'last_applied_at' => $now,
                    'last_apply_result' => $result,
                    'last_error' => $reason,
                ));
                return array(
                    'applied' => true,
                    'blocked' => true,
                    'stage' => 'apply',
                    'execution_state' => 'failed',
                    'action_key' => $action_key,
                    'execution_package_id' => $package_id,
                    'affected_count' => $affected_count,
                    'apply_result' => $result,
                );
            }
            $result = array(
                'result_type' => 'execution_package_queued',
                'execution_boundary' => sanitize_text_field((string) ($bundle['target_scope'] ?? 'review')),
                'automatic_site_mutation' => false,
                'message' => 'The action package was moved into execution state. Site mutations still require the mapped worker/manual executor for this issue family.',
                'worker_action_keys' => array_values(array_unique(array_filter(array_map(static function($issue) {
                    return sanitize_key((string) ($issue['worker_action_key'] ?? ''));
                }, isset($bundle['issue_breakdown']) && is_array($bundle['issue_breakdown']) ? $bundle['issue_breakdown'] : array())))),
            );
            $changes = array(
                'approval_state' => 'applied',
                'execution_state' => 'applied',
                'execution_package_id' => $package_id,
                'last_applied_at' => $now,
                'last_apply_result' => $result,
                'last_error' => '',
            );
            $this->aysa_adjustments_update_site_audit_action_bundle_state($project_id, $action_key, $changes);
            return array(
                'applied' => true,
                'stage' => 'apply',
                'execution_state' => 'applied',
                'action_key' => $action_key,
                'execution_package_id' => $package_id,
                'affected_count' => $affected_count,
                'apply_result' => $result,
            );
        }

        $baseline_count = intval($payload['baseline_affected_count'] ?? ($bundle['affected_count'] ?? 0));
        if ($action_key === 'prepare_metadata_remediation') {
            return $this->aysa_technical_site_audit_verify_metadata_remediation($project_id, $bundle, $payload);
        }
        if ($action_key === 'prepare_response_code_review') {
            return $this->aysa_technical_site_audit_verify_response_code_review($project_id, $bundle, $payload);
        }
        if ($action_key === 'prepare_internal_linking_plan') {
            return $this->aysa_technical_site_audit_verify_internal_linking_plan($project_id, $bundle, $payload);
        }
        if ($action_key === 'prepare_image_alt_text') {
            return $this->aysa_technical_site_audit_verify_image_alt_text($project_id, $bundle, $payload);
        }
        if ($action_key === 'prepare_indexability_review') {
            return $this->aysa_technical_site_audit_verify_indexability_review($project_id, $bundle, $payload);
        }

        $current_count = $affected_count;
        $verified = $history_count > 1 && $current_count < $baseline_count;
        $verification_state = $verified ? 'verified' : 'verification_pending';
        $verify_result = array(
            'verification_method' => 'site_audit_delta',
            'baseline_affected_count' => $baseline_count,
            'current_affected_count' => $current_count,
            'history_count' => $history_count,
            'version_number' => $version_number,
            'verified' => $verified,
            'evidence' => $verified
                ? 'Latest Site Audit snapshot shows fewer affected rows than the baseline.'
                : 'No newer improving audit evidence exists yet; not marking this action verified.',
        );
        $changes = array(
            'execution_state' => $verification_state,
            'approval_state' => $verified ? 'verified' : 'verification_pending',
            'last_verified_at' => $now,
            'last_verify_result' => $verify_result,
            'last_error' => '',
        );
        $this->aysa_adjustments_update_site_audit_action_bundle_state($project_id, $action_key, $changes);
        return array(
            'applied' => true,
            'stage' => 'verify',
            'execution_state' => $verification_state,
            'action_key' => $action_key,
            'execution_package_id' => $package_id,
            'verification' => $verify_result,
        );
    }

    private function aysa_technical_site_audit_verify_internal_linking_plan($project_id, $bundle, $payload = array()) {
        $project_id = intval($project_id);
        $bundle = is_array($bundle) ? $bundle : array();
        $payload = is_array($payload) ? $payload : array();
        $now = current_time('mysql');
        $package_id = sanitize_text_field((string) ($bundle['execution_package_id'] ?? ''));
        $apply_result = isset($bundle['last_apply_result']['apply_result']) && is_array($bundle['last_apply_result']['apply_result'])
            ? $bundle['last_apply_result']['apply_result']
            : (isset($bundle['last_apply_result']) && is_array($bundle['last_apply_result']) ? $bundle['last_apply_result'] : array());
        $checked = 0;
        $verified = 0;
        $results = array();
        foreach ((array) ($apply_result['results'] ?? array()) as $row) {
            if (!is_array($row) || sanitize_key((string) ($row['status'] ?? '')) !== 'applied') {
                continue;
            }
            $source_post_id = intval($row['source_post_id'] ?? 0);
            $target_url = esc_url_raw((string) ($row['target_url'] ?? ''));
            if ($source_post_id <= 0 || $target_url === '') {
                continue;
            }
            $checked++;
            $post = get_post($source_post_id);
            $content = $post && isset($post->post_content) ? (string) $post->post_content : '';
            $found = $content !== '' && strpos($content, $target_url) !== false;
            if ($found) {
                $verified++;
            }
            $results[] = array(
                'source_post_id' => $source_post_id,
                'target_url' => $target_url,
                'verified' => $found,
                'reason' => $found ? 'target_link_found_in_source_content' : 'target_link_not_found_in_source_content',
            );
        }
        $is_verified = $checked > 0 && $checked === $verified;
        $verify_result = $this->aysa_technical_site_audit_normalize_verification_result(array(
            'verification_method' => 'live_internal_link_readback',
            'checked_count' => $checked,
            'verified_count' => $verified,
            'verified' => $is_verified,
            'evidence' => $is_verified
                ? 'Every applied scoped internal link is present in the source post content.'
                : 'Not every applied scoped internal link can be confirmed by live source-content readback.',
            'results' => $results,
        ));
        $this->aysa_adjustments_update_site_audit_action_bundle_state($project_id, 'prepare_internal_linking_plan', array(
            'execution_state' => $is_verified ? 'verified' : 'verification_pending',
            'approval_state' => $is_verified ? 'verified' : 'verification_pending',
            'last_verified_at' => $now,
            'last_verify_result' => $verify_result,
            'last_error' => '',
        ));
        return array(
            'applied' => true,
            'stage' => 'verify',
            'execution_state' => $is_verified ? 'verified' : 'verification_pending',
            'action_key' => 'prepare_internal_linking_plan',
            'execution_package_id' => $package_id,
            'verification' => $verify_result,
        );
    }

    private function aysa_technical_site_audit_verify_image_alt_text($project_id, $bundle, $payload = array()) {
        $project_id = intval($project_id);
        $bundle = is_array($bundle) ? $bundle : array();
        $now = current_time('mysql');
        $package_id = sanitize_text_field((string) ($bundle['execution_package_id'] ?? ''));
        $apply_result = isset($bundle['last_apply_result']['apply_result']) && is_array($bundle['last_apply_result']['apply_result'])
            ? $bundle['last_apply_result']['apply_result']
            : (isset($bundle['last_apply_result']) && is_array($bundle['last_apply_result']) ? $bundle['last_apply_result'] : array());
        $checked = 0;
        $verified = 0;
        $results = array();
        foreach ((array) ($apply_result['results'] ?? array()) as $row) {
            if (!is_array($row) || !in_array(sanitize_key((string) ($row['status'] ?? '')), array('success', 'already_present'), true)) {
                continue;
            }
            $attachment_id = intval($row['attachment_id'] ?? 0);
            $expected = sanitize_text_field((string) ($row['proposed_value'] ?? ''));
            if ($attachment_id <= 0 || $expected === '') {
                continue;
            }
            $checked++;
            $current = sanitize_text_field((string) get_post_meta($attachment_id, '_wp_attachment_image_alt', true));
            $matches = $current === $expected;
            if ($matches) {
                $verified++;
            }
            $results[] = array(
                'attachment_id' => $attachment_id,
                'image_url' => esc_url_raw((string) ($row['image_url'] ?? '')),
                'expected_value' => $expected,
                'current_value' => $current,
                'verified' => $matches,
            );
        }
        $is_verified = $checked > 0 && $checked === $verified;
        $verify_result = $this->aysa_technical_site_audit_normalize_verification_result(array(
            'verification_method' => 'live_attachment_alt_readback',
            'checked_count' => $checked,
            'verified_count' => $verified,
            'verified' => $is_verified,
            'evidence' => $is_verified
                ? 'Every applied image alt value matches the live WordPress attachment alt readback.'
                : 'Not every applied image alt value matches the live WordPress attachment alt readback yet.',
            'results' => $results,
        ));
        $verify_result['communication_event'] = $this->aysa_agent8_create_site_audit_communication_event(
            $project_id,
            $is_verified ? 'site_audit_image_alt_verify_completed' : 'site_audit_image_alt_verify_pending',
            $is_verified ? 'Agent8 Site Audit: Image alt text verified' : 'Agent8 Site Audit: Image alt text verification pending',
            $is_verified
                ? sprintf("I checked the live attachment alt text and verified all %d applied image alt changes.", $checked)
                : sprintf("I checked the live attachment alt text, but only %d of %d applied image alt changes are confirmed.", $verified, $checked),
            array(
                'action_key' => 'prepare_image_alt_text',
                'checked_count' => $checked,
                'verified_count' => $verified,
                'verification_method' => 'live_attachment_alt_readback',
            ),
            $is_verified ? 'info' : 'warning'
        );
        $this->aysa_adjustments_update_site_audit_action_bundle_state($project_id, 'prepare_image_alt_text', array(
            'execution_state' => $is_verified ? 'verified' : 'verification_pending',
            'approval_state' => $is_verified ? 'verified' : 'verification_pending',
            'last_verified_at' => $now,
            'last_verify_result' => $verify_result,
            'last_error' => '',
        ));
        return array(
            'applied' => true,
            'stage' => 'verify',
            'execution_state' => $is_verified ? 'verified' : 'verification_pending',
            'action_key' => 'prepare_image_alt_text',
            'execution_package_id' => $package_id,
            'verification' => $verify_result,
        );
    }

    private function aysa_technical_site_audit_verify_indexability_review($project_id, $bundle, $payload = array()) {
        $project_id = intval($project_id);
        $bundle = is_array($bundle) ? $bundle : array();
        $now = current_time('mysql');
        $package_id = sanitize_text_field((string) ($bundle['execution_package_id'] ?? ''));
        $apply_result = isset($bundle['last_apply_result']['apply_result']) && is_array($bundle['last_apply_result']['apply_result'])
            ? $bundle['last_apply_result']['apply_result']
            : (isset($bundle['last_apply_result']) && is_array($bundle['last_apply_result']) ? $bundle['last_apply_result'] : array());
        $checked = 0;
        $verified = 0;
        $results = array();
        foreach ((array) ($apply_result['results'] ?? array()) as $row) {
            if (!is_array($row) || sanitize_key((string) ($row['status'] ?? '')) !== 'success') {
                continue;
            }
            $page_url = esc_url_raw((string) ($row['page_url'] ?? ''));
            $expected = esc_url_raw((string) ($row['expected_canonical'] ?? ''));
            if ($page_url === '' || $expected === '') {
                continue;
            }
            $checked++;
            $response = wp_remote_get($page_url, array('timeout' => 20, 'redirection' => 3, 'user-agent' => 'AYSA Site Audit Canonical Verify'));
            $body = is_wp_error($response) ? '' : (string) wp_remote_retrieve_body($response);
            $metrics = $this->aysa_local_site_audit_extract_html_metrics($body, $page_url);
            $current = esc_url_raw((string) ($metrics['canonical'] ?? ''));
            $matches = untrailingslashit($current) === untrailingslashit($expected);
            if ($matches) {
                $verified++;
            }
            $results[] = array(
                'page_url' => $page_url,
                'expected_canonical' => $expected,
                'current_canonical' => $current,
                'verified' => $matches,
            );
        }
        $is_verified = $checked > 0 && $checked === $verified;
        $verify_result = $this->aysa_technical_site_audit_normalize_verification_result(array(
            'verification_method' => 'live_html_canonical_readback',
            'checked_count' => $checked,
            'verified_count' => $verified,
            'verified' => $is_verified,
            'evidence' => $is_verified
                ? 'Every applied canonical matches the live HTML canonical readback.'
                : 'Not every applied canonical matches the live HTML canonical readback yet.',
            'results' => $results,
        ));
        $verify_result['communication_event'] = $this->aysa_agent8_create_site_audit_communication_event(
            $project_id,
            $is_verified ? 'site_audit_canonical_verify_completed' : 'site_audit_canonical_verify_pending',
            $is_verified ? 'Agent8 Site Audit: Canonical verified' : 'Agent8 Site Audit: Canonical verification pending',
            $is_verified
                ? sprintf("I checked the live HTML and confirmed all %d canonical changes via canonical readback.", $checked)
                : sprintf("I checked the live HTML, but only %d of %d canonical changes are confirmed so far.", $verified, $checked),
            array(
                'action_key' => 'prepare_indexability_review',
                'checked_count' => $checked,
                'verified_count' => $verified,
                'verification_method' => 'live_html_canonical_readback',
            ),
            $is_verified ? 'info' : 'warning'
        );
        $this->aysa_adjustments_update_site_audit_action_bundle_state($project_id, 'prepare_indexability_review', array(
            'execution_state' => $is_verified ? 'verified' : 'verification_pending',
            'approval_state' => $is_verified ? 'verified' : 'verification_pending',
            'last_verified_at' => $now,
            'last_verify_result' => $verify_result,
            'last_error' => '',
        ));
        return array(
            'applied' => true,
            'stage' => 'verify',
            'execution_state' => $is_verified ? 'verified' : 'verification_pending',
            'action_key' => 'prepare_indexability_review',
            'execution_package_id' => $package_id,
            'verification' => $verify_result,
        );
    }

    private function aysa_adjustments_build_site_audit_bundle_approval_request($action_key, $action_payload = array()) {
        $action_key = sanitize_key((string) $action_key);
        $action_payload = is_array($action_payload) ? $action_payload : array();
        if ($action_key === '' || !$this->aysa_adjustments_is_site_audit_bundle_action_key($action_key)) {
            return null;
        }

        $label = sanitize_text_field((string) ($action_payload['action_label'] ?? 'Prepare technical remediation bundle'));
        $issue_title = sanitize_text_field((string) ($action_payload['issue_title'] ?? 'Technical remediation bundle'));
        $affected_count = max(0, intval($action_payload['affected_count'] ?? 0));
        $source_files = isset($action_payload['source_files']) && is_array($action_payload['source_files'])
            ? array_values(array_filter(array_map('sanitize_text_field', $action_payload['source_files'])))
            : array();
        $next_step = sanitize_text_field((string) ($action_payload['next_step'] ?? 'Review the remediation preparation output in Agent8.'));

        return array(
            'action_key' => $action_key,
            'title' => $label !== '' ? $label : 'Confirm technical remediation bundle',
            'message' => 'Agent8 will prepare the remediation workspace for this audit finding, without changing the site directly.',
            'confirmation_label' => 'Approve bundle',
            'cancel_label' => 'Not now',
            'summary' => array(
                'issue' => $issue_title,
                'affected_urls' => $affected_count,
                'source_files' => $source_files,
                'next_step' => $next_step,
            ),
        );
    }

    private function aysa_adjustments_get_technical_pagespeed_snapshot($project_id) {
        $project_id = intval($project_id);
        $state = get_option($this->aysa_technical_pagespeed_state_key($project_id), array());
        if (!is_array($state)) {
            $state = array();
        }
        $state_status = sanitize_key((string) ($state['status'] ?? 'idle'));

        $data = !empty($state['data']) && is_array($state['data']) ? $state['data'] : array();
        $overview = !empty($data['overview']) && is_array($data['overview']) ? $data['overview'] : array();
        $desktop = !empty($data['desktop']) && is_array($data['desktop']) ? $data['desktop'] : array();
        $mobile = !empty($data['mobile']) && is_array($data['mobile']) ? $data['mobile'] : array();
        $conclusion = !empty($data['conclusion']) && is_array($data['conclusion']) ? $data['conclusion'] : array();

        $desktop_metrics = !empty($desktop['metrics']) && is_array($desktop['metrics']) ? $desktop['metrics'] : array();
        $mobile_metrics = !empty($mobile['metrics']) && is_array($mobile['metrics']) ? $mobile['metrics'] : array();
        $desktop_opportunities = !empty($desktop['opportunities']) && is_array($desktop['opportunities']) ? array_values($desktop['opportunities']) : array();
        $mobile_opportunities = !empty($mobile['opportunities']) && is_array($mobile['opportunities']) ? array_values($mobile['opportunities']) : array();
        $priority_actions = !empty($conclusion['priority_actions']) && is_array($conclusion['priority_actions']) ? array_values($conclusion['priority_actions']) : array();

        $overview_rows = array(
            array('metric' => 'Overall score', 'value' => (string) intval($overview['overallScore'] ?? $conclusion['overall_score'] ?? 0)),
            array('metric' => 'Desktop score', 'value' => (string) intval($overview['pageSpeedDesktop'] ?? $desktop_metrics['performance']['score_percent'] ?? 0)),
            array('metric' => 'Mobile score', 'value' => (string) intval($overview['pageSpeedMobile'] ?? $mobile_metrics['performance']['score_percent'] ?? 0)),
            array('metric' => 'Mobile friendly', 'value' => (string) intval($overview['mobileFriendly'] ?? 0)),
            array('metric' => 'Indexability', 'value' => (string) intval($overview['indexability'] ?? 0)),
            array('metric' => 'Status', 'value' => sanitize_text_field((string) ($conclusion['status'] ?? ''))),
        );

        $metrics_rows = array();
        $metric_map = array(
            'firstContentfulPaint' => 'First Contentful Paint',
            'largestContentfulPaint' => 'Largest Contentful Paint',
            'totalBlockingTime' => 'Total Blocking Time',
            'cumulativeLayoutShift' => 'Cumulative Layout Shift',
            'speedIndex' => 'Speed Index',
            'interactive' => 'Time to Interactive',
            'totalByteWeight' => 'Total Byte Weight',
        );
        foreach ($metric_map as $key => $label) {
            $metrics_rows[] = array(
                'metric' => $label,
                'desktop' => (string) ($desktop_metrics[$key] ?? ''),
                'mobile' => (string) ($mobile_metrics[$key] ?? ''),
            );
        }

        $opportunities_rows = array();
        foreach (array('desktop' => $desktop_opportunities, 'mobile' => $mobile_opportunities) as $strategy => $rows) {
            foreach ($rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $opportunities_rows[] = array(
                    'id' => sanitize_key((string) ($row['id'] ?? '')),
                    'strategy' => $strategy,
                    'title' => sanitize_text_field((string) ($row['title'] ?? '')),
                    'description' => sanitize_text_field((string) ($row['description'] ?? '')),
                    'display_value' => sanitize_text_field((string) ($row['displayValue'] ?? '')),
                    'score' => isset($row['score']) ? floatval($row['score']) : null,
                    'impact' => intval($row['impact'] ?? 0),
                    'savings_ms' => intval(round(floatval($row['savings_ms'] ?? 0))),
                    'savings_bytes' => intval(round(floatval($row['savings_bytes'] ?? 0))),
                );
            }
        }

        $priority_rows = array();
        foreach ($priority_actions as $action) {
            if (!is_string($action) || trim($action) === '') {
                continue;
            }
            $priority_rows[] = array(
                'action' => sanitize_text_field($action),
            );
        }

        $has_data = !empty($data) || !empty($opportunities_rows) || intval($overview['overallScore'] ?? 0) > 0;
        $snapshot_status = $has_data ? 'ready' : (in_array($state_status, array('queued', 'running', 'completed', 'failed', 'blocked'), true) ? $state_status : 'idle');

        return array(
            'id' => 'technical_pagespeed',
            'status' => $snapshot_status,
            'updated_at' => sanitize_text_field((string) ($state['updated_at'] ?? '')),
            'url' => esc_url_raw((string) ($data['url'] ?? '')),
            'overall_score' => intval($overview['overallScore'] ?? $conclusion['overall_score'] ?? 0),
            'desktop_score' => intval($overview['pageSpeedDesktop'] ?? $desktop_metrics['performance']['score_percent'] ?? 0),
            'mobile_score' => intval($overview['pageSpeedMobile'] ?? $mobile_metrics['performance']['score_percent'] ?? 0),
            'mobile_friendly' => intval($overview['mobileFriendly'] ?? 0),
            'indexability' => intval($overview['indexability'] ?? 0),
            'summary' => sanitize_text_field((string) ($conclusion['summary'] ?? '')),
            'priority_actions' => $priority_rows,
            'overview_rows' => array_values($overview_rows),
            'rows' => array_values($opportunities_rows),
            'metrics_rows' => array_values($metrics_rows),
            'count' => count($opportunities_rows),
            'has_data' => $has_data,
            'raw_payload_available' => !empty($data['raw']) && is_array($data['raw']),
            'run_id' => intval($state['run_id'] ?? 0),
            'last_report_email' => isset($state['last_report_email']) && is_array($state['last_report_email']) ? $state['last_report_email'] : array(),
        );
    }


    private function aysa_adjustments_get_technical_mobile_optimization_snapshot($project_id) {
        $pagespeed = $this->aysa_adjustments_get_technical_pagespeed_snapshot($project_id);
        $rows = isset($pagespeed['rows']) && is_array($pagespeed['rows']) ? $pagespeed['rows'] : array();
        $mobile_rows = array_values(array_filter($rows, static function ($row) {
            return is_array($row) && strtolower(trim((string) ($row['strategy'] ?? ''))) === 'mobile';
        }));

        return array(
            'id' => 'technical_mobile_optimization',
            'status' => !empty($pagespeed['has_data']) ? 'ready' : 'idle',
            'updated_at' => sanitize_text_field((string) ($pagespeed['updated_at'] ?? '')),
            'url' => esc_url_raw((string) ($pagespeed['url'] ?? '')),
            'mobile_score' => intval($pagespeed['mobile_score'] ?? 0),
            'mobile_friendly' => intval($pagespeed['mobile_friendly'] ?? 0),
            'indexability' => intval($pagespeed['indexability'] ?? 0),
            'summary' => sanitize_text_field((string) ($pagespeed['summary'] ?? '')),
            'priority_actions' => isset($pagespeed['priority_actions']) && is_array($pagespeed['priority_actions']) ? $pagespeed['priority_actions'] : array(),
            'overview_rows' => array(
                array('metric' => 'Mobile score', 'value' => (string) intval($pagespeed['mobile_score'] ?? 0)),
                array('metric' => 'Mobile friendly', 'value' => (string) intval($pagespeed['mobile_friendly'] ?? 0)),
                array('metric' => 'Indexability', 'value' => (string) intval($pagespeed['indexability'] ?? 0)),
            ),
            'rows' => $mobile_rows,
            'metrics_rows' => isset($pagespeed['metrics_rows']) && is_array($pagespeed['metrics_rows']) ? array_values($pagespeed['metrics_rows']) : array(),
            'count' => count($mobile_rows),
            'has_data' => !empty($pagespeed['has_data']),
        );
    }

    private function aysa_adjustments_get_technical_404_errors_snapshot($project_id) {
        global $wpdb;

        $project_id = intval($project_id);
        $state_key = 'aysa_technical_site_audit_state_' . $project_id;
        $state = get_option($state_key, array());
        $snapshot = (is_array($state) && !empty($state['audit_snapshot']) && is_array($state['audit_snapshot']))
            ? $state['audit_snapshot']
            : array();

        $rows = array();
        $seen = array();
        $catalog_items = $this->aysa_adjustments_get_wp_catalog_items();

        if (!empty($snapshot['pages']) && is_array($snapshot['pages'])) {
            foreach ($snapshot['pages'] as $page) {
                if (!is_array($page)) {
                    continue;
                }
                if (intval($page['status_code'] ?? 0) !== 404) {
                    continue;
                }
                $url = trim((string) ($page['url'] ?? ''));
                if ($url === '' || isset($seen[$url])) {
                    continue;
                }
                $seen[$url] = true;
                $candidate = $this->aysa_adjustments_guess_redirect_target_for_url($url, $catalog_items);
                $rows[] = array(
                    'id' => 0,
                    'url' => $url,
                    'status_code' => 404,
                    'source' => 'site_audit',
                    'hits' => 0,
                    'best_redirect_url' => $candidate !== null ? (string) ($candidate['url'] ?? '') : '',
                    'best_redirect_confidence' => $candidate !== null ? floatval($candidate['score'] ?? 0) : 0.0,
                    'best_redirect_action' => $candidate !== null ? 'redirect' : 'none',
                    'last_hit' => sanitize_text_field((string) ($state['updated_at'] ?? '')),
                );
            }
        }

        $table_name = $wpdb->prefix . 'aysa_404_errors';
        $local_rows = $wpdb->get_results(
            "SELECT id, url, hit_count, last_hit, suggested_url FROM {$table_name} WHERE is_ignored = 0 ORDER BY last_hit DESC LIMIT 200",
            ARRAY_A
        );
        if (is_array($local_rows)) {
            foreach ($local_rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $url = trim((string) ($row['url'] ?? ''));
                if ($url === '' || isset($seen[$url])) {
                    continue;
                }
                $seen[$url] = true;
                $suggested_url = (string) ($row['suggested_url'] ?? '');
                $suggested_confidence = trim($suggested_url) !== '' ? 1.0 : 0.0;
                if (trim($suggested_url) === '') {
                    $candidate = $this->aysa_adjustments_guess_redirect_target_for_url($url, $catalog_items);
                    if ($candidate !== null) {
                        $suggested_url = (string) ($candidate['url'] ?? '');
                        $suggested_confidence = floatval($candidate['score'] ?? 0);
                    }
                }
                $rows[] = array(
                    'id' => intval($row['id'] ?? 0),
                    'url' => $url,
                    'status_code' => 404,
                    'source' => 'tracker',
                    'hits' => intval($row['hit_count'] ?? 0),
                    'best_redirect_url' => $suggested_url,
                    'best_redirect_confidence' => $suggested_confidence,
                    'best_redirect_action' => trim($suggested_url) !== '' ? 'redirect' : 'none',
                    'last_hit' => sanitize_text_field((string) ($row['last_hit'] ?? '')),
                );
            }
        }

        usort($rows, static function ($a, $b) {
            return strcmp((string) ($b['last_hit'] ?? ''), (string) ($a['last_hit'] ?? ''));
        });

        $audit_count = 0;
        $tracker_count = 0;
        foreach ($rows as $row) {
            if (($row['source'] ?? '') === 'site_audit') {
                $audit_count++;
            } elseif (($row['source'] ?? '') === 'tracker') {
                $tracker_count++;
            }
        }

        return array(
            'id' => 'technical_404_errors',
            'status' => !empty($rows) ? 'ready' : 'idle',
            'updated_at' => sanitize_text_field((string) ($state['updated_at'] ?? '')),
            'count' => count($rows),
            'audit_count' => $audit_count,
            'tracker_count' => $tracker_count,
            'overview_rows' => array(
                array('metric' => '404 URLs', 'value' => (string) count($rows)),
                array('metric' => 'From site audit', 'value' => (string) $audit_count),
                array('metric' => 'From local tracker', 'value' => (string) $tracker_count),
            ),
            'rows' => array_values($rows),
            'has_data' => !empty($rows),
        );
    }

    private function aysa_adjustments_guess_redirect_target_for_url($url, $catalog_items) {
        $path = (string) parse_url((string) $url, PHP_URL_PATH);
        $query = (string) parse_url((string) $url, PHP_URL_QUERY);
        $source = trim($path !== '' ? $path : (string) $url);
        if ($query !== '' && $path !== '') {
            $source .= ' ' . str_replace(array('=', '&', '%'), ' ', $query);
        }

        $source = trim($source, '/');
        if ($source === '') {
            return null;
        }

        $tokens = $this->aysa_adjustments_tokenize_keyword_text(str_replace(array('-', '_', '/'), ' ', $source));
        if (empty($tokens)) {
            return null;
        }

        $weak_tokens = $this->aysa_adjustments_get_weak_context_tokens();
        $tokens = array_values(array_diff($tokens, array_merge($weak_tokens, array(
            'wp', 'admin', 'api', 'uploads', 'upload', 'page', 'id', 'php', 'href', 'project', 'domain',
            'about', 'our', 'ours', 'us', 'www', 'http', 'https', 'com', 'ro',
            'product', 'products', 'category', 'categories', 'tag', 'tags', 'attachment', 'media',
            'feed', 'index', 'html', 'htm', 'amp', 'page_id', 'utm', 'source'
        ))));
        if (empty($tokens)) {
            return null;
        }

        if (in_array('contact', $tokens, true)) {
            foreach ((array) $catalog_items as $item) {
                $candidate_url = (string) ($item['url'] ?? '');
                $candidate_path = (string) parse_url($candidate_url, PHP_URL_PATH);
                if ($candidate_path !== '' && str_contains($candidate_path, '/contact')) {
                    return array(
                        'type' => (string) ($item['type'] ?? ''),
                        'id' => intval($item['id'] ?? 0),
                        'name' => (string) ($item['name'] ?? ''),
                        'url' => $candidate_url,
                        'score' => 0.95,
                    );
                }
            }
        }

        if (in_array('shop', $tokens, true)) {
            return array(
                'type' => 'Page',
                'id' => 0,
                'name' => 'Shop',
                'url' => home_url('/shop/'),
                'score' => 0.9,
            );
        }

        if (count($tokens) < 2) {
            return null;
        }

        $best = null;
        $best_overlap = 0;
        foreach ((array) $catalog_items as $item) {
            if (!is_array($item) || empty($item['tokens']) || empty($item['url'])) {
                continue;
            }
            $item_tokens = is_array($item['tokens']) ? array_values($item['tokens']) : array();
            $overlap = count(array_intersect($tokens, $item_tokens));
            if ($overlap < 2) {
                continue;
            }

            $score = ($overlap / max(count($tokens), 1)) * 0.72
                + ($overlap / max(count($item_tokens), 1)) * 0.28;
            $item_text = strtolower(trim(remove_accents((string) ($item['text'] ?? ''))));
            $token_phrase = implode(' ', $tokens);
            if ($item_text !== '' && str_contains($item_text, $token_phrase)) {
                $score += 0.18;
            }

            if ($best === null || $score > floatval($best['score'] ?? 0)) {
                $best = array(
                    'type' => (string) ($item['type'] ?? ''),
                    'id' => intval($item['id'] ?? 0),
                    'name' => (string) ($item['name'] ?? ''),
                    'url' => (string) ($item['url'] ?? ''),
                    'score' => min(1, $score),
                );
                $best_overlap = $overlap;
            }
        }

        if ($best === null || floatval($best['score'] ?? 0) < 0.58 || $best_overlap < 2) {
            return null;
        }

        $best_path = (string) parse_url((string) ($best['url'] ?? ''), PHP_URL_PATH);
        if ($best_path !== '' && strcasecmp(rtrim($best_path, '/'), '/' . trim($source, '/')) === 0) {
            return null;
        }

        return $best;
    }

    private function aysa_adjustments_get_technical_301_redirects_snapshot($project_id) {
        $htaccess_file = ABSPATH . '.htaccess';
        if (!file_exists($htaccess_file) || !is_readable($htaccess_file)) {
            return array(
                'id' => 'technical_301_redirects',
                'count' => 0,
                'rows' => array(),
                'has_data' => false,
            );
        }

        $content = file_get_contents($htaccess_file);
        if (!is_string($content) || trim($content) === '') {
            return array(
                'id' => 'technical_301_redirects',
                'count' => 0,
                'rows' => array(),
                'has_data' => false,
            );
        }

        $section_start = '# BEGIN Aysa Redirects';
        $section_end = '# END Aysa Redirects';
        $rows = array();

        if (preg_match('/' . preg_quote($section_start, '/') . '(.*?)' . preg_quote($section_end, '/') . '/s', $content, $matches) === 1) {
            $section = trim((string) ($matches[1] ?? ''));
            $lines = preg_split('/\R+/', $section);
            if (is_array($lines)) {
                foreach ($lines as $line) {
                    $line = trim((string) $line);
                    if ($line === '' || str_starts_with($line, '#')) {
                        continue;
                    }

                    if (preg_match('/^RedirectMatch\s+301\s+\^(.+?)\$\s+(.+)$/i', $line, $rule) !== 1) {
                        continue;
                    }

                    $source = trim((string) ($rule[1] ?? ''));
                    $target = trim((string) ($rule[2] ?? ''));
                    if ($source === '' || $target === '') {
                        continue;
                    }

                    $decoded_source = stripcslashes($source);
                    if ($decoded_source === '' || $decoded_source[0] !== '/') {
                        $decoded_source = '/' . ltrim($decoded_source, '/');
                    }

                    if (!preg_match('#^https?://#i', $target)) {
                        $target = '/' . ltrim($target, '/');
                    }

                    $rows[] = array(
                        'source_url' => sanitize_text_field($decoded_source),
                        'target_url' => esc_url_raw($target),
                        'status' => '301',
                        'rule_type' => 'manual',
                        'source' => 'htaccess',
                    );
                }
            }
        }

        return array(
            'id' => 'technical_301_redirects',
            'count' => count($rows),
            'rows' => $rows,
            'has_data' => !empty($rows),
            'updated_at' => @filemtime($htaccess_file) ?: 0,
        );
    }

    private function aysa_adjustments_get_technical_410_gone_snapshot($project_id) {
        $htaccess_file = ABSPATH . '.htaccess';
        if (!file_exists($htaccess_file) || !is_readable($htaccess_file)) {
            return array(
                'id' => 'technical_410_gone',
                'count' => 0,
                'rows' => array(),
                'has_data' => false,
            );
        }

        $content = file_get_contents($htaccess_file);
        if (!is_string($content) || trim($content) === '') {
            return array(
                'id' => 'technical_410_gone',
                'count' => 0,
                'rows' => array(),
                'has_data' => false,
            );
        }

        $section_start = '# BEGIN Aysa Redirects';
        $section_end = '# END Aysa Redirects';
        $rows = array();

        if (preg_match('/' . preg_quote($section_start, '/') . '(.*?)' . preg_quote($section_end, '/') . '/s', $content, $matches) === 1) {
            $section = trim((string) ($matches[1] ?? ''));
            $lines = preg_split('/\R+/', $section);
            if (is_array($lines)) {
                foreach ($lines as $line) {
                    $line = trim((string) $line);
                    if ($line === '' || str_starts_with($line, '#')) {
                        continue;
                    }

                    if (preg_match('/^RedirectMatch\s+410\s+\^(.+?)\$$/i', $line, $rule) !== 1) {
                        continue;
                    }

                    $url = trim((string) ($rule[1] ?? ''));
                    if ($url === '') {
                        continue;
                    }

                    $decoded_url = stripcslashes($url);
                    if ($decoded_url === '' || $decoded_url[0] !== '/') {
                        $decoded_url = '/' . ltrim($decoded_url, '/');
                    }

                    $rows[] = array(
                        'url' => sanitize_text_field($decoded_url),
                        'status' => '410',
                        'rule_type' => 'manual',
                        'source' => 'htaccess',
                    );
                }
            }
        }

        return array(
            'id' => 'technical_410_gone',
            'count' => count($rows),
            'rows' => $rows,
            'has_data' => !empty($rows),
            'updated_at' => @filemtime($htaccess_file) ?: 0,
        );
    }

    private function aysa_adjustments_get_technical_errors_snapshot($project_id, array $technical_404_errors = array(), array $technical_301_redirects = array(), array $technical_410_gone = array()) {
        $project_id = intval($project_id);

        if (empty($technical_404_errors)) {
            $technical_404_errors = $this->aysa_adjustments_get_technical_404_errors_snapshot($project_id);
        }
        if (empty($technical_301_redirects)) {
            $technical_301_redirects = $this->aysa_adjustments_get_technical_301_redirects_snapshot($project_id);
        }
        if (empty($technical_410_gone)) {
            $technical_410_gone = $this->aysa_adjustments_get_technical_410_gone_snapshot($project_id);
        }

        $pending_reviews = get_option('aysa_pending_redirect_reviews', array());
        if (!is_array($pending_reviews)) {
            $pending_reviews = array();
        }

        $rows = array();
        $conflicts = array();
        $redirect_targets = array();
        $gone_urls = array();
        $pending_review_rows = array();

        foreach ((array) ($technical_301_redirects['rows'] ?? array()) as $row) {
            if (!is_array($row)) {
                continue;
            }

            $source_url = $this->aysa_normalize_redirect_path((string) ($row['source_url'] ?? ''));
            $target_url = trim((string) ($row['target_url'] ?? ''));
            if ($source_url === '') {
                continue;
            }

            $redirect_targets[$source_url] = array(
                'target_url' => $target_url,
                'source' => (string) ($row['source'] ?? 'htaccess'),
            );

            $rows[] = array(
                'kind' => '301',
                'source_url' => $source_url,
                'target_url' => $target_url,
                'status' => (string) ($row['status'] ?? '301'),
                'origin' => (string) ($row['source'] ?? 'htaccess'),
                'hits' => intval($row['hit_count'] ?? 0),
                'last_seen' => sanitize_text_field((string) ($row['created_at'] ?? '')),
                'seo_priority' => 'medium',
                'suggested_action' => 'redirect',
                'suggested_target_url' => $target_url,
                'suggested_confidence' => 100,
                'suggested_reason' => 'Existing redirect rule is active for this source URL.',
                'history_signals' => array('existing_301_rule'),
                'approval_state' => 'applied',
                'conflict_flags' => array(),
            );
        }

        foreach ((array) ($technical_410_gone['rows'] ?? array()) as $row) {
            if (!is_array($row)) {
                continue;
            }

            $source_url = $this->aysa_normalize_redirect_path((string) ($row['url'] ?? ''));
            if ($source_url === '') {
                continue;
            }
            $gone_urls[$source_url] = true;

            $rows[] = array(
                'kind' => '410',
                'source_url' => $source_url,
                'target_url' => '',
                'status' => (string) ($row['status'] ?? '410'),
                'origin' => (string) ($row['source'] ?? 'htaccess'),
                'hits' => 0,
                'last_seen' => sanitize_text_field((string) ($row['created_at'] ?? '')),
                'seo_priority' => 'medium',
                'suggested_action' => 'gone',
                'suggested_target_url' => '',
                'suggested_confidence' => 100,
                'suggested_reason' => 'URL is intentionally marked as gone.',
                'history_signals' => array('existing_410_rule'),
                'approval_state' => 'applied',
                'conflict_flags' => array(),
            );
        }

        foreach ($pending_reviews as $review_key => $review) {
            if (!is_array($review)) {
                continue;
            }
            $source_url = $this->aysa_normalize_redirect_path((string) ($review['source_url'] ?? ''));
            if ($source_url === '') {
                continue;
            }
            $pending_review_rows[] = array(
                'review_key' => sanitize_text_field((string) $review_key),
                'source_url' => $source_url,
                'confidence_score' => floatval($review['confidence_score'] ?? 0),
                'suggestions' => array_values(array_filter((array) ($review['suggestions'] ?? array()), 'is_array')),
                'created_at' => sanitize_text_field((string) ($review['created_at'] ?? '')),
            );
        }

        $noise_count = 0;
        $redirect_ready = 0;
        $gone_candidates = 0;
        $review_required = 0;

        foreach ((array) ($technical_404_errors['rows'] ?? array()) as $row) {
            if (!is_array($row)) {
                continue;
            }

            $source_url = $this->aysa_normalize_redirect_path((string) ($row['url'] ?? ''));
            if ($source_url === '') {
                continue;
            }

            $best_redirect_url = trim((string) ($row['best_redirect_url'] ?? ''));
            $best_redirect_confidence = floatval($row['best_redirect_confidence'] ?? 0);
            if ($best_redirect_confidence > 0 && $best_redirect_confidence <= 1) {
                $best_redirect_confidence = round($best_redirect_confidence * 100);
            } else {
                $best_redirect_confidence = round($best_redirect_confidence);
            }

            $is_noise = false;
            $lower_source = strtolower($source_url);
            $lower_referer = strtolower((string) ($row['referer'] ?? ''));
            $noise_needles = array('/wp-admin/', '/wp-login.php', '/xmlrpc.php', '/.env', '/admin', 'wp-json', 'rest_route=', 'author=', 'feed=', '?s=');
            foreach ($noise_needles as $needle) {
                if (strpos($lower_source, $needle) !== false || strpos($lower_referer, $needle) !== false) {
                    $is_noise = true;
                    break;
                }
            }

            $suggested_action = 'review';
            $suggested_reason = 'Manual review is recommended before applying a redirect or 410 rule.';
            $seo_priority = intval($row['hits'] ?? 0) > 0 ? 'high' : 'medium';
            $history_signals = array((string) ($row['source'] ?? 'tracker'));
            $approval_state = 'suggested';
            $conflict_flags = array();

            if ($is_noise) {
                $suggested_action = 'ignore';
                $suggested_reason = 'This 404 looks like admin, probe, or crawler noise and should not trigger an SEO redirect.';
                $seo_priority = 'low';
                $noise_count++;
                $history_signals[] = 'noise_pattern';
            } elseif (isset($gone_urls[$source_url])) {
                $suggested_action = 'gone';
                $suggested_reason = 'This URL is already covered by a 410 rule and should stay removed.';
                $seo_priority = 'medium';
                $approval_state = 'applied';
                $history_signals[] = 'existing_410_rule';
                $gone_candidates++;
            } elseif (isset($redirect_targets[$source_url])) {
                $suggested_action = 'redirect';
                $best_redirect_url = (string) ($redirect_targets[$source_url]['target_url'] ?? $best_redirect_url);
                $best_redirect_confidence = 100;
                $suggested_reason = 'This 404 already has an active 301 rule configured.';
                $approval_state = 'applied';
                $history_signals[] = 'existing_301_rule';
                $redirect_ready++;
            } elseif ($best_redirect_url !== '' && $best_redirect_confidence >= 90) {
                $suggested_action = 'redirect';
                $suggested_reason = 'High-confidence replacement found based on URL semantics and local history.';
                $redirect_ready++;
                $history_signals[] = 'semantic_match';
            } elseif ($best_redirect_url === '' && intval($row['hits'] ?? 0) <= 1 && (string) ($row['source'] ?? '') === 'site_audit') {
                $suggested_action = 'gone';
                $suggested_reason = 'No relevant replacement found and the URL looks removable rather than redirectable.';
                $gone_candidates++;
                $history_signals[] = 'no_replacement';
            } else {
                $review_required++;
                if ($best_redirect_url !== '') {
                    $history_signals[] = 'weak_match';
                    $suggested_reason = 'A possible replacement exists, but confidence is not high enough for auto-redirect.';
                } else {
                    $history_signals[] = 'manual_review';
                }
            }

            if ($best_redirect_url !== '') {
                $normalized_target = $best_redirect_url;
                if (!preg_match('#^https?://#i', $normalized_target)) {
                    $normalized_target = $this->aysa_normalize_redirect_path($normalized_target);
                }
                if ($normalized_target === $source_url) {
                    $conflict_flags[] = 'self_redirect';
                }
                if (isset($gone_urls[$normalized_target])) {
                    $conflict_flags[] = 'target_is_410';
                }
                if (isset($redirect_targets[$normalized_target])) {
                    $conflict_flags[] = 'target_has_redirect';
                }
            }

            if (isset($gone_urls[$source_url]) && isset($redirect_targets[$source_url])) {
                $conflict_flags[] = 'redirect_and_gone';
            }

            if ($suggested_action === 'redirect' && !empty($conflict_flags)) {
                $suggested_action = 'review';
                $suggested_reason = 'Suggested redirect has unresolved conflicts and needs manual review.';
                $review_required++;
                $redirect_ready = max(0, $redirect_ready - 1);
            }

            if (!empty($conflict_flags)) {
                $conflicts[] = array(
                    'source_url' => $source_url,
                    'flags' => array_values(array_unique($conflict_flags)),
                );
            }

            $rows[] = array(
                'kind' => '404',
                'source_url' => $source_url,
                'target_url' => '',
                'status' => '404',
                'origin' => (string) ($row['source'] ?? 'tracker'),
                'hits' => intval($row['hits'] ?? 0),
                'last_seen' => sanitize_text_field((string) ($row['last_hit'] ?? '')),
                'seo_priority' => $seo_priority,
                'suggested_action' => $suggested_action,
                'suggested_target_url' => $best_redirect_url,
                'suggested_confidence' => $best_redirect_confidence,
                'suggested_reason' => $suggested_reason,
                'history_signals' => array_values(array_unique($history_signals)),
                'approval_state' => $approval_state,
                'conflict_flags' => array_values(array_unique($conflict_flags)),
            );
        }

        foreach ($rows as &$row) {
            if ($row['kind'] === '301' && isset($gone_urls[$row['source_url']])) {
                $row['conflict_flags'][] = 'redirect_and_gone';
                $conflicts[] = array(
                    'source_url' => $row['source_url'],
                    'flags' => array('redirect_and_gone'),
                );
            }
            if ($row['kind'] === '301' && $row['target_url'] !== '') {
                $normalized_target = preg_match('#^https?://#i', $row['target_url'])
                    ? $row['target_url']
                    : $this->aysa_normalize_redirect_path($row['target_url']);
                if (isset($gone_urls[$normalized_target])) {
                    $row['conflict_flags'][] = 'target_is_410';
                    $conflicts[] = array(
                        'source_url' => $row['source_url'],
                        'flags' => array('target_is_410'),
                    );
                }
            }
            $row['conflict_flags'] = array_values(array_unique(array_filter((array) $row['conflict_flags'])));
        }
        unset($row);

        $bulk_candidates = array(
            'redirect' => array_values(array_filter($rows, static function ($row) {
                return is_array($row)
                    && ($row['kind'] ?? '') === '404'
                    && ($row['suggested_action'] ?? '') === 'redirect'
                    && intval($row['suggested_confidence'] ?? 0) >= 90
                    && empty($row['conflict_flags']);
            })),
            'gone' => array_values(array_filter($rows, static function ($row) {
                return is_array($row)
                    && ($row['kind'] ?? '') === '404'
                    && ($row['suggested_action'] ?? '') === 'gone'
                    && empty($row['conflict_flags']);
            })),
        );

        usort($rows, static function ($a, $b) {
            $priority_rank = array('high' => 3, 'medium' => 2, 'low' => 1);
            $a_rank = intval($priority_rank[(string) ($a['seo_priority'] ?? 'low')] ?? 0);
            $b_rank = intval($priority_rank[(string) ($b['seo_priority'] ?? 'low')] ?? 0);
            if ($a_rank !== $b_rank) {
                return $b_rank <=> $a_rank;
            }
            return strcmp((string) ($b['last_seen'] ?? ''), (string) ($a['last_seen'] ?? ''));
        });

        $summary = array(
            'total_404' => intval($technical_404_errors['count'] ?? 0),
            'total_301' => intval($technical_301_redirects['count'] ?? 0),
            'total_410' => intval($technical_410_gone['count'] ?? 0),
            'redirect_ready' => count($bulk_candidates['redirect']),
            'gone_candidates' => count($bulk_candidates['gone']),
            'review_required' => intval($review_required),
            'ignored_noise' => intval($noise_count),
            'pending_reviews' => count($pending_review_rows),
            'conflicts' => count($conflicts),
        );

        return array(
            'id' => 'technical_errors',
            'status' => !empty($rows) ? 'ready' : 'idle',
            'count' => count($rows),
            'summary' => $summary,
            'rows' => array_values($rows),
            'pending_reviews' => array_values($pending_review_rows),
            'conflicts' => array_values($conflicts),
            'bulk_candidates' => $bulk_candidates,
            'recent_actions' => array(),
            'has_data' => !empty($rows),
        );
    }

    private function aysa_adjustments_get_cached_or_idle_technical_errors_snapshot($project_id) {
        $project_id = intval($project_id);
        $state = get_option($this->aysa_technical_redirects_state_key($project_id), array());
        if (!is_array($state)) {
            $state = array();
        }

        $snapshot = isset($state['snapshot']) && is_array($state['snapshot']) ? $state['snapshot'] : array();
        $status = sanitize_key((string) ($state['status'] ?? 'idle'));
        $run_id = intval($state['run_id'] ?? 0);

        if ($snapshot !== array()) {
            $snapshot['status'] = $status !== '' ? $status : (string) ($snapshot['status'] ?? 'ready');
            if ($run_id > 0) {
                $snapshot['run_id'] = $run_id;
            }
            return $snapshot;
        }

        if (in_array($status, array('queued', 'running', 'failed', 'blocked'), true)) {
            return array(
                'id' => 'technical_errors',
                'status' => $status,
                'run_id' => $run_id,
                'count' => 0,
                'summary' => array(
                    'total_404' => 0,
                    'total_301' => 0,
                    'total_410' => 0,
                    'redirect_ready' => 0,
                    'gone_candidates' => 0,
                    'review_required' => 0,
                    'ignored_noise' => 0,
                    'pending_reviews' => 0,
                    'conflicts' => 0,
                ),
                'rows' => array(),
                'pending_reviews' => array(),
                'conflicts' => array(),
                'bulk_candidates' => array('redirect' => array(), 'gone' => array()),
                'recent_actions' => array(),
                'has_data' => false,
            );
        }

        $live = $this->aysa_adjustments_get_technical_errors_snapshot($project_id);
        if ($run_id > 0 && is_array($live)) {
            $live['run_id'] = $run_id;
        }
        return $live;
    }

    private function aysa_adjustments_normalize_schema_url($url) {
        $url = trim((string) $url);
        if ($url === '') {
            return '';
        }
        $url = esc_url_raw($url);
        if ($url === '') {
            return '';
        }
        $parts = wp_parse_url($url);
        if (!$parts || empty($parts['host'])) {
            return untrailingslashit($url) . '/';
        }
        $scheme = isset($parts['scheme']) ? strtolower((string) $parts['scheme']) : (is_ssl() ? 'https' : 'http');
        $host = strtolower((string) $parts['host']);
        $port = isset($parts['port']) ? ':' . intval($parts['port']) : '';
        $path = isset($parts['path']) ? (string) $parts['path'] : '/';
        $path = '/' . ltrim($path, '/');
        if ($path === '//') {
            $path = '/';
        }
        if (substr($path, -1) !== '/') {
            $path .= '/';
        }
        return $scheme . '://' . $host . $port . $path;
    }

    private function aysa_adjustments_get_schema_cache_key($project_id, $suffix = 'snapshot') {
        return 'aysa_technical_schema_' . sanitize_key((string) $suffix) . '_' . sanitize_key((string) $project_id) . '_v3';
    }

    private function aysa_technical_schema_resolve_project_id($project_id) {
        return $this->aysa_resolve_technical_project_id(intval($project_id));
    }

    private function aysa_schema_inventory_table() {
        global $wpdb;
        return $wpdb->prefix . 'aysa_schema_inventory';
    }

    private function aysa_schema_inventory_schema_version() {
        return '1.0.0';
    }

    private function aysa_schema_inventory_schema_version_option() {
        return 'aysa_schema_inventory_schema_version';
    }

    private function aysa_schema_inventory_delta_queue_option_key() {
        return 'aysa_schema_inventory_delta_queue_v1';
    }

    private function aysa_schema_inventory_context_key($project_id) {
        return 'aysa_schema_inventory_context_' . intval($project_id);
    }

    private function aysa_schema_inventory_override_key($project_id) {
        return 'aysa_schema_inventory_overrides_' . intval($project_id);
    }

    public function aysa_schema_inventory_maybe_upgrade_table() {
        $current = (string) get_option($this->aysa_schema_inventory_schema_version_option(), '');
        if ($current === $this->aysa_schema_inventory_schema_version()) {
            return;
        }

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

        $table = $this->aysa_schema_inventory_table();
        $charset_collate = $wpdb->get_charset_collate();
        $sql = "CREATE TABLE {$table} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            project_id bigint(20) unsigned NOT NULL,
            url_normalized varchar(500) NOT NULL,
            title text DEFAULT NULL,
            object_id bigint(20) unsigned DEFAULT NULL,
            object_kind varchar(30) NOT NULL DEFAULT 'unknown',
            post_type varchar(80) DEFAULT NULL,
            taxonomy varchar(80) DEFAULT NULL,
            page_family varchar(40) NOT NULL DEFAULT 'page',
            entity_group varchar(40) NOT NULL DEFAULT 'page',
            google_policy_type varchar(60) NOT NULL DEFAULT 'generic_inventory_only',
            google_supported tinyint(1) NOT NULL DEFAULT 0,
            generation_mode varchar(40) NOT NULL DEFAULT 'inventory_only',
            not_applicable_reason text DEFAULT NULL,
            discovery_status varchar(30) NOT NULL DEFAULT 'discovered',
            generation_status varchar(40) NOT NULL DEFAULT 'inventory_only',
            google_readiness_status varchar(40) NOT NULL DEFAULT 'not_applicable',
            needs_takeover tinyint(1) NOT NULL DEFAULT 0,
            needs_fields longtext DEFAULT NULL,
            generated_by_aysa tinyint(1) NOT NULL DEFAULT 0,
            has_conflict tinyint(1) NOT NULL DEFAULT 0,
            content_fingerprint varchar(64) DEFAULT NULL,
            source_last_modified_at datetime DEFAULT NULL,
            last_generated_at datetime DEFAULT NULL,
            last_validated_at datetime DEFAULT NULL,
            last_seen_at datetime DEFAULT NULL,
            last_error text DEFAULT NULL,
            payload_snapshot_json longtext DEFAULT NULL,
            created_at datetime NOT NULL,
            updated_at datetime NOT NULL,
            PRIMARY KEY  (id),
            UNIQUE KEY project_url (project_id, url_normalized),
            KEY project_group (project_id, entity_group),
            KEY project_policy (project_id, google_policy_type),
            KEY project_generated (project_id, generated_by_aysa),
            KEY project_readiness (project_id, google_readiness_status),
            KEY object_lookup (object_kind, object_id)
        ) {$charset_collate};";

        dbDelta($sql);
        update_option($this->aysa_schema_inventory_schema_version_option(), $this->aysa_schema_inventory_schema_version(), false);
    }

    public function aysa_schema_inventory_ensure_scheduled_reconcile() {
        if (!wp_next_scheduled('aysa_schema_inventory_nightly_reconcile_event')) {
            wp_schedule_event(time() + HOUR_IN_SECONDS, 'daily', 'aysa_schema_inventory_nightly_reconcile_event');
        }
    }

    private function aysa_schema_inventory_policy_catalog() {
        return array(
            'organization_website' => array(
                'google_policy_type' => 'organization_website',
                'google_supported' => true,
                'generation_mode' => 'generate_or_validate',
                'required_fields' => array('name', 'url', 'logo'),
                'recommended_fields' => array('sameAs', 'contactPoint'),
                'not_applicable_reason' => '',
                'label' => 'Organization + WebSite',
            ),
            'local_business' => array(
                'google_policy_type' => 'local_business',
                'google_supported' => true,
                'generation_mode' => 'generate_or_validate',
                'required_fields' => array('name', 'address', 'telephone'),
                'recommended_fields' => array('openingHours', 'sameAs'),
                'not_applicable_reason' => '',
                'label' => 'LocalBusiness',
            ),
            'product_offer' => array(
                'google_policy_type' => 'product_offer',
                'google_supported' => true,
                'generation_mode' => 'generate_or_validate',
                'required_fields' => array('name', 'offers.price', 'offers.availability'),
                'recommended_fields' => array('image', 'sku', 'brand'),
                'not_applicable_reason' => '',
                'label' => 'Product + Offer',
            ),
            'article' => array(
                'google_policy_type' => 'article',
                'google_supported' => true,
                'generation_mode' => 'generate_or_validate',
                'required_fields' => array('headline', 'image', 'author', 'datePublished', 'dateModified'),
                'recommended_fields' => array('publisher'),
                'not_applicable_reason' => '',
                'label' => 'Article / BlogPosting',
            ),
            'faq_page' => array(
                'google_policy_type' => 'faq_page',
                'google_supported' => true,
                'generation_mode' => 'generate_or_validate',
                'required_fields' => array('mainEntity'),
                'recommended_fields' => array(),
                'not_applicable_reason' => '',
                'label' => 'FAQPage',
            ),
            'generic_inventory_only' => array(
                'google_policy_type' => 'generic_inventory_only',
                'google_supported' => false,
                'generation_mode' => 'inventory_only',
                'required_fields' => array(),
                'recommended_fields' => array(),
                'not_applicable_reason' => 'No primary Google rich result target is defined for this page type in V2.',
                'label' => 'Inventory only',
            ),
        );
    }

    private function aysa_schema_inventory_get_overrides($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array();
        }

        $overrides = get_option($this->aysa_schema_inventory_override_key($project_id), array());
        return is_array($overrides) ? $overrides : array();
    }

    private function aysa_schema_inventory_save_overrides($project_id, array $overrides) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return;
        }

        update_option($this->aysa_schema_inventory_override_key($project_id), $overrides, false);
    }

    private function aysa_schema_inventory_get_override($project_id, $url) {
        $url = $this->aysa_adjustments_normalize_schema_url((string) $url);
        if ($url === '') {
            return array();
        }

        $overrides = $this->aysa_schema_inventory_get_overrides($project_id);
        $override = $overrides[$url] ?? array();
        return is_array($override) ? $override : array();
    }

    private function aysa_schema_inventory_set_override($project_id, $url, array $override) {
        $project_id = intval($project_id);
        $url = $this->aysa_adjustments_normalize_schema_url((string) $url);
        if ($project_id <= 0 || $url === '') {
            return;
        }

        $overrides = $this->aysa_schema_inventory_get_overrides($project_id);
        $policy_override = sanitize_key((string) ($override['policy_override'] ?? ''));
        $inventory_only = !empty($override['inventory_only']);

        if ($policy_override === '' && !$inventory_only) {
            unset($overrides[$url]);
        } else {
            $overrides[$url] = array(
                'policy_override' => $policy_override,
                'inventory_only' => $inventory_only ? 1 : 0,
            );
        }

        $this->aysa_schema_inventory_save_overrides($project_id, $overrides);
    }

    private function aysa_schema_inventory_enqueue_delta(array $payload) {
        $queue = get_option($this->aysa_schema_inventory_delta_queue_option_key(), array());
        if (!is_array($queue)) {
            $queue = array();
        }
        $key = sanitize_key((string) ($payload['kind'] ?? '')) . ':' . sanitize_key((string) ($payload['taxonomy'] ?? '')) . ':' . intval($payload['id'] ?? 0);
        if ($key === '::0') {
            return;
        }
        $payload['queued_at'] = time();
        $queue[$key] = $payload;
        update_option($this->aysa_schema_inventory_delta_queue_option_key(), $queue, false);
        if (!wp_next_scheduled('aysa_schema_inventory_process_delta_event')) {
            wp_schedule_single_event(time() + 15, 'aysa_schema_inventory_process_delta_event');
        }
    }

    public function handle_schema_inventory_post_change($post_id, $post, $update) {
        if (!($post instanceof WP_Post)) {
            return;
        }
        if (wp_is_post_revision($post_id) || wp_is_post_autosave($post_id)) {
            return;
        }
        $this->aysa_schema_inventory_enqueue_delta(array(
            'kind' => 'post',
            'id' => intval($post_id),
            'action' => 'upsert',
        ));
    }

    public function handle_schema_inventory_post_status_transition($new_status, $old_status, $post) {
        if (!($post instanceof WP_Post)) {
            return;
        }
        if ($new_status === $old_status) {
            return;
        }
        $this->aysa_schema_inventory_enqueue_delta(array(
            'kind' => 'post',
            'id' => intval($post->ID),
            'action' => $new_status === 'publish' ? 'upsert' : 'delete',
        ));
    }

    public function handle_schema_inventory_post_delete($post_id) {
        $this->aysa_schema_inventory_enqueue_delta(array(
            'kind' => 'post',
            'id' => intval($post_id),
            'action' => 'delete',
        ));
    }

    public function handle_schema_inventory_term_change($term_id, $tt_id = 0, $taxonomy = '') {
        $taxonomy = sanitize_key((string) $taxonomy);
        if ($taxonomy === '') {
            return;
        }
        $this->aysa_schema_inventory_enqueue_delta(array(
            'kind' => 'term',
            'id' => intval($term_id),
            'taxonomy' => $taxonomy,
            'action' => 'upsert',
        ));
    }

    public function handle_schema_inventory_term_delete($term_id, $tt_id = 0, $taxonomy = '', $deleted_term = null, $object_ids = array()) {
        $taxonomy = sanitize_key((string) $taxonomy);
        if ($taxonomy === '') {
            return;
        }
        $this->aysa_schema_inventory_enqueue_delta(array(
            'kind' => 'term',
            'id' => intval($term_id),
            'taxonomy' => $taxonomy,
            'action' => 'delete',
        ));
    }

    public function handle_schema_inventory_delta_event() {
        $queue = get_option($this->aysa_schema_inventory_delta_queue_option_key(), array());
        if (!is_array($queue) || empty($queue)) {
            return;
        }

        $project_id = $this->aysa_technical_schema_resolve_project_id(0);
        $processed = 0;
        foreach ($queue as $key => $payload) {
            if ($processed >= 20) {
                break;
            }
            $processed++;
            unset($queue[$key]);
            $action = sanitize_key((string) ($payload['action'] ?? 'upsert'));
            if ($action === 'delete') {
                $this->aysa_schema_inventory_delete_by_reference($project_id, $payload);
                continue;
            }
            $page_data = $this->aysa_schema_inventory_resolve_page_data_from_reference($payload);
            if (!is_array($page_data)) {
                $this->aysa_schema_inventory_delete_by_reference($project_id, $payload);
                continue;
            }
            $legacy_row = $this->aysa_adjustments_build_schema_row($page_data, $this->aysa_adjustments_get_schema_db_lookup(), true);
            $this->aysa_schema_inventory_upsert_row($project_id, $page_data, $legacy_row);
        }

        update_option($this->aysa_schema_inventory_delta_queue_option_key(), $queue, false);
        $this->aysa_schema_inventory_update_client_context($project_id);
        if (!empty($queue) && !wp_next_scheduled('aysa_schema_inventory_process_delta_event')) {
            wp_schedule_single_event(time() + 20, 'aysa_schema_inventory_process_delta_event');
        }
    }

    public function handle_schema_inventory_nightly_reconcile_event() {
        $project_id = $this->aysa_technical_schema_resolve_project_id(0);
        $running_job = $this->aysa_technical_schema_get_current_refresh_job($project_id);
        if ($running_job && in_array((string) ($running_job['status'] ?? ''), array('queued', 'running'), true)) {
            return;
        }

        $items = array();
        foreach ((array) $this->aysa_adjustments_collect_schema_scope_pages() as $page_data) {
            if (!is_array($page_data)) {
                continue;
            }
            $url = $this->aysa_adjustments_normalize_schema_url((string) ($page_data['url'] ?? ''));
            if ($url === '') {
                continue;
            }
            $page_data['url'] = $url;
            $items[$url] = $page_data;
        }
        if (empty($items)) {
            return;
        }

        $job = $this->aysa_technical_schema_create_job($project_id, 'reconcile_inventory', 'sitewide', array_values($items), 'snapshot_refresh');
        set_transient($this->aysa_technical_schema_refresh_job_pointer_key($project_id), (string) $job['job_id'], 12 * HOUR_IN_SECONDS);
        $this->aysa_technical_schema_schedule_job((string) $job['job_id'], 1);
    }

    private function aysa_schema_inventory_resolve_page_data_from_reference(array $payload) {
        $kind = sanitize_key((string) ($payload['kind'] ?? ''));
        $id = intval($payload['id'] ?? 0);
        if ($id <= 0) {
            return null;
        }

        if ($kind === 'post') {
            $post = get_post($id);
            if (!($post instanceof WP_Post) || $post->post_status !== 'publish') {
                return null;
            }
            $post_type_object = get_post_type_object($post->post_type);
            if (!($post_type_object instanceof WP_Post_Type) || empty($post_type_object->public)) {
                return null;
            }
            if (in_array($post->post_type, array('attachment', 'revision', 'nav_menu_item', 'custom_css', 'customize_changeset'), true)) {
                return null;
            }
            $page_family = 'custom';
            if ($post->post_type === 'post') {
                $page_family = 'article';
            } elseif ($post->post_type === 'page') {
                $page_family = 'page';
            } elseif ($post->post_type === 'product') {
                $page_family = 'product';
            }
            $page_data = array(
                'url' => get_permalink($post),
                'title' => get_the_title($post),
                'page_family' => $page_family,
                'object_id' => intval($post->ID),
                'object_kind' => 'post',
                'post_type' => $post->post_type,
            );
            $page_data['entity_group'] = $this->aysa_technical_schema_resolve_entity_group($page_data);
            return $page_data;
        }

        if ($kind === 'term') {
            $taxonomy = sanitize_key((string) ($payload['taxonomy'] ?? ''));
            $taxonomy_object = $taxonomy !== '' ? get_taxonomy($taxonomy) : null;
            if (!($taxonomy_object instanceof WP_Taxonomy) || empty($taxonomy_object->public)) {
                return null;
            }
            if (in_array($taxonomy, array('post_format', 'nav_menu', 'link_category', 'product_shipping_class'), true)) {
                return null;
            }
            $term = get_term($id, $taxonomy);
            if (!($term instanceof WP_Term) || is_wp_error($term)) {
                return null;
            }
            $url = get_term_link($term);
            if (is_wp_error($url)) {
                return null;
            }
            $page_family = 'custom';
            if ($taxonomy === 'category' || $taxonomy === 'product_cat' || $taxonomy === 'product_tag') {
                $page_family = 'category';
            } elseif ($taxonomy === 'post_tag') {
                $page_family = 'tag';
            }
            $page_data = array(
                'url' => $url,
                'title' => $term->name,
                'page_family' => $page_family,
                'object_id' => intval($term->term_id),
                'object_kind' => 'term',
                'taxonomy' => $taxonomy,
            );
            $page_data['entity_group'] = $this->aysa_technical_schema_resolve_entity_group($page_data);
            return $page_data;
        }

        return null;
    }

    private function aysa_schema_inventory_delete_by_reference($project_id, array $payload) {
        global $wpdb;
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return;
        }
        $table = $this->aysa_schema_inventory_table();
        $kind = sanitize_key((string) ($payload['kind'] ?? ''));
        $id = intval($payload['id'] ?? 0);
        if ($kind === 'post' || $kind === 'term') {
            $wpdb->delete($table, array(
                'project_id' => $project_id,
                'object_kind' => $kind,
                'object_id' => $id,
            ), array('%d', '%s', '%d'));
        }
    }

    private function aysa_schema_inventory_determine_google_policy(array $page_data, array $legacy_row = array()) {
        $entity_group = sanitize_key((string) ($page_data['entity_group'] ?? 'page'));
        $page_family = sanitize_key((string) ($page_data['page_family'] ?? 'page'));
        $title = strtolower(trim((string) ($page_data['title'] ?? '')));
        $url = strtolower(trim((string) ($page_data['url'] ?? '')));
        $catalog = $this->aysa_schema_inventory_policy_catalog();
        $policy = $catalog['generic_inventory_only'];

        if ($page_family === 'homepage' || $entity_group === 'business') {
            $business_like = $page_family === 'homepage'
                || strpos($url, '/contact') !== false
                || strpos($url, '/despre') !== false
                || strpos($url, '/about') !== false
                || strpos($url, '/location') !== false
                || strpos($url, '/showroom') !== false
                || strpos($title, 'contact') !== false
                || strpos($title, 'showroom') !== false
                || strpos($title, 'about') !== false;

            if ($page_family === 'homepage') {
                $policy = $catalog['organization_website'];
            } elseif ($business_like) {
                $has_local_data = !empty($legacy_row['business_source']) && !empty($legacy_row['nap_status']) && (string) ($legacy_row['nap_status'] ?? '') === 'ready';
                $policy = $has_local_data
                    ? $catalog['local_business']
                    : $catalog['generic_inventory_only'];
                if (!$has_local_data) {
                    $policy['not_applicable_reason'] = 'Business page is missing verified local business details.';
                }
            }
        } elseif ($entity_group === 'product') {
            $policy = $catalog['product_offer'];
        } elseif ($entity_group === 'article') {
            $policy = $catalog['article'];
        } elseif (!empty($legacy_row['primary_recommended_node']) && (string) $legacy_row['primary_recommended_node'] === 'faqpage') {
            $policy = $catalog['faq_page'];
        }

        $project_id = intval($page_data['project_id'] ?? 0);
        $override = $this->aysa_schema_inventory_get_override($project_id, (string) ($page_data['url'] ?? ''));
        $override_policy = sanitize_key((string) ($override['policy_override'] ?? ''));
        $inventory_only = !empty($override['inventory_only']);
        if ($inventory_only) {
            $policy = $catalog['generic_inventory_only'];
            $policy['not_applicable_reason'] = 'Manually set to inventory-only.';
        } elseif ($override_policy !== '' && isset($catalog[$override_policy])) {
            $policy = $catalog[$override_policy];
        }

        return $policy;
    }

    private function aysa_schema_inventory_build_entry($project_id, array $page_data, array $legacy_row = array()) {
        $project_id = intval($project_id);
        $url = $this->aysa_adjustments_normalize_schema_url((string) ($page_data['url'] ?? ''));
        $page_data['project_id'] = $project_id;
        $payload = $this->aysa_schema_inventory_determine_google_policy($page_data, $legacy_row);
        $google_supported = !empty($payload['google_supported']);
        $google_status = sanitize_key((string) ($legacy_row['google_status'] ?? ($google_supported ? 'missing_target' : 'not_applicable')));
        $status = sanitize_key((string) ($legacy_row['status'] ?? ($google_supported ? 'missing' : 'inventory_only')));
        $execution = sanitize_key((string) ($legacy_row['execution_eligibility'] ?? 'manual_review'));
        $generated_by_aysa = !empty($legacy_row['is_aysa_present']);
        $has_conflict = !empty($legacy_row['has_conflict']);

        $generation_status = 'inventory_only';
        if ($generated_by_aysa) {
            $generation_status = 'generated_by_aysa';
        } elseif (!$google_supported) {
            $generation_status = 'inventory_only';
        } elseif ($google_status === 'needs_fields') {
            $generation_status = 'needs_fields';
        } elseif ($google_status === 'needs_takeover' || $has_conflict) {
            $generation_status = 'needs_takeover';
        } elseif (in_array($execution, array('generate_safe', 'rewrite_existing_aysa', 'rewrite_takeover'), true)) {
            $generation_status = 'eligible';
        } else {
            $generation_status = 'pending_validation';
        }

        $google_readiness_status = 'not_applicable';
        if ($google_supported) {
            if ($google_status === 'ready') {
                $google_readiness_status = 'ready';
            } elseif ($google_status === 'needs_fields') {
                $google_readiness_status = 'needs_fields';
            } elseif ($has_conflict || $google_status === 'conflict') {
                $google_readiness_status = 'validation_failed';
            } else {
                $google_readiness_status = 'needs_action';
            }
        }

        $modified_gmt = null;
        if (!empty($page_data['post_type']) && !empty($page_data['object_id'])) {
            $post = get_post(intval($page_data['object_id']));
            if ($post instanceof WP_Post) {
                $modified_gmt = $post->post_modified_gmt && $post->post_modified_gmt !== '0000-00-00 00:00:00'
                    ? get_date_from_gmt($post->post_modified_gmt, 'Y-m-d H:i:s')
                    : null;
            }
        }
        if ($modified_gmt === null && !empty($page_data['taxonomy']) && !empty($page_data['object_id'])) {
            $modified_gmt = current_time('mysql');
        }

        $fingerprint = md5(wp_json_encode(array(
            'title' => (string) ($page_data['title'] ?? ''),
            'page_family' => (string) ($page_data['page_family'] ?? ''),
            'entity_group' => (string) ($page_data['entity_group'] ?? ''),
            'status' => $status,
            'google_status' => $google_status,
            'generated_by_aysa' => $generated_by_aysa ? 1 : 0,
        )));

        return array(
            'project_id' => $project_id,
            'url_normalized' => $url,
            'title' => sanitize_text_field((string) ($page_data['title'] ?? $url)),
            'object_id' => intval($page_data['object_id'] ?? 0),
            'object_kind' => sanitize_key((string) ($page_data['object_kind'] ?? (!empty($page_data['taxonomy']) ? 'term' : 'post'))),
            'post_type' => sanitize_key((string) ($page_data['post_type'] ?? '')),
            'taxonomy' => sanitize_key((string) ($page_data['taxonomy'] ?? '')),
            'page_family' => sanitize_key((string) ($page_data['page_family'] ?? 'page')),
            'entity_group' => sanitize_key((string) ($page_data['entity_group'] ?? 'page')),
            'google_policy_type' => sanitize_key((string) ($payload['google_policy_type'] ?? 'generic_inventory_only')),
            'google_supported' => $google_supported ? 1 : 0,
            'generation_mode' => sanitize_key((string) ($payload['generation_mode'] ?? 'inventory_only')),
            'not_applicable_reason' => sanitize_text_field((string) ($payload['not_applicable_reason'] ?? '')),
            'discovery_status' => 'discovered',
            'generation_status' => $generation_status,
            'google_readiness_status' => $google_readiness_status,
            'needs_takeover' => ($google_status === 'needs_takeover' || $has_conflict) ? 1 : 0,
            'needs_fields' => wp_json_encode(array_values((array) ($legacy_row['google_required_missing'] ?? $legacy_row['required_fields_missing'] ?? array()))),
            'generated_by_aysa' => $generated_by_aysa ? 1 : 0,
            'has_conflict' => $has_conflict ? 1 : 0,
            'content_fingerprint' => $fingerprint,
            'source_last_modified_at' => $modified_gmt,
            'last_generated_at' => $generated_by_aysa ? current_time('mysql') : null,
            'last_validated_at' => current_time('mysql'),
            'last_seen_at' => current_time('mysql'),
            'last_error' => sanitize_text_field((string) ($legacy_row['google_reason'] ?? '')),
            'payload_snapshot_json' => wp_json_encode(array(
                'page_data' => $page_data,
                'legacy_row' => $legacy_row,
                'policy' => $payload,
            )),
            'status_label' => sanitize_text_field((string) ($legacy_row['google_target_label'] ?? '')),
        );
    }

    private function aysa_schema_inventory_upsert_row($project_id, array $page_data, array $legacy_row = array()) {
        global $wpdb;
        $entry = $this->aysa_schema_inventory_build_entry($project_id, $page_data, $legacy_row);
        if (empty($entry['url_normalized'])) {
            return;
        }
        $table = $this->aysa_schema_inventory_table();
        $existing_id = $wpdb->get_var($wpdb->prepare(
            "SELECT id FROM {$table} WHERE project_id = %d AND url_normalized = %s LIMIT 1",
            intval($entry['project_id']),
            (string) $entry['url_normalized']
        ));

        $data = array(
            'project_id' => intval($entry['project_id']),
            'url_normalized' => (string) $entry['url_normalized'],
            'title' => (string) $entry['title'],
            'object_id' => intval($entry['object_id']),
            'object_kind' => (string) $entry['object_kind'],
            'post_type' => (string) $entry['post_type'],
            'taxonomy' => (string) $entry['taxonomy'],
            'page_family' => (string) $entry['page_family'],
            'entity_group' => (string) $entry['entity_group'],
            'google_policy_type' => (string) $entry['google_policy_type'],
            'google_supported' => intval($entry['google_supported']),
            'generation_mode' => (string) $entry['generation_mode'],
            'not_applicable_reason' => (string) $entry['not_applicable_reason'],
            'discovery_status' => (string) $entry['discovery_status'],
            'generation_status' => (string) $entry['generation_status'],
            'google_readiness_status' => (string) $entry['google_readiness_status'],
            'needs_takeover' => intval($entry['needs_takeover']),
            'needs_fields' => (string) $entry['needs_fields'],
            'generated_by_aysa' => intval($entry['generated_by_aysa']),
            'has_conflict' => intval($entry['has_conflict']),
            'content_fingerprint' => (string) $entry['content_fingerprint'],
            'source_last_modified_at' => $entry['source_last_modified_at'],
            'last_generated_at' => $entry['last_generated_at'],
            'last_validated_at' => $entry['last_validated_at'],
            'last_seen_at' => $entry['last_seen_at'],
            'last_error' => (string) $entry['last_error'],
            'payload_snapshot_json' => (string) $entry['payload_snapshot_json'],
            'updated_at' => current_time('mysql'),
        );

        if ($existing_id) {
            $wpdb->update($table, $data, array('id' => intval($existing_id)));
        } else {
            $data['created_at'] = current_time('mysql');
            $wpdb->insert($table, $data);
        }
    }

    private function aysa_schema_inventory_finalize_reconcile($project_id, array $seen_urls) {
        global $wpdb;
        $table = $this->aysa_schema_inventory_table();
        $project_id = intval($project_id);
        $seen_urls = array_values(array_filter(array_map(array($this, 'aysa_adjustments_normalize_schema_url'), $seen_urls)));
        if ($project_id <= 0) {
            return;
        }
        if (empty($seen_urls)) {
            $wpdb->delete($table, array('project_id' => $project_id), array('%d'));
            return;
        }

        $placeholders = implode(',', array_fill(0, count($seen_urls), '%s'));
        $params = array_merge(array($project_id), $seen_urls);
        $sql = $wpdb->prepare(
            "DELETE FROM {$table} WHERE project_id = %d AND url_normalized NOT IN ({$placeholders})",
            $params
        );
        $wpdb->query($sql);
    }

    private function aysa_schema_inventory_fetch_rows($project_id) {
        global $wpdb;
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array();
        }
        $table = $this->aysa_schema_inventory_table();
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$table} WHERE project_id = %d ORDER BY entity_group ASC, title ASC, url_normalized ASC",
            $project_id
        ), ARRAY_A);
        $rows = is_array($rows) ? $rows : array();
        $overrides = $this->aysa_schema_inventory_get_overrides($project_id);
        foreach ($rows as &$row) {
            $row['needs_fields'] = json_decode((string) ($row['needs_fields'] ?? '[]'), true);
            if (!is_array($row['needs_fields'])) {
                $row['needs_fields'] = array();
            }
            $payload = json_decode((string) ($row['payload_snapshot_json'] ?? '{}'), true);
            $row['payload_snapshot'] = is_array($payload) ? $payload : array();
            $override = $overrides[(string) ($row['url_normalized'] ?? '')] ?? array();
            $row['policy_override'] = sanitize_key((string) ($override['policy_override'] ?? ''));
            $row['inventory_only_override'] = !empty($override['inventory_only']) ? 1 : 0;
            $legacy = is_array($row['payload_snapshot']['legacy_row'] ?? null) ? $row['payload_snapshot']['legacy_row'] : array();
            if (!empty($row['has_conflict'])) {
                $row['ownership'] = 'mixed/conflict';
            } elseif (!empty($row['generated_by_aysa'])) {
                $row['ownership'] = 'aysa';
            } elseif (!empty($legacy['is_external_schema_present']) || !empty($row['needs_takeover'])) {
                $row['ownership'] = 'external';
            } else {
                $row['ownership'] = 'none';
            }
        }
        unset($row);
        return $rows;
    }

    private function aysa_schema_inventory_get_ui_snapshot($project_id) {
        $project_id = intval($project_id);
        $rows = $this->aysa_schema_inventory_fetch_rows($project_id);
        $summary = array(
            'inventory_total' => 0,
            'google_eligible_total' => 0,
            'needs_action_total' => 0,
            'generated_total' => 0,
            'conflicts_total' => 0,
            'google_ready_total' => 0,
            'inventory_only_total' => 0,
            'entity_groups' => array(),
        );
        $supported_groups = $this->aysa_technical_schema_supported_entity_groups();
        $group_rows = array_fill_keys($supported_groups, array());

        foreach ($rows as $row) {
            $summary['inventory_total']++;
            if (!empty($row['google_supported'])) {
                $summary['google_eligible_total']++;
            } else {
                $summary['inventory_only_total']++;
            }
            if (!empty($row['generated_by_aysa'])) {
                $summary['generated_total']++;
            }
            if ((string) ($row['google_readiness_status'] ?? '') === 'ready') {
                $summary['google_ready_total']++;
            }
            if (!empty($row['has_conflict'])) {
                $summary['conflicts_total']++;
            }
            $needs_action = !empty($row['google_supported']) && (
                !empty($row['needs_takeover'])
                || in_array((string) ($row['google_readiness_status'] ?? ''), array('needs_action', 'needs_fields', 'validation_failed'), true)
                || in_array((string) ($row['generation_status'] ?? ''), array('needs_takeover', 'needs_fields', 'pending_validation'), true)
            );
            if ($needs_action) {
                $summary['needs_action_total']++;
            }

            $group = sanitize_key((string) ($row['entity_group'] ?? 'page'));
            if (!isset($group_rows[$group])) {
                $group_rows[$group] = array();
            }
            $group_rows[$group][] = $row;
        }

        foreach ($group_rows as $group => $items) {
            $summary['entity_groups'][$group] = array(
                'label' => $this->aysa_technical_schema_entity_group_label($group),
                'total' => count($items),
                'generated' => count(array_filter($items, static function($row) {
                    return !empty($row['generated_by_aysa']);
                })),
                'google_ready' => count(array_filter($items, static function($row) {
                    return (string) ($row['google_readiness_status'] ?? '') === 'ready';
                })),
                'needs_action' => count(array_filter($items, static function($row) {
                    return !empty($row['google_supported']) && (
                        !empty($row['needs_takeover'])
                        || in_array((string) ($row['google_readiness_status'] ?? ''), array('needs_action', 'needs_fields', 'validation_failed'), true)
                        || in_array((string) ($row['generation_status'] ?? ''), array('needs_takeover', 'needs_fields', 'pending_validation'), true)
                    );
                })),
            );
        }

        $refresh_job = $this->aysa_technical_schema_get_current_refresh_job($project_id);
        $status = ($refresh_job && in_array((string) ($refresh_job['status'] ?? ''), array('queued', 'running'), true))
            ? 'reconciling'
            : 'ready';

        return array(
            'resolved_project_id' => $project_id,
            'status' => $status,
            'summary' => $summary,
            'rows' => $rows,
            'group_rows' => $group_rows,
            'needs_reconcile' => empty($rows),
            'job' => $refresh_job ? $this->aysa_technical_schema_get_job_response($refresh_job) : null,
            'updated_at' => current_time('mysql'),
        );
    }

    private function aysa_schema_inventory_update_client_context($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return;
        }
        $snapshot = $this->aysa_schema_inventory_get_ui_snapshot($project_id);
        update_option($this->aysa_schema_inventory_context_key($project_id), array(
            'project_id' => $project_id,
            'updated_at' => current_time('mysql'),
            'summary' => $snapshot['summary'],
            'recent_rows' => array_slice(array_values((array) $snapshot['rows']), 0, 20),
            'status' => $snapshot['status'],
        ), false);
    }

    public function handle_schema_inventory_summary() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }
        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_technical_schema_resolve_project_id($project_id);
        wp_send_json_success(array(
            'snapshot' => $this->aysa_schema_inventory_get_ui_snapshot($project_id),
        ));
    }

    public function handle_schema_inventory_list() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }
        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_technical_schema_resolve_project_id($project_id);
        $filter = sanitize_key((string) ($_POST['filter'] ?? 'inventory'));
        $rows = $this->aysa_schema_inventory_fetch_rows($project_id);
        $rows = array_values(array_filter($rows, static function($row) use ($filter) {
            switch ($filter) {
                case 'google_eligible':
                    return !empty($row['google_supported']);
                case 'needs_action':
                    return !empty($row['google_supported']) && (
                        !empty($row['needs_takeover'])
                        || in_array((string) ($row['google_readiness_status'] ?? ''), array('needs_action', 'needs_fields', 'validation_failed'), true)
                        || in_array((string) ($row['generation_status'] ?? ''), array('needs_takeover', 'needs_fields', 'pending_validation'), true)
                    );
                case 'generated':
                    return !empty($row['generated_by_aysa']);
                case 'conflicts':
                    return !empty($row['has_conflict']);
                case 'no_conflict':
                    return empty($row['has_conflict']);
                case 'page':
                case 'product':
                case 'ecommerce_category':
                case 'article':
                case 'business':
                    return sanitize_key((string) ($row['entity_group'] ?? 'page')) === $filter;
                default:
                    return true;
            }
        }));
        wp_send_json_success(array(
            'rows' => $rows,
        ));
    }

    private function aysa_schema_inventory_find_row($project_id, $url) {
        $url = $this->aysa_adjustments_normalize_schema_url((string) $url);
        if ($url === '') {
            return array();
        }

        foreach ($this->aysa_schema_inventory_fetch_rows($project_id) as $row) {
            if ((string) ($row['url_normalized'] ?? '') === $url) {
                return $row;
            }
        }

        return array();
    }

    private function aysa_schema_inventory_row_to_job_item(array $row) {
        $payload = is_array($row['payload_snapshot'] ?? null) ? $row['payload_snapshot'] : array();
        $legacy = is_array($payload['legacy_row'] ?? null) ? $payload['legacy_row'] : array();
        $schema_type = sanitize_key((string) ($legacy['primary_recommended_node'] ?? ''));
        if ($schema_type === '') {
            $policy_type = sanitize_key((string) ($row['google_policy_type'] ?? ''));
            $schema_type_map = array(
                'organization_website' => 'organization',
                'local_business' => 'localbusiness',
                'product_offer' => 'product',
                'article' => 'article',
                'faq_page' => 'faqpage',
            );
            $schema_type = $schema_type_map[$policy_type] ?? 'webpage';
        }

        return array(
            'url' => (string) ($row['url_normalized'] ?? ''),
            'title' => sanitize_text_field((string) ($row['title'] ?? '')),
            'page_family' => sanitize_key((string) ($row['page_family'] ?? 'page')),
            'entity_group' => sanitize_key((string) ($row['entity_group'] ?? 'page')),
            'schema_type' => $schema_type,
            'primary_recommended_node' => $schema_type,
        );
    }

    private function aysa_schema_inventory_get_row_detail($project_id, array $row) {
        $project_id = intval($project_id);
        if (empty($row)) {
            return array();
        }

        $payload = is_array($row['payload_snapshot'] ?? null) ? $row['payload_snapshot'] : array();
        $legacy = is_array($payload['legacy_row'] ?? null) ? $payload['legacy_row'] : array();
        $policy = is_array($payload['policy'] ?? null) ? $payload['policy'] : array();
        $schemas = class_exists('AysaSchemaManager')
            ? AysaSchemaManager::get_page_schemas((string) ($row['url_normalized'] ?? ''))
            : array();

        $current_schema_types = array_values(array_filter(array_unique(array_map(static function($schema) {
            return sanitize_key((string) ($schema['type'] ?? ''));
        }, is_array($schemas) ? $schemas : array()))));

        $detected_frontend_types = array_values(array_filter(array_unique(array_merge(
            array_map('sanitize_key', (array) ($legacy['detected_nodes'] ?? array())),
            array_map('sanitize_key', (array) ($legacy['generated_types'] ?? array()))
        ))));

        $available_actions = array('validate');
        if (!empty($row['google_supported']) && (string) ($row['generation_status'] ?? '') !== 'generated_by_aysa') {
            $available_actions[] = 'generate';
        }
        if (!empty($row['needs_takeover']) || !empty($row['generated_by_aysa']) || !empty($row['has_conflict'])) {
            $available_actions[] = 'rewrite';
        }
        $available_actions[] = 'disable';

        return array(
            'identity' => array(
                'url' => (string) ($row['url_normalized'] ?? ''),
                'title' => (string) ($row['title'] ?? ''),
                'entity_group' => (string) ($row['entity_group'] ?? 'page'),
                'entity_group_label' => $this->aysa_technical_schema_entity_group_label((string) ($row['entity_group'] ?? 'page')),
                'page_family' => (string) ($row['page_family'] ?? 'page'),
                'post_type' => (string) ($row['post_type'] ?? ''),
                'taxonomy' => (string) ($row['taxonomy'] ?? ''),
            ),
            'state' => array(
                'generation_status' => (string) ($row['generation_status'] ?? ''),
                'google_readiness_status' => (string) ($row['google_readiness_status'] ?? ''),
                'ownership' => (string) ($row['ownership'] ?? 'none'),
                'conflict' => !empty($row['has_conflict']),
                'conflict_reason' => sanitize_text_field((string) ($legacy['conflict_reason'] ?? $row['last_error'] ?? '')),
                'next_step' => sanitize_text_field((string) ($row['last_error'] ?? '')),
                'generated_by_aysa' => !empty($row['generated_by_aysa']),
                'google_supported' => !empty($row['google_supported']),
                'inventory_only_override' => !empty($row['inventory_only_override']),
                'policy_override' => (string) ($row['policy_override'] ?? ''),
            ),
            'policy' => array(
                'google_policy_type' => (string) ($row['google_policy_type'] ?? ''),
                'required_fields' => array_values((array) ($policy['required_fields'] ?? array())),
                'recommended_fields' => array_values((array) ($policy['recommended_fields'] ?? array())),
                'generation_mode' => (string) ($row['generation_mode'] ?? ''),
                'not_applicable_reason' => (string) ($row['not_applicable_reason'] ?? ''),
                'available_overrides' => $this->aysa_schema_inventory_policy_catalog(),
            ),
            'validation' => array(
                'needs_fields' => array_values((array) ($row['needs_fields'] ?? array())),
                'last_checked' => (string) ($row['last_validated_at'] ?? ''),
                'last_generated_at' => (string) ($row['last_generated_at'] ?? ''),
                'last_error' => (string) ($row['last_error'] ?? ''),
            ),
            'schemas' => array(
                'aysa_db_schemas' => is_array($schemas) ? $schemas : array(),
                'aysa_schema_types' => $current_schema_types,
                'frontend_detected_types' => $detected_frontend_types,
            ),
            'available_actions' => array_values(array_unique($available_actions)),
            'row' => $row,
        );
    }

    public function handle_schema_inventory_detail() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_technical_schema_resolve_project_id($project_id);
        $url = isset($_POST['url']) ? (string) wp_unslash($_POST['url']) : '';
        $row = $this->aysa_schema_inventory_find_row($project_id, $url);
        if (empty($row)) {
            wp_send_json_error('Schema inventory row not found.');
            return;
        }

        wp_send_json_success(array(
            'detail' => $this->aysa_schema_inventory_get_row_detail($project_id, $row),
        ));
    }

    public function handle_schema_inventory_auto_generate() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_technical_schema_resolve_project_id($project_id);
        $rows = array_values(array_filter($this->aysa_schema_inventory_fetch_rows($project_id), static function($row) {
            return !empty($row['google_supported']) && (string) ($row['generation_mode'] ?? '') === 'generate_or_validate';
        }));

        if (empty($rows)) {
            wp_send_json_error('No Google-eligible schema rows are ready for automatic generation.');
            return;
        }

        $items = array_values(array_filter(array_map(array($this, 'aysa_schema_inventory_row_to_job_item'), $rows), static function($item) {
            return !empty($item['url']);
        }));
        if (empty($items)) {
            wp_send_json_error('Automatic generation could not build any valid schema items.');
            return;
        }

        $job = $this->aysa_technical_schema_create_job($project_id, 'full_sitewide_orchestrator', 'selection', $items, 'worker');
        $this->aysa_technical_schema_schedule_job((string) ($job['job_id'] ?? ''), 1);

        wp_send_json_success(array(
            'job' => $this->aysa_technical_schema_get_job_response((array) $job),
            'selected_count' => count($items),
        ));
    }

    public function handle_schema_inventory_row_action() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_technical_schema_resolve_project_id($project_id);
        $url = isset($_POST['url']) ? (string) wp_unslash($_POST['url']) : '';
        $action = sanitize_key((string) ($_POST['row_action'] ?? 'validate'));
        $row = $this->aysa_schema_inventory_find_row($project_id, $url);
        if (empty($row)) {
            wp_send_json_error('Schema inventory row not found.');
            return;
        }

        $mode_map = array(
            'generate' => 'generate_safe',
            'rewrite' => 'rewrite_recommended',
            'validate' => 'validate',
            'disable' => 'disable_selected',
        );
        $mode = $mode_map[$action] ?? 'validate';
        $job = $this->aysa_technical_schema_create_job(
            $project_id,
            $mode,
            'selection',
            array($this->aysa_schema_inventory_row_to_job_item($row)),
            'worker'
        );
        $this->aysa_technical_schema_schedule_job((string) ($job['job_id'] ?? ''), 1);

        wp_send_json_success(array(
            'job' => $this->aysa_technical_schema_get_job_response((array) $job),
        ));
    }

    public function handle_schema_inventory_row_update() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_technical_schema_resolve_project_id($project_id);
        $url = isset($_POST['url']) ? (string) wp_unslash($_POST['url']) : '';
        $row = $this->aysa_schema_inventory_find_row($project_id, $url);
        if (empty($row)) {
            wp_send_json_error('Schema inventory row not found.');
            return;
        }

        $policy_override = sanitize_key((string) ($_POST['policy_override'] ?? ''));
        $catalog = $this->aysa_schema_inventory_policy_catalog();
        if ($policy_override !== '' && !isset($catalog[$policy_override])) {
            wp_send_json_error('Invalid policy override.');
            return;
        }
        $inventory_only = !empty($_POST['inventory_only']);
        $this->aysa_schema_inventory_set_override($project_id, $url, array(
            'policy_override' => $policy_override,
            'inventory_only' => $inventory_only,
        ));

        $payload = is_array($row['payload_snapshot'] ?? null) ? $row['payload_snapshot'] : array();
        $page_data = is_array($payload['page_data'] ?? null) ? $payload['page_data'] : array();
        $legacy_row = is_array($payload['legacy_row'] ?? null) ? $payload['legacy_row'] : array();
        if (!empty($page_data)) {
            $this->aysa_schema_inventory_upsert_row($project_id, $page_data, $legacy_row);
        }
        $this->aysa_schema_inventory_update_client_context($project_id);
        $updated_row = $this->aysa_schema_inventory_find_row($project_id, $url);

        wp_send_json_success(array(
            'detail' => $this->aysa_schema_inventory_get_row_detail($project_id, $updated_row),
        ));
    }

    private function aysa_adjustments_get_cached_technical_schema_snapshot($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return null;
        }

        $cached = get_transient($this->aysa_adjustments_get_schema_cache_key($project_id, 'snapshot'));
        return is_array($cached) ? $cached : null;
    }

    private function aysa_adjustments_get_cached_technical_schema_summary($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return null;
        }

        $cached = get_transient($this->aysa_adjustments_get_schema_cache_key($project_id, 'summary'));
        return is_array($cached) ? $cached : null;
    }

    private function aysa_adjustments_get_cached_or_idle_technical_schema_summary($project_id) {
        $summary = $this->aysa_adjustments_get_cached_technical_schema_summary($project_id);
        if (is_array($summary)) {
            return $summary;
        }

        return array(
            'status' => 'idle',
            'summary' => array(
                'total_pages' => 0,
                'active' => 0,
                'missing' => 0,
                'wrong_type' => 0,
                'incomplete' => 0,
                'conflict' => 0,
                'blocked_output' => 0,
                'blocked' => 0,
                'safe_auto_generate' => 0,
                'needs_manual_review' => 0,
                'google_target_pages' => 0,
                'google_ready_pages' => 0,
                'website_wide_ready_pages' => 0,
                'page_level_ready_pages' => 0,
            ),
            'queue_preview_summary' => array(),
            'policy_summary' => $this->aysa_adjustments_get_schema_policy_summary(),
            'has_data' => false,
            'needs_refresh' => true,
        );
    }

    private function aysa_adjustments_write_technical_schema_snapshot_cache($project_id, array $snapshot) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return;
        }

        set_transient(
            $this->aysa_adjustments_get_schema_cache_key($project_id, 'snapshot'),
            $snapshot,
            HOUR_IN_SECONDS
        );
        set_transient(
            $this->aysa_adjustments_get_schema_cache_key($project_id, 'summary'),
            array(
                'status' => sanitize_key((string) ($snapshot['status'] ?? 'idle')),
                'summary' => is_array($snapshot['summary'] ?? null) ? $snapshot['summary'] : array(),
                'queue_preview_summary' => is_array($snapshot['queue_preview_summary'] ?? null) ? $snapshot['queue_preview_summary'] : array(),
                'policy_summary' => is_array($snapshot['policy_summary'] ?? null) ? $snapshot['policy_summary'] : array(),
                'has_data' => !empty($snapshot['has_data']),
                'needs_refresh' => !empty($snapshot['needs_refresh']),
                'updated_at' => current_time('mysql'),
            ),
            HOUR_IN_SECONDS
        );
    }

    private function aysa_adjustments_delete_technical_schema_snapshot_cache($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return;
        }

        delete_transient($this->aysa_adjustments_get_schema_cache_key($project_id, 'snapshot'));
        delete_transient($this->aysa_adjustments_get_schema_cache_key($project_id, 'summary'));
    }

    private function aysa_adjustments_normalize_cached_technical_schema_snapshot($snapshot) {
        if (!is_array($snapshot)) {
            return null;
        }

        $coverage_rows = isset($snapshot['coverage_rows']) && is_array($snapshot['coverage_rows'])
            ? array_values(array_filter($snapshot['coverage_rows'], 'is_array'))
            : array();

        if (empty($coverage_rows)) {
            return $snapshot;
        }

        $supported_groups = $this->aysa_technical_schema_supported_entity_groups();
        $supported_coverage_rows = array_values(array_filter($coverage_rows, function ($row) use ($supported_groups) {
            $entity_group = sanitize_key((string) ($row['entity_group'] ?? ''));
            return in_array($entity_group, $supported_groups, true);
        }));

        $cached_total = intval($snapshot['summary']['total_pages'] ?? 0);
        $supported_total = count($supported_coverage_rows);
        $entity_rows = isset($snapshot['entity_rows']) && is_array($snapshot['entity_rows']) ? $snapshot['entity_rows'] : array();
        $entity_total = 0;
        foreach ($supported_groups as $entity_group) {
            $entity_total += count(isset($entity_rows[$entity_group]) && is_array($entity_rows[$entity_group]) ? $entity_rows[$entity_group] : array());
        }

        $missing_supported_group = false;
        foreach ($supported_groups as $entity_group) {
            if (!array_key_exists($entity_group, $entity_rows)) {
                $missing_supported_group = true;
                break;
            }
        }

        $has_unsupported_group = count($coverage_rows) !== $supported_total;
        $needs_rebuild = $missing_supported_group
            || $has_unsupported_group
            || $cached_total !== $supported_total
            || $entity_total !== $supported_total;

        if (!$needs_rebuild) {
            return $snapshot;
        }

        $rebuilt = $this->aysa_adjustments_build_technical_schema_snapshot_from_rows($coverage_rows);
        if (!empty($snapshot['status']) && empty($rebuilt['status'])) {
            $rebuilt['status'] = sanitize_key((string) $snapshot['status']);
        }
        if (array_key_exists('needs_refresh', $snapshot)) {
            $rebuilt['needs_refresh'] = !empty($snapshot['needs_refresh']);
        }
        if (isset($snapshot['snapshot_updated_at'])) {
            $rebuilt['snapshot_updated_at'] = sanitize_text_field((string) $snapshot['snapshot_updated_at']);
        }

        return $rebuilt;
    }

    private function aysa_adjustments_get_cached_or_idle_technical_schema_snapshot($project_id) {
        $project_id = intval($project_id);
        $state = get_option($this->aysa_technical_schema_state_key($project_id), array());
        if (!is_array($state)) {
            $state = array();
        }
        $state_status = sanitize_key((string) ($state['status'] ?? 'idle'));
        $snapshot = $this->aysa_adjustments_get_cached_technical_schema_snapshot($project_id);
        if (is_array($snapshot)) {
            $normalized_snapshot = $this->aysa_adjustments_normalize_cached_technical_schema_snapshot($snapshot);
            if (is_array($normalized_snapshot) && !empty($normalized_snapshot)) {
                if (in_array($state_status, array('queued', 'running', 'failed', 'blocked'), true)) {
                    $normalized_snapshot['status'] = $state_status;
                } elseif ($state_status === 'completed' && empty($normalized_snapshot['status'])) {
                    $normalized_snapshot['status'] = $state_status;
                }
                $normalized_snapshot['run_id'] = intval($state['run_id'] ?? ($normalized_snapshot['run_id'] ?? 0));
            }
            if (is_array($normalized_snapshot) && $normalized_snapshot !== $snapshot) {
                $this->aysa_adjustments_write_technical_schema_snapshot_cache($project_id, $normalized_snapshot);
                return $normalized_snapshot;
            }
            return $normalized_snapshot;
        }

        return array(
            'id' => 'technical_schema',
            'status' => in_array($state_status, array('queued', 'running', 'completed', 'failed', 'blocked'), true) ? $state_status : 'idle',
            'summary' => array(
                'total_pages' => 0,
                'active' => 0,
                'missing' => 0,
                'wrong_type' => 0,
                'incomplete' => 0,
                'conflict' => 0,
                'blocked_output' => 0,
                'blocked' => 0,
                'safe_auto_generate' => 0,
                'needs_manual_review' => 0,
                'google_target_pages' => 0,
                'google_ready_pages' => 0,
                'website_wide_ready_pages' => 0,
                'page_level_ready_pages' => 0,
            ),
            'count' => 0,
            'coverage_rows' => array(),
            'validation_rows' => array(),
            'generated_rows' => array(),
            'conflict_rows' => array(),
            'queue_preview_summary' => array(),
            'policy_summary' => $this->aysa_adjustments_get_schema_policy_summary(),
            'has_data' => false,
            'needs_refresh' => true,
            'run_id' => intval($state['run_id'] ?? 0),
        );
    }

    private function aysa_adjustments_get_schema_db_lookup() {
        global $wpdb;

        $table_name = $wpdb->prefix . 'aysa_schemas';
        $active_schema_urls = $wpdb->get_results(
            "SELECT id, url, schema_type, schema_data, is_active, created_at, updated_at FROM {$table_name} WHERE is_active = 1",
            ARRAY_A
        );

        $schema_lookup = array();
        if (is_array($active_schema_urls)) {
            foreach ($active_schema_urls as $schema_row) {
                if (!is_array($schema_row)) {
                    continue;
                }
                $url = $this->aysa_adjustments_normalize_schema_url((string) ($schema_row['url'] ?? ''));
                if ($url === '') {
                    continue;
                }
                if (!isset($schema_lookup[$url])) {
                    $schema_lookup[$url] = array(
                        'types' => array(),
                        'rows' => array(),
                    );
                }
                $type = sanitize_key((string) ($schema_row['schema_type'] ?? ''));
                if ($type !== '' && !in_array($type, $schema_lookup[$url]['types'], true)) {
                    $schema_lookup[$url]['types'][] = $type;
                }
                $decoded = json_decode((string) ($schema_row['schema_data'] ?? ''), true);
                $schema_lookup[$url]['rows'][] = array(
                    'id' => intval($schema_row['id'] ?? 0),
                    'type' => $type,
                    'data' => is_array($decoded) ? $decoded : array(),
                    'updated_at' => sanitize_text_field((string) ($schema_row['updated_at'] ?? '')),
                );
            }
        }
        return $schema_lookup;
    }

    private function aysa_technical_schema_supported_entity_groups() {
        return array('page', 'product', 'ecommerce_category', 'article', 'business');
    }

    private function aysa_technical_schema_is_entity_group_supported($entity_group) {
        return in_array(sanitize_key((string) $entity_group), $this->aysa_technical_schema_supported_entity_groups(), true);
    }

    private function aysa_technical_schema_entity_group_label($entity_group) {
        switch (sanitize_key((string) $entity_group)) {
            case 'page':
                return 'Normal Pages';
            case 'product':
                return 'Products';
            case 'ecommerce_category':
                return 'Ecommerce Categories';
            case 'article':
                return 'Articles';
            case 'business':
                return 'Business Schema';
            default:
                return 'Other';
        }
    }

    private function aysa_technical_schema_resolve_entity_group(array $page_data) {
        $page_family = sanitize_key((string) ($page_data['page_family'] ?? ''));
        $post_type = sanitize_key((string) ($page_data['post_type'] ?? ''));
        $taxonomy = sanitize_key((string) ($page_data['taxonomy'] ?? ''));

        if ($page_family === 'homepage') {
            return 'business';
        }

        if ($page_family === 'page' && $this->aysa_adjustments_is_local_business_override_page($page_data)) {
            return 'business';
        }

        if ($page_family === 'product' || $post_type === 'product') {
            return 'product';
        }

        if ($page_family === 'article' || $post_type === 'post') {
            return 'article';
        }

        if (in_array($taxonomy, array('product_cat', 'product_tag'), true)) {
            return 'ecommerce_category';
        }

        return 'page';
    }

    private function aysa_adjustments_collect_schema_scope_pages() {
        $pages_to_check = array(
            array(
                'url' => home_url('/'),
                'title' => get_bloginfo('name'),
                'page_family' => 'homepage',
                'entity_group' => 'business',
                'object_id' => intval(get_option('page_on_front', 0)),
                'post_type' => 'page',
            ),
        );

        $post_types = get_post_types(array('public' => true), 'objects');
        foreach ($post_types as $post_type => $post_type_object) {
            if (!($post_type_object instanceof WP_Post_Type)) {
                continue;
            }
            if (in_array($post_type, array('attachment', 'revision', 'nav_menu_item', 'custom_css', 'customize_changeset'), true)) {
                continue;
            }

            $family = 'custom';
            if ($post_type === 'post') {
                $family = 'article';
            } elseif ($post_type === 'page') {
                $family = 'page';
            } elseif ($post_type === 'product') {
                $family = 'product';
            }

            $paged = 1;
            do {
                $query = new WP_Query(array(
                    'post_type' => $post_type,
                    'post_status' => 'publish',
                    'posts_per_page' => 100,
                    'paged' => $paged,
                    'orderby' => 'ID',
                    'order' => 'ASC',
                    'fields' => 'ids',
                    'no_found_rows' => false,
                    'update_post_meta_cache' => false,
                    'update_post_term_cache' => false,
                ));

                $post_ids = is_array($query->posts) ? $query->posts : array();
                foreach ($post_ids as $post_id) {
                    $page_data = array(
                        'url' => get_permalink($post_id),
                        'title' => get_the_title($post_id),
                        'page_family' => $family,
                        'object_id' => intval($post_id),
                        'post_type' => $post_type,
                    );
                    $page_data['entity_group'] = $this->aysa_technical_schema_resolve_entity_group($page_data);
                    $pages_to_check[] = array(
                        'url' => $page_data['url'],
                        'title' => $page_data['title'],
                        'page_family' => $page_data['page_family'],
                        'entity_group' => $page_data['entity_group'],
                        'object_id' => $page_data['object_id'],
                        'post_type' => $page_data['post_type'],
                    );
                }

                $max_pages = max(1, intval($query->max_num_pages));
                $paged++;
                wp_reset_postdata();
            } while (!empty($post_ids) && $paged <= $max_pages);
        }

        $taxonomies = get_taxonomies(array('public' => true), 'objects');
        foreach ($taxonomies as $taxonomy => $taxonomy_object) {
            if (!($taxonomy_object instanceof WP_Taxonomy)) {
                continue;
            }
            if (in_array($taxonomy, array('post_format', 'nav_menu', 'link_category', 'product_shipping_class'), true)) {
                continue;
            }

            $page_family = 'custom';
            if ($taxonomy === 'category') {
                $page_family = 'category';
            } elseif ($taxonomy === 'post_tag') {
                $page_family = 'tag';
            } elseif (in_array($taxonomy, array('product_cat', 'product_tag'), true)) {
                $page_family = 'category';
            }

            $offset = 0;
            $batch_size = 100;
            do {
                $terms = get_terms(array(
                    'taxonomy' => $taxonomy,
                    'hide_empty' => false,
                    'orderby' => 'term_id',
                    'order' => 'ASC',
                    'number' => $batch_size,
                    'offset' => $offset,
                ));

                if (is_wp_error($terms) || empty($terms)) {
                    break;
                }

                foreach ($terms as $term) {
                    if (!($term instanceof WP_Term)) {
                        continue;
                    }
                    $term_url = get_term_link($term);
                    if (is_wp_error($term_url)) {
                        continue;
                    }
                    $term_page_data = array(
                        'url' => $term_url,
                        'title' => $term->name,
                        'page_family' => $page_family,
                        'object_id' => intval($term->term_id),
                        'taxonomy' => $taxonomy,
                    );
                    $term_page_data['entity_group'] = $this->aysa_technical_schema_resolve_entity_group($term_page_data);
                    $pages_to_check[] = array(
                        'url' => $term_page_data['url'],
                        'title' => $term_page_data['title'],
                        'page_family' => $term_page_data['page_family'],
                        'entity_group' => $term_page_data['entity_group'],
                        'object_id' => $term_page_data['object_id'],
                        'taxonomy' => $term_page_data['taxonomy'],
                    );
                }

                $offset += $batch_size;
            } while (count($terms) === $batch_size);
        }

        $unique_pages = array();
        $seen_urls = array();
        foreach ($pages_to_check as $page_data) {
            $url = $this->aysa_adjustments_normalize_schema_url((string) ($page_data['url'] ?? ''));
            if ($url === '' || isset($seen_urls[$url])) {
                continue;
            }
            $seen_urls[$url] = true;
            $page_data['url'] = $url;
            $unique_pages[] = $page_data;
        }

        usort($unique_pages, static function ($a, $b) {
            return strcasecmp((string) ($a['title'] ?? $a['url'] ?? ''), (string) ($b['title'] ?? $b['url'] ?? ''));
        });

        return $unique_pages;
    }

    private function aysa_adjustments_flatten_schema_nodes($data, &$nodes) {
        if (empty($data)) {
            return;
        }
        if (is_array($data) && array_keys($data) === range(0, count($data) - 1)) {
            foreach ($data as $item) {
                $this->aysa_adjustments_flatten_schema_nodes($item, $nodes);
            }
            return;
        }
        if (!is_array($data)) {
            return;
        }
        if (isset($data['@graph']) && is_array($data['@graph'])) {
            foreach ($data['@graph'] as $item) {
                $this->aysa_adjustments_flatten_schema_nodes($item, $nodes);
            }
        }
        if (!isset($data['@type'])) {
            return;
        }
        $types = array_values(array_filter(array_map('strval', (array) $data['@type'])));
        if (empty($types)) {
            return;
        }
        $nodes[] = array(
            'types' => $types,
            'data' => $data,
            'source' => 'jsonld',
        );
    }

    private function aysa_adjustments_parse_schema_nodes_from_html($html) {
        $nodes = array();
        $has_debug_comments = strpos((string) $html, 'AYSA Schema Debug:') !== false;
        if ($html !== '' && preg_match_all('/<script[^>]*type=["\']application\/ld\+json["\'][^>]*>(.*?)<\/script>/is', $html, $matches)) {
            foreach ((array) ($matches[1] ?? array()) as $json_blob) {
                $decoded = json_decode(html_entity_decode(trim((string) $json_blob)), true);
                $this->aysa_adjustments_flatten_schema_nodes($decoded, $nodes);
            }
        }
        $has_breadcrumb_markup = (bool) preg_match('/schema\.org\/BreadcrumbList|aria-label=["\']breadcrumb["\']|class=["\'][^"\']*breadcrumb/i', (string) $html);
        $has_faq_markup = (bool) preg_match('/schema\.org\/FAQPage|schema\.org\/Question|schema\.org\/Answer|întrebări frecvente|frequently asked questions/i', (string) $html);
        if ($has_breadcrumb_markup && empty(array_filter($nodes, static function ($node) { return in_array('BreadcrumbList', (array) ($node['types'] ?? array()), true); }))) {
            $nodes[] = array('types' => array('BreadcrumbList'), 'data' => array(), 'source' => 'microdata');
        }
        if ($has_faq_markup && empty(array_filter($nodes, static function ($node) { return in_array('FAQPage', (array) ($node['types'] ?? array()), true); }))) {
            $nodes[] = array('types' => array('FAQPage'), 'data' => array(), 'source' => 'microdata');
        }
        return array(
            'nodes' => $nodes,
            'has_debug_comments' => $has_debug_comments,
            'has_breadcrumb_markup' => $has_breadcrumb_markup,
            'has_faq_markup' => $has_faq_markup,
        );
    }

    private function aysa_adjustments_get_schema_frontend_snapshot($url, $force_refresh = false) {
        $cache_key = $this->aysa_adjustments_get_schema_cache_key(md5((string) $url), 'frontend');
        if (!$force_refresh) {
            $cached = get_transient($cache_key);
            if (is_array($cached)) {
                return $cached;
            }
        }
        $response = wp_remote_get($url, array(
            'timeout' => 8,
            'redirection' => 3,
            'user-agent' => 'AYSA-Schema-Audit/1.0',
        ));
        $body = '';
        if (!is_wp_error($response)) {
            $body = (string) wp_remote_retrieve_body($response);
        }
        $parsed = $this->aysa_adjustments_parse_schema_nodes_from_html($body);
        $parsed['fetched_at'] = current_time('mysql');
        set_transient($cache_key, $parsed, HOUR_IN_SECONDS);
        return $parsed;
    }

    private function aysa_adjustments_has_cached_schema_frontend_snapshot($url) {
        $cache_key = $this->aysa_adjustments_get_schema_cache_key(md5((string) $url), 'frontend');
        $cached = get_transient($cache_key);
        return is_array($cached) && !empty($cached);
    }

    private function aysa_adjustments_sanitize_business_snapshot(array $raw, $source = 'local_fallback') {
        $company_name = trim((string) ($raw['company_name'] ?? $raw['name'] ?? get_bloginfo('name')));
        $phone = trim((string) ($raw['phone'] ?? $raw['telephone'] ?? ''));
        $email = trim((string) ($raw['email'] ?? ''));
        $address_line_1 = trim((string) ($raw['address_line_1'] ?? $raw['streetAddress'] ?? $raw['address'] ?? ''));
        $address_line_2 = trim((string) ($raw['address_line_2'] ?? ''));
        $city = trim((string) ($raw['city'] ?? $raw['addressLocality'] ?? ''));
        $region = trim((string) ($raw['region'] ?? $raw['addressRegion'] ?? ''));
        $postal_code = trim((string) ($raw['postal_code'] ?? $raw['postalCode'] ?? ''));
        $country = trim((string) ($raw['country'] ?? $raw['addressCountry'] ?? 'Romania'));
        $latitude = trim((string) ($raw['latitude'] ?? $raw['lat'] ?? ($raw['geo']['latitude'] ?? $raw['geo']['lat'] ?? '')));
        $longitude = trim((string) ($raw['longitude'] ?? $raw['lng'] ?? $raw['lon'] ?? ($raw['geo']['longitude'] ?? $raw['geo']['lng'] ?? $raw['geo']['lon'] ?? '')));
        $opening_hours = array_values(array_filter(array_map('sanitize_text_field', (array) ($raw['opening_hours'] ?? $raw['openingHours'] ?? array()))));
        $same_as = array_values(array_filter(array_map('esc_url_raw', (array) ($raw['same_as'] ?? $raw['sameAs'] ?? array()))));

        return array(
            'company_name' => $company_name,
            'phone' => $phone,
            'email' => $email,
            'address_line_1' => $address_line_1,
            'address_line_2' => $address_line_2,
            'city' => $city,
            'region' => $region,
            'postal_code' => $postal_code,
            'country' => $country,
            'latitude' => $latitude,
            'longitude' => $longitude,
            'opening_hours' => $opening_hours,
            'same_as' => $same_as,
            'source' => sanitize_key((string) $source),
        );
    }

    private function aysa_adjustments_get_google_maps_business_snapshot() {
        $candidate_options = array(
            'aysa_google_maps_business_snapshot',
            'aysa_google_business_profile_snapshot',
            'aysa_google_maps_nap_snapshot',
            'aysa_google_business_snapshot',
        );

        foreach ($candidate_options as $option_name) {
            $snapshot = get_option($option_name, array());
            if (!is_array($snapshot) || empty($snapshot)) {
                continue;
            }

            $normalized = $this->aysa_adjustments_sanitize_business_snapshot($snapshot, 'google_maps');
            if (
                $normalized['company_name'] !== ''
                && ($normalized['address_line_1'] !== '' || $normalized['city'] !== '')
                && ($normalized['phone'] !== '' || $normalized['email'] !== '')
            ) {
                return $normalized;
            }
        }

        return array();
    }

    private function aysa_adjustments_get_schema_business_context() {
        $general = get_option('aysa_general_info', array());
        $local = $this->aysa_adjustments_sanitize_business_snapshot(array(
            'company_name' => get_option('aysa_company_name', (string) ($general['company_name'] ?? get_bloginfo('name'))),
            'phone' => get_option('aysa_phone_number', get_option('aysa_contact_phone', (string) ($general['phone'] ?? ''))),
            'email' => get_option('aysa_email_address', get_option('admin_email', (string) ($general['email'] ?? ''))),
            'address_line_1' => get_option('aysa_address', (string) ($general['address'] ?? $general['streetAddress'] ?? '')),
            'address_line_2' => (string) ($general['address_line_2'] ?? ''),
            'city' => get_option('aysa_city', (string) ($general['city'] ?? $general['addressLocality'] ?? '')),
            'region' => (string) ($general['region'] ?? $general['addressRegion'] ?? ''),
            'postal_code' => (string) ($general['postal_code'] ?? $general['postalCode'] ?? ''),
            'country' => (string) ($general['country'] ?? $general['addressCountry'] ?? 'Romania'),
            'latitude' => (string) ($general['latitude'] ?? $general['lat'] ?? ''),
            'longitude' => (string) ($general['longitude'] ?? $general['lng'] ?? ''),
            'opening_hours' => (array) ($general['opening_hours'] ?? $general['openingHours'] ?? array()),
            'same_as' => (array) ($general['same_as'] ?? $general['sameAs'] ?? array()),
        ), 'local_fallback');
        $google_maps = $this->aysa_adjustments_get_google_maps_business_snapshot();

        if (!empty($google_maps)) {
            $google_maps['fallback'] = $local;
            return $google_maps;
        }

        return $local;
    }

    private function aysa_adjustments_has_local_business_data(array $context = array()) {
        if (empty($context)) {
            $context = $this->aysa_adjustments_get_schema_business_context();
        }

        return trim((string) ($context['company_name'] ?? '')) !== ''
            && (
                trim((string) ($context['address_line_1'] ?? '')) !== ''
                || trim((string) ($context['city'] ?? '')) !== ''
            )
            && (
                trim((string) ($context['phone'] ?? '')) !== ''
                || trim((string) ($context['email'] ?? '')) !== ''
            );
    }

    private function aysa_adjustments_is_local_business_override_page(array $page_data) {
        if ((string) ($page_data['page_family'] ?? '') !== 'page') {
            return false;
        }

        $haystack = strtolower(trim(
            (string) ($page_data['url'] ?? '') . ' ' .
            (string) ($page_data['title'] ?? '')
        ));
        if ($haystack === '') {
            return false;
        }

        foreach (array('contact', 'contacte', 'despre', 'despre-noi', 'about', 'locatie', 'locație', 'showroom', 'magazin', 'store') as $token) {
            if (strpos($haystack, $token) !== false) {
                return true;
            }
        }

        return false;
    }

    private function aysa_adjustments_get_schema_policy_summary() {
        return array(
            'homepage' => array(
                'primary' => 'WebSite',
                'business_nodes' => array('Organization', 'LocalBusiness'),
                'support_nodes' => array('FAQPage'),
            ),
            'product' => array(
                'primary' => 'Product',
                'business_nodes' => array(),
                'support_nodes' => array('BreadcrumbList', 'FAQPage'),
            ),
            'article' => array(
                'primary' => 'Article',
                'business_nodes' => array(),
                'support_nodes' => array('BreadcrumbList', 'FAQPage'),
            ),
            'page' => array(
                'primary' => 'WebPage',
                'business_nodes' => array(),
                'support_nodes' => array('BreadcrumbList', 'FAQPage'),
            ),
            'category' => array(
                'primary' => 'CollectionPage',
                'business_nodes' => array(),
                'support_nodes' => array('BreadcrumbList', 'ItemList'),
            ),
            'tag' => array(
                'primary' => 'CollectionPage',
                'business_nodes' => array(),
                'support_nodes' => array('BreadcrumbList', 'ItemList'),
            ),
            'custom' => array(
                'primary' => 'WebPage',
                'business_nodes' => array(),
                'support_nodes' => array('BreadcrumbList'),
            ),
        );
    }

    private function aysa_adjustments_get_recommended_schema_nodes($page_family, array $frontend_snapshot = array(), array $page_data = array()) {
        $policy = $this->aysa_adjustments_get_schema_policy_summary();
        $family_policy = isset($policy[$page_family]) ? $policy[$page_family] : $policy['custom'];
        $nodes = array((string) ($family_policy['primary'] ?? 'WebPage'));
        $has_local_data = $this->aysa_adjustments_has_local_business_data();

        if ($page_family === 'homepage') {
            $nodes[] = 'Organization';
            if ($has_local_data) {
                $nodes[] = 'LocalBusiness';
            }
        } elseif ($page_family === 'page' && $has_local_data && $this->aysa_adjustments_is_local_business_override_page($page_data)) {
            $nodes[] = 'LocalBusiness';
        }

        foreach ((array) ($family_policy['support_nodes'] ?? array()) as $support_node) {
            if ($support_node === 'FAQPage' && empty($frontend_snapshot['has_faq_markup'])) {
                continue;
            }
            $nodes[] = $support_node;
        }

        return array_values(array_unique($nodes));
    }

    private function aysa_adjustments_get_primary_schema_nodes($page_family) {
        $policy = $this->aysa_adjustments_get_schema_policy_summary();
        $family_policy = isset($policy[$page_family]) ? $policy[$page_family] : $policy['custom'];
        $primary = (string) ($family_policy['primary'] ?? 'WebPage');
        return $primary !== '' ? array($primary) : array('WebPage');
    }

    private function aysa_adjustments_get_primary_recommended_schema_node($page_family) {
        $primary_nodes = $this->aysa_adjustments_get_primary_schema_nodes($page_family);
        return (string) ($primary_nodes[0] ?? 'WebPage');
    }

    private function aysa_adjustments_get_google_target_node($page_family) {
        switch ((string) $page_family) {
            case 'product':
                return 'Product';
            case 'article':
                return 'Article';
            case 'page':
            case 'category':
            case 'tag':
            case 'custom':
                return 'BreadcrumbList';
            case 'homepage':
                return $this->aysa_adjustments_has_local_business_data() ? 'Organization' : '';
            default:
                return '';
        }
    }

    private function aysa_adjustments_get_google_target_label($target_node) {
        switch ((string) $target_node) {
            case 'Product':
                return 'Product';
            case 'Article':
                return 'Article';
            case 'BreadcrumbList':
                return 'Breadcrumb';
            case 'Organization':
                return 'Organization';
            default:
                return 'Not targeted by Google rich results';
        }
    }

    private function aysa_adjustments_get_website_wide_schema_nodes(array $page_data = array()) {
        $page_family = (string) ($page_data['page_family'] ?? '');
        $nodes = array();

        if ($page_family === 'homepage') {
            $nodes = array('WebSite', 'Organization');
            if ($this->aysa_adjustments_has_local_business_data()) {
                $nodes[] = 'LocalBusiness';
            }
        } elseif ($page_family === 'page' && $this->aysa_adjustments_is_local_business_override_page($page_data)) {
            $nodes = array('Organization');
            if ($this->aysa_adjustments_has_local_business_data()) {
                $nodes[] = 'LocalBusiness';
            }
        }

        return array_values(array_unique(array_filter(array_map('strval', $nodes))));
    }

    private function aysa_adjustments_get_semantic_schema_nodes($page_family, array $page_data = array()) {
        $nodes = array();
        switch ((string) $page_family) {
            case 'homepage':
                $nodes = array('WebSite', 'Organization');
                if ($this->aysa_adjustments_has_local_business_data()) {
                    $nodes[] = 'LocalBusiness';
                }
                break;
            case 'product':
                $nodes = array('Product', 'BreadcrumbList');
                break;
            case 'article':
                $nodes = array('Article', 'BreadcrumbList');
                break;
            case 'category':
            case 'tag':
                $nodes = array('CollectionPage', 'BreadcrumbList', 'ItemList');
                break;
            case 'page':
                $nodes = array('WebPage', 'BreadcrumbList');
                break;
            case 'custom':
            default:
                $nodes = array('WebPage', 'BreadcrumbList');
                break;
        }

        return array_values(array_unique(array_filter(array_map('strval', $nodes))));
    }

    private function aysa_adjustments_get_semantic_schema_label(array $nodes) {
        if (empty($nodes)) {
            return 'No semantic schema target';
        }

        return implode(' + ', array_values(array_filter(array_map('strval', $nodes))));
    }

    private function aysa_adjustments_get_google_target_state($target_node, array $frontend_snapshot) {
        $result = array(
            'supported' => $target_node !== '',
            'detected' => false,
            'ready' => false,
            'missing_fields' => array(),
            'status' => $target_node === '' ? 'not_applicable' : 'missing',
            'notes' => $target_node === ''
                ? 'This page family is not mapped to a primary Google rich result target.'
                : 'Google rich result target is missing from frontend output.',
        );

        if ($target_node === '') {
            return $result;
        }

        $all_missing = array();
        foreach ((array) ($frontend_snapshot['nodes'] ?? array()) as $node) {
            $types = array_values(array_filter(array_map('strval', (array) ($node['types'] ?? array()))));
            if (!in_array($target_node, $types, true)) {
                continue;
            }

            $result['detected'] = true;
            $missing = $this->aysa_adjustments_validate_schema_required_fields($target_node, (array) ($node['data'] ?? array()));
            if (empty($missing)) {
                $result['ready'] = true;
                $result['missing_fields'] = array();
                $result['status'] = 'ready';
                $result['notes'] = 'Frontend output satisfies the Google rich result target.';
                return $result;
            }

            $all_missing = array_merge($all_missing, array_values($missing));
        }

        $all_missing = array_values(array_unique(array_filter(array_map('strval', $all_missing))));
        $result['missing_fields'] = $all_missing;
        if ($result['detected']) {
            $result['status'] = 'incomplete';
            $result['notes'] = 'Google rich result target is present but missing required fields.';
        }

        return $result;
    }

    private function aysa_adjustments_can_generate_primary_schema($primary_type, array $page_data) {
        $primary_type = (string) $primary_type;

        switch ($primary_type) {
            case 'WebSite':
            case 'WebPage':
            case 'CollectionPage':
                return true;
            case 'Organization':
                return trim((string) get_option('aysa_company_name', get_bloginfo('name'))) !== '';
            case 'LocalBusiness':
                return $this->aysa_adjustments_has_local_business_data()
                    && (
                        (string) ($page_data['page_family'] ?? '') === 'homepage'
                        || $this->aysa_adjustments_is_local_business_override_page($page_data)
                    );
            case 'Article':
                if (intval($page_data['object_id'] ?? 0) <= 0) {
                    return false;
                }
                $post = get_post(intval($page_data['object_id']));
                return $post instanceof WP_Post
                    && trim((string) $post->post_title) !== ''
                    && intval($post->post_author) > 0
                    && trim((string) $post->post_date_gmt) !== '';
            case 'Product':
                if (intval($page_data['object_id'] ?? 0) <= 0 || !function_exists('wc_get_product')) {
                    return false;
                }
                $product = wc_get_product(intval($page_data['object_id']));
                if (!$product) {
                    return false;
                }
                return trim((string) $product->get_name()) !== ''
                    && !empty($product->get_image_id())
                    && $product->get_price() !== ''
                    && trim((string) $product->get_sku()) !== '';
        }

        return false;
    }

    private function aysa_adjustments_validate_schema_required_fields($type, array $node) {
        $missing = array();
        switch ($type) {
            case 'Organization':
            case 'LocalBusiness':
            case 'WebSite':
                if (empty($node['name'])) { $missing[] = 'name'; }
                if (empty($node['url'])) { $missing[] = 'url'; }
                if ($type === 'LocalBusiness') {
                    if (empty($node['telephone']) && empty($node['email'])) { $missing[] = 'telephone'; }
                    if (empty($node['address'])) { $missing[] = 'address'; }
                }
                break;
            case 'Product':
                if (empty($node['name'])) { $missing[] = 'name'; }
                if (empty($node['description'])) { $missing[] = 'description'; }
                if (empty($node['sku'])) { $missing[] = 'sku'; }
                if (empty($node['image'])) { $missing[] = 'image'; }
                if (empty($node['offers'])) {
                    $missing[] = 'offers';
                } else {
                    $offers = $node['offers'];
                    if (is_array($offers) && isset($offers[0]) && is_array($offers[0])) {
                        $offers = $offers[0];
                    }
                    if (empty($offers['price']) && empty($offers['lowPrice'])) { $missing[] = 'price'; }
                    if (empty($offers['priceCurrency'])) { $missing[] = 'priceCurrency'; }
                }
                break;
            case 'Article':
            case 'BlogPosting':
                if (empty($node['headline']) && empty($node['name'])) { $missing[] = 'headline'; }
                if (empty($node['author'])) { $missing[] = 'author'; }
                if (!empty($node['author']) && is_array($node['author']) && empty($node['author']['url'])) { $missing[] = 'author.url'; }
                if (empty($node['datePublished'])) { $missing[] = 'datePublished'; }
                break;
            case 'BreadcrumbList':
                if (empty($node['itemListElement']) || count((array) $node['itemListElement']) < 2) { $missing[] = 'itemListElement'; }
                break;
            case 'ItemList':
                if (empty($node['itemListElement']) || count((array) $node['itemListElement']) < 1) { $missing[] = 'itemListElement'; }
                break;
            case 'FAQPage':
                if (empty($node['mainEntity']) || count((array) $node['mainEntity']) < 1) { $missing[] = 'mainEntity'; }
                break;
        }
        return $missing;
    }

    private function aysa_adjustments_build_schema_row(array $page_data, array $db_lookup, $force_refresh = false) {
        $url = (string) ($page_data['url'] ?? '');
        $frontend = $this->aysa_adjustments_get_schema_frontend_snapshot($url, $force_refresh);
        $recommended_nodes = $this->aysa_adjustments_get_recommended_schema_nodes((string) ($page_data['page_family'] ?? 'page'), $frontend, $page_data);
        $semantic_nodes = $this->aysa_adjustments_get_semantic_schema_nodes((string) ($page_data['page_family'] ?? 'page'), $page_data);
        $primary_nodes = $this->aysa_adjustments_get_primary_schema_nodes((string) ($page_data['page_family'] ?? 'page'));
        $primary_recommended_node = $this->aysa_adjustments_get_primary_recommended_schema_node((string) ($page_data['page_family'] ?? 'page'));
        $google_target_node = $this->aysa_adjustments_get_google_target_node((string) ($page_data['page_family'] ?? 'page'));
        $google_target = $this->aysa_adjustments_get_google_target_state($google_target_node, $frontend);
        $website_wide_nodes = $this->aysa_adjustments_get_website_wide_schema_nodes($page_data);
        $business = $this->aysa_adjustments_get_schema_business_context();
        $entity_group = sanitize_key((string) ($page_data['entity_group'] ?? $this->aysa_technical_schema_resolve_entity_group($page_data)));
        $db_entry = $db_lookup[$url] ?? array('types' => array(), 'rows' => array());
        $aysa_types = array_values(array_unique(array_map('strval', (array) ($db_entry['types'] ?? array()))));
        $detected_types = array();
        $required_missing = array();
        $semantic_node_missing = array();
        $type_counts = array();
        foreach ((array) ($frontend['nodes'] ?? array()) as $node) {
            foreach ((array) ($node['types'] ?? array()) as $type) {
                $type = (string) $type;
                if ($type === '') {
                    continue;
                }
                $detected_types[] = $type;
                $type_counts[$type] = intval($type_counts[$type] ?? 0) + 1;
                $validated_missing = $this->aysa_adjustments_validate_schema_required_fields($type, (array) ($node['data'] ?? array()));
                foreach ($validated_missing as $field_name) {
                    $required_missing[] = $type . ':' . $field_name;
                }
                if (in_array($type, $semantic_nodes, true)) {
                    foreach ($validated_missing as $field_name) {
                        $semantic_node_missing[] = $type . ':' . $field_name;
                    }
                }
            }
        }
        $detected_types = array_values(array_unique($detected_types));
        $required_missing = array_values(array_unique($required_missing));
        $external_types = array_values(array_diff($detected_types, $aysa_types));
        $disable_all = (bool) get_option('aysa_disable_all_schema_output', false);
        $is_blacklisted = $this->aysa_technical_schema_is_blacklisted_url($url);
        $is_output_blocked = $disable_all || $is_blacklisted;
        $duplicate_types = array_keys(array_filter($type_counts, static function ($count) { return intval($count) > 1; }));
        $has_primary = !empty(array_intersect($detected_types, $primary_nodes));
        $has_google_target = $google_target_node !== '' && in_array($google_target_node, $detected_types, true);
        $has_external_google_target = $google_target_node !== '' && in_array($google_target_node, $external_types, true);
        $semantic_required_missing = array();
        $semantic_detected = true;
        $website_wide_required_missing = array();
        $website_wide_detected = true;
        foreach ($semantic_nodes as $semantic_type) {
            if (!in_array($semantic_type, $detected_types, true)) {
                $semantic_detected = false;
                $semantic_required_missing[] = $semantic_type;
            }
        }
        foreach ($website_wide_nodes as $website_node) {
            if (!in_array($website_node, $detected_types, true)) {
                $website_wide_detected = false;
                $website_wide_required_missing[] = $website_node;
            }
        }
        $semantic_required_missing = array_values(array_unique(array_merge($semantic_required_missing, $semantic_node_missing)));
        $semantic_ready = !empty($semantic_nodes) && $semantic_detected && empty($semantic_node_missing);
        $website_wide_ready = empty($website_wide_nodes) ? true : $website_wide_detected;
        $has_conflict = false;
        $conflict_reason = '';
        if (!empty($frontend['has_debug_comments'])) {
            $has_conflict = true;
            $conflict_reason = 'debug_output_in_frontend';
        } elseif (!empty($duplicate_types)) {
            $has_conflict = true;
            $conflict_reason = 'duplicate_schema_types';
        } elseif (!empty($aysa_types) && !empty($external_types) && !empty(array_intersect($aysa_types, $external_types))) {
            $has_conflict = true;
            $conflict_reason = 'parallel_schema_sources';
        } elseif (!empty($aysa_types) && !$disable_all && empty($detected_types)) {
            $has_conflict = true;
            $conflict_reason = 'schema_in_db_missing_in_frontend';
        }

        if ($is_output_blocked && (!empty($aysa_types) || $is_blacklisted)) {
            $status = 'blocked_output';
        } elseif ($has_conflict) {
            $status = 'conflict';
        } elseif (!$has_primary && empty($detected_types)) {
            $status = 'missing';
        } elseif (!$has_primary && !empty($detected_types)) {
            $status = 'wrong_type';
        } elseif (!empty($required_missing)) {
            $status = 'incomplete';
        } else {
            $status = 'active';
        }

        $source_summary = array();
        if (!empty($aysa_types)) {
            $source_summary[] = 'AYSA: ' . implode(', ', $aysa_types);
        }
        if (!empty($external_types)) {
            $source_summary[] = 'External: ' . implode(', ', $external_types);
        }
        if (empty($source_summary) && !empty($detected_types)) {
            $source_summary[] = 'Frontend: ' . implode(', ', $detected_types);
        }
        $notes = array();
        switch ($status) {
            case 'missing': $notes[] = 'Missing primary schema nodes in frontend output.'; break;
            case 'wrong_type': $notes[] = 'Frontend schema exists, but not the recommended primary type.'; break;
            case 'incomplete': $notes[] = 'Detected schema is missing required fields.'; break;
            case 'conflict': $notes[] = 'Multiple schema sources or duplicate schema types need review before AYSA can become the single source of truth.'; break;
            case 'blocked_output': $notes[] = 'AYSA schema output is blocked by a global disable or URL blacklist.'; break;
        }
        if (!empty($google_target['notes'])) {
            $notes[] = (string) $google_target['notes'];
        }
        $can_generate_primary = $this->aysa_adjustments_can_generate_primary_schema($primary_recommended_node, $page_data);
        $google_status = 'not_applicable';
        $google_reason = 'This page family does not target a primary Google rich result.';
        if (!empty($google_target['supported'])) {
            if ($is_output_blocked && (!empty($aysa_types) || $is_blacklisted)) {
                $google_status = 'blocked_output';
                $google_reason = 'AYSA schema output is blocked before Google-target validation can pass.';
            } elseif ($has_conflict && $has_google_target) {
                $google_status = 'needs_takeover';
                $google_reason = 'Google target is present, but duplicate or parallel schema sources require takeover.';
            } elseif ($has_conflict) {
                $google_status = 'conflict';
                $google_reason = 'Schema conflict prevents reliable Google-target validation.';
            } elseif (!empty($google_target['ready']) && !$has_conflict) {
                $google_status = 'ready';
                $google_reason = 'Google rich result target is present and validated in frontend output.';
            } elseif ((string) ($google_target['status'] ?? '') === 'incomplete') {
                if (in_array($google_target_node, array('Product', 'Article'), true)) {
                    $google_status = 'needs_fields';
                    $google_reason = 'Google target is present but missing required fields.';
                } else {
                    $google_status = !empty($aysa_types) || $has_external_google_target ? 'needs_takeover' : 'missing_target';
                    $google_reason = 'Google target is incomplete and should be rewritten to a valid frontend state.';
                }
            } elseif ($has_external_google_target || (!empty($external_types) && in_array($status, array('wrong_type', 'incomplete', 'conflict'), true))) {
                $google_status = 'needs_takeover';
                $google_reason = 'External schema is controlling the Google target and AYSA takeover is required.';
            } elseif (!$has_google_target) {
                if (in_array($google_target_node, array('Product', 'Article'), true) && !$can_generate_primary) {
                    $google_status = 'needs_fields';
                    $google_reason = 'Google target cannot be generated safely because required content fields are missing.';
                } else {
                    $google_status = 'missing_target';
                    $google_reason = 'Google rich result target is missing from frontend output.';
                }
            }
        }
        $execution_eligibility = 'manual_review';
        $execution_reason = 'Agent 8 decision required before automatic schema actions can run.';
        if ($google_status === 'ready') {
            $execution_eligibility = 'skip';
            $execution_reason = 'Page is Google-ready for its primary rich result target.';
        } elseif ($google_status === 'not_applicable') {
            $execution_eligibility = 'not_applicable';
            $execution_reason = 'No primary Google rich result target applies to this page family.';
        } elseif ($google_status === 'needs_fields') {
            $execution_eligibility = 'needs_fields';
            $execution_reason = 'Required content fields are missing for the Google rich result target.';
        } elseif ($google_status === 'needs_takeover' && (!empty($aysa_types) || !empty($has_external_google_target) || !empty($can_generate_primary) || (string) ($status ?? '') === 'conflict')) {
            $execution_eligibility = 'rewrite_takeover';
            $execution_reason = 'AYSA can take over this URL and align the Google rich result target.';
        } elseif ($google_status === 'missing_target' && empty($external_types) && $can_generate_primary) {
            $execution_eligibility = 'generate_safe';
            $execution_reason = 'Google rich result target can be generated safely.';
        } elseif ($google_status === 'blocked_output' && !empty($aysa_types)) {
            $execution_eligibility = 'rewrite_existing_aysa';
            $execution_reason = 'AYSA schema exists but output is blocked; rewrite can restore frontend visibility.';
        } elseif ($status === 'active') {
            $execution_eligibility = 'skip';
            $execution_reason = 'Page is already aligned with the recommended schema policy.';
        } elseif ($status === 'conflict' && in_array($conflict_reason, array('parallel_schema_sources', 'duplicate_schema_types', 'schema_in_db_missing_in_frontend'), true) && $can_generate_primary) {
            $execution_eligibility = 'rewrite_takeover';
            $execution_reason = 'AYSA can take over this URL and replace the external schema output.';
        } elseif ($status === 'missing' && empty($external_types) && $can_generate_primary) {
            $execution_eligibility = 'generate_safe';
            $execution_reason = 'Primary schema can be generated safely.';
        } elseif (in_array($status, array('wrong_type', 'incomplete', 'blocked_output'), true) && !$has_conflict && !empty($aysa_types)) {
            $execution_eligibility = 'rewrite_existing_aysa';
            $execution_reason = $status === 'blocked_output'
                ? 'AYSA schema exists but output is blocked; worker can restore it through rewrite.'
                : 'Existing AYSA schema can be rewritten to the recommended primary type.';
        } elseif (in_array($status, array('wrong_type', 'incomplete'), true) && empty($aysa_types) && $can_generate_primary) {
            $execution_eligibility = 'rewrite_takeover';
            $execution_reason = 'AYSA can replace the external or incomplete schema on this URL.';
        } elseif ($status === 'blocked_output' && empty($aysa_types) && $can_generate_primary) {
            $execution_eligibility = 'rewrite_takeover';
            $execution_reason = 'AYSA can generate and restore schema output on this blocked URL.';
        } elseif ($status === 'conflict') {
            $execution_eligibility = 'manual_review';
            $execution_reason = 'Conflict detected. Route this to Agent 8 before AYSA becomes source of truth.';
        } elseif ($status === 'missing' && !$can_generate_primary) {
            $execution_eligibility = 'manual_review';
            $execution_reason = 'Primary schema is not safe to auto-generate for this page. Route it to Agent 8.';
        }
        $needs_manual_review = in_array($status, array('wrong_type', 'incomplete', 'conflict', 'blocked_output'), true)
            || ($status === 'missing' && !$can_generate_primary);
        $overwrite_status = 'ready_for_overwrite';
        $overwrite_reason = 'AYSA can overwrite this URL when you want it to become source of truth.';
        if ($google_status === 'ready' && $semantic_ready && $website_wide_ready && !$has_conflict) {
            $overwrite_status = 'already_owned';
            $overwrite_reason = 'Google, semantic, and website-wide schema targets are already valid.';
        } elseif ($google_status === 'needs_fields') {
            $overwrite_status = 'needs_fields';
            $overwrite_reason = 'AYSA is missing required fields before overwrite can happen.';
        } elseif ($is_output_blocked) {
            $overwrite_status = 'blocked_output';
            $overwrite_reason = 'Schema output is blocked and must be restored before overwrite is visible.';
        } elseif ($has_conflict || $google_status === 'needs_takeover') {
            $overwrite_status = 'needs_takeover';
            $overwrite_reason = 'External schema is present; rewrite will replace it.';
        } elseif ($google_status === 'not_applicable' && $semantic_ready) {
            $overwrite_status = 'semantic_ready';
            $overwrite_reason = 'This page is semantically ready even though it has no primary Google target.';
        }
        $nap_status = $this->aysa_adjustments_has_local_business_data($business) ? 'ready' : 'needs_fields';
        $business_source = sanitize_key((string) ($business['source'] ?? 'local_fallback'));
        $location_source = $business_source;

        return array(
            'url' => esc_url_raw($url),
            'title' => sanitize_text_field((string) ($page_data['title'] ?? $url)),
            'page_family' => sanitize_key((string) ($page_data['page_family'] ?? 'page')),
            'entity_group' => $entity_group,
            'entity_group_label' => $this->aysa_technical_schema_entity_group_label($entity_group),
            'website_wide_schema' => $website_wide_nodes,
            'website_wide_schema_label' => $this->aysa_adjustments_get_semantic_schema_label($website_wide_nodes),
            'website_wide_required_missing' => array_values(array_unique($website_wide_required_missing)),
            'website_required_missing' => array_values(array_unique($website_wide_required_missing)),
            'website_wide_ready' => $website_wide_ready,
            'page_level_ready' => $semantic_ready,
            'google_target' => $google_target_node,
            'google_target_label' => $this->aysa_adjustments_get_google_target_label($google_target_node),
            'google_target_supported' => !empty($google_target['supported']),
            'google_target_detected' => !empty($google_target['detected']),
            'google_rich_result_ready' => !empty($google_target['ready']),
            'google_target_status' => sanitize_key((string) ($google_target['status'] ?? 'not_applicable')),
            'google_required_missing' => array_values((array) ($google_target['missing_fields'] ?? array())),
            'google_status' => sanitize_key($google_status),
            'google_reason' => sanitize_text_field($google_reason),
            'semantic_schema' => $semantic_nodes,
            'semantic_schema_label' => $this->aysa_adjustments_get_semantic_schema_label($semantic_nodes),
            'semantic_required_missing' => $semantic_required_missing,
            'semantic_ready' => $semantic_ready,
            'recommended_nodes' => array_values($recommended_nodes),
            'primary_recommended_node' => $primary_recommended_node,
            'detected_nodes' => array_values($detected_types),
            'status' => $status,
            'required_fields_missing' => $required_missing,
            'source_summary' => $source_summary,
            'is_aysa_present' => !empty($aysa_types),
            'is_external_schema_present' => !empty($external_types),
            'has_conflict' => $has_conflict,
            'conflict_reason' => $conflict_reason,
            'can_auto_generate' => in_array($status, array('missing', 'wrong_type'), true) && empty($external_types) && $can_generate_primary,
            'can_auto_fix' => !empty($aysa_types) && in_array($status, array('blocked_output', 'incomplete', 'wrong_type'), true) && !$has_conflict,
            'execution_eligibility' => $execution_eligibility,
            'execution_reason' => $execution_reason,
            'overwrite_status' => $overwrite_status,
            'overwrite_reason' => $overwrite_reason,
            'is_output_blocked' => $is_output_blocked,
            'blocked_output_reason' => $is_blacklisted ? 'url_blacklisted' : ($disable_all ? 'global_disable' : ''),
            'needs_manual_review' => $needs_manual_review,
            'business_source' => $business_source,
            'location_source' => $location_source,
            'nap_status' => $nap_status,
            'notes' => $notes,
            'last_checked_at' => sanitize_text_field((string) ($frontend['fetched_at'] ?? current_time('mysql'))),
            'generated_types' => $aysa_types,
        );
    }

    private function aysa_adjustments_build_technical_schema_snapshot_from_rows(array $rows) {
        $supported_groups = $this->aysa_technical_schema_supported_entity_groups();
        $unsupported_rows = array_values(array_filter($rows, function ($row) use ($supported_groups) {
            $entity_group = sanitize_key((string) ($row['entity_group'] ?? ''));
            return !in_array($entity_group, $supported_groups, true);
        }));
        $rows = array_values(array_filter($rows, function ($row) use ($supported_groups) {
            $entity_group = sanitize_key((string) ($row['entity_group'] ?? ''));
            return in_array($entity_group, $supported_groups, true);
        }));

        $summary = array(
            'total_pages' => 0,
            'active' => 0,
            'missing' => 0,
            'wrong_type' => 0,
            'incomplete' => 0,
            'conflict' => 0,
            'blocked_output' => 0,
            'blocked' => 0,
            'safe_auto_generate' => 0,
            'needs_manual_review' => 0,
            'ready_to_apply' => 0,
            'google_target_pages' => 0,
            'google_ready_pages' => 0,
            'website_wide_ready_pages' => 0,
            'page_level_ready_pages' => 0,
            'needs_takeover' => 0,
            'needs_fields' => 0,
            'not_applicable' => 0,
            'ignored_unsupported' => count($unsupported_rows),
        );
        $entity_summaries = array();
        foreach ($supported_groups as $entity_group) {
            $entity_summaries[$entity_group] = array(
                'entity_group' => $entity_group,
                'label' => $this->aysa_technical_schema_entity_group_label($entity_group),
                'total_pages' => 0,
                'google_ready_pages' => 0,
                'semantic_ready_pages' => 0,
                'website_wide_ready_pages' => 0,
                'safe_auto_generate' => 0,
                'active_generated' => 0,
                'needs_fields' => 0,
                'needs_takeover' => 0,
            );
        }
        $queue_preview = array(
            'eligible_generate' => 0,
            'eligible_rewrite_existing' => 0,
            'manual_review' => 0,
            'conflict' => 0,
            'already_aligned' => 0,
        );
        foreach ($rows as $row) {
            $summary['total_pages']++;
            if (isset($summary[$row['status']])) {
                $summary[$row['status']]++;
            }
            if (($row['status'] ?? '') === 'blocked_output') {
                $summary['blocked']++;
            }
            if (!empty($row['can_auto_generate'])) {
                $summary['safe_auto_generate']++;
            }
            $execution_eligibility = (string) ($row['execution_eligibility'] ?? 'manual_review');
            if (in_array($execution_eligibility, array('generate_safe', 'rewrite_existing_aysa', 'rewrite_takeover'), true)) {
                $summary['ready_to_apply']++;
            } elseif ($execution_eligibility === 'manual_review') {
                $summary['needs_manual_review']++;
            }
            if (!empty($row['google_target_supported'])) {
                $summary['google_target_pages']++;
            }
            if (!empty($row['google_rich_result_ready'])) {
                $summary['google_ready_pages']++;
            }
            if (!empty($row['website_wide_schema']) && !empty($row['website_wide_ready'])) {
                $summary['website_wide_ready_pages']++;
            }
            if (!empty($row['page_level_ready'])) {
                $summary['page_level_ready_pages']++;
            }
            if (isset($summary[$row['google_status'] ?? ''])) {
                $summary[$row['google_status']]++;
            }
            $entity_group = sanitize_key((string) ($row['entity_group'] ?? ''));
            if (isset($entity_summaries[$entity_group])) {
                $entity_summaries[$entity_group]['total_pages']++;
                if (!empty($row['google_rich_result_ready'])) {
                    $entity_summaries[$entity_group]['google_ready_pages']++;
                }
                if (!empty($row['semantic_ready'])) {
                    $entity_summaries[$entity_group]['semantic_ready_pages']++;
                }
                if (!empty($row['website_wide_schema']) && !empty($row['website_wide_ready'])) {
                    $entity_summaries[$entity_group]['website_wide_ready_pages']++;
                }
                if (!empty($row['can_auto_generate'])) {
                    $entity_summaries[$entity_group]['safe_auto_generate']++;
                }
                if (!empty($row['is_aysa_present'])) {
                    $entity_summaries[$entity_group]['active_generated']++;
                }
                if ((string) ($row['google_status'] ?? '') === 'needs_fields') {
                    $entity_summaries[$entity_group]['needs_fields']++;
                }
                if ((string) ($row['google_status'] ?? '') === 'needs_takeover') {
                    $entity_summaries[$entity_group]['needs_takeover']++;
                }
            }
            switch ((string) ($row['execution_eligibility'] ?? 'manual_review')) {
                case 'generate_safe':
                    $queue_preview['eligible_generate']++;
                    break;
                case 'rewrite_existing_aysa':
                    $queue_preview['eligible_rewrite_existing']++;
                    break;
                case 'rewrite_takeover':
                    $queue_preview['eligible_rewrite_existing']++;
                    break;
                case 'skip':
                    $queue_preview['already_aligned']++;
                    break;
                case 'not_applicable':
                    $queue_preview['already_aligned']++;
                    break;
                default:
                    if ((string) ($row['status'] ?? '') === 'conflict') {
                        $queue_preview['conflict']++;
                    } else {
                        $queue_preview['manual_review']++;
                    }
                    break;
            }
        }
        $snapshot = array(
            'id' => 'technical_schema',
            'status' => !empty($rows) ? 'ready' : 'idle',
            'summary' => $summary,
            'count' => count($rows),
            'coverage_rows' => array_values($rows),
            'validation_rows' => array_values($rows),
            'generated_rows' => array_values(array_filter($rows, static function ($row) {
                return (string) ($row['execution_eligibility'] ?? 'skip') !== 'skip';
            })),
            'entity_rows' => array(
                'page' => array_values(array_filter($rows, static function ($row) { return (string) ($row['entity_group'] ?? '') === 'page'; })),
                'product' => array_values(array_filter($rows, static function ($row) { return (string) ($row['entity_group'] ?? '') === 'product'; })),
                'ecommerce_category' => array_values(array_filter($rows, static function ($row) { return (string) ($row['entity_group'] ?? '') === 'ecommerce_category'; })),
                'article' => array_values(array_filter($rows, static function ($row) { return (string) ($row['entity_group'] ?? '') === 'article'; })),
                'business' => array_values(array_filter($rows, static function ($row) { return (string) ($row['entity_group'] ?? '') === 'business'; })),
            ),
            'entity_summaries' => $entity_summaries,
            'conflict_rows' => array_values(array_filter($rows, static function ($row) { return !empty($row['has_conflict']) || in_array((string) ($row['status'] ?? ''), array('blocked_output', 'wrong_type', 'incomplete'), true); })),
            'queue_preview_summary' => $queue_preview,
            'policy_summary' => $this->aysa_adjustments_get_schema_policy_summary(),
            'has_data' => !empty($rows),
            'ignored_rows' => $unsupported_rows,
        );
        return $snapshot;
    }

    private function aysa_adjustments_get_technical_schema_snapshot($project_id, $force_refresh = false) {
        $project_id = intval($project_id);
        $cache_key = $this->aysa_adjustments_get_schema_cache_key($project_id, 'snapshot');
        if (!$force_refresh) {
            $cached = get_transient($cache_key);
            if (is_array($cached)) {
                return $cached;
            }
        }
        $db_lookup = $this->aysa_adjustments_get_schema_db_lookup();
        $scope_pages = $this->aysa_adjustments_collect_schema_scope_pages();
        $rows = array();
        foreach ($scope_pages as $page_data) {
            $rows[] = $this->aysa_adjustments_build_schema_row($page_data, $db_lookup, $force_refresh);
        }
        $snapshot = $this->aysa_adjustments_build_technical_schema_snapshot_from_rows($rows);
        $this->aysa_adjustments_write_technical_schema_snapshot_cache($project_id, $snapshot);
        return $snapshot;
    }

    private function aysa_adjustments_get_technical_schema_markup_snapshot($project_id) {
        $schema_snapshot = $this->aysa_adjustments_get_cached_or_idle_technical_schema_snapshot($project_id);
        $state = get_option($this->aysa_technical_schema_state_key($project_id), array());
        if (!is_array($state)) {
            $state = array();
        }
        $state_status = sanitize_key((string) ($state['status'] ?? 'idle'));
        $cached_markup = isset($state['schema_markup_snapshot']) && is_array($state['schema_markup_snapshot'])
            ? $state['schema_markup_snapshot']
            : array();
        if ($cached_markup !== array() && empty($schema_snapshot['coverage_rows'])) {
            return $cached_markup;
        }
        $rows = array();
        foreach ((array) ($schema_snapshot['coverage_rows'] ?? array()) as $row) {
            $rows[] = array(
                'url' => (string) ($row['url'] ?? ''),
                'title' => (string) ($row['title'] ?? ''),
                'page_type' => ucfirst((string) ($row['page_family'] ?? 'page')),
                'schema_type' => !empty($row['primary_recommended_node']) ? sanitize_key((string) $row['primary_recommended_node']) : '',
                'status' => (string) ($row['status'] ?? 'missing'),
                'current_schema' => !empty($row['detected_nodes'][0]) ? sanitize_key((string) $row['detected_nodes'][0]) : '',
            );
        }
        $summary = (array) ($schema_snapshot['summary'] ?? array());
        $total_pages = intval($summary['total_pages'] ?? count($rows));
        $active_count = intval($summary['active'] ?? 0);
        $missing_count = intval($summary['missing'] ?? 0);
        $incomplete_count = intval(($summary['incomplete'] ?? 0) + ($summary['wrong_type'] ?? 0) + ($summary['conflict'] ?? 0) + ($summary['blocked'] ?? 0));
        return array(
            'id' => 'technical_schema_markup',
            'status' => !empty($rows)
                ? (in_array($state_status, array('queued', 'running', 'failed', 'blocked'), true) ? $state_status : 'ready')
                : (in_array($state_status, array('queued', 'running', 'completed', 'failed', 'blocked'), true)
                    ? $state_status
                    : ((string) ($schema_snapshot['status'] ?? 'idle'))),
            'total_pages' => $total_pages,
            'active_count' => $active_count,
            'missing_count' => $missing_count,
            'incomplete_count' => $incomplete_count,
            'coverage_percentage' => $total_pages > 0 ? intval(round(($active_count / $total_pages) * 100)) : 0,
            'overview_rows' => array(
                array('metric' => 'Total pages', 'value' => (string) $total_pages),
                array('metric' => 'Active schema', 'value' => (string) $active_count),
                array('metric' => 'Missing schema', 'value' => (string) $missing_count),
                array('metric' => 'Incomplete schema', 'value' => (string) $incomplete_count),
                array('metric' => 'Coverage', 'value' => (string) ($total_pages > 0 ? intval(round(($active_count / $total_pages) * 100)) : 0) . '%'),
            ),
            'rows' => array_values($rows),
            'count' => count($rows),
            'has_data' => !empty($rows),
            'needs_refresh' => !empty($schema_snapshot['needs_refresh']),
        );
    }

    private function aysa_technical_schema_job_key($job_id) {
        return 'aysa_technical_schema_job_' . sanitize_key((string) $job_id);
    }

    private function aysa_technical_schema_refresh_job_pointer_key($project_id) {
        return $this->aysa_adjustments_get_schema_cache_key($project_id, 'refresh_job');
    }

    private function aysa_technical_schema_read_job($job_id) {
        $job = get_transient($this->aysa_technical_schema_job_key($job_id));
        return is_array($job) ? $job : null;
    }

    private function aysa_technical_schema_write_job($job_id, array $job) {
        $job['updated_at'] = current_time('mysql');
        $job['updated_at_unix'] = time();
        set_transient($this->aysa_technical_schema_job_key($job_id), $job, 12 * HOUR_IN_SECONDS);
        return $job;
    }

    private function aysa_technical_schema_schedule_job($job_id, $delay = 1) {
        $timestamp = time() + max(1, intval($delay));
        wp_schedule_single_event($timestamp, 'aysa_process_schema_job_batch', array((string) $job_id));
    }

    private function aysa_technical_schema_get_current_refresh_job($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return null;
        }
        $job_id = get_transient($this->aysa_technical_schema_refresh_job_pointer_key($project_id));
        if (!is_string($job_id) || $job_id === '') {
            return null;
        }
        $job = $this->aysa_technical_schema_read_job($job_id);
        return is_array($job) ? $job : null;
    }

    private function aysa_technical_schema_delete_job($job_id) {
        delete_transient($this->aysa_technical_schema_job_key($job_id));
    }

    private function aysa_technical_schema_scope_lookup() {
        $lookup = array();
        foreach ((array) $this->aysa_adjustments_collect_schema_scope_pages() as $page_data) {
            if (!is_array($page_data)) {
                continue;
            }
            $url = $this->aysa_adjustments_normalize_schema_url((string) ($page_data['url'] ?? ''));
            if ($url === '') {
                continue;
            }
            $lookup[$url] = $page_data;
        }
        return $lookup;
    }

    private function aysa_technical_schema_unblacklist_url($url) {
        $url = $this->aysa_adjustments_normalize_schema_url($url);
        if ($url === '') {
            return;
        }
        $blacklist = get_option('aysa_schema_blacklist', array());
        if (!is_array($blacklist) || empty($blacklist)) {
            return;
        }
        $updated = array();
        foreach ($blacklist as $blocked_url) {
            $normalized_blocked = $this->aysa_adjustments_normalize_schema_url((string) $blocked_url);
            if ($normalized_blocked === '' || $normalized_blocked === $url) {
                continue;
            }
            $updated[] = $blocked_url;
        }
        update_option('aysa_schema_blacklist', array_values($updated), false);
    }

    private function aysa_technical_schema_blacklist_url($url) {
        $url = $this->aysa_adjustments_normalize_schema_url($url);
        if ($url === '') {
            return;
        }
        $blacklist = get_option('aysa_schema_blacklist', array());
        if (!is_array($blacklist)) {
            $blacklist = array();
        }
        if (!in_array($url, $blacklist, true)) {
            $blacklist[] = $url;
            update_option('aysa_schema_blacklist', array_values($blacklist), false);
        }
    }

    private function aysa_technical_schema_is_blacklisted_url($url) {
        $url = $this->aysa_adjustments_normalize_schema_url($url);
        if ($url === '') {
            return false;
        }

        $blacklist = get_option('aysa_schema_blacklist', array());
        if (!is_array($blacklist) || empty($blacklist)) {
            return false;
        }

        foreach ($blacklist as $blocked_url) {
            if ($this->aysa_adjustments_normalize_schema_url((string) $blocked_url) === $url) {
                return true;
            }
        }

        return false;
    }

    private function aysa_technical_schema_invalidate_caches($project_id, $url = '') {
        $project_id = intval($project_id);
        if ($project_id > 0) {
            delete_transient($this->aysa_adjustments_get_schema_cache_key($project_id, 'snapshot'));
            delete_transient($this->aysa_adjustments_get_schema_cache_key($project_id, 'summary'));
        }
        if ($url !== '') {
            delete_transient($this->aysa_adjustments_get_schema_cache_key(md5((string) $url), 'frontend'));
        }
    }

    private function aysa_technical_schema_map_type_label($schema_type) {
        $map = array(
            'website' => 'WebSite',
            'organization' => 'Organization',
            'localbusiness' => 'LocalBusiness',
            'product' => 'Product',
            'article' => 'Article',
            'webpage' => 'WebPage',
            'collectionpage' => 'CollectionPage',
            'breadcrumblist' => 'BreadcrumbList',
            'faqpage' => 'FAQPage',
        );
        $key = sanitize_key((string) $schema_type);
        return (string) ($map[$key] ?? 'WebPage');
    }

    private function aysa_technical_schema_make_item_list_entry($position, $url, $name = '', $image = '') {
        $entry = array(
            '@type' => 'ListItem',
            'position' => max(1, intval($position)),
            'url' => esc_url_raw((string) $url),
            'name' => sanitize_text_field((string) $name),
        );
        if (trim((string) $image) !== '') {
            $entry['image'] = esc_url_raw((string) $image);
        }

        return $entry;
    }

    private function aysa_technical_schema_collect_archive_item_list_elements($taxonomy, $term_id) {
        $taxonomy = sanitize_key((string) $taxonomy);
        $term_id = intval($term_id);
        if ($taxonomy === '' || $term_id <= 0 || !taxonomy_exists($taxonomy) || !class_exists('WP_Query')) {
            return array();
        }

        $query_post_types = array();
        if (
            (function_exists('taxonomy_is_product_attribute') && taxonomy_is_product_attribute($taxonomy))
            || in_array($taxonomy, array('product_cat', 'product_tag'), true)
        ) {
            $query_post_types = array('product');
        } else {
            $taxonomy_object = get_taxonomy($taxonomy);
            $object_types = is_object($taxonomy_object) ? (array) ($taxonomy_object->object_type ?? array()) : array();
            foreach ($object_types as $candidate_type) {
                $candidate_type = sanitize_key((string) $candidate_type);
                if ($candidate_type === '' || !post_type_exists($candidate_type)) {
                    continue;
                }
                $post_type_object = get_post_type_object($candidate_type);
                if (!$post_type_object || empty($post_type_object->public) || in_array($candidate_type, array('attachment', 'revision', 'nav_menu_item', 'product_variation'), true)) {
                    continue;
                }
                $query_post_types[] = $candidate_type;
            }
            if (empty($query_post_types)) {
                $query_post_types = array('post');
            }
        }

        $archive_query = new WP_Query(array(
            'post_type' => array_values(array_unique($query_post_types)),
            'post_status' => 'publish',
            'posts_per_page' => 12,
            'ignore_sticky_posts' => true,
            'tax_query' => array(
                array(
                    'taxonomy' => $taxonomy,
                    'field' => 'term_id',
                    'terms' => array($term_id),
                ),
            ),
            'fields' => 'ids',
        ));

        $elements = array();
        if ($archive_query->have_posts()) {
            $position = 1;
            foreach ((array) $archive_query->posts as $listed_post_id) {
                $listed_post_id = intval($listed_post_id);
                if ($listed_post_id <= 0) {
                    continue;
                }
                $listed_url = get_permalink($listed_post_id);
                if (!$listed_url) {
                    continue;
                }
                $listed_name = get_the_title($listed_post_id);
                $listed_image = get_the_post_thumbnail_url($listed_post_id, 'medium');
                $elements[] = $this->aysa_technical_schema_make_item_list_entry(
                    $position,
                    $listed_url,
                    $listed_name,
                    $listed_image ? $listed_image : ''
                );
                $position++;
            }
        }
        wp_reset_postdata();

        return $elements;
    }

    private function aysa_technical_schema_build_page_context(array $page_data) {
        $url = $this->aysa_adjustments_normalize_schema_url((string) ($page_data['url'] ?? ''));
        $title = sanitize_text_field((string) ($page_data['title'] ?? $url));
        $description = '';
        $image_url = '';
        $author_name = '';
        $author_url = '';
        $published_date = '';
        $modified_date = '';
        $product_price = '';
        $product_currency = function_exists('get_woocommerce_currency') ? get_woocommerce_currency() : 'RON';
        $product_availability = 'https://schema.org/InStock';
        $product_sku = '';
        $item_list_elements = array();
        $autofill_actions = array();

        $object_id = intval($page_data['object_id'] ?? 0);
        $post_type = sanitize_key((string) ($page_data['post_type'] ?? ''));
        if ($object_id > 0 && $post_type !== '' && taxonomy_exists($post_type) === false) {
            $post = get_post($object_id);
            if ($post instanceof WP_Post) {
                if ($title === '') {
                    $title = sanitize_text_field((string) $post->post_title);
                }
                $description = sanitize_text_field(wp_strip_all_tags((string) ($post->post_excerpt ?: $post->post_content)));
                if ($description !== '') {
                    $description = wp_trim_words($description, 32, '...');
                } else {
                    $autofill_actions[] = 'Synthesized description from page title.';
                    $description = wp_trim_words(sprintf(__('Learn more about %s on our website.', 'aysa'), $title !== '' ? $title : get_bloginfo('name')), 32, '...');
                }
                $image_id = intval(get_post_thumbnail_id($post->ID));
                if ($image_id > 0) {
                    $image_url = (string) wp_get_attachment_image_url($image_id, 'full');
                } elseif (($fallback_image = get_the_post_thumbnail_url($post->ID, 'large'))) {
                    $image_url = (string) $fallback_image;
                    $autofill_actions[] = 'Autofilled image from available attachment fallback.';
                }
                $author_name = sanitize_text_field((string) get_the_author_meta('display_name', intval($post->post_author)));
                $author_url = esc_url_raw(get_author_posts_url(intval($post->post_author)));
                if ($author_url === '') {
                    $user_url = trim((string) get_the_author_meta('user_url', intval($post->post_author)));
                    if ($user_url !== '') {
                        $author_url = esc_url_raw($user_url);
                        $autofill_actions[] = 'Autofilled author.url from WordPress author profile.';
                    }
                } elseif ((string) ($page_data['page_family'] ?? '') === 'article') {
                    $autofill_actions[] = 'Autofilled author.url from WordPress author archive.';
                }
                $published_date = mysql2date('c', (string) $post->post_date_gmt ?: (string) $post->post_date, false);
                $modified_date = mysql2date('c', (string) $post->post_modified_gmt ?: (string) $post->post_modified, false);

                if ($post_type === 'product' && function_exists('wc_get_product')) {
                    $product = wc_get_product($post->ID);
                    if ($product) {
                        $title = sanitize_text_field((string) $product->get_name());
                        $description = sanitize_text_field(wp_strip_all_tags((string) $product->get_short_description()));
                        if ($description === '') {
                            $description = sanitize_text_field(wp_strip_all_tags((string) $product->get_description()));
                            if ($description !== '') {
                                $autofill_actions[] = 'Autofilled product description from WooCommerce content.';
                            }
                        }
                        $description = wp_trim_words($description, 32, '...');
                        $image_id = intval($product->get_image_id());
                        if ($image_id > 0) {
                            $image_url = (string) wp_get_attachment_image_url($image_id, 'full');
                        } elseif (!empty($product->get_gallery_image_ids())) {
                            $gallery_ids = array_values(array_filter(array_map('intval', (array) $product->get_gallery_image_ids())));
                            if (!empty($gallery_ids[0])) {
                                $image_url = (string) wp_get_attachment_image_url($gallery_ids[0], 'full');
                                $autofill_actions[] = 'Autofilled product image from gallery.';
                            }
                        }
                        $product_price = (string) $product->get_price();
                        if ($product_price === '') {
                            $product_price = (string) ($product->get_regular_price() !== '' ? $product->get_regular_price() : $product->get_sale_price());
                            if ($product_price !== '') {
                                $autofill_actions[] = 'Autofilled product price from WooCommerce pricing metadata.';
                            }
                        }
                        $product_currency = function_exists('get_woocommerce_currency') ? get_woocommerce_currency() : $product_currency;
                        $product_availability = $product->is_in_stock() ? 'https://schema.org/InStock' : 'https://schema.org/OutOfStock';
                        $product_sku = sanitize_text_field((string) $product->get_sku());
                        if ($product_sku === '') {
                            $meta_sku = sanitize_text_field((string) get_post_meta($post->ID, '_sku', true));
                            if ($meta_sku === '') {
                                $meta_sku = sanitize_text_field((string) get_post_meta($post->ID, 'sku', true));
                            }
                            if ($meta_sku !== '') {
                                $product_sku = $meta_sku;
                                $autofill_actions[] = 'Autofilled product SKU from product meta.';
                            } elseif (trim((string) $post->post_name) !== '') {
                                $product_sku = strtoupper(sanitize_title((string) $post->post_name));
                                $autofill_actions[] = 'Autofilled product SKU from product slug.';
                            }
                        }
                    }
                }
            }
        } elseif ($object_id > 0 && !empty($page_data['taxonomy']) && taxonomy_exists((string) $page_data['taxonomy'])) {
            $term = get_term($object_id, (string) $page_data['taxonomy']);
            if ($term && !is_wp_error($term)) {
                $title = sanitize_text_field((string) $term->name);
                $description = sanitize_text_field(wp_strip_all_tags((string) $term->description));
                $description = wp_trim_words($description, 32, '...');
                if ($description === '') {
                    $description = sprintf(__('Browse the latest items filed under %s.', 'aysa'), $title !== '' ? $title : get_bloginfo('name'));
                    $autofill_actions[] = 'Synthesized archive description from taxonomy title.';
                }
                $item_list_elements = $this->aysa_technical_schema_collect_archive_item_list_elements((string) $page_data['taxonomy'], intval($term->term_id));
                if (!empty($item_list_elements)) {
                    $autofill_actions[] = 'Autofilled category ItemList from archive contents.';
                }
            }
        }

        if ($description === '') {
            $description = sprintf(__('Learn more about %s on our website.', 'aysa'), $title !== '' ? $title : get_bloginfo('name'));
            $autofill_actions[] = 'Synthesized fallback description from site context.';
        }

        if ($image_url === '') {
            $site_icon = (string) get_site_icon_url();
            if ($site_icon !== '') {
                $image_url = $site_icon;
                $autofill_actions[] = 'Autofilled image from site icon fallback.';
            }
        }

        return array(
            'url' => $url,
            'title' => $title !== '' ? $title : $url,
            'description' => $description,
            'image_url' => $image_url !== '' ? $image_url : (string) get_site_icon_url(),
            'author_name' => $author_name !== '' ? $author_name : 'Admin',
            'author_url' => $author_url,
            'published_date' => $published_date !== '' ? $published_date : current_time('c'),
            'modified_date' => $modified_date !== '' ? $modified_date : current_time('c'),
            'product_price' => $product_price,
            'product_currency' => $product_currency,
            'product_availability' => $product_availability,
            'product_sku' => $product_sku,
            'item_list_elements' => $item_list_elements,
            'autofill_actions' => array_values(array_unique(array_filter(array_map('sanitize_text_field', $autofill_actions)))),
        );
    }

    private function aysa_technical_schema_generate_payload($schema_type, array $page_data, array $frontend_snapshot = array()) {
        $schema_type = sanitize_key((string) $schema_type);
        $page_context = $this->aysa_technical_schema_build_page_context($page_data);
        $url = (string) ($page_context['url'] ?? '');
        if ($url === '') {
            return new WP_Error('missing_url', 'Missing URL for schema generation.');
        }

        $site_url = trailingslashit(home_url('/'));
        $company_name = trim((string) get_option('aysa_company_name', get_bloginfo('name')));
        if ($company_name === '') {
            $company_name = get_bloginfo('name');
        }
        $logo_url = (string) get_site_icon_url();
        $business = $this->aysa_adjustments_get_schema_business_context();
        $graph = array();

        $organization = array(
            '@type' => 'Organization',
            '@id' => trailingslashit($site_url) . '#organization',
            'name' => $company_name,
            'url' => trailingslashit($site_url),
        );
        if ($logo_url !== '') {
            $organization['logo'] = array(
                '@type' => 'ImageObject',
                'url' => $logo_url,
            );
        }
        if (!empty($business['same_as'])) {
            $organization['sameAs'] = array_values((array) $business['same_as']);
        }
        $graph[] = $organization;

        if (in_array($schema_type, array('website', 'webpage', 'collectionpage', 'article', 'product'), true)) {
            $graph[] = array(
                '@type' => 'WebSite',
                '@id' => trailingslashit($site_url) . '#website',
                'url' => trailingslashit($site_url),
                'name' => $company_name,
                'publisher' => array('@id' => trailingslashit($site_url) . '#organization'),
            );
        }

        if (
            ($schema_type === 'localbusiness' || (string) ($page_data['page_family'] ?? '') === 'homepage' || $this->aysa_adjustments_is_local_business_override_page($page_data))
            && $this->aysa_adjustments_has_local_business_data($business)
        ) {
            $local_business = array(
                '@type' => 'LocalBusiness',
                '@id' => trailingslashit($site_url) . '#localbusiness',
                'name' => trim((string) ($business['company_name'] ?? '')) !== '' ? sanitize_text_field((string) $business['company_name']) : $company_name,
                'url' => trailingslashit($site_url),
            );
            if (!empty($business['phone'])) {
                $local_business['telephone'] = sanitize_text_field((string) $business['phone']);
            }
            if (!empty($business['email'])) {
                $local_business['email'] = sanitize_email((string) $business['email']);
            }
            if (!empty($business['address_line_1']) || !empty($business['city'])) {
                $address = array(
                    '@type' => 'PostalAddress',
                );
                if (!empty($business['address_line_1'])) {
                    $address['streetAddress'] = sanitize_text_field((string) $business['address_line_1']);
                }
                if (!empty($business['address_line_2'])) {
                    $address['streetAddress'] = trim(((string) ($address['streetAddress'] ?? '')) . ', ' . sanitize_text_field((string) $business['address_line_2']));
                }
                if (!empty($business['city'])) {
                    $address['addressLocality'] = sanitize_text_field((string) $business['city']);
                }
                if (!empty($business['region'])) {
                    $address['addressRegion'] = sanitize_text_field((string) $business['region']);
                }
                if (!empty($business['postal_code'])) {
                    $address['postalCode'] = sanitize_text_field((string) $business['postal_code']);
                }
                if (!empty($business['country'])) {
                    $address['addressCountry'] = sanitize_text_field((string) $business['country']);
                }
                $local_business['address'] = $address;
                $local_business['location'] = $address;
            }
            if (!empty($business['latitude']) && !empty($business['longitude'])) {
                $local_business['geo'] = array(
                    '@type' => 'GeoCoordinates',
                    'latitude' => (string) $business['latitude'],
                    'longitude' => (string) $business['longitude'],
                );
            }
            if (!empty($business['opening_hours'])) {
                $local_business['openingHours'] = array_values((array) $business['opening_hours']);
            }
            if (!empty($business['same_as'])) {
                $local_business['sameAs'] = array_values((array) $business['same_as']);
            }
            $graph[] = $local_business;
        }

        if (in_array($schema_type, array('webpage', 'collectionpage', 'article', 'product'), true)) {
            $graph[] = array(
                '@type' => 'BreadcrumbList',
                '@id' => $url . '#breadcrumbs',
                'itemListElement' => array(
                    array(
                        '@type' => 'ListItem',
                        'position' => 1,
                        'name' => __('Home', 'aysa'),
                        'item' => trailingslashit($site_url),
                    ),
                    array(
                        '@type' => 'ListItem',
                        'position' => 2,
                        'name' => (string) ($page_context['title'] ?? $url),
                        'item' => $url,
                    ),
                ),
            );
        }

        switch ($schema_type) {
            case 'website':
                $graph[] = array(
                    '@type' => 'WebSite',
                    '@id' => trailingslashit($site_url) . '#website-primary',
                    'url' => trailingslashit($site_url),
                    'name' => $company_name,
                    'publisher' => array('@id' => trailingslashit($site_url) . '#organization'),
                );
                break;
            case 'organization':
                break;
            case 'localbusiness':
                break;
            case 'product':
                $product_name = trim((string) ($page_context['title'] ?? ''));
                $product_description = trim((string) ($page_context['description'] ?? ''));
                $product_sku = trim((string) ($page_context['product_sku'] ?? ''));
                $product_price = trim((string) ($page_context['product_price'] ?? ''));
                $product_image_url = trim((string) ($page_context['image_url'] ?? ''));
                $site_icon_url = (string) get_site_icon_url();
                if (
                    $product_name === ''
                    || $product_description === ''
                    || $product_sku === ''
                    || $product_price === ''
                    || $product_image_url === ''
                    || ($site_icon_url !== '' && $product_image_url === $site_icon_url)
                ) {
                    return new WP_Error('incomplete_product_payload', 'Product schema requires real name, description, SKU, price, and product image.');
                }
                $offers = array(
                    '@type' => 'Offer',
                    'url' => $url,
                    'priceCurrency' => (string) ($page_context['product_currency'] ?? 'RON'),
                    'price' => $product_price,
                    'availability' => (string) ($page_context['product_availability'] ?? 'https://schema.org/InStock'),
                    'itemCondition' => 'https://schema.org/NewCondition',
                );
                $graph[] = array(
                    '@type' => 'Product',
                    '@id' => $url . '#product',
                    'name' => $product_name,
                    'description' => $product_description,
                    'sku' => $product_sku,
                    'image' => array($product_image_url),
                    'brand' => array(
                        '@type' => 'Brand',
                        'name' => $company_name,
                    ),
                    'offers' => $offers,
                );
                break;
            case 'article':
                $graph[] = array(
                    '@type' => 'Article',
                    '@id' => $url . '#article',
                    'mainEntityOfPage' => array('@type' => 'WebPage', '@id' => $url),
                    'headline' => (string) ($page_context['title'] ?? $url),
                    'description' => (string) ($page_context['description'] ?? ''),
                    'author' => array(
                        '@type' => 'Person',
                        'name' => (string) ($page_context['author_name'] ?? 'Admin'),
                    ),
                    'datePublished' => (string) ($page_context['published_date'] ?? current_time('c')),
                    'dateModified' => (string) ($page_context['modified_date'] ?? current_time('c')),
                    'publisher' => array('@id' => trailingslashit($site_url) . '#organization'),
                    'image' => !empty($page_context['image_url']) ? array((string) $page_context['image_url']) : array(),
                );
                if (!empty($page_context['author_url'])) {
                    $graph[count($graph) - 1]['author']['url'] = esc_url_raw((string) $page_context['author_url']);
                }
                break;
            case 'collectionpage':
                $collection_page = array(
                    '@type' => 'CollectionPage',
                    '@id' => $url . '#collection',
                    'url' => $url,
                    'name' => (string) ($page_context['title'] ?? $url),
                    'description' => (string) ($page_context['description'] ?? ''),
                    'isPartOf' => array('@id' => trailingslashit($site_url) . '#website'),
                );
                $graph[] = $collection_page;
                if (!empty($page_context['item_list_elements'])) {
                    $graph[] = array(
                        '@type' => 'ItemList',
                        '@id' => $url . '#itemlist',
                        'name' => (string) ($page_context['title'] ?? $url) . ' items',
                        'itemListOrder' => 'https://schema.org/ItemListOrderAscending',
                        'numberOfItems' => count((array) $page_context['item_list_elements']),
                        'itemListElement' => array_values(array_map(static function ($item) {
                            if (!empty($item['image'])) {
                                return $item;
                            }
                            unset($item['image']);
                            return $item;
                        }, (array) $page_context['item_list_elements'])),
                    );
                }
                break;
            case 'webpage':
            default:
                $graph[] = array(
                    '@type' => 'WebPage',
                    '@id' => $url . '#webpage',
                    'url' => $url,
                    'name' => (string) ($page_context['title'] ?? $url),
                    'description' => (string) ($page_context['description'] ?? ''),
                    'isPartOf' => array('@id' => trailingslashit($site_url) . '#website'),
                );
                break;
        }

        $graph = array_values(array_filter($graph, static function ($node) {
            return is_array($node) && !empty($node['@type']);
        }));

        if (empty($graph)) {
            return new WP_Error('empty_schema', 'Schema payload could not be built.');
        }

        return array(
            '@context' => 'https://schema.org',
            '@graph' => $graph,
        );
    }

    private function aysa_technical_schema_preflight_payload($schema_type, array $item) {
        $schema_type = sanitize_key((string) $schema_type);
        $url = $this->aysa_adjustments_normalize_schema_url((string) ($item['url'] ?? ''));
        if ($url === '') {
            return new WP_Error('missing_url', 'Missing URL for schema generation.');
        }

        $frontend_snapshot = $this->aysa_adjustments_get_schema_frontend_snapshot($url, true);
        $page_context = $this->aysa_technical_schema_build_page_context($item);
        $payload = $this->aysa_technical_schema_generate_payload($schema_type, $item, $frontend_snapshot);
        if (is_wp_error($payload)) {
            return $payload;
        }

        return array(
            'payload' => $payload,
            'page_context' => $page_context,
            'frontend_snapshot' => $frontend_snapshot,
        );
    }

    private function aysa_technical_schema_classify_preflight_error(WP_Error $error, array $row_before, $schema_type) {
        $schema_type = sanitize_key((string) $schema_type);
        $code = sanitize_key((string) $error->get_error_code());
        $message = sanitize_text_field((string) $error->get_error_message());

        if (in_array($code, array('incomplete_product_payload', 'missing_url', 'empty_schema'), true)) {
            return array(
                'action_result' => ($code === 'missing_url') ? 'blocked_policy' : 'needs_fields',
                'message' => $message,
                'schema_type' => $schema_type,
            );
        }

        if (
            strpos(strtolower($message), 'missing required') !== false
            || strpos(strtolower($message), 'requires real') !== false
            || strpos(strtolower($message), 'missing') !== false
        ) {
            return array(
                'action_result' => 'needs_fields',
                'message' => $message,
                'schema_type' => $schema_type,
            );
        }

        return array(
            'action_result' => !empty($row_before['can_auto_generate']) || !empty($row_before['can_auto_fix']) ? 'needs_fields' : 'blocked_policy',
            'message' => $message,
            'schema_type' => $schema_type,
        );
    }

    private function aysa_technical_schema_resolve_scope_item(array $item, array $scope_lookup = array()) {
        $url = $this->aysa_adjustments_normalize_schema_url((string) ($item['url'] ?? ''));
        $scope_item = ($url !== '' && isset($scope_lookup[$url]) && is_array($scope_lookup[$url])) ? $scope_lookup[$url] : array();

        return array_merge($scope_item, array(
            'url' => $url,
            'title' => sanitize_text_field((string) ($item['title'] ?? ($scope_item['title'] ?? $url))),
            'page_family' => sanitize_key((string) ($item['page_family'] ?? ($scope_item['page_family'] ?? 'page'))),
            'entity_group' => sanitize_key((string) ($item['entity_group'] ?? ($scope_item['entity_group'] ?? $this->aysa_technical_schema_resolve_entity_group($scope_item)))),
            'schema_type' => sanitize_key((string) ($item['schema_type'] ?? ($item['primary_recommended_node'] ?? ($scope_item['schema_type'] ?? '')))),
            'primary_recommended_node' => sanitize_text_field((string) ($item['primary_recommended_node'] ?? '')),
        ));
    }

    private function aysa_technical_schema_normalize_mode($mode) {
        $mode = sanitize_key((string) $mode);
        if (preg_match('/^entity_(page|product|ecommerce_category|article|business)_(generate|rewrite|validate|test_selected)$/', $mode, $matches)) {
            switch ((string) $matches[2]) {
                case 'generate':
                    return 'generate_safe';
                case 'rewrite':
                    return 'rewrite_recommended';
                case 'validate':
                    return 'validate';
                case 'test_selected':
                    return 'clean_google_rewrite_selected';
            }
        }

        return $mode;
    }

    private function aysa_technical_schema_get_single_row(array $item, $force_refresh = true) {
        $scope_item = $this->aysa_technical_schema_resolve_scope_item($item, $this->aysa_technical_schema_scope_lookup());
        $url = (string) ($scope_item['url'] ?? '');
        if ($url === '') {
            return array();
        }

        return $this->aysa_adjustments_build_schema_row(
            $scope_item,
            $this->aysa_adjustments_get_schema_db_lookup(),
            $force_refresh
        );
    }

    private function aysa_technical_schema_execute_disable($project_id, $url, $schema_type = '') {
        $url = $this->aysa_adjustments_normalize_schema_url($url);
        if ($url === '') {
            return new WP_Error('missing_url', 'Missing URL');
        }

        if (class_exists('AysaSchemaManager')) {
            AysaSchemaManager::remove_schema($url, $schema_type !== '' ? $schema_type : null);
        }
        $this->aysa_technical_schema_blacklist_url($url);
        $this->aysa_technical_schema_invalidate_caches($project_id, $url);

        return array(
            'url' => $url,
            'schema_type' => sanitize_key((string) $schema_type),
        );
    }

    private function aysa_technical_schema_plan_action($mode, array $row_before, array $item) {
        $mode = $this->aysa_technical_schema_normalize_mode($mode);
        $clean_mode = in_array($mode, array('clean_google_rewrite_all', 'clean_google_rewrite_selected'), true);
        $status_before = sanitize_key((string) ($row_before['status'] ?? 'missing'));
        $google_status = sanitize_key((string) ($row_before['google_status'] ?? 'not_applicable'));
        $google_target = sanitize_text_field((string) ($row_before['google_target'] ?? ''));
        $schema_type = sanitize_key((string) ($item['schema_type'] ?? ($row_before['primary_recommended_node'] ?? '')));
        if ($schema_type === '') {
            $schema_type = sanitize_key((string) ($row_before['primary_recommended_node'] ?? 'webpage'));
        }
        if (in_array($google_target, array('Product', 'Article'), true)) {
            $schema_type = sanitize_key($google_target);
        }

        if ($clean_mode) {
            if ($status_before === 'missing' && empty($row_before['google_target_supported']) && empty($row_before['semantic_schema'])) {
                return array('action' => 'not_applicable', 'schema_type' => $schema_type, 'reason' => 'This URL has no Google or semantic schema target for clean rewrite.');
            }
            if (!empty($row_before['has_conflict']) && empty($row_before['can_auto_generate']) && empty($row_before['can_auto_fix']) && empty($row_before['is_aysa_present']) && empty($row_before['is_external_schema_present'])) {
                return array('action' => 'conflict', 'schema_type' => $schema_type, 'reason' => 'Schema conflict prevents clean rewrite on this URL.');
            }
            if ($google_status === 'blocked_output' && empty($row_before['is_aysa_present']) && empty($row_before['can_auto_generate']) && empty($row_before['can_auto_fix'])) {
                return array('action' => 'blocked_output', 'schema_type' => $schema_type, 'reason' => 'AYSA schema output is blocked and no valid payload can be restored automatically.');
            }
            if (
                $google_status === 'not_applicable'
                && !empty($row_before['semantic_schema'])
                && !empty($row_before['can_auto_generate'])
                && empty($row_before['is_external_schema_present'])
            ) {
                return array('action' => 'generate_safe', 'schema_type' => $schema_type, 'reason' => 'AYSA will generate the semantic schema stack on this URL.');
            }
            if (
                !empty($row_before['google_target_supported'])
                || !empty($row_before['semantic_schema'])
                || !empty($row_before['is_external_schema_present'])
                || !empty($row_before['is_aysa_present'])
                || !empty($row_before['can_auto_generate'])
                || !empty($row_before['can_auto_fix'])
            ) {
                return array('action' => 'rewrite_recommended', 'schema_type' => $schema_type, 'reason' => 'AYSA will rewrite this URL and become the schema source of truth.');
            }
            return array('action' => 'blocked_policy', 'schema_type' => $schema_type, 'reason' => 'Clean rewrite could not build a valid payload for this URL.');
        }

        if (in_array($mode, array('full_sitewide_orchestrator', 'force_rewrite_all', 'test_selected_urls'), true)) {
            if ($google_status === 'not_applicable') {
                if (!empty($row_before['semantic_schema']) && !empty($row_before['can_auto_generate'])) {
                    return array('action' => 'generate_safe', 'schema_type' => $schema_type, 'reason' => 'No primary Google target applies, but AYSA can generate the semantic schema stack.');
                }
                if (!empty($row_before['semantic_schema']) && ((string) ($row_before['overwrite_status'] ?? '') === 'needs_takeover' || !empty($row_before['is_external_schema_present']) || !empty($row_before['can_auto_fix']))) {
                    return array('action' => 'rewrite_recommended', 'schema_type' => $schema_type, 'reason' => 'AYSA will rewrite this URL to own the semantic schema stack.');
                }
                return array('action' => 'not_applicable', 'schema_type' => $schema_type, 'reason' => 'This page family has no primary Google rich result target.');
            }
            if ($google_status === 'ready') {
                if ($mode === 'force_rewrite_all' && ((string) ($row_before['overwrite_status'] ?? '') === 'needs_takeover' || !$row_before['semantic_ready'])) {
                    return array('action' => 'rewrite_recommended', 'schema_type' => $schema_type, 'reason' => 'Google target is valid, but AYSA will still rewrite to own the semantic schema stack.');
                }
                return array('action' => 'skip', 'schema_type' => $schema_type, 'reason' => 'Google rich result target is already valid in frontend output.');
            }
            if ($google_status === 'needs_fields') {
                return array(
                    'action' => 'needs_fields',
                    'schema_type' => $schema_type,
                    'reason' => 'Google target ' . $google_target . ' missing required fields: ' . implode(', ', array_values((array) ($row_before['google_required_missing'] ?? array()))),
                );
            }
            if ($google_status === 'blocked_output') {
                if (!empty($row_before['is_aysa_present'])) {
                    return array('action' => 'rewrite_recommended', 'schema_type' => $schema_type, 'reason' => 'AYSA output is blocked; rewrite will restore frontend schema.');
                }
                return array('action' => 'blocked_output', 'schema_type' => $schema_type, 'reason' => 'AYSA schema output is blocked and there is no existing AYSA schema to restore automatically.');
            }
            if ($google_status === 'needs_takeover') {
                if (!empty($row_before['is_aysa_present']) || !empty($row_before['can_auto_generate']) || !empty($row_before['can_auto_fix']) || (string) ($row_before['execution_eligibility'] ?? '') === 'rewrite_takeover' || in_array($mode, array('force_rewrite_all', 'test_selected_urls'), true)) {
                    return array('action' => 'rewrite_recommended', 'schema_type' => $schema_type, 'reason' => 'External ' . $google_target . ' schema detected; AYSA takeover required.');
                }
                return array('action' => 'needs_takeover', 'schema_type' => $schema_type, 'reason' => 'Google target takeover is required before AYSA can become source of truth.');
            }
            if ($google_status === 'missing_target') {
                if (!empty($row_before['can_auto_generate']) || in_array($mode, array('force_rewrite_all', 'test_selected_urls'), true)) {
                    return array('action' => 'generate_safe', 'schema_type' => $schema_type, 'reason' => 'Google target ' . $google_target . ' can be generated safely.');
                }
                return array('action' => 'blocked_policy', 'schema_type' => $schema_type, 'reason' => 'Google rich result target is missing and cannot be generated safely.');
            }
            if ($google_status === 'conflict') {
                return array('action' => 'conflict', 'schema_type' => $schema_type, 'reason' => 'Schema conflict prevents reliable Google-target execution.');
            }
            if ($status_before === 'conflict') {
                if (in_array((string) ($row_before['conflict_reason'] ?? ''), array('parallel_schema_sources', 'duplicate_schema_types', 'schema_in_db_missing_in_frontend'), true)
                    && (!empty($row_before['can_auto_generate']) || !empty($row_before['is_aysa_present']) || !empty($row_before['can_auto_fix']) || (string) ($row_before['execution_eligibility'] ?? '') === 'rewrite_takeover')) {
                    return array('action' => 'rewrite_recommended', 'schema_type' => $schema_type, 'reason' => 'AYSA will take over this URL and replace the external schema output.');
                }
                return array('action' => 'conflict', 'schema_type' => $schema_type, 'reason' => 'Agent 8 decision required before AYSA becomes source of truth.');
            }
            if ($status_before === 'missing') {
                if (!empty($row_before['can_auto_generate']) || in_array($mode, array('force_rewrite_all', 'test_selected_urls'), true)) {
                    return array('action' => 'generate_safe', 'schema_type' => $schema_type);
                }
                return array('action' => 'blocked_policy', 'schema_type' => $schema_type, 'reason' => 'Primary schema is not safe to auto-generate for this page.');
            }
            if (in_array($status_before, array('wrong_type', 'incomplete', 'blocked_output'), true)) {
                if (!empty($row_before['has_conflict'])) {
                    return array('action' => 'conflict', 'schema_type' => $schema_type, 'reason' => 'Schema conflict blocks automatic repair.');
                }
                if (!empty($row_before['is_aysa_present']) || !empty($row_before['can_auto_generate']) || !empty($row_before['can_auto_fix']) || (string) ($row_before['execution_eligibility'] ?? '') === 'rewrite_takeover') {
                    return array('action' => 'rewrite_recommended', 'schema_type' => $schema_type);
                }
                if ($status_before === 'blocked_output') {
                    return array('action' => 'blocked_output', 'schema_type' => $schema_type, 'reason' => 'AYSA schema output is blocked and there is no existing AYSA schema to restore automatically.');
                }
                return array('action' => 'blocked_policy', 'schema_type' => $schema_type, 'reason' => 'Page is not eligible for automatic rewrite.');
            }
            return array('action' => 'skip', 'schema_type' => $schema_type, 'reason' => 'Page is already aligned with the recommended schema policy.');
        }

        if ($mode === 'generate_safe') {
            if ($google_status === 'needs_fields') {
                return array('action' => 'needs_fields', 'schema_type' => $schema_type, 'reason' => 'Selected row is missing required Google-target fields.');
            }
            if (!empty($row_before['can_auto_generate'])) {
                return array('action' => 'generate_safe', 'schema_type' => $schema_type);
            }
            return array('action' => 'blocked_policy', 'schema_type' => $schema_type, 'reason' => 'Selected row is not safe to auto-generate.');
        }

        if ($mode === 'rewrite_recommended') {
            if ($google_status === 'needs_fields') {
                return array('action' => 'needs_fields', 'schema_type' => $schema_type, 'reason' => 'Selected row is missing required Google-target fields.');
            }
            if ($google_status === 'not_applicable') {
                if (!empty($row_before['semantic_schema']) && (!empty($row_before['is_external_schema_present']) || !empty($row_before['can_auto_generate']) || !empty($row_before['can_auto_fix']) || !empty($row_before['is_aysa_present']))) {
                    return array('action' => 'rewrite_recommended', 'schema_type' => $schema_type, 'reason' => 'Selected row will be rewritten to the semantic AYSA schema stack.');
                }
                return array('action' => 'not_applicable', 'schema_type' => $schema_type, 'reason' => 'Selected row has no primary Google rich result target.');
            }
            if (!empty($row_before['has_conflict'])) {
                if (in_array((string) ($row_before['conflict_reason'] ?? ''), array('parallel_schema_sources', 'duplicate_schema_types', 'schema_in_db_missing_in_frontend'), true)
                    && (!empty($row_before['can_auto_generate']) || !empty($row_before['is_aysa_present']) || !empty($row_before['can_auto_fix']) || (string) ($row_before['execution_eligibility'] ?? '') === 'rewrite_takeover')) {
                    return array('action' => 'rewrite_recommended', 'schema_type' => $schema_type, 'reason' => 'AYSA will take over this URL and replace the external schema output.');
                }
                return array('action' => 'conflict', 'schema_type' => $schema_type, 'reason' => 'Conflict detected. AYSA will not rewrite automatically.');
            }
            if (!empty($row_before['is_aysa_present']) || !empty($row_before['can_auto_fix']) || !empty($row_before['can_auto_generate']) || (string) ($row_before['execution_eligibility'] ?? '') === 'rewrite_takeover') {
                return array('action' => 'rewrite_recommended', 'schema_type' => $schema_type);
            }
            return array('action' => 'blocked_policy', 'schema_type' => $schema_type, 'reason' => 'Selected row cannot be rewritten automatically.');
        }

        if ($mode === 'disable_selected') {
            return array('action' => 'disable_selected', 'schema_type' => $schema_type);
        }

        if (in_array($mode, array('validate', 'revalidate', 'scan'), true)) {
            return array('action' => 'validate', 'schema_type' => $schema_type);
        }

        return array('action' => 'validate', 'schema_type' => $schema_type);
    }

    private function aysa_technical_schema_run_action($project_id, $action, array $item) {
        $action = sanitize_key((string) $action);
        $url = $this->aysa_adjustments_normalize_schema_url((string) ($item['url'] ?? ''));
        $schema_type = sanitize_key((string) ($item['schema_type'] ?? ($item['primary_recommended_node'] ?? 'webpage')));

        switch ($action) {
            case 'generate_safe':
            case 'rewrite_recommended':
                return $this->aysa_technical_schema_rewrite_single_item($project_id, array_merge($item, array(
                    'url' => $url,
                    'schema_type' => $schema_type,
                )));
            case 'disable_selected':
                return $this->aysa_technical_schema_execute_disable($project_id, $url, $schema_type);
            case 'validate':
                return array(
                    'url' => $url,
                    'schema_type' => $schema_type,
                );
            default:
                return new WP_Error('unsupported_schema_action', 'Unsupported schema action: ' . $action);
        }
    }

    private function aysa_technical_schema_rewrite_single_item($project_id, array $item) {
        $url = $this->aysa_adjustments_normalize_schema_url((string) ($item['url'] ?? ''));
        if ($url === '') {
            return new WP_Error('missing_url', 'Missing URL');
        }

        $schema_type = sanitize_key((string) ($item['schema_type'] ?? $item['primary_recommended_node'] ?? 'webpage'));
        if ($schema_type === '') {
            $schema_type = 'webpage';
        }

        $preflight = $this->aysa_technical_schema_preflight_payload($schema_type, $item);
        if (is_wp_error($preflight)) {
            return $preflight;
        }
        $payload = (array) ($preflight['payload'] ?? array());

        if (class_exists('AysaSchemaManager')) {
            AysaSchemaManager::remove_schema($url, null);
        }
        $this->aysa_technical_schema_unblacklist_url($url);

        $inserted = class_exists('AysaSchemaManager')
            ? AysaSchemaManager::insert_schema($url, $schema_type, wp_json_encode($payload, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), true)
            : false;

        if (!$inserted) {
            return new WP_Error('schema_insert_failed', 'Failed to insert rewritten schema.');
        }

        $this->aysa_technical_schema_invalidate_caches($project_id, $url);

        return array(
            'url' => $url,
            'schema_type' => $schema_type,
            'schema_id' => intval($inserted),
        );
    }

    private function aysa_technical_schema_get_job_response(array $job) {
        return array(
            'job_id' => sanitize_text_field((string) ($job['job_id'] ?? '')),
            'job_kind' => sanitize_key((string) ($job['job_kind'] ?? 'worker')),
            'mode' => sanitize_key((string) ($job['mode'] ?? 'validate')),
            'entity_group' => sanitize_key((string) ($job['entity_group'] ?? '')),
            'scope' => sanitize_key((string) ($job['scope'] ?? 'selection')),
            'status' => sanitize_key((string) ($job['status'] ?? 'queued')),
            'total' => intval($job['total'] ?? 0),
            'processed' => intval($job['processed'] ?? 0),
            'successful' => intval($job['successful'] ?? 0),
            'failed' => intval($job['failed'] ?? 0),
            'skipped' => intval($job['skipped'] ?? 0),
            'generated' => intval($job['generated'] ?? 0),
            'rewritten' => intval($job['rewritten'] ?? 0),
            'overwritten' => intval($job['overwritten'] ?? ($job['rewritten'] ?? 0)),
            'autofilled' => intval($job['autofilled'] ?? 0),
            'validated' => intval($job['validated'] ?? 0),
            'failed_payload' => intval($job['failed_payload'] ?? ($job['failed'] ?? 0)),
            'needs_takeover' => intval($job['needs_takeover'] ?? 0),
            'needs_fields' => intval($job['needs_fields'] ?? 0),
            'skipped_google_ready' => intval($job['skipped_google_ready'] ?? 0),
            'not_applicable' => intval($job['not_applicable'] ?? 0),
            'failed_post_validation' => intval($job['failed_post_validation'] ?? 0),
            'google_ready' => intval($job['google_ready'] ?? 0),
            'semantic_ready' => intval($job['semantic_ready'] ?? 0),
            'website_wide_ready_after' => intval($job['website_wide_ready_after'] ?? 0),
            'page_level_ready_after' => intval($job['page_level_ready_after'] ?? 0),
            'blocked_policy' => intval($job['blocked_policy'] ?? 0),
            'blocked_output' => intval($job['blocked_output'] ?? 0),
            'blocked' => intval($job['blocked'] ?? 0),
            'conflict' => intval($job['conflict'] ?? 0),
            'cached' => intval($job['cached'] ?? 0),
            'refetched' => intval($job['refetched'] ?? 0),
            'project_id' => intval($job['project_id'] ?? 0),
            'updated_at' => sanitize_text_field((string) ($job['updated_at'] ?? '')),
            'snapshot_updated_at' => sanitize_text_field((string) ($job['snapshot_updated_at'] ?? '')),
            'failed_urls' => array_values(array_slice((array) ($job['failed_urls'] ?? array()), 0, 10)),
            'results' => array_values(array_slice((array) ($job['results'] ?? array()), 0, 25)),
            'last_error' => sanitize_text_field((string) ($job['last_error'] ?? '')),
        );
    }

    private function aysa_technical_schema_post_validation_passes($mode, array $row_after) {
        $mode = sanitize_key((string) $mode);
        $clean_mode = in_array($mode, array('clean_google_rewrite_all', 'clean_google_rewrite_selected'), true);
        if (!$clean_mode) {
            $google_required = !empty($row_after['google_target_supported']);
            $google_ok = !$google_required || (string) ($row_after['google_status'] ?? 'missing_target') === 'ready';
            $semantic_ok = empty($row_after['semantic_schema']) || !empty($row_after['semantic_ready']);
            return $google_ok && $semantic_ok;
        }

        $page_family = sanitize_key((string) ($row_after['page_family'] ?? 'page'));
        $google_required = !empty($row_after['google_target_supported']);
        $semantic_required = !empty($row_after['semantic_schema']);
        $google_ok = true;
        $semantic_ok = true;

        if ($page_family === 'homepage') {
            $semantic_ok = !$semantic_required || !empty($row_after['semantic_ready']);
            $website_wide_ok = !empty($row_after['website_wide_ready']);
            if ((string) ($row_after['nap_status'] ?? '') === 'needs_fields') {
                return $semantic_ok && $website_wide_ok;
            }
            return $semantic_ok && $website_wide_ok;
        }

        if (in_array($page_family, array('category', 'tag'), true)) {
            $google_ok = !$google_required || (string) ($row_after['google_status'] ?? 'missing_target') === 'ready';
            $semantic_ok = !$semantic_required || !empty($row_after['semantic_ready']);
            return $google_ok && $semantic_ok;
        }

        if (in_array($page_family, array('product', 'article'), true)) {
            return !$google_required || (string) ($row_after['google_status'] ?? 'missing_target') === 'ready';
        }

        if ($google_required) {
            $google_ok = (string) ($row_after['google_status'] ?? 'missing_target') === 'ready';
        }
        if ($semantic_required) {
            $semantic_ok = !empty($row_after['semantic_ready']);
        }
        $website_ok = empty($row_after['website_wide_schema']) || !empty($row_after['website_wide_ready']);

        return $google_ok && $semantic_ok && $website_ok;
    }

    private function aysa_technical_schema_process_refresh_job(array $job, $chunk_size = 12) {
        $items = isset($job['items']) && is_array($job['items']) ? array_values($job['items']) : array();
        $rows = isset($job['refresh_rows']) && is_array($job['refresh_rows']) ? $job['refresh_rows'] : array();
        $db_lookup = $this->aysa_adjustments_get_schema_db_lookup();
        $processed_in_chunk = 0;

        while (!empty($items) && $processed_in_chunk < max(1, intval($chunk_size))) {
            $item = array_shift($items);
            $item = is_array($item) ? $item : array();
            $url = $this->aysa_adjustments_normalize_schema_url((string) ($item['url'] ?? ''));
            if ($url === '') {
                $job['processed'] = intval($job['processed'] ?? 0) + 1;
                $job['failed'] = intval($job['failed'] ?? 0) + 1;
                $job['failed_urls'][] = '';
                $processed_in_chunk++;
                continue;
            }

            if ($this->aysa_adjustments_has_cached_schema_frontend_snapshot($url)) {
                $job['cached'] = intval($job['cached'] ?? 0) + 1;
            }

            try {
                $row = $this->aysa_adjustments_build_schema_row($item, $db_lookup, true);
                $rows[$url] = $row;
                $this->aysa_schema_inventory_upsert_row(intval($job['project_id'] ?? 0), $item, $row);
                $job['refetched'] = intval($job['refetched'] ?? 0) + 1;
            } catch (Throwable $e) {
                $job['failed'] = intval($job['failed'] ?? 0) + 1;
                $job['last_error'] = $e->getMessage();
                $job['failed_urls'][] = sanitize_text_field($url);
                $job['results'][] = array(
                    'url' => esc_url_raw($url),
                    'page_family' => sanitize_key((string) ($item['page_family'] ?? 'page')),
                    'action_result' => 'failed',
                    'message' => sanitize_text_field($e->getMessage()),
                );
            }

            $job['processed'] = intval($job['processed'] ?? 0) + 1;
            $processed_in_chunk++;
        }

        $job['items'] = array_values($items);
        $job['refresh_rows'] = $rows;

        if (empty($job['items'])) {
            $this->aysa_schema_inventory_finalize_reconcile(intval($job['project_id'] ?? 0), array_keys($rows));
            $this->aysa_schema_inventory_update_client_context(intval($job['project_id'] ?? 0));
            $job['status'] = intval($job['failed'] ?? 0) > 0 && empty($rows) ? 'failed' : 'completed';
            $job['snapshot_updated_at'] = current_time('mysql');
            delete_transient($this->aysa_technical_schema_refresh_job_pointer_key(intval($job['project_id'] ?? 0)));
        } else {
            $job['status'] = 'running';
        }

        return $job;
    }

    private function aysa_technical_schema_process_job($job_id, $chunk_size = 12) {
        $job = $this->aysa_technical_schema_read_job($job_id);
        if (!$job) {
            return null;
        }

        $status = (string) ($job['status'] ?? 'queued');
        if (in_array($status, array('completed', 'failed', 'cancelled'), true)) {
            return $job;
        }

        $lock_until = intval($job['lock_until'] ?? 0);
        if ($lock_until > time()) {
            return $job;
        }

        $job['status'] = 'running';
        $job['lock_until'] = time() + 20;
        $job = $this->aysa_technical_schema_write_job($job_id, $job);

        if ((string) ($job['job_kind'] ?? 'worker') === 'snapshot_refresh') {
            $job = $this->aysa_technical_schema_process_refresh_job($job, $chunk_size);
            $job['lock_until'] = 0;
            $job = $this->aysa_technical_schema_write_job($job_id, $job);
            if ((string) ($job['status'] ?? '') === 'running') {
                $this->aysa_technical_schema_schedule_job($job_id, 1);
            }
            return $job;
        }

        $items = isset($job['items']) && is_array($job['items']) ? array_values($job['items']) : array();
        $processed_in_chunk = 0;

        while (!empty($items) && $processed_in_chunk < max(1, intval($chunk_size))) {
            $item = array_shift($items);
            $item = is_array($item) ? $item : array();
            $row_before = $this->aysa_technical_schema_get_single_row($item, true);
            $action_plan = $this->aysa_technical_schema_plan_action((string) ($job['mode'] ?? 'validate'), $row_before, $item);

            $result_entry = array(
                'url' => esc_url_raw((string) ($item['url'] ?? ($row_before['url'] ?? ''))),
                'title' => sanitize_text_field((string) ($item['title'] ?? ($row_before['title'] ?? ''))),
                'page_family' => sanitize_key((string) ($item['page_family'] ?? ($row_before['page_family'] ?? 'page'))),
                'entity_group' => sanitize_key((string) ($item['entity_group'] ?? ($row_before['entity_group'] ?? ''))),
                'schema_type' => sanitize_key((string) ($action_plan['schema_type'] ?? ($row_before['primary_recommended_node'] ?? 'webpage'))),
                'google_target' => sanitize_text_field((string) ($row_before['google_target_label'] ?? ($row_before['google_target'] ?? ''))),
                'semantic_schema' => sanitize_text_field((string) ($row_before['semantic_schema_label'] ?? '')),
                'website_wide_schema' => sanitize_text_field((string) ($row_before['website_wide_schema_label'] ?? '')),
                'google_status' => sanitize_key((string) ($row_before['google_status'] ?? 'not_applicable')),
                'semantic_before' => !empty($row_before['semantic_ready']) ? 'ready' : 'needs_update',
                'website_wide_before' => !empty($row_before['website_wide_ready']) ? 'ready' : 'needs_update',
                'primary_recommended_node' => sanitize_text_field((string) ($row_before['primary_recommended_node'] ?? '')),
                'status_before' => sanitize_key((string) ($row_before['status'] ?? 'missing')),
                'eligibility' => sanitize_key((string) ($row_before['execution_eligibility'] ?? 'manual_review')),
                'overwrite_status' => sanitize_key((string) ($row_before['overwrite_status'] ?? 'ready_for_overwrite')),
                'action_planned' => sanitize_key((string) ($action_plan['action'] ?? 'validate')),
                'action_result' => 'pending',
                'google_before' => sanitize_key((string) ($row_before['google_status'] ?? 'not_applicable')),
                'autofill_actions' => array(),
                'validation_after' => array(),
                'message' => sanitize_text_field((string) ($action_plan['reason'] ?? '')),
            );

            $job['processed'] = intval($job['processed'] ?? 0) + 1;

            if (($action_plan['action'] ?? '') === 'conflict') {
                $job['conflict'] = intval($job['conflict'] ?? 0) + 1;
                $job['skipped'] = intval($job['skipped'] ?? 0) + 1;
                $result_entry['action_result'] = 'conflict';
                $this->aysa_schema_inventory_upsert_row(intval($job['project_id'] ?? 0), $item, $row_before);
            } elseif (($action_plan['action'] ?? '') === 'skip') {
                $job['skipped'] = intval($job['skipped'] ?? 0) + 1;
                $job['skipped_google_ready'] = intval($job['skipped_google_ready'] ?? 0) + 1;
                $result_entry['action_result'] = 'skipped';
                $this->aysa_schema_inventory_upsert_row(intval($job['project_id'] ?? 0), $item, $row_before);
            } elseif (($action_plan['action'] ?? '') === 'not_applicable') {
                $job['skipped'] = intval($job['skipped'] ?? 0) + 1;
                $job['not_applicable'] = intval($job['not_applicable'] ?? 0) + 1;
                $result_entry['action_result'] = 'not_applicable';
                $this->aysa_schema_inventory_upsert_row(intval($job['project_id'] ?? 0), $item, $row_before);
            } elseif (($action_plan['action'] ?? '') === 'needs_fields') {
                $job['needs_fields'] = intval($job['needs_fields'] ?? 0) + 1;
                $job['blocked_policy'] = intval($job['blocked_policy'] ?? 0) + 1;
                $job['blocked'] = intval($job['blocked'] ?? 0) + 1;
                $job['skipped'] = intval($job['skipped'] ?? 0) + 1;
                $result_entry['action_result'] = 'needs_fields';
                $this->aysa_schema_inventory_upsert_row(intval($job['project_id'] ?? 0), $item, $row_before);
            } elseif (($action_plan['action'] ?? '') === 'needs_takeover') {
                $job['needs_takeover'] = intval($job['needs_takeover'] ?? 0) + 1;
                $job['blocked_policy'] = intval($job['blocked_policy'] ?? 0) + 1;
                $job['blocked'] = intval($job['blocked'] ?? 0) + 1;
                $job['skipped'] = intval($job['skipped'] ?? 0) + 1;
                $result_entry['action_result'] = 'needs_takeover';
                $this->aysa_schema_inventory_upsert_row(intval($job['project_id'] ?? 0), $item, $row_before);
            } elseif (($action_plan['action'] ?? '') === 'blocked_policy') {
                $job['blocked_policy'] = intval($job['blocked_policy'] ?? 0) + 1;
                $job['blocked'] = intval($job['blocked'] ?? 0) + 1;
                $job['skipped'] = intval($job['skipped'] ?? 0) + 1;
                $result_entry['action_result'] = 'blocked_policy';
                $this->aysa_schema_inventory_upsert_row(intval($job['project_id'] ?? 0), $item, $row_before);
            } elseif (($action_plan['action'] ?? '') === 'blocked_output') {
                $job['blocked_output'] = intval($job['blocked_output'] ?? 0) + 1;
                $job['blocked'] = intval($job['blocked'] ?? 0) + 1;
                $job['skipped'] = intval($job['skipped'] ?? 0) + 1;
                $result_entry['action_result'] = 'blocked_output';
                $this->aysa_schema_inventory_upsert_row(intval($job['project_id'] ?? 0), $item, $row_before);
            } else {
                $mutating_action = in_array((string) ($action_plan['action'] ?? ''), array('generate_safe', 'rewrite_recommended'), true);
                if ($mutating_action) {
                    $preflight = $this->aysa_technical_schema_preflight_payload(
                        (string) ($action_plan['schema_type'] ?? ''),
                        array_merge($item, array(
                            'schema_type' => (string) ($action_plan['schema_type'] ?? ''),
                        ))
                    );
                    if (is_wp_error($preflight)) {
                        $classified = $this->aysa_technical_schema_classify_preflight_error(
                            $preflight,
                            $row_before,
                            (string) ($action_plan['schema_type'] ?? '')
                        );
                        if (($classified['action_result'] ?? '') === 'needs_fields') {
                            $job['needs_fields'] = intval($job['needs_fields'] ?? 0) + 1;
                            $job['blocked_policy'] = intval($job['blocked_policy'] ?? 0) + 1;
                            $job['blocked'] = intval($job['blocked'] ?? 0) + 1;
                            $job['skipped'] = intval($job['skipped'] ?? 0) + 1;
                            $result_entry['action_result'] = 'needs_fields';
                        } else {
                            $job['blocked_policy'] = intval($job['blocked_policy'] ?? 0) + 1;
                            $job['blocked'] = intval($job['blocked'] ?? 0) + 1;
                            $job['skipped'] = intval($job['skipped'] ?? 0) + 1;
                            $result_entry['action_result'] = 'blocked_policy';
                        }
                        $result_entry['message'] = (string) ($classified['message'] ?? 'Schema payload cannot be built for this URL.');
                        $this->aysa_schema_inventory_upsert_row(intval($job['project_id'] ?? 0), $item, $row_before);
                        if (count((array) ($job['results'] ?? array())) >= 100) {
                            $job['results'] = array_slice((array) $job['results'], -99);
                        }
                        $job['results'][] = $result_entry;
                        $processed_in_chunk++;
                        continue;
                    }
                    $result_entry['autofill_actions'] = array_values((array) (($preflight['page_context']['autofill_actions'] ?? array())));
                }

                $result = $this->aysa_technical_schema_run_action(
                    intval($job['project_id'] ?? 0),
                    (string) ($action_plan['action'] ?? 'validate'),
                    array_merge($item, array(
                        'schema_type' => (string) ($action_plan['schema_type'] ?? ''),
                    ))
                );

                if (is_wp_error($result)) {
                    $job['failed'] = intval($job['failed'] ?? 0) + 1;
                    $job['failed_payload'] = intval($job['failed_payload'] ?? 0) + 1;
                    $job['last_error'] = $result->get_error_message();
                    $job['failed_urls'][] = sanitize_text_field((string) ($item['url'] ?? ''));
                    $result_entry['action_result'] = 'failed';
                    $result_entry['message'] = $result->get_error_message();
                    $this->aysa_schema_inventory_upsert_row(intval($job['project_id'] ?? 0), $item, $row_before);
                } else {
                    $row_after = $this->aysa_technical_schema_get_single_row($item, true);
                    $this->aysa_schema_inventory_upsert_row(intval($job['project_id'] ?? 0), $item, $row_after);
                    $result_entry['validation_after'] = array(
                        'status' => sanitize_key((string) ($row_after['status'] ?? 'missing')),
                        'google_status' => sanitize_key((string) ($row_after['google_status'] ?? 'not_applicable')),
                        'google_target' => sanitize_text_field((string) ($row_after['google_target_label'] ?? ($row_after['google_target'] ?? ''))),
                        'semantic_status' => !empty($row_after['semantic_ready']) ? 'ready' : 'needs_update',
                        'semantic_schema' => sanitize_text_field((string) ($row_after['semantic_schema_label'] ?? '')),
                        'website_wide_status' => !empty($row_after['website_wide_ready']) ? 'ready' : 'needs_update',
                        'website_wide_schema' => sanitize_text_field((string) ($row_after['website_wide_schema_label'] ?? '')),
                        'detected_nodes' => array_values((array) ($row_after['detected_nodes'] ?? array())),
                        'required_fields_missing' => array_values((array) ($row_after['required_fields_missing'] ?? array())),
                        'google_required_missing' => array_values((array) ($row_after['google_required_missing'] ?? array())),
                    );
                    if ((string) ($row_after['google_status'] ?? '') === 'ready') {
                        $job['google_ready'] = intval($job['google_ready'] ?? 0) + 1;
                    }
                    if (!empty($row_after['semantic_ready'])) {
                        $job['semantic_ready'] = intval($job['semantic_ready'] ?? 0) + 1;
                    }
                    if (!empty($row_after['website_wide_ready'])) {
                        $job['website_wide_ready_after'] = intval($job['website_wide_ready_after'] ?? 0) + 1;
                    }
                    if (!empty($row_after['page_level_ready'])) {
                        $job['page_level_ready_after'] = intval($job['page_level_ready_after'] ?? 0) + 1;
                    }

                    $mutating_action = in_array((string) ($action_plan['action'] ?? ''), array('generate_safe', 'rewrite_recommended', 'disable_selected'), true);
                    $post_validation_passed = $this->aysa_technical_schema_post_validation_passes((string) ($job['mode'] ?? 'validate'), $row_after);
                    if ($mutating_action && ($action_plan['action'] ?? '') !== 'disable_selected' && !$post_validation_passed) {
                        $job['failed'] = intval($job['failed'] ?? 0) + 1;
                        $job['failed_post_validation'] = intval($job['failed_post_validation'] ?? 0) + 1;
                        $job['last_error'] = 'Post-validation failed for ' . sanitize_text_field((string) ($item['url'] ?? ''));
                        $job['failed_urls'][] = sanitize_text_field((string) ($item['url'] ?? ''));
                        $result_entry['action_result'] = 'failed_post_validation';
                        $result_entry['message'] = 'Schema was written but frontend validation did not reach the required clean rewrite state for this page family.';
                    } else {
                        $job['successful'] = intval($job['successful'] ?? 0) + 1;
                        switch ((string) ($action_plan['action'] ?? 'validate')) {
                            case 'generate_safe':
                                $job['generated'] = intval($job['generated'] ?? 0) + 1;
                                $result_entry['action_result'] = 'generated';
                                break;
                            case 'rewrite_recommended':
                                $job['rewritten'] = intval($job['rewritten'] ?? 0) + 1;
                                $job['overwritten'] = intval($job['overwritten'] ?? 0) + 1;
                                if (!empty($result_entry['autofill_actions'])) {
                                    $job['autofilled'] = intval($job['autofilled'] ?? 0) + 1;
                                    $result_entry['action_result'] = 'autofilled_then_overwritten';
                                    $result_entry['message'] = implode(' ', array_values((array) $result_entry['autofill_actions']));
                                } else {
                                    $result_entry['action_result'] = 'rewritten';
                                }
                                break;
                            case 'disable_selected':
                                $result_entry['action_result'] = 'disabled';
                                break;
                            default:
                                $job['validated'] = intval($job['validated'] ?? 0) + 1;
                                $result_entry['action_result'] = 'validated';
                                break;
                        }
                    }
                }
            }

            if (count((array) ($job['results'] ?? array())) >= 100) {
                $job['results'] = array_slice((array) $job['results'], -99);
            }
            $job['results'][] = $result_entry;
            $processed_in_chunk++;
        }

        $job['items'] = array_values($items);
        $job['lock_until'] = 0;
        if ((string) ($job['status'] ?? '') !== 'cancelled') {
            if (empty($job['items'])) {
                $job['status'] = intval($job['failed'] ?? 0) > 0 && intval($job['successful'] ?? 0) === 0 ? 'failed' : 'completed';
                $this->aysa_technical_schema_invalidate_caches(intval($job['project_id'] ?? 0));
                $this->aysa_schema_inventory_update_client_context(intval($job['project_id'] ?? 0));
            } else {
                $job['status'] = 'running';
            }
        }

        $job = $this->aysa_technical_schema_write_job($job_id, $job);
        if ((string) ($job['status'] ?? '') === 'running') {
            $this->aysa_technical_schema_schedule_job($job_id, 1);
        }

        return $job;
    }

    public function process_technical_schema_job_event($job_id) {
        $job = $this->aysa_technical_schema_process_job((string) $job_id, 12);
        if (is_array($job) && in_array((string) ($job['status'] ?? ''), array('completed', 'failed', 'cancelled'), true)) {
            $this->aysa_project_jobs_finalize(
                intval($job['project_id'] ?? 0),
                $this->aysa_project_jobs_make_worker_ref('technical_schema', 'schema_job', (string) ($job['job_id'] ?? $job_id)),
                ((string) ($job['status'] ?? '') === 'completed') ? 'completed' : 'failed',
                is_array($job) ? $job : null,
                (string) ($job['last_error'] ?? '')
            );
        }
    }

    public function process_technical_schema_rewrite_job_event($job_id) {
        $this->aysa_technical_schema_process_job((string) $job_id, 12);
    }

    private function aysa_technical_schema_collect_start_items($project_id, $mode, $scope, array $decoded_items) {
        $scope_lookup = $this->aysa_technical_schema_scope_lookup();
        $items = array();
        $entity_group = '';
        if (sanitize_key((string) $scope) === 'sitewide' && !empty($decoded_items[0]) && is_array($decoded_items[0])) {
            $entity_group = sanitize_key((string) ($decoded_items[0]['entity_group'] ?? ''));
        }

        if (sanitize_key((string) $scope) === 'sitewide') {
            foreach ($scope_lookup as $url => $page_data) {
                if (!is_array($page_data) || $url === '') {
                    continue;
                }
                if ($entity_group !== '' && sanitize_key((string) ($page_data['entity_group'] ?? '')) !== $entity_group) {
                    continue;
                }
                $items[$url] = array(
                    'url' => $url,
                    'title' => sanitize_text_field((string) ($page_data['title'] ?? $url)),
                    'page_family' => sanitize_key((string) ($page_data['page_family'] ?? 'page')),
                    'entity_group' => sanitize_key((string) ($page_data['entity_group'] ?? '')),
                    'schema_type' => sanitize_key((string) ($page_data['schema_type'] ?? '')),
                    'primary_recommended_node' => sanitize_key((string) ($page_data['schema_type'] ?? '')),
                );
            }
            return array_values($items);
        }

        foreach ($decoded_items as $row) {
            if (!is_array($row)) {
                continue;
            }
            $item = $this->aysa_technical_schema_resolve_scope_item($row, $scope_lookup);
            if (!empty($item['url'])) {
                if ($entity_group !== '' && sanitize_key((string) ($item['entity_group'] ?? '')) !== $entity_group) {
                    continue;
                }
                $items[$item['url']] = $item;
            }
        }

        return array_values($items);
    }

    private function aysa_technical_schema_create_job($project_id, $mode, $scope, array $items, $job_kind = 'worker') {
        $job_id = 'schema_' . sanitize_key((string) $mode) . '_' . wp_generate_password(8, false, false) . '_' . time();
        $entity_group = '';
        if (!empty($items[0]) && is_array($items[0])) {
            $entity_group = sanitize_key((string) ($items[0]['entity_group'] ?? ''));
        }
        $job = array(
            'job_id' => $job_id,
            'project_id' => intval($project_id),
            'mode' => sanitize_key((string) $mode),
            'entity_group' => $entity_group,
            'scope' => sanitize_key((string) $scope),
            'job_kind' => sanitize_key((string) $job_kind),
            'status' => 'queued',
            'total' => count($items),
            'processed' => 0,
            'successful' => 0,
            'failed' => 0,
            'skipped' => 0,
            'generated' => 0,
            'rewritten' => 0,
            'overwritten' => 0,
            'autofilled' => 0,
            'validated' => 0,
            'failed_payload' => 0,
            'needs_takeover' => 0,
            'needs_fields' => 0,
            'skipped_google_ready' => 0,
            'not_applicable' => 0,
            'failed_post_validation' => 0,
            'google_ready' => 0,
            'semantic_ready' => 0,
            'website_wide_ready_after' => 0,
            'page_level_ready_after' => 0,
            'blocked_policy' => 0,
            'blocked_output' => 0,
            'blocked' => 0,
            'conflict' => 0,
            'cached' => 0,
            'refetched' => 0,
            'items' => array_values($items),
            'results' => array(),
            'failed_urls' => array(),
            'last_error' => '',
            'created_at' => current_time('mysql'),
            'created_by' => get_current_user_id(),
            'lock_until' => 0,
        );

        return $this->aysa_technical_schema_write_job($job_id, $job);
    }

    public function handle_technical_schema_job_start() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_technical_schema_resolve_project_id($project_id);

        $mode = isset($_POST['mode']) ? sanitize_key((string) wp_unslash($_POST['mode'])) : 'validate';
        $scope = isset($_POST['scope']) ? sanitize_key((string) wp_unslash($_POST['scope'])) : 'selection';
        $raw_items = isset($_POST['items']) ? wp_unslash($_POST['items']) : '[]';
        $decoded_items = is_string($raw_items) ? json_decode($raw_items, true) : (is_array($raw_items) ? $raw_items : array());
        if (!is_array($decoded_items)) {
            $decoded_items = array();
        }

        $items = $this->aysa_technical_schema_collect_start_items($project_id, $mode, $scope, $decoded_items);
        if (empty($items)) {
            wp_send_json_error('No valid schema rows were selected.');
            return;
        }

        $job = $this->aysa_project_jobs_enqueue(
            $project_id,
            'technical',
            'schema_job',
            array(
                'project_id' => $project_id,
                'mode' => $mode,
                'scope' => $scope,
                'items' => $items,
            ),
            array(
                'dedupe_key' => md5(wp_json_encode(array($project_id, $mode, $scope, $items))),
                'input_summary' => array(
                    'mode' => $mode,
                    'scope' => $scope,
                    'selected_count' => count($items),
                ),
            )
        );

        $worker_job = !empty($job['worker_ref']) ? $this->aysa_technical_schema_read_job((string) ($job['worker_ref'] !== '' ? preg_replace('/^technical_schema:schema_job:/', '', (string) $job['worker_ref']) : '')) : null;
        wp_send_json_success(array(
            'job' => is_array($worker_job) ? $this->aysa_technical_schema_get_job_response((array) $worker_job) : null,
            'started' => in_array((string) ($job['status'] ?? ''), array('queued', 'running', 'completed'), true),
            'project_job_status' => array(
                'run_id' => (string) ($job['job_id'] ?? ''),
                'status' => (string) ($job['status'] ?? 'queued'),
                'worker_ref' => (string) ($job['worker_ref'] ?? ''),
                'worker_status' => (string) ($job['worker_status'] ?? ''),
            ),
            'project_job_rack' => $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id)),
        ));
    }

    public function handle_technical_schema_job_status() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_technical_schema_resolve_project_id($project_id);

        $job_id = isset($_POST['job_id']) ? sanitize_text_field((string) wp_unslash($_POST['job_id'])) : '';
        if ($job_id === '') {
            wp_send_json_error('Missing job id');
            return;
        }

        $job = $this->aysa_technical_schema_read_job($job_id);
        if (!$job) {
            wp_send_json_error('Schema job not found.');
            return;
        }
        if (intval($job['project_id'] ?? 0) !== $project_id) {
            wp_send_json_error('Schema job does not belong to this project.');
            return;
        }

        if ((string) ($job['status'] ?? '') === 'queued') {
            $job = $this->aysa_technical_schema_process_job($job_id, 2);
        } elseif ((string) ($job['status'] ?? '') === 'running' && intval($job['lock_until'] ?? 0) <= time()) {
            $job = $this->aysa_technical_schema_process_job($job_id, 2);
        }

        if (in_array((string) ($job['status'] ?? ''), array('completed', 'failed', 'cancelled'), true)) {
            $this->aysa_project_jobs_finalize(
                $project_id,
                $this->aysa_project_jobs_make_worker_ref('technical_schema', 'schema_job', (string) ($job['job_id'] ?? $job_id)),
                ((string) ($job['status'] ?? '') === 'completed') ? 'completed' : 'failed',
                is_array($job) ? $job : null,
                (string) ($job['last_error'] ?? '')
            );
        }

        wp_send_json_success(array(
            'job' => $this->aysa_technical_schema_get_job_response((array) $job),
            'project_job_rack' => $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id)),
        ));
    }

    public function handle_technical_schema_job_cancel() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_technical_schema_resolve_project_id($project_id);
        $job_id = isset($_POST['job_id']) ? sanitize_text_field((string) wp_unslash($_POST['job_id'])) : '';
        $job = $job_id !== '' ? $this->aysa_technical_schema_read_job($job_id) : null;

        if (!$job || intval($job['project_id'] ?? 0) !== $project_id) {
            wp_send_json_error('Schema job not found.');
            return;
        }

        $job['status'] = 'cancelled';
        $job['lock_until'] = 0;
        $job = $this->aysa_technical_schema_write_job($job_id, $job);
        if ((string) ($job['job_kind'] ?? '') === 'snapshot_refresh') {
            delete_transient($this->aysa_technical_schema_refresh_job_pointer_key($project_id));
        }

        wp_send_json_success(array(
            'job' => $this->aysa_technical_schema_get_job_response((array) $job),
            'cancelled' => true,
        ));
    }

    public function handle_technical_schema_refresh_start() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_technical_schema_resolve_project_id($project_id);
        $this->aysa_adjustments_delete_technical_schema_snapshot_cache($project_id);
        $running_job = $this->aysa_technical_schema_get_current_refresh_job($project_id);
        if ($running_job && in_array((string) ($running_job['status'] ?? ''), array('queued', 'running'), true)) {
            wp_send_json_success(array(
                'job' => $this->aysa_technical_schema_get_job_response($running_job),
                'started' => false,
                'already_running' => true,
            ));
            return;
        }

        $items = array();
        foreach ((array) $this->aysa_adjustments_collect_schema_scope_pages() as $page_data) {
            if (!is_array($page_data)) {
                continue;
            }
            $url = $this->aysa_adjustments_normalize_schema_url((string) ($page_data['url'] ?? ''));
            if ($url === '') {
                continue;
            }
            $page_data['url'] = $url;
            $items[$url] = $page_data;
        }
        $items = array_values($items);
        if (empty($items)) {
            wp_send_json_error('No schema scope pages were found for snapshot refresh.');
            return;
        }

        $job = $this->aysa_technical_schema_create_job($project_id, 'snapshot_refresh', 'sitewide', $items, 'snapshot_refresh');
        set_transient($this->aysa_technical_schema_refresh_job_pointer_key($project_id), (string) $job['job_id'], 12 * HOUR_IN_SECONDS);
        $summary_state = $this->aysa_adjustments_get_cached_or_idle_technical_schema_summary($project_id);
        $summary_state['status'] = 'rebuilding';
        $summary_state['needs_refresh'] = false;
        set_transient(
            $this->aysa_adjustments_get_schema_cache_key($project_id, 'summary'),
            $summary_state,
            HOUR_IN_SECONDS
        );
        $this->aysa_technical_schema_schedule_job((string) $job['job_id'], 1);

        wp_send_json_success(array(
            'job' => $this->aysa_technical_schema_get_job_response((array) $job),
            'started' => true,
        ));
    }

    public function handle_technical_schema_refresh_status() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_technical_schema_resolve_project_id($project_id);
        $job_id = isset($_POST['job_id']) ? sanitize_text_field((string) wp_unslash($_POST['job_id'])) : '';
        $job = $job_id !== '' ? $this->aysa_technical_schema_read_job($job_id) : $this->aysa_technical_schema_get_current_refresh_job($project_id);

        if (!$job || intval($job['project_id'] ?? 0) !== $project_id) {
            wp_send_json_error('Schema refresh job not found.');
            return;
        }

        if ((string) ($job['status'] ?? '') === 'queued') {
            $job = $this->aysa_technical_schema_process_job((string) ($job['job_id'] ?? ''), 4);
        } elseif ((string) ($job['status'] ?? '') === 'running' && intval($job['lock_until'] ?? 0) <= time()) {
            $job = $this->aysa_technical_schema_process_job((string) ($job['job_id'] ?? ''), 4);
        }

        wp_send_json_success(array(
            'job' => $this->aysa_technical_schema_get_job_response((array) $job),
        ));
    }

    private function aysa_internal_linking_workspace_service() {
        static $service = null;
        if (!$service && class_exists('Aysa_Internal_Linking_Workspace')) {
            $service = new Aysa_Internal_Linking_Workspace();
        }
        return $service;
    }

    private function aysa_onpage_workspace_service() {
        static $service = null;
        if (!$service && class_exists('Aysa_Onpage_Workspace')) {
            $service = new Aysa_Onpage_Workspace();
        }
        return $service;
    }

    private function aysa_internal_linking_workspace_filters_from_request() {
        $filters = isset($_POST['filters']) ? wp_unslash($_POST['filters']) : array();
        if (is_string($filters)) {
            $decoded = json_decode($filters, true);
            $filters = is_array($decoded) ? $decoded : array_filter(array_map('sanitize_key', explode(',', $filters)));
        }
        return is_array($filters) ? $filters : array();
    }

    private function aysa_onpage_workspace_filters_from_request() {
        $filters = isset($_POST['filters']) ? wp_unslash($_POST['filters']) : array();
        if (is_string($filters)) {
            $decoded = json_decode($filters, true);
            $filters = is_array($decoded) ? $decoded : array('view' => sanitize_key($filters));
        }
        return is_array($filters) ? $filters : array();
    }

    private function aysa_onpage_workspace_paging_from_request() {
        $page = isset($_POST['page']) ? intval(wp_unslash((string) $_POST['page'])) : 1;
        $per_page = isset($_POST['per_page']) ? intval(wp_unslash((string) $_POST['per_page'])) : 50;

        return array(
            'page' => max(1, $page),
            'per_page' => max(1, min(100, $per_page)),
        );
    }

    public function handle_onpage_workspace() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $service = $this->aysa_onpage_workspace_service();
        if (!$service) {
            wp_send_json_error('On-page workspace service unavailable.');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        $entity_group = isset($_POST['entity_group']) ? sanitize_key(wp_unslash((string) $_POST['entity_group'])) : 'page';
        $filters = $this->aysa_onpage_workspace_filters_from_request();
        $paging = $this->aysa_onpage_workspace_paging_from_request();

        wp_send_json_success(array(
            'workspace' => $service->build_workspace($project_id, array(
                'entity_group' => $entity_group,
                'filters' => $filters,
                'page' => $paging['page'],
                'per_page' => $paging['per_page'],
                'refresh' => !empty($_POST['refresh']),
            )),
        ));
    }

    public function handle_onpage_detail() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $service = $this->aysa_onpage_workspace_service();
        if (!$service) {
            wp_send_json_error('On-page workspace service unavailable.');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        $entity_key = isset($_POST['entity_key']) ? sanitize_text_field(wp_unslash((string) $_POST['entity_key'])) : '';

        wp_send_json_success(array(
            'detail' => $service->get_detail($project_id, $entity_key),
        ));
    }

    public function handle_onpage_start_run() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error(array('message' => 'Invalid nonce'));
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(array('message' => 'Access denied'));
            return;
        }
        if (!$this->check_authentication(false)) {
            wp_send_json_error(array('message' => 'Authentication required.'));
            return;
        }

        $service = $this->aysa_onpage_workspace_service();
        if (!$service) {
            wp_send_json_error(array('message' => 'On-page workspace service unavailable.'));
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        if ($project_id <= 0) {
            wp_send_json_error(array('message' => 'Missing project ID.'));
            return;
        }

        $run_type = sanitize_key((string) ($_POST['run_type'] ?? 'analyze'));
        $aliases = array(
            'analyze' => 'mapping_sync',
            'reconcile' => 'output_reconcile',
            'generate_meta_only' => 'meta_generate',
            'generate_content_rewrite' => 'rewrite_generate',
        );
        if (isset($aliases[$run_type])) {
            $run_type = $aliases[$run_type];
        }
        $allowed = array('mapping_sync', 'mapping_autofill', 'meta_generate', 'rewrite_decide', 'rewrite_generate', 'output_reconcile', 'keyword_validate_for_page');
        if (!in_array($run_type, $allowed, true)) {
            wp_send_json_error(array('message' => 'Unsupported on-page run type.'));
            return;
        }

        $entity_group = isset($_POST['entity_group']) ? sanitize_key(wp_unslash((string) $_POST['entity_group'])) : 'page';
        $filters = $this->aysa_onpage_workspace_filters_from_request();
        $paging = $this->aysa_onpage_workspace_paging_from_request();
        $selection = isset($_POST['selection']) ? wp_unslash($_POST['selection']) : array();
        if (!is_array($selection)) {
            $selection = array_filter(array_map('sanitize_text_field', explode(',', (string) $selection)));
        }

        $ad_hoc_page = array();
        if (isset($_POST['ad_hoc_page'])) {
            $raw_ad_hoc_page = wp_unslash($_POST['ad_hoc_page']);
            if (is_string($raw_ad_hoc_page) && $raw_ad_hoc_page !== '') {
                $decoded = json_decode($raw_ad_hoc_page, true);
                if (is_array($decoded)) {
                    $raw_ad_hoc_page = $decoded;
                }
            }
            if (is_array($raw_ad_hoc_page)) {
                $ad_hoc_page = array(
                    'title' => isset($raw_ad_hoc_page['title']) ? sanitize_text_field((string) $raw_ad_hoc_page['title']) : '',
                    'meta_description' => isset($raw_ad_hoc_page['meta_description']) ? sanitize_textarea_field((string) $raw_ad_hoc_page['meta_description']) : '',
                    'page_text' => isset($raw_ad_hoc_page['page_text']) ? sanitize_textarea_field((string) $raw_ad_hoc_page['page_text']) : '',
                    'url' => isset($raw_ad_hoc_page['url']) ? esc_url_raw((string) $raw_ad_hoc_page['url']) : '',
                );
                $ad_hoc_page = array_filter($ad_hoc_page, static function ($value) {
                    return $value !== '';
                });
            }
        }

        $language = isset($_POST['language']) ? sanitize_text_field(wp_unslash((string) $_POST['language'])) : '';
        $country_code = isset($_POST['country_code']) ? sanitize_key(wp_unslash((string) $_POST['country_code'])) : '';
        $location_code = isset($_POST['location_code']) ? intval(wp_unslash((string) $_POST['location_code'])) : 0;

        $payload = array(
            'project_id' => $project_id,
            'run_type' => $run_type,
            'website' => home_url('/'),
            'seed_projection' => $service->seed_projection($project_id),
            'state_summary' => $service->build_workspace($project_id, array(
                'entity_group' => $entity_group,
                'filters' => $filters,
                'page' => $paging['page'],
                'per_page' => $paging['per_page'],
            ))['summary'] ?? array(),
            'selection_meta' => $service->build_run_selection_meta(
                $project_id,
                $entity_group !== '' ? $entity_group : 'all',
                $filters,
                array_values(array_map('sanitize_text_field', $selection))
            ),
            'selection' => array_values(array_map('sanitize_text_field', $selection)),
            'run_source' => 'plugin_onpage_workspace',
        );

        if ($ad_hoc_page !== array()) {
            $payload['ad_hoc_page'] = $ad_hoc_page;
        }
        if ($language !== '') {
            $payload['language'] = $language;
        }
        if ($country_code !== '') {
            $payload['country_code'] = $country_code;
        }
        if ($location_code > 0) {
            $payload['location_code'] = $location_code;
        }
        if (!empty($_POST['fast_lane'])) {
            $payload['fast_lane'] = true;
        }

        if (empty($payload['selection']) && !empty($payload['selection_meta']) && is_array($payload['selection_meta'])) {
            $payload['selection'] = array_values(array_filter(array_map(static function ($row): string {
                return is_array($row) ? sanitize_text_field((string) ($row['entity_key'] ?? '')) : '';
            }, $payload['selection_meta'])));
        }

        if ($payload['selection'] === array() && empty($payload['ad_hoc_page']) && $run_type === 'keyword_validate_for_page') {
            wp_send_json_error(array('message' => 'Keyword validation requires either an entity selection or ad hoc page content.'));
            return;
        }

        $result = $this->aysa_broker_request('POST', '/v1/onpage-runs/start', $payload, 45);
        if (is_wp_error($result)) {
            wp_send_json_error(array(
                'message' => 'On-page worker unavailable right now. Local fallback is disabled for this flow.',
                'broker_error' => $result->get_error_message(),
                'workspace' => $service->build_workspace($project_id, array(
                    'entity_group' => $entity_group,
                    'filters' => $filters,
                    'page' => $paging['page'],
                    'per_page' => $paging['per_page'],
                )),
            ));
            return;
        }

        if (!empty($result['run']['id'])) {
            $service->sync_remote_run_status(
                $project_id,
                (array) ($result['run'] ?? array()),
                isset($result['tasks']) && is_array($result['tasks']) ? $result['tasks'] : array()
            );
        }
        if (!empty($result['projection']) && !empty($result['run']['id'])) {
            $result['local_projection_sync'] = $service->apply_projection(
                $project_id,
                intval($result['run']['id']),
                (array) $result['projection']
            );
        }

        if (empty($result['deduped'])) {
            $billing_map = array(
                'mapping_sync' => array('action_key' => 'onpage_optimization_run', 'fallback_cost_cents' => 8),
                'mapping_autofill' => array('action_key' => 'onpage_mapping_autofill', 'fallback_cost_cents' => 12),
                'meta_generate' => array('action_key' => 'onpage_meta_generate', 'fallback_cost_cents' => 18),
                'rewrite_decide' => array('action_key' => 'onpage_rewrite_decide', 'fallback_cost_cents' => 10),
                'rewrite_generate' => array('action_key' => 'onpage_rewrite_generate', 'fallback_cost_cents' => 35),
                'output_reconcile' => array('action_key' => 'onpage_output_reconcile', 'fallback_cost_cents' => 6),
                'keyword_validate_for_page' => array('action_key' => 'onpage_keyword_validate_page', 'fallback_cost_cents' => 14),
            );
            $billing = $billing_map[$run_type] ?? $billing_map['mapping_sync'];
            $run_reference = !empty($result['run']['id'])
                ? 'onpage_run:' . intval($result['run']['id'])
                : 'onpage_run:' . $project_id . ':' . gmdate('YmdHis');
            $this->aysa_wallet_track_action_usage(
                $project_id,
                (string) $billing['action_key'],
                $run_reference,
                array(
                    'module' => 'on_page_optimization',
                    'run_type' => $run_type,
                    'entity_group' => $entity_group,
                    'selection_count' => count((array) $payload['selection']),
                    'has_ad_hoc_page' => !empty($payload['ad_hoc_page']),
                ),
                intval($billing['fallback_cost_cents']),
                '/v1/onpage-runs/start'
            );
        }

        wp_send_json_success(array(
            'status' => $result,
            'workspace' => $service->build_workspace($project_id, array(
                'entity_group' => $entity_group,
                'filters' => $filters,
                'page' => $paging['page'],
                'per_page' => $paging['per_page'],
            )),
        ));
    }

    public function handle_onpage_run_status() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error(array('message' => 'Invalid nonce'));
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(array('message' => 'Access denied'));
            return;
        }
        if (!$this->check_authentication(false)) {
            wp_send_json_error(array('message' => 'Authentication required.'));
            return;
        }

        $service = $this->aysa_onpage_workspace_service();
        if (!$service) {
            wp_send_json_error(array('message' => 'On-page workspace service unavailable.'));
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        $run_id = isset($_POST['run_id']) ? intval(wp_unslash((string) $_POST['run_id'])) : 0;
        $after_event_id = isset($_POST['after_event_id']) ? intval(wp_unslash((string) $_POST['after_event_id'])) : 0;
        $entity_group = isset($_POST['entity_group']) ? sanitize_key(wp_unslash((string) $_POST['entity_group'])) : 'page';
        $filters = $this->aysa_onpage_workspace_filters_from_request();
        $paging = $this->aysa_onpage_workspace_paging_from_request();

        $result = $this->aysa_broker_request('POST', '/v1/onpage-runs/status', array(
            'project_id' => $project_id,
            'run_id' => $run_id,
            'after_event_id' => $after_event_id,
        ), 30);
        if (is_wp_error($result)) {
            wp_send_json_error(array(
                'message' => 'On-page worker status is unavailable right now. Local fallback is disabled for this flow.',
                'broker_error' => $result->get_error_message(),
                'workspace' => $service->build_workspace($project_id, array(
                    'entity_group' => $entity_group,
                    'filters' => $filters,
                    'page' => $paging['page'],
                    'per_page' => $paging['per_page'],
                )),
            ));
            return;
        }

        if (!empty($result['run']['id'])) {
            $service->sync_remote_run_status(
                $project_id,
                (array) ($result['run'] ?? array()),
                isset($result['tasks']) && is_array($result['tasks']) ? $result['tasks'] : array()
            );
        }
        if (!empty($result['projection']) && !empty($result['run']['id'])) {
            $result['local_projection_sync'] = $service->apply_projection(
                $project_id,
                intval($result['run']['id']),
                (array) $result['projection']
            );
        }

        wp_send_json_success(array(
            'status' => $result,
            'workspace' => $service->build_workspace($project_id, array(
                'entity_group' => $entity_group,
                'filters' => $filters,
                'page' => $paging['page'],
                'per_page' => $paging['per_page'],
            )),
        ));
    }

    public function handle_onpage_entity_action_v2() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error(array('message' => 'Invalid nonce'));
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(array('message' => 'Access denied'));
            return;
        }

        $service = $this->aysa_onpage_workspace_service();
        if (!$service) {
            wp_send_json_error(array('message' => 'On-page workspace service unavailable.'));
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        $entity_key = isset($_POST['entity_key']) ? sanitize_text_field(wp_unslash((string) $_POST['entity_key'])) : '';
        $row_action = sanitize_key((string) ($_POST['row_action'] ?? 'refresh'));
        $entity_group = isset($_POST['entity_group']) ? sanitize_key(wp_unslash((string) $_POST['entity_group'])) : 'page';
        $filters = $this->aysa_onpage_workspace_filters_from_request();
        $paging = $this->aysa_onpage_workspace_paging_from_request();

        if ($row_action === 'refresh') {
            $result = $service->refresh_entity($project_id, $entity_key);
            if (empty($result['ok'])) {
                wp_send_json_error($result);
                return;
            }
            wp_send_json_success(array(
                'result' => $result,
                'detail' => $service->get_detail($project_id, $entity_key),
                'workspace' => $service->build_workspace($project_id, array(
                    'entity_group' => $entity_group,
                    'filters' => $filters,
                    'page' => $paging['page'],
                    'per_page' => $paging['per_page'],
                )),
            ));
            return;
        }

        if ($row_action === 'accept_mapping') {
            $result = $service->accept_mapping_review($project_id, $entity_key);
            if (empty($result['ok'])) {
                wp_send_json_error($result);
                return;
            }
            wp_send_json_success(array(
                'result' => $result,
                'detail' => $service->get_detail($project_id, $entity_key),
                'workspace' => $service->build_workspace($project_id, array(
                    'entity_group' => $entity_group,
                    'filters' => $filters,
                    'page' => $paging['page'],
                    'per_page' => $paging['per_page'],
                )),
            ));
            return;
        }

        if ($row_action === 'reject_mapping') {
            $result = $service->reject_mapping_review($project_id, $entity_key);
            if (empty($result['ok'])) {
                wp_send_json_error($result);
                return;
            }
            wp_send_json_success(array(
                'result' => $result,
                'detail' => $service->get_detail($project_id, $entity_key),
                'workspace' => $service->build_workspace($project_id, array(
                    'entity_group' => $entity_group,
                    'filters' => $filters,
                    'page' => $paging['page'],
                    'per_page' => $paging['per_page'],
                )),
            ));
            return;
        }

        if ($row_action === 'apply_meta') {
            $result = $service->apply_meta_output($project_id, $entity_key);
            if (empty($result['ok'])) {
                wp_send_json_error($result);
                return;
            }
            wp_send_json_success(array(
                'result' => $result,
                'detail' => $service->get_detail($project_id, $entity_key),
                'workspace' => $service->build_workspace($project_id, array(
                    'entity_group' => $entity_group,
                    'filters' => $filters,
                    'page' => $paging['page'],
                    'per_page' => $paging['per_page'],
                )),
            ));
            return;
        }

        if ($row_action === 'apply_rewrite') {
            $result = $service->apply_rewrite_output($project_id, $entity_key);
            if (empty($result['ok'])) {
                wp_send_json_error($result);
                return;
            }
            wp_send_json_success(array(
                'result' => $result,
                'detail' => $service->get_detail($project_id, $entity_key),
                'workspace' => $service->build_workspace($project_id, array(
                    'entity_group' => $entity_group,
                    'filters' => $filters,
                    'page' => $paging['page'],
                    'per_page' => $paging['per_page'],
                )),
            ));
            return;
        }

        if ($row_action === 'skip') {
            $result = $service->skip_entity($project_id, $entity_key);
            if (empty($result['ok'])) {
                wp_send_json_error($result);
                return;
            }
            wp_send_json_success(array(
                'result' => $result,
                'detail' => $service->get_detail($project_id, $entity_key),
                'workspace' => $service->build_workspace($project_id, array(
                    'entity_group' => $entity_group,
                    'filters' => $filters,
                    'page' => $paging['page'],
                    'per_page' => $paging['per_page'],
                )),
            ));
            return;
        }

        $mapped_action = match ($row_action) {
            'generate_meta' => 'meta_generate',
            'generate_meta_only' => 'meta_generate',
            'validate_keywords' => 'keyword_validate_for_page',
            'rewrite_decide' => 'rewrite_decide',
            'generate_rewrite' => 'rewrite_generate',
            'generate_content_rewrite' => 'rewrite_generate',
            default => '',
        };
        if ($mapped_action === '') {
            wp_send_json_error(array('message' => 'Unsupported entity action.'));
            return;
        }

        $_POST['run_type'] = $mapped_action;
        $_POST['selection'] = array($entity_key);
        $_POST['fast_lane'] = '1';
        $this->handle_onpage_start_run();
    }

    public function handle_onpage_batch_action_v2() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error(array('message' => 'Invalid nonce'));
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(array('message' => 'Access denied'));
            return;
        }

        $service = $this->aysa_onpage_workspace_service();
        if (!$service) {
            wp_send_json_error(array('message' => 'On-page workspace service unavailable.'));
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        $entity_group = isset($_POST['entity_group']) ? sanitize_key(wp_unslash((string) $_POST['entity_group'])) : 'page';
        $filters = $this->aysa_onpage_workspace_filters_from_request();
        $paging = $this->aysa_onpage_workspace_paging_from_request();
        $batch_action = sanitize_key((string) ($_POST['batch_action'] ?? ''));
        $selection = isset($_POST['selection']) ? wp_unslash($_POST['selection']) : array();
        if (!is_array($selection)) {
            $selection = array_filter(array_map('sanitize_text_field', explode(',', (string) $selection)));
        } else {
            $selection = array_values(array_filter(array_map('sanitize_text_field', $selection)));
        }

        $result = match ($batch_action) {
            'apply_meta' => $service->batch_apply_operation($project_id, $entity_group, $filters, $selection, 'apply_meta'),
            'apply_rewrite' => $service->batch_apply_operation($project_id, $entity_group, $filters, $selection, 'apply_rewrite'),
            'skip' => $service->batch_apply_operation($project_id, $entity_group, $filters, $selection, 'skip'),
            default => array('ok' => false, 'error' => 'unsupported_batch_action'),
        };

        if (empty($result['ok'])) {
            wp_send_json_error($result);
            return;
        }

        wp_send_json_success(array(
            'result' => $result,
            'workspace' => $service->build_workspace($project_id, array(
                'entity_group' => $entity_group,
                'filters' => $filters,
                'page' => $paging['page'],
                'per_page' => $paging['per_page'],
            )),
        ));
    }

    private function aysa_onpage_build_local_run_fallback($service, $project_id, $run_type, $entity_group, $filters = array()) {
        $workspace = $service->build_workspace($project_id, array(
            'entity_group' => $entity_group,
            'filters' => is_array($filters) ? $filters : array(),
            'page' => 1,
            'per_page' => 50,
            'refresh' => true,
        ));

        return array(
            'status' => array(
                'ok' => true,
                'run' => array(
                    'id' => 0,
                    'status' => 'completed',
                    'run_type' => $run_type,
                    'current_milestone' => 'Local workspace refresh completed',
                ),
                'events' => array(
                    array(
                        'id' => 0,
                        'event_type' => 'local_fallback',
                        'event_level' => 'warning',
                        'message_text' => 'Broker unavailable. Local workspace refresh was used instead.',
                    ),
                ),
                'tasks' => array(),
                'projection' => null,
            ),
            'workspace' => $workspace,
        );
    }

    public function handle_internal_linking_workspace() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $service = $this->aysa_internal_linking_workspace_service();
        if (!$service) {
            wp_send_json_error('Internal linking workspace service unavailable.');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        $filters = $this->aysa_internal_linking_workspace_filters_from_request();

        wp_send_json_success(array(
            'workspace' => $service->build_workspace($project_id, array(
                'filters' => $filters,
                'page' => isset($_POST['page']) ? intval($_POST['page']) : 1,
                'per_page' => isset($_POST['per_page']) ? intval($_POST['per_page']) : 50,
                'refresh' => !empty($_POST['refresh']),
            )),
        ));
    }

    public function handle_internal_linking_detail() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $service = $this->aysa_internal_linking_workspace_service();
        if (!$service) {
            wp_send_json_error('Internal linking workspace service unavailable.');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        $opportunity_id = isset($_POST['opportunity_id']) ? intval(wp_unslash((string) $_POST['opportunity_id'])) : 0;
        $entity_key = isset($_POST['entity_key']) ? sanitize_text_field(wp_unslash((string) $_POST['entity_key'])) : '';

        wp_send_json_success(array(
            'detail' => $service->get_detail($project_id, $opportunity_id, $entity_key),
        ));
    }

    public function handle_internal_linking_start_run() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error(array('message' => 'Invalid nonce'));
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(array('message' => 'Access denied'));
            return;
        }
        if (!$this->check_authentication(false)) {
            wp_send_json_error(array('message' => 'Authentication required.'));
            return;
        }

        $service = $this->aysa_internal_linking_workspace_service();
        if (!$service) {
            wp_send_json_error(array('message' => 'Internal linking workspace service unavailable.'));
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        if ($project_id <= 0) {
            wp_send_json_error(array('message' => 'Missing project ID.'));
            return;
        }
        $keyword_mapping_sync = $this->aysa_keyword_mapping_ensure_local_associations($project_id);

        $run_type = sanitize_key((string) ($_POST['run_type'] ?? 'analyze'));
        $filters = $this->aysa_internal_linking_workspace_filters_from_request();
        $allowed = array('analyze', 'apply_safe', 'reconcile', 'remove_aysa_links', 'rewrite_selected', 'target_revalidate');
        if (!in_array($run_type, $allowed, true)) {
            wp_send_json_error(array('message' => 'Unsupported internal linking run type.'));
            return;
        }

        $legacy_map = array(
            'analyze' => 'analyze',
            'apply_safe' => 'apply_safe',
            'reconcile' => 'analyze',
        );
        $legacy_result = null;
        if (isset($legacy_map[$run_type])) {
            $legacy_result = $service->run_legacy_action($project_id, $legacy_map[$run_type]);
            if (empty($legacy_result['ok'])) {
                wp_send_json_error(array(
                    'message' => (string) ($legacy_result['error'] ?? 'legacy_internal_linking_failed'),
                    'keyword_mapping_sync' => $keyword_mapping_sync,
                ));
                return;
            }
        }

        $payload = array(
            'project_id' => $project_id,
            'run_type' => $run_type,
            'website' => home_url('/'),
            'seed_projection' => is_array($legacy_result['projection'] ?? null) ? $legacy_result['projection'] : $service->seed_projection($project_id),
            'selection' => isset($_POST['selection']) ? wp_unslash($_POST['selection']) : array(),
            'run_source' => 'plugin_internal_linking_workspace',
        );

        $result = $this->aysa_broker_request('POST', '/v1/internal-link-runs/start', $payload, 45);
        if (is_wp_error($result)) {
            wp_send_json_error(array(
                'message' => $result->get_error_message(),
                'local_result' => $legacy_result,
            ));
            return;
        }

        if (!empty($result['projection']) && !empty($result['run']['id'])) {
            $result['local_projection_sync'] = $service->apply_projection(
                $project_id,
                intval($result['run']['id']),
                (array) $result['projection']
            );
        }

        wp_send_json_success(array(
            'status' => $result,
            'keyword_mapping_sync' => $keyword_mapping_sync,
            'workspace' => $service->build_workspace($project_id, array('page' => 1, 'per_page' => 50, 'filters' => $filters)),
        ));
    }

    public function handle_internal_linking_run_status() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error(array('message' => 'Invalid nonce'));
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(array('message' => 'Access denied'));
            return;
        }
        if (!$this->check_authentication(false)) {
            wp_send_json_error(array('message' => 'Authentication required.'));
            return;
        }

        $service = $this->aysa_internal_linking_workspace_service();
        if (!$service) {
            wp_send_json_error(array('message' => 'Internal linking workspace service unavailable.'));
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        $run_id = isset($_POST['run_id']) ? intval(wp_unslash((string) $_POST['run_id'])) : 0;
        $after_event_id = isset($_POST['after_event_id']) ? intval(wp_unslash((string) $_POST['after_event_id'])) : 0;
        $filters = $this->aysa_internal_linking_workspace_filters_from_request();

        $result = $this->aysa_broker_request('POST', '/v1/internal-link-runs/status', array(
            'project_id' => $project_id,
            'run_id' => $run_id,
            'after_event_id' => $after_event_id,
        ), 30);
        if (is_wp_error($result)) {
            wp_send_json_error(array('message' => $result->get_error_message()));
            return;
        }

        if (!empty($result['projection']) && !empty($result['run']['id'])) {
            $result['local_projection_sync'] = $service->apply_projection(
                $project_id,
                intval($result['run']['id']),
                (array) $result['projection']
            );
        }

        wp_send_json_success(array(
            'status' => $result,
            'workspace' => $service->build_workspace($project_id, array('page' => 1, 'per_page' => 50, 'filters' => $filters)),
        ));
    }

    public function handle_internal_linking_row_action_v2() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $service = $this->aysa_internal_linking_workspace_service();
        if (!$service) {
            wp_send_json_error('Internal linking workspace service unavailable.');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        $filters = $this->aysa_internal_linking_workspace_filters_from_request();
        $opportunity_id = isset($_POST['opportunity_id']) ? intval(wp_unslash((string) $_POST['opportunity_id'])) : 0;
        $row_action = sanitize_key((string) ($_POST['row_action'] ?? 'validate'));
        $result = $service->update_row_action($project_id, $opportunity_id, $row_action);
        if (empty($result['ok'])) {
            wp_send_json_error(is_array($result) ? $result : array('message' => 'Unable to update row.'));
            return;
        }

        wp_send_json_success(array(
            'result' => $result,
            'workspace' => $service->build_workspace($project_id, array('page' => 1, 'per_page' => 50, 'filters' => $filters)),
        ));
    }

    private function aysa_adjustments_get_technical_internal_linking_snapshot($project_id) {
        $service = $this->aysa_internal_linking_workspace_service();
        if ($service) {
            $workspace = $service->build_workspace(intval($project_id), array('page' => 1, 'per_page' => 100));
            $summary = is_array($workspace['summary'] ?? null) ? $workspace['summary'] : array();
            $rows = is_array($workspace['rows'] ?? null) ? $workspace['rows'] : array();
            $run = is_array($workspace['run'] ?? null) ? $workspace['run'] : array();
            $preview_package = method_exists($service, 'build_preview_package') ? $service->build_preview_package(intval($project_id)) : array();

            $overview_rows = array(
                array('metric' => 'Inventory', 'value' => intval($summary['all'] ?? 0)),
                array('metric' => 'Ready to apply', 'value' => intval($summary['ready_to_apply'] ?? 0)),
                array('metric' => 'Applied by AYSA', 'value' => intval($summary['applied_by_aysa'] ?? 0)),
                array('metric' => 'Needs review', 'value' => intval($summary['needs_review'] ?? 0)),
                array('metric' => 'Conflict', 'value' => intval($summary['conflict'] ?? 0)),
                array('metric' => 'Manual only', 'value' => intval($summary['manual_only'] ?? 0)),
            );

            return array(
                'id' => 'technical_internal_linking',
                'status' => !empty($rows) ? 'ready' : 'idle',
                'enabled' => true,
                'matching_type' => 'workspace_v2',
                'count' => intval($summary['all'] ?? count($rows)),
                'total_links' => intval($summary['applied_by_aysa'] ?? 0),
                'content_items_affected' => intval($summary['all'] ?? 0),
                'rows' => $rows,
                'preview_rows' => array(),
                'preview_keywords_found' => intval($summary['ready_to_apply'] ?? 0),
                'preview_links_to_add' => intval($summary['ready_to_apply'] ?? 0),
                'preview_content_items_scanned' => intval($summary['all'] ?? 0),
                'preview_summary' => sanitize_text_field((string) ($run['current_milestone'] ?? '')),
                'preview_package' => $preview_package,
                'preview_rows' => isset($preview_package['items']) && is_array($preview_package['items']) ? $preview_package['items'] : array(),
                'has_preview' => intval($summary['ready_to_apply'] ?? 0) > 0,
                'overview_rows' => $overview_rows,
                'has_data' => !empty($rows),
                'workspace' => $workspace,
            );
        }

        $default_settings = array(
            'enabled' => false,
            'matching_type' => 'both',
            'process_posts' => true,
            'process_pages' => true,
            'process_categories' => true,
            'process_products' => true,
            'case_sensitive' => false,
            'ignore_existing_links' => true,
            'priority_by_authority' => true,
        );
        $settings = wp_parse_args(get_option('aysa_internal_linking_settings', array()), $default_settings);
        $log = get_option('aysa_internal_linking_log', array());
        $preview_state = get_option($this->aysa_technical_internal_linking_preview_state_key($project_id), array());
        $preview_data = is_array($preview_state) && isset($preview_state['data']) && is_array($preview_state['data']) ? $preview_state['data'] : array();

        $grouped = array();
        $affected_sources = array();
        foreach (is_array($log) ? $log : array() as $entry) {
            if (!is_array($entry)) {
                continue;
            }
            $keyword = sanitize_text_field((string) ($entry['keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }
            if (!isset($grouped[$keyword])) {
                $grouped[$keyword] = array(
                    'keyword' => $keyword,
                    'target_title' => sanitize_text_field((string) ($entry['target_title'] ?? '')),
                    'target_url' => esc_url_raw((string) ($entry['target_url'] ?? '')),
                    'total_links' => 0,
                    'sources_count' => 0,
                    'last_placement' => '',
                    '_sources' => array(),
                );
            }

            $grouped[$keyword]['total_links']++;
            $source_key = sanitize_text_field((string) ($entry['source_title'] ?? '')) . '|' . esc_url_raw((string) ($entry['source_url'] ?? ''));
            if ($source_key !== '|' && !isset($grouped[$keyword]['_sources'][$source_key])) {
                $grouped[$keyword]['_sources'][$source_key] = true;
            }
            $timestamp = sanitize_text_field((string) ($entry['timestamp'] ?? ''));
            if ($timestamp !== '' && ($grouped[$keyword]['last_placement'] === '' || strcmp($timestamp, $grouped[$keyword]['last_placement']) > 0)) {
                $grouped[$keyword]['last_placement'] = $timestamp;
            }
            if ($source_key !== '|') {
                $affected_sources[$source_key] = true;
            }
        }

        $rows = array();
        foreach ($grouped as $row) {
            $row['sources_count'] = count($row['_sources']);
            unset($row['_sources']);
            $rows[] = $row;
        }
        usort($rows, static function($left, $right) {
            return intval($right['total_links'] ?? 0) <=> intval($left['total_links'] ?? 0);
        });

        $preview_rows = array();
        $preview_changes = isset($preview_data['preview_changes']) && is_array($preview_data['preview_changes']) ? $preview_data['preview_changes'] : array();
        foreach ($preview_changes as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keywords = isset($row['changes']) && is_array($row['changes']) ? array_values(array_filter(array_map(function($change) {
                return is_array($change) ? sanitize_text_field((string) ($change['keyword'] ?? '')) : '';
            }, $row['changes']))) : array();
            $targets = isset($row['changes']) && is_array($row['changes']) ? array_values(array_filter(array_map(function($change) {
                return is_array($change) ? sanitize_text_field((string) ($change['target_title'] ?? '')) : '';
            }, $row['changes']))) : array();

            $preview_rows[] = array(
                'content_title' => sanitize_text_field((string) ($row['content_title'] ?? '')),
                'content_type' => sanitize_text_field((string) ($row['content_type'] ?? '')),
                'links_count' => intval($row['links_count'] ?? 0),
                'keywords' => implode(', ', array_slice($keywords, 0, 5)),
                'targets' => implode(', ', array_slice(array_unique($targets), 0, 3)),
            );
        }

        $total_links = array_sum(array_map(static fn($row) => intval($row['total_links'] ?? 0), $rows));

        return array(
            'id' => 'technical_internal_linking',
            'status' => (!empty($rows) || !empty($preview_rows)) ? 'ready' : 'idle',
            'enabled' => !empty($settings['enabled']),
            'matching_type' => sanitize_text_field((string) ($settings['matching_type'] ?? 'both')),
            'count' => count($rows),
            'total_links' => $total_links,
            'content_items_affected' => count($affected_sources),
            'rows' => $rows,
            'preview_rows' => $preview_rows,
            'preview_keywords_found' => intval($preview_data['total_keywords_found'] ?? 0),
            'preview_links_to_add' => intval($preview_data['links_to_be_added'] ?? 0),
            'preview_content_items_scanned' => intval($preview_data['content_items_scanned'] ?? 0),
            'preview_summary' => sanitize_text_field((string) ($preview_data['summary'] ?? '')),
            'has_preview' => !empty($preview_rows),
            'overview_rows' => array(
                array('metric' => 'Enabled', 'value' => !empty($settings['enabled']) ? 'Yes' : 'No'),
                array('metric' => 'Matching type', 'value' => sanitize_text_field((string) ($settings['matching_type'] ?? 'both'))),
                array('metric' => 'Keywords linked', 'value' => count($rows)),
                array('metric' => 'Links added', 'value' => $total_links),
                array('metric' => 'Affected content items', 'value' => count($affected_sources)),
                array('metric' => 'Preview links to add', 'value' => intval($preview_data['links_to_be_added'] ?? 0)),
            ),
            'has_data' => !empty($rows) || !empty($preview_rows),
        );
    }

    private function aysa_adjustments_get_technical_sitemap_robots_snapshot($project_id) {
        $project_id = intval($project_id);
        $state = get_option($this->aysa_technical_sitemap_robots_state_key($project_id), array());
        if (!is_array($state)) {
            $state = array();
        }
        $state_status = sanitize_key((string) ($state['status'] ?? 'idle'));

        $data = !empty($state['data']) && is_array($state['data']) ? $state['data'] : array();
        $summary = !empty($data['summary']) && is_array($data['summary']) ? $data['summary'] : array();
        $checks = !empty($data['checks']) && is_array($data['checks']) ? array_values($data['checks']) : array();
        $sitemaps = !empty($data['sitemaps']) && is_array($data['sitemaps']) ? array_values($data['sitemaps']) : array();
        $robots = !empty($data['robots']) && is_array($data['robots']) ? $data['robots'] : array();
        $generation = !empty($data['generation']) && is_array($data['generation']) ? $data['generation'] : array();

        $overview_rows = array(
            array('metric' => 'Errors', 'value' => (string) intval($summary['errors'] ?? 0)),
            array('metric' => 'Warnings', 'value' => (string) intval($summary['warnings'] ?? 0)),
            array('metric' => 'Passes', 'value' => (string) intval($summary['passes'] ?? 0)),
            array('metric' => 'Robots health', 'value' => sanitize_text_field((string) ($summary['robots_health'] ?? ''))),
            array('metric' => 'Sitemap health', 'value' => sanitize_text_field((string) ($summary['sitemap_health'] ?? ''))),
            array('metric' => 'Primary sitemap', 'value' => esc_url_raw((string) ($data['primary_sitemap'] ?? ''))),
        );

        $robots_rows = array(
            array('field' => 'Source', 'value' => sanitize_text_field((string) ($robots['source_label'] ?? ''))),
            array('field' => 'Target URL', 'value' => esc_url_raw((string) ($data['target_url'] ?? ''))),
            array('field' => 'Robots content', 'value' => sanitize_textarea_field((string) ($robots['content'] ?? ''))),
        );

        $has_data = !empty($checks) || !empty($sitemaps) || !empty($robots['content']);
        $snapshot_status = $has_data ? 'ready' : (in_array($state_status, array('queued', 'running', 'completed', 'failed', 'blocked'), true) ? $state_status : 'idle');

        return array(
            'id' => 'technical_sitemap_robots',
            'status' => $snapshot_status,
            'updated_at' => sanitize_text_field((string) ($state['updated_at'] ?? '')),
            'target_url' => esc_url_raw((string) ($data['target_url'] ?? '')),
            'primary_sitemap' => esc_url_raw((string) ($data['primary_sitemap'] ?? '')),
            'errors' => intval($summary['errors'] ?? 0),
            'warnings' => intval($summary['warnings'] ?? 0),
            'passes' => intval($summary['passes'] ?? 0),
            'robots_health' => sanitize_text_field((string) ($summary['robots_health'] ?? '')),
            'sitemap_health' => sanitize_text_field((string) ($summary['sitemap_health'] ?? '')),
            'overview_rows' => array_values($overview_rows),
            'rows' => array_values($checks),
            'sitemaps' => array_values($sitemaps),
            'robots_rows' => array_values($robots_rows),
            'generation' => $generation,
            'count' => count($checks),
            'has_data' => $has_data,
            'run_id' => intval($state['run_id'] ?? 0),
        );
    }

    private function aysa_adjustments_fetch_live_gsc_snapshot($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array('rows' => array());
        }

        $site_host = wp_parse_url(home_url(), PHP_URL_HOST);
        $site_host = is_string($site_host) ? $site_host : '';

        $result = $this->aysa_broker_request('POST', '/v1/google/keyword-mappings', array(
            'project_id' => $project_id,
            'site_host' => $site_host,
            'days' => 90,
            'limit' => 1000,
        ), 45);
        if (is_wp_error($result) || !is_array($result) || empty($result['connected'])) {
            return array('rows' => array());
        }

        $query_rows = isset($result['gsc_queries']) && is_array($result['gsc_queries']) ? $result['gsc_queries'] : array();
        if ($query_rows === array()) {
            return array('rows' => array());
        }

        $query_map = array();
        foreach ($query_rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['query'] ?? $row['keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }
            $norm = strtolower($keyword);
            $clicks = intval($row['clicks'] ?? 0);
            $impressions = intval($row['impressions'] ?? 0);
            if (!isset($query_map[$norm]) || $clicks > intval($query_map[$norm]['traffic'] ?? 0) || $impressions > intval($query_map[$norm]['impressions'] ?? 0)) {
                $query_map[$norm] = array(
                    'keyword' => $keyword,
                    'search_volume' => null,
                    'difficulty' => null,
                    'position' => isset($row['position']) ? round(floatval($row['position']), 2) : null,
                    'traffic' => $clicks,
                    'target_url' => '',
                    'source' => 'gsc_query',
                    'updated_at' => gmdate('Y-m-d H:i:s'),
                    'impressions' => $impressions,
                );
            }
        }

        return array(
            'rows' => array_slice(array_values($query_map), 0, 8),
        );
    }

    private function aysa_adjustments_run_keyword_research_sync($project_id, $setup_user_id = 0) {
        $snapshot = $this->aysa_adjustments_get_keyword_research_snapshot($project_id, $setup_user_id);
        $gsc_count = intval($snapshot['gsc_count'] ?? 0);
        $suggestions_count = intval($snapshot['suggestions_count'] ?? 0);
        $status = isset($snapshot['refresh_status']) && is_array($snapshot['refresh_status']) ? $snapshot['refresh_status'] : array();

        if ($gsc_count <= 0 && (($status['status'] ?? 'idle') !== 'running')) {
            $run_id = uniqid('kwd_', true);
            $now = time();
            $this->aysa_keyword_discovery_save_status($project_id, array(
                'run_id' => $run_id,
                'status' => 'running',
                'total' => 0,
                'processed' => 0,
                'inserted' => 0,
                'updated' => 0,
                'staged' => 0,
                'apply_to_wp' => 1,
                'last_error' => '',
                'started_at' => $now,
                'updated_at' => $now,
                'finished_at' => 0,
            ));
            $this->aysa_keyword_discovery_schedule_background_refresh($project_id, 1);
        }

        if ($suggestions_count <= 0) {
            $this->aysa_keyword_discovery_refresh_keyword_suggestions($project_id, $setup_user_id, false);
        }

        return $this->aysa_adjustments_get_keyword_research_snapshot($project_id, $setup_user_id);
    }

    private function aysa_adjustments_run_competitor_gap_sync($project_id, $setup_user_id = 0) {
        $seed_data = $this->aysa_keyword_strategy_extract_seed_data($setup_user_id > 0 ? $setup_user_id : get_current_user_id());
        $competitors = isset($seed_data['competitors']) && is_array($seed_data['competitors']) ? array_values($seed_data['competitors']) : array();
        $target = (string) wp_parse_url(home_url('/'), PHP_URL_HOST);
        $context = $this->aysa_monitoring_resolve_context($target);
        $payload = array(
            'project_id' => intval($project_id),
            'target' => trim((string) ($context['target'] ?? $target)),
            'location_code' => intval($context['location_code'] ?? 2840),
            'language_code' => (string) ($context['language_code'] ?? 'ro'),
            'limit' => 200,
            'competitors' => array_values(array_filter(array_map(function($competitor) {
                return $this->aysa_monitoring_extract_host((string) $competitor);
            }, $competitors))),
        );
        $result = $this->aysa_broker_request('POST', '/v1/keywords/gap', $payload, 180);
        if (!is_wp_error($result)) {
            $data = isset($result['data']) && is_array($result['data']) ? $result['data'] : $result;
            if (is_array($data) && isset($data['rankedKeywords'])) {
                $this->aysa_competitor_gap_save_cached_payload($project_id, $payload, $data);
            }
        }
        return $this->aysa_adjustments_get_competitor_gap_snapshot($project_id);
    }

    private function aysa_adjustments_run_relevant_keywords_sync($project_id, $setup_user_id = 0, $language = 'en') {
        $project_id = intval($project_id);
        $setup_user_id = intval($setup_user_id);
        $language = in_array($language, array('en', 'ro', 'bg'), true) ? $language : 'en';

        $gap_snapshot = $this->aysa_adjustments_get_competitor_gap_snapshot($project_id);
        $gap_started_now = empty($gap_snapshot['has_data']);
        if ($gap_started_now) {
            $gap_snapshot = $this->aysa_adjustments_run_competitor_gap_sync($project_id, $setup_user_id);
        }

        if (empty($gap_snapshot['has_data'])) {
            return array(
                'module' => 'competitor_gap',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I started from competitor gap, but I still do not have saved gap rows to filter yet. The next good step is to make sure we have competitors and then rerun the gap.',
                    'Am început cu competitor gap, dar încă nu am rânduri salvate pe care să le filtrez. Următorul pas bun este să ne asigurăm că avem competitori și apoi să rulăm din nou gap-ul.',
                    'Започнах с competitor gap, но все още нямам запазени редове, които да филтрирам. Следващата добра стъпка е да се уверим, че имаме конкуренти, и после да пуснем gap-а отново.'
                ),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'Show competitors', 'Arată competitorii', 'Покажи конкурентите'),
                        'message' => $this->aysa_adjustments_tr($language, 'Show me the competitors found so far.', 'Arată-mi competitorii găsiți până acum.', 'Покажи ми намерените конкуренти дотук.'),
                        'ui_action' => 'open_artifact_competitor_identification',
                    ),
                ),
            );
        }

        $keyword_snapshot = $this->aysa_adjustments_get_keyword_research_snapshot($project_id, $setup_user_id);
        $selection = $this->aysa_adjustments_select_relevant_keywords_from_all_sources(
            $project_id,
            $setup_user_id,
            $keyword_snapshot,
            $gap_snapshot
        );
        $selected_rows = isset($selection['rows']) && is_array($selection['rows']) ? array_values($selection['rows']) : array();
        if (empty($selected_rows)) {
            return array(
                'module' => 'keyword_research',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I checked the keywords already available from Google, suggestions, and competitor gap, but I did not find a clean enough set to add automatically without noise. I prefer to stay conservative than save the wrong terms.',
                    'Am verificat keyword-urile deja disponibile din Google, suggestions și competitor gap, dar nu am găsit un set suficient de curat ca să îl adaug automat fără zgomot. Prefer să rămân conservator decât să salvez termenii greșiți.',
                    'Проверих keyword-ите, които вече са налични от Google, suggestions и competitor gap, но не намерих достатъчно чист набор, който да добавя автоматично без шум. Предпочитам да остана по-консервативен, вместо да запазя грешни термини.'
                ),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See unified keywords', 'Vezi keyword-urile unificate', 'Виж обединените keyword-и'),
                        'message' => $this->aysa_adjustments_tr($language, 'Open the unified keyword view and show me the current sources.', 'Deschide vederea unificată și arată-mi sursele curente.', 'Отвори обединения keyword изглед и ми покажи текущите източници.'),
                        'ui_action' => 'open_artifact_unified_keywords',
                    ),
                ),
                'ui_action' => 'open_artifact_unified_keywords',
            );
        }

        $existing_rows = $this->aysa_adjustments_get_local_keywords_by_sources(
            $project_id,
            array('gsc', 'gsc_query', 'gsc_query_only', 'keyword_suggestion', 'related_keyword', 'competitor_gap')
        );
        $existing_lookup = array();
        foreach ($existing_rows as $existing_row) {
            if (!is_array($existing_row)) {
                continue;
            }
            $keyword = trim((string) ($existing_row['keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }
            $existing_lookup[$this->aysa_adjustments_normalize_keyword_lookup_key($keyword)] = true;
        }

        $persist_rows = array();
        foreach ($selected_rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $source_keys = isset($row['source_keys']) && is_array($row['source_keys']) ? $row['source_keys'] : array();
            $keyword = (string) ($row['keyword'] ?? '');
            $keyword_norm = $this->aysa_adjustments_normalize_keyword_lookup_key($keyword);
            $already_exists = !empty($existing_lookup[$keyword_norm]);
            $needs_insert = !$already_exists && !empty($source_keys['competitor_gap']);
            if (!$needs_insert) {
                continue;
            }
            $persist_rows[] = array(
                'keyword' => $keyword,
                'searchVolume' => isset($row['search_volume']) ? intval($row['search_volume']) : null,
                'difficulty' => isset($row['difficulty']) ? floatval($row['difficulty']) : null,
                'intent' => (string) ($row['intent'] ?? ''),
                'competition' => '',
                'traffic' => null,
                'trafficPct' => null,
                'position' => null,
                'cpc' => null,
                'url' => '',
                'item_types' => array(),
                'source' => 'competitor_gap',
            );
        }

        $persist_result = $this->aysa_keyword_discovery_persist_keywords($project_id, $persist_rows);
        $association_result = $this->aysa_adjustments_auto_associate_selected_keywords(
            $project_id,
            isset($selection['matched_items']) && is_array($selection['matched_items']) ? $selection['matched_items'] : array()
        );

        $selected_count = count($persist_rows);
        $associated_count = intval($association_result['associated'] ?? 0);
        $candidate_count = intval($association_result['matched_candidates'] ?? 0);
        $selection_count = count($selected_rows);

        $this->aysa_adjustments_save_recent_relevant_keywords($project_id, array(
            'selected_keywords' => array_values(array_map(function($row) {
                return (string) ($row['keyword'] ?? '');
            }, $selected_rows)),
            'updated_at' => current_time('mysql'),
        ));

        $message = $gap_started_now
            ? $this->aysa_adjustments_tr(
                $language,
                sprintf(
                    'I first started competitor gap because it was missing. Then I checked Google visibility, keyword suggestions, and gap opportunities together, kept %d relevant keywords for your business, added %d new gap terms into the keyword map, found direct page matches for %d of them, and created %d new saved associations.',
                    $selection_count,
                    $selected_count,
                    $candidate_count,
                    $associated_count
                ),
                sprintf(
                    'Am pornit mai întâi competitor gap-ul, pentru că lipsea. Apoi am verificat împreună vizibilitatea din Google, keyword suggestions și oportunitățile din gap, am păstrat %d keyword-uri relevante pentru afacerea ta, am adăugat %d termeni noi din gap în keyword map, am găsit potriviri directe cu pagini pentru %d dintre ele și am creat %d asocieri noi salvate.',
                    $selection_count,
                    $selected_count,
                    $candidate_count,
                    $associated_count
                ),
                sprintf(
                    'Първо стартирах competitor gap, защото липсваше. След това проверих заедно Google видимостта, keyword suggestions и gap възможностите, запазих %d релевантни keyword-а за твоя бизнес, добавих %d нови gap термина в keyword map-а, намерих директни съвпадения със страници за %d от тях и създадох %d нови запазени свързвания.',
                    $selection_count,
                    $selected_count,
                    $candidate_count,
                    $associated_count
                )
            )
            : $this->aysa_adjustments_tr(
                $language,
                sprintf(
                    'I checked the Google rows, the saved keyword suggestions, and the competitor gap together. I kept %d relevant keywords for your business, added %d new gap terms into the keyword map where needed, found direct page matches for %d of them, and created %d new saved associations.',
                    $selection_count,
                    $selected_count,
                    $candidate_count,
                    $associated_count
                ),
                sprintf(
                    'Am verificat împreună rândurile din Google, keyword suggestions și competitor gap. Am păstrat %d keyword-uri relevante pentru afacerea ta, am adăugat %d termeni noi din gap în keyword map unde a fost nevoie, am găsit potriviri directe cu pagini pentru %d dintre ele și am creat %d asocieri noi salvate.',
                    $selection_count,
                    $selected_count,
                    $candidate_count,
                    $associated_count
                ),
                sprintf(
                    'Проверих заедно Google редовете, keyword suggestions и competitor gap. Запазих %d релевантни keyword-а за твоя бизнес, добавих %d нови gap термина в keyword map-а при нужда, намерих директни съвпадения със страници за %d от тях и създадох %d нови запазени свързвания.',
                    $selection_count,
                    $selected_count,
                    $candidate_count,
                    $associated_count
                )
            );

        $suggested_actions = array(
            array(
                'label' => $this->aysa_adjustments_tr($language, 'See unified keywords', 'Vezi keyword-urile unificate', 'Виж обединените keyword-и'),
                'message' => $this->aysa_adjustments_tr($language, 'Show me the unified keywords and source markers.', 'Arată-mi keyword-urile unificate și marcajele de sursă.', 'Покажи ми обединените keyword-и и маркерите за източник.'),
                'ui_action' => 'open_artifact_unified_keywords',
            ),
        );
        if ($associated_count > 0) {
            $suggested_actions[] = array(
                'label' => $this->aysa_adjustments_tr($language, 'See matches', 'Vezi asocierile', 'Виж свързванията'),
                'message' => $this->aysa_adjustments_tr($language, 'Show me the keyword-to-page matches.', 'Arată-mi asocierile keyword-urilor cu paginile.', 'Покажи ми свързването между keyword-ите и страниците.'),
                'ui_action' => 'open_artifact_matches',
            );
        }

        return array(
            'module' => 'keyword_research',
            'assistant_message' => $message,
            'suggested_actions' => $suggested_actions,
            'ui_action' => '',
            'meta' => array(
                'selected_keywords' => $selection_count,
                'persisted_keywords' => intval($persist_result['total'] ?? 0),
                'associated_keywords' => $associated_count,
            ),
        );
    }

    private function aysa_adjustments_select_relevant_keywords_from_all_sources($project_id, $setup_user_id, $keyword_snapshot, $gap_snapshot) {
        $context = $this->aysa_adjustments_collect_business_relevance_context($setup_user_id, $project_id);
        $selected = array();
        $matched_items = array();
        $rows_by_keyword = array();

        $collect = function($rows, $source_key) use (&$rows_by_keyword) {
            foreach (is_array($rows) ? $rows : array() as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $keyword = trim((string) ($row['keyword'] ?? ''));
                if ($keyword === '') {
                    continue;
                }
                $norm = $this->aysa_adjustments_normalize_keyword_lookup_key($keyword);
                if ($norm === '') {
                    continue;
                }
                if (!isset($rows_by_keyword[$norm])) {
                    $rows_by_keyword[$norm] = array(
                        'keyword' => $keyword,
                        'search_volume' => 0,
                        'difficulty' => '',
                        'intent' => '',
                        'position' => '',
                        'source_keys' => array(),
                    );
                }
                $entry = $rows_by_keyword[$norm];
                $entry['source_keys'][$source_key] = true;
                $volume = intval($row['search_volume'] ?? $row['searchVolume'] ?? 0);
                if ($volume > intval($entry['search_volume'])) {
                    $entry['search_volume'] = $volume;
                }
                $difficulty = $row['difficulty'] ?? '';
                if ($entry['difficulty'] === '' && $difficulty !== '' && $difficulty !== 'N/A') {
                    $entry['difficulty'] = (string) $difficulty;
                }
                $intent = trim((string) ($row['intent'] ?? ''));
                if ($entry['intent'] === '' && $intent !== '' && $intent !== 'N/A') {
                    $entry['intent'] = $intent;
                }
                $position = $row['position'] ?? '';
                if ($entry['position'] === '' && $position !== '' && $position !== 'N/A') {
                    $entry['position'] = (string) $position;
                }
                $rows_by_keyword[$norm] = $entry;
            }
        };

        $collect(isset($keyword_snapshot['gsc_rows']) ? $keyword_snapshot['gsc_rows'] : array(), 'gsc');
        $collect(isset($keyword_snapshot['suggestion_rows']) ? $keyword_snapshot['suggestion_rows'] : array(), 'suggestions');
        $collect(isset($gap_snapshot['rows']) ? $gap_snapshot['rows'] : array(), 'competitor_gap');

        foreach ($rows_by_keyword as $norm => $row) {
            $keyword = trim((string) ($row['keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }
            $candidate = $this->aysa_adjustments_match_keyword_to_wp_item($keyword, $context['catalog_items']);
            if (!$this->aysa_adjustments_keyword_passes_business_gate($keyword, $row, $context, $candidate)) {
                continue;
            }
            $score = $this->aysa_adjustments_score_keyword_relevance($keyword, $row, $context, $candidate);
            if (!empty($row['source_keys']['gsc'])) {
                $score += 0.12;
            }
            if (!empty($row['source_keys']['suggestions'])) {
                $score += 0.06;
            }
            if (!empty($row['source_keys']['competitor_gap'])) {
                $score += 0.05;
            }
            $score = min(1, $score);
            if ($score < 0.42) {
                continue;
            }

            $row['relevance_score'] = $score;
            $selected[] = $row;
            if (is_array($candidate) && !empty($candidate['id'])) {
                $candidate['keyword'] = $keyword;
                $matched_items[$norm] = $candidate;
            }
        }

        usort($selected, function($left, $right) {
            $left_score = floatval($left['relevance_score'] ?? 0);
            $right_score = floatval($right['relevance_score'] ?? 0);
            if ($left_score === $right_score) {
                return intval($right['search_volume'] ?? 0) <=> intval($left['search_volume'] ?? 0);
            }
            return $right_score <=> $left_score;
        });

        return array(
            'rows' => $selected,
            'matched_items' => $matched_items,
        );
    }

    private function aysa_adjustments_select_relevant_keywords_from_competitor_gap($project_id, $setup_user_id, $gap_snapshot) {
        $context = $this->aysa_adjustments_collect_business_relevance_context($setup_user_id, $project_id);
        $rows = isset($gap_snapshot['rows']) && is_array($gap_snapshot['rows']) ? array_values($gap_snapshot['rows']) : array();
        $selected = array();
        $matched_items = array();

        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }

            $candidate = $this->aysa_adjustments_match_keyword_to_wp_item($keyword, $context['catalog_items']);
            if (!$this->aysa_adjustments_keyword_passes_business_gate($keyword, $row, $context, $candidate)) {
                continue;
            }
            $score = $this->aysa_adjustments_score_keyword_relevance($keyword, $row, $context, $candidate);
            if ($score < 0.42) {
                continue;
            }

            $selected[] = array_merge($row, array(
                'relevance_score' => $score,
            ));

            if (is_array($candidate) && !empty($candidate['id'])) {
                $candidate['keyword'] = $keyword;
                $matched_items[strtolower(trim($keyword))] = $candidate;
            }
        }

        usort($selected, function($left, $right) {
            $left_score = floatval($left['relevance_score'] ?? 0);
            $right_score = floatval($right['relevance_score'] ?? 0);
            if ($left_score === $right_score) {
                return intval($right['search_volume'] ?? 0) <=> intval($left['search_volume'] ?? 0);
            }
            return $right_score <=> $left_score;
        });

        return array(
            'rows' => $selected,
            'matched_items' => $matched_items,
        );
    }

    private function aysa_adjustments_collect_business_relevance_context($setup_user_id, $project_id = 0) {
        $setup_user_id = intval($setup_user_id);
        $project_id = intval($project_id);
        if ($setup_user_id <= 0) {
            $setup_user_id = intval($this->aysa_get_authenticated_external_user_id());
            if ($setup_user_id <= 0) {
                $setup_user_id = get_current_user_id();
            }
        }

        $seed_data = $this->aysa_keyword_strategy_extract_seed_data($setup_user_id);
        $general = get_option('aysa_wizard_general_' . intval($setup_user_id), array());
        if (!is_array($general)) {
            $general = array();
        }
        $context = $this->aysa_keyword_strategy_get_context($setup_user_id);

        $text_parts = array_filter(array(
            (string) ($general['company_name'] ?? ''),
            (string) ($general['brand'] ?? ''),
            (string) ($general['company_description'] ?? ''),
            (string) ($general['business_information'] ?? ''),
            (string) ($general['industry'] ?? ''),
            (string) ($general['website_type'] ?? ''),
            implode(' ', isset($seed_data['seed_keywords']) && is_array($seed_data['seed_keywords']) ? $seed_data['seed_keywords'] : array()),
        ));
        $business_tokens = $this->aysa_adjustments_tokenize_keyword_text(implode(' ', $text_parts));
        $industry_hints = $this->aysa_adjustments_get_industry_keyword_hints((string) ($context['industry'] ?? ''), (string) ($context['website_type'] ?? ''));
        $hint_tokens = $this->aysa_adjustments_tokenize_keyword_text(implode(' ', $industry_hints));
        $own_brand_tokens = $this->aysa_adjustments_extract_brand_tokens(array(
            (string) ($general['company_name'] ?? ''),
            (string) ($general['brand'] ?? ''),
            (string) ($general['website'] ?? ''),
            (string) home_url('/'),
        ));
        $competitor_brand_tokens = $project_id > 0 ? $this->aysa_adjustments_get_competitor_brand_tokens($project_id, array_values(array_unique(array_merge($business_tokens, $hint_tokens, $own_brand_tokens)))) : array();
        $catalog_items = $this->aysa_adjustments_get_wp_catalog_items();
        $catalog_items = $this->aysa_adjustments_filter_catalog_items_for_business($catalog_items, array_values(array_unique(array_merge($business_tokens, $hint_tokens))));

        return array(
            'seed_keywords' => isset($seed_data['seed_keywords']) && is_array($seed_data['seed_keywords']) ? array_values($seed_data['seed_keywords']) : array(),
            'business_tokens' => array_values(array_unique(array_merge($business_tokens, $hint_tokens))),
            'hint_tokens' => array_values(array_unique($hint_tokens)),
            'own_brand_tokens' => $own_brand_tokens,
            'competitor_brand_tokens' => $competitor_brand_tokens,
            'catalog_items' => $catalog_items,
            'industry_hints' => $industry_hints,
            'industry_exclusions' => $this->aysa_adjustments_get_industry_exclusion_tokens((string) ($context['industry'] ?? ''), (string) ($context['website_type'] ?? '')),
        );
    }

    private function aysa_adjustments_get_industry_keyword_hints($industry, $website_type = '') {
        $industry = strtolower(trim(remove_accents((string) $industry)));
        $website_type = strtolower(trim(remove_accents((string) $website_type)));

        if (str_contains($industry, 'flor') || str_contains($website_type, 'commerce') || str_contains($website_type, 'ecommerce')) {
            return array(
                'flori', 'florarie', 'florarie online', 'buchet', 'buchete', 'aranjamente florale',
                'trandafiri', 'bujori', 'lalele', 'cadouri', 'livrare flori', 'flori cadou',
                'flori pentru evenimente', 'flori pentru aniversare', 'flori pentru nunta'
            );
        }

        return array((string) $industry, (string) $website_type);
    }

    private function aysa_adjustments_get_industry_exclusion_tokens($industry, $website_type = '') {
        $industry = strtolower(trim(remove_accents((string) $industry)));
        $website_type = strtolower(trim(remove_accents((string) $website_type)));

        if (str_contains($industry, 'flor') || str_contains($website_type, 'commerce') || str_contains($website_type, 'ecommerce')) {
            return array(
                'wifi', 'camera', 'yala', 'lac', 'unghii', 'pisici',
                'prostata', 'sinuzita', 'menopauza'
            );
        }

        if (str_contains($industry, 'contab') || str_contains($industry, 'factur')) {
            return array(
                'flori', 'buchet', 'trandafiri', 'bujori', 'lalele', 'aranjamente'
            );
        }

        return array();
    }

    private function aysa_adjustments_extract_brand_tokens($values) {
        $values = is_array($values) ? $values : array();
        $tokens = array();
        $skip = array('www', 'http', 'https', 'com', 'ro', 'net', 'org', 'shop', 'store', 'online');

        foreach ($values as $value) {
            $value = trim((string) $value);
            if ($value === '') {
                continue;
            }
            $parsed_host = wp_parse_url($value, PHP_URL_HOST);
            if (is_string($parsed_host) && $parsed_host !== '') {
                $host_parts = preg_split('/[.\-]+/', strtolower(trim(remove_accents($parsed_host)))) ?: array();
                foreach ($host_parts as $part) {
                    $part = trim((string) $part);
                    if ($part === '' || strlen($part) < 3 || in_array($part, $skip, true)) {
                        continue;
                    }
                    $tokens[] = $part;
                }
            }

            foreach ($this->aysa_adjustments_tokenize_keyword_text($value) as $token) {
                if (strlen($token) < 3 || in_array($token, $skip, true)) {
                    continue;
                }
                $tokens[] = $token;
            }
        }

        return array_values(array_unique($tokens));
    }

    private function aysa_adjustments_get_competitor_brand_tokens($project_id, $protected_tokens = array()) {
        $snapshot = $this->aysa_adjustments_get_competitor_identification_snapshot($project_id);
        $rows = isset($snapshot['rows']) && is_array($snapshot['rows']) ? $snapshot['rows'] : array();
        $protected_tokens = array_values(array_unique(array_map(function($token) {
            return strtolower(trim(remove_accents((string) $token)));
        }, is_array($protected_tokens) ? $protected_tokens : array())));
        $skip = array('www', 'http', 'https', 'com', 'ro', 'net', 'org', 'shop', 'store', 'online', 'facebook', 'instagram');

        $tokens = array();
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $domain = trim((string) ($row['domain'] ?? ''));
            if ($domain === '') {
                continue;
            }
            foreach ($this->aysa_adjustments_extract_brand_tokens(array($domain)) as $token) {
                if (strlen($token) < 4 || in_array($token, $skip, true) || in_array($token, $protected_tokens, true)) {
                    continue;
                }
                $tokens[] = $token;
            }
        }

        return array_values(array_unique($tokens));
    }

    private function aysa_adjustments_get_wp_catalog_items() {
        $items = array();

        $terms = get_terms(array(
            'taxonomy' => 'product_cat',
            'hide_empty' => false,
        ));
        if (is_array($terms)) {
            foreach ($terms as $term) {
                if (!is_object($term)) {
                    continue;
                }
                $text = trim($term->name . ' ' . $term->slug . ' ' . wp_strip_all_tags((string) $term->description));
                $items[] = array(
                    'type' => 'Category',
                    'id' => intval($term->term_id),
                    'name' => (string) $term->name,
                    'url' => !is_wp_error(get_term_link($term)) ? (string) get_term_link($term) : '',
                    'text' => $text,
                    'tokens' => $this->aysa_adjustments_tokenize_keyword_text($text),
                );
            }
        }

        foreach (get_posts(array('post_type' => 'product', 'posts_per_page' => -1, 'post_status' => 'publish')) as $post) {
            if (!is_object($post)) {
                continue;
            }
            $text = trim($post->post_title . ' ' . $post->post_name . ' ' . wp_strip_all_tags(wp_trim_words((string) $post->post_content, 40, '')));
            $items[] = array(
                'type' => 'Product',
                'id' => intval($post->ID),
                'name' => (string) $post->post_title,
                'url' => (string) get_permalink($post->ID),
                'text' => $text,
                'tokens' => $this->aysa_adjustments_tokenize_keyword_text($text),
            );
        }

        foreach (get_posts(array('post_type' => 'page', 'posts_per_page' => -1, 'post_status' => 'publish')) as $post) {
            if (!is_object($post)) {
                continue;
            }
            $text = trim($post->post_title . ' ' . $post->post_name . ' ' . wp_strip_all_tags(wp_trim_words((string) $post->post_content, 40, '')));
            $items[] = array(
                'type' => 'Page',
                'id' => intval($post->ID),
                'name' => (string) $post->post_title,
                'url' => (string) get_permalink($post->ID),
                'text' => $text,
                'tokens' => $this->aysa_adjustments_tokenize_keyword_text($text),
            );
        }

        return $items;
    }

    private function aysa_adjustments_filter_catalog_items_for_business($catalog_items, $focus_tokens) {
        $catalog_items = is_array($catalog_items) ? $catalog_items : array();
        $focus_tokens = array_values(array_filter(array_unique(array_map(function($token) {
            return trim((string) $token);
        }, is_array($focus_tokens) ? $focus_tokens : array()))));
        if (empty($focus_tokens)) {
            return $catalog_items;
        }

        $keep = array();
        foreach ($catalog_items as $item) {
            if (!is_array($item)) {
                continue;
            }
            $item_tokens = isset($item['tokens']) && is_array($item['tokens']) ? $item['tokens'] : array();
            $item_text = strtolower(trim(remove_accents((string) ($item['text'] ?? ''))));
            $item_name = strtolower(trim(remove_accents((string) ($item['name'] ?? ''))));
            $overlap = count(array_intersect($item_tokens, $focus_tokens));
            $keep_item = ($overlap >= 1);

            if (!$keep_item) {
                foreach ($focus_tokens as $focus_token) {
                    if ($focus_token !== '' && (str_contains($item_text, $focus_token) || str_contains($item_name, $focus_token))) {
                        $keep_item = true;
                        break;
                    }
                }
            }

            if ($keep_item) {
                $keep[] = $item;
            }
        }

        return !empty($keep) ? $keep : $catalog_items;
    }

    private function aysa_adjustments_tokenize_keyword_text($text) {
        $text = strtolower(trim(remove_accents((string) $text)));
        if ($text === '') {
            return array();
        }
        $text = preg_replace('/[^a-z0-9 ]+/u', ' ', $text);
        $parts = preg_split('/\s+/', (string) $text) ?: array();
        $stopwords = array(
            'si', 'sau', 'de', 'din', 'la', 'in', 'pe', 'cu', 'fara', 'pentru', 'prin', 'un', 'o',
            'the', 'and', 'for', 'to', 'of', 'a', 'al', 'ale', 'ai', 'catre', 'despre', 'este', 'sunt',
            'fi', 'ca', 'ce', 'mai', 'sunt', 'sau', 'your', 'site'
        );
        $tokens = array();
        foreach ($parts as $token) {
            $token = trim((string) $token);
            if ($token === '' || strlen($token) < 2 || in_array($token, $stopwords, true)) {
                continue;
            }
            $tokens[] = $token;
        }
        return array_values(array_unique($tokens));
    }

    private function aysa_adjustments_get_weak_context_tokens() {
        return array(
            'bucuresti', 'bragadiru', 'romania', 'sector', 'online', 'local', 'oras', 'city'
        );
    }

    private function aysa_adjustments_match_keyword_to_wp_item($keyword, $catalog_items) {
        $keyword_tokens = $this->aysa_adjustments_tokenize_keyword_text($keyword);
        if (empty($keyword_tokens) || !is_array($catalog_items)) {
            return null;
        }

        $keyword_norm = implode(' ', $keyword_tokens);
        $best = null;
        foreach ($catalog_items as $item) {
            if (!is_array($item) || empty($item['tokens'])) {
                continue;
            }
            $item_tokens = is_array($item['tokens']) ? $item['tokens'] : array();
            $overlap = count(array_intersect($keyword_tokens, $item_tokens));
            if ($overlap <= 0) {
                continue;
            }

            $score = ($overlap / max(count($keyword_tokens), 1)) * 0.72
                + ($overlap / max(count($item_tokens), 1)) * 0.28;
            $item_text = strtolower(trim(remove_accents((string) ($item['text'] ?? ''))));
            if ($item_text !== '' && (str_contains($item_text, $keyword_norm) || str_contains($keyword_norm, strtolower(trim(remove_accents((string) ($item['name'] ?? ''))))))) {
                $score += 0.18;
            }

            if ($best === null || $score > floatval($best['score'] ?? 0)) {
                $best = array(
                    'type' => (string) ($item['type'] ?? ''),
                    'id' => intval($item['id'] ?? 0),
                    'name' => (string) ($item['name'] ?? ''),
                    'url' => (string) ($item['url'] ?? ''),
                    'score' => min(1, $score),
                );
            }
        }

        if ($best === null || floatval($best['score'] ?? 0) < 0.34) {
            return null;
        }

        return $best;
    }

    private function aysa_adjustments_score_keyword_relevance($keyword, $row, $context, $candidate = null) {
        $keyword_tokens = $this->aysa_adjustments_tokenize_keyword_text($keyword);
        if (empty($keyword_tokens)) {
            return 0;
        }

        $score = 0.0;
        $business_tokens = isset($context['business_tokens']) && is_array($context['business_tokens']) ? $context['business_tokens'] : array();
        $weak_context_tokens = $this->aysa_adjustments_get_weak_context_tokens();
        $strong_business_tokens = array_values(array_diff($business_tokens, $weak_context_tokens));
        $strong_overlap = count(array_intersect($keyword_tokens, $strong_business_tokens));
        $weak_overlap = count(array_intersect($keyword_tokens, $weak_context_tokens));
        $overlap = $strong_overlap + $weak_overlap;
        if ($strong_overlap > 0) {
            $score += min(0.58, $strong_overlap * 0.2);
        }
        if ($weak_overlap > 0) {
            $score += min(0.08, $weak_overlap * 0.03);
        }

        $keyword_norm = strtolower(trim(remove_accents((string) $keyword)));
        $seed_keywords = isset($context['seed_keywords']) && is_array($context['seed_keywords']) ? $context['seed_keywords'] : array();
        foreach ($seed_keywords as $seed_keyword) {
            $seed_norm = strtolower(trim(remove_accents((string) $seed_keyword)));
            if ($seed_norm === '') {
                continue;
            }
            if (str_contains($keyword_norm, $seed_norm) || str_contains($seed_norm, $keyword_norm)) {
                $score = max($score, 0.78);
                break;
            }

            $seed_tokens = $this->aysa_adjustments_tokenize_keyword_text($seed_keyword);
            $seed_strong_tokens = array_values(array_diff($seed_tokens, $weak_context_tokens));
            $seed_overlap = count(array_intersect($keyword_tokens, $seed_strong_tokens));
            if ($seed_overlap > 0) {
                $score = max($score, min(0.76, ($seed_overlap / max(count($seed_strong_tokens), 1)) * 0.76));
            }
        }

        if (is_array($candidate) && !empty($candidate['id'])) {
            $score = max($score, min(0.92, 0.42 + (floatval($candidate['score'] ?? 0) * 0.5)));
        }

        $intent = strtolower(trim((string) ($row['intent'] ?? '')));
        if (in_array($intent, array('transactional', 'commercial'), true)) {
            $score += 0.05;
        }

        if (intval($row['search_volume'] ?? 0) > 0) {
            $score += 0.02;
        }

        if ($overlap <= 0 && !is_array($candidate)) {
            $score -= 0.2;
        }

        $source_keys = isset($row['source_keys']) && is_array($row['source_keys']) ? $row['source_keys'] : array();
        if (!empty($source_keys['competitor_gap']) && empty($source_keys['gsc']) && empty($source_keys['suggestions']) && !is_array($candidate) && $strong_overlap <= 1) {
            $score -= 0.24;
        }

        $disallowed_tokens = isset($context['industry_exclusions']) && is_array($context['industry_exclusions'])
            ? $context['industry_exclusions']
            : array();
        $disallowed_overlap = count(array_intersect($keyword_tokens, $disallowed_tokens));
        if ($disallowed_overlap > 0 && $strong_overlap <= 0) {
            $score -= min(0.45, 0.16 * $disallowed_overlap);
        }

        $competitor_brand_tokens = isset($context['competitor_brand_tokens']) && is_array($context['competitor_brand_tokens'])
            ? $context['competitor_brand_tokens']
            : array();
        $own_brand_tokens = isset($context['own_brand_tokens']) && is_array($context['own_brand_tokens'])
            ? $context['own_brand_tokens']
            : array();
        $competitor_overlap = count(array_intersect($keyword_tokens, $competitor_brand_tokens));
        $own_brand_overlap = count(array_intersect($keyword_tokens, $own_brand_tokens));
        if ($competitor_overlap > 0 && $own_brand_overlap <= 0) {
            $score -= min(0.55, 0.22 * $competitor_overlap);
        }

        return max(0, min(1, $score));
    }

    private function aysa_adjustments_keyword_passes_business_gate($keyword, $row, $context, $candidate = null) {
        $keyword_tokens = $this->aysa_adjustments_tokenize_keyword_text($keyword);
        if (empty($keyword_tokens)) {
            return false;
        }

        $weak_context_tokens = $this->aysa_adjustments_get_weak_context_tokens();
        $hint_tokens = isset($context['hint_tokens']) && is_array($context['hint_tokens']) ? $context['hint_tokens'] : array();
        $core_tokens = array_values(array_diff($hint_tokens, $weak_context_tokens, array('flori', 'florarie')));
        $core_overlap = count(array_intersect($keyword_tokens, $core_tokens));
        $generic_overlap = count(array_intersect($keyword_tokens, array('flori', 'florarie', 'plante')));

        $seed_keywords = isset($context['seed_keywords']) && is_array($context['seed_keywords']) ? $context['seed_keywords'] : array();
        $seed_overlap = 0;
        foreach ($seed_keywords as $seed_keyword) {
            $seed_tokens = array_values(array_diff($this->aysa_adjustments_tokenize_keyword_text($seed_keyword), $weak_context_tokens));
            $seed_overlap = max($seed_overlap, count(array_intersect($keyword_tokens, $seed_tokens)));
        }

        $source_keys = isset($row['source_keys']) && is_array($row['source_keys']) ? $row['source_keys'] : array();
        $candidate_score = is_array($candidate) ? floatval($candidate['score'] ?? 0) : 0.0;
        $competitor_brand_tokens = isset($context['competitor_brand_tokens']) && is_array($context['competitor_brand_tokens'])
            ? $context['competitor_brand_tokens']
            : array();
        $own_brand_tokens = isset($context['own_brand_tokens']) && is_array($context['own_brand_tokens'])
            ? $context['own_brand_tokens']
            : array();
        $competitor_overlap = count(array_intersect($keyword_tokens, $competitor_brand_tokens));
        $own_brand_overlap = count(array_intersect($keyword_tokens, $own_brand_tokens));
        $brand_intent_tokens = array('florarie', 'flori', 'online', 'shop', 'store', 'bucuresti', 'bragadiru', 'romania');

        if ($competitor_overlap > 0 && $own_brand_overlap <= 0) {
            $brand_intent_overlap = count(array_intersect($keyword_tokens, $brand_intent_tokens));
            if ($brand_intent_overlap > 0 || count($keyword_tokens) <= 3 || $candidate_score < 0.78) {
                return false;
            }
        }

        if ($core_overlap >= 1) {
            return true;
        }

        if ($seed_overlap >= 2) {
            return true;
        }

        if (!empty($source_keys['gsc']) && ($generic_overlap >= 1 || $candidate_score >= 0.45)) {
            return true;
        }

        if (is_array($candidate) && $candidate_score >= 0.62 && $generic_overlap >= 1) {
            return true;
        }

        return false;
    }

    private function aysa_adjustments_auto_associate_selected_keywords($project_id, $matched_items) {
        $existing_rows = $this->get_keyword_discovery_local_associations($project_id);
        $existing_keywords = array();
        if (is_array($existing_rows)) {
            foreach ($existing_rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $existing_keywords[strtolower(trim((string) ($row['keyword'] ?? '')))] = true;
            }
        }

        $associated = 0;
        $matched_candidates = 0;
        foreach ($matched_items as $keyword_norm => $candidate) {
            if (!is_array($candidate) || empty($candidate['id']) || empty($candidate['type'])) {
                continue;
            }
            $matched_candidates++;
            if (!empty($existing_keywords[$keyword_norm])) {
                continue;
            }
            $this->upsert_keyword_discovery_local_association(
                $project_id,
                (string) ($candidate['keyword'] ?? $keyword_norm),
                (string) $candidate['type'],
                intval($candidate['id']),
                (string) ($candidate['name'] ?? '')
            );
            $associated++;
            $existing_keywords[$keyword_norm] = true;
        }

        return array(
            'associated' => $associated,
            'matched_candidates' => $matched_candidates,
        );
    }

    private function aysa_adjustments_run_competitor_identification_sync($project_id, $setup_user_id = 0) {
        $setup_user_id = intval($setup_user_id);
        if ($setup_user_id <= 0) {
            $setup_user_id = intval($this->aysa_get_authenticated_external_user_id());
            if ($setup_user_id <= 0) {
                $setup_user_id = intval(get_option('aysa_user_id', 0));
            }
            if ($setup_user_id <= 0) {
                $setup_user_id = get_current_user_id();
            }
        }

        $seed_data = $this->aysa_keyword_strategy_extract_seed_data($setup_user_id);
        $seed_keywords = isset($seed_data['seed_keywords']) && is_array($seed_data['seed_keywords']) ? array_values($seed_data['seed_keywords']) : array();
        $configured_competitors = isset($seed_data['competitors']) && is_array($seed_data['competitors']) ? array_values($seed_data['competitors']) : array();
        $configured_competitors = array_values(array_unique(array_filter(array_map(function($domain) {
            return $this->aysa_monitoring_extract_host((string) $domain);
        }, $configured_competitors), function($domain) {
            return $domain !== '';
        })));
        if (empty($seed_keywords)) {
            return $this->aysa_adjustments_get_competitor_identification_snapshot($project_id);
        }
        $home_host = $this->aysa_monitoring_extract_host((string) home_url('/'));
        $resolved_context = $this->aysa_monitoring_resolve_context($home_host !== '' ? $home_host : 'florariebragadiru.ro');
        $payload = array(
            'project_id' => intval($project_id),
            'seed_keywords' => array_values($seed_keywords),
            'active_competitors' => array_values($configured_competitors),
            'location_code' => intval($resolved_context['location_code'] ?? 2840),
            'language_code' => strtolower(trim((string) ($resolved_context['language_code'] ?? 'ro'))),
            'top_n' => 10,
            'own_domains' => array_values(array_filter(array_unique(array($home_host, trim((string) ($resolved_context['target'] ?? '')))))),
        );
        $result = $this->aysa_broker_request('POST', '/v1/competitors/identify/from-seeds', $payload, 180);
        if (!is_wp_error($result)) {
            $data = isset($result['data']) && is_array($result['data']) ? $result['data'] : $result;
            if (is_array($data) && !empty($data['ok']) && isset($data['unique_competitors'])) {
                $data = $this->aysa_keyword_v2_finalize_snapshot_contract($project_id, 'competitor_identification_v2', $data, 'unique_competitors');
                $this->aysa_competitor_identification_save_cached_payload($project_id, $payload, $data);
            }
        }
        return $this->aysa_adjustments_get_competitor_identification_snapshot($project_id);
    }

    private function aysa_adjustments_run_competitor_backlinks_sync($project_id, $setup_user_id = 0) {
        $this->handle_competitor_backlinks_gap_insights_for_workspace($project_id, $setup_user_id);
        return $this->aysa_adjustments_get_competitor_backlinks_snapshot($project_id);
    }

    private function handle_competitor_backlinks_gap_insights_for_workspace($project_id, $setup_user_id = 0) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return;
        }
        $home_host = $this->aysa_monitoring_extract_host((string) home_url('/'));
        $resolved_context = $this->aysa_monitoring_resolve_context($home_host !== '' ? $home_host : 'florariebragadiru.ro');
        $target = strtolower(trim((string) ($resolved_context['target'] ?? '')));
        if ($target !== '') {
            $target = $this->aysa_monitoring_extract_host($target);
        }
        if ($target === '') {
            $target = $home_host;
        }
        $seed_data = $this->aysa_keyword_strategy_extract_seed_data($setup_user_id > 0 ? $setup_user_id : get_current_user_id());
        $competitors = isset($seed_data['competitors']) && is_array($seed_data['competitors']) ? $seed_data['competitors'] : array();
        $normalized_competitors = array();
        foreach ($competitors as $raw) {
            $host = $this->aysa_monitoring_extract_host((string) $raw);
            if ($host === '') {
                $host = strtolower(trim((string) $raw));
            }
            $host = preg_replace('/^www\./', '', $host);
            if ($host === '' || $host === $target || !preg_match('/^[a-z0-9.-]+\.[a-z]{2,}$/', $host)) {
                continue;
            }
            $normalized_competitors[$host] = true;
        }

        if (empty($normalized_competitors)) {
            $identified_snapshot = $this->aysa_adjustments_get_competitor_identification_snapshot($project_id);
            $identified_rows = isset($identified_snapshot['rows']) && is_array($identified_snapshot['rows']) ? $identified_snapshot['rows'] : array();
            foreach ($identified_rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $host = strtolower(trim((string) ($row['domain'] ?? '')));
                $host = preg_replace('/^www\./', '', $host);
                if ($host === '' || $host === $target || !preg_match('/^[a-z0-9.-]+\.[a-z]{2,}$/', $host)) {
                    continue;
                }
                $normalized_competitors[$host] = true;
            }
        }

        $domains = array_merge(array($target), array_keys($normalized_competitors));
        $profiles = array();
        foreach ($domains as $domain) {
            $broker_result = $this->aysa_broker_request('POST', '/v1/dataforseo/backlink-audit', array(
                'target' => $domain,
                'domain' => $domain,
                'limit' => 150,
                'project_id' => $project_id,
            ), 180);
            if (is_wp_error($broker_result)) {
                continue;
            }
            $items = (isset($broker_result['items']) && is_array($broker_result['items'])) ? $broker_result['items'] : array();
            $snapshot = array(
                'domain' => $domain,
                'provider' => (string) ($broker_result['provider'] ?? 'dataforseo'),
                'generated_at' => current_time('mysql'),
                'count' => count($items),
                'items' => $items,
            );
            update_option($this->aysa_get_backlink_audit_domain_key($project_id, $domain), $snapshot, false);
            $profiles[$domain] = $this->aysa_backlinks_build_profile($domain, $items);
        }
        if (!empty($profiles)) {
            update_option('aysa_backlink_summary_' . intval($project_id), array(
                'generated_at' => current_time('mysql'),
                'items' => array_values($profiles),
            ), false);
        }
    }

    private function aysa_adjustments_execute_research_action($action, $project_id, $setup_user_id, $language) {
        $action = (string) $action;
        if (strpos($action, 'wallet_topup_buy:') === 0) {
            $credits_cents = intval(substr($action, strlen('wallet_topup_buy:')));
            return $this->aysa_adjustments_create_wallet_topup_order($project_id, $credits_cents, $language);
        }
        if (strpos($action, 'apply_preview_changes:') === 0) {
            $encoded = substr($action, strlen('apply_preview_changes:'));
            $decoded = base64_decode(strtr($encoded, '-_', '+/'), true);
            $payload = json_decode((string) $decoded, true);
            if (!is_array($payload)) {
                return array(
                    'module' => 'onpage_optimization',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not read the approved preview payload, so I stopped before queuing any changes.',
                        'Nu am putut citi payload-ul preview aprobat, așa că m-am oprit înainte să trimit modificările în review.',
                        'Не успях да прочета одобрения preview payload, затова спрях преди да изпратя промените за review.'
                    ),
                );
            }

            return $this->aysa_adjustments_apply_preview_changes_to_pending_reviews($project_id, $payload, $language);
        }

        if (strpos($action, 'technical_redirects_apply:') === 0) {
            $encoded = substr($action, strlen('technical_redirects_apply:'));
            $decoded = base64_decode(strtr($encoded, '-_', '+/'), true);
            $payload = json_decode((string) $decoded, true);
            if (!is_array($payload)) {
                return array(
                    'module' => 'technical_404_errors',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not read the redirect preview payload, so I stopped before changing any redirect rules.',
                        'Nu am putut citi payload-ul preview pentru redirecturi, așa că m-am oprit înainte să schimb vreo regulă.',
                        'Не успях да прочета preview payload-а за redirect-ите, затова спрях преди да променя каквито и да било правила.'
                    ),
                );
            }

            return $this->aysa_adjustments_apply_redirect_preview($payload, $language);
        }

        if ($action === 'monitoring_refresh_start') {
            $start = $this->aysa_adjustments_start_monitoring_refresh($project_id);
            $snapshot = $this->aysa_adjustments_get_monitoring_ranking_snapshot($project_id);
            $tracked = intval($snapshot['tracked_count'] ?? 0);
            $status = (string) ($snapshot['job_status'] ?? 'idle');
            $processed = intval($snapshot['job_processed'] ?? 0);
            $total = intval($snapshot['job_total'] ?? 0);
            $message = '';
            if (empty($start['ok'])) {
                $message = $this->aysa_adjustments_tr(
                    $language,
                    'I could not start the ranking refresh right now.',
                    'Nu am reușit să pornesc acum refresh-ul pentru ranking.',
                    'Не успях да стартирам refresh-а за ranking в момента.'
                );
            } elseif (!empty($start['already_running'])) {
                $message = match ($language) {
                    'ro' => 'Refresh-ul de ranking rulează deja. Jobul este `' . $status . '` și a procesat ' . $processed . ' din ' . $total . ' keyworduri urmărite.',
                    'bg' => 'Ranking refresh-ът вече върви. Job-ът е `' . $status . '` и е обработил ' . $processed . ' от ' . $total . ' следени keyword-а.',
                    default => 'The ranking refresh is already running. The job is `' . $status . '` and processed ' . $processed . ' out of ' . $total . ' tracked keywords.',
                };
            } else {
                $message = match ($language) {
                    'ro' => 'Am pornit refresh-ul de ranking în background pentru ' . $tracked . ' keyworduri urmărite. Îți pot verifica imediat și statusul jobului.',
                    'bg' => 'Стартирах ranking refresh във фонов режим за ' . $tracked . ' следени keyword-а. Мога веднага да проверя и статуса на job-а.',
                    default => 'I started the background ranking refresh for ' . $tracked . ' tracked keywords. I can also check the job status right away.',
                };
            }

            return array(
                'module' => 'monitoring_ranking',
                'assistant_message' => $message,
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'Check job status', 'Verifică statusul jobului', 'Провери статуса на job-а'),
                        'message' => '',
                        'ui_action' => 'open_artifact_monitoring_status',
                    ),
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See ranking rows', 'Vezi rândurile de ranking', 'Виж ranking редовете'),
                        'message' => '',
                        'ui_action' => 'open_artifact_monitoring_ranking',
                    ),
                ),
                'ui_action' => 'open_artifact_monitoring_status',
            );
        }
        if ($action === 'monitoring_refresh_status') {
            $snapshot = $this->aysa_adjustments_get_monitoring_ranking_snapshot($project_id);
            $status = (string) ($snapshot['job_status'] ?? 'idle');
            $processed = intval($snapshot['job_processed'] ?? 0);
            $total = intval($snapshot['job_total'] ?? 0);
            $failed = intval($snapshot['job_failed'] ?? 0);
            $stale = intval($snapshot['stale_count'] ?? 0);
            $message = match ($language) {
                'ro' => 'Status refresh ranking: `' . $status . '` (' . $processed . '/' . $total . '). Ai ' . $stale . ' keyworduri încă stale și ' . $failed . ' eșecuri în jobul curent.',
                'bg' => 'Статус на ranking refresh: `' . $status . '` (' . $processed . '/' . $total . '). Имаш ' . $stale . ' все още stale keyword-а и ' . $failed . ' неуспеха в текущия job.',
                default => 'Ranking refresh status: `' . $status . '` (' . $processed . '/' . $total . '). You still have ' . $stale . ' stale keywords and ' . $failed . ' failures in the current job.',
            };

            return array(
                'module' => 'monitoring_ranking',
                'assistant_message' => $message,
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See job status', 'Vezi statusul jobului', 'Виж статуса на job-а'),
                        'message' => '',
                        'ui_action' => 'open_artifact_monitoring_status',
                    ),
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See stale keywords', 'Vezi keywordurile stale', 'Виж stale keyword-ите'),
                        'message' => '',
                        'ui_action' => 'open_artifact_monitoring_stale',
                    ),
                ),
                'ui_action' => 'open_artifact_monitoring_status',
            );
        }
        if ($action === 'offpage_link_insertion_recommend') {
            return $this->aysa_adjustments_recommend_link_insertion_portals($project_id, $setup_user_id, $language);
        }
        if (strpos($action, 'offpage_link_insertion_place:') === 0) {
            $encoded = substr($action, strlen('offpage_link_insertion_place:'));
            $decoded = base64_decode(strtr($encoded, '-_', '+/'));
            $payload = json_decode((string) $decoded, true);
            if (!is_array($payload)) {
                return array(
                    'module' => 'offpage_link_building',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not understand the link insertion order details well enough to place the order.',
                        'Nu am reușit să înțeleg suficient de bine detaliile comenzii de link insertion ca să plasez comanda.',
                        'Не успях да разбера достатъчно добре детайлите за link insertion поръчката, за да я пусна.'
                    ),
                );
            }

            return $this->aysa_adjustments_place_link_insertion_order($project_id, $setup_user_id, $payload, $language);
        }
        if ($action === 'offpage_seo_article_recommend') {
            return $this->aysa_adjustments_recommend_article_portals($project_id, $setup_user_id, 'seo_article', $language);
        }
        if ($action === 'publisher_article_draft_open_latest') {
            $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/publisher-articles/list', array(
                'project_id' => intval($project_id),
                'status' => 'draft',
                'limit' => 1,
            ), 60);
            if (is_wp_error($broker_result)) {
                return array(
                    'module' => 'offpage_link_building',
                    'assistant_message' => $this->aysa_humanize_broker_error($broker_result, 'Could not load the latest publisher draft.'),
                );
            }
            $items = isset($broker_result['items']) && is_array($broker_result['items']) ? $broker_result['items'] : array();
            $draft = (!empty($items[0]) && is_array($items[0])) ? $items[0] : array();
            if (empty($draft)) {
                return array(
                    'module' => 'offpage_link_building',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I did not find any publisher draft yet.',
                        'Nu am găsit încă niciun draft pentru publisher.',
                        'Все още не намерих publisher чернова.'
                    ),
                    'ui_action' => 'open_artifact_offpage_publisher_drafts',
                );
            }

            return array(
                'module' => 'offpage_link_building',
                'assistant_message' => match ($language) {
                    'ro' => 'Am deschis draftul "' . sanitize_text_field((string) ($draft['title'] ?? 'Draft')) . '". Îl poți revizui și apoi aproba pentru publicare.',
                    'bg' => 'Отворих черновата "' . sanitize_text_field((string) ($draft['title'] ?? 'Draft')) . '". Можеш да я прегледаш и после да я одобриш за публикуване.',
                    default => 'I opened the draft "' . sanitize_text_field((string) ($draft['title'] ?? 'Draft')) . '". You can review it and then approve it for publishing.',
                },
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'Approve draft', 'Aprobă draftul', 'Одобри черновата'),
                        'message' => '',
                        'execution_action' => 'publisher_article_draft_approve:' . intval($draft['id'] ?? 0),
                    ),
                ),
                'ui_action' => 'open_artifact_offpage_publisher_drafts',
            );
        }
        if (strpos($action, 'publisher_article_draft_approve:') === 0) {
            $draft_id = intval(substr($action, strlen('publisher_article_draft_approve:')));
            if ($draft_id <= 0) {
                return array(
                    'module' => 'offpage_link_building',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not understand which draft should be approved.',
                        'Nu am reușit să înțeleg ce draft trebuie aprobat.',
                        'Не успях да разбера коя чернова трябва да бъде одобрена.'
                    ),
                );
            }
            $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/publisher-articles/approve', array(
                'draft_id' => $draft_id,
            ), 60);
            if (is_wp_error($broker_result)) {
                return array(
                    'module' => 'offpage_link_building',
                    'assistant_message' => $this->aysa_humanize_broker_error($broker_result, 'Could not approve the publisher draft.'),
                );
            }
            $draft = isset($broker_result['draft']) && is_array($broker_result['draft']) ? $broker_result['draft'] : array();
            return array(
                'module' => 'offpage_link_building',
                'assistant_message' => match ($language) {
                    'ro' => 'Am aprobat draftul pentru publicare. Îl mut automat în Published Articles după ce comanda devine live și apare URL-ul public.',
                    'bg' => 'Одобрих черновата за публикуване. Ще я преместя автоматично в Published Articles, след като поръчката стане live и се появи публичният URL.',
                    default => 'I approved the draft for publishing. I will move it automatically into Published Articles once the order goes live and a public URL appears.',
                },
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See published articles', 'Vezi articolele publicate', 'Виж публикуваните статии'),
                        'message' => '',
                        'ui_action' => 'open_artifact_offpage_publisher_published',
                    ),
                ),
                'ui_action' => 'open_artifact_offpage_publisher_published',
            );
        }
        if (strpos($action, 'offpage_seo_article_place:') === 0) {
            $encoded = substr($action, strlen('offpage_seo_article_place:'));
            $decoded = base64_decode(strtr($encoded, '-_', '+/'));
            $payload = json_decode((string) $decoded, true);
            if (!is_array($payload)) {
                return array(
                    'module' => 'offpage_link_building',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not understand the SEO article order details well enough to place the order.',
                        'Nu am reușit să înțeleg suficient de bine detaliile comenzii de SEO article ca să plasez comanda.',
                        'Не успях да разбера достатъчно добре детайлите за SEO article поръчката, за да я пусна.'
                    ),
                );
            }

            return $this->aysa_adjustments_place_article_order($project_id, $setup_user_id, $payload, 'seo_article', $language);
        }
        if ($action === 'offpage_professional_article_recommend') {
            return $this->aysa_adjustments_recommend_article_portals($project_id, $setup_user_id, 'professional_article', $language);
        }
        if (strpos($action, 'offpage_professional_article_place:') === 0) {
            $encoded = substr($action, strlen('offpage_professional_article_place:'));
            $decoded = base64_decode(strtr($encoded, '-_', '+/'));
            $payload = json_decode((string) $decoded, true);
            if (!is_array($payload)) {
                return array(
                    'module' => 'offpage_link_building',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not understand the professional article order details well enough to place the order.',
                        'Nu am reușit să înțeleg suficient de bine detaliile comenzii de professional article ca să plasez comanda.',
                        'Не успях да разбера достатъчно добре детайлите за professional article поръчката, за да я пусна.'
                    ),
                );
            }

            return $this->aysa_adjustments_place_article_order($project_id, $setup_user_id, $payload, 'professional_article', $language);
        }
        if ($action === 'offpage_seo_text_ads_recommend') {
            return $this->aysa_adjustments_recommend_seo_text_ads_portals($project_id, $setup_user_id, $language);
        }
        if (strpos($action, 'offpage_seo_text_ads_place:') === 0) {
            $encoded = substr($action, strlen('offpage_seo_text_ads_place:'));
            $decoded = base64_decode(strtr($encoded, '-_', '+/'));
            $payload = json_decode((string) $decoded, true);
            if (!is_array($payload)) {
                return array(
                    'module' => 'offpage_link_building',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not understand the SEO Text Ads order details well enough to place the order.',
                        'Nu am reușit să înțeleg suficient de bine detaliile comenzii SEO Text Ads ca să plasez comanda.',
                        'Не успях да разбера достатъчно добре детайлите за SEO Text Ads, за да пусна поръчката.'
                    ),
                );
            }

            return $this->aysa_adjustments_place_seo_text_ads_order($project_id, $setup_user_id, $payload, $language);
        }
        if (strpos($action, 'content_create_item:') === 0) {
            $encoded = substr($action, strlen('content_create_item:'));
            $decoded = base64_decode(strtr($encoded, '-_', '+/'));
            $payload = json_decode((string) $decoded, true);
            if (!is_array($payload)) {
                return array(
                    'module' => 'content_strategy',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not understand the content creation request well enough to execute it.',
                        'Nu am reușit să înțeleg suficient de bine cererea de creare conținut ca să o execut.',
                        'Не успях да разбера достатъчно добре заявката за създаване на съдържание, за да я изпълня.'
                    ),
                );
            }

            return $this->aysa_adjustments_create_content_item($project_id, $setup_user_id, $payload, $language);
        }
        if (strpos($action, 'content_optimize_item:') === 0) {
            $encoded = substr($action, strlen('content_optimize_item:'));
            $decoded = base64_decode(strtr($encoded, '-_', '+/'));
            $payload = json_decode((string) $decoded, true);
            if (!is_array($payload)) {
                return array(
                    'module' => 'onpage_optimization',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not understand the optimization request well enough to execute it.',
                        'Nu am reușit să înțeleg suficient de bine cererea de optimizare ca să o execut.',
                        'Не успях да разбера достатъчно добре заявката за оптимизация, за да я изпълня.'
                    ),
                );
            }

            return $this->aysa_adjustments_optimize_content_item($project_id, $payload, $language);
        }
        if (strpos($action, 'content_strategy_generate:') === 0) {
            $strategy_type = sanitize_key(substr($action, strlen('content_strategy_generate:')));
            if ($strategy_type === '') {
                $strategy_type = 'local_seo';
            }

            return $this->aysa_adjustments_run_content_strategy_generation($project_id, $setup_user_id, $strategy_type, array(), $language);
        }
        if (strpos($action, 'content_strategy_generate_custom:') === 0) {
            $encoded = substr($action, strlen('content_strategy_generate_custom:'));
            $decoded = base64_decode(strtr($encoded, '-_', '+/'));
            $payload = json_decode((string) $decoded, true);
            if (!is_array($payload)) {
                return array(
                    'module' => 'content_strategy',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not understand the approved content preview well enough to execute it.',
                        'Nu am reușit să înțeleg suficient de bine preview-ul aprobat de conținut ca să-l execut.',
                        'Не успях да разбера достатъчно добре одобреното content preview, за да го изпълня.'
                    ),
                );
            }

            $strategy_type = sanitize_key((string) ($payload['strategy_type'] ?? 'competitor_derived_titles'));
            $custom_payload = isset($payload['payload']) && is_array($payload['payload']) ? $payload['payload'] : array();
            if ($strategy_type === '') {
                $strategy_type = 'competitor_derived_titles';
            }

            return $this->aysa_adjustments_run_content_strategy_generation($project_id, $setup_user_id, $strategy_type, $custom_payload, $language);
        }
        if (strpos($action, 'content_strategy_delete_batch:') === 0) {
            $plan_batch = sanitize_text_field(substr($action, strlen('content_strategy_delete_batch:')));
            $result = $this->aysa_adjustments_delete_content_strategy_batch($project_id, $plan_batch);

            return array(
                'module' => 'content_strategy',
                'assistant_message' => match ($language) {
                    'ro' => 'Am șters batch-ul generat acum din planul de conținut. Am eliminat ' . intval($result['deleted'] ?? 0) . ' idei.',
                    'bg' => 'Изтрих току-що генерирания batch от content плана. Премахнах ' . intval($result['deleted'] ?? 0) . ' идеи.',
                    default => 'I removed the batch generated just now from the content plan. I deleted ' . intval($result['deleted'] ?? 0) . ' ideas.',
                },
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See content plan', 'Vezi planul de conținut', 'Виж content плана'),
                        'message' => '',
                        'ui_action' => 'open_artifact_content_strategy',
                    ),
                ),
                'state_meta' => array(
                    'last_generated_content_plan_batch' => '',
                ),
            );
        }
        if ($action === 'keyword_suggestions_refresh') {
            $start = $this->aysa_keyword_discovery_start_background_refresh($project_id, true);
            if (is_wp_error($start)) {
                return array(
                    'module' => 'keyword_research',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not start the keyword refresh right now.',
                        'Nu am reușit să pornesc acum refresh-ul de keyworduri.',
                        'Не успях да стартирам keyword refresh-а в момента.'
                    ) . ' ' . $start->get_error_message(),
                    'ui_action' => 'open_keyword_discovery',
                );
            }

            $status = isset($start['status']) && is_array($start['status']) ? $start['status'] : array();
            $pending_item = isset($start['pending_item']) && is_array($start['pending_item']) ? $start['pending_item'] : $this->aysa_keyword_discovery_build_pending_item($project_id);
            $already_running = !empty($start['already_running']);

            return array(
                'module' => 'keyword_research',
                'assistant_message' => $already_running ? match ($language) {
                    'ro' => 'Keyword refresh rulează deja în background. Îți arăt progresul aici și te anunț imediat când termină.',
                    'bg' => 'Keyword refresh вече върви във фонов режим. Ще ти показвам прогреса тук и ще те уведомя веднага щом приключи.',
                    default => 'The keyword refresh is already running in the background. I will show you the progress here and notify you as soon as it finishes.',
                } : match ($language) {
                    'ro' => 'Am pornit în background refresh-ul de keyworduri. Poți continua conversația, iar eu îți arăt progresul și revin imediat ce am setul nou pregătit.',
                    'bg' => 'Стартирах keyword refresh-а във фонов режим. Можеш да продължиш разговора, а аз ще ти показвам прогреса и ще се върна веднага щом новият набор е готов.',
                    default => 'I started the keyword refresh in the background. You can keep going, and I will show you the progress and come back as soon as the new set is ready.',
                },
                'suggested_actions' => array_values(array_filter(array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See refresh status', 'Vezi statusul refresh-ului', 'Виж refresh статуса'),
                        'message' => '',
                        'ui_action' => 'open_keyword_discovery',
                    ),
                    !$already_running ? array(
                        'label' => $this->aysa_adjustments_tr($language, 'Open Keyword Discovery', 'Deschide Keyword Discovery', 'Отвори Keyword Discovery'),
                        'message' => '',
                        'ui_action' => 'open_keyword_discovery',
                    ) : array(),
                ))),
                'ui_action' => 'open_keyword_discovery',
                'state_meta' => array(
                    'pending_generated_item_id' => intval($pending_item['post_id'] ?? $project_id),
                    'pending_generated_item_title' => (string) ($pending_item['title'] ?? 'Keyword refresh'),
                    'pending_generated_item_type' => (string) ($pending_item['item_type'] ?? 'keyword_refresh'),
                    'pending_generated_item_key' => (string) ($pending_item['item_key'] ?? ('keyword_refresh:' . $project_id)),
                    'keyword_refresh_status' => (string) ($status['status'] ?? 'running'),
                ),
            );
        }
        if ($action === 'keyword_refresh_status') {
            $status = $this->aysa_keyword_discovery_get_status($project_id);
            return array(
                'module' => 'keyword_research',
                'assistant_message' => match ($language) {
                    'ro' => 'Status keyword refresh: `' . (string) ($status['status'] ?? 'idle') . '` (' . intval($status['processed'] ?? 0) . '/' . intval($status['total'] ?? 0) . '). Am staged ' . intval($status['staged'] ?? 0) . ', inserate ' . intval($status['inserted'] ?? 0) . ', actualizate ' . intval($status['updated'] ?? 0) . '.' . ((string) ($status['metrics_notice'] ?? '') !== '' ? ' ' . (string) $status['metrics_notice'] : ''),
                    'bg' => 'Статус на keyword refresh: `' . (string) ($status['status'] ?? 'idle') . '` (' . intval($status['processed'] ?? 0) . '/' . intval($status['total'] ?? 0) . '). Staged ' . intval($status['staged'] ?? 0) . ', нови ' . intval($status['inserted'] ?? 0) . ', обновени ' . intval($status['updated'] ?? 0) . '.' . ((string) ($status['metrics_notice'] ?? '') !== '' ? ' ' . (string) $status['metrics_notice'] : ''),
                    default => 'Keyword refresh status: `' . (string) ($status['status'] ?? 'idle') . '` (' . intval($status['processed'] ?? 0) . '/' . intval($status['total'] ?? 0) . '). Staged ' . intval($status['staged'] ?? 0) . ', inserted ' . intval($status['inserted'] ?? 0) . ', updated ' . intval($status['updated'] ?? 0) . '.' . ((string) ($status['metrics_notice'] ?? '') !== '' ? ' ' . (string) $status['metrics_notice'] : ''),
                },
                'ui_action' => 'open_keyword_discovery',
            );
        }
        if ($action === 'keyword_apply_staged') {
            $snapshot = get_option($this->aysa_keyword_discovery_snapshot_key($project_id), array());
            $keywords = (is_array($snapshot) && isset($snapshot['keywords']) && is_array($snapshot['keywords'])) ? $snapshot['keywords'] : array();
            if (empty($keywords)) {
                return array(
                    'module' => 'keyword_research',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'There are no staged keyword suggestions to apply right now.',
                        'Nu există acum sugestii staged de keyworduri pe care să le aplic.',
                        'В момента няма staged предложения за keywords, които да приложа.'
                    ),
                    'ui_action' => 'open_keyword_discovery',
                );
            }

            $persist = $this->aysa_keyword_discovery_persist_keywords($project_id, $keywords);
            $this->aysa_adjustments_add_pending_approval(
                $project_id,
                'keyword_research_review',
                'Keyword research is ready for review',
                match ($language) {
                    'ro' => 'Keyword research este pregatit. Am actualizat datasetul local si am nevoie de acordul tau ca sa stabilim setul urmarit, targeturile si pasii urmatori.',
                    'bg' => 'Keyword research е готов. Обнових локалния dataset и ми трябва твоето одобрение, за да зададем tracked набора, target-ите и следващите стъпки.',
                    default => 'Keyword research is ready. I updated the local dataset and now I need your approval to set the tracked set, the targets, and the next steps.',
                },
                'open_keyword_discovery'
            );
            return array(
                'module' => 'keyword_research',
                'assistant_message' => match ($language) {
                    'ro' => 'Am aplicat staged keywords în datasetul local. Inserate: ' . intval($persist['inserted'] ?? 0) . ', actualizate: ' . intval($persist['updated'] ?? 0) . ', total procesate: ' . count($keywords) . '.',
                    'bg' => 'Приложих staged keywords в локалния dataset. Нови: ' . intval($persist['inserted'] ?? 0) . ', обновени: ' . intval($persist['updated'] ?? 0) . ', общо обработени: ' . count($keywords) . '.',
                    default => 'I applied the staged keywords into the local dataset. Inserted: ' . intval($persist['inserted'] ?? 0) . ', updated: ' . intval($persist['updated'] ?? 0) . ', total processed: ' . count($keywords) . '.',
                },
                'ui_action' => 'open_keyword_discovery',
            );
        }
        if (strpos($action, 'keyword_association_upsert:') === 0) {
            $encoded = substr($action, strlen('keyword_association_upsert:'));
            $decoded = base64_decode(strtr($encoded, '-_', '+/'), true);
            $payload = json_decode((string) $decoded, true);
            if (!is_array($payload)) {
                return array(
                    'module' => 'keyword_research',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not understand the keyword mapping payload well enough to save the association.',
                        'Nu am reusit sa inteleg suficient de bine payload-ul de mapare ca sa salvez asocierea.',
                        'Не успях да разбера достатъчно добре payload-а за mapping, за да запазя свързването.'
                    ),
                    'ui_action' => 'open_artifact_matches',
                );
            }

            return $this->aysa_adjustments_execute_keyword_association_upsert($project_id, $payload, $language);
        }
        if (strpos($action, 'keyword_target_position_set:') === 0) {
            $encoded = substr($action, strlen('keyword_target_position_set:'));
            $decoded = base64_decode(strtr($encoded, '-_', '+/'), true);
            $payload = json_decode((string) $decoded, true);
            if (!is_array($payload)) {
                return array(
                    'module' => 'keyword_research',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not understand the keyword target payload well enough to update the desired position.',
                        'Nu am reusit sa inteleg suficient de bine payload-ul de target ca sa actualizez pozitia dorita.',
                        'Не успях да разбера достатъчно добре payload-а за target, за да обновя desired position.'
                    ),
                    'ui_action' => 'open_artifact_matches',
                );
            }

            return $this->aysa_adjustments_execute_keyword_target_position_set($project_id, $payload, $language);
        }
        if (strpos($action, 'keyword_primary_set:') === 0) {
            $encoded = substr($action, strlen('keyword_primary_set:'));
            $decoded = base64_decode(strtr($encoded, '-_', '+/'), true);
            $payload = json_decode((string) $decoded, true);
            if (!is_array($payload)) {
                return array(
                    'module' => 'keyword_research',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not understand the primary keyword payload well enough to save it.',
                        'Nu am reusit sa inteleg suficient de bine payload-ul pentru keywordul principal ca sa il salvez.',
                        'Не успях да разбера достатъчно добре payload-а за primary keyword, за да го запазя.'
                    ),
                    'ui_action' => 'open_keyword_discovery',
                );
            }

            return $this->aysa_adjustments_execute_keyword_primary_set($project_id, $payload, $language);
        }
        if (strpos($action, 'keyword_supporting_add:') === 0) {
            $encoded = substr($action, strlen('keyword_supporting_add:'));
            $decoded = base64_decode(strtr($encoded, '-_', '+/'), true);
            $payload = json_decode((string) $decoded, true);
            if (!is_array($payload)) {
                return array(
                    'module' => 'keyword_research',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not understand the supporting keyword payload well enough to save it.',
                        'Nu am reusit sa inteleg suficient de bine payload-ul pentru keywordul de suport ca sa il salvez.',
                        'Не успях да разбера достатъчно добре payload-а за supporting keyword, за да го запазя.'
                    ),
                    'ui_action' => 'open_keyword_discovery',
                );
            }

            return $this->aysa_adjustments_execute_keyword_supporting_add($project_id, $payload, $language);
        }
        if (strpos($action, 'keyword_cluster_map_to_entity:') === 0) {
            $encoded = substr($action, strlen('keyword_cluster_map_to_entity:'));
            $decoded = base64_decode(strtr($encoded, '-_', '+/'), true);
            $payload = json_decode((string) $decoded, true);
            if (!is_array($payload)) {
                return array(
                    'module' => 'keyword_research',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not understand the keyword cluster payload well enough to save the cluster mapping.',
                        'Nu am reusit sa inteleg suficient de bine payload-ul clusterului ca sa salvez maparea clusterului.',
                        'Не успях да разбера достатъчно добре payload-а на cluster-а, за да запазя mapping-а му.'
                    ),
                    'ui_action' => 'open_keyword_discovery',
                );
            }

            return $this->aysa_adjustments_execute_keyword_cluster_map_to_entity($project_id, $payload, $language);
        }
        if (strpos($action, 'keyword_tracking_update:') === 0) {
            $encoded = substr($action, strlen('keyword_tracking_update:'));
            $decoded = base64_decode(strtr($encoded, '-_', '+/'), true);
            $payload = json_decode((string) $decoded, true);
            if (!is_array($payload)) {
                return array(
                    'module' => 'keyword_research',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not understand the tracked keywords payload well enough to save the tracking plan.',
                        'Nu am reusit sa inteleg suficient de bine payload-ul pentru keywordurile urmarite ca sa salvez planul de tracking.',
                        'Не успях да разбера достатъчно добре payload-а за tracked keywords, за да запазя tracking плана.'
                    ),
                    'ui_action' => 'open_keyword_discovery',
                );
            }

            return $this->aysa_adjustments_execute_keyword_tracking_update($project_id, $payload, $language);
        }
        if ($action === 'keyword_strategy_show') {
            $option_key = $this->aysa_keyword_strategy_option_key($project_id, $setup_user_id);
            $saved = get_option($option_key, array());
            $items = (is_array($saved) && isset($saved['items']) && is_array($saved['items'])) ? $saved['items'] : array();
            $summary = (is_array($saved) && isset($saved['summary']) && is_array($saved['summary'])) ? $saved['summary'] : array();
            if (empty($items)) {
                return array(
                    'module' => 'keyword_research',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I do not have a saved keyword strategy yet. I can generate one from the current research if you want.',
                        'Nu am încă o strategie salvată de keyworduri. Pot să generez una din research-ul curent dacă vrei.',
                        'Все още нямам записана стратегия за keywords. Мога да генерирам такава от текущия research, ако искаш.'
                    ),
                    'suggested_actions' => array(
                        array(
                            'label' => $this->aysa_adjustments_tr($language, 'Generate strategy', 'Generează strategia', 'Генерирай стратегия'),
                            'message' => '',
                            'execution_action' => 'keyword_strategy_generate',
                        ),
                    ),
                    'ui_action' => 'open_keyword_discovery',
                );
            }

            return array(
                'module' => 'keyword_research',
                'assistant_message' => match ($language) {
                    'ro' => 'Strategia de keyworduri este pregătită. Am ' . count($items) . ' keyworduri în plan, cu ' . intval($summary['associate_existing'] ?? 0) . ' de asociat pe pagini existente, ' . intval($summary['enhance_existing'] ?? 0) . ' de consolidat și ' . intval($summary['create_new_page'] ?? 0) . ' care sugerează pagini noi.',
                    'bg' => 'Keyword стратегията е готова. Имам ' . count($items) . ' keywords в плана, с ' . intval($summary['associate_existing'] ?? 0) . ' за свързване към съществуващи страници, ' . intval($summary['enhance_existing'] ?? 0) . ' за подсилване и ' . intval($summary['create_new_page'] ?? 0) . ' които подсказват нови страници.',
                    default => 'The keyword strategy is ready. I have ' . count($items) . ' keywords in the plan, with ' . intval($summary['associate_existing'] ?? 0) . ' to map to existing pages, ' . intval($summary['enhance_existing'] ?? 0) . ' to strengthen, and ' . intval($summary['create_new_page'] ?? 0) . ' suggesting new pages.',
                },
                'ui_action' => 'open_keyword_discovery',
            );
        }
        if ($action === 'keyword_strategy_generate') {
            $seed_data = $this->aysa_keyword_strategy_extract_seed_data($setup_user_id);
            $seed_keywords = is_array($seed_data['seed_keywords'] ?? null) ? $seed_data['seed_keywords'] : array();
            $competitors = is_array($seed_data['competitors'] ?? null) ? $seed_data['competitors'] : array();
            if (empty($seed_keywords)) {
                return array(
                    'module' => 'keyword_research',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not generate the keyword strategy because I do not have seed keywords yet.',
                        'Nu am putut genera strategia de keyworduri pentru că nu am încă seed keywords.',
                        'Не успях да генерирам keyword стратегията, защото все още нямам seed keywords.'
                    ),
                    'ui_action' => 'open_keyword_discovery',
                );
            }

            $context = $this->aysa_keyword_strategy_get_context($setup_user_id);
            $strategy = array();
            $used_remote = false;
            $broker_payload = array(
                'project_id' => $project_id,
                'seed_keywords' => $seed_keywords,
                'competitors' => $competitors,
                'targeted_country' => (string) ($context['targeted_country'] ?? ''),
                'targeted_region' => (string) ($context['targeted_region'] ?? ''),
                'targeted_city' => (string) ($context['targeted_city'] ?? ''),
                'primary_language' => (string) ($context['primary_language'] ?? ''),
                'website_type' => (string) ($context['website_type'] ?? ''),
                'industry' => (string) ($context['industry'] ?? ''),
                'max_keywords' => 250,
            );

            $remote_result = $this->aysa_broker_request('POST', '/v1/keywords/strategy/from-seeds', $broker_payload, 120);
            if (!is_wp_error($remote_result) && is_array($remote_result)) {
                $remote_data = isset($remote_result['data']) && is_array($remote_result['data']) ? $remote_result['data'] : $remote_result;
                $remote_items = isset($remote_data['items']) && is_array($remote_data['items']) ? $remote_data['items'] : array();
                if (!empty($remote_items)) {
                    $strategy = array(
                        'project_id' => intval($project_id),
                        'user_id' => intval($setup_user_id),
                        'generated_at' => current_time('mysql'),
                        'source' => 'broker',
                        'seed_keywords' => array_values($seed_keywords),
                        'competitors' => array_values($competitors),
                        'context' => $context,
                        'summary' => isset($remote_data['summary']) && is_array($remote_data['summary']) ? $remote_data['summary'] : array(),
                        'items' => array_values($remote_items),
                    );
                    $used_remote = true;
                }
            }

            if (empty($strategy)) {
                $strategy = $this->aysa_keyword_strategy_build_local_plan($project_id, $setup_user_id, $seed_keywords, $competitors, $context);
            }

            update_option($this->aysa_keyword_strategy_option_key($project_id, $setup_user_id), $strategy, false);

            return array(
                'module' => 'keyword_research',
                'assistant_message' => match ($language) {
                    'ro' => 'Am generat strategia de keyworduri cu ' . count((array) ($strategy['items'] ?? array())) . ' keyworduri prioritizate. Sursa: ' . ($used_remote ? 'broker' : 'local fallback') . '.',
                    'bg' => 'Генерирах keyword стратегията с ' . count((array) ($strategy['items'] ?? array())) . ' приоритизирани keywords. Източник: ' . ($used_remote ? 'broker' : 'local fallback') . '.',
                    default => 'I generated the keyword strategy with ' . count((array) ($strategy['items'] ?? array())) . ' prioritized keywords. Source: ' . ($used_remote ? 'broker' : 'local fallback') . '.',
                },
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'Open Keyword Discovery', 'Deschide Keyword Discovery', 'Отвори Keyword Discovery'),
                        'message' => '',
                        'ui_action' => 'open_keyword_discovery',
                    ),
                ),
                'ui_action' => 'open_keyword_discovery',
            );
        }
        return match ($action) {
            'keyword_research_sync' => array(
                'module' => 'keyword_research',
                'assistant_message' => $this->aysa_adjustments_build_keyword_research_completion_message(
                    $this->aysa_adjustments_run_keyword_research_sync($project_id, $setup_user_id),
                    $language
                ),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'Continue', 'Continuă', 'Продължи'),
                        'message' => $this->aysa_adjustments_tr($language, 'Continue to the next step.', 'Continuă la pasul următor.', 'Продължи към следващата стъпка.'),
                    ),
                ),
            ),
            'competitor_gap_sync' => array(
                'module' => 'competitor_gap',
                'assistant_message' => (function() use ($project_id, $setup_user_id, $language) {
                    $snapshot = $this->aysa_adjustments_run_competitor_gap_sync($project_id, $setup_user_id);
                    return $this->aysa_adjustments_build_competitor_gap_completion_message($snapshot, $language);
                })(),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See competitor gap', 'Vezi competitor gap', 'Виж competitor gap'),
                        'message' => '',
                        'ui_action' => 'open_artifact_competitor_gap',
                    ),
                ),
                'ui_action' => 'open_artifact_competitor_gap',
            ),
            'relevant_keywords_sync' => $this->aysa_adjustments_run_relevant_keywords_sync($project_id, $setup_user_id, $language),
            'competitor_identification_sync' => array(
                'module' => 'competitor_identification',
                'assistant_message' => (function() use ($project_id, $setup_user_id, $language) {
                    $snapshot = $this->aysa_adjustments_run_competitor_identification_sync($project_id, $setup_user_id);
                    return $this->aysa_adjustments_build_competitor_identification_completion_message($snapshot, $language);
                })(),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See competitors', 'Vezi competitorii', 'Виж конкурентите'),
                        'message' => '',
                        'ui_action' => 'open_artifact_competitor_identification',
                    ),
                ),
                'ui_action' => 'open_artifact_competitor_identification',
            ),
            'competitor_backlinks_sync' => array(
                'module' => 'competitor_backlinks',
                'assistant_message' => (function() use ($project_id, $setup_user_id, $language) {
                    $snapshot = $this->aysa_adjustments_run_competitor_backlinks_sync($project_id, $setup_user_id);
                    return $this->aysa_adjustments_build_competitor_backlinks_completion_message($snapshot, $language);
                })(),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See backlinks', 'Vezi backlinks', 'Виж backlinks'),
                        'message' => '',
                        'ui_action' => 'open_artifact_competitor_backlinks',
                    ),
                ),
                'ui_action' => 'open_artifact_competitor_backlinks',
            ),
            'offpage_backlink_audit_refresh' => array(
                'module' => 'offpage_backlink_audit',
                'assistant_message' => (function() use ($project_id, $language) {
                    $target = $this->aysa_monitoring_extract_host((string) home_url('/'));
                    if ($target === '') {
                        $target = 'florariebragadiru.ro';
                    }
                    $this->aysa_adjustments_run_offpage_backlink_audit($project_id, $target, 100, true);
                    $snapshot = $this->aysa_adjustments_get_offpage_backlink_audit_snapshot($project_id);
                    return $this->aysa_adjustments_build_offpage_backlink_audit_completion_message($snapshot, $language);
                })(),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See backlink audit', 'Vezi auditul backlinkurilor', 'Виж backlink audit-а'),
                        'message' => '',
                        'ui_action' => 'open_artifact_offpage_backlink_audit',
                    ),
                ),
                'ui_action' => 'open_artifact_offpage_backlink_audit',
            ),
            'offpage_link_building_refresh' => array(
                'module' => 'offpage_link_building',
                'assistant_message' => (function() use ($project_id, $language) {
                    $result = $this->aysa_fetch_adverlink_link_issues($project_id);
                    if (!is_wp_error($result)) {
                        $items = (is_array($result) && isset($result['items']) && is_array($result['items'])) ? $result['items'] : array();
                        $this->aysa_save_adverlink_link_issues($project_id, $items, 'manual');
                    }
                    $snapshot = $this->aysa_adjustments_get_offpage_link_building_snapshot($project_id);
                    $pending = intval($snapshot['pending_orders'] ?? 0);
                    $completed = intval($snapshot['completed_orders'] ?? 0);
                    $issues = intval($snapshot['issues_count'] ?? 0);
                    return $this->aysa_adjustments_tr(
                        $language,
                        'I refreshed the off-page order tracking. Right now I see ' . $pending . ' pending orders, ' . $completed . ' completed placements, and ' . $issues . ' link issues worth checking.',
                        'Am reîmprospătat tracking-ul pentru comenzile off-page. Acum văd ' . $pending . ' comenzi în lucru, ' . $completed . ' plasări finalizate și ' . $issues . ' probleme de linkuri care merită verificate.',
                        'Опресних tracking-а за off-page поръчките. В момента виждам ' . $pending . ' чакащи поръчки, ' . $completed . ' завършени публикации и ' . $issues . ' link проблема, които си струва да бъдат проверени.'
                    );
                })(),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See orders', 'Vezi comenzile', 'Виж поръчките'),
                        'message' => '',
                        'ui_action' => 'open_artifact_offpage_link_building_orders',
                    ),
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See link issues', 'Vezi problemele de linkuri', 'Виж проблемите с линковете'),
                        'message' => '',
                        'ui_action' => 'open_artifact_offpage_link_building_issues',
                    ),
                ),
                'ui_action' => 'open_artifact_offpage_link_building_orders',
            ),
            'missing_pages_refresh' => array(
                'module' => 'missing_pages',
                'assistant_message' => (function() use ($project_id, $language) {
                    $snapshot = $this->aysa_adjustments_get_missing_pages_snapshot($project_id);
                    return $this->aysa_adjustments_build_missing_pages_completion_message($snapshot, $language);
                })(),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See missing pages', 'Vezi paginile lipsă', 'Виж липсващите страници'),
                        'message' => '',
                        'ui_action' => 'open_artifact_missing_pages',
                    ),
                ),
                'ui_action' => 'open_artifact_missing_pages',
            ),
            'missing_pages_send_to_plan' => array(
                'module' => 'missing_pages',
                'assistant_message' => (function() use ($project_id, $language) {
                    $snapshot = $this->aysa_adjustments_get_missing_pages_snapshot($project_id);
                    $items = isset($snapshot['rows']) && is_array($snapshot['rows']) ? array_values($snapshot['rows']) : array();
                    $result = $this->aysa_missing_pages_send_items_to_plan($project_id, $items);
                    $added = intval($result['added'] ?? 0);
                    $existing = intval($result['existing'] ?? 0);
                    $errors = intval($result['errors'] ?? 0);

                    return $this->aysa_adjustments_tr(
                        $language,
                        'I moved the missing page ideas into the content plan. I added ' . $added . ' new planning items, ' . $existing . ' were already there, and ' . $errors . ' could not be saved.',
                        'Am trimis ideile de pagini lipsă în planul de conținut. Am adăugat ' . $added . ' itemi noi în plan, ' . $existing . ' existau deja, iar ' . $errors . ' nu au putut fi salvate.',
                        'Изпратих идеите за липсващи страници към content плана. Добавих ' . $added . ' нови елемента в плана, ' . $existing . ' вече съществуваха, а ' . $errors . ' не можаха да бъдат запазени.'
                    );
                })(),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See content plan', 'Vezi planul de conținut', 'Виж content плана'),
                        'message' => '',
                        'ui_action' => 'open_artifact_content_strategy',
                    ),
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See missing pages', 'Vezi paginile lipsă', 'Виж липсващите страници'),
                        'message' => '',
                        'ui_action' => 'open_artifact_missing_pages',
                    ),
                ),
                'ui_action' => 'open_artifact_content_strategy',
            ),
            'content_strategy_refresh' => array(
                'module' => 'content_strategy',
                'assistant_message' => (function() use ($project_id, $language) {
                    $snapshot = $this->aysa_adjustments_get_content_strategy_snapshot($project_id);
                    return $this->aysa_adjustments_build_content_strategy_completion_message($snapshot, $language);
                })(),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See content strategy', 'Vezi content strategy', 'Виж content strategy'),
                        'message' => '',
                        'ui_action' => 'open_artifact_content_strategy',
                    ),
                ),
                'ui_action' => 'open_artifact_content_strategy',
            ),
            'technical_site_audit_start' => array(
                'module' => 'technical_site_audit',
                'assistant_message' => (function() use ($project_id, $language) {
                    $snapshot = $this->aysa_adjustments_run_technical_site_audit_refresh($project_id, true);
                    return $this->aysa_adjustments_build_technical_site_audit_completion_message($snapshot, $language, true);
                })(),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See site audit', 'Vezi auditul tehnic', 'Виж site audit-а'),
                        'message' => '',
                        'ui_action' => 'open_artifact_technical_site_audit',
                    ),
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See issues', 'Vezi problemele', 'Виж проблемите'),
                        'message' => '',
                        'ui_action' => 'open_artifact_technical_site_audit_issues',
                    ),
                ),
            ),
            'technical_site_audit_refresh' => array(
                'module' => 'technical_site_audit',
                'assistant_message' => (function() use ($project_id, $language) {
                    $snapshot = $this->aysa_adjustments_run_technical_site_audit_refresh($project_id, false);
                    return $this->aysa_adjustments_build_technical_site_audit_completion_message($snapshot, $language, false);
                })(),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See site audit', 'Vezi auditul tehnic', 'Виж site audit-а'),
                        'message' => '',
                        'ui_action' => 'open_artifact_technical_site_audit',
                    ),
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See crawled pages', 'Vezi paginile crawl-ate', 'Виж обходените страници'),
                        'message' => '',
                        'ui_action' => 'open_artifact_technical_site_audit_pages',
                    ),
                ),
            ),
            'technical_pagespeed_refresh' => array(
                'module' => 'technical_pagespeed',
                'assistant_message' => (function() use ($project_id, $language) {
                    $snapshot = $this->aysa_adjustments_run_technical_pagespeed_refresh($project_id, true);
                    return $this->aysa_adjustments_build_technical_pagespeed_completion_message($snapshot, $language);
                })(),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See PageSpeed', 'Vezi PageSpeed', 'Виж PageSpeed'),
                        'message' => '',
                        'ui_action' => 'open_artifact_technical_pagespeed',
                    ),
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See opportunities', 'Vezi oportunitățile', 'Виж възможностите'),
                        'message' => '',
                        'ui_action' => 'open_artifact_technical_pagespeed_opportunities',
                    ),
                ),
                'ui_action' => 'open_artifact_technical_pagespeed',
            ),
            'technical_sitemap_robots_refresh' => array(
                'module' => 'technical_sitemap_robots',
                'assistant_message' => (function() use ($project_id, $language) {
                    $snapshot = $this->aysa_adjustments_run_technical_sitemap_robots_refresh($project_id, true);
                    return $this->aysa_adjustments_build_technical_sitemap_robots_completion_message($snapshot, $language);
                })(),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See checks', 'Vezi verificările', 'Виж проверките'),
                        'message' => '',
                        'ui_action' => 'open_artifact_technical_sitemap_robots_checks',
                    ),
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See robots.txt', 'Vezi robots.txt', 'Виж robots.txt'),
                        'message' => '',
                        'ui_action' => 'open_artifact_technical_sitemap_robots_file',
                    ),
                ),
                'ui_action' => 'open_artifact_technical_sitemap_robots',
            ),
            'technical_internal_linking_preview' => array(
                'module' => 'technical_internal_linking',
                'assistant_message' => (function() use ($project_id, $language) {
                    $result = $this->aysa_adjustments_run_technical_internal_linking_preview($project_id);
                    return $this->aysa_adjustments_build_technical_internal_linking_preview_message($result, $language);
                })(),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See internal linking opportunities', 'Vezi oportunitățile de linkuri interne', 'Виж възможностите за вътрешни линкове'),
                        'message' => '',
                        'ui_action' => 'open_artifact_technical_internal_linking_preview',
                    ),
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'Apply internal links', 'Aplică linkurile interne', 'Приложи вътрешните линкове'),
                        'message' => $this->aysa_adjustments_tr($language, 'Apply the internal links now.', 'Aplică linkurile interne acum.', 'Приложи вътрешните линкове сега.'),
                    ),
                ),
                'ui_action' => 'open_artifact_technical_internal_linking_preview',
            ),
            'technical_internal_linking_process' => array(
                'module' => 'technical_internal_linking',
                'assistant_message' => (function() use ($project_id, $language) {
                    $result = $this->aysa_adjustments_run_technical_internal_linking_process($project_id);
                    return $this->aysa_adjustments_build_technical_internal_linking_process_message($result, $language);
                })(),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See internal linking', 'Vezi linkurile interne', 'Виж вътрешните линкове'),
                        'message' => '',
                        'ui_action' => 'open_artifact_technical_internal_linking',
                    ),
                ),
                'ui_action' => 'open_artifact_technical_internal_linking',
            ),
            'onpage_optimize_needed' => array(
                'module' => 'onpage_optimization',
                'assistant_message' => (function() use ($project_id, $language) {
                    $result = $this->aysa_adjustments_run_onpage_optimize_needed($project_id);
                    return $this->aysa_adjustments_build_onpage_optimize_needed_message($result, $language);
                })(),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See on-page pages', 'Vezi paginile on-page', 'Виж on-page страниците'),
                        'message' => '',
                        'ui_action' => 'open_artifact_onpage_optimization',
                    ),
                ),
                'ui_action' => 'open_artifact_onpage_optimization',
            ),
            'technical_robots_unblock' => array(
                'module' => 'technical_sitemap_robots',
                'assistant_message' => (function() use ($project_id, $language) {
                    $target_url = $this->aysa_technical_resolve_target_url();
                    $sitemaps = $this->aysa_technical_fetch_sitemap_candidates($target_url);
                    $primary_sitemap = '';
                    foreach ($sitemaps as $row) {
                        if (!empty($row['reachable']) && !empty($row['url'])) {
                            $primary_sitemap = (string) $row['url'];
                            break;
                        }
                    }
                    $content = $this->aysa_technical_robots_default_content($target_url, $primary_sitemap);
                    $saved = $this->aysa_technical_save_robots_content($content);
                    if (is_wp_error($saved)) {
                        return $this->aysa_adjustments_tr(
                            $language,
                            'I could not update robots.txt automatically. Please check file permissions in WordPress root and try again.',
                            'Nu am putut actualiza automat robots.txt. Verifică permisiunile fișierului din rădăcina WordPress și încearcă din nou.',
                            'Не успях да обновя robots.txt автоматично. Провери правата на файла в WordPress root и опитай отново.'
                        );
                    }
                    $audit = $this->aysa_technical_build_sitemap_robots_audit($target_url);
                    $state = array(
                        'project_id' => $project_id,
                        'updated_at' => current_time('mysql'),
                        'updated_at_unix' => time(),
                        'data' => $audit,
                    );
                    update_option($this->aysa_technical_sitemap_robots_state_key($project_id), $state, false);
                    $this->aysa_wallet_track_action_usage(
                        $project_id,
                        'aysa_technical_robots_restore_default',
                        'technical_robots_unblock',
                        array(
                            'module' => 'technical_optimization',
                            'target_url' => $target_url,
                            'source' => 'adjustments_agent',
                        ),
                        12,
                        '/v1/technical/robots/restore-default'
                    );
                    return $this->aysa_adjustments_tr(
                        $language,
                        'I updated robots.txt so search engines can crawl the site again. I also refreshed the sitemap and robots checks for you.',
                        'Am actualizat robots.txt astfel încât motoarele de căutare să poată crawl-a din nou site-ul. Am reîmprospătat și verificările pentru sitemap și robots.',
                        'Обнових robots.txt така, че търсачките отново да могат да обхождат сайта. Опресних и проверките за sitemap и robots.'
                    );
                })(),
                'suggested_actions' => array(
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See checks', 'Vezi verificările', 'Виж проверките'),
                        'message' => '',
                        'ui_action' => 'open_artifact_technical_sitemap_robots_checks',
                    ),
                    array(
                        'label' => $this->aysa_adjustments_tr($language, 'See robots.txt', 'Vezi robots.txt', 'Виж robots.txt'),
                        'message' => '',
                        'ui_action' => 'open_artifact_technical_sitemap_robots_file',
                    ),
                ),
                'ui_action' => 'open_artifact_technical_sitemap_robots',
            ),
            default => array(),
        };
    }

    private function aysa_adjustments_run_content_strategy_generation($project_id, $setup_user_id, $strategy_type, $custom_payload, $language) {
        $project_id = intval($project_id);
        $setup_user_id = intval($setup_user_id);
        $strategy_type = sanitize_key((string) $strategy_type);
        $custom_payload = is_array($custom_payload) ? $custom_payload : array();

        if ($strategy_type === '') {
            $strategy_type = 'local_seo';
        }

        $context_bundle = $this->aysa_collect_content_strategy_context_bundle($project_id, $setup_user_id);
        $result = $this->aysa_broker_request('POST', '/v1/content-strategy/generate', array(
            'project_id' => $project_id,
            'strategy_type' => $strategy_type,
            'raw_context' => $context_bundle,
            'payload_overrides' => $custom_payload,
        ), 120);
        if (is_wp_error($result)) {
            return array(
                'module' => 'content_strategy',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I could not generate the content plan right now. We can try again in a moment.',
                    'Nu am reușit să generez planul de conținut acum. Putem încerca din nou imediat.',
                    'Не успях да генерирам content плана точно сега. Можем да опитаме отново след малко.'
                ),
            );
        }

        $items = isset($result['items']) && is_array($result['items']) ? $result['items'] : array();
        $save_payload = array(
            'strategy_type' => (string) ($result['strategy_type'] ?? $strategy_type),
            'model' => (string) ($result['model'] ?? ''),
            'usage' => (isset($result['usage']) && is_array($result['usage'])) ? $result['usage'] : array(),
            'warnings' => (isset($result['warnings']) && is_array($result['warnings'])) ? $result['warnings'] : array(),
            'source' => 'api_thin_client',
        );
        $save_result = $this->aysa_adjustments_save_content_strategy_items($project_id, $strategy_type, $items, $save_payload);
        $snapshot = $this->aysa_adjustments_get_content_strategy_snapshot($project_id);
        $saved = intval($save_result['saved'] ?? 0);
        $existing = intval($save_result['existing'] ?? 0);
        $errors = intval($save_result['errors'] ?? 0);
        $saved_items = isset($save_result['saved_items']) && is_array($save_result['saved_items']) ? $save_result['saved_items'] : array();
        $existing_items = isset($save_result['existing_items']) && is_array($save_result['existing_items']) ? $save_result['existing_items'] : array();
        $preview_lines = $this->aysa_adjustments_build_content_strategy_preview_lines(!empty($saved_items) ? $saved_items : $existing_items, $language, 5);
        $plan_batch = sanitize_text_field((string) ($save_result['plan_batch'] ?? ''));

        return array(
            'module' => 'content_strategy',
            'assistant_message' => $this->aysa_adjustments_build_content_strategy_generation_message(
                $language,
                $strategy_type,
                $saved,
                $existing,
                $errors,
                intval($snapshot['total'] ?? 0),
                intval($snapshot['published'] ?? 0),
                $preview_lines,
                !empty($saved_items) ? 'saved' : (!empty($existing_items) ? 'existing' : '')
            ),
            'suggested_actions' => array(
                array(
                    'label' => $this->aysa_adjustments_tr($language, 'See content plan', 'Vezi planul de conținut', 'Виж content плана'),
                    'message' => '',
                    'ui_action' => 'open_artifact_content_strategy',
                ),
                array(
                    'label' => $this->aysa_adjustments_tr($language, 'Delete this batch', 'Șterge ideile generate acum', 'Изтрий генерираните идеи сега'),
                    'message' => $this->aysa_adjustments_tr($language, 'Delete the ideas generated just now.', 'Șterge ideile generate acum.', 'Изтрий идеите, генерирани току-що.'),
                ),
            ),
            'state_meta' => array(
                'last_generated_content_plan_batch' => $plan_batch,
                'last_generated_content_strategy_type' => $strategy_type,
            ),
        );
    }

    private function aysa_adjustments_apply_preview_changes_to_pending_reviews($project_id, array $payload, $language) {
        global $wpdb;

        $pages = isset($payload['pages']) && is_array($payload['pages']) ? $payload['pages'] : array();
        if (empty($pages)) {
            return array(
                'module' => 'onpage_optimization',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I did not find any approved preview rows to send into review.',
                    'Nu am găsit niciun rând aprobat din preview pe care să-l trimit în review.',
                    'Не открих одобрени preview редове, които да изпратя за review.'
                ),
            );
        }

        $table_name = $wpdb->prefix . 'aysa_automated_pending_reviews';
        $created = 0;
        $existing = 0;
        $failed = 0;
        $titles = array();

        foreach ($pages as $page) {
            if (!is_array($page)) {
                continue;
            }

            $entity_id = intval($page['entity_id'] ?? 0);
            $entity_type = $this->aysa_adjustments_normalize_preview_entity_type((string) ($page['entity_type'] ?? ''));
            $keyword = sanitize_text_field((string) ($page['keyword'] ?? ''));
            $title = sanitize_text_field((string) ($page['title'] ?? ''));
            $fields = isset($page['fields']) && is_array($page['fields']) ? $page['fields'] : array();

            if ($entity_id <= 0 || $entity_type === '' || $keyword === '' || empty($fields)) {
                $failed++;
                continue;
            }

            if ($this->aysa_adjustments_has_pending_seo_review($entity_id, $entity_type, $keyword)) {
                $existing++;
                continue;
            }

            $meta_title = '';
            $meta_description = '';
            $heading_h1 = '';
            foreach ($fields as $field) {
                if (!is_array($field)) {
                    continue;
                }
                $field_name = sanitize_key((string) ($field['field'] ?? ''));
                $proposed = isset($field['proposed']) ? sanitize_textarea_field((string) $field['proposed']) : '';
                if ($field_name === 'seo_title') {
                    $meta_title = sanitize_text_field($proposed);
                } elseif ($field_name === 'meta_description') {
                    $meta_description = sanitize_textarea_field($proposed);
                } elseif ($field_name === 'heading_h1') {
                    $heading_h1 = sanitize_text_field($proposed);
                }
            }

            if ($meta_title === '' && $meta_description === '' && $heading_h1 === '') {
                $failed++;
                continue;
            }

            $seo_data = array(
                'metaTitle' => $meta_title,
                'metaDescription' => $meta_description,
                'metaKeywords' => $keyword,
                'proposedHeadingH1' => $heading_h1,
                'previewSource' => 'adjustments_preview_v2',
                'previewTitle' => $title,
            );

            $inserted = $wpdb->insert(
                $table_name,
                array(
                    'entity_id' => $entity_id,
                    'entity_type' => $entity_type,
                    'keyword' => $keyword,
                    'seo_data' => wp_json_encode($seo_data),
                    'project_id' => intval($project_id),
                    'status' => 'pending',
                    'created_at' => current_time('mysql'),
                    'source' => 'adjustments_preview_v2',
                ),
                array('%d', '%s', '%s', '%s', '%d', '%s', '%s', '%s')
            );

            if ($inserted === false) {
                $failed++;
                continue;
            }

            $created++;
            if ($title !== '') {
                $titles[] = $title;
            }
        }

        $examples = array_slice($titles, 0, 3);
        $examples_text = !empty($examples) ? implode(', ', $examples) : '';
        $assistant_message = match ($language) {
            'ro' => 'Am trimis ' . $created . ' pagini în pending review pe baza preview-ului aprobat.'
                . ($existing > 0 ? ' ' . $existing . ' erau deja în review.' : '')
                . ($failed > 0 ? ' ' . $failed . ' nu au putut fi adăugate.' : '')
                . ($examples_text !== '' ? "\n\nPrimele exemple: " . $examples_text . '.' : '')
                . "\n\nAm pus în review titlurile SEO și meta description-urile propuse. Heading-urile rămân vizibile în preview pentru verificare manuală înainte de orice aplicare live.",
            'bg' => 'Изпратих ' . $created . ' страници в pending review според одобрения preview.'
                . ($existing > 0 ? ' ' . $existing . ' вече бяха в review.' : '')
                . ($failed > 0 ? ' ' . $failed . ' не можаха да бъдат добавени.' : '')
                . ($examples_text !== '' ? "\n\nПърви примери: " . $examples_text . '.' : '')
                . "\n\nИзпратих за review предложените SEO title-и и meta description-и. Heading-ите остават видими в preview за ръчна проверка преди live прилагане.",
            default => 'I sent ' . $created . ' pages into pending review from the approved preview.'
                . ($existing > 0 ? ' ' . $existing . ' were already pending review.' : '')
                . ($failed > 0 ? ' ' . $failed . ' could not be queued.' : '')
                . ($examples_text !== '' ? "\n\nFirst examples: " . $examples_text . '.' : '')
                . "\n\nI queued the proposed SEO titles and meta descriptions for review. The H1 suggestions stay visible in preview for manual verification before any live apply.",
        };

        return array(
            'module' => 'onpage_optimization',
            'assistant_message' => $assistant_message,
            'suggested_actions' => array(
                array(
                    'label' => $this->aysa_adjustments_tr($language, 'See on-page pages', 'Vezi paginile on-page', 'Виж on-page страниците'),
                    'message' => '',
                    'ui_action' => 'open_artifact_onpage_optimization',
                ),
            ),
            'ui_action' => 'open_artifact_onpage_optimization',
            'state_meta' => array(
                'active_research_module' => 'onpage_optimization',
            ),
        );
    }

    private function aysa_adjustments_apply_redirect_preview(array $payload, $language) {
        $items = isset($payload['items']) && is_array($payload['items']) ? array_values($payload['items']) : array();
        if ($items === array()) {
            return array(
                'module' => 'technical_404_errors',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I did not receive any redirect candidates to apply.',
                    'Nu am primit niciun redirect candidat de aplicat.',
                    'Не получих никакви кандидати за redirect, които да приложа.'
                ),
            );
        }

        $applied = 0;
        $duplicates = 0;
        $skipped = 0;
        $errors = array();

        foreach ($items as $item) {
            if (!is_array($item)) {
                $skipped++;
                continue;
            }

            $source = isset($item['source_url']) ? (string) $item['source_url'] : '';
            $destination = isset($item['destination_url']) ? (string) $item['destination_url'] : '';
            if (trim($source) === '' || trim($destination) === '') {
                $skipped++;
                continue;
            }

            $result = $this->aysa_add_htaccess_redirect_rule($source, $destination);
            if (!empty($result['ok'])) {
                $applied++;
                continue;
            }

            $message = (string) ($result['message'] ?? '');
            if (stripos($message, 'already exists') !== false) {
                $duplicates++;
            } else {
                $errors[] = $message !== '' ? $message : 'unknown_error';
            }
        }

        $assistant_message = match ($language) {
            'ro' => 'Am adăugat ' . $applied . ' redirecturi 301 pentru URL-urile 404 cu destinație clară.'
                . ($duplicates > 0 ? ' ' . $duplicates . ' reguli existau deja.' : '')
                . ($skipped > 0 ? ' Am sărit peste ' . $skipped . ' rânduri incomplete.' : '')
                . (!empty($errors) ? ' Au rămas și ' . count($errors) . ' erori de aplicare care cer verificare manuală.' : ''),
            'bg' => 'Добавих ' . $applied . ' 301 redirect-а за 404 URL-ите с ясна дестинация.'
                . ($duplicates > 0 ? ' ' . $duplicates . ' правила вече съществуваха.' : '')
                . ($skipped > 0 ? ' Пропуснах ' . $skipped . ' непълни реда.' : '')
                . (!empty($errors) ? ' Останаха и ' . count($errors) . ' грешки за ръчна проверка.' : ''),
            default => 'I added ' . $applied . ' 301 redirects for the 404 URLs that already had clear destinations.'
                . ($duplicates > 0 ? ' ' . $duplicates . ' rules already existed.' : '')
                . ($skipped > 0 ? ' I skipped ' . $skipped . ' incomplete rows.' : '')
                . (!empty($errors) ? ' There are also ' . count($errors) . ' apply errors that still need manual review.' : ''),
        };

        return array(
            'module' => 'technical_404_errors',
            'assistant_message' => $assistant_message,
            'suggested_actions' => array(
                array(
                    'label' => $this->aysa_adjustments_tr($language, 'See 301 redirects', 'Vezi redirecturile 301', 'Виж 301 redirect-ите'),
                    'message' => '',
                    'ui_action' => 'open_artifact_technical_301_redirects',
                ),
                array(
                    'label' => $this->aysa_adjustments_tr($language, 'See remaining 404s', 'Vezi 404-urile rămase', 'Виж оставащите 404'),
                    'message' => '',
                    'ui_action' => 'open_artifact_technical_404_errors',
                ),
            ),
            'ui_action' => 'open_artifact_technical_301_redirects',
            'state_meta' => array(
                'active_research_module' => 'technical_404_errors',
            ),
        );
    }

    private function aysa_normalize_redirect_source($source) {
        $source = trim((string) $source);
        if ($source === '') {
            return '';
        }

        if (preg_match('#^https?://#i', $source) === 1) {
            $path = (string) parse_url($source, PHP_URL_PATH);
            $query = (string) parse_url($source, PHP_URL_QUERY);
            $source = $path !== '' ? $path : '/';
            if ($query !== '') {
                $source .= '?' . $query;
            }
        }

        if (strpos($source, '/') !== 0) {
            $source = '/' . ltrim($source, '/');
        }

        return $source;
    }

    private function aysa_normalize_redirect_destination($destination) {
        $destination = trim((string) $destination);
        if ($destination === '') {
            return '';
        }

        if (preg_match('#^https?://#i', $destination) === 1) {
            $host = (string) parse_url($destination, PHP_URL_HOST);
            $site_host = (string) parse_url(home_url('/'), PHP_URL_HOST);
            if ($host !== '' && $site_host !== '' && strcasecmp($host, $site_host) === 0) {
                $path = (string) parse_url($destination, PHP_URL_PATH);
                $query = (string) parse_url($destination, PHP_URL_QUERY);
                $destination = $path !== '' ? $path : '/';
                if ($query !== '') {
                    $destination .= '?' . $query;
                }
            } else {
                return $destination;
            }
        }

        if (strpos($destination, '/') !== 0) {
            $destination = '/' . ltrim($destination, '/');
        }

        return $destination;
    }

    private function aysa_add_htaccess_redirect_rule($source, $destination) {
        $source = $this->aysa_normalize_redirect_source($source);
        $destination = $this->aysa_normalize_redirect_destination($destination);

        if ($source === '' || $destination === '') {
            return array(
                'ok' => false,
                'message' => 'Source or destination URL is missing.',
            );
        }

        $htaccess_file = ABSPATH . '.htaccess';
        if (!file_exists($htaccess_file)) {
            return array(
                'ok' => false,
                'message' => 'The .htaccess file does not exist in the WordPress root directory.',
            );
        }

        if (!is_writable($htaccess_file)) {
            return array(
                'ok' => false,
                'message' => 'The .htaccess file is not writable. Please check file permissions.',
            );
        }

        $htaccess_content = file_get_contents($htaccess_file);
        if (!is_string($htaccess_content)) {
            return array(
                'ok' => false,
                'message' => 'Failed to read the .htaccess file.',
            );
        }

        $aysa_section_start = '# BEGIN Aysa Redirects';
        $aysa_section_end = '# END Aysa Redirects';
        $redirect_rule = 'RedirectMatch 301 ^' . preg_quote($source, '/') . '$ ' . $destination;

        if (strpos($htaccess_content, $aysa_section_start) !== false) {
            $pattern = '/' . preg_quote($aysa_section_start, '/') . '(.*?)' . preg_quote($aysa_section_end, '/') . '/s';
            if (preg_match($pattern, $htaccess_content, $matches)) {
                $existing_content = isset($matches[1]) ? (string) $matches[1] : '';
                if (strpos($existing_content, $redirect_rule) !== false) {
                    return array(
                        'ok' => true,
                        'already_done' => true,
                        'code' => 'redirect_rule_exists',
                        'message' => 'This redirect already exists in .htaccess.',
                    );
                }

                $new_content = $aysa_section_start . "\n" . $existing_content . $redirect_rule . "\n" . $aysa_section_end;
                $htaccess_content = (string) preg_replace($pattern, $new_content, $htaccess_content);
            }
        } else {
            $new_section = $aysa_section_start . "\n" . $redirect_rule . "\n" . $aysa_section_end . "\n\n";
            $htaccess_content = $new_section . $htaccess_content;
        }

        $result = file_put_contents($htaccess_file, $htaccess_content);
        if ($result === false) {
            return array(
                'ok' => false,
                'message' => 'Failed to write to .htaccess file.',
            );
        }

        return array(
            'ok' => true,
            'message' => 'Redirect added successfully to .htaccess file.',
            'redirect_rule' => $redirect_rule,
        );
    }

    private function aysa_adjustments_normalize_preview_entity_type($value) {
        $value = trim((string) $value);
        return match (strtolower($value)) {
            'product' => 'Product',
            'category' => 'Category',
            'page' => 'Page',
            'post' => 'Post',
            default => in_array($value, array('Product', 'Category', 'Page', 'Post'), true) ? $value : '',
        };
    }

    private function aysa_adjustments_recommend_seo_text_ads_portals($project_id, $setup_user_id, $language) {
        $summary = $this->aysa_get_local_setup_state_for_user($setup_user_id);
        $general = isset($summary['general']) && is_array($summary['general']) ? $summary['general'] : array();
        $competition = isset($summary['competition']) && is_array($summary['competition']) ? $summary['competition'] : array();
        $seed_keyword = '';
        for ($i = 1; $i <= 5; $i++) {
            $candidate = trim((string) ($competition['keyword' . $i] ?? ''));
            if ($candidate !== '') {
                $seed_keyword = $candidate;
                break;
            }
        }
        if ($seed_keyword === '') {
            $seed_keyword = sanitize_text_field((string) ($general['industry'] ?? ''));
        }

        $portal_result = $this->aysa_adjustments_list_adverlink_portals_internal($project_id, 'seo_ads_text', 'monthly', 50, true);
        if (is_wp_error($portal_result)) {
            return array(
                'module' => 'offpage_link_building',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I could not load SEO Text Ads websites right now. We can try again in a moment.',
                    'Nu am reușit să încarc acum website-urile pentru SEO Text Ads. Putem încerca din nou imediat.',
                    'Не успях да заредя сайтовете за SEO Text Ads точно сега. Можем да опитаме отново след малко.'
                ),
            );
        }

        $items = isset($portal_result['items']) && is_array($portal_result['items']) ? $portal_result['items'] : array();
        $shortlist = $this->aysa_adjustments_build_seo_text_ads_shortlist($items, $summary);
        if (empty($shortlist)) {
            return array(
                'module' => 'offpage_link_building',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I did not find suitable SEO Text Ads websites right now. We can refresh the marketplace and try again.',
                    'Nu am găsit acum website-uri potrivite pentru SEO Text Ads. Putem reîmprospăta marketplace-ul și încerca din nou.',
                    'В момента не намерих подходящи сайтове за SEO Text Ads. Можем да опресним marketplace-а и да опитаме отново.'
                ),
            );
        }

        $preview_lines = array();
        $suggested_actions = array();
        foreach ($shortlist as $index => $portal) {
            $domain = (string) ($portal['domain'] ?? '');
            $authority = (float) ($portal['authority'] ?? 0);
            $monthly_cost = intval($portal['monthly_cost_cents'] ?? 0);
            $preview_lines[] = ($index + 1) . '. ' . $domain
                . ' | ' . $this->aysa_adjustments_tr($language, 'authority', 'autoritate', 'authority') . ': ' . number_format($authority, 1)
                . ' | ' . $this->aysa_adjustments_tr($language, 'monthly cost', 'cost lunar', 'месечна цена') . ': ' . $monthly_cost;
            $suggested_actions[] = array(
                'label' => $domain,
                'message' => $domain,
            );
        }

        $message = match ($language) {
            'ro' => 'Am ales pentru tine 3 website-uri potrivite pentru SEO Text Ads. M-am uitat la autoritate, relevanță și disponibilitatea acestui tip de plasare. '
                . 'Uite opțiunile cele mai bune acum:' . "\n\n- " . implode("\n- ", $preview_lines)
                . "\n\n" . 'Spune-mi pe care îl dorești și îți pregătesc comanda lunară.',
            'bg' => 'Подбрах за теб 3 подходящи сайта за SEO Text Ads. Гледах authority, релевантност и наличност за този тип placement. '
                . 'Ето най-добрите варианти в момента:' . "\n\n- " . implode("\n- ", $preview_lines)
                . "\n\n" . 'Кажи ми кой искаш и ще подготвя месечната поръчка.',
            default => 'I picked 3 suitable websites for SEO Text Ads for you. I looked at authority, relevance, and whether they support this placement type. '
                . 'Here are the best options right now:' . "\n\n- " . implode("\n- ", $preview_lines)
                . "\n\n" . 'Tell me which one you want and I will prepare the monthly order.',
        };

        $suggested_actions[] = array(
            'label' => $this->aysa_adjustments_tr($language, 'Refresh options', 'Reîmprospătează opțiunile', 'Опресни опциите'),
            'message' => $this->aysa_adjustments_tr($language, 'Refresh the SEO Text Ads options.', 'Reîmprospătează opțiunile SEO Text Ads.', 'Опресни опциите за SEO Text Ads.'),
        );

        return array(
            'module' => 'offpage_link_building',
            'assistant_message' => $message,
            'suggested_actions' => $suggested_actions,
            'state_meta' => array(
                'offpage_purchase_flow' => 'seo_text_ads',
                'offpage_purchase_stage' => 'awaiting_selection',
                'offpage_purchase_selection_mode' => 'ai_shortlist',
                'offpage_purchase_candidates' => $shortlist,
                'offpage_purchase_keyword' => $seed_keyword,
            ),
        );
    }

    private function aysa_adjustments_place_seo_text_ads_order($project_id, $setup_user_id, array $payload, $language) {
        $portal_id = sanitize_text_field((string) ($payload['portal_id'] ?? ''));
        $portal_domain = sanitize_text_field((string) ($payload['portal_domain'] ?? ''));
        $keyword = sanitize_text_field((string) ($payload['keyword'] ?? ''));
        $monthly_cost_cents = max(0, intval($payload['monthly_cost_cents'] ?? 0));

        if ($portal_id === '' || $portal_domain === '' || $keyword === '' || $monthly_cost_cents <= 0) {
            return array(
                'module' => 'offpage_link_building',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I am missing a few order details for this SEO Text Ads placement, so I stopped before placing it.',
                    'Îmi lipsesc câteva detalii pentru această comandă SEO Text Ads, așa că m-am oprit înainte să o plasez.',
                    'Липсват ми няколко детайла за тази SEO Text Ads поръчка, затова спрях преди да я пусна.'
                ),
            );
        }

        $setup = $this->aysa_get_local_setup_state_for_user($setup_user_id);
        $general = isset($setup['general']) && is_array($setup['general']) ? $setup['general'] : array();
        $business_name = sanitize_text_field((string) ($general['company_name'] ?? get_bloginfo('name')));
        $target_url = $this->aysa_setup_normalize_url((string) ($general['website'] ?? home_url('/')));
        if ($target_url === '') {
            $target_url = home_url('/');
        }
        $display_host = wp_parse_url($target_url, PHP_URL_HOST);
        $display_url = $display_host ? 'https://' . $display_host : $target_url;
        $title = sanitize_text_field($business_name . ' - ' . $keyword);
        $description = $this->aysa_adjustments_build_seo_text_ads_description($business_name, $keyword);

        $order_result = $this->aysa_adjustments_create_adverlink_order_internal($project_id, array(
            'portal_id' => $portal_id,
            'portal_domain' => $portal_domain,
            'service_type' => 'seo_ads_text',
            'billing_type' => 'monthly',
            'keyword' => $keyword,
            'target_url' => $target_url,
            'title' => $title,
            'ad_display_url' => $display_url,
            'ad_description' => $description,
            'duration_months' => 1,
            'estimated_credits' => $monthly_cost_cents,
            'client_name' => $business_name,
            'project_ref' => 'wp-project-' . intval($project_id),
            'order_meta' => array(
                'source' => 'adjustments_agent',
                'placement_type' => 'seo_text_ads',
            ),
        ));

        if (is_wp_error($order_result)) {
            if ($order_result->get_error_code() === 'insufficient_credits') {
                $error_data = $order_result->get_error_data();
                return $this->aysa_build_insufficient_credits_payment_response(
                    $project_id,
                    intval(is_array($error_data) ? ($error_data['required_credits'] ?? 0) : 0),
                    intval(is_array($error_data) ? ($error_data['available_credits'] ?? 0) : 0),
                    $language,
                    'seo_text_ads_order',
                    'offpage_link_building',
                    array(
                        'portal_id' => $portal_id,
                        'portal_domain' => $portal_domain,
                    )
                );
            }
            return array(
                'module' => 'offpage_link_building',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I could not place the SEO Text Ads order right now: ',
                    'Nu am putut plasa acum comanda SEO Text Ads: ',
                    'Не успях да пусна SEO Text Ads поръчката сега: '
                ) . $order_result->get_error_message(),
            );
        }

        $charged = intval($order_result['charged_credits_cents'] ?? 0);
        $included_link_applied = !empty($order_result['included_link_applied']);
        $order_ref = sanitize_text_field((string) ($order_result['order_ref'] ?? ''));
        $message = match ($language) {
            'ro' => 'Am trimis comanda SEO Text Ads pentru ' . $portal_domain . '. '
                . ($included_link_applied
                    ? 'Prima lună a fost acoperită din linkurile incluse în abonament, fără cost suplimentar în credite. '
                    : 'Am consumat acum ' . $charged . ' credite pentru prima lună. ')
                . 'De aici înainte, costul se va deduce lunar din contul tău cât timp păstrăm plasarea activă. '
                . 'Beneficiul tău este dublu: reclamă text clickabilă în articol și transfer de autoritate către site-ul tău.',
            'bg' => 'Изпратих SEO Text Ads поръчката за ' . $portal_domain . '. '
                . ($included_link_applied
                    ? 'Първият месец е покрит от включените линкове в абонамента, без допълнителен разход на кредити. '
                    : 'Сега изразходвах ' . $charged . ' кредита за първия месец. ')
                . 'Оттук нататък сумата ще се удържа месечно от акаунта ти, докато placement-ът е активен. '
                . 'Ползата е двойна: кликаема текстова реклама в статията и прехвърляне на authority към сайта ти.',
            default => 'I placed the SEO Text Ads order for ' . $portal_domain . '. '
                . ($included_link_applied
                    ? 'The first month was covered by your included subscription links, so no extra credits were charged. '
                    : 'I charged ' . $charged . ' credits now for the first month. ')
                . 'From here on, the cost will be deducted monthly from your account while the placement stays active. '
                . 'You get a double benefit: a clickable text ad inside the article and authority passed to your website.',
        };
        if ($order_ref !== '') {
            $message .= "\n\n" . $this->aysa_adjustments_tr($language, 'Order reference', 'Referință comandă', 'Референция на поръчка') . ': ' . $order_ref;
        }

        return array(
            'module' => 'offpage_link_building',
            'assistant_message' => $message,
            'suggested_actions' => array(
                array(
                    'label' => $this->aysa_adjustments_tr($language, 'See link building orders', 'Vezi comenzile de link building', 'Виж поръчките за link building'),
                    'message' => '',
                    'ui_action' => 'open_artifact_offpage_link_building_orders',
                ),
            ),
            'ui_action' => 'open_artifact_offpage_link_building_orders',
            'state_meta' => array(
                'offpage_purchase_flow' => '',
                'offpage_purchase_stage' => '',
                'offpage_purchase_selection_mode' => '',
                'offpage_purchase_candidates' => array(),
                'offpage_purchase_selected_candidate' => array(),
            ),
        );
    }

    private function aysa_adjustments_get_seed_keyword_from_setup(array $summary) {
        $general = isset($summary['general']) && is_array($summary['general']) ? $summary['general'] : array();
        $competition = isset($summary['competition']) && is_array($summary['competition']) ? $summary['competition'] : array();

        for ($i = 1; $i <= 5; $i++) {
            $candidate = trim((string) ($competition['keyword' . $i] ?? ''));
            if ($candidate !== '') {
                return sanitize_text_field($candidate);
            }
        }

        $fallback = trim((string) ($general['industry'] ?? ''));
        return sanitize_text_field($fallback);
    }

    private function aysa_adjustments_extract_portal_service_cost_cents(array $row, $service_type, $billing_type) {
        $service_type = sanitize_key((string) $service_type);
        $billing_type = sanitize_key((string) $billing_type);

        $services = isset($row['services']) && is_array($row['services']) ? $row['services'] : array();
        foreach ($services as $service) {
            if (!is_array($service)) {
                continue;
            }
            $serviceType = sanitize_key((string) ($service['service_type'] ?? ''));
            $billingType = sanitize_key((string) ($service['billing_type'] ?? ''));
            if ($serviceType !== $service_type || $billingType !== $billing_type) {
                continue;
            }
            foreach (array('sale_price_eur', 'purchase_price_eur', 'price_eur') as $priceKey) {
                if (!isset($service[$priceKey]) || $service[$priceKey] === '') {
                    continue;
                }
                if (is_numeric($service[$priceKey])) {
                    $credits = (int) round(((float) $service[$priceKey]) * 100);
                    if ($credits > 0) {
                        return $credits;
                    }
                }
            }
        }

        return 0;
    }

    private function aysa_adjustments_build_portal_shortlist(array $items, array $setup_state, $service_type, $billing_type, $limit = 3) {
        $summary_general = isset($setup_state['general']) && is_array($setup_state['general']) ? $setup_state['general'] : array();
        $summary_competition = isset($setup_state['competition']) && is_array($setup_state['competition']) ? $setup_state['competition'] : array();
        $industry = strtolower(trim((string) ($summary_general['industry'] ?? '')));
        $business = strtolower(trim((string) ($summary_general['company_name'] ?? '')));
        $site_host = strtolower((string) wp_parse_url((string) ($summary_general['website'] ?? home_url('/')), PHP_URL_HOST));
        $seed_keywords = array();
        for ($i = 1; $i <= 5; $i++) {
            $keyword = strtolower(trim((string) ($summary_competition['keyword' . $i] ?? '')));
            if ($keyword !== '') {
                $seed_keywords[] = $keyword;
            }
        }

        $prepared = array();
        foreach ($items as $row) {
            if (!is_array($row)) {
                continue;
            }
            $domain = $this->aysa_adjustments_extract_portal_domain($row);
            if ($domain === '' || ($site_host !== '' && $domain === $site_host)) {
                continue;
            }
            $authority = $this->aysa_adjustments_extract_portal_authority($row);
            if ($authority < 10) {
                continue;
            }
            $cost_cents = $this->aysa_adjustments_extract_portal_service_cost_cents($row, $service_type, $billing_type);
            if ($cost_cents <= 0) {
                continue;
            }

            $score = $authority;
            $haystack = strtolower(wp_json_encode($row));
            if ($industry !== '' && str_contains($haystack, $industry)) {
                $score += 4;
            }
            foreach ($seed_keywords as $seed) {
                if ($seed !== '' && str_contains($haystack, $seed)) {
                    $score += 2;
                }
            }
            if ($business !== '' && str_contains($haystack, $business)) {
                $score -= 3;
            }

            $prepared[] = array(
                'portal_id' => sanitize_text_field((string) ($row['portal_id'] ?? $row['id'] ?? '')),
                'domain' => $domain,
                'authority' => $authority,
                'estimated_cost_cents' => $cost_cents,
                'currency' => sanitize_text_field((string) ($row['currency'] ?? 'credits')),
                'score' => $score,
            );
        }

        usort($prepared, static function(array $a, array $b): int {
            $scoreCompare = (float) ($b['score'] ?? 0) <=> (float) ($a['score'] ?? 0);
            if ($scoreCompare !== 0) {
                return $scoreCompare;
            }
            return intval($a['estimated_cost_cents'] ?? 0) <=> intval($b['estimated_cost_cents'] ?? 0);
        });

        $deduped = array();
        $seen = array();
        foreach ($prepared as $portal) {
            $key = strtolower((string) ($portal['domain'] ?? ''));
            if ($key === '' || isset($seen[$key])) {
                continue;
            }
            $seen[$key] = true;
            $deduped[] = $portal;
            if (count($deduped) >= max(1, intval($limit))) {
                break;
            }
        }

        return $deduped;
    }

    private function aysa_adjustments_recommend_link_insertion_portals($project_id, $setup_user_id, $language) {
        $summary = $this->aysa_get_local_setup_state_for_user($setup_user_id);
        $seed_keyword = $this->aysa_adjustments_get_seed_keyword_from_setup($summary);

        $portal_result = $this->aysa_adjustments_list_adverlink_portals_internal($project_id, 'link_insertion', 'one_time', 50, true);
        if (is_wp_error($portal_result)) {
            return array(
                'module' => 'offpage_link_building',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I could not load link insertion websites right now. We can try again in a moment.',
                    'Nu am reușit să încarc acum website-urile pentru link insertion. Putem încerca din nou imediat.',
                    'Не успях да заредя сайтовете за link insertion точно сега. Можем да опитаме отново след малко.'
                ),
            );
        }

        $items = isset($portal_result['items']) && is_array($portal_result['items']) ? $portal_result['items'] : array();
        $shortlist = $this->aysa_adjustments_build_portal_shortlist($items, $summary, 'link_insertion', 'one_time', 3);
        if (empty($shortlist)) {
            return array(
                'module' => 'offpage_link_building',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I did not find suitable link insertion websites right now. We can refresh the marketplace and try again.',
                    'Nu am găsit acum website-uri potrivite pentru link insertion. Putem reîmprospăta marketplace-ul și încerca din nou.',
                    'В момента не намерих подходящи сайтове за link insertion. Можем да опресним marketplace-а и да опитаме отново.'
                ),
            );
        }

        $preview_lines = array();
        $suggested_actions = array();
        foreach ($shortlist as $index => $portal) {
            $domain = (string) ($portal['domain'] ?? '');
            $authority = (float) ($portal['authority'] ?? 0);
            $cost = intval($portal['estimated_cost_cents'] ?? 0);
            $preview_lines[] = ($index + 1) . '. ' . $domain
                . ' | ' . $this->aysa_adjustments_tr($language, 'authority', 'autoritate', 'authority') . ': ' . number_format($authority, 1)
                . ' | ' . $this->aysa_adjustments_tr($language, 'one-time cost', 'cost unic', 'еднократна цена') . ': ' . $cost;
            $suggested_actions[] = array(
                'label' => $domain,
                'message' => $domain,
            );
        }

        return array(
            'module' => 'offpage_link_building',
            'assistant_message' => match ($language) {
                'ro' => 'Am ales pentru tine 3 website-uri potrivite pentru link insertion. Uite opțiunile cele mai bune acum:' . "\n\n- " . implode("\n- ", $preview_lines) . "\n\n" . 'Spune-mi pe care îl dorești și pregătesc comanda cu keyword-ul principal al site-ului tău.',
                'bg' => 'Подбрах за теб 3 подходящи сайта за link insertion. Ето най-добрите варианти в момента:' . "\n\n- " . implode("\n- ", $preview_lines) . "\n\n" . 'Кажи ми кой искаш и ще подготвя поръчката с основната ключова дума на сайта ти.',
                default => 'I picked 3 suitable websites for link insertion for you. Here are the best options right now:' . "\n\n- " . implode("\n- ", $preview_lines) . "\n\n" . 'Tell me which one you want and I will prepare the order with your site\'s main keyword.',
            },
            'suggested_actions' => $suggested_actions,
            'state_meta' => array(
                'offpage_purchase_flow' => 'link_insertion',
                'offpage_purchase_stage' => 'awaiting_selection',
                'offpage_purchase_selection_mode' => 'ai_shortlist',
                'offpage_purchase_candidates' => $shortlist,
                'offpage_purchase_keyword' => $seed_keyword,
            ),
        );
    }

    private function aysa_adjustments_place_link_insertion_order($project_id, $setup_user_id, array $payload, $language) {
        $portal_id = sanitize_text_field((string) ($payload['portal_id'] ?? ''));
        $portal_domain = sanitize_text_field((string) ($payload['portal_domain'] ?? ''));
        $keyword = sanitize_text_field((string) ($payload['keyword'] ?? ''));
        $cost_cents = max(0, intval($payload['estimated_cost_cents'] ?? 0));

        if ($portal_id === '' || $portal_domain === '' || $keyword === '' || $cost_cents <= 0) {
            return array(
                'module' => 'offpage_link_building',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I am missing a few details for this link insertion order, so I stopped before placing it.',
                    'Îmi lipsesc câteva detalii pentru această comandă de link insertion, așa că m-am oprit înainte să o plasez.',
                    'Липсват ми няколко детайла за тази link insertion поръчка, затова спрях преди да я пусна.'
                ),
            );
        }

        $setup = $this->aysa_get_local_setup_state_for_user($setup_user_id);
        $general = isset($setup['general']) && is_array($setup['general']) ? $setup['general'] : array();
        $business_name = sanitize_text_field((string) ($general['company_name'] ?? get_bloginfo('name')));
        $target_url = $this->aysa_setup_normalize_url((string) ($general['website'] ?? home_url('/')));
        if ($target_url === '') {
            $target_url = home_url('/');
        }
        $title = sanitize_text_field('Link insertion - ' . $keyword);

        $order_result = $this->aysa_adjustments_create_adverlink_order_internal($project_id, array(
            'portal_id' => $portal_id,
            'portal_domain' => $portal_domain,
            'service_type' => 'link_insertion',
            'billing_type' => 'one_time',
            'keyword' => $keyword,
            'target_url' => $target_url,
            'title' => $title,
            'rel_type' => 'follow',
            'estimated_credits' => $cost_cents,
            'client_name' => $business_name,
            'project_ref' => 'wp-project-' . intval($project_id),
            'order_meta' => array(
                'source' => 'adjustments_agent',
                'placement_type' => 'link_insertion',
            ),
        ));

        if (is_wp_error($order_result)) {
            if ($order_result->get_error_code() === 'insufficient_credits') {
                $error_data = $order_result->get_error_data();
                return $this->aysa_build_insufficient_credits_payment_response(
                    $project_id,
                    intval(is_array($error_data) ? ($error_data['required_credits'] ?? 0) : 0),
                    intval(is_array($error_data) ? ($error_data['available_credits'] ?? 0) : 0),
                    $language,
                    'link_insertion_order',
                    'offpage_link_building',
                    array(
                        'portal_id' => $portal_id,
                        'portal_domain' => $portal_domain,
                    )
                );
            }
            return array(
                'module' => 'offpage_link_building',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I could not place the link insertion order right now: ',
                    'Nu am putut plasa acum comanda de link insertion: ',
                    'Не успях да пусна link insertion поръчката сега: '
                ) . $order_result->get_error_message(),
            );
        }

        $charged = intval($order_result['charged_credits_cents'] ?? 0);
        $included_link_applied = !empty($order_result['included_link_applied']);
        $order_ref = sanitize_text_field((string) ($order_result['order_ref'] ?? ''));

        return array(
            'module' => 'offpage_link_building',
            'assistant_message' => match ($language) {
                'ro' => 'Am trimis comanda de link insertion pentru ' . $portal_domain . '. '
                    . ($included_link_applied
                        ? 'Am folosit 1 link inclus din abonament, fără cost suplimentar în credite. '
                        : 'Am consumat acum ' . $charged . ' credite. ')
                    . 'Keyword-ul folosit este "' . $keyword . '".',
                'bg' => 'Изпратих link insertion поръчката за ' . $portal_domain . '. '
                    . ($included_link_applied
                        ? 'Използвах 1 включен линк от абонамента, без допълнителен разход на кредити. '
                        : 'Сега изразходвах ' . $charged . ' кредита. ')
                    . 'Използваната ключова дума е "' . $keyword . '".',
                default => 'I placed the link insertion order for ' . $portal_domain . '. '
                    . ($included_link_applied
                        ? 'I used 1 included subscription link, so no extra credits were charged. '
                        : 'I charged ' . $charged . ' credits now. ')
                    . 'The keyword used is "' . $keyword . '".',
            } . ($order_ref !== '' ? "\n\n" . $this->aysa_adjustments_tr($language, 'Order reference', 'Referință comandă', 'Референция на поръчка') . ': ' . $order_ref : ''),
            'suggested_actions' => array(
                array(
                    'label' => $this->aysa_adjustments_tr($language, 'See link building orders', 'Vezi comenzile de link building', 'Виж поръчките за link building'),
                    'message' => '',
                    'ui_action' => 'open_artifact_offpage_link_building_orders',
                ),
            ),
            'ui_action' => 'open_artifact_offpage_link_building_orders',
            'state_meta' => array(
                'offpage_purchase_flow' => '',
                'offpage_purchase_stage' => '',
                'offpage_purchase_selection_mode' => '',
                'offpage_purchase_candidates' => array(),
                'offpage_purchase_selected_candidate' => array(),
            ),
        );
    }

    private function aysa_adjustments_recommend_article_portals($project_id, $setup_user_id, $service_type, $language) {
        $service_type = $service_type === 'professional_article' ? 'professional_article' : 'seo_article';
        $summary = $this->aysa_get_local_setup_state_for_user($setup_user_id);
        $seed_keyword = $this->aysa_adjustments_get_seed_keyword_from_setup($summary);

        $portal_result = $this->aysa_adjustments_list_adverlink_portals_internal($project_id, $service_type, 'one_time', 50, true);
        if (is_wp_error($portal_result)) {
            return array(
                'module' => 'offpage_link_building',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I could not load article placement websites right now. We can try again in a moment.',
                    'Nu am reușit să încarc acum website-urile pentru articol. Putem încerca din nou imediat.',
                    'Не успях да заредя сайтовете за статия точно сега. Можем да опитаме отново след малко.'
                ),
            );
        }

        $items = isset($portal_result['items']) && is_array($portal_result['items']) ? $portal_result['items'] : array();
        $shortlist = $this->aysa_adjustments_build_portal_shortlist($items, $summary, $service_type, 'one_time', 3);
        if (empty($shortlist)) {
            return array(
                'module' => 'offpage_link_building',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I did not find suitable article websites right now. We can refresh the marketplace and try again.',
                    'Nu am găsit acum website-uri potrivite pentru articol. Putem reîmprospăta marketplace-ul și încerca din nou.',
                    'В момента не намерих подходящи сайтове за статия. Можем да опресним marketplace-а и да опитаме отново.'
                ),
            );
        }

        $preview_lines = array();
        $suggested_actions = array();
        foreach ($shortlist as $index => $portal) {
            $domain = (string) ($portal['domain'] ?? '');
            $authority = (float) ($portal['authority'] ?? 0);
            $cost = intval($portal['estimated_cost_cents'] ?? 0);
            $preview_lines[] = ($index + 1) . '. ' . $domain
                . ' | ' . $this->aysa_adjustments_tr($language, 'authority', 'autoritate', 'authority') . ': ' . number_format($authority, 1)
                . ' | ' . $this->aysa_adjustments_tr($language, 'one-time cost', 'cost unic', 'еднократна цена') . ': ' . $cost;
            $suggested_actions[] = array(
                'label' => $domain,
                'message' => $domain,
            );
        }

        $articleLabel = $service_type === 'professional_article'
            ? $this->aysa_adjustments_tr($language, 'professional article', 'professional article', 'professional article')
            : $this->aysa_adjustments_tr($language, 'SEO article', 'SEO article', 'SEO article');

        return array(
            'module' => 'offpage_link_building',
            'assistant_message' => match ($language) {
                'ro' => 'Am ales pentru tine 3 website-uri potrivite pentru ' . $articleLabel . '. Uite opțiunile cele mai bune acum:' . "\n\n- " . implode("\n- ", $preview_lines) . "\n\n" . 'Spune-mi pe care îl dorești și pregătesc comanda articolului.',
                'bg' => 'Подбрах за теб 3 подходящи сайта за ' . $articleLabel . '. Ето най-добрите варианти в момента:' . "\n\n- " . implode("\n- ", $preview_lines) . "\n\n" . 'Кажи ми кой искаш и ще подготвя поръчката за статия.',
                default => 'I picked 3 suitable websites for ' . $articleLabel . '. Here are the best options right now:' . "\n\n- " . implode("\n- ", $preview_lines) . "\n\n" . 'Tell me which one you want and I will prepare the article order.',
            },
            'suggested_actions' => $suggested_actions,
            'state_meta' => array(
                'offpage_purchase_flow' => $service_type,
                'offpage_purchase_stage' => 'awaiting_selection',
                'offpage_purchase_selection_mode' => 'ai_shortlist',
                'offpage_purchase_candidates' => $shortlist,
                'offpage_purchase_keyword' => $seed_keyword,
            ),
        );
    }

    private function aysa_adjustments_build_article_brief_text($business_name, $keyword, $language) {
        return match ($language) {
            'ro' => 'Scrie un articol natural și util despre ' . $keyword . ', cu context real despre ' . $business_name . ' și un link integrat natural către website-ul companiei.',
            'bg' => 'Напиши естествена и полезна статия за ' . $keyword . ', с реален контекст за ' . $business_name . ' и естествено интегриран линк към сайта на компанията.',
            default => 'Write a natural, useful article about ' . $keyword . ', with real context about ' . $business_name . ' and one naturally integrated link to the company website.',
        };
    }

    private function aysa_adjustments_place_article_order($project_id, $setup_user_id, array $payload, $service_type, $language) {
        $service_type = $service_type === 'professional_article' ? 'professional_article' : 'seo_article';
        $portal_id = sanitize_text_field((string) ($payload['portal_id'] ?? ''));
        $portal_domain = sanitize_text_field((string) ($payload['portal_domain'] ?? ''));
        $keyword = sanitize_text_field((string) ($payload['keyword'] ?? ''));
        $cost_cents = max(0, intval($payload['estimated_cost_cents'] ?? 0));

        if ($portal_id === '' || $portal_domain === '' || $keyword === '' || $cost_cents <= 0) {
            return array(
                'module' => 'offpage_link_building',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I am missing a few details for this article order, so I stopped before placing it.',
                    'Îmi lipsesc câteva detalii pentru această comandă de articol, așa că m-am oprit înainte să o plasez.',
                    'Липсват ми няколко детайла за тази поръчка за статия, затова спрях преди да я пусна.'
                ),
            );
        }

        $setup = $this->aysa_get_local_setup_state_for_user($setup_user_id);
        $general = isset($setup['general']) && is_array($setup['general']) ? $setup['general'] : array();
        $business_name = sanitize_text_field((string) ($general['company_name'] ?? get_bloginfo('name')));
        $target_url = $this->aysa_setup_normalize_url((string) ($general['website'] ?? home_url('/')));
        if ($target_url === '') {
            $target_url = home_url('/');
        }
        $article_topic = sanitize_text_field($business_name . ' - ' . $keyword);
        $article_brief = $this->aysa_adjustments_build_article_brief_text($business_name, $keyword, $language);
        $article_word_count = $service_type === 'professional_article' ? 1200 : 900;
        $article_language = $language === 'bg' ? 'bg' : ($language === 'ro' ? 'ro' : 'en');
        $title = $article_topic;

        $order_result = $this->aysa_adjustments_create_adverlink_order_internal($project_id, array(
            'portal_id' => $portal_id,
            'portal_domain' => $portal_domain,
            'service_type' => $service_type,
            'billing_type' => 'one_time',
            'keyword' => $keyword,
            'target_url' => $target_url,
            'title' => $title,
            'rel_type' => 'follow',
            'article_topic' => $article_topic,
            'article_brief' => $article_brief,
            'article_word_count' => $article_word_count,
            'article_language' => $article_language,
            'article_tone' => 'professional',
            'estimated_credits' => $cost_cents,
            'client_name' => $business_name,
            'project_ref' => 'wp-project-' . intval($project_id),
            'order_meta' => array(
                'source' => 'adjustments_agent',
                'placement_type' => $service_type,
            ),
        ));

        if (is_wp_error($order_result)) {
            if ($order_result->get_error_code() === 'insufficient_credits') {
                $error_data = $order_result->get_error_data();
                return $this->aysa_build_insufficient_credits_payment_response(
                    $project_id,
                    intval(is_array($error_data) ? ($error_data['required_credits'] ?? 0) : 0),
                    intval(is_array($error_data) ? ($error_data['available_credits'] ?? 0) : 0),
                    $language,
                    $service_type === 'professional_article' ? 'professional_article_order' : 'seo_article_order',
                    'offpage_link_building',
                    array(
                        'portal_id' => $portal_id,
                        'portal_domain' => $portal_domain,
                    )
                );
            }
            return array(
                'module' => 'offpage_link_building',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I could not place the article order right now: ',
                    'Nu am putut plasa acum comanda de articol: ',
                    'Не успях да пусна поръчката за статия сега: '
                ) . $order_result->get_error_message(),
            );
        }

        $charged = intval($order_result['charged_credits_cents'] ?? 0);
        $included_link_applied = !empty($order_result['included_link_applied']);
        $order_ref = sanitize_text_field((string) ($order_result['order_ref'] ?? ''));
        $serviceLabel = $service_type === 'professional_article'
            ? $this->aysa_adjustments_tr($language, 'professional article', 'professional article', 'professional article')
            : $this->aysa_adjustments_tr($language, 'SEO article', 'SEO article', 'SEO article');
        $publisher_draft = null;
        $publisher_draft_note = '';
        if ($service_type === 'seo_article' && is_array($order_result)) {
            $draft_result = $this->aysa_create_publisher_article_draft_for_order($project_id, $order_result);
            if (is_wp_error($draft_result)) {
                $publisher_draft_note = $draft_result->get_error_message();
            } elseif (isset($draft_result['draft']) && is_array($draft_result['draft'])) {
                $publisher_draft = $draft_result['draft'];
            }
        }

        return array(
            'module' => 'offpage_link_building',
            'assistant_message' => match ($language) {
                'ro' => 'Am trimis comanda de ' . $serviceLabel . ' pentru ' . $portal_domain . '. '
                    . ($included_link_applied
                        ? 'Am folosit 1 link inclus din abonament, fără cost suplimentar în credite. '
                        : 'Am consumat acum ' . $charged . ' credite. ')
                    . 'Tema articolului este "' . $article_topic . '".',
                'bg' => 'Изпратих поръчката за ' . $serviceLabel . ' за ' . $portal_domain . '. '
                    . ($included_link_applied
                        ? 'Използвах 1 включен линк от абонамента, без допълнителен разход на кредити. '
                        : 'Сега изразходвах ' . $charged . ' кредита. ')
                    . 'Темата на статията е "' . $article_topic . '".',
                default => 'I placed the ' . $serviceLabel . ' order for ' . $portal_domain . '. '
                    . ($included_link_applied
                        ? 'I used 1 included subscription link, so no extra credits were charged. '
                        : 'I charged ' . $charged . ' credits now. ')
                    . 'The article topic is "' . $article_topic . '".',
            } . ($order_ref !== '' ? "\n\n" . $this->aysa_adjustments_tr($language, 'Order reference', 'Referință comandă', 'Референция на поръчка') . ': ' . $order_ref : '')
              . ($publisher_draft && !empty($publisher_draft['id'])
                    ? "\n\n" . $this->aysa_adjustments_tr($language, 'Draft article created', 'Draft de articol creat', 'Създадена чернова на статия') . ': #' . intval($publisher_draft['id'])
                    : ($publisher_draft_note !== '' ? "\n\n" . $this->aysa_adjustments_tr($language, 'Draft note', 'Notă draft', 'Бележка за чернова') . ': ' . $publisher_draft_note : '')),
            'suggested_actions' => array(
                array(
                    'label' => $this->aysa_adjustments_tr($language, 'See link building orders', 'Vezi comenzile de link building', 'Виж поръчките за link building'),
                    'message' => '',
                    'ui_action' => 'open_artifact_offpage_link_building_orders',
                ),
                array(
                    'label' => $this->aysa_adjustments_tr($language, 'See draft articles', 'Vezi drafturile de articole', 'Виж черновите на статиите'),
                    'message' => '',
                    'ui_action' => 'open_artifact_offpage_publisher_drafts',
                ),
            ),
            'ui_action' => $publisher_draft ? 'open_artifact_offpage_publisher_drafts' : 'open_artifact_offpage_link_building_orders',
            'state_meta' => array(
                'offpage_purchase_flow' => '',
                'offpage_purchase_stage' => '',
                'offpage_purchase_selection_mode' => '',
                'offpage_purchase_candidates' => array(),
                'offpage_purchase_selected_candidate' => array(),
            ),
        );
    }

    private function aysa_adjustments_list_adverlink_portals_internal($project_id, $service_type, $billing_type, $per_page = 20, $force_refresh = false) {
        $creds = $this->aysa_get_adverlink_credentials();
        $api_key = $creds['api_key'];
        $advertiser_org_id = $creds['org_id'];

        $credentials_check = $this->aysa_validate_adverlink_marketplace_credentials($creds);
        if (is_wp_error($credentials_check)) {
            return $credentials_check;
        }

        $decoded = $this->aysa_broker_request('POST', '/adverlink/v1/public/portals', array(
            'project_id' => intval($project_id),
            'api_key' => $api_key,
            'advertiser_org_id' => $advertiser_org_id,
            'service_type' => sanitize_text_field((string) $service_type),
            'billing_type' => sanitize_text_field((string) $billing_type),
            'per_page' => max(1, min(200, intval($per_page))),
            'force_refresh' => !empty($force_refresh),
        ), 45);

        if (is_wp_error($decoded)) {
            return $decoded;
        }

        $items = array();
        if (is_array($decoded)) {
            if (isset($decoded['data']) && is_array($decoded['data'])) {
                $items = $decoded['data'];
            } elseif (isset($decoded['items']) && is_array($decoded['items'])) {
                $items = $decoded['items'];
            } elseif (isset($decoded['portals']) && is_array($decoded['portals'])) {
                $items = $decoded['portals'];
            } elseif (array_keys($decoded) === range(0, count($decoded) - 1)) {
                $items = $decoded;
            }
        }

        return array(
            'items' => $items,
            'raw' => $decoded,
        );
    }

    private function aysa_adjustments_create_adverlink_order_internal($project_id, array $args) {
        $project_id = intval($project_id);
        $creds = $this->aysa_get_adverlink_credentials();
        $api_key = $creds['api_key'];
        $advertiser_org_id = $creds['org_id'];
        if ($project_id <= 0) {
            return new WP_Error('invalid_project', 'Invalid project.');
        }

        $credentials_check = $this->aysa_validate_adverlink_marketplace_credentials($creds);
        if (is_wp_error($credentials_check)) {
            return $credentials_check;
        }

        $portal_id = sanitize_text_field((string) ($args['portal_id'] ?? ''));
        $service_type = sanitize_text_field((string) ($args['service_type'] ?? 'link_insertion'));
        $billing_type = sanitize_text_field((string) ($args['billing_type'] ?? 'one_time'));
        $keyword = sanitize_text_field((string) ($args['keyword'] ?? ''));
        $target_url = esc_url_raw(trim((string) ($args['target_url'] ?? '')));
        $title = sanitize_text_field((string) ($args['title'] ?? ''));
        $rel_type = sanitize_text_field((string) ($args['rel_type'] ?? 'follow'));
        $client_name = sanitize_text_field((string) ($args['client_name'] ?? get_bloginfo('name')));
        $project_ref = sanitize_text_field((string) ($args['project_ref'] ?? ('wp-project-' . $project_id)));
        $ad_display_url = esc_url_raw(trim((string) ($args['ad_display_url'] ?? '')));
        $ad_description = sanitize_textarea_field((string) ($args['ad_description'] ?? ''));
        $duration_months = max(1, min(12, intval($args['duration_months'] ?? 1)));
        $estimated_credits = max(0, intval($args['estimated_credits'] ?? 0));
        $portal_domain = sanitize_text_field((string) ($args['portal_domain'] ?? ''));
        $order_meta = isset($args['order_meta']) && is_array($args['order_meta']) ? $args['order_meta'] : array();
        $article_topic = sanitize_text_field((string) ($args['article_topic'] ?? ''));
        $article_brief = sanitize_textarea_field((string) ($args['article_brief'] ?? ''));
        $article_word_count = max(0, intval($args['article_word_count'] ?? 0));
        $article_language = sanitize_text_field((string) ($args['article_language'] ?? ''));
        $article_tone = sanitize_text_field((string) ($args['article_tone'] ?? ''));
        $article_content_html = wp_kses_post((string) ($args['article_content_html'] ?? ''));
        $target_article_url = esc_url_raw(trim((string) ($args['target_article_url'] ?? '')));
        $article_keywords = isset($args['article_keywords']) && is_array($args['article_keywords']) ? array_values(array_filter(array_map('sanitize_text_field', $args['article_keywords']))) : array();
        $order_label = sanitize_text_field((string) ($args['order_label'] ?? ''));
        if ($order_label === '') {
            $order_label = $service_type === 'seo_ads_text'
                ? 'SEO Text Ads'
                : ($service_type === 'seo_article'
                    ? 'SEO article'
                    : ($service_type === 'professional_article' ? 'professional article' : 'link insertion'));
        }

        if ($portal_id === '' || $keyword === '' || $target_url === '' || $estimated_credits <= 0) {
            return new WP_Error('missing_fields', 'Missing required ' . $order_label . ' order fields.');
        }

        $included_link_applied = false;
        $wallet_manager = $this->aysa_get_wallet_manager_instance();
        if ($wallet_manager && $this->aysa_is_included_link_eligible_service($service_type) && method_exists($wallet_manager, 'has_included_link_available')) {
            $included_link_applied = (bool) $wallet_manager->has_included_link_available($project_id);
        }

        $required_credits = $included_link_applied ? 0 : $estimated_credits;
        $actual_credits = $this->aysa_get_project_credit_balance($project_id, true);
        $reserved_credits = intval(get_option($this->aysa_get_reserved_credits_key($project_id), 0));
        $available_credits = max(0, $actual_credits - $reserved_credits);
        if ($available_credits < $required_credits) {
            return new WP_Error(
                'insufficient_credits',
                'Insufficient credits for this ' . $order_label . ' order.',
                array(
                    'required_credits' => $required_credits,
                    'available_credits' => $available_credits,
                    'missing_credits' => max(0, $required_credits - $available_credits),
                    'order_label' => $order_label,
                )
            );
        }

        $payload = array(
            'portal_id' => $portal_id,
            'service_type' => $service_type,
            'billing_type' => $billing_type,
            'keyword' => $keyword,
            'target_url' => $target_url,
            'title' => $title,
            'rel_type' => $rel_type,
            'client_ref' => 'AYSA-' . $project_id . '-' . gmdate('YmdHis') . '-' . wp_generate_password(6, false, false),
            'client_name' => $client_name,
            'project_ref' => $project_ref,
            'ad_display_url' => $ad_display_url,
            'ad_description' => $ad_description,
            'duration_months' => $duration_months,
        );
        if ($service_type === 'seo_article' || $service_type === 'professional_article') {
            if ($article_topic !== '') {
                $payload['article_topic'] = $article_topic;
            }
            if ($article_brief !== '') {
                $payload['article_brief'] = $article_brief;
            }
            if ($article_word_count > 0) {
                $payload['article_word_count'] = $article_word_count;
            }
            if ($article_language !== '') {
                $payload['article_language'] = $article_language;
            }
            if ($article_tone !== '') {
                $payload['article_tone'] = $article_tone;
            }
            if (!empty($article_keywords)) {
                $payload['article_keywords'] = array_values(array_unique($article_keywords));
            }
            if ($article_content_html !== '') {
                $payload['article_content_html'] = $article_content_html;
            }
        } elseif ($service_type === 'seo_ads_text' && $target_article_url !== '') {
            $payload['target_article_url'] = $target_article_url;
        }

        $idempotency_key = 'aysa-' . $project_id . '-' . wp_generate_password(16, false, false);
        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/public/orders/create', array(
            'project_id' => $project_id,
            'api_key' => $api_key,
            'advertiser_org_id' => $advertiser_org_id,
            'idempotency_key' => $idempotency_key,
            'payload' => $payload,
        ), 60);
        if (is_wp_error($broker_result)) {
            return $broker_result;
        }

        $decoded = is_array($broker_result['provider_response'] ?? null)
            ? $broker_result['provider_response']
            : (is_array($broker_result['data'] ?? null) ? $broker_result['data'] : $broker_result);
        $order_ref = is_array($decoded) ? (string) $this->aysa_extract_external_order_ref($decoded) : '';
        $order_status = is_array($decoded) ? (string) $this->aysa_extract_external_order_status($decoded) : '';
        if ($order_ref === '') {
            $order_ref = 'local-' . time();
        }
        if ($order_status === '') {
            $order_status = 'created';
        }

        $external_cost_cents = $this->aysa_extract_external_order_cost_cents($decoded);
        if ($external_cost_cents <= 0) {
            $external_cost_cents = $estimated_credits;
        }
        $charged_credits_cents = $included_link_applied ? 0 : $external_cost_cents;
        $included_link_consumption = false;
        if ($included_link_applied && $wallet_manager && method_exists($wallet_manager, 'consume_included_link')) {
            $included_link_consumption = $wallet_manager->consume_included_link(
                $project_id,
                'offpage_create_order_' . $order_ref,
                array(
                    'service_type' => $service_type,
                    'portal_id' => $portal_id,
                    'portal_domain' => $portal_domain,
                    'order_ref' => $order_ref,
                    'provider_cost_cents' => $external_cost_cents,
                )
            );
            if ($included_link_consumption === false) {
                $included_link_applied = false;
                $charged_credits_cents = $external_cost_cents;
            }
        }

        $orders = $this->aysa_get_adverlink_orders($project_id);
        $orders[] = array(
            'order_ref' => $order_ref,
            'status' => $order_status,
            'project_id' => $project_id,
            'portal_id' => $portal_id,
            'portal_domain' => $portal_domain,
            'service_type' => $service_type,
            'billing_type' => $billing_type,
            'keyword' => $keyword,
            'target_url' => $target_url,
            'title' => $title,
            'rel_type' => $rel_type,
            'client_name' => $client_name,
            'project_ref' => $project_ref,
            'ad_display_url' => $ad_display_url,
            'ad_description' => $ad_description,
            'duration_months' => $duration_months,
            'estimated_credits' => 0,
            'credits_released' => true,
            'charged_credits_cents' => $charged_credits_cents,
            'provider_cost_cents' => $external_cost_cents,
            'included_link_applied' => $included_link_applied,
            'created_at' => current_time('mysql'),
            'updated_at' => current_time('mysql'),
            'order_meta' => $order_meta,
            'verification' => $this->aysa_extract_external_verification($decoded),
            'subscription' => $this->aysa_extract_external_subscription($decoded),
            'raw_response' => $decoded,
        );
        if (count($orders) > 200) {
            $orders = array_slice($orders, -200);
        }
        $this->aysa_save_adverlink_orders($project_id, $orders);

        if ($charged_credits_cents > 0) {
            $this->aysa_wallet_track_server_usage(
                $project_id,
                'wp_ajax/aysa_adverlink_create_order',
                $charged_credits_cents,
                'offpage_create_order_' . $order_ref,
                array(
                    'module' => 'offpage',
                    'action_key' => 'aysa_adverlink_create_order',
                    'service_type' => $service_type,
                    'portal_id' => $portal_id,
                    'order_ref' => $order_ref,
                    'provider_cost_cents' => $external_cost_cents,
                    'included_link_applied' => $included_link_applied,
                )
            );
        }

        $actual_credits_after = $this->aysa_get_project_credit_balance($project_id, true);
        $reserved_credits_after = intval(get_option($this->aysa_get_reserved_credits_key($project_id), 0));

        return array(
            'order_ref' => $order_ref,
            'status' => $order_status,
            'charged_credits_cents' => $charged_credits_cents,
            'provider_cost_cents' => $external_cost_cents,
            'included_link_applied' => $included_link_applied,
            'included_link_snapshot' => is_array($included_link_consumption) ? $included_link_consumption : array(),
            'actual_credits' => $actual_credits_after,
            'reserved_credits' => $reserved_credits_after,
            'available_credits' => max(0, $actual_credits_after - $reserved_credits_after),
            'raw' => $decoded,
        );
    }

    private function aysa_adjustments_build_seo_text_ads_shortlist(array $items, array $setup_state) {
        $summary_general = isset($setup_state['general']) && is_array($setup_state['general']) ? $setup_state['general'] : array();
        $summary_competition = isset($setup_state['competition']) && is_array($setup_state['competition']) ? $setup_state['competition'] : array();
        $industry = strtolower(trim((string) ($summary_general['industry'] ?? '')));
        $business = strtolower(trim((string) ($summary_general['company_name'] ?? '')));
        $site_host = strtolower((string) wp_parse_url((string) ($summary_general['website'] ?? home_url('/')), PHP_URL_HOST));
        $seed_keywords = array();
        for ($i = 1; $i <= 5; $i++) {
            $keyword = strtolower(trim((string) ($summary_competition['keyword' . $i] ?? '')));
            if ($keyword !== '') {
                $seed_keywords[] = $keyword;
            }
        }

        $prepared = array();
        foreach ($items as $row) {
            if (!is_array($row)) {
                continue;
            }
            $domain = $this->aysa_adjustments_extract_portal_domain($row);
            if ($domain === '' || ($site_host !== '' && $domain === $site_host)) {
                continue;
            }
            $authority = $this->aysa_adjustments_extract_portal_authority($row);
            if ($authority < 10) {
                continue;
            }
            $monthly_cost = $this->aysa_adjustments_extract_portal_monthly_cost_cents($row);
            if ($monthly_cost <= 0) {
                continue;
            }

            $score = $authority;
            $haystack = strtolower(wp_json_encode($row));
            if ($industry !== '' && str_contains($haystack, $industry)) {
                $score += 4;
            }
            foreach ($seed_keywords as $seed) {
                if ($seed !== '' && str_contains($haystack, $seed)) {
                    $score += 2;
                }
            }
            if ($business !== '' && str_contains($haystack, $business)) {
                $score -= 3;
            }

            $prepared[] = array(
                'portal_id' => sanitize_text_field((string) ($row['portal_id'] ?? $row['id'] ?? '')),
                'domain' => $domain,
                'authority' => $authority,
                'monthly_cost_cents' => $monthly_cost,
                'currency' => sanitize_text_field((string) ($row['currency'] ?? 'credits')),
                'score' => $score,
            );
        }

        usort($prepared, static function(array $a, array $b): int {
            $scoreCompare = (float) ($b['score'] ?? 0) <=> (float) ($a['score'] ?? 0);
            if ($scoreCompare !== 0) {
                return $scoreCompare;
            }
            return intval($a['monthly_cost_cents'] ?? 0) <=> intval($b['monthly_cost_cents'] ?? 0);
        });

        $deduped = array();
        $seen = array();
        foreach ($prepared as $portal) {
            $key = strtolower((string) ($portal['domain'] ?? ''));
            if ($key === '' || isset($seen[$key])) {
                continue;
            }
            $seen[$key] = true;
            $deduped[] = $portal;
            if (count($deduped) >= 3) {
                break;
            }
        }

        return $deduped;
    }

    private function aysa_adjustments_extract_portal_domain(array $row) {
        foreach (array('domain', 'portal_domain', 'host', 'hostname', 'site', 'site_domain') as $key) {
            $value = trim((string) ($row[$key] ?? ''));
            if ($value !== '') {
                $value = preg_replace('#^https?://#i', '', $value);
                return strtolower(trim((string) $value, "/ \t\n\r\0\x0B"));
            }
        }
        return '';
    }

    private function aysa_adjustments_extract_portal_authority(array $row) {
        foreach (array('authority', 'domain_authority', 'domainAuthority', 'da', 'dr', 'trust_flow', 'trustFlow') as $key) {
            if (isset($row[$key]) && $row[$key] !== '') {
                return (float) $row[$key];
            }
        }
        $metrics = isset($row['metrics']) && is_array($row['metrics']) ? $row['metrics'] : array();
        foreach (array('domain_authority', 'domainAuthority', 'da', 'dr', 'trust_flow', 'trustFlow', 'ur_medium', 'qualitative_evaluation') as $key) {
            if (isset($metrics[$key]) && $metrics[$key] !== '') {
                return (float) $metrics[$key];
            }
        }
        return 0.0;
    }

    private function aysa_adjustments_extract_portal_monthly_cost_cents(array $row) {
        foreach (array('price_cents', 'monthly_price_cents', 'billing_price_cents', 'estimated_credits', 'credits_cents', 'price') as $key) {
            if (!isset($row[$key]) || $row[$key] === '') {
                continue;
            }
            $value = $row[$key];
            if (is_numeric($value)) {
                $int = (int) round((float) $value);
                if ($int > 0) {
                    return $int;
                }
            }
        }
        $services = isset($row['services']) && is_array($row['services']) ? $row['services'] : array();
        foreach ($services as $service) {
            if (!is_array($service)) {
                continue;
            }
            $serviceType = sanitize_text_field((string) ($service['service_type'] ?? ''));
            $billingType = sanitize_text_field((string) ($service['billing_type'] ?? ''));
            if ($serviceType !== 'seo_ads_text' || $billingType !== 'monthly') {
                continue;
            }
            foreach (array('sale_price_eur', 'purchase_price_eur', 'price_eur') as $priceKey) {
                if (!isset($service[$priceKey]) || $service[$priceKey] === '') {
                    continue;
                }
                $value = $service[$priceKey];
                if (is_numeric($value)) {
                    $credits = (int) round(((float) $value) * 100);
                    if ($credits > 0) {
                        return $credits;
                    }
                }
            }
        }
        return 0;
    }

    private function aysa_adjustments_build_seo_text_ads_description($business_name, $keyword) {
        $base = trim($business_name . ' - ' . $keyword);
        $description = 'Descopera ' . $keyword . ' la ' . $business_name . '. Comanda rapid online.';
        if (function_exists('mb_strlen') && mb_strlen($description, 'UTF-8') > 90) {
            $description = mb_substr($description, 0, 87, 'UTF-8') . '...';
        } elseif (!function_exists('mb_strlen') && strlen($description) > 90) {
            $description = substr($description, 0, 87) . '...';
        }
        return sanitize_text_field($description !== '' ? $description : $base);
    }

    private function aysa_adjustments_create_content_item($project_id, $setup_user_id, $payload, $language) {
        $project_id = intval($project_id);
        $setup_user_id = intval($setup_user_id);
        $payload = is_array($payload) ? $payload : array();

        $content_type = sanitize_key((string) ($payload['content_type'] ?? 'post'));
        if (!in_array($content_type, array('post', 'page', 'product', 'category'), true)) {
            $content_type = 'post';
        }
        $title = sanitize_text_field((string) ($payload['title'] ?? ''));
        $publish_mode = sanitize_key((string) ($payload['publish_mode'] ?? 'draft'));
        if (!in_array($publish_mode, array('draft', 'publish'), true)) {
            $publish_mode = 'draft';
        }

        if ($title === '') {
            return array(
                'module' => 'content_strategy',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I need a clearer title before I can create this content item.',
                    'Am nevoie de un titlu mai clar înainte să pot crea acest item de conținut.',
                    'Имам нужда от по-ясно заглавие, преди да мога да създам този content елемент.'
                ),
            );
        }

        if (!current_user_can('edit_posts')) {
            return array(
                'module' => 'content_strategy',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I do not have permission to create content items in WordPress.',
                    'Nu am permisiunea să creez itemi de conținut în WordPress.',
                    'Нямам право да създавам content елементи в WordPress.'
                ),
            );
        }

        $main_keyword = $title;
        $edit_url = '';
        $view_url = '';
        $entity_id = 0;
        $queued_generation = false;
        $pending_queue_key = '';
        $reused_existing_entity = false;

        $existing_entity = $this->aysa_adjustments_find_existing_content_entity($content_type, $title);
        if (is_array($existing_entity) && !empty($existing_entity['entity_id'])) {
            $entity_id = intval($existing_entity['entity_id']);
            $edit_url = (string) ($existing_entity['edit_url'] ?? '');
            $view_url = (string) ($existing_entity['view_url'] ?? '');
            $reused_existing_entity = true;
        }

        if ($entity_id <= 0 && $content_type === 'category') {
            $term = wp_insert_term($title, 'category', array(
                'slug' => sanitize_title($title),
            ));
            if (is_wp_error($term)) {
                return array(
                    'module' => 'content_strategy',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not create that category right now: ',
                        'Nu am putut crea categoria acum: ',
                        'Не успях да създам категорията сега: '
                    ) . $term->get_error_message(),
                );
            }
            $entity_id = intval($term['term_id'] ?? 0);
            $edit_url = admin_url('term.php?taxonomy=category&tag_ID=' . $entity_id . '&post_type=post');
            $view_url = get_term_link($entity_id, 'category');
            if (is_wp_error($view_url)) {
                $view_url = '';
            }
        } elseif ($entity_id <= 0) {
            if ($content_type === 'post' && class_exists('Aysa_Content_Strategy_Create_Post')) {
                $result = Aysa_Content_Strategy_Create_Post::create_post_programmatically(array(
                    'title' => $title,
                    'keywords' => array($main_keyword),
                    'intent' => 'I',
                    'associated_pages' => array(),
                    'associated_page' => '',
                    'strategy_id' => 0,
                    'source' => 'adjustments-agent',
                ));
                if (is_wp_error($result)) {
                    return array(
                        'module' => 'content_strategy',
                        'assistant_message' => $this->aysa_adjustments_tr(
                            $language,
                            'I could not create that article right now: ',
                            'Nu am putut crea acel articol acum: ',
                            'Не успях да създам тази статия сега: '
                        ) . $result->get_error_message(),
                    );
                }

                $entity_id = intval($result['post_id'] ?? 0);
                $edit_url = (string) ($result['edit_url'] ?? '');
                $view_url = (string) ($result['view_url'] ?? '');
                $queued_generation = !empty($result['generation_queued']);
            } else {
            $post_status = $publish_mode === 'publish' ? 'publish' : 'draft';
            $post_data = array(
                'post_title' => $title,
                'post_content' => '',
                'post_status' => $post_status,
                'post_type' => $content_type,
                'post_author' => get_current_user_id(),
            );
            $entity_id = wp_insert_post($post_data, true);
            if (is_wp_error($entity_id)) {
                return array(
                    'module' => 'content_strategy',
                    'assistant_message' => $this->aysa_adjustments_tr(
                        $language,
                        'I could not create that content item right now: ',
                        'Nu am putut crea acel item de conținut acum: ',
                        'Не успях да създам този content елемент сега: '
                    ) . $entity_id->get_error_message(),
                );
            }
            $entity_id = intval($entity_id);
            $edit_url = (string) get_edit_post_link($entity_id, 'raw');
            $view_url = (string) get_permalink($entity_id);

            if ($content_type === 'post') {
                $keyword_data = array(
                    'article_topic'  => $title,
                    'keyword'        => $main_keyword,
                    'second_keyword' => '',
                    'intent'         => 'I',
                    'source'         => 'adjustments-agent',
                );
                update_post_meta($entity_id, 'aysa_keyword_data', $keyword_data);
                update_post_meta($entity_id, 'aysa_main_keyword', $main_keyword);
                update_post_meta($entity_id, 'aysa_secondary_keywords', '');
                update_post_meta($entity_id, 'aysa_search_intent', 'I');
                update_post_meta($entity_id, 'aysa_generation_source', 'adjustments-agent');

                $generation_payload = array(
                    'project_id' => $project_id,
                    'post_id' => $entity_id,
                    'title' => $title,
                    'main_keyword' => $main_keyword,
                    'secondary_keywords' => array(),
                    'associated_pages' => array(),
                );
                update_post_meta($entity_id, '_aysa_existing_post_generation_status', 'queued');
                update_post_meta($entity_id, '_aysa_existing_post_generation_queued_at', current_time('mysql'));
                delete_post_meta($entity_id, '_aysa_existing_post_generation_error');
                wp_schedule_single_event(time() + 2, 'aysa_generate_existing_strategy_post', array($generation_payload));
                $queued_generation = true;
            } elseif (in_array($content_type, array('page', 'product'), true)) {
                $queued_generation = true;
            }
            }
        }

        if ($entity_id > 0 && $content_type !== 'category') {
            update_post_meta($entity_id, '_aysa_adjustments_project_id', $project_id);
            update_post_meta($entity_id, '_aysa_adjustments_notification_sent', '0');
        }

        if ($entity_id > 0 && $content_type !== 'post' && in_array($content_type, array('page', 'product', 'category'), true)) {
            $pending_queue_key = $this->aysa_adjustments_queue_created_entity_generation($project_id, array(
                'entity_id' => $entity_id,
                'content_type' => $content_type,
                'title' => $title,
                'keyword' => $main_keyword,
                'edit_url' => $edit_url,
                'view_url' => $view_url,
                'publish_mode' => $publish_mode,
            ));
            if ($pending_queue_key !== '') {
                $queued_generation = true;
            }
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_content_create_item',
            'adjustments_content_create_item',
            array(
                'module' => 'content_strategy',
                'content_type' => $content_type,
                'entity_id' => $entity_id,
                'publish_mode' => $publish_mode,
            ),
            30,
            '/v1/conversation/adjustments/content-create'
        );

        $type_label = match ($content_type) {
            'page' => $this->aysa_adjustments_tr($language, 'page', 'pagină', 'страница'),
            'product' => $this->aysa_adjustments_tr($language, 'product', 'produs', 'продукт'),
            'category' => $this->aysa_adjustments_tr($language, 'category', 'categorie', 'категория'),
            default => $this->aysa_adjustments_tr($language, 'article', 'articol', 'статия'),
        };

        $status_label = $publish_mode === 'publish'
            ? $this->aysa_adjustments_tr($language, 'published', 'publicat', 'публикуван')
            : $this->aysa_adjustments_tr($language, 'created as draft', 'creat ca draft', 'създаден като чернова');

        if ($queued_generation) {
            $message = match ($content_type) {
                'page' => match ($language) {
                    'ro' => 'Lucrez acum la pagina "' . $title . '". Îi pregătesc conținutul și optimizarea SEO și te voi anunța aici imediat ce este gata.',
                    'bg' => 'Работя по страницата "' . $title . '". Подготвям съдържанието и SEO оптимизацията и ще те уведомя тук веднага щом е готова.',
                    default => 'I am working on the page "' . $title . '" now. I am preparing the content and SEO optimization and I will notify you here as soon as it is ready.',
                },
                'product' => match ($language) {
                    'ro' => 'Lucrez acum la produsul "' . $title . '". Îi pregătesc conținutul și optimizarea SEO și te voi anunța aici imediat ce este gata.',
                    'bg' => 'Работя по продукта "' . $title . '". Подготвям съдържанието и SEO оптимизацията и ще те уведомя тук веднага щом е готов.',
                    default => 'I am working on the product "' . $title . '" now. I am preparing the content and SEO optimization and I will notify you here as soon as it is ready.',
                },
                'category' => match ($language) {
                    'ro' => 'Lucrez acum la categoria "' . $title . '". Îi pregătesc optimizarea SEO și te voi anunța aici imediat ce este gata.',
                    'bg' => 'Работя по категорията "' . $title . '". Подготвям SEO оптимизацията и ще те уведомя тук веднага щом е готова.',
                    default => 'I am working on the category "' . $title . '" now. I am preparing the SEO optimization and I will notify you here as soon as it is ready.',
                },
                default => match ($language) {
                    'ro' => 'Lucrez acum la articolul "' . $title . '". Încă îl generez și te voi anunța aici imediat ce este gata.',
                    'bg' => 'Работя по статията "' . $title . '". Все още я генерирам и ще те уведомя тук веднага щом е готова.',
                    default => 'I am working on the article "' . $title . '" now. I am still generating it and I will notify you here as soon as it is ready.',
                },
            };
        } elseif ($reused_existing_entity) {
            $message = match ($language) {
                'ro' => 'Am găsit deja ' . $type_label . 'ul "' . $title . '" și nu l-am creat din nou. Dacă vrei, îl pot optimiza sau îți pot arăta linkul final.',
                'bg' => 'Вече намерих ' . $type_label . ' "' . $title . '" и не го създадох отново. Ако искаш, мога да го оптимизирам или да ти покажа крайния линк.',
                default => 'I already found the ' . $type_label . ' "' . $title . '" and did not create it again. If you want, I can optimize it or show you the final link.',
            };
            if ($view_url !== '') {
                $message .= "\n" . $this->aysa_adjustments_tr($language, 'Final link:', 'Link final:', 'Финален линк:') . ' ' . $view_url;
            } elseif ($edit_url !== '') {
                $message .= "\n" . $this->aysa_adjustments_tr($language, 'Edit link:', 'Link de editare:', 'Линк за редакция:') . ' ' . $edit_url;
            }
        } else {
            $message = match ($language) {
                'ro' => 'Am ' . $status_label . ' ' . $type_label . 'ul "' . $title . '".',
                'bg' => 'Създадох ' . $status_label . ' ' . $type_label . ' "' . $title . '".',
                default => 'I ' . $status_label . ' the ' . $type_label . ' "' . $title . '".',
            };
            if ($edit_url !== '') {
                $message .= "\n" . $this->aysa_adjustments_tr($language, 'Edit link:', 'Link de editare:', 'Линк за редакция:') . ' ' . $edit_url;
            }
            if ($view_url !== '') {
                $message .= "\n" . $this->aysa_adjustments_tr($language, 'View link:', 'Link de vizualizare:', 'Линк за преглед:') . ' ' . $view_url;
            }
        }

        $response = array(
            'module' => 'content_strategy',
            'assistant_message' => $message,
            'suggested_actions' => array(
                array(
                    'label' => $this->aysa_adjustments_tr($language, 'See content plan', 'Vezi planul de conținut', 'Виж content плана'),
                    'message' => '',
                    'ui_action' => 'open_artifact_content_strategy',
                ),
            ),
            'state_meta' => array(
                'last_created_content_type' => $content_type,
                'last_created_entity_id' => $entity_id,
            ),
        );
        if ($queued_generation && $content_type === 'post' && $entity_id > 0) {
            $response['state_meta']['pending_generated_post_id'] = $entity_id;
            $response['state_meta']['pending_generated_post_title'] = $title;
        }
        if ($queued_generation && $entity_id > 0) {
            $response['state_meta']['pending_generated_item_id'] = $entity_id;
            $response['state_meta']['pending_generated_item_title'] = $title;
            $response['state_meta']['pending_generated_item_type'] = $content_type;
            if ($pending_queue_key !== '') {
                $response['state_meta']['pending_generated_item_key'] = $pending_queue_key;
            }
        }
        return $response;
    }

    private function aysa_adjustments_optimize_content_item($project_id, $payload, $language) {
        $project_id = intval($project_id);
        $payload = is_array($payload) ? $payload : array();

        $entity_id = intval($payload['entity_id'] ?? 0);
        $entity_type = sanitize_text_field((string) ($payload['entity_type'] ?? ''));
        $content_type = sanitize_key((string) ($payload['content_type'] ?? 'page'));
        $title = sanitize_text_field((string) ($payload['title'] ?? ''));
        $keyword = sanitize_text_field((string) ($payload['keyword'] ?? ''));
        $edit_url = esc_url_raw((string) ($payload['edit_url'] ?? ''));
        $view_url = esc_url_raw((string) ($payload['view_url'] ?? ''));

        if ($entity_id <= 0 || $entity_type === '' || $keyword === '') {
            return array(
                'module' => 'onpage_optimization',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I need a valid page, product, category, or article with an assigned keyword before I can optimize it.',
                    'Am nevoie de o pagină, un produs, o categorie sau un articol valid, cu un keyword asociat, înainte să îl pot optimiza.',
                    'Имам нужда от валидна страница, продукт, категория или статия с назначен keyword, преди да мога да я оптимизирам.'
                ),
            );
        }

        if ($this->aysa_adjustments_has_pending_seo_review($entity_id, $entity_type, $keyword)) {
            return array(
                'module' => 'onpage_optimization',
                'assistant_message' => match ($language) {
                    'ro' => 'Am deja o optimizare în lucru pentru "' . $title . '" pe keyword-ul "' . $keyword . '". O las să termine și te anunț aici imediat ce este gata.',
                    'bg' => 'Вече имам текуща оптимизация за "' . $title . '" по keyword-а "' . $keyword . '". Оставям я да приключи и ще те уведомя тук веднага щом е готова.',
                    default => 'I already have an optimization running for "' . $title . '" on the keyword "' . $keyword . '". I will let it finish and notify you here as soon as it is ready.',
                },
            );
        }

        $queue_key = $this->aysa_adjustments_queue_created_entity_generation($project_id, array(
            'entity_id' => $entity_id,
            'content_type' => $content_type,
            'title' => $title,
            'keyword' => $keyword,
            'edit_url' => $edit_url,
            'view_url' => $view_url,
            'publish_mode' => 'draft',
        ));

        return array(
            'module' => 'onpage_optimization',
            'assistant_message' => match ($language) {
                'ro' => 'Lucrez acum la optimizarea pentru "' . $title . '" pe keyword-ul "' . $keyword . '". O trimit în pipeline-ul SEO existent și te anunț aici imediat ce este gata.',
                'bg' => 'Работя сега по оптимизацията за "' . $title . '" по keyword-а "' . $keyword . '". Изпращам я към съществуващия SEO pipeline и ще те уведомя тук веднага щом е готова.',
                default => 'I am working now on the optimization for "' . $title . '" on the keyword "' . $keyword . '". I am sending it through the existing SEO pipeline and I will notify you here as soon as it is ready.',
            },
            'state_meta' => array(
                'pending_generated_item_id' => $entity_id,
                'pending_generated_item_title' => $title,
                'pending_generated_item_type' => $content_type,
                'pending_generated_item_key' => $queue_key,
            ),
        );
    }

    private function aysa_adjustments_collect_generation_notifications($project_id, $language) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array('pending' => array(), 'completed' => array(), 'failed' => array());
        }

        $query = new WP_Query(array(
            'post_type' => 'post',
            'post_status' => array('draft', 'publish', 'future', 'pending', 'private'),
            'posts_per_page' => 20,
            'orderby' => 'date',
            'order' => 'DESC',
            'fields' => 'ids',
            'meta_query' => array(
                'relation' => 'AND',
                array(
                    'key' => 'aysa_generation_source',
                    'value' => 'adjustments-agent',
                ),
                array(
                    'key' => '_aysa_adjustments_project_id',
                    'value' => $project_id,
                    'compare' => '=',
                    'type' => 'NUMERIC',
                ),
                array(
                    'key' => '_aysa_adjustments_notification_sent',
                    'value' => '1',
                    'compare' => '!=',
                ),
            ),
        ));

        $pending = array();
        $completed = array();
        $failed = array();

        $keyword_refresh_status = $this->aysa_keyword_discovery_get_status($project_id);
        $keyword_refresh_state = (string) ($keyword_refresh_status['status'] ?? 'idle');
        $keyword_refresh_notified = (string) ($keyword_refresh_status['notification_sent'] ?? '0') === '1';
        $keyword_refresh_item = $this->aysa_keyword_discovery_build_pending_item($project_id);
        if ($keyword_refresh_state === 'running') {
            $pending[] = array(
                'item_key' => (string) ($keyword_refresh_item['item_key'] ?? ('keyword_refresh:' . $project_id)),
                'item_type' => 'keyword_refresh',
                'post_id' => intval($keyword_refresh_item['post_id'] ?? $project_id),
                'title' => (string) ($keyword_refresh_item['title'] ?? 'Keyword refresh'),
                'message' => match ($language) {
                    'ro' => 'Încă lucrez la refresh-ul de keyworduri. Îți arăt imediat rezultatul final când este gata.',
                    'bg' => 'Все още работя по keyword refresh-а. Ще ти покажа крайния резултат веднага щом е готов.',
                    default => 'I am still working on the keyword refresh. I will show you the final result as soon as it is ready.',
                },
            );
        } elseif ($keyword_refresh_state === 'completed' && !$keyword_refresh_notified) {
            $completed[] = array(
                'item_key' => (string) ($keyword_refresh_item['item_key'] ?? ('keyword_refresh:' . $project_id)),
                'post_id' => intval($keyword_refresh_item['post_id'] ?? $project_id),
                'title' => (string) ($keyword_refresh_item['title'] ?? 'Keyword refresh'),
                'message' => match ($language) {
                    'ro' => 'Keyword refresh este gata. Am pregătit ' . intval($keyword_refresh_status['staged'] ?? 0) . ' keyworduri, cu ' . intval($keyword_refresh_status['inserted'] ?? 0) . ' inserate și ' . intval($keyword_refresh_status['updated'] ?? 0) . ' actualizate.'
                        . ((string) ($keyword_refresh_status['metrics_notice'] ?? '') !== '' ? ' ' . (string) $keyword_refresh_status['metrics_notice'] : ''),
                    'bg' => 'Keyword refresh-ът е готов. Подготвих ' . intval($keyword_refresh_status['staged'] ?? 0) . ' keyword-а, с ' . intval($keyword_refresh_status['inserted'] ?? 0) . ' нови и ' . intval($keyword_refresh_status['updated'] ?? 0) . ' обновени.'
                        . ((string) ($keyword_refresh_status['metrics_notice'] ?? '') !== '' ? ' ' . (string) $keyword_refresh_status['metrics_notice'] : ''),
                    default => 'The keyword refresh is ready. I prepared ' . intval($keyword_refresh_status['staged'] ?? 0) . ' keywords, with ' . intval($keyword_refresh_status['inserted'] ?? 0) . ' inserted and ' . intval($keyword_refresh_status['updated'] ?? 0) . ' updated.'
                        . ((string) ($keyword_refresh_status['metrics_notice'] ?? '') !== '' ? ' ' . (string) $keyword_refresh_status['metrics_notice'] : ''),
                },
            );
            $this->notify_app_aysa_conversation_event(
                'task_completed',
                'Keyword refresh',
                admin_url('admin.php?page=aysa-research&tab=keyword-discovery'),
                array(
                    'project_id' => $project_id,
                    'resource_type' => 'project',
                    'resource_id' => (string) $project_id,
                    'job_type' => 'keyword_refresh',
                    'staged' => intval($keyword_refresh_status['staged'] ?? 0),
                    'inserted' => intval($keyword_refresh_status['inserted'] ?? 0),
                    'updated' => intval($keyword_refresh_status['updated'] ?? 0),
                )
            );
            $keyword_refresh_status['notification_sent'] = '1';
            $this->aysa_keyword_discovery_save_status($project_id, $keyword_refresh_status);
        } elseif ($keyword_refresh_state === 'failed' && !$keyword_refresh_notified) {
            $failed[] = array(
                'item_key' => (string) ($keyword_refresh_item['item_key'] ?? ('keyword_refresh:' . $project_id)),
                'post_id' => intval($keyword_refresh_item['post_id'] ?? $project_id),
                'title' => (string) ($keyword_refresh_item['title'] ?? 'Keyword refresh'),
                'message' => match ($language) {
                    'ro' => 'Keyword refresh a eșuat.' . ((string) ($keyword_refresh_status['last_error'] ?? '') !== '' ? ' ' . (string) $keyword_refresh_status['last_error'] : ''),
                    'bg' => 'Keyword refresh-ът се провали.' . ((string) ($keyword_refresh_status['last_error'] ?? '') !== '' ? ' ' . (string) $keyword_refresh_status['last_error'] : ''),
                    default => 'The keyword refresh failed.' . ((string) ($keyword_refresh_status['last_error'] ?? '') !== '' ? ' ' . (string) $keyword_refresh_status['last_error'] : ''),
                },
            );
            $this->notify_app_aysa_conversation_event(
                'task_failed',
                'Keyword refresh',
                admin_url('admin.php?page=aysa-research&tab=keyword-discovery'),
                array(
                    'project_id' => $project_id,
                    'resource_type' => 'project',
                    'resource_id' => (string) $project_id,
                    'job_type' => 'keyword_refresh',
                    'last_error' => (string) ($keyword_refresh_status['last_error'] ?? ''),
                )
            );
            $keyword_refresh_status['notification_sent'] = '1';
            $this->aysa_keyword_discovery_save_status($project_id, $keyword_refresh_status);
        }

        foreach ((array) $query->posts as $post_id) {
            $post_id = intval($post_id);
            if ($post_id <= 0) {
                continue;
            }
            $status = (string) get_post_meta($post_id, '_aysa_existing_post_generation_status', true);
            $title = (string) get_the_title($post_id);
            if ($status === 'generated') {
                $post_status = (string) get_post_status($post_id);
                $url = $post_status === 'publish'
                    ? (string) get_permalink($post_id)
                    : (string) get_edit_post_link($post_id, 'raw');
                $completed[] = array(
                    'post_id' => $post_id,
                    'title' => $title,
                    'url' => $url,
                    'message' => match ($language) {
                        'ro' => 'Articolul "' . $title . '" este gata.' . ($url !== '' ? ' Îți las aici linkul final: ' . esc_url_raw($url) : ''),
                        'bg' => 'Статията "' . $title . '" е готова.' . ($url !== '' ? ' Оставям ти финалния линк тук: ' . esc_url_raw($url) : ''),
                        default => 'The article "' . $title . '" is ready.' . ($url !== '' ? ' Here is the final link: ' . esc_url_raw($url) : ''),
                    },
                );
                update_post_meta($post_id, '_aysa_adjustments_notification_sent', '1');
                continue;
            }
            if ($status === 'failed') {
                $error = (string) get_post_meta($post_id, '_aysa_existing_post_generation_error', true);
                $failed[] = array(
                    'post_id' => $post_id,
                    'title' => $title,
                    'message' => match ($language) {
                        'ro' => 'Nu am reușit să finalizez articolul "' . $title . '"' . ($error !== '' ? ': ' . $error : '.'),
                        'bg' => 'Не успях да завърша статията "' . $title . '"' . ($error !== '' ? ': ' . $error : '.'),
                        default => 'I could not finish the article "' . $title . '"' . ($error !== '' ? ': ' . $error : '.'),
                    },
                );
                update_post_meta($post_id, '_aysa_adjustments_notification_sent', '1');
                continue;
            }

            $pending[] = array(
                'item_key' => 'post:' . $post_id,
                'item_type' => 'post',
                'post_id' => $post_id,
                'title' => $title,
                'message' => match ($language) {
                    'ro' => 'Încă lucrez la articolul "' . $title . '". Te anunț aici imediat ce este gata.',
                    'bg' => 'Все още работя по статията "' . $title . '". Ще те уведомя тук веднага щом е готова.',
                    default => 'I am still working on the article "' . $title . '". I will notify you here as soon as it is ready.',
                },
            );
        }

        $queue = $this->aysa_adjustments_get_generation_queue($project_id);
        foreach ($queue as $item_key => $item) {
            if (!is_array($item)) {
                continue;
            }
            $title = (string) ($item['title'] ?? '');
            $item_type = (string) ($item['item_type'] ?? 'item');
            $entity_id = intval($item['entity_id'] ?? 0);
            $status = (string) ($item['status'] ?? 'queued');
            if (in_array($status, array('queued', 'processing'), true)) {
                $matched_review = $this->aysa_adjustments_find_pending_review_for_entity(
                    $entity_id,
                    (string) ($item['entity_processor_type'] ?? ''),
                    (string) ($item['keyword'] ?? '')
                );
                if (!empty($matched_review['id'])) {
                    $item['status'] = 'generated';
                    $item['review_id'] = intval($matched_review['id']);
                    $item['updated_at'] = current_time('mysql');
                    $queue[$item_key] = $item;
                    $status = 'generated';
                }
            }
            if ($status === 'generated') {
                $edit_url = (string) ($item['edit_url'] ?? '');
                $view_url = (string) ($item['view_url'] ?? '');
                $link = $view_url !== '' ? $view_url : $edit_url;
                $item_type_label = match ($item_type) {
                    'page' => $this->aysa_adjustments_tr($language, 'page', 'pagina', 'страницата'),
                    'product' => $this->aysa_adjustments_tr($language, 'product', 'produsul', 'продуктът'),
                    'category' => $this->aysa_adjustments_tr($language, 'category', 'categoria', 'категорията'),
                    default => $this->aysa_adjustments_tr($language, 'item', 'elementul', 'елементът'),
                };
                $completed[] = array(
                    'item_key' => (string) $item_key,
                    'item_type' => $item_type,
                    'post_id' => $entity_id,
                    'title' => $title,
                    'message' => match ($language) {
                        'ro' => ucfirst($item_type_label) . ' "' . $title . '" este gata.' . ($link !== '' ? ' Îți las aici linkul final: ' . esc_url_raw($link) : ''),
                        'bg' => ucfirst($item_type_label) . ' "' . $title . '" е готов.' . ($link !== '' ? ' Оставям ти финалния линк тук: ' . esc_url_raw($link) : ''),
                        default => 'The ' . $item_type_label . ' "' . $title . '" is ready.' . ($link !== '' ? ' Here is the final link: ' . esc_url_raw($link) : ''),
                    },
                );
                unset($queue[$item_key]);
                continue;
            }
            if ($status === 'failed') {
                $error = trim((string) ($item['error'] ?? ''));
                $failed[] = array(
                    'item_key' => (string) $item_key,
                    'item_type' => $item_type,
                    'post_id' => $entity_id,
                    'title' => $title,
                    'message' => match ($language) {
                        'ro' => 'Nu am reușit să finalizez "' . $title . '"' . ($error !== '' ? ': ' . $error : '.'),
                        'bg' => 'Не успях да завърша "' . $title . '"' . ($error !== '' ? ': ' . $error : '.'),
                        default => 'I could not finish "' . $title . '"' . ($error !== '' ? ': ' . $error : '.'),
                    },
                );
                unset($queue[$item_key]);
                continue;
            }
            $pending[] = array(
                'item_key' => (string) $item_key,
                'item_type' => $item_type,
                'post_id' => $entity_id,
                'title' => $title,
                'message' => match ($language) {
                    'ro' => match ($item_type) {
                        'page' => 'Încă lucrez la pagina "' . $title . '". Te anunț aici imediat ce este gata.',
                        'product' => 'Încă lucrez la produsul "' . $title . '". Te anunț aici imediat ce este gata.',
                        'category' => 'Încă lucrez la categoria "' . $title . '". Te anunț aici imediat ce este gata.',
                        default => 'Încă lucrez la "' . $title . '". Te anunț aici imediat ce este gata.',
                    },
                    'bg' => match ($item_type) {
                        'page' => 'Все още работя по страницата "' . $title . '". Ще те уведомя тук веднага щом е готова.',
                        'product' => 'Все още работя по продукта "' . $title . '". Ще те уведомя тук веднага щом е готов.',
                        'category' => 'Все още работя по категорията "' . $title . '". Ще те уведомя тук веднага щом е готова.',
                        default => 'Все още работя по "' . $title . '". Ще te уведомя тук веднага щом е готово.',
                    },
                    default => match ($item_type) {
                        'page' => 'I am still working on the page "' . $title . '". I will notify you here as soon as it is ready.',
                        'product' => 'I am still working on the product "' . $title . '". I will notify you here as soon as it is ready.',
                        'category' => 'I am still working on the category "' . $title . '". I will notify you here as soon as it is ready.',
                        default => 'I am still working on "' . $title . '". I will notify you here as soon as it is ready.',
                    },
                },
            );
        }
        $this->aysa_adjustments_save_generation_queue($project_id, $queue);

        return array(
            'pending' => $pending,
            'completed' => $completed,
            'failed' => $failed,
        );
    }

    private function aysa_adjustments_find_pending_review_for_entity($entity_id, $entity_type, $keyword = '') {
        global $wpdb;

        $entity_id = intval($entity_id);
        $entity_type = sanitize_text_field((string) $entity_type);
        $keyword = sanitize_text_field((string) $keyword);
        if ($entity_id <= 0 || $entity_type === '') {
            return null;
        }

        $table = $wpdb->prefix . 'aysa_automated_pending_reviews';
        $table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($table_exists !== $table) {
            return null;
        }

        $row = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT id, entity_id, entity_type, keyword, status, created_at
                 FROM {$table}
                 WHERE entity_id = %d
                   AND entity_type = %s
                 ORDER BY id DESC
                 LIMIT 1",
                $entity_id,
                $entity_type
            ),
            ARRAY_A
        );
        if (!is_array($row) || empty($row['id'])) {
            return null;
        }

        if ($keyword !== '') {
            $stored_keyword = sanitize_text_field((string) ($row['keyword'] ?? ''));
            if ($stored_keyword !== '' && $stored_keyword !== $keyword) {
                return null;
            }
        }

        return $row;
    }

    private function aysa_adjustments_generation_queue_key($project_id) {
        return 'aysa_adjustments_generation_queue_' . intval($project_id);
    }

    private function aysa_adjustments_get_generation_queue($project_id) {
        $queue = get_option($this->aysa_adjustments_generation_queue_key($project_id), array());
        return is_array($queue) ? $queue : array();
    }

    private function aysa_adjustments_save_generation_queue($project_id, $queue) {
        update_option($this->aysa_adjustments_generation_queue_key($project_id), is_array($queue) ? $queue : array(), false);
    }

    private function aysa_adjustments_queue_created_entity_generation($project_id, $data) {
        $project_id = intval($project_id);
        $data = is_array($data) ? $data : array();
        $entity_id = intval($data['entity_id'] ?? 0);
        $content_type = sanitize_key((string) ($data['content_type'] ?? 'page'));
        $title = sanitize_text_field((string) ($data['title'] ?? ''));
        $keyword = sanitize_text_field((string) ($data['keyword'] ?? $title));
        $entity_processor_type = match ($content_type) {
            'page' => 'Page',
            'product' => 'Product',
            'category' => 'Category',
            default => 'Post',
        };
        $queue = $this->aysa_adjustments_get_generation_queue($project_id);
        foreach ($queue as $existing_key => $existing_item) {
            if (!is_array($existing_item)) {
                continue;
            }
            if (intval($existing_item['entity_id'] ?? 0) !== $entity_id) {
                continue;
            }
            if (sanitize_key((string) ($existing_item['item_type'] ?? '')) !== $content_type) {
                continue;
            }
            if (trim((string) ($existing_item['keyword'] ?? '')) !== $keyword) {
                continue;
            }
            $existing_status = sanitize_key((string) ($existing_item['status'] ?? 'queued'));
            if (in_array($existing_status, array('queued', 'processing', 'generated'), true)) {
                return (string) $existing_key;
            }
        }
        $queue_key = $content_type . ':' . $entity_id . ':' . substr(md5($title . '|' . $keyword), 0, 10);
        $queue[$queue_key] = array(
            'item_key' => $queue_key,
            'item_type' => $content_type,
            'entity_id' => $entity_id,
            'entity_processor_type' => $entity_processor_type,
            'title' => $title,
            'keyword' => $keyword,
            'edit_url' => (string) ($data['edit_url'] ?? ''),
            'view_url' => (string) ($data['view_url'] ?? ''),
            'publish_mode' => sanitize_key((string) ($data['publish_mode'] ?? 'draft')),
            'status' => 'queued',
            'created_at' => current_time('mysql'),
            'updated_at' => current_time('mysql'),
        );
        $this->aysa_adjustments_save_generation_queue($project_id, $queue);
        wp_schedule_single_event(time() + 2, 'aysa_adjustments_generate_created_entity', array(array(
            'queue_key' => $queue_key,
            'project_id' => $project_id,
        )));
        return $queue_key;
    }

    private function aysa_adjustments_process_generation_queue_now($project_id, $limit = 3) {
        $project_id = intval($project_id);
        $limit = max(1, min(10, intval($limit)));
        if ($project_id <= 0) {
            return;
        }

        $queue = $this->aysa_adjustments_get_generation_queue($project_id);
        if (empty($queue)) {
            return;
        }

        $requeue_after_seconds = 180;
        $queue_changed = false;
        foreach ($queue as $item_key => $item) {
            if (!is_array($item)) {
                continue;
            }
            $status = sanitize_key((string) ($item['status'] ?? 'queued'));
            if ($status !== 'processing') {
                continue;
            }
            $updated_at = strtotime((string) ($item['updated_at'] ?? ''));
            if ($updated_at <= 0 || (time() - $updated_at) < $requeue_after_seconds) {
                continue;
            }
            $item['status'] = 'queued';
            $item['updated_at'] = current_time('mysql');
            $item['retries'] = intval($item['retries'] ?? 0) + 1;
            $queue[$item_key] = $item;
            $queue_changed = true;
        }
        if ($queue_changed) {
            $this->aysa_adjustments_save_generation_queue($project_id, $queue);
        }

        $processed = 0;
        foreach ($queue as $item_key => $item) {
            if ($processed >= $limit) {
                break;
            }
            if (!is_array($item)) {
                continue;
            }
            $status = sanitize_key((string) ($item['status'] ?? 'queued'));
            if ($status !== 'queued') {
                continue;
            }
            $this->handle_adjustments_generate_created_entity(array(
                'queue_key' => (string) $item_key,
                'project_id' => $project_id,
            ));
            $processed++;
        }
    }

    private function aysa_adjustments_find_existing_content_entity($content_type, $title) {
        $content_type = sanitize_key((string) $content_type);
        $title = sanitize_text_field((string) $title);
        if ($content_type === '' || $title === '') {
            return null;
        }

        $slug = sanitize_title($title);
        $normalized_title = strtolower(trim(remove_accents($title)));
        if ($content_type === 'category') {
            $term = get_term_by('slug', $slug, 'category');
            if ($term && !is_wp_error($term)) {
                $view_url = get_term_link($term);
                return array(
                    'entity_id' => intval($term->term_id),
                    'edit_url' => admin_url('term.php?taxonomy=category&tag_ID=' . intval($term->term_id) . '&post_type=post'),
                    'view_url' => is_wp_error($view_url) ? '' : (string) $view_url,
                );
            }

            $matching_terms = get_terms(array(
                'taxonomy' => 'category',
                'hide_empty' => false,
                'name__like' => $title,
                'number' => 20,
            ));
            if (!is_wp_error($matching_terms) && !empty($matching_terms)) {
                foreach ($matching_terms as $candidate_term) {
                    if (!($candidate_term instanceof WP_Term)) {
                        continue;
                    }
                    $candidate_name = strtolower(trim(remove_accents((string) $candidate_term->name)));
                    if ($candidate_name !== $normalized_title) {
                        continue;
                    }
                    $view_url = get_term_link($candidate_term);
                    return array(
                        'entity_id' => intval($candidate_term->term_id),
                        'edit_url' => admin_url('term.php?taxonomy=category&tag_ID=' . intval($candidate_term->term_id) . '&post_type=post'),
                        'view_url' => is_wp_error($view_url) ? '' : (string) $view_url,
                    );
                }
            }
            return null;
        }

        $posts = get_posts(array(
            'post_type' => $content_type,
            'name' => $slug,
            'post_status' => array('draft', 'publish', 'pending', 'future', 'private'),
            'posts_per_page' => 1,
            'fields' => 'ids',
            'orderby' => 'date',
            'order' => 'DESC',
            'suppress_filters' => true,
        ));
        if (empty($posts)) {
            global $wpdb;
            $post_type_sql = esc_sql($content_type);
            $normalized_sql = '%' . $wpdb->esc_like($normalized_title) . '%';
            $row = $wpdb->get_row($wpdb->prepare(
                "SELECT ID, post_title FROM {$wpdb->posts}
                 WHERE post_type = %s
                   AND post_status IN ('draft','publish','pending','future','private')
                   AND post_title <> ''
                   AND LOWER(TRIM(post_title)) <> ''
                 ORDER BY ID DESC",
                $post_type_sql
            ));
            if (!$row) {
                $candidate_rows = $wpdb->get_results($wpdb->prepare(
                    "SELECT ID, post_title FROM {$wpdb->posts}
                     WHERE post_type = %s
                       AND post_status IN ('draft','publish','pending','future','private')
                       AND post_title LIKE %s
                     ORDER BY ID DESC
                     LIMIT 20",
                    $post_type_sql,
                    '%' . $wpdb->esc_like($title) . '%'
                ));
            } else {
                $candidate_rows = array($row);
            }
            if (!empty($candidate_rows)) {
                foreach ($candidate_rows as $candidate_row) {
                    $candidate_title = strtolower(trim(remove_accents((string) ($candidate_row->post_title ?? ''))));
                    if ($candidate_title !== $normalized_title) {
                        continue;
                    }
                    $entity_id = intval($candidate_row->ID ?? 0);
                    if ($entity_id > 0) {
                        return array(
                            'entity_id' => $entity_id,
                            'edit_url' => (string) get_edit_post_link($entity_id, 'raw'),
                            'view_url' => (string) get_permalink($entity_id),
                        );
                    }
                }
            }
            return null;
        }
        $entity_id = intval($posts[0]);
        return array(
            'entity_id' => $entity_id,
            'edit_url' => (string) get_edit_post_link($entity_id, 'raw'),
            'view_url' => (string) get_permalink($entity_id),
        );
    }

    private function aysa_adjustments_build_content_strategy_generation_message($language, $strategy_type, $saved, $existing, $errors, $total, $published, $preview_lines = array(), $preview_mode = '') {
        $strategy_label = $this->aysa_adjustments_content_strategy_type_label((string) $strategy_type, (string) $language);
        $preview_lines = is_array($preview_lines) ? array_values(array_filter(array_map('strval', $preview_lines))) : array();
        $preview_mode = sanitize_key((string) $preview_mode);
        $preview_intro = '';
        if (!empty($preview_lines)) {
            $preview_intro = match ($preview_mode) {
                'saved' => match ($language) {
                    'ro' => 'Iată primele idei noi salvate:',
                    'bg' => 'Ето първите нови запазени идеи:',
                    default => 'Here are the first new ideas that were saved:',
                },
                'existing' => match ($language) {
                    'ro' => 'Iată ideile propuse acum, dar recunoscute ca fiind deja acoperite în plan:',
                    'bg' => 'Ето идеите, предложени сега, но разпознати като вече покрити в плана:',
                    default => 'Here are the ideas proposed now, but already covered by the plan:',
                },
                default => '',
            };
        }
        $preview_block = ($preview_intro !== '' ? "\n\n" . $preview_intro : '') . ($preview_lines ? "\n\n- " . implode("\n- ", $preview_lines) : '');

        return match ($language) {
            'ro' => 'Am generat strategia de conținut de tip ' . $strategy_label . '. Am salvat ' . intval($saved) . ' idei noi, ' . intval($existing) . ' existau deja, iar ' . intval($errors) . ' nu au putut fi salvate. În total, planul tău are acum ' . intval($total) . ' itemi, dintre care ' . intval($published) . ' sunt deja legați de pagini.' .
                ($preview_block !== '' ? $preview_block : ''),
            'bg' => 'Генерирах content стратегия от типа ' . $strategy_label . '. Запазих ' . intval($saved) . ' нови идеи, ' . intval($existing) . ' вече съществуваха, а ' . intval($errors) . ' не можаха да бъдат запазени. Общо планът ти сега има ' . intval($total) . ' елемента, от които ' . intval($published) . ' вече са свързани със страници.' .
                ($preview_block !== '' ? $preview_block : ''),
            default => 'I generated the ' . $strategy_label . ' content strategy. I saved ' . intval($saved) . ' new ideas, ' . intval($existing) . ' were already there, and ' . intval($errors) . ' could not be saved. Your plan now has ' . intval($total) . ' items in total, of which ' . intval($published) . ' are already tied to pages.' .
                ($preview_block !== '' ? $preview_block : ''),
        };
    }

    private function aysa_adjustments_build_content_strategy_preview_lines($items, $language, $limit = 5) {
        $items = is_array($items) ? $items : array();
        $limit = max(1, intval($limit));
        $lines = array();

        foreach ($items as $item) {
            if (!is_array($item)) {
                continue;
            }
            $title = sanitize_text_field((string) ($item['title'] ?? ''));
            if ($title === '') {
                continue;
            }

            $keywords = array();
            $raw_keywords = $item['target_keywords'] ?? $item['TargetKeywords'] ?? $item['targetKeywords'] ?? array();
            if (is_array($raw_keywords)) {
                foreach ($raw_keywords as $keyword) {
                    $keyword = sanitize_text_field((string) $keyword);
                    if ($keyword !== '' && !in_array($keyword, $keywords, true)) {
                        $keywords[] = $keyword;
                    }
                    if (count($keywords) >= 3) {
                        break;
                    }
                }
            }

            $associated_page = sanitize_text_field((string) ($item['associated_page'] ?? $item['associatedPage'] ?? ''));
            $parts = array($title);
            if (!empty($keywords)) {
                $parts[] = match ($language) {
                    'ro' => 'keyword-uri: ' . implode(', ', $keywords),
                    'bg' => 'keywords: ' . implode(', ', $keywords),
                    default => 'keywords: ' . implode(', ', $keywords),
                };
            }
            if ($associated_page !== '') {
                $parts[] = match ($language) {
                    'ro' => 'pagină: ' . $associated_page,
                    'bg' => 'страница: ' . $associated_page,
                    default => 'page: ' . $associated_page,
                };
            }
            $lines[] = implode(' | ', $parts);
            if (count($lines) >= $limit) {
                break;
            }
        }

        return $lines;
    }

    private function aysa_adjustments_content_strategy_type_label($strategy_type, $language) {
        $strategy_type = sanitize_key((string) $strategy_type);
        return match ($strategy_type) {
            'cluster_titles' => 'Cluster-based / Keyword clusters',
            'competitor_derived_titles' => 'Competitor analysis',
            'intent_layering' => 'Intent layering',
            'seasonal_event' => 'Seasonal and events',
            'serp_features' => 'SERP features',
            'evergreen_fresh_mix' => 'Evergreen and fresh',
            'customer_journey' => 'Customer journey',
            'local_seo' => 'Local SEO',
            'authority_building' => 'Authority building',
            'persona_based' => 'Persona-based',
            default => $strategy_type,
        };
    }

    private function aysa_adjustments_build_keyword_workspace_artifact($snapshot, $language = 'en') {
        if (!is_array($snapshot)) {
            return array();
        }
        $language = in_array($language, array('en', 'ro', 'bg'), true) ? $language : 'en';
        $gsc_count = intval($snapshot['gsc_count'] ?? 0);
        $suggestions_count = intval($snapshot['suggestions_count'] ?? 0);
        $total_terms = $gsc_count + $suggestions_count;
        $status = isset($snapshot['refresh_status']) && is_array($snapshot['refresh_status']) ? $snapshot['refresh_status'] : array();
        $seed_keywords = isset($snapshot['seed_keywords']) && is_array($snapshot['seed_keywords']) ? $snapshot['seed_keywords'] : array();
        $gsc_rows = isset($snapshot['gsc_rows']) && is_array($snapshot['gsc_rows']) ? $snapshot['gsc_rows'] : array();
        $suggestion_rows = isset($snapshot['suggestion_rows']) && is_array($snapshot['suggestion_rows']) ? $snapshot['suggestion_rows'] : array();
        $competitor_gap_rows = isset($snapshot['competitor_gap_rows']) && is_array($snapshot['competitor_gap_rows']) ? $snapshot['competitor_gap_rows'] : array();
        $unified_rows = isset($snapshot['unified_rows']) && is_array($snapshot['unified_rows']) ? $snapshot['unified_rows'] : array();
        $association_rows = isset($snapshot['association_rows']) && is_array($snapshot['association_rows']) ? $snapshot['association_rows'] : array();
        $recent_relevant = $this->aysa_adjustments_get_recent_relevant_keywords(intval($snapshot['project_id'] ?? 0));
        $recent_selected = isset($recent_relevant['selected_keywords']) && is_array($recent_relevant['selected_keywords']) ? $recent_relevant['selected_keywords'] : array();
        $recent_selected_lookup = array_fill_keys(array_map(array($this, 'aysa_adjustments_normalize_keyword_lookup_key'), $recent_selected), true);

        if ($gsc_count <= 0 && $suggestions_count <= 0 && (($status['status'] ?? '') !== 'running')) {
            return array();
        }

        $mapped_pages = array();
        foreach ($association_rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $association_value = trim((string) ($row['value'] ?? ''));
            if ($association_value !== '') {
                $mapped_pages[$association_value] = true;
            }
        }
        foreach (array_merge($gsc_rows, $suggestion_rows) as $row) {
            if (!is_array($row)) {
                continue;
            }
            $target_url = trim((string) ($row['target_url'] ?? $row['url'] ?? ''));
            if ($target_url !== '') {
                $mapped_pages[$target_url] = true;
            }
        }
        $mapped_pages_count = count($mapped_pages);
        $seed_count = count($seed_keywords);
        $keyword_map_status = $total_terms > 0 ? 'ready' : 'pending';
        if (($status['status'] ?? '') === 'running') {
            $keyword_map_status = 'building';
        }

        $artifact = array(
            'type' => 'keyword_map',
            'status' => ($status['status'] ?? 'idle'),
            'title' => $this->aysa_adjustments_tr($language, 'Keyword research overview', 'Overview keyword research', 'Преглед на keyword research'),
            'summary' => '',
            'summary_cards' => array(
                array(
                    'title' => $this->aysa_adjustments_tr($language, 'Already visible in Google', 'Vizibilitate deja existentă în Google', 'Вече налична видимост в Google'),
                    'value' => (string) $gsc_count,
                    'detail' => $this->aysa_adjustments_tr(
                        $language,
                        ($gsc_count > 0
                            ? ($mapped_pages_count > 0 ? $mapped_pages_count . ' pages already connected to real Google search data' : 'Real Google queries are available and ready to be matched to pages')
                            : 'Waiting for confirmed Google ranking rows'),
                        ($gsc_count > 0
                            ? ($mapped_pages_count > 0 ? $mapped_pages_count . ' pagini deja conectate la date reale din Google' : 'Query-uri reale din Google sunt disponibile și pregătite pentru asociere pe pagini')
                            : 'Aștept rânduri confirmate de ranking din Google'),
                        ($gsc_count > 0
                            ? ($mapped_pages_count > 0 ? $mapped_pages_count . ' страници вече са свързани с реални данни от Google' : 'Налични са реални Google query-и и могат да бъдат свързани със страници')
                            : 'Изчаквам потвърдени Google ranking редове')
                    ),
                    'tone' => $gsc_count > 0 ? 'positive' : 'neutral',
                ),
                array(
                    'title' => $this->aysa_adjustments_tr($language, 'New keyword opportunities', 'Oportunități noi de keyword-uri', 'Нови keyword opportunities'),
                    'value' => (string) $suggestions_count,
                    'detail' => $this->aysa_adjustments_tr(
                        $language,
                        ($seed_count > 0 ? 'Built from ' . $seed_count . ' saved setup topics' : 'Built from the saved project context'),
                        ($seed_count > 0 ? 'Construite din ' . $seed_count . ' teme salvate în setup' : 'Construite din contextul deja salvat al proiectului'),
                        ($seed_count > 0 ? 'Изградени от ' . $seed_count . ' запазени теми в setup' : 'Изградени от запазения проектен контекст')
                    ),
                    'tone' => $suggestions_count > 0 ? 'positive' : 'neutral',
                ),
                array(
                    'title' => $this->aysa_adjustments_tr($language, 'Keyword map in progress', 'Keyword map în lucru', 'Keyword map в процес'),
                    'value' => (string) max($total_terms, count($unified_rows)),
                    'detail' => $this->aysa_adjustments_tr(
                        $language,
                        ($mapped_pages_count > 0 ? $mapped_pages_count . ' pages already have direct ranking signals to anchor the map' : 'The map is being prepared from the terms collected so far'),
                        ($mapped_pages_count > 0 ? $mapped_pages_count . ' pagini au deja semnale directe care ancorează harta' : 'Harta se pregătește din termenii colectați până acum'),
                        ($mapped_pages_count > 0 ? $mapped_pages_count . ' страници вече имат директни сигнали, които закотвят картата' : 'Картата се подготвя от събраните досега термини')
                    ),
                    'tone' => $keyword_map_status === 'ready' ? 'accent' : 'neutral',
                ),
                array(
                    'title' => $this->aysa_adjustments_tr($language, 'Next recommended step', 'Următorul pas recomandat', 'Следваща препоръчана стъпка'),
                    'value' => $this->aysa_adjustments_tr($language, 'Competitors', 'Competitori', 'Конкуренти'),
                    'detail' => $this->aysa_adjustments_tr(
                        $language,
                        'After the map is clear, I can expand the competitor set and compare gaps.',
                        'După ce clarificăm harta, pot extinde lista de competitori și compara golurile.',
                        'След като картата е ясна, мога да разширя списъка с конкуренти и да сравня липсите.'
                    ),
                    'tone' => 'neutral',
                ),
            ),
            'workflow_steps' => array(
                array(
                    'title' => $this->aysa_adjustments_tr($language, 'Extracting the keywords you already rank for', 'Extrag cuvintele pentru care rankezi deja', 'Извличам keyword-ите, за които вече rank-ваш'),
                    'status' => $gsc_count > 0 ? 'done' : ((($status['status'] ?? '') === 'running') ? 'running' : 'pending'),
                    'detail' => $gsc_count > 0
                        ? $this->aysa_adjustments_tr($language, 'I found ' . $gsc_count . ' real keywords already visible in Google.', 'Am găsit ' . $gsc_count . ' keyword-uri reale deja vizibile în Google.', 'Намерих ' . $gsc_count . ' реални keyword-а, които вече се виждат в Google.')
                        : $this->aysa_adjustments_tr($language, 'I am gathering the searches that already bring your site into Google.', 'Adun căutările care îți aduc deja site-ul în Google.', 'Събирам търсенията, които вече водят сайта ти в Google.'),
                ),
                array(
                    'title' => $this->aysa_adjustments_tr($language, 'Extracting new suggestions from your topics', 'Extrag sugestii noi din temele tale', 'Извличам нови предложения от твоите теми'),
                    'status' => $suggestions_count > 0 ? 'done' : 'pending',
                    'detail' => $suggestions_count > 0
                        ? $this->aysa_adjustments_tr($language, 'I generated ' . $suggestions_count . ' new keyword suggestions from the saved topics.', 'Am generat ' . $suggestions_count . ' sugestii noi de keyword-uri din temele salvate.', 'Генерирах ' . $suggestions_count . ' нови keyword предложения от запазените теми.')
                        : $this->aysa_adjustments_tr($language, 'I am extracting new suggestions based on the keywords you already gave me.', 'Extrag sugestii noi pe baza keyword-urilor pe care mi le-ai dat deja.', 'Извличам нови предложения на база keyword-ите, които вече ми даде.'),
                ),
                array(
                    'title' => $this->aysa_adjustments_tr($language, 'Matching the extracted keywords to your pages', 'Asociez keyword-urile extrase cu paginile tale', 'Свързвам извлечените keyword-и с твоите страници'),
                    'status' => $keyword_map_status,
                    'detail' => $this->aysa_adjustments_tr(
                        $language,
                        ($mapped_pages_count > 0 ? 'I already have direct matches for ' . $mapped_pages_count . ' pages.' : 'I am deciding which extracted keywords belong on each page.'),
                        ($mapped_pages_count > 0 ? 'Am deja asocieri directe pentru ' . $mapped_pages_count . ' pagini.' : 'Decid ce keyword-uri extrase se potrivesc pe fiecare pagină.'),
                        ($mapped_pages_count > 0 ? 'Вече имам директни съвпадения за ' . $mapped_pages_count . ' страници.' : 'Определям кои извлечени keyword-и пасват на всяка страница.')
                    ),
                ),
                array(
                    'title' => $this->aysa_adjustments_tr($language, 'Preparing the next step: competitors', 'Pregătesc pasul următor: competitori', 'Подготвям следващата стъпка: конкуренти'),
                    'status' => 'upcoming',
                    'detail' => $this->aysa_adjustments_tr(
                        $language,
                        'After the keyword map is clear, I will move to competitor discovery.',
                        'După ce harta de keyword-uri e clară, trec la descoperirea competitorilor.',
                        'След като keyword картата е ясна, ще премина към откриване на конкуренти.'
                    ),
                ),
            ),
            'sections' => array(),
            'cta_label' => $this->aysa_adjustments_tr($language, 'Open keyword map', 'Deschide keyword map', 'Отвори keyword map'),
        );

        if (($status['status'] ?? '') === 'running' && $gsc_count <= 0) {
            $artifact['summary'] = $this->aysa_adjustments_tr(
                $language,
                'Google ranking data is being collected in the background. Existing keyword suggestions are already available below.',
                'Datele de ranking din Google se colectează acum în fundal. Sugestiile de keyword-uri deja disponibile sunt afișate mai jos.',
                'Данните за ranking от Google се събират във фонов режим. Наличните keyword suggestions вече са показани по-долу.'
            );
        } else {
            $artifact['summary'] = $this->aysa_adjustments_tr(
                $language,
                'This view is organized from the real data already stored in Keyword Research.',
                'Această vedere este organizată din datele reale deja stocate în Keyword Research.',
                'Този изглед е организиран от реалните данни, които вече са налични в Keyword Research.'
            );
        }

        $unified_table_rows = array();
        foreach ($unified_rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }
            $unified_table_rows[] = array(
                'keyword' => $keyword,
                'sources' => (string) ($row['sources'] ?? ''),
                'selected' => (string) ($row['selected'] ?? 'Available'),
                'volume' => (string) ($row['search_volume'] ?? ''),
                'page' => trim((string) ($row['page'] ?? '')),
                'group' => trim((string) ($row['group'] ?? '')),
            );
        }
        if (!empty($unified_table_rows)) {
            $artifact['sections'][] = array(
                'id' => 'unified_keywords',
                'kind' => 'table',
                'title' => $this->aysa_adjustments_tr($language, 'Unified keyword map', 'Keyword map unificat', 'Обединен keyword map'),
                'summary' => $this->aysa_adjustments_tr(
                    $language,
                    count($unified_table_rows) . ' deduplicated keywords combined from Google, suggestions, and selected competitor gap terms. ' . count($recent_selected_lookup) . ' are in the current relevant set.',
                    count($unified_table_rows) . ' keyword-uri deduplicate combinate din Google, suggestions și termenii selectați din competitor gap. ' . count($recent_selected_lookup) . ' sunt în setul relevant curent.',
                    count($unified_table_rows) . ' дедупликирани keyword-а, комбинирани от Google, suggestions и избраните competitor gap термини. ' . count($recent_selected_lookup) . ' са в текущия релевантен набор.'
                ),
                'button_label' => $this->aysa_adjustments_tr($language, 'See unified keywords', 'Vezi keyword-urile unificate', 'Виж обединените keyword-и'),
                'filter_placeholder' => $this->aysa_adjustments_tr($language, 'Filter keywords, sources, groups, or pages', 'Filtrează keyword-uri, surse, grupuri sau pagini', 'Филтрирай keyword-и, източници, групи или страници'),
                'empty_label' => $this->aysa_adjustments_tr($language, 'No unified keyword rows are available yet.', 'Nu există încă rânduri unificate.', 'Все още няма налични обединени редове.'),
                'columns' => array(
                    array('key' => 'keyword', 'label' => $this->aysa_adjustments_tr($language, 'Keyword', 'Keyword', 'Keyword')),
                    array('key' => 'sources', 'label' => $this->aysa_adjustments_tr($language, 'Sources', 'Surse', 'Източници')),
                    array('key' => 'selected', 'label' => $this->aysa_adjustments_tr($language, 'Status', 'Status', 'Статус')),
                    array('key' => 'volume', 'label' => $this->aysa_adjustments_tr($language, 'Volume', 'Volum', 'Обем')),
                    array('key' => 'group', 'label' => $this->aysa_adjustments_tr($language, 'Mapped group', 'Grup mapat', 'Свързана група')),
                    array('key' => 'page', 'label' => $this->aysa_adjustments_tr($language, 'Page', 'Pagină', 'Страница')),
                ),
                'rows' => $unified_table_rows,
            );
        }

        $gsc_table_rows = array();
        foreach ($gsc_rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }
            $gsc_table_rows[] = array(
                'keyword' => $keyword,
                'volume' => (string) ($row['search_volume'] ?? $row['searchVolume'] ?? ''),
                'position' => (string) ($row['position'] ?? ''),
                'traffic' => (string) ($row['traffic'] ?? ''),
                'page' => trim((string) ($row['target_url'] ?? $row['url'] ?? '')),
            );
        }
        if (!empty($gsc_table_rows)) {
            $artifact['sections'][] = array(
                'id' => 'gsc_keywords',
                'kind' => 'table',
                'title' => $this->aysa_adjustments_tr($language, 'Keywords already visible in Google', 'Keyword-uri deja vizibile în Google', 'Keyword-и, които вече се виждат в Google'),
                'summary' => $this->aysa_adjustments_tr(
                    $language,
                    count($gsc_table_rows) . ' real rows currently available from Google visibility data.',
                    count($gsc_table_rows) . ' rânduri reale disponibile acum din datele de vizibilitate Google.',
                    count($gsc_table_rows) . ' реални реда, налични в момента от данните за видимост в Google.'
                ),
                'button_label' => $this->aysa_adjustments_tr($language, 'See Google keywords', 'Vezi keyword-urile din Google', 'Виж keyword-ите от Google'),
                'filter_placeholder' => $this->aysa_adjustments_tr($language, 'Filter Google keywords or pages', 'Filtrează keyword-uri Google sau pagini', 'Филтрирай Google keyword-и или страници'),
                'empty_label' => $this->aysa_adjustments_tr($language, 'No Google keyword rows are available yet.', 'Nu există încă rânduri Google disponibile.', 'Все още няма налични Google keyword редове.'),
                'columns' => array(
                    array('key' => 'keyword', 'label' => $this->aysa_adjustments_tr($language, 'Keyword', 'Keyword', 'Keyword')),
                    array('key' => 'volume', 'label' => $this->aysa_adjustments_tr($language, 'Volume', 'Volum', 'Обем')),
                    array('key' => 'position', 'label' => $this->aysa_adjustments_tr($language, 'Position', 'Poziție', 'Позиция')),
                    array('key' => 'traffic', 'label' => $this->aysa_adjustments_tr($language, 'Traffic', 'Trafic', 'Трафик')),
                    array('key' => 'page', 'label' => $this->aysa_adjustments_tr($language, 'Page', 'Pagină', 'Страница')),
                ),
                'rows' => $gsc_table_rows,
            );
        }

        $suggestion_table_rows = array();
        foreach ($suggestion_rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }
            $suggestion_table_rows[] = array(
                'keyword' => $keyword,
                'volume' => (string) ($row['search_volume'] ?? $row['searchVolume'] ?? ''),
                'difficulty' => (string) ($row['difficulty'] ?? ''),
                'source' => (string) ($row['source'] ?? $this->aysa_adjustments_tr($language, 'New suggestion', 'Sugestie nouă', 'Ново предложение')),
                'page' => trim((string) ($row['target_url'] ?? $row['url'] ?? '')),
            );
        }
        if (!empty($suggestion_table_rows)) {
            $artifact['sections'][] = array(
                'id' => 'keyword_suggestions',
                'kind' => 'table',
                'title' => $this->aysa_adjustments_tr($language, 'Keyword suggestions', 'Sugestii de keyword-uri', 'Keyword предложения'),
                'summary' => $this->aysa_adjustments_tr(
                    $language,
                    count($suggestion_table_rows) . ' suggestion rows currently available from the saved setup topics.',
                    count($suggestion_table_rows) . ' rânduri de sugestii disponibile acum din temele salvate în setup.',
                    count($suggestion_table_rows) . ' реда с предложения, налични в момента от темите, запазени в setup.'
                ),
                'button_label' => $this->aysa_adjustments_tr($language, 'See keywords', 'Vezi keyword-urile', 'Виж keyword-ите'),
                'filter_placeholder' => $this->aysa_adjustments_tr($language, 'Filter suggestions or pages', 'Filtrează sugestii sau pagini', 'Филтрирай предложения или страници'),
                'empty_label' => $this->aysa_adjustments_tr($language, 'No keyword suggestions are available yet.', 'Nu există încă sugestii disponibile.', 'Все още няма налични предложения.'),
                'columns' => array(
                    array('key' => 'keyword', 'label' => $this->aysa_adjustments_tr($language, 'Keyword', 'Keyword', 'Keyword')),
                    array('key' => 'volume', 'label' => $this->aysa_adjustments_tr($language, 'Volume', 'Volum', 'Обем')),
                    array('key' => 'difficulty', 'label' => $this->aysa_adjustments_tr($language, 'Difficulty', 'Dificultate', 'Трудност')),
                    array('key' => 'source', 'label' => $this->aysa_adjustments_tr($language, 'Type', 'Tip', 'Тип')),
                    array('key' => 'page', 'label' => $this->aysa_adjustments_tr($language, 'Page', 'Pagină', 'Страница')),
                ),
                'rows' => $suggestion_table_rows,
            );
        }

        $page_rows = array();
        if (!empty($association_rows)) {
            $grouped_associations = $this->aysa_adjustments_group_associations_for_workspace($association_rows);
            $source_map = array();
            foreach ($unified_rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $source_map[$this->aysa_adjustments_normalize_keyword_lookup_key((string) ($row['keyword'] ?? ''))] = (string) ($row['sources'] ?? '');
            }
            foreach ($grouped_associations as $group) {
                if (!is_array($group)) {
                    continue;
                }
                $keywords = isset($group['keywords']) && is_array($group['keywords']) ? $group['keywords'] : array();
                if (empty($keywords)) {
                    continue;
                }
                if (!empty($recent_selected_lookup)) {
                    $keywords = array_values(array_filter($keywords, function($keyword) use ($recent_selected_lookup) {
                        return !empty($recent_selected_lookup[$this->aysa_adjustments_normalize_keyword_lookup_key($keyword)]);
                    }));
                    if (empty($keywords)) {
                        continue;
                    }
                }
                $source_labels = array();
                foreach ($keywords as $keyword) {
                    $source_string = (string) ($source_map[$this->aysa_adjustments_normalize_keyword_lookup_key($keyword)] ?? '');
                    foreach (array_filter(array_map('trim', explode(',', $source_string))) as $label) {
                        $source_labels[$label] = true;
                    }
                }
                $page_rows[] = array(
                    'keywords' => implode(', ', array_slice($keywords, 0, 8)),
                    'count' => (string) count($keywords),
                    'type' => (string) ($group['type'] ?? ''),
                    'entity' => (string) ($group['entity'] ?? ''),
                    'sources' => implode(', ', array_keys($source_labels)),
                    'page' => (string) ($group['page'] ?? ''),
                );
            }
        } else {
            $page_matches = array();
            foreach (array_merge($gsc_rows, $suggestion_rows) as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $target_url = trim((string) ($row['target_url'] ?? ''));
                $keyword = trim((string) ($row['keyword'] ?? ''));
                if ($target_url === '' || $keyword === '') {
                    continue;
                }
                if (!isset($page_matches[$target_url])) {
                    $page_matches[$target_url] = array();
                }
                $page_matches[$target_url][$keyword] = true;
            }
            foreach ($page_matches as $page_url => $keywords_map) {
                $keywords = array_keys($keywords_map);
                sort($keywords);
                $page_rows[] = array(
                    'keyword' => implode(', ', array_slice($keywords, 0, 6)),
                    'type' => $this->aysa_adjustments_tr($language, 'Page', 'Pagină', 'Страница'),
                    'entity' => $page_url,
                    'page' => $page_url,
                );
            }
        }

        if (!empty($page_rows)) {
            $artifact['sections'][] = array(
                'id' => 'page_matches',
                'kind' => 'table',
                'title' => $this->aysa_adjustments_tr($language, 'Keyword-to-page matches', 'Asocieri keyword-uri cu pagini', 'Свързвания на keyword-и към страници'),
                'summary' => $this->aysa_adjustments_tr(
                    $language,
                    count($page_rows) . ' grouped page mappings are currently available from the stored research data.',
                    count($page_rows) . ' grupări de asocieri pe pagini sunt disponibile acum în datele de research stocate.',
                    count($page_rows) . ' групирани свързвания към страници са налични в момента в запазените research данни.'
                ),
                'button_label' => $this->aysa_adjustments_tr($language, 'See matches', 'Vezi asocierile', 'Виж свързванията'),
                'filter_placeholder' => $this->aysa_adjustments_tr($language, 'Filter keywords, entities, or pages', 'Filtrează keyword-uri, entități sau pagini', 'Филтрирай keyword-и, обекти или страници'),
                'empty_label' => $this->aysa_adjustments_tr($language, 'No page matches are available yet.', 'Nu există încă asocieri pe pagini.', 'Все още няма свързвания по страници.'),
                'columns' => array(
                    array('key' => 'keywords', 'label' => $this->aysa_adjustments_tr($language, 'Keywords', 'Keyword-uri', 'Keyword-и')),
                    array('key' => 'count', 'label' => $this->aysa_adjustments_tr($language, 'Count', 'Număr', 'Брой')),
                    array('key' => 'type', 'label' => $this->aysa_adjustments_tr($language, 'Type', 'Tip', 'Тип')),
                    array('key' => 'entity', 'label' => $this->aysa_adjustments_tr($language, 'Entity', 'Entitate', 'Обект')),
                    array('key' => 'sources', 'label' => $this->aysa_adjustments_tr($language, 'Sources', 'Surse', 'Източници')),
                    array('key' => 'page', 'label' => $this->aysa_adjustments_tr($language, 'Matched page', 'Pagină asociată', 'Свързана страница')),
                ),
                'rows' => $page_rows,
            );
        }

        return $artifact;
    }

    private function aysa_adjustments_build_research_workspace_artifact($snapshot, $language = 'en') {
        $artifact = $this->aysa_adjustments_build_keyword_workspace_artifact($snapshot, $language);
        $language = in_array($language, array('en', 'ro', 'bg'), true) ? $language : 'en';
        $modules = isset($snapshot['modules']) && is_array($snapshot['modules']) ? $snapshot['modules'] : array();
        if ($artifact === array()) {
            $artifact = array(
                'type' => 'research_analysis',
                'status' => 'ready',
                'title' => $this->aysa_adjustments_tr($language, 'Research & Analysis', 'Research & Analysis', 'Research & Analysis'),
                'summary' => $this->aysa_adjustments_tr($language, 'I organized the current research data by module.', 'Am organizat datele actuale de research pe module.', 'Организирах текущите research данни по модули.'),
                'sections' => array(),
                'cta_label' => $this->aysa_adjustments_tr($language, 'Open research data', 'Deschide datele de research', 'Отвори research данните'),
            );
        } else {
            $artifact['type'] = 'research_analysis';
            $artifact['title'] = $this->aysa_adjustments_tr($language, 'Research & Analysis', 'Research & Analysis', 'Research & Analysis');
            $artifact['cta_label'] = $this->aysa_adjustments_tr($language, 'Open research data', 'Deschide datele de research', 'Отвори research данните');
        }

        $extra_sections = array();
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'monitoring_status',
            $this->aysa_adjustments_tr($language, 'Ranking monitoring status', 'Status monitorizare ranking', 'Статус на ranking monitoring'),
            $this->aysa_adjustments_tr($language, 'Current keyword tracking totals and background refresh status.', 'Totalurile curente pentru keyword tracking și statusul refresh-ului în background.', 'Текущите totals за keyword tracking и статусът на background refresh-а.'),
            $this->aysa_adjustments_tr($language, 'Filter status rows', 'Filtrează statusul', 'Филтрирай status редовете'),
            isset($modules['monitoring_ranking']['status_rows']) ? $modules['monitoring_ranking']['status_rows'] : array(),
            array(
                array('key' => 'metric', 'label' => $this->aysa_adjustments_tr($language, 'Metric', 'Metrică', 'Метрика')),
                array('key' => 'value', 'label' => $this->aysa_adjustments_tr($language, 'Value', 'Valoare', 'Стойност')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'monitoring_ranking',
            $this->aysa_adjustments_tr($language, 'Ranking monitoring', 'Monitorizare ranking', 'Ranking monitoring'),
            $this->aysa_adjustments_tr($language, 'Latest local rank rows saved for tracked keywords.', 'Ultimele rânduri locale de ranking salvate pentru keywordurile urmărite.', 'Последните локални ranking редове, запазени за следените keyword-и.'),
            $this->aysa_adjustments_tr($language, 'Filter ranking rows', 'Filtrează rândurile', 'Филтрирай ranking редовете'),
            isset($modules['monitoring_ranking']['rows']) ? $modules['monitoring_ranking']['rows'] : array(),
            array(
                array('key' => 'keyword', 'label' => $this->aysa_adjustments_tr($language, 'Keyword', 'Keyword', 'Keyword')),
                array('key' => 'position', 'label' => $this->aysa_adjustments_tr($language, 'Position', 'Poziție', 'Позиция')),
                array('key' => 'previous_position', 'label' => $this->aysa_adjustments_tr($language, 'Prev. position', 'Poziție anterioară', 'Пред. позиция')),
                array('key' => 'position_delta', 'label' => $this->aysa_adjustments_tr($language, 'Delta', 'Diferență', 'Разлика')),
                array('key' => 'search_volume', 'label' => $this->aysa_adjustments_tr($language, 'Volume', 'Volum', 'Обем')),
                array('key' => 'traffic', 'label' => $this->aysa_adjustments_tr($language, 'Traffic', 'Trafic', 'Трафик')),
                array('key' => 'last_checked_at', 'label' => $this->aysa_adjustments_tr($language, 'Last checked', 'Ultima verificare', 'Последна проверка')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'monitoring_stale',
            $this->aysa_adjustments_tr($language, 'Stale tracking rows', 'Keyworduri stale', 'Stale keyword-и'),
            $this->aysa_adjustments_tr($language, 'Tracked keywords that still do not have a local rank snapshot.', 'Keyworduri urmărite care încă nu au un snapshot local de ranking.', 'Следени keyword-и, които все още нямат локален ranking snapshot.'),
            $this->aysa_adjustments_tr($language, 'Filter stale rows', 'Filtrează keywordurile stale', 'Филтрирай stale редовете'),
            isset($modules['monitoring_ranking']['stale_rows']) ? $modules['monitoring_ranking']['stale_rows'] : array(),
            array(
                array('key' => 'keyword', 'label' => $this->aysa_adjustments_tr($language, 'Keyword', 'Keyword', 'Keyword')),
                array('key' => 'associated_on', 'label' => $this->aysa_adjustments_tr($language, 'Association', 'Asociere', 'Асоциация')),
                array('key' => 'link', 'label' => $this->aysa_adjustments_tr($language, 'Target URL', 'URL țintă', 'Target URL')),
                array('key' => 'reason', 'label' => $this->aysa_adjustments_tr($language, 'Reason', 'Motiv', 'Причина')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'competitor_gap',
            $this->aysa_adjustments_tr($language, 'Competitor gap', 'Competitor gap', 'Competitor gap'),
            $this->aysa_adjustments_tr($language, 'Keywords competitors cover and you should review.', 'Keyword-uri pe care competitorii le acoperă și merită să le verifici.', 'Keyword-и, които конкурентите покриват и си струва да прегледаш.'),
            $this->aysa_adjustments_tr($language, 'Filter keywords', 'Filtrează keyword-uri', 'Филтрирай keyword-и'),
            isset($modules['competitor_gap']['rows']) ? $modules['competitor_gap']['rows'] : array(),
            array(
                array('key' => 'keyword', 'label' => $this->aysa_adjustments_tr($language, 'Keyword', 'Keyword', 'Keyword')),
                array('key' => 'search_volume', 'label' => $this->aysa_adjustments_tr($language, 'Volume', 'Volum', 'Обем')),
                array('key' => 'difficulty', 'label' => $this->aysa_adjustments_tr($language, 'Difficulty', 'Dificultate', 'Трудност')),
                array('key' => 'intent', 'label' => $this->aysa_adjustments_tr($language, 'Intent', 'Intenție', 'Intent')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'competitor_identification',
            $this->aysa_adjustments_tr($language, 'Competitor identification', 'Competitor identification', 'Competitor identification'),
            $this->aysa_adjustments_tr($language, 'Competitors identified from the seed keywords.', 'Competitori identificați pornind de la seed keywords.', 'Конкуренти, идентифицирани от seed keywords.'),
            $this->aysa_adjustments_tr($language, 'Filter competitors', 'Filtrează competitori', 'Филтрирай конкуренти'),
            isset($modules['competitor_identification']['rows']) ? $modules['competitor_identification']['rows'] : array(),
            array(
                array('key' => 'domain', 'label' => $this->aysa_adjustments_tr($language, 'Domain', 'Domeniu', 'Домейн')),
                array('key' => 'overlap_keywords', 'label' => $this->aysa_adjustments_tr($language, 'Overlap', 'Suprapunere', 'Припокриване')),
                array('key' => 'avg_position', 'label' => $this->aysa_adjustments_tr($language, 'Avg. Position', 'Poziție medie', 'Средна позиция')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'competitor_backlinks',
            $this->aysa_adjustments_tr($language, 'Competitor backlinks', 'Competitor backlinks', 'Competitor backlinks'),
            $this->aysa_adjustments_tr($language, 'Backlink and authority signals saved locally.', 'Semnale locale despre backlinks și autoritate.', 'Локално запазени сигнали за backlinks и authority.'),
            $this->aysa_adjustments_tr($language, 'Filter domains', 'Filtrează domenii', 'Филтрирай домейни'),
            isset($modules['competitor_backlinks']['rows']) ? $modules['competitor_backlinks']['rows'] : array(),
            array(
                array('key' => 'domain', 'label' => $this->aysa_adjustments_tr($language, 'Domain', 'Domeniu', 'Домейн')),
                array('key' => 'authority', 'label' => $this->aysa_adjustments_tr($language, 'Authority', 'Autoritate', 'Authority')),
                array('key' => 'backlinks', 'label' => $this->aysa_adjustments_tr($language, 'Backlinks', 'Backlink-uri', 'Backlinks')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'offpage_backlink_audit',
            $this->aysa_adjustments_tr($language, 'Backlink audit', 'Audit backlinkuri', 'Backlink audit'),
            $this->aysa_adjustments_tr($language, 'Backlink profile health, risks, and broken links already saved locally.', 'Starea profilului de backlinkuri, riscurile și linkurile rupte deja salvate local.', 'Състоянието на backlink профила, рисковете и счупените линкове, вече запазени локално.'),
            $this->aysa_adjustments_tr($language, 'Filter backlink rows', 'Filtrează backlinkurile', 'Филтрирай backlink редовете'),
            isset($modules['offpage_backlink_audit']['rows']) ? $modules['offpage_backlink_audit']['rows'] : array(),
            array(
                array('key' => 'referring_domain', 'label' => $this->aysa_adjustments_tr($language, 'Referring domain', 'Domeniu sursă', 'Препращащ домейн')),
                array('key' => 'source_url', 'label' => $this->aysa_adjustments_tr($language, 'Source URL', 'URL sursă', 'Source URL')),
                array('key' => 'target_url', 'label' => $this->aysa_adjustments_tr($language, 'Target URL', 'URL țintă', 'Target URL')),
                array('key' => 'anchor_text', 'label' => $this->aysa_adjustments_tr($language, 'Anchor', 'Anchor', 'Anchor')),
                array('key' => 'risk', 'label' => $this->aysa_adjustments_tr($language, 'Risk', 'Risc', 'Риск')),
                array('key' => 'health', 'label' => $this->aysa_adjustments_tr($language, 'Health', 'Stare', 'Състояние')),
                array('key' => 'issue_type', 'label' => $this->aysa_adjustments_tr($language, 'Issue', 'Problemă', 'Проблем')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'offpage_link_building_orders',
            $this->aysa_adjustments_tr($language, 'Link building orders', 'Comenzi link building', 'Link building поръчки'),
            $this->aysa_adjustments_tr($language, 'Orders already placed for off-page placements and sponsored articles.', 'Comenzile deja plasate pentru plasări off-page și articole sponsorizate.', 'Вече създадените поръчки за off-page публикации и спонсорирани статии.'),
            $this->aysa_adjustments_tr($language, 'Filter orders', 'Filtrează comenzile', 'Филтрирай поръчките'),
            isset($modules['offpage_link_building']['orders_rows']) ? $modules['offpage_link_building']['orders_rows'] : array(),
            array(
                array('key' => 'portal_domain', 'label' => $this->aysa_adjustments_tr($language, 'Portal', 'Portal', 'Портал')),
                array('key' => 'service_type', 'label' => $this->aysa_adjustments_tr($language, 'Service', 'Serviciu', 'Услуга')),
                array('key' => 'keyword', 'label' => $this->aysa_adjustments_tr($language, 'Keyword', 'Keyword', 'Keyword')),
                array('key' => 'target_url', 'label' => $this->aysa_adjustments_tr($language, 'Target URL', 'URL țintă', 'Target URL')),
                array('key' => 'status', 'label' => $this->aysa_adjustments_tr($language, 'Status', 'Status', 'Статус')),
                array('key' => 'verification', 'label' => $this->aysa_adjustments_tr($language, 'Verification', 'Verificare', 'Проверка')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'offpage_link_building_issues',
            $this->aysa_adjustments_tr($language, 'Link issues', 'Probleme de linkuri', 'Проблеми с линковете'),
            $this->aysa_adjustments_tr($language, 'Placements or links that need attention after publishing.', 'Plasări sau linkuri care au nevoie de atenție după publicare.', 'Публикации или линкове, които имат нужда от внимание след публикуване.'),
            $this->aysa_adjustments_tr($language, 'Filter issues', 'Filtrează problemele', 'Филтрирай проблемите'),
            isset($modules['offpage_link_building']['issues_rows']) ? $modules['offpage_link_building']['issues_rows'] : array(),
            array(
                array('key' => 'portal_domain', 'label' => $this->aysa_adjustments_tr($language, 'Portal', 'Portal', 'Портал')),
                array('key' => 'target_url', 'label' => $this->aysa_adjustments_tr($language, 'Target URL', 'URL țintă', 'Target URL')),
                array('key' => 'live_url', 'label' => $this->aysa_adjustments_tr($language, 'Live URL', 'URL live', 'Live URL')),
                array('key' => 'issue_type', 'label' => $this->aysa_adjustments_tr($language, 'Issue', 'Problemă', 'Проблем')),
                array('key' => 'status', 'label' => $this->aysa_adjustments_tr($language, 'Status', 'Status', 'Статус')),
                array('key' => 'message', 'label' => $this->aysa_adjustments_tr($language, 'Details', 'Detalii', 'Детайли')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'missing_pages',
            $this->aysa_adjustments_tr($language, 'Missing pages', 'Missing pages', 'Missing pages'),
            $this->aysa_adjustments_tr($language, 'Keywords and plans that still need a page.', 'Keyword-uri și planuri care încă au nevoie de o pagină.', 'Keyword-и и планове, които все още имат нужда от страница.'),
            $this->aysa_adjustments_tr($language, 'Filter missing pages', 'Filtrează lipsurile', 'Филтрирай липсите'),
            isset($modules['missing_pages']['rows']) ? $modules['missing_pages']['rows'] : array(),
            array(
                array('key' => 'keyword', 'label' => $this->aysa_adjustments_tr($language, 'Keyword', 'Keyword', 'Keyword')),
                array('key' => 'source', 'label' => $this->aysa_adjustments_tr($language, 'Source', 'Sursă', 'Източник')),
                array('key' => 'search_volume', 'label' => $this->aysa_adjustments_tr($language, 'Volume', 'Volum', 'Обем')),
                array('key' => 'target', 'label' => $this->aysa_adjustments_tr($language, 'Recommended page', 'Pagină recomandată', 'Препоръчана страница')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'content_strategy',
            $this->aysa_adjustments_tr($language, 'Content strategy', 'Content strategy', 'Content strategy'),
            $this->aysa_adjustments_tr($language, 'Existing content plans already saved in the project.', 'Planurile de content deja salvate în proiect.', 'Съществуващите content планове, вече запазени в проекта.'),
            $this->aysa_adjustments_tr($language, 'Filter content plans', 'Filtrează planurile', 'Филтрирай плановете'),
            isset($modules['content_strategy']['rows']) ? $modules['content_strategy']['rows'] : array(),
            array(
                array('key' => 'title', 'label' => $this->aysa_adjustments_tr($language, 'Title', 'Titlu', 'Заглавие')),
                array('key' => 'strategy_type', 'label' => $this->aysa_adjustments_tr($language, 'Type', 'Tip', 'Тип')),
                array('key' => 'status', 'label' => $this->aysa_adjustments_tr($language, 'Status', 'Status', 'Статус')),
                array('key' => 'stage', 'label' => $this->aysa_adjustments_tr($language, 'Stage', 'Etapă', 'Етап')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'onpage_optimization',
            $this->aysa_adjustments_tr($language, 'On-page optimization', 'On-page optimization', 'On-page optimization'),
            $this->aysa_adjustments_tr($language, 'Pages grouped by entity type, keyword focus, and optimization status.', 'Pagini grupate după tip de entitate, focusul de keyword și statusul optimizării.', 'Страници, групирани по тип обект, keyword фокус и статус на оптимизация.'),
            $this->aysa_adjustments_tr($language, 'Filter pages or keywords', 'Filtrează pagini sau keyword-uri', 'Филтрирай страници или keyword-и'),
            isset($modules['onpage_optimization']['rows']) ? $modules['onpage_optimization']['rows'] : array(),
            array(
                array('key' => 'entity_type', 'label' => $this->aysa_adjustments_tr($language, 'Entity type', 'Tip entitate', 'Тип обект')),
                array('key' => 'entity', 'label' => $this->aysa_adjustments_tr($language, 'Page', 'Pagină', 'Страница')),
                array('key' => 'main_keyword', 'label' => $this->aysa_adjustments_tr($language, 'Main keyword', 'Keyword principal', 'Основен keyword')),
                array('key' => 'all_keywords', 'label' => $this->aysa_adjustments_tr($language, 'All keywords', 'Toate keyword-urile', 'Всички keyword-и')),
                array('key' => 'optimized', 'label' => $this->aysa_adjustments_tr($language, 'Optimized', 'Optimizat', 'Оптимизирано')),
                array('key' => 'seo_score', 'label' => $this->aysa_adjustments_tr($language, 'SEO score', 'Scor SEO', 'SEO резултат')),
                array('key' => 'page', 'label' => $this->aysa_adjustments_tr($language, 'URL', 'URL', 'URL')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_site_audit',
            $this->aysa_adjustments_tr($language, 'Site audit overview', 'Privire generală audit tehnic', 'Преглед на site audit-а'),
            $this->aysa_adjustments_tr($language, 'Overview of the current technical crawl.', 'Privire generală asupra crawl-ului tehnic curent.', 'Общ преглед на текущото техническо обхождане.'),
            $this->aysa_adjustments_tr($language, 'Filter metrics', 'Filtrează metricile', 'Филтрирай метриките'),
            isset($modules['technical_site_audit']['overview_rows']) ? $modules['technical_site_audit']['overview_rows'] : array(),
            array(
                array('key' => 'metric', 'label' => $this->aysa_adjustments_tr($language, 'Metric', 'Metrică', 'Метрика')),
                array('key' => 'value', 'label' => $this->aysa_adjustments_tr($language, 'Value', 'Valoare', 'Стойност')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_site_audit_issues',
            $this->aysa_adjustments_tr($language, 'Site audit issues', 'Probleme audit tehnic', 'Проблеми от site audit-а'),
            $this->aysa_adjustments_tr($language, 'Issues found during the crawl.', 'Problemele găsite în timpul crawl-ului.', 'Проблемите, намерени по време на обхождането.'),
            $this->aysa_adjustments_tr($language, 'Filter issues', 'Filtrează problemele', 'Филтрирай проблемите'),
            isset($modules['technical_site_audit']['rows']) ? $modules['technical_site_audit']['rows'] : array(),
            array(
                array('key' => 'severity', 'label' => $this->aysa_adjustments_tr($language, 'Severity', 'Severitate', 'Тежест')),
                array('key' => 'title', 'label' => $this->aysa_adjustments_tr($language, 'Issue', 'Problemă', 'Проблем')),
                array('key' => 'affected_pages', 'label' => $this->aysa_adjustments_tr($language, 'Affected pages', 'Pagini afectate', 'Засегнати страници')),
                array('key' => 'description', 'label' => $this->aysa_adjustments_tr($language, 'Description', 'Descriere', 'Описание')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_site_audit_pages',
            $this->aysa_adjustments_tr($language, 'Crawled pages', 'Pagini crawl-ate', 'Обходени страници'),
            $this->aysa_adjustments_tr($language, 'Pages included in the audit.', 'Paginile incluse în audit.', 'Страници, включени в одита.'),
            $this->aysa_adjustments_tr($language, 'Filter pages', 'Filtrează paginile', 'Филтрирай страниците'),
            isset($modules['technical_site_audit']['pages']) ? $modules['technical_site_audit']['pages'] : array(),
            array(
                array('key' => 'url', 'label' => $this->aysa_adjustments_tr($language, 'URL', 'URL', 'URL')),
                array('key' => 'title', 'label' => $this->aysa_adjustments_tr($language, 'Title', 'Titlu', 'Заглавие')),
                array('key' => 'status_code', 'label' => $this->aysa_adjustments_tr($language, 'Status', 'Status', 'Статус')),
                array('key' => 'indexability', 'label' => $this->aysa_adjustments_tr($language, 'Indexability', 'Indexabilitate', 'Индексация')),
                array('key' => 'crawl_depth', 'label' => $this->aysa_adjustments_tr($language, 'Depth', 'Adâncime', 'Дълбочина')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_site_audit_files',
            $this->aysa_adjustments_tr($language, 'Audit exports', 'Exporturi audit', 'Експорти от audit-а'),
            $this->aysa_adjustments_tr($language, 'Saved audit files and exports.', 'Fișierele și exporturile salvate din audit.', 'Запазени файлове и експорти от одита.'),
            $this->aysa_adjustments_tr($language, 'Filter exports', 'Filtrează exporturile', 'Филтрирай експортите'),
            isset($modules['technical_site_audit']['files']) ? $modules['technical_site_audit']['files'] : array(),
            array(
                array('key' => 'filename', 'label' => $this->aysa_adjustments_tr($language, 'File', 'Fișier', 'Файл')),
                array('key' => 'type', 'label' => $this->aysa_adjustments_tr($language, 'Type', 'Tip', 'Тип')),
                array('key' => 'row_count', 'label' => $this->aysa_adjustments_tr($language, 'Rows', 'Rânduri', 'Редове')),
                array('key' => 'updated_at', 'label' => $this->aysa_adjustments_tr($language, 'Updated', 'Actualizat', 'Обновен')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_pagespeed',
            $this->aysa_adjustments_tr($language, 'PageSpeed overview', 'Privire generală PageSpeed', 'Преглед на PageSpeed'),
            $this->aysa_adjustments_tr($language, 'Overview of the current Google PageSpeed summary.', 'Privire generală asupra rezumatului Google PageSpeed.', 'Общ преглед на текущото Google PageSpeed резюме.'),
            $this->aysa_adjustments_tr($language, 'Filter PageSpeed metrics', 'Filtrează metricile PageSpeed', 'Филтрирай PageSpeed метриките'),
            isset($modules['technical_pagespeed']['overview_rows']) ? $modules['technical_pagespeed']['overview_rows'] : array(),
            array(
                array('key' => 'metric', 'label' => $this->aysa_adjustments_tr($language, 'Metric', 'Metrică', 'Метрика')),
                array('key' => 'value', 'label' => $this->aysa_adjustments_tr($language, 'Value', 'Valoare', 'Стойност')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_pagespeed_opportunities',
            $this->aysa_adjustments_tr($language, 'PageSpeed opportunities', 'Oportunități PageSpeed', 'PageSpeed възможности'),
            $this->aysa_adjustments_tr($language, 'The main opportunities reported by Google PageSpeed.', 'Principalele oportunități raportate de Google PageSpeed.', 'Основните възможности, отчетени от Google PageSpeed.'),
            $this->aysa_adjustments_tr($language, 'Filter opportunities', 'Filtrează oportunitățile', 'Филтрирай възможностите'),
            isset($modules['technical_pagespeed']['rows']) ? $modules['technical_pagespeed']['rows'] : array(),
            array(
                array('key' => 'strategy', 'label' => $this->aysa_adjustments_tr($language, 'Mode', 'Mod', 'Режим')),
                array('key' => 'title', 'label' => $this->aysa_adjustments_tr($language, 'Opportunity', 'Oportunitate', 'Възможност')),
                array('key' => 'display_value', 'label' => $this->aysa_adjustments_tr($language, 'Display value', 'Valoare afișată', 'Показвана стойност')),
                array('key' => 'impact', 'label' => $this->aysa_adjustments_tr($language, 'Impact', 'Impact', 'Влияние')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_pagespeed_metrics',
            $this->aysa_adjustments_tr($language, 'PageSpeed metrics', 'Metrici PageSpeed', 'PageSpeed метрики'),
            $this->aysa_adjustments_tr($language, 'Desktop and mobile performance metrics.', 'Metrici de performanță pentru desktop și mobile.', 'Performance метрики за desktop и mobile.'),
            $this->aysa_adjustments_tr($language, 'Filter metrics', 'Filtrează metricile', 'Филтрирай метриките'),
            isset($modules['technical_pagespeed']['metrics_rows']) ? $modules['technical_pagespeed']['metrics_rows'] : array(),
            array(
                array('key' => 'metric', 'label' => $this->aysa_adjustments_tr($language, 'Metric', 'Metrică', 'Метрика')),
                array('key' => 'desktop', 'label' => $this->aysa_adjustments_tr($language, 'Desktop', 'Desktop', 'Desktop')),
                array('key' => 'mobile', 'label' => $this->aysa_adjustments_tr($language, 'Mobile', 'Mobile', 'Mobile')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_mobile_optimization',
            $this->aysa_adjustments_tr($language, 'Mobile optimization', 'Optimizare mobile', 'Mobile оптимизация'),
            $this->aysa_adjustments_tr($language, 'Mobile-focused performance and usability summary.', 'Rezumat axat pe performanța și usability-ul pe mobile.', 'Обобщение за performance и usability на mobile.'),
            $this->aysa_adjustments_tr($language, 'Filter mobile metrics', 'Filtrează metricile mobile', 'Филтрирай mobile метриките'),
            isset($modules['technical_mobile_optimization']['overview_rows']) ? $modules['technical_mobile_optimization']['overview_rows'] : array(),
            array(
                array('key' => 'metric', 'label' => $this->aysa_adjustments_tr($language, 'Metric', 'Metrică', 'Метрика')),
                array('key' => 'value', 'label' => $this->aysa_adjustments_tr($language, 'Value', 'Valoare', 'Стойност')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_mobile_optimization_opportunities',
            $this->aysa_adjustments_tr($language, 'Mobile opportunities', 'Oportunități mobile', 'Mobile възможности'),
            $this->aysa_adjustments_tr($language, 'The main mobile-only opportunities reported by PageSpeed.', 'Principalele oportunități doar pentru mobile raportate de PageSpeed.', 'Основните възможности само за mobile, отчетени от PageSpeed.'),
            $this->aysa_adjustments_tr($language, 'Filter mobile opportunities', 'Filtrează oportunitățile mobile', 'Филтрирай mobile възможностите'),
            isset($modules['technical_mobile_optimization']['rows']) ? $modules['technical_mobile_optimization']['rows'] : array(),
            array(
                array('key' => 'title', 'label' => $this->aysa_adjustments_tr($language, 'Opportunity', 'Oportunitate', 'Възможност')),
                array('key' => 'display_value', 'label' => $this->aysa_adjustments_tr($language, 'Display value', 'Valoare afișată', 'Показвана стойност')),
                array('key' => 'impact', 'label' => $this->aysa_adjustments_tr($language, 'Impact', 'Impact', 'Влияние')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_errors',
            $this->aysa_adjustments_tr($language, 'Errors hub', 'Hub erori', 'Errors hub'),
            $this->aysa_adjustments_tr($language, 'Unified 404 / 301 / 410 snapshot for review, approval, and automation.', 'Snapshot unificat 404 / 301 / 410 pentru review, aprobare și automatizare.', 'Обединен 404 / 301 / 410 snapshot за преглед, одобрение и автоматизация.'),
            $this->aysa_adjustments_tr($language, 'Filter technical errors', 'Filtrează erorile tehnice', 'Филтрирай техническите грешки'),
            isset($modules['technical_errors']['rows']) ? $modules['technical_errors']['rows'] : array(),
            array(
                array('key' => 'kind', 'label' => $this->aysa_adjustments_tr($language, 'Kind', 'Tip', 'Вид')),
                array('key' => 'source_url', 'label' => $this->aysa_adjustments_tr($language, 'Source URL', 'URL sursă', 'Source URL')),
                array('key' => 'suggested_action', 'label' => $this->aysa_adjustments_tr($language, 'Suggested action', 'Acțiune sugerată', 'Предложено действие')),
                array('key' => 'suggested_target_url', 'label' => $this->aysa_adjustments_tr($language, 'Suggested target', 'Țintă sugerată', 'Предложена цел')),
                array('key' => 'suggested_confidence', 'label' => $this->aysa_adjustments_tr($language, 'Confidence', 'Încredere', 'Увереност')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_404_errors',
            $this->aysa_adjustments_tr($language, '404 errors', 'Erori 404', '404 грешки'),
            $this->aysa_adjustments_tr($language, 'URLs that currently return 404 and need attention.', 'URL-uri care întorc acum 404 și au nevoie de atenție.', 'URL адреси, които в момента връщат 404 и изискват внимание.'),
            $this->aysa_adjustments_tr($language, 'Filter 404 URLs', 'Filtrează URL-urile 404', 'Филтрирай 404 URL адресите'),
            isset($modules['technical_404_errors']['rows']) ? $modules['technical_404_errors']['rows'] : array(),
            array(
                array('key' => 'url', 'label' => $this->aysa_adjustments_tr($language, '404 URL', 'URL 404', '404 URL')),
                array('key' => 'source', 'label' => $this->aysa_adjustments_tr($language, 'Source', 'Sursă', 'Източник')),
                array('key' => 'hits', 'label' => $this->aysa_adjustments_tr($language, 'Hits', 'Lovituri', 'Попадения')),
                array('key' => 'best_redirect_url', 'label' => $this->aysa_adjustments_tr($language, 'Suggested redirect', 'Redirect sugerat', 'Предложен redirect')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_301_redirects',
            $this->aysa_adjustments_tr($language, '301 redirects', 'Redirecturi 301', '301 redirect-и'),
            $this->aysa_adjustments_tr($language, 'Redirect rules currently managed in the project.', 'Regulile de redirect gestionate acum în proiect.', 'Redirect правилата, които в момента се управляват в проекта.'),
            $this->aysa_adjustments_tr($language, 'Filter redirects', 'Filtrează redirecturile', 'Филтрирай redirect-ите'),
            isset($modules['technical_301_redirects']['rows']) ? $modules['technical_301_redirects']['rows'] : array(),
            array(
                array('key' => 'source_url', 'label' => $this->aysa_adjustments_tr($language, 'Source URL', 'URL sursă', 'Source URL')),
                array('key' => 'target_url', 'label' => $this->aysa_adjustments_tr($language, 'Target URL', 'URL destinație', 'Target URL')),
                array('key' => 'status', 'label' => $this->aysa_adjustments_tr($language, 'Status', 'Status', 'Статус')),
                array('key' => 'rule_type', 'label' => $this->aysa_adjustments_tr($language, 'Type', 'Tip', 'Тип')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_410_gone',
            $this->aysa_adjustments_tr($language, '410 gone URLs', 'URL-uri marcate 410 Gone', '410 gone URL адреси'),
            $this->aysa_adjustments_tr($language, 'URLs intentionally marked as gone and removed from search.', 'URL-uri marcate intenționat ca gone și scoase din căutare.', 'URL адреси, умишлено маркирани като gone и извадени от търсенето.'),
            $this->aysa_adjustments_tr($language, 'Filter gone URLs', 'Filtrează URL-urile gone', 'Филтрирай gone URL адресите'),
            isset($modules['technical_410_gone']['rows']) ? $modules['technical_410_gone']['rows'] : array(),
            array(
                array('key' => 'url', 'label' => $this->aysa_adjustments_tr($language, 'Gone URL', 'URL marcat gone', 'Gone URL')),
                array('key' => 'status', 'label' => $this->aysa_adjustments_tr($language, 'Status', 'Status', 'Статус')),
                array('key' => 'rule_type', 'label' => $this->aysa_adjustments_tr($language, 'Type', 'Tip', 'Тип')),
                array('key' => 'source', 'label' => $this->aysa_adjustments_tr($language, 'Source', 'Sursă', 'Източник')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_schema_markup',
            $this->aysa_adjustments_tr($language, 'Schema markup', 'Schema markup', 'Schema markup'),
            $this->aysa_adjustments_tr($language, 'Schema coverage across the pages currently checked by the project.', 'Acoperirea schema pe paginile verificate acum de proiect.', 'Schema покритието върху страниците, които в момента се проверяват от проекта.'),
            $this->aysa_adjustments_tr($language, 'Filter schema rows', 'Filtrează rândurile schema', 'Филтрирай schema редовете'),
            isset($modules['technical_schema_markup']['rows']) ? $modules['technical_schema_markup']['rows'] : array(),
            array(
                array('key' => 'title', 'label' => $this->aysa_adjustments_tr($language, 'Page', 'Pagină', 'Страница')),
                array('key' => 'page_type', 'label' => $this->aysa_adjustments_tr($language, 'Recommended schema', 'Schema recomandată', 'Препоръчана schema')),
                array('key' => 'status', 'label' => $this->aysa_adjustments_tr($language, 'Status', 'Status', 'Статус')),
                array('key' => 'url', 'label' => $this->aysa_adjustments_tr($language, 'URL', 'URL', 'URL')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_internal_linking',
            $this->aysa_adjustments_tr($language, 'Internal linking', 'Linkuri interne', 'Вътрешни линкове'),
            $this->aysa_adjustments_tr($language, 'Current internal linking report grouped by keyword.', 'Raportul actual de linkuri interne, grupat pe keyword.', 'Текущият отчет за вътрешни линкове, групиран по keyword.'),
            $this->aysa_adjustments_tr($language, 'Filter internal links', 'Filtrează linkurile interne', 'Филтрирай вътрешните линкове'),
            isset($modules['technical_internal_linking']['rows']) ? $modules['technical_internal_linking']['rows'] : array(),
            array(
                array('key' => 'keyword', 'label' => $this->aysa_adjustments_tr($language, 'Keyword', 'Keyword', 'Keyword')),
                array('key' => 'target_title', 'label' => $this->aysa_adjustments_tr($language, 'Target page', 'Pagină țintă', 'Целева страница')),
                array('key' => 'total_links', 'label' => $this->aysa_adjustments_tr($language, 'Links added', 'Linkuri adăugate', 'Добавени линкове')),
                array('key' => 'sources_count', 'label' => $this->aysa_adjustments_tr($language, 'Source pages', 'Pagini sursă', 'Източници')),
                array('key' => 'last_placement', 'label' => $this->aysa_adjustments_tr($language, 'Last placement', 'Ultima plasare', 'Последно поставяне')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_internal_linking_preview',
            $this->aysa_adjustments_tr($language, 'Internal linking opportunities', 'Oportunități de linkuri interne', 'Възможности за вътрешни линкове'),
            $this->aysa_adjustments_tr($language, 'Preview of the links that can be added safely next.', 'Previzualizare a linkurilor care pot fi adăugate în siguranță în pasul următor.', 'Преглед на линковете, които могат да бъдат добавени безопасно на следващата стъпка.'),
            $this->aysa_adjustments_tr($language, 'Filter opportunities', 'Filtrează oportunitățile', 'Филтрирай възможностите'),
            isset($modules['technical_internal_linking']['preview_rows']) ? $modules['technical_internal_linking']['preview_rows'] : array(),
            array(
                array('key' => 'content_title', 'label' => $this->aysa_adjustments_tr($language, 'Source content', 'Conținut sursă', 'Изходно съдържание')),
                array('key' => 'content_type', 'label' => $this->aysa_adjustments_tr($language, 'Type', 'Tip', 'Тип')),
                array('key' => 'links_count', 'label' => $this->aysa_adjustments_tr($language, 'Links to add', 'Linkuri de adăugat', 'Линкове за добавяне')),
                array('key' => 'keywords', 'label' => $this->aysa_adjustments_tr($language, 'Keywords', 'Keyword-uri', 'Keyword-и')),
                array('key' => 'targets', 'label' => $this->aysa_adjustments_tr($language, 'Targets', 'Ținte', 'Цели')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_sitemap_robots',
            $this->aysa_adjustments_tr($language, 'Sitemap + Robots overview', 'Privire generală Sitemap + Robots', 'Преглед на Sitemap + Robots'),
            $this->aysa_adjustments_tr($language, 'The current sitemap and robots.txt status.', 'Starea curentă pentru sitemap și robots.txt.', 'Текущото състояние на sitemap и robots.txt.'),
            $this->aysa_adjustments_tr($language, 'Filter checks', 'Filtrează verificările', 'Филтрирай проверките'),
            isset($modules['technical_sitemap_robots']['overview_rows']) ? $modules['technical_sitemap_robots']['overview_rows'] : array(),
            array(
                array('key' => 'metric', 'label' => $this->aysa_adjustments_tr($language, 'Metric', 'Metrică', 'Метрика')),
                array('key' => 'value', 'label' => $this->aysa_adjustments_tr($language, 'Value', 'Valoare', 'Стойност')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_sitemap_robots_checks',
            $this->aysa_adjustments_tr($language, 'Sitemap + Robots checks', 'Verificări Sitemap + Robots', 'Sitemap + Robots проверки'),
            $this->aysa_adjustments_tr($language, 'The checks performed on sitemap endpoints and robots.txt.', 'Verificările făcute pe endpointurile sitemap și pe robots.txt.', 'Проверките върху sitemap endpoint-ите и robots.txt.'),
            $this->aysa_adjustments_tr($language, 'Filter checks', 'Filtrează verificările', 'Филтрирай проверките'),
            isset($modules['technical_sitemap_robots']['rows']) ? $modules['technical_sitemap_robots']['rows'] : array(),
            array(
                array('key' => 'label', 'label' => $this->aysa_adjustments_tr($language, 'Check', 'Verificare', 'Проверка')),
                array('key' => 'status', 'label' => $this->aysa_adjustments_tr($language, 'Status', 'Status', 'Статус')),
                array('key' => 'message', 'label' => $this->aysa_adjustments_tr($language, 'Details', 'Detalii', 'Детайли')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_sitemap_robots_sitemaps',
            $this->aysa_adjustments_tr($language, 'Detected sitemaps', 'Sitemap-uri detectate', 'Открити sitemap-и'),
            $this->aysa_adjustments_tr($language, 'Detected sitemap endpoints and their reachability.', 'Endpointurile sitemap detectate și starea lor.', 'Откритите sitemap endpoint-и и тяхната достъпност.'),
            $this->aysa_adjustments_tr($language, 'Filter sitemap endpoints', 'Filtrează sitemap-urile', 'Филтрирай sitemap-ите'),
            isset($modules['technical_sitemap_robots']['sitemaps']) ? $modules['technical_sitemap_robots']['sitemaps'] : array(),
            array(
                array('key' => 'url', 'label' => $this->aysa_adjustments_tr($language, 'URL', 'URL', 'URL')),
                array('key' => 'reachable', 'label' => $this->aysa_adjustments_tr($language, 'Reachable', 'Accesibil', 'Достъпен')),
                array('key' => 'message', 'label' => $this->aysa_adjustments_tr($language, 'Details', 'Detalii', 'Детайли')),
            )
        );
        $extra_sections[] = $this->aysa_adjustments_build_table_section(
            'technical_sitemap_robots_file',
            $this->aysa_adjustments_tr($language, 'robots.txt', 'robots.txt', 'robots.txt'),
            $this->aysa_adjustments_tr($language, 'Current robots.txt source and content.', 'Sursa și conținutul curent pentru robots.txt.', 'Текущият source и content на robots.txt.'),
            $this->aysa_adjustments_tr($language, 'Filter robots content', 'Filtrează robots', 'Филтрирай robots'),
            isset($modules['technical_sitemap_robots']['robots_rows']) ? $modules['technical_sitemap_robots']['robots_rows'] : array(),
            array(
                array('key' => 'field', 'label' => $this->aysa_adjustments_tr($language, 'Field', 'Câmp', 'Поле')),
                array('key' => 'value', 'label' => $this->aysa_adjustments_tr($language, 'Value', 'Valoare', 'Стойност')),
            )
        );

        foreach ($extra_sections as $section) {
            if (!empty($section)) {
                $artifact['sections'][] = $section;
            }
        }

        return $artifact;
    }

    private function aysa_adjustments_build_table_section($id, $title, $summary, $filter_placeholder, $rows, $columns) {
        if (!is_array($rows) || $rows === array()) {
            return array();
        }
        return array(
            'id' => sanitize_key((string) $id),
            'title' => (string) $title,
            'button_label' => (string) $title,
            'summary' => (string) $summary,
            'kind' => 'table',
            'filter_placeholder' => (string) $filter_placeholder,
            'empty_label' => 'No rows available.',
            'columns' => array_values($columns),
            'rows' => array_values($rows),
        );
    }

    private function aysa_adjustments_run_technical_site_audit_refresh($project_id, $start_if_missing = false) {
        $project_id = intval($project_id);
        $snapshot = $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id);
        $job_id = sanitize_text_field((string) ($snapshot['job_id'] ?? ''));

        if ($job_id === '' && $start_if_missing) {
            $target_url = $this->aysa_technical_resolve_target_url();
            $payload = array(
                'project_id' => $project_id,
                'url' => $target_url,
                'job_id' => 'wp_' . $project_id . '_' . gmdate('Ymd_His'),
                'mode' => 'baseline_full',
            );
            $start = $this->aysa_broker_request('POST', '/v1/technical/screamingfrog/start', $payload, 90);
            if (is_array($start) && !empty($start['ok'])) {
                update_option($this->aysa_technical_site_audit_state_key($project_id), array(
                    'project_id' => $project_id,
                    'job_id' => (string) ($start['job_id'] ?? ''),
                    'target_url' => (string) ($start['target_url'] ?? $target_url),
                    'status' => 'running',
                    'started_at' => current_time('mysql'),
                    'updated_at' => current_time('mysql'),
                    'mode' => (string) ($start['mode'] ?? 'baseline_full'),
                    'config_path' => (string) ($start['config_path'] ?? ''),
                    'export_tabs' => (string) ($start['export_tabs'] ?? ''),
                    'bulk_exports' => (string) ($start['bulk_exports'] ?? ''),
                    'reports' => (string) ($start['reports'] ?? ''),
                    'result_count' => 0,
                ), false);
                $snapshot = $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id);
                $job_id = sanitize_text_field((string) ($snapshot['job_id'] ?? ''));
            }
        }

        if ($job_id !== '') {
            $status = $this->aysa_broker_request('POST', '/v1/technical/screamingfrog/status', array('job_id' => $job_id), 45);
            if (is_array($status)) {
                $state = get_option($this->aysa_technical_site_audit_state_key($project_id), array());
                if (!is_array($state)) {
                    $state = array();
                }
                $state = array_merge($state, array(
                    'project_id' => $project_id,
                    'job_id' => $job_id,
                    'status' => (string) ($status['status'] ?? ($state['status'] ?? 'unknown')),
                    'updated_at' => current_time('mysql'),
                    'job_dir' => (string) ($status['job_dir'] ?? ''),
                    'mode' => (string) ($status['mode'] ?? ($state['mode'] ?? 'baseline_full')),
                    'config_path' => (string) ($status['config_path'] ?? ($state['config_path'] ?? '')),
                    'export_tabs' => (string) ($status['export_tabs'] ?? ($state['export_tabs'] ?? '')),
                    'bulk_exports' => (string) ($status['bulk_exports'] ?? ($state['bulk_exports'] ?? '')),
                    'reports' => (string) ($status['reports'] ?? ($state['reports'] ?? '')),
                    'artifacts' => isset($status['artifacts']) && is_array($status['artifacts']) ? $status['artifacts'] : array(),
                ));
                update_option($this->aysa_technical_site_audit_state_key($project_id), $state, false);
            }

            $results = $this->aysa_broker_request('POST', '/v1/technical/screamingfrog/results', array(
                'job_id' => $job_id,
                'preview_rows' => 50,
            ), 60);
            if (is_array($results)) {
                $state = get_option($this->aysa_technical_site_audit_state_key($project_id), array());
                if (!is_array($state)) {
                    $state = array();
                }
                $state = array_merge($state, array(
                    'project_id' => $project_id,
                    'job_id' => $job_id,
                    'status' => (string) ($results['status'] ?? ($state['status'] ?? 'unknown')),
                    'updated_at' => current_time('mysql'),
                    'mode' => (string) ($results['mode'] ?? ($state['mode'] ?? 'baseline_full')),
                    'config_path' => (string) ($results['config_path'] ?? ($state['config_path'] ?? '')),
                    'export_tabs' => (string) ($results['export_tabs'] ?? ($state['export_tabs'] ?? '')),
                    'bulk_exports' => (string) ($results['bulk_exports'] ?? ($state['bulk_exports'] ?? '')),
                    'reports' => (string) ($results['reports'] ?? ($state['reports'] ?? '')),
                    'result_count' => count(isset($results['files']) && is_array($results['files']) ? $results['files'] : array()),
                    'latest_output_dir' => (string) ($results['latest_output_dir'] ?? ''),
                    'audit_snapshot' => isset($results['audit']) && is_array($results['audit']) ? $results['audit'] : array(),
                    'result_files' => isset($results['files']) && is_array($results['files']) ? $results['files'] : array(),
                ));
                $version_id = $this->aysa_sf_site_audit_persist_version($project_id, $state);
                if ($version_id > 0) {
                    $latest_version = $this->aysa_sf_site_audit_get_latest_version_row($project_id);
                    if (is_array($latest_version) && !empty($latest_version)) {
                        $state['version_id'] = intval($latest_version['id'] ?? 0);
                        $state['version_number'] = intval($latest_version['version_number'] ?? 0);
                    }
                }
                update_option($this->aysa_technical_site_audit_state_key($project_id), $state, false);
            }
        }

        return $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id);
    }

    private function aysa_adjustments_build_technical_site_audit_completion_message($snapshot, $language = 'en', $just_started = false) {
        $snapshot = is_array($snapshot) ? $snapshot : array();
        $status = sanitize_key((string) ($snapshot['status'] ?? 'idle'));
        $pages_crawled = intval($snapshot['pages_crawled'] ?? 0);
        $site_health = intval($snapshot['site_health'] ?? 0);
        $errors = intval($snapshot['errors'] ?? 0);
        $warnings = intval($snapshot['warnings'] ?? 0);
        $rows = isset($snapshot['rows']) && is_array($snapshot['rows']) ? array_values($snapshot['rows']) : array();
        $issue_titles = array();
        foreach (array_slice($rows, 0, 3) as $row) {
            if (!is_array($row)) {
                continue;
            }
            $title = trim((string) ($row['title'] ?? ''));
            if ($title !== '') {
                $issue_titles[] = $title;
            }
        }
        $issues_preview = implode(', ', $issue_titles);

        if (($status === 'running' || $status === 'queued') && $pages_crawled <= 0) {
            return $this->aysa_adjustments_tr(
                $language,
                'I started the full technical audit. The crawl is running now, and I will use the saved results as soon as the first pages and issues are available.',
                'Am pornit auditul tehnic complet. Crawl-ul rulează acum, iar imediat ce apar primele pagini și probleme, folosesc rezultatele salvate aici.',
                'Стартирах пълния технически одит. Обхождането върви сега и веднага щом има първи страници и проблеми, ще използвам запазените резултати тук.'
            );
        }

        if ($pages_crawled > 0 || !empty($rows)) {
            return match ($language) {
                'ro' => 'Am audit tehnic salvat pentru site. În acest moment au fost crawl-ate ' . $pages_crawled . ' pagini, health score-ul este ' . $site_health . '%, am ' . $errors . ' erori și ' . $warnings . ' avertismente. Cele mai vizibile probleme sunt: ' . ($issues_preview !== '' ? $issues_preview : 'câteva probleme tehnice care merită verificate') . '. Dacă vrei, îți arăt imediat problemele sau paginile crawl-ate.',
                'bg' => 'Имам запазен технически одит за сайта. В момента са обходени ' . $pages_crawled . ' страници, health score е ' . $site_health . '%, има ' . $errors . ' грешки и ' . $warnings . ' предупреждения. Най-видимите проблеми са: ' . ($issues_preview !== '' ? $issues_preview : 'няколко технически проблема, които си струва да се проверят') . '. Ако искаш, веднага ще ти покажа проблемите или обходените страници.',
                default => 'I already have a saved technical audit for the site. Right now it has crawled ' . $pages_crawled . ' pages, the health score is ' . $site_health . '%, and I can see ' . $errors . ' errors and ' . $warnings . ' warnings. The most visible issues are: ' . ($issues_preview !== '' ? $issues_preview : 'a few technical issues worth reviewing') . '. If you want, I can show you the issues or the crawled pages next.',
            };
        }

        return $this->aysa_adjustments_tr(
            $language,
            'I do not have a saved technical audit yet. I can start a full site audit now and then show you the errors, warnings, and crawled pages.',
            'Încă nu am un audit tehnic salvat. Pot porni acum un audit complet de site și apoi îți arăt erorile, avertismentele și paginile crawl-ate.',
            'Все още нямам запазен технически одит. Мога да стартирам пълен site audit сега и после да ти покажа грешките, предупрежденията и обходените страници.'
        );
    }

    private function aysa_adjustments_run_technical_pagespeed_refresh($project_id, $refresh = true) {
        $project_id = intval($project_id);
        $target_url = $this->aysa_technical_resolve_target_url();
        $payload = array(
            'project_id' => $project_id,
            'url' => $target_url,
            'force_refresh' => !empty($refresh),
        );
        $result = $this->aysa_broker_request('POST', '/v1/technical/pagespeed/summary', $payload, 90);
        if (is_wp_error($result)) {
            $fallback = $this->aysa_pagespeed_summary_fallback($target_url);
            if (!is_wp_error($fallback)) {
                $result = $fallback;
            } else {
                $legacy = $this->aysa_pagespeed_summary_from_legacy_endpoints($project_id, $target_url, !empty($refresh));
                if (!is_wp_error($legacy)) {
                    $result = $legacy;
                } else {
                    $state = get_option($this->aysa_technical_pagespeed_state_key($project_id), array());
                    if (is_array($state) && !empty($state['data']) && is_array($state['data'])) {
                        $result = $state['data'];
                    } else {
                        $result = array();
                    }
                }
            }
        }

        if (is_array($result) && !empty($result)) {
            update_option($this->aysa_technical_pagespeed_state_key($project_id), array(
                'project_id' => $project_id,
                'updated_at' => current_time('mysql'),
                'updated_at_unix' => time(),
                'data' => $result,
            ), false);
        }

        return $this->aysa_adjustments_get_technical_pagespeed_snapshot($project_id);
    }

    private function aysa_adjustments_run_technical_sitemap_robots_refresh($project_id, $refresh = true) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array();
        }

        $target_url = $this->aysa_technical_resolve_target_url();
        $state_key = $this->aysa_technical_sitemap_robots_state_key($project_id);
        $cached = get_option($state_key, array());
        if (!is_array($cached)) {
            $cached = array();
        }

        $max_age_seconds = 6 * HOUR_IN_SECONDS;
        if (
            !$refresh &&
            !empty($cached['updated_at_unix']) &&
            (time() - intval($cached['updated_at_unix']) < $max_age_seconds) &&
            !empty($cached['data']) &&
            is_array($cached['data'])
        ) {
            return $this->aysa_adjustments_get_technical_sitemap_robots_snapshot($project_id);
        }

        $audit = $this->aysa_technical_build_sitemap_robots_audit($target_url);
        $state = array(
            'project_id' => $project_id,
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'data' => $audit,
        );
        update_option($state_key, $state, false);
        return $this->aysa_adjustments_get_technical_sitemap_robots_snapshot($project_id);
    }

    private function aysa_adjustments_build_technical_sitemap_robots_completion_message($snapshot, $language = 'en') {
        $snapshot = is_array($snapshot) ? $snapshot : array();
        $errors = intval($snapshot['errors'] ?? 0);
        $warnings = intval($snapshot['warnings'] ?? 0);
        $passes = intval($snapshot['passes'] ?? 0);
        $robots_health = sanitize_text_field((string) ($snapshot['robots_health'] ?? ''));
        $sitemap_health = sanitize_text_field((string) ($snapshot['sitemap_health'] ?? ''));
        $checks = isset($snapshot['rows']) && is_array($snapshot['rows']) ? $snapshot['rows'] : array();
        $sitemaps = isset($snapshot['sitemaps']) && is_array($snapshot['sitemaps']) ? $snapshot['sitemaps'] : array();
        $primary = trim((string) ($snapshot['primary_sitemap'] ?? ''));

        $topIssues = array();
        foreach (array_slice(array_values(array_filter($checks, static function ($row) {
            return is_array($row) && ((string) ($row['status'] ?? '')) !== 'pass';
        })), 0, 3) as $row) {
            $label = trim((string) ($row['label'] ?? ''));
            if ($label !== '') {
                $topIssues[] = $label;
            }
        }
        $issuePreview = implode(', ', $topIssues);
        $reachable = 0;
        foreach ($sitemaps as $row) {
            if (!empty($row['reachable'])) {
                $reachable++;
            }
        }

        return match ($language) {
            'ro' => 'Am verificat sitemap-ul și robots.txt. În acest moment am ' . $errors . ' probleme, ' . $warnings . ' avertismente și ' . $passes . ' verificări care trec corect. Sănătatea robots.txt este ' . ($robots_health !== '' ? $robots_health : 'necunoscută') . ', iar sănătatea sitemap-ului este ' . ($sitemap_health !== '' ? $sitemap_health : 'necunoscută') . '. '
                . ($primary !== '' ? 'Sitemap-ul principal detectat este ' . $primary . '. ' : '')
                . ($reachable > 0 ? 'Am găsit ' . $reachable . ' endpointuri sitemap accesibile. ' : 'Nu am găsit încă un endpoint sitemap accesibil. ')
                . ($issuePreview !== '' ? 'Cele mai importante puncte de verificat sunt ' . $issuePreview . '. ' : '')
                . 'Dacă vrei, îți arăt verificările, sitemap-urile detectate sau conținutul actual din robots.txt.',
            'bg' => 'Проверих sitemap-а и robots.txt. В момента имам ' . $errors . ' проблема, ' . $warnings . ' предупреждения и ' . $passes . ' проверки, които минават успешно. Health на robots.txt е ' . ($robots_health !== '' ? $robots_health : 'неизвестен') . ', а health на sitemap-а е ' . ($sitemap_health !== '' ? $sitemap_health : 'неизвестен') . '. '
                . ($primary !== '' ? 'Откритият primary sitemap е ' . $primary . '. ' : '')
                . ($reachable > 0 ? 'Намерих ' . $reachable . ' достъпни sitemap endpoint-а. ' : 'Все още не намерих достъпен sitemap endpoint. ')
                . ($issuePreview !== '' ? 'Най-важните неща за проверка са ' . $issuePreview . '. ' : '')
                . 'Ако искаш, ще ти покажа проверките, откритите sitemap-и или текущото съдържание на robots.txt.',
            default => 'I checked the sitemap and robots.txt. Right now I have ' . $errors . ' issues, ' . $warnings . ' warnings, and ' . $passes . ' checks passing correctly. robots.txt health is ' . ($robots_health !== '' ? $robots_health : 'unknown') . ', and sitemap health is ' . ($sitemap_health !== '' ? $sitemap_health : 'unknown') . '. '
                . ($primary !== '' ? 'The primary sitemap detected is ' . $primary . '. ' : '')
                . ($reachable > 0 ? 'I found ' . $reachable . ' reachable sitemap endpoints. ' : 'I have not found a reachable sitemap endpoint yet. ')
                . ($issuePreview !== '' ? 'The main things worth checking are ' . $issuePreview . '. ' : '')
                . 'If you want, I can show you the checks, the detected sitemaps, or the current robots.txt content.',
        };
    }

    private function aysa_adjustments_run_technical_internal_linking_preview($project_id) {
        $service = $this->aysa_internal_linking_workspace_service();
        if ($service) {
            $result = $service->run_legacy_action(intval($project_id), 'analyze');
            if (!empty($result['ok']) && is_array($result['result'] ?? null)) {
                return array(
                    'total_keywords_found' => intval($result['result']['total_keywords_found'] ?? 0),
                    'content_items_scanned' => intval($result['result']['content_items_scanned'] ?? 0),
                    'links_to_be_added' => intval($result['result']['links_added'] ?? 0),
                    'preview_changes' => $this->aysa_adjustments_format_internal_linking_preview_changes($result['result']['link_mappings'] ?? array()),
                    'summary' => sanitize_text_field((string) ($result['result']['summary'] ?? '')),
                    'success' => !empty($result['result']['success']),
                    'message' => sanitize_text_field((string) ($result['result']['message'] ?? '')),
                );
            }
        }

        if (!class_exists('Aysa_Internal_Linking')) {
            return array('success' => false, 'message' => 'Internal linking class is not available.');
        }
        $linking = new Aysa_Internal_Linking();
        $result = $linking->process_all_content_with_main_keywords(true);
        $state = array(
            'project_id' => $project_id,
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'data' => array(
                'total_keywords_found' => intval($result['total_keywords_found'] ?? 0),
                'content_items_scanned' => intval($result['content_items_scanned'] ?? 0),
                'links_to_be_added' => intval($result['links_added'] ?? 0),
                'preview_changes' => $this->aysa_adjustments_format_internal_linking_preview_changes($result['link_mappings'] ?? array()),
                'summary' => sanitize_text_field((string) ($result['summary'] ?? '')),
                'success' => !empty($result['success']),
                'message' => sanitize_text_field((string) ($result['message'] ?? '')),
            ),
        );
        update_option($this->aysa_technical_internal_linking_preview_state_key($project_id), $state, false);
        return $state['data'];
    }

    private function aysa_adjustments_run_technical_internal_linking_process($project_id) {
        $service = $this->aysa_internal_linking_workspace_service();
        if ($service) {
            $result = $service->run_legacy_action(intval($project_id), 'apply_safe');
            if (!empty($result['ok']) && is_array($result['result'] ?? null)) {
                return $result['result'];
            }
        }

        if (!class_exists('Aysa_Internal_Linking')) {
            return array('success' => false, 'message' => 'Internal linking class is not available.');
        }
        $linking = new Aysa_Internal_Linking();
        $result = $linking->process_all_content_with_main_keywords(false);
        delete_option($this->aysa_technical_internal_linking_preview_state_key($project_id));
        return is_array($result) ? $result : array('success' => false, 'message' => 'Internal linking process did not return a valid result.');
    }

    private function aysa_adjustments_run_onpage_optimize_needed($project_id, $limit = 5) {
        $project_id = intval($project_id);
        $limit = max(1, min(10, intval($limit)));
        $snapshot = $this->aysa_adjustments_get_onpage_optimization_snapshot($project_id);
        $rows = isset($snapshot['rows']) && is_array($snapshot['rows']) ? $snapshot['rows'] : array();

        if (!class_exists('Aysa_Seo_Generation_Processor')) {
            $processor_file = plugin_dir_path(dirname(__FILE__)) . 'includes/automation/processors/class-seo-generation-processor.php';
            if (file_exists($processor_file)) {
                require_once $processor_file;
            }
        }

        if (!class_exists('Aysa_Seo_Generation_Processor')) {
            return array(
                'processed' => 0,
                'generated' => 0,
                'existing' => 0,
                'errors' => 1,
                'items' => array(),
                'error_message' => 'SEO generation processor is not available.',
            );
        }

        $processor = new Aysa_Seo_Generation_Processor();
        $items = array();
        $generated = 0;
        $existing = 0;
        $errors = 0;

        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }

            $status = (string) ($row['optimized'] ?? '');
            if ($status === 'Optimized') {
                continue;
            }

            $entity_id = intval($row['entity_id'] ?? 0);
            $entity_type = trim((string) ($row['entity_processor_type'] ?? ''));
            $keyword = trim((string) ($row['main_keyword'] ?? ''));
            if ($entity_id <= 0 || $entity_type === '' || $keyword === '') {
                continue;
            }

            if ($this->aysa_adjustments_has_pending_seo_review($entity_id, $entity_type, $keyword)) {
                $existing++;
                $items[] = array(
                    'entity' => (string) ($row['entity'] ?? ''),
                    'keyword' => $keyword,
                    'status' => 'already_pending',
                );
                if (count($items) >= $limit) {
                    break;
                }
                continue;
            }

            $result = $processor->process(array(
                'entity_id' => $entity_id,
                'entity_type' => $entity_type,
                'keyword' => $keyword,
                'project_id' => $project_id,
            ));

            if (is_wp_error($result)) {
                $errors++;
                $items[] = array(
                    'entity' => (string) ($row['entity'] ?? ''),
                    'keyword' => $keyword,
                    'status' => 'error',
                );
            } else {
                $generated++;
                $items[] = array(
                    'entity' => (string) ($row['entity'] ?? ''),
                    'keyword' => $keyword,
                    'status' => 'generated',
                );
            }

            if (count($items) >= $limit) {
                break;
            }
        }

        return array(
            'processed' => count($items),
            'generated' => $generated,
            'existing' => $existing,
            'errors' => $errors,
            'items' => $items,
        );
    }

    private function aysa_adjustments_has_pending_seo_review($entity_id, $entity_type, $keyword) {
        global $wpdb;

        $entity_id = intval($entity_id);
        $entity_type = trim((string) $entity_type);
        $keyword = trim((string) $keyword);
        if ($entity_id <= 0 || $entity_type === '' || $keyword === '') {
            return false;
        }

        $table_name = $wpdb->prefix . 'aysa_automated_pending_reviews';
        $exists = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$table_name} WHERE entity_id = %d AND entity_type = %s AND keyword = %s AND status = 'pending'",
            $entity_id,
            $entity_type,
            $keyword
        ));

        return intval($exists) > 0;
    }

    private function aysa_adjustments_build_onpage_optimize_needed_message($result, $language = 'en') {
        $processed = intval($result['processed'] ?? 0);
        $generated = intval($result['generated'] ?? 0);
        $existing = intval($result['existing'] ?? 0);
        $errors = intval($result['errors'] ?? 0);
        $items = isset($result['items']) && is_array($result['items']) ? $result['items'] : array();
        $error_message = trim((string) ($result['error_message'] ?? ''));

        if ($error_message !== '') {
            return $this->aysa_adjustments_tr(
                $language,
                'I could not start the on-page optimization run right now. ' . $error_message,
                'Nu am putut porni acum runda de optimizare on-page. ' . $error_message,
                'Не успях да стартирам сега on-page оптимизацията. ' . $error_message
            );
        }

        $lines = array();
        foreach (array_slice($items, 0, 5) as $item) {
            if (!is_array($item)) {
                continue;
            }
            $entity = trim((string) ($item['entity'] ?? ''));
            $keyword = trim((string) ($item['keyword'] ?? ''));
            $status = trim((string) ($item['status'] ?? ''));
            if ($entity === '' || $keyword === '') {
                continue;
            }
            $suffix = match ($status) {
                'generated' => $this->aysa_adjustments_tr($language, 'added to pending review', 'trimisă în pending review', 'добавено в pending review'),
                'already_pending' => $this->aysa_adjustments_tr($language, 'already pending review', 'deja în pending review', 'вече е в pending review'),
                default => $this->aysa_adjustments_tr($language, 'could not be generated', 'nu a putut fi generată', 'не можа да бъде генерирана'),
            };
            $lines[] = '- ' . $entity . ' -> ' . $keyword . ' (' . $suffix . ')';
        }

        $summary = $this->aysa_adjustments_tr(
            $language,
            'I processed ' . $processed . ' on-page pages. I generated ' . $generated . ' new SEO reviews, ' . $existing . ' were already pending, and ' . $errors . ' failed.',
            'Am procesat ' . $processed . ' pagini on-page. Am generat ' . $generated . ' review-uri SEO noi, ' . $existing . ' erau deja în pending, iar ' . $errors . ' au eșuat.',
            'Обработих ' . $processed . ' on-page страници. Генерирах ' . $generated . ' нови SEO review-та, ' . $existing . ' вече бяха pending, а ' . $errors . ' се провалиха.'
        );

        if (!empty($lines)) {
            $summary .= "\n\n" . $this->aysa_adjustments_tr(
                $language,
                'Here are the first pages I handled:',
                'Iată primele pagini pe care le-am tratat:',
                'Ето първите страници, които обработих:'
            ) . "\n" . implode("\n", $lines);
        }

        return trim($summary);
    }

    private function aysa_adjustments_format_internal_linking_preview_changes($link_mappings) {
        $changes_by_content = array();
        foreach (is_array($link_mappings) ? $link_mappings : array() as $mapping) {
            if (!is_array($mapping)) {
                continue;
            }
            $content_id = intval($mapping['source_content_id'] ?? 0);
            $content_title = sanitize_text_field((string) ($mapping['source_content_title'] ?? ($content_id > 0 ? 'Content #' . $content_id : '')));
            if ($content_title === '') {
                continue;
            }
            if (!isset($changes_by_content[$content_title])) {
                $changes_by_content[$content_title] = array(
                    'content_title' => $content_title,
                    'content_type' => sanitize_text_field((string) ($mapping['source_content_type'] ?? 'post')),
                    'links_count' => 0,
                    'changes' => array(),
                );
            }
            $changes_by_content[$content_title]['links_count']++;
            $changes_by_content[$content_title]['changes'][] = array(
                'keyword' => sanitize_text_field((string) ($mapping['keyword'] ?? '')),
                'target_title' => sanitize_text_field((string) ($mapping['target_page'] ?? '')),
            );
        }
        return array_values($changes_by_content);
    }

    private function aysa_adjustments_build_technical_internal_linking_preview_message($result, $language = 'en') {
        if (empty($result['success'])) {
            return $this->aysa_adjustments_tr(
                $language,
                'I could not analyze internal linking opportunities right now. We can try again in a moment.',
                'Nu am putut analiza acum oportunitățile de linkuri interne. Putem încerca din nou imediat.',
                'Не успях да анализирам възможностите за вътрешни линкове точно сега. Можем да опитаме отново след малко.'
            );
        }

        return $this->aysa_adjustments_tr(
            $language,
            'I analyzed the current internal linking opportunities. I found ' . intval($result['total_keywords_found'] ?? 0) . ' relevant keywords across ' . intval($result['content_items_scanned'] ?? 0) . ' content items and I can safely add about ' . intval($result['links_to_be_added'] ?? 0) . ' internal links next.',
            'Am analizat oportunitățile actuale de linkuri interne. Am găsit ' . intval($result['total_keywords_found'] ?? 0) . ' keyword-uri relevante în ' . intval($result['content_items_scanned'] ?? 0) . ' elemente de conținut și pot adăuga în siguranță aproximativ ' . intval($result['links_to_be_added'] ?? 0) . ' linkuri interne în pasul următor.',
            'Анализирах текущите възможности за вътрешни линкове. Намерих ' . intval($result['total_keywords_found'] ?? 0) . ' релевантни keyword-а в ' . intval($result['content_items_scanned'] ?? 0) . ' елемента съдържание и мога безопасно да добавя около ' . intval($result['links_to_be_added'] ?? 0) . ' вътрешни линка в следващата стъпка.'
        );
    }

    private function aysa_adjustments_build_technical_internal_linking_process_message($result, $language = 'en') {
        if (empty($result['success'])) {
            return $this->aysa_adjustments_tr(
                $language,
                'I could not apply the internal links right now. We can try again in a moment.',
                'Nu am putut aplica acum linkurile interne. Putem încerca din nou imediat.',
                'Не успях да приложа вътрешните линкове точно сега. Можем да опитаме отново след малко.'
            );
        }

        return $this->aysa_adjustments_tr(
            $language,
            'I applied the internal linking pass. I added ' . intval($result['links_added'] ?? 0) . ' links across ' . intval($result['processed_items'] ?? 0) . ' content items, using ' . intval($result['total_keywords_found'] ?? 0) . ' matched keywords.',
            'Am aplicat runda de internal linking. Am adăugat ' . intval($result['links_added'] ?? 0) . ' linkuri în ' . intval($result['processed_items'] ?? 0) . ' elemente de conținut, folosind ' . intval($result['total_keywords_found'] ?? 0) . ' keyword-uri potrivite.',
            'Приложих internal linking pass-а. Добавих ' . intval($result['links_added'] ?? 0) . ' линка в ' . intval($result['processed_items'] ?? 0) . ' елемента съдържание, използвайки ' . intval($result['total_keywords_found'] ?? 0) . ' съвпаднали keyword-а.'
        );
    }

    private function aysa_adjustments_build_technical_pagespeed_completion_message($snapshot, $language = 'en') {
        $snapshot = is_array($snapshot) ? $snapshot : array();
        $overall = intval($snapshot['overall_score'] ?? 0);
        $desktop = intval($snapshot['desktop_score'] ?? 0);
        $mobile = intval($snapshot['mobile_score'] ?? 0);
        $rows = isset($snapshot['rows']) && is_array($snapshot['rows']) ? array_values($snapshot['rows']) : array();
        $priority = isset($snapshot['priority_actions']) && is_array($snapshot['priority_actions']) ? array_values($snapshot['priority_actions']) : array();
        $top_titles = array();
        foreach (array_slice($rows, 0, 3) as $row) {
            if (!is_array($row)) {
                continue;
            }
            $title = trim((string) ($row['title'] ?? ''));
            if ($title !== '') {
                $top_titles[] = $title;
            }
        }
        $preview = implode(', ', $top_titles);
        $priority_preview = '';
        foreach (array_slice($priority, 0, 2) as $row) {
            if (!is_array($row)) {
                continue;
            }
            $action = trim((string) ($row['action'] ?? ''));
            if ($action !== '') {
                $priority_preview .= ($priority_preview !== '' ? ', ' : '') . $action;
            }
        }

        if ($overall > 0 || !empty($rows)) {
            return match ($language) {
                'ro' => 'Am date PageSpeed salvate pentru site. În acest moment scorul general este ' . $overall . ', cu ' . $desktop . ' pe desktop și ' . $mobile . ' pe mobile. Cele mai vizibile oportunități sunt ' . ($preview !== '' ? $preview : 'câteva optimizări de performanță care merită verificate') . ($priority_preview !== '' ? '. Ca prioritate aș începe cu ' . $priority_preview : '') . '. Dacă vrei, îți arăt rezumatul complet sau oportunitățile raportate de Google.',
                'bg' => 'Имам запазени PageSpeed данни за сайта. В момента общият резултат е ' . $overall . ', с ' . $desktop . ' за desktop и ' . $mobile . ' за mobile. Най-видимите възможности са ' . ($preview !== '' ? $preview : 'няколко performance оптимизации, които си струва да се проверят') . ($priority_preview !== '' ? '. Като приоритет бих започнал с ' . $priority_preview : '') . '. Ако искаш, ще ти покажа пълното резюме или възможностите, отчетени от Google.',
                default => 'I already have saved PageSpeed data for the site. Right now the overall score is ' . $overall . ', with ' . $desktop . ' on desktop and ' . $mobile . ' on mobile. The most visible opportunities are ' . ($preview !== '' ? $preview : 'a few performance optimizations worth reviewing') . ($priority_preview !== '' ? '. As a priority I would start with ' . $priority_preview : '') . '. If you want, I can show you the full summary or the opportunities reported by Google.',
            };
        }

        return $this->aysa_adjustments_tr(
            $language,
            'I do not have a saved Google PageSpeed summary yet. I can refresh it now and then show you the desktop score, the mobile score, and the biggest performance opportunities.',
            'Încă nu am un rezumat Google PageSpeed salvat. Pot să-l reîmprospătez acum și apoi îți arăt scorul de desktop, scorul de mobile și cele mai mari oportunități de performanță.',
            'Все още нямам запазено Google PageSpeed резюме. Мога да го опресня сега и после да ти покажа desktop резултата, mobile резултата и най-големите performance възможности.'
        );
    }

    private function aysa_pagespeed_report_start_queued($broker_payload) {
        $project_id = intval($broker_payload['project_id'] ?? 0);
        if ($project_id <= 0) {
            return new WP_Error('invalid_pagespeed_project', 'Invalid project id.');
        }

        $result = $this->aysa_broker_request('POST', '/v1/technical/pagespeed/report/start', $broker_payload, 30);
        if (is_wp_error($result)) {
            return $result;
        }
        if (!is_array($result) || empty($result['ok'])) {
            return new WP_Error('pagespeed_report_start_failed', isset($result['error']) ? (string) $result['error'] : 'PageSpeed report generation failed.');
        }

        $job_id = sanitize_text_field((string) ($result['job_id'] ?? ''));
        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_technical_pagespeed_report_start',
            'technical_pagespeed_report_start:' . $job_id,
            array(
                'module' => 'technical_optimization',
                'site_url' => (string) ($broker_payload['site_url'] ?? ''),
                'site_host' => (string) ($broker_payload['site_host'] ?? ''),
                'job_id' => $job_id,
                'language' => (string) ($broker_payload['language'] ?? ''),
            ),
            36,
            '/v1/technical/pagespeed/report/start'
        );

        return array(
            'status' => sanitize_key((string) ($result['status'] ?? 'queued')),
            'worker_ref' => $this->aysa_project_jobs_make_worker_ref('pagespeed_report', 'pagespeed_report', $job_id),
            'worker_status' => sanitize_key((string) ($result['status'] ?? 'queued')),
            'result_summary' => array(
                'progress_label' => sanitize_text_field((string) ($result['progress_label'] ?? 'Queued')),
            ),
            'started_at' => time(),
        );
    }

    private function aysa_adjustments_build_keyword_research_completion_message($snapshot, $language = 'en') {
        if (!is_array($snapshot)) {
            return $this->aysa_adjustments_tr(
                $language,
                'I finished the first keyword research pass. You can open the keyword map to review the current result.',
                'Am terminat primul pas de keyword research. Poți deschide keyword map ca să vezi rezultatul curent.',
                'Приключих първия етап от keyword research. Можеш да отвориш keyword map, за да видиш текущия резултат.'
            );
        }

        $gsc_count = intval($snapshot['gsc_count'] ?? 0);
        $suggestions_count = intval($snapshot['suggestions_count'] ?? 0);
        $gsc_rows = isset($snapshot['gsc_rows']) && is_array($snapshot['gsc_rows']) ? $snapshot['gsc_rows'] : array();
        $mapped_pages = array();
        foreach ($gsc_rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $target_url = trim((string) ($row['target_url'] ?? ''));
            if ($target_url === '') {
                $target_url = trim((string) ($row['url'] ?? ''));
            }
            if ($target_url !== '') {
                $mapped_pages[$target_url] = true;
            }
        }
        $mapped_pages_count = count($mapped_pages);

        return match ($language) {
            'ro' => 'Am terminat baza pentru primul pas. Acum am ' . $gsc_count . ' keyword-uri care apar deja în Google, ' . $suggestions_count . ' sugestii noi și ' . $mapped_pages_count . ' pagini cu asocieri reale. Încep imediat să te ghidez pas cu pas. Dacă vrei să mergem mai departe, apasă `Continuă`.',
            'bg' => 'Приключих основата за първата стъпка. В момента имам ' . $gsc_count . ' keyword-а, които вече се появяват в Google, ' . $suggestions_count . ' нови предложения и ' . $mapped_pages_count . ' страници с реални свързвания. Следва да те преведа стъпка по стъпка. Ако искаш да продължим, натисни `Продължи`.',
            default => 'I finished the base for the first step. Right now I have ' . $gsc_count . ' keywords already appearing in Google, ' . $suggestions_count . ' new suggestions, and ' . $mapped_pages_count . ' pages with real matches. I can now walk you through it step by step. If you want to move on, press `Continue`.',
        };
    }

    private function aysa_adjustments_build_competitor_gap_completion_message($snapshot, $language = 'en') {
        $count = intval($snapshot['count'] ?? 0);
        if ($count <= 0) {
            return $this->aysa_adjustments_tr(
                $language,
                'I refreshed the competitor gap view, but I do not have saved gap keywords yet.',
                'Am reîmprospătat competitor gap, dar încă nu am keyword-uri salvate în gap.',
                'Опресних competitor gap изгледа, но все още нямам запазени gap keyword-и.'
            );
        }

        return match ($language) {
            'ro' => 'Am actualizat competitor gap și am identificat ' . $count . ' keyword-uri pe care competitorii le acoperă iar tu merită să le evaluezi. Din acest punct pot să te ajut să alegem ce este relevant pentru business și ce merită adăugat în keyword map.',
            'bg' => 'Актуализирах competitor gap и идентифицирах ' . $count . ' keyword-а, които конкурентите покриват, а при теб си струва да бъдат прегледани. Оттук мога да ти помогна да изберем кое е релевантно за бизнеса и какво си струва да влезе в keyword map-а.',
            default => 'I refreshed the competitor gap and identified ' . $count . ' keywords competitors already cover and you should review. From here I can help you decide what is relevant for the business and what is worth adding into the keyword map.',
        };
    }

    private function aysa_adjustments_build_competitor_identification_completion_message($snapshot, $language = 'en') {
        $rows = isset($snapshot['rows']) && is_array($snapshot['rows']) ? $snapshot['rows'] : array();
        $count = intval($snapshot['count'] ?? count($rows));
        if ($count <= 0) {
            return $this->aysa_adjustments_tr(
                $language,
                'I ran competitor discovery, but I still do not have saved competitors for the current seed keywords.',
                'Am rulat competitor discovery, dar încă nu am competitori salvați pentru seed keywords-urile curente.',
                'Стартирах competitor discovery, но все още нямам запазени конкуренти за текущите seed keywords.'
            );
        }

        return match ($language) {
            'ro' => 'Am actualizat competitor discovery și am identificat ' . $count . ' competitori relevanți pe baza temelor salvate în setup. De aici putem să comparăm keyword gap-ul și apoi să vedem unde au ei semnale mai puternice decât tine.',
            'bg' => 'Актуализирах competitor discovery и идентифицирах ' . $count . ' релевантни конкурента на база темите, запазени в setup. Оттук можем да сравним keyword gap-а и после да видим къде те имат по-силни сигнали от теб.',
            default => 'I refreshed competitor discovery and identified ' . $count . ' relevant competitors from the topics saved in setup. From here we can compare the keyword gap and then see where they have stronger signals than you.',
        };
    }

    private function aysa_adjustments_build_competitor_backlinks_completion_message($snapshot, $language = 'en') {
        $rows = isset($snapshot['rows']) && is_array($snapshot['rows']) ? $snapshot['rows'] : array();
        $count = intval($snapshot['count'] ?? count($rows));
        if ($count <= 0) {
            return $this->aysa_adjustments_tr(
                $language,
                'I refreshed the competitor backlink analysis, but I still do not have saved backlink rows to show yet.',
                'Am actualizat competitor backlink analysis, dar încă nu am rânduri salvate de backlink-uri pe care să ți le arăt.',
                'Опресних competitor backlink analysis, но все още нямам запазени backlink редове, които да ти покажа.'
            );
        }

        $stronger_count = 0;
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            if (floatval($row['authority'] ?? 0) > 0 || intval($row['backlinks'] ?? 0) > 0) {
                $stronger_count++;
            }
        }

        return match ($language) {
            'ro' => 'Am actualizat competitor backlink analysis și am găsit date salvate pentru ' . $count . ' domenii concurente. În lista de acum poți vedea rapid autoritatea și backlink-urile lor, ca să înțelegi unde au semnale mai puternice și unde merită să cauți oportunități noi.',
            'bg' => 'Актуализирах competitor backlink analysis и намерих запазени данни за ' . $count . ' конкурентни домейна. В текущия списък можеш бързо да видиш authority и backlinks, за да разбереш къде те имат по-силни сигнали и къде си струва да търсиш нови възможности.',
            default => 'I refreshed the competitor backlink analysis and found saved data for ' . $count . ' competitor domains. In the current list you can quickly review their authority and backlinks to understand where they have stronger signals and where new opportunities may be worth pursuing.',
        };
    }

    private function aysa_adjustments_build_offpage_backlink_audit_completion_message($snapshot, $language = 'en') {
        $count = intval($snapshot['count'] ?? 0);
        $domains = intval($snapshot['referring_domains'] ?? 0);
        $broken = intval($snapshot['broken_count'] ?? 0);
        $high = intval($snapshot['risk_high'] ?? 0);
        $medium = intval($snapshot['risk_medium'] ?? 0);
        $rows = isset($snapshot['rows']) && is_array($snapshot['rows']) ? $snapshot['rows'] : array();
        $examples = array();
        foreach (array_slice($rows, 0, 3) as $row) {
            if (!is_array($row)) {
                continue;
            }
            $domain = trim((string) ($row['referring_domain'] ?? ''));
            $issue = trim((string) ($row['issue_type'] ?? $row['health'] ?? ''));
            if ($domain === '') {
                continue;
            }
            $examples[] = $domain . ($issue !== '' ? ' (' . $issue . ')' : '');
        }
        $exampleText = implode(', ', $examples);

        return match ($language) {
            'ro' => 'Am terminat auditul backlinkurilor. În acest moment văd ' . $count . ' backlinkuri analizate din ' . $domains . ' domenii, cu ' . $broken . ' linkuri rupte, ' . $high . ' riscuri mari și ' . $medium . ' riscuri medii.' . ($exampleText !== '' ? ' Exemple utile: ' . $exampleText . '.' : ''),
            'bg' => 'Приключих backlink audit-а. В момента виждам ' . $count . ' анализирани backlinks от ' . $domains . ' домейна, с ' . $broken . ' счупени линка, ' . $high . ' high risk и ' . $medium . ' medium risk.' . ($exampleText !== '' ? ' Полезни примери: ' . $exampleText . '.' : ''),
            default => 'I finished the backlink audit. Right now I see ' . $count . ' analyzed backlinks from ' . $domains . ' referring domains, with ' . $broken . ' broken links, ' . $high . ' high-risk items, and ' . $medium . ' medium-risk items.' . ($exampleText !== '' ? ' Useful examples: ' . $exampleText . '.' : ''),
        };
    }

    private function aysa_adjustments_build_missing_pages_completion_message($snapshot, $language = 'en') {
        $count = intval($snapshot['count'] ?? 0);
        if ($count <= 0) {
            return $this->aysa_adjustments_tr(
                $language,
                'I refreshed the missing pages view and I do not currently see saved page gaps.',
                'Am reîmprospătat missing pages și momentan nu văd goluri de pagini salvate.',
                'Опресних missing pages изгледа и в момента не виждам запазени липси на страници.'
            );
        }

        return match ($language) {
            'ro' => 'Am reîmprospătat missing pages și am identificat ' . $count . ' keyword-uri sau planuri care încă nu au o pagină potrivită. Din lista asta putem decide ce pagini noi merită create și ce poate fi acoperit prin paginile existente.',
            'bg' => 'Опресних missing pages и идентифицирах ' . $count . ' keyword-а или плана, които все още нямат подходяща страница. От този списък можем да решим кои нови страници си струва да бъдат създадени и какво може да се покрие от съществуващите.',
            default => 'I refreshed missing pages and identified ' . $count . ' keywords or planned items that still do not have a suitable page. From this list we can decide what new pages are worth creating and what can still be covered by existing pages.',
        };
    }

    private function aysa_adjustments_build_content_strategy_completion_message($snapshot, $language = 'en') {
        $total = intval($snapshot['total'] ?? 0);
        $generated = intval($snapshot['generated'] ?? 0);
        $published = intval($snapshot['published'] ?? 0);
        $pending = intval($snapshot['pending'] ?? 0);

        if ($total <= 0) {
            return $this->aysa_adjustments_tr(
                $language,
                'I refreshed the content strategy view, but I do not have saved strategy rows yet.',
                'Am reîmprospătat content strategy, dar încă nu am rânduri salvate în strategie.',
                'Опресних content strategy изгледа, но все още нямам запазени редове в стратегията.'
            );
        }

        return match ($language) {
            'ro' => 'Am reîmprospătat content strategy și acum am ' . $total . ' idei sau piese de plan salvate. Dintre ele, ' . $generated . ' sunt deja generate, ' . $published . ' sunt deja legate de pagini, iar ' . $pending . ' sunt încă în așteptare. Putem folosi asta ca să decidem ce conținut merită făcut mai departe.',
            'bg' => 'Опресних content strategy и сега имам ' . $total . ' запазени идеи или стратегически елементи. От тях ' . $generated . ' вече са генерирани, ' . $published . ' вече са свързани със страници, а ' . $pending . ' все още са в изчакване. Можем да използваме това, за да решим какво съдържание си струва да се прави нататък.',
            default => 'I refreshed the content strategy and I now have ' . $total . ' saved ideas or strategy items. Of these, ' . $generated . ' are already generated, ' . $published . ' are already tied to pages, and ' . $pending . ' are still pending. We can use this to decide what content is worth producing next.',
        };
    }

    private function aysa_adjustments_tr($language, $en, $ro, $bg) {
        switch ($language) {
            case 'ro':
                return $ro;
            case 'bg':
                return $bg;
            default:
                return $en;
        }
    }

    private function aysa_reset_local_project_runtime($project_id) {
        $project_id = intval($project_id);
        $breakdown = array(
            'keyword_metrics_deleted' => 0,
            'monitoring_rank_history_deleted' => 0,
            'keyword_v2_snapshots_deleted' => 0,
            'content_strategies_deleted' => 0,
            'missing_page_meta_deleted' => 0,
            'pending_reviews_deleted' => 0,
            'options_deleted' => 0,
            'transients_deleted' => 0,
            'scheduled_events_cleared' => 0,
        );

        if ($project_id <= 0) {
            return $breakdown;
        }

        $database_breakdown = $this->aysa_reset_local_project_database_state($project_id);
        foreach ($database_breakdown as $key => $value) {
            if (array_key_exists($key, $breakdown)) {
                $breakdown[$key] = intval($value);
            }
        }

        $option_keys = array(
            $this->aysa_keyword_discovery_status_key($project_id),
            $this->aysa_keyword_discovery_snapshot_key($project_id),
            $this->get_keyword_discovery_local_associations_option_key($project_id),
            $this->aysa_monitoring_refresh_status_key($project_id),
            $this->aysa_monitoring_refresh_queue_key($project_id),
            $this->aysa_keyword_tracking_plan_option_key($project_id),
            $this->aysa_adjustments_get_recent_relevant_keywords_option_key($project_id),
            $this->aysa_adjustments_pending_approvals_option_key($project_id),
            $this->aysa_adjustments_generation_queue_key($project_id),
            $this->aysa_competitor_gap_cache_key($project_id),
            $this->aysa_competitor_identification_cache_key($project_id),
            'aysa_backlink_summary_' . $project_id,
            $this->aysa_get_backlink_audit_key($project_id),
            $this->aysa_get_backlink_audit_sync_key($project_id),
            $this->aysa_get_adverlink_link_issues_key($project_id),
            $this->aysa_get_adverlink_link_issues_sync_key($project_id),
            $this->aysa_get_adverlink_orders_key($project_id),
            $this->aysa_technical_site_audit_state_key($project_id),
            $this->aysa_technical_pagespeed_state_key($project_id),
            $this->aysa_technical_sitemap_robots_state_key($project_id),
            $this->aysa_technical_internal_linking_preview_state_key($project_id),
            'aysa_content_strategy_hard_reset_' . $project_id,
        );

        foreach ($option_keys as $key) {
            if ($key && delete_option($key)) {
                $breakdown['options_deleted']++;
            }
        }

        $transient_keys = array(
            $this->get_suggested_keywords_cache_key($project_id),
            $this->aysa_keyword_discovery_lock_key($project_id),
            $this->aysa_monitoring_refresh_lock_key($project_id),
        );

        foreach ($transient_keys as $key) {
            if ($key && delete_transient($key)) {
                $breakdown['transients_deleted']++;
            }
        }

        global $wpdb;
        if (isset($wpdb) && is_object($wpdb)) {
            $like_patterns = array(
                'aysa_keyword_discovery_%_' . $project_id,
                'aysa_monitoring_%_' . $project_id,
                'aysa_adjustments_%_' . $project_id,
                'aysa_competitor_%_' . $project_id,
                'aysa_backlink_%_' . $project_id,
                'aysa_technical_%_' . $project_id,
                'aysa_adverlink_%_' . $project_id,
            );
            foreach ($like_patterns as $pattern) {
                $deleted_rows = $wpdb->query(
                    $wpdb->prepare(
                        "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
                        $pattern
                    )
                );
                if ($deleted_rows !== false) {
                    $breakdown['options_deleted'] += intval($deleted_rows);
                }
            }
        }

        while (false !== ($ts = wp_next_scheduled('aysa_keyword_discovery_refresh_background_event', array($project_id)))) {
            wp_unschedule_event($ts, 'aysa_keyword_discovery_refresh_background_event', array($project_id));
            $breakdown['scheduled_events_cleared']++;
        }
        while (false !== ($ts = wp_next_scheduled('aysa_monitoring_rank_refresh_background_event', array($project_id)))) {
            wp_unschedule_event($ts, 'aysa_monitoring_rank_refresh_background_event', array($project_id));
            $breakdown['scheduled_events_cleared']++;
        }
        while (false !== ($ts = wp_next_scheduled('aysa_monitoring_rank_refresh_cron_tick', array($project_id)))) {
            wp_unschedule_event($ts, 'aysa_monitoring_rank_refresh_cron_tick', array($project_id));
            $breakdown['scheduled_events_cleared']++;
        }

        return $breakdown;
    }

    private function aysa_reset_local_project_database_state($project_id) {
        global $wpdb;

        $project_id = intval($project_id);
        $breakdown = array(
            'keyword_metrics_deleted' => 0,
            'monitoring_rank_history_deleted' => 0,
            'keyword_v2_snapshots_deleted' => 0,
            'content_strategies_deleted' => 0,
            'missing_page_meta_deleted' => 0,
            'pending_reviews_deleted' => 0,
        );

        if ($project_id <= 0 || !isset($wpdb) || !is_object($wpdb)) {
            return $breakdown;
        }

        $table_map = array(
            'keyword_metrics_deleted' => $this->aysa_keyword_discovery_table(),
            'monitoring_rank_history_deleted' => $this->aysa_monitoring_history_table(),
            'keyword_v2_snapshots_deleted' => $this->aysa_keyword_v2_snapshot_table_name(),
            'content_strategies_deleted' => $wpdb->prefix . 'aysa_content_strategies',
            'missing_page_meta_deleted' => $this->aysa_missing_page_meta_table(),
            'pending_reviews_deleted' => $wpdb->prefix . 'aysa_automated_pending_reviews',
        );

        foreach ($table_map as $key => $table_name) {
            $table_name = (string) $table_name;
            if ($table_name === '') {
                continue;
            }

            $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table_name));
            if ($exists !== $table_name) {
                continue;
            }

            $deleted_rows = $wpdb->query(
                $wpdb->prepare(
                    "DELETE FROM {$table_name} WHERE project_id = %d",
                    $project_id
                )
            );
            if ($deleted_rows !== false) {
                $breakdown[$key] = intval($deleted_rows);
            }
        }

        return $breakdown;
    }

    private function aysa_reset_local_setup_state($user_id, $project_id = 0) {
        $user_id = intval($user_id);
        $project_id = intval($project_id);
        $steps = array('integrations', 'general', 'objectives', 'seo-settings', 'competition', 'tone-of-voice');
        $deleted = 0;

        foreach ($steps as $step) {
            if ($user_id > 0 && delete_option('aysa_wizard_' . $step . '_' . $user_id)) {
                $deleted++;
            }
            if ($project_id > 0 && delete_option('aysa_wizard_' . $step . '_project_' . $project_id)) {
                $deleted++;
            }
        }

        return $deleted;
    }

    public function handle_agent8_factory_reset_dry_run() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }

        $target = $this->aysa_agent8_factory_reset_detect_target();
        $guard = $this->aysa_agent8_factory_reset_validate_target($target);
        if (empty($guard['ok'])) {
            wp_send_json_error(array(
                'message' => 'Factory Reset dry-run aborted: ' . (string) ($guard['reason'] ?? 'unsafe context'),
                'target' => $target,
                'guard' => $guard,
            ));
            return;
        }
        if (empty($target['project_id']) || empty($target['site_host'])) {
            wp_send_json_error(array(
                'message' => 'Factory Reset dry-run target is incomplete.',
                'target' => $target,
            ));
            return;
        }

        $api = $this->aysa_broker_request('POST', '/v1/conversation/apprentice/factory-reset-dry-run', array(
            'project_id' => intval($target['project_id']),
            'site_host' => (string) $target['site_host'],
            'wp_user_id' => intval($target['wp_user_id']),
            'aysa_user_id' => intval($target['aysa_user_id']),
            'channels' => array('agent8', 'setup_seo_profile', 'adjustments_and_improvements'),
        ), 45);

        $api_result = is_wp_error($api)
            ? array('ok' => false, 'message' => $api->get_error_message())
            : $api;

        $wordpress = $this->aysa_agent8_factory_reset_wordpress_dry_run($target);
        $snapshot_payload = array(
            'ok' => true,
            'mode' => 'dry_run',
            'target' => $target,
            'current_context' => $target,
            'guard' => $guard,
            'confirmation_phrase' => 'RESET AYSA TEST DATA ' . intval($target['project_id']) . ' ' . (string) $target['site_host'],
            'wordpress' => $wordpress,
            'api' => $api_result,
            'preserve' => $this->aysa_agent8_factory_reset_preserve_dry_run(),
            'safety' => array(
                'dry_run_only' => true,
                'deletes' => 0,
                'updates' => 0,
                'reset_executed' => false,
            ),
        );
        if (!empty($api_result['ok'])) {
            $snapshot_payload = $this->aysa_agent8_factory_reset_attach_snapshot($snapshot_payload);
        }

        wp_send_json_success($snapshot_payload);
    }

    public function handle_agent8_factory_reset_execute() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }

        $token = isset($_POST['token']) ? sanitize_text_field(wp_unslash((string) $_POST['token'])) : '';
        $phrase = isset($_POST['confirmation_phrase']) ? trim(sanitize_text_field(wp_unslash((string) $_POST['confirmation_phrase']))) : '';
        $snapshot = $this->aysa_agent8_factory_reset_get_snapshot($token);
        if (!is_array($snapshot)) {
            wp_send_json_error(array('message' => 'Factory Reset execute aborted: dry-run snapshot is missing or expired.'));
            return;
        }
        if (time() > intval($snapshot['expires_at'] ?? 0)) {
            $this->aysa_agent8_factory_reset_delete_snapshot($token);
            wp_send_json_error(array('message' => 'Factory Reset execute aborted: dry-run snapshot expired.'));
            return;
        }
        if ($phrase === '' || $phrase !== (string) ($snapshot['confirmation_phrase'] ?? '')) {
            wp_send_json_error(array('message' => 'Factory Reset execute aborted: confirmation phrase does not match.'));
            return;
        }

        $target = $this->aysa_agent8_factory_reset_detect_target();
        $guard = $this->aysa_agent8_factory_reset_validate_target($target);
        if (empty($guard['ok'])) {
            wp_send_json_error(array(
                'message' => 'Factory Reset execute aborted: ' . (string) ($guard['reason'] ?? 'unsafe context'),
                'target' => $target,
                'guard' => $guard,
            ));
            return;
        }
        if (!$this->aysa_agent8_factory_reset_same_context($snapshot['target'] ?? array(), $target)) {
            wp_send_json_error(array('message' => 'Factory Reset execute aborted: current context changed since dry-run.'));
            return;
        }

        $api = $this->aysa_broker_request('POST', '/v1/conversation/apprentice/factory-reset-dry-run', array(
            'project_id' => intval($target['project_id']),
            'site_host' => (string) $target['site_host'],
            'wp_user_id' => intval($target['wp_user_id']),
            'aysa_user_id' => intval($target['aysa_user_id']),
            'channels' => array('agent8', 'setup_seo_profile', 'adjustments_and_improvements'),
        ), 45);
        if (is_wp_error($api) || empty($api['ok'])) {
            wp_send_json_error(array('message' => 'Factory Reset execute aborted: API dry-run could not be refreshed.'));
            return;
        }

        $current_counts = $this->aysa_agent8_factory_reset_snapshot_counts($this->aysa_agent8_factory_reset_wordpress_dry_run($target), $api);
        if ($current_counts !== ($snapshot['counts'] ?? array())) {
            wp_send_json_error(array(
                'message' => 'Factory Reset execute aborted: dry-run counts changed. Run dry-run again.',
                'expected_counts' => $snapshot['counts'] ?? array(),
                'current_counts' => $current_counts,
            ));
            return;
        }

        $api_execute = $this->aysa_broker_request('POST', '/v1/conversation/apprentice/factory-reset-execute', array(
            'project_id' => intval($target['project_id']),
            'site_host' => (string) $target['site_host'],
            'wp_user_id' => intval($target['wp_user_id']),
            'aysa_user_id' => intval($target['aysa_user_id']),
            'channels' => array('agent8', 'setup_seo_profile', 'adjustments_and_improvements'),
            'expected_counts' => $snapshot['counts']['api'] ?? array(),
        ), 60);
        if (is_wp_error($api_execute) || empty($api_execute['ok'])) {
            wp_send_json_error(array(
                'message' => is_wp_error($api_execute) ? $api_execute->get_error_message() : (string) ($api_execute['error'] ?? 'Factory Reset API execute failed.'),
                'api' => is_wp_error($api_execute) ? array() : $api_execute,
            ));
            return;
        }

        $wp_execute = $this->aysa_agent8_factory_reset_wordpress_execute($target);
        $this->aysa_agent8_factory_reset_delete_snapshot($token);

        wp_send_json_success(array(
            'ok' => true,
            'mode' => 'execute',
            'target' => $target,
            'api' => $api_execute,
            'wordpress' => $wp_execute,
            'reload' => true,
        ));
    }

    private function aysa_agent8_factory_reset_detect_target() {
        $site_host = wp_parse_url(home_url('/'), PHP_URL_HOST);
        $site_host = is_string($site_host) ? strtolower(trim($site_host)) : '';
        $request_site_host = isset($_POST['site_host']) ? strtolower(trim(sanitize_text_field(wp_unslash((string) $_POST['site_host'])))) : '';
        $auth_domain = strtolower(trim((string) get_option('aysa_auth_domain', '')));
        $wp_user_id = intval(get_current_user_id());
        $auth_manager = class_exists('Aysa_Auth_Manager') ? Aysa_Auth_Manager::get_instance() : null;
        $auth_status = is_object($auth_manager) && method_exists($auth_manager, 'is_authenticated')
            ? $auth_manager->is_authenticated()
            : array();
        $aysa_user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : 0;
        if ($aysa_user_id <= 0) {
            $aysa_user_id = intval(get_option('aysa_user_id', 0));
        }
        if ($aysa_user_id <= 0) {
            $aysa_user_id = intval($this->get_user_id());
        }

        $project_id = intval(get_option('aysa_project_id', 0));
        $request_project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $website_id = 0;
        $validation = $this->aysa_validate_test_access_key($this->aysa_get_effective_access_key(''), $site_host);
        if (!empty($validation['valid'])) {
            if ($project_id <= 0) {
                $project_id = intval($validation['project_id'] ?? 0);
            }
            $website_id = intval($validation['website_id'] ?? 0);
        }

        return array(
            'project_id' => $project_id,
            'website_id' => $website_id,
            'site_host' => $site_host,
            'wp_user_id' => $wp_user_id,
            'aysa_user_id' => $aysa_user_id,
            'home_url' => home_url('/'),
            'request_project_id' => $request_project_id,
            'request_site_host' => $request_site_host,
            'auth_domain' => $auth_domain,
            'auth_status' => array(
                'authenticated' => !empty($auth_status['authenticated']),
                'source' => isset($auth_status['source']) ? sanitize_key((string) $auth_status['source']) : '',
            ),
            'access_key_validation' => array(
                'valid' => !empty($validation['valid']),
                'project_id' => intval($validation['project_id'] ?? 0),
                'website_id' => intval($validation['website_id'] ?? 0),
                'code' => isset($validation['code']) ? sanitize_key((string) $validation['code']) : '',
            ),
        );
    }

    private function aysa_agent8_factory_reset_validate_target($target) {
        $project_id = intval($target['project_id'] ?? 0);
        $site_host = strtolower(trim((string) ($target['site_host'] ?? '')));
        $request_project_id = intval($target['request_project_id'] ?? 0);
        $request_site_host = strtolower(trim((string) ($target['request_site_host'] ?? '')));
        $auth_domain = strtolower(trim((string) ($target['auth_domain'] ?? '')));
        $validation_project_id = intval($target['access_key_validation']['project_id'] ?? 0);

        if ($project_id <= 0) {
            return array('ok' => false, 'reason' => 'missing project_id');
        }
        if ($site_host === '') {
            return array('ok' => false, 'reason' => 'missing site_host');
        }
        if ($request_project_id > 0 && $request_project_id !== $project_id) {
            return array('ok' => false, 'reason' => 'request project_id does not match detected project_id');
        }
        if ($validation_project_id > 0 && $validation_project_id !== $project_id) {
            return array('ok' => false, 'reason' => 'access key project_id does not match detected project_id');
        }
        if ($request_site_host !== '' && $request_site_host !== $site_host) {
            return array('ok' => false, 'reason' => 'request site_host does not match detected site_host');
        }
        if ($auth_domain !== '' && $auth_domain !== $site_host) {
            return array('ok' => false, 'reason' => 'auth domain does not match detected site_host');
        }

        return array(
            'ok' => true,
            'scope' => array(
                'project_ids' => array($project_id, 0),
                'site_host' => $site_host,
                'project_zero_requires_site_host_match' => true,
                'confirmation_phrase' => 'RESET AYSA TEST DATA ' . $project_id . ' ' . $site_host,
            ),
        );
    }

    private function aysa_agent8_factory_reset_attach_snapshot($payload) {
        $ttl = 10 * MINUTE_IN_SECONDS;
        $token = wp_generate_password(40, false, false);
        $expires_at = time() + $ttl;
        $snapshot = array(
            'target' => $payload['target'] ?? array(),
            'counts' => $this->aysa_agent8_factory_reset_snapshot_counts($payload['wordpress'] ?? array(), $payload['api'] ?? array()),
            'confirmation_phrase' => (string) ($payload['confirmation_phrase'] ?? ''),
            'created_at' => time(),
            'expires_at' => $expires_at,
        );
        set_transient($this->aysa_agent8_factory_reset_snapshot_key($token), $snapshot, $ttl);
        $payload['execute'] = array(
            'token' => $token,
            'expires_at' => $expires_at,
            'expires_in_minutes' => 10,
        );
        return $payload;
    }

    private function aysa_agent8_factory_reset_snapshot_key($token) {
        return 'aysa_factory_reset_snapshot_' . hash('sha256', (string) $token);
    }

    private function aysa_agent8_factory_reset_get_snapshot($token) {
        if ((string) $token === '') {
            return null;
        }
        $snapshot = get_transient($this->aysa_agent8_factory_reset_snapshot_key($token));
        return is_array($snapshot) ? $snapshot : null;
    }

    private function aysa_agent8_factory_reset_delete_snapshot($token) {
        if ((string) $token !== '') {
            delete_transient($this->aysa_agent8_factory_reset_snapshot_key($token));
        }
    }

    private function aysa_agent8_factory_reset_same_context($expected, $current) {
        foreach (array('project_id', 'website_id', 'site_host', 'wp_user_id', 'aysa_user_id') as $key) {
            $left = isset($expected[$key]) ? (string) $expected[$key] : '';
            $right = isset($current[$key]) ? (string) $current[$key] : '';
            if ($left !== $right) {
                return false;
            }
        }
        return true;
    }

    private function aysa_agent8_factory_reset_snapshot_counts($wordpress, $api) {
        $conversation = is_array($api) && isset($api['conversation']) && is_array($api['conversation']) ? $api['conversation'] : array();
        $research = is_array($api) && isset($api['research']) && is_array($api['research']) ? $api['research'] : array();
        $wp_tables = is_array($wordpress) && isset($wordpress['custom_tables']) && is_array($wordpress['custom_tables']) ? $wordpress['custom_tables'] : array();
        $wp_custom_rows = 0;
        foreach ($wp_tables as $row) {
            $wp_custom_rows += intval($row['count'] ?? 0);
        }

        return array(
            'wordpress' => array(
                'options' => intval($wordpress['options']['count'] ?? 0),
                'user_meta' => intval($wordpress['user_meta']['count'] ?? 0),
                'custom_table_rows' => $wp_custom_rows,
            ),
            'api' => array(
                'sessions' => intval($conversation['project_conversation_sessions']['count'] ?? 0),
                'messages' => intval($conversation['project_conversation_messages']['count'] ?? 0),
                'action_history' => intval($conversation['project_action_history']['count'] ?? 0),
                'notifications' => intval($conversation['project_notifications']['count'] ?? 0),
                'knowledge_docs' => intval($conversation['project_knowledge_docs']['count'] ?? 0),
                'rag_requests' => intval($conversation['project_rag_requests']['count'] ?? 0),
                'research_runs' => intval($research['runs']['count'] ?? 0),
                'research_tasks' => intval($research['tasks']['count'] ?? 0),
                'research_events' => intval($research['events']['count'] ?? 0),
                'worker_project_files' => intval($conversation['worker_project_storage']['file_count'] ?? 0),
                'worker_task_artifacts' => intval($research['worker_artifacts']['count'] ?? 0),
            ),
        );
    }

    private function aysa_agent8_factory_reset_wordpress_dry_run($target) {
        global $wpdb;

        $project_id = intval($target['project_id'] ?? 0);
        $wp_user_id = intval($target['wp_user_id'] ?? 0);
        $aysa_user_id = intval($target['aysa_user_id'] ?? 0);
        $site_host = (string) ($target['site_host'] ?? '');
        $steps = array('integrations', 'general', 'objectives', 'seo-settings', 'competition', 'tone-of-voice');

        $option_names = array(
            $this->aysa_test_access_key_option_name(),
            'aysa_active_access_key',
            'aysa_user_secret_key',
            'aysa_user_id',
            'aysa_auth_domain',
            'aysa_project_id',
            'aysa_website_id',
            'aysa_setup_completed',
        );
        if ($project_id > 0) {
            $option_names[] = 'aysa_onboarding_run_token_project_' . $project_id;
            foreach ($steps as $step) {
                $option_names[] = 'aysa_wizard_' . $step . '_project_' . $project_id;
            }
            foreach (array('research', 'technical', 'onpage', 'offpage', 'monitoring') as $domain) {
                $option_names[] = $this->aysa_agent8_canonical_state_option_key($domain, $project_id);
            }
            $option_names = array_merge($option_names, $this->aysa_agent8_factory_reset_runtime_option_names($project_id));
        }
        if ($aysa_user_id > 0) {
            foreach ($steps as $step) {
                $option_names[] = 'aysa_wizard_' . $step . '_' . $aysa_user_id;
            }
        }
        $option_names = array_values(array_unique(array_filter(array_map('strval', $option_names))));

        $options = array();
        foreach ($option_names as $name) {
            $exists = false;
            $bytes = 0;
            if (isset($wpdb) && is_object($wpdb)) {
                $raw = $wpdb->get_var($wpdb->prepare("SELECT option_value FROM {$wpdb->options} WHERE option_name = %s LIMIT 1", $name));
                $exists = $raw !== null;
                $bytes = is_string($raw) ? strlen($raw) : 0;
            } else {
                $value = get_option($name, null);
                $exists = $value !== null;
                $bytes = strlen((string) maybe_serialize($value));
            }
            $options[] = array(
                'option_name' => $name,
                'exists' => $exists,
                'bytes' => $bytes,
            );
        }

        $like_patterns = array();
        if ($project_id > 0) {
            $like_patterns = array(
                'aysa_keyword_discovery_%_' . $project_id,
                'aysa_monitoring_%_' . $project_id,
                'aysa_adjustments_%_' . $project_id,
                'aysa_competitor_%_' . $project_id,
                'aysa_backlink_%_' . $project_id,
                'aysa_technical_%_' . $project_id,
                'aysa_adverlink_%_' . $project_id,
            );
        }

        return array(
            'options' => array(
                'count' => count(array_filter($options, static function ($row) { return !empty($row['exists']); })),
                'items' => $options,
                'like_patterns' => $this->aysa_agent8_factory_reset_option_like_dry_run($like_patterns),
            ),
            'user_meta' => $this->aysa_agent8_factory_reset_user_meta_dry_run($wp_user_id, $site_host),
            'custom_tables' => $this->aysa_agent8_factory_reset_wp_table_dry_run($project_id, $wp_user_id, $site_host),
        );
    }

    private function aysa_agent8_factory_reset_runtime_option_names($project_id) {
        return array(
            $this->aysa_keyword_discovery_status_key($project_id),
            $this->aysa_keyword_discovery_snapshot_key($project_id),
            $this->get_keyword_discovery_local_associations_option_key($project_id),
            $this->aysa_monitoring_refresh_status_key($project_id),
            $this->aysa_monitoring_refresh_queue_key($project_id),
            $this->aysa_keyword_tracking_plan_option_key($project_id),
            $this->aysa_adjustments_get_recent_relevant_keywords_option_key($project_id),
            $this->aysa_adjustments_pending_approvals_option_key($project_id),
            $this->aysa_adjustments_generation_queue_key($project_id),
            $this->aysa_competitor_gap_cache_key($project_id),
            $this->aysa_competitor_identification_cache_key($project_id),
            'aysa_backlink_summary_' . $project_id,
            $this->aysa_get_backlink_audit_key($project_id),
            $this->aysa_get_backlink_audit_sync_key($project_id),
            $this->aysa_get_adverlink_link_issues_key($project_id),
            $this->aysa_get_adverlink_link_issues_sync_key($project_id),
            $this->aysa_get_adverlink_orders_key($project_id),
            $this->aysa_technical_site_audit_state_key($project_id),
            $this->aysa_technical_pagespeed_state_key($project_id),
            $this->aysa_technical_sitemap_robots_state_key($project_id),
            $this->aysa_technical_internal_linking_preview_state_key($project_id),
            'aysa_content_strategy_hard_reset_' . $project_id,
        );
    }

    private function aysa_agent8_factory_reset_option_like_dry_run($patterns) {
        global $wpdb;
        $rows = array();
        if (!isset($wpdb) || !is_object($wpdb)) {
            return $rows;
        }
        foreach ($patterns as $pattern) {
            $count = intval($wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$wpdb->options} WHERE option_name LIKE %s", $pattern)));
            $samples = $wpdb->get_col($wpdb->prepare("SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s ORDER BY option_name ASC LIMIT 10", $pattern));
            $rows[] = array(
                'pattern' => $pattern,
                'count' => $count,
                'sample_option_names' => is_array($samples) ? array_values($samples) : array(),
            );
        }
        return $rows;
    }

    private function aysa_agent8_factory_reset_user_meta_dry_run($wp_user_id, $site_host) {
        global $wpdb;
        $meta_key = $this->aysa_agent8_storage_key($wp_user_id, $site_host);
        if ($wp_user_id <= 0 || !isset($wpdb) || !is_object($wpdb)) {
            return array('count' => 0, 'items' => array());
        }
        $raw = $wpdb->get_var($wpdb->prepare("SELECT meta_value FROM {$wpdb->usermeta} WHERE user_id = %d AND meta_key = %s LIMIT 1", $wp_user_id, $meta_key));
        return array(
            'count' => $raw === null ? 0 : 1,
            'items' => array(array(
                'user_id' => $wp_user_id,
                'meta_key' => $meta_key,
                'exists' => $raw !== null,
                'bytes' => is_string($raw) ? strlen($raw) : 0,
            )),
        );
    }

    private function aysa_agent8_factory_reset_wp_table_dry_run($project_id, $wp_user_id, $site_host) {
        global $wpdb;
        $rows = array();
        if (!isset($wpdb) || !is_object($wpdb)) {
            return $rows;
        }

        $project_tables = array(
            'keyword_metrics' => $this->aysa_keyword_discovery_table(),
            'monitoring_rank_history' => $this->aysa_monitoring_history_table(),
            'keyword_v2_snapshots' => $this->aysa_keyword_v2_snapshot_table_name(),
            'content_strategies' => $wpdb->prefix . 'aysa_content_strategies',
            'missing_page_meta' => $this->aysa_missing_page_meta_table(),
            'pending_reviews' => $wpdb->prefix . 'aysa_automated_pending_reviews',
        );
        foreach ($project_tables as $label => $table) {
            $rows[] = $this->aysa_agent8_factory_reset_count_table_where($table, $label, 'project_id = %d', array($project_id));
        }

        $auth_table = $wpdb->prefix . Aysa_Auth_Manager::TABLE_NAME;
        $rows[] = $this->aysa_agent8_factory_reset_count_table_where(
            $auth_table,
            'aysa_user_auth',
            'domain = %s',
            array($site_host)
        );

        return $rows;
    }

    private function aysa_agent8_factory_reset_count_table_where($table, $label, $where_sql, $where_args) {
        global $wpdb;
        $table = (string) $table;
        if ($table === '' || !isset($wpdb) || !is_object($wpdb)) {
            return array('label' => $label, 'table' => $table, 'exists' => false, 'count' => 0, 'sample_ids' => array());
        }
        $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table)) === $table;
        if (!$exists) {
            return array('label' => $label, 'table' => $table, 'exists' => false, 'count' => 0, 'sample_ids' => array());
        }
        $count_sql = "SELECT COUNT(*) FROM {$table} WHERE {$where_sql}";
        $sample_sql = "SELECT id FROM {$table} WHERE {$where_sql} ORDER BY id DESC LIMIT 10";
        return array(
            'label' => $label,
            'table' => $table,
            'exists' => true,
            'count' => intval($wpdb->get_var($wpdb->prepare($count_sql, $where_args))),
            'sample_ids' => array_map('intval', (array) $wpdb->get_col($wpdb->prepare($sample_sql, $where_args))),
        );
    }

    private function aysa_agent8_factory_reset_wordpress_execute($target) {
        $dry_run = $this->aysa_agent8_factory_reset_wordpress_dry_run($target);
        $deleted_options = 0;
        $deleted_like_options = 0;
        $deleted_user_meta = 0;
        $deleted_custom_rows = 0;

        foreach ((array) ($dry_run['options']['items'] ?? array()) as $row) {
            $name = isset($row['option_name']) ? (string) $row['option_name'] : '';
            if ($name !== '' && !empty($row['exists']) && delete_option($name)) {
                $deleted_options++;
            }
        }
        foreach ((array) ($dry_run['options']['like_patterns'] ?? array()) as $row) {
            $pattern = isset($row['pattern']) ? (string) $row['pattern'] : '';
            if ($pattern !== '') {
                $deleted_like_options += $this->aysa_agent8_factory_reset_delete_options_like($pattern);
            }
        }

        $wp_user_id = intval($target['wp_user_id'] ?? 0);
        $site_host = (string) ($target['site_host'] ?? '');
        if ($wp_user_id > 0 && $site_host !== '') {
            $deleted_user_meta += delete_user_meta($wp_user_id, $this->aysa_agent8_storage_key($wp_user_id, $site_host));
        }

        $project_id = intval($target['project_id'] ?? 0);
        if ($project_id > 0) {
            $deleted_custom_rows += $this->aysa_agent8_factory_reset_delete_wp_project_rows($project_id);
        }
        if ($wp_user_id > 0 && $site_host !== '' && class_exists('Aysa_Auth_Manager')) {
            global $wpdb;
            $auth_table = $wpdb->prefix . Aysa_Auth_Manager::TABLE_NAME;
            $deleted_custom_rows += $this->aysa_agent8_factory_reset_delete_table_where(
                $auth_table,
                'domain = %s',
                array($site_host)
            );
        }

        return array(
            'deleted_options' => $deleted_options,
            'deleted_like_options' => $deleted_like_options,
            'deleted_user_meta' => $deleted_user_meta,
            'deleted_custom_rows' => $deleted_custom_rows,
        );
    }

    private function aysa_agent8_factory_reset_delete_options_like($pattern) {
        global $wpdb;
        if (!isset($wpdb) || !is_object($wpdb) || (string) $pattern === '') {
            return 0;
        }
        $names = $wpdb->get_col($wpdb->prepare("SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s", $pattern));
        $deleted = 0;
        foreach ((array) $names as $name) {
            if (delete_option((string) $name)) {
                $deleted++;
            }
        }
        return $deleted;
    }

    private function aysa_agent8_factory_reset_delete_wp_project_rows($project_id) {
        global $wpdb;
        if (!isset($wpdb) || !is_object($wpdb)) {
            return 0;
        }
        $tables = array(
            $this->aysa_keyword_discovery_table(),
            $this->aysa_monitoring_history_table(),
            $this->aysa_keyword_v2_snapshot_table_name(),
            $wpdb->prefix . 'aysa_content_strategies',
            $this->aysa_missing_page_meta_table(),
            $wpdb->prefix . 'aysa_automated_pending_reviews',
        );
        $deleted = 0;
        foreach ($tables as $table) {
            $deleted += $this->aysa_agent8_factory_reset_delete_table_where($table, 'project_id = %d', array($project_id));
        }
        return $deleted;
    }

    private function aysa_agent8_factory_reset_delete_table_where($table, $where_sql, $where_args) {
        global $wpdb;
        $table = (string) $table;
        if ($table === '' || !isset($wpdb) || !is_object($wpdb)) {
            return 0;
        }
        $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table)) === $table;
        if (!$exists) {
            return 0;
        }
        $sql = "DELETE FROM {$table} WHERE {$where_sql}";
        $result = $wpdb->query($wpdb->prepare($sql, $where_args));
        return $result === false ? 0 : intval($result);
    }

    private function aysa_agent8_factory_reset_preserve_dry_run() {
        $post_counts = array(
            'posts' => wp_count_posts('post'),
            'pages' => wp_count_posts('page'),
            'products' => post_type_exists('product') ? wp_count_posts('product') : null,
            'media' => wp_count_posts('attachment'),
            'shop_orders' => post_type_exists('shop_order') ? wp_count_posts('shop_order') : null,
        );

        return array(
            'wordpress_content' => array(
                'status' => 'preserved',
                'counts' => $post_counts,
            ),
            'woocommerce_orders_customers' => array(
                'status' => 'preserved',
                'orders_table_not_touched' => true,
                'customers_table_not_touched' => true,
            ),
            'applied_seo_meta' => array(
                'status' => 'preserved',
                'postmeta_not_touched' => true,
            ),
            'live_redirects_htaccess' => array(
                'status' => 'preserved',
                'htaccess_not_touched' => true,
            ),
            'billing_wallet_ledger' => array(
                'status' => 'preserved',
                'wallet_tables_not_touched' => true,
            ),
            'global_config_secrets' => array(
                'status' => 'preserved',
                'secure_config_not_touched' => true,
            ),
            'other_projects_sites' => array(
                'status' => 'preserved',
                'scope' => 'current detected project/site only',
            ),
        );
    }

    public function handle_apprentice_reset_test() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }
        $wp_user_id = get_current_user_id();
        $site_host = isset($_POST['site_host']) ? sanitize_text_field(wp_unslash($_POST['site_host'])) : '';
        if (!$this->check_authentication(false)) {
            $this->aysa_agent8_clear_session($wp_user_id, $site_host);
            wp_send_json_success(array(
                'message' => 'Agent8 pre-activation session reset completed.',
                'pre_activation' => true,
            ));
            return;
        }

        $key_validation = $this->aysa_validate_test_access_key(
            $this->aysa_get_effective_access_key(isset($_POST['access_key']) ? sanitize_text_field(wp_unslash($_POST['access_key'])) : ''),
            isset($_POST['site_host']) ? sanitize_text_field(wp_unslash($_POST['site_host'])) : ''
        );
        if (empty($key_validation['valid'])) {
            wp_send_json_error(array(
                'message' => (string) ($key_validation['message'] ?? 'Access key is invalid.'),
                'code' => 'access_key_invalid',
            ));
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : intval(get_option('aysa_project_id', 0));
        $auth_manager = Aysa_Auth_Manager::get_instance();
        $auth_status = $auth_manager->is_authenticated();
        $user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : 0;

        if ($project_id <= 0) {
            wp_send_json_error(array('message' => 'Missing reset context.'));
            return;
        }

        $setup_options_deleted = $this->aysa_reset_local_setup_state($user_id, $project_id);
        $this->aysa_agent8_clear_session($wp_user_id, $site_host);
        $onboarding_run_token = $this->aysa_rotate_onboarding_run_token($project_id);
        delete_option('aysa_setup_completed');
        $this->aysa_mark_setup_reset_forced();
        $oauth_disconnect = $this->aysa_broker_request('POST', '/v1/oauth/disconnect', array(
            'project_id' => $project_id,
        ), 30);
        $local_breakdown = $this->aysa_reset_local_project_runtime($project_id);

        $result = $this->aysa_broker_request('POST', '/v1/conversation/apprentice/reset', array(
            'project_id' => $project_id,
            'site_host' => $site_host,
            'channels' => array('agent8', 'setup_seo_profile', 'adjustments_and_improvements'),
            'clear_keyword_research_runs' => true,
        ), 45);

        if (is_wp_error($result)) {
            wp_send_json_error(array('message' => $result->get_error_message()));
            return;
        }

        $this->aysa_clear_activation_binding($wp_user_id);

        wp_send_json_success(array(
            'message' => 'Apprentice test reset completed.',
            'setup_options_deleted' => $setup_options_deleted,
            'onboarding_run_token' => $onboarding_run_token,
            'local' => $local_breakdown,
            'oauth_disconnect' => is_wp_error($oauth_disconnect)
                ? array('ok' => false, 'message' => $oauth_disconnect->get_error_message())
                : array('ok' => true, 'result' => $oauth_disconnect),
            'result' => $result,
        ));
    }

    public function handle_setup_reset_chat_test() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }
        $key_validation = $this->aysa_validate_test_access_key(
            $this->aysa_get_effective_access_key(isset($_POST['access_key']) ? sanitize_text_field(wp_unslash($_POST['access_key'])) : ''),
            isset($_POST['site_host']) ? sanitize_text_field(wp_unslash($_POST['site_host'])) : ''
        );
        if (empty($key_validation['valid'])) {
            wp_send_json_error(array(
                'message' => (string) ($key_validation['message'] ?? 'Access key is invalid.'),
                'code' => 'access_key_invalid',
            ));
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : intval(get_option('aysa_project_id', 0));
        $auth_manager = Aysa_Auth_Manager::get_instance();
        $auth_status = $auth_manager->is_authenticated();
        $user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : 0;

        if ($project_id <= 0) {
            wp_send_json_error(array('message' => 'Missing reset context.'));
            return;
        }

        $setup_options_deleted = $this->aysa_reset_local_setup_state($user_id, $project_id);
        $onboarding_run_token = $this->aysa_rotate_onboarding_run_token($project_id);
        delete_option('aysa_setup_completed');
        $this->aysa_mark_setup_reset_forced();

        $result = $this->aysa_broker_request('POST', '/v1/conversation/setup/reset', array(
            'project_id' => $project_id,
            'site_host' => isset($_POST['site_host']) ? sanitize_text_field(wp_unslash($_POST['site_host'])) : '',
        ), 45);

        if (is_wp_error($result)) {
            wp_send_json_error(array('message' => $result->get_error_message()));
            return;
        }

        wp_send_json_success(array(
            'message' => 'Setup chat test reset completed.',
            'setup_options_deleted' => $setup_options_deleted,
            'onboarding_run_token' => $onboarding_run_token,
            'result' => $result,
        ));
    }

    private function aysa_setup_normalize_url($value) {
        $value = trim((string) $value);
        if ($value === '') {
            return '';
        }
        if (!preg_match('#^[a-z][a-z0-9+.-]*://#i', $value)) {
            $value = 'https://' . ltrim($value, '/');
        }
        $host = wp_parse_url($value, PHP_URL_HOST);
        if (!is_string($host) || trim($host) === '') {
            return '';
        }
        $scheme = wp_parse_url($value, PHP_URL_SCHEME);
        if (!is_string($scheme) || $scheme === '') {
            $scheme = 'https';
        }
        return strtolower($scheme) . '://' . $host;
    }

    private function aysa_setup_is_local_host($host) {
        $host = strtolower(trim((string) $host));
        if ($host === '') {
            return false;
        }
        if ($host === 'localhost' || $host === '127.0.0.1' || $host === '::1') {
            return true;
        }
        if (substr($host, -10) === '.localhost') {
            return true;
        }
        if (substr($host, -6) === '.local') {
            return true;
        }
        return false;
    }

    private function aysa_get_local_setup_state_for_user($user_id, $project_id = 0) {
        $user_id = intval($user_id);
        $project_id = intval($project_id);
        if ($user_id <= 0 && $project_id <= 0) {
            return array();
        }

        $steps = array('integrations', 'general', 'objectives', 'seo-settings', 'competition', 'tone-of-voice');
        $state = array();
        foreach ($steps as $step) {
            $project_raw = $project_id > 0 ? get_option('aysa_wizard_' . $step . '_project_' . $project_id, null) : null;
            $raw = $user_id > 0 ? get_option('aysa_wizard_' . $step . '_' . $user_id, array()) : array();
            if ($step === 'general') {
                $state[$step] = $this->aysa_choose_best_general_setup_state($project_raw, $raw);
                continue;
            }
            if (is_array($project_raw) && !empty($project_raw)) {
                $state[$step] = $project_raw;
                continue;
            }
            $state[$step] = is_array($raw) ? $raw : array();
        }
        return $state;
    }

    private function aysa_choose_best_general_setup_state($project_raw, $user_raw) {
        $project_state = is_array($project_raw) ? $project_raw : array();
        $user_state = is_array($user_raw) ? $user_raw : array();
        if (empty($project_state)) {
            return $user_state;
        }
        if (empty($user_state)) {
            return $project_state;
        }

        $project_score = $this->aysa_general_setup_completeness_score($project_state);
        $user_score = $this->aysa_general_setup_completeness_score($user_state);
        $best = ($user_score > $project_score) ? $user_state : $project_state;
        $fallback = ($best === $user_state) ? $project_state : $user_state;

        foreach ($fallback as $key => $value) {
            if (!is_string($key)) {
                continue;
            }
            if (!array_key_exists($key, $best) || trim((string) $best[$key]) === '') {
                $best[$key] = $value;
            }
        }

        return $best;
    }

    private function aysa_general_setup_completeness_score($state) {
        if (!is_array($state)) {
            return 0;
        }
        $score = 0;
        foreach (array('company_name', 'brand_name', 'support_phone', 'website', 'website_type', 'shipping', 'payment_method', 'industry', 'company_info') as $field) {
            if (trim((string) ($state[$field] ?? '')) !== '') {
                $score++;
            }
        }
        return $score;
    }

    private function aysa_general_setup_validated_true($state) {
        if (!is_array($state)) {
            return false;
        }
        $validated = $state['validated'] ?? false;
        return $validated === true || $validated === 1 || $validated === '1' || $validated === 'true';
    }

    private function aysa_guard_general_setup_persistence($incoming, $project_existing, $user_existing) {
        $incoming = is_array($incoming) ? $incoming : array();
        $project_existing = is_array($project_existing) ? $project_existing : array();
        $user_existing = is_array($user_existing) ? $user_existing : array();

        $best_existing = $this->aysa_choose_best_general_setup_state($project_existing, $user_existing);
        $incoming_score = $this->aysa_general_setup_completeness_score($incoming);
        $existing_score = $this->aysa_general_setup_completeness_score($best_existing);

        if ($existing_score > $incoming_score) {
            $guarded = $best_existing;
            foreach ($incoming as $key => $value) {
                if (!is_string($key)) {
                    continue;
                }
                if (!array_key_exists($key, $guarded) || trim((string) $guarded[$key]) === '') {
                    $guarded[$key] = $value;
                }
            }
        } else {
            $guarded = $incoming;
        }

        if (
            $this->aysa_general_setup_validated_true($incoming)
            || $this->aysa_general_setup_validated_true($project_existing)
            || $this->aysa_general_setup_validated_true($user_existing)
        ) {
            $guarded['validated'] = '1';
        }

        return $guarded;
    }

    private function aysa_merge_local_setup_state_fallback($existing, $incoming) {
        if (!is_array($existing)) {
            $existing = array();
        }
        if (!is_array($incoming) || empty($incoming)) {
            return $existing;
        }

        foreach ($incoming as $key => $value) {
            if (!is_string($key)) {
                $existing[$key] = $value;
                continue;
            }

            if (!array_key_exists($key, $existing)) {
                $existing[$key] = $value;
                continue;
            }

            if (is_array($value) && is_array($existing[$key]) && $this->aysa_is_assoc_array($existing[$key]) && $this->aysa_is_assoc_array($value)) {
                $existing[$key] = $this->aysa_merge_local_setup_state_fallback($existing[$key], $value);
            }
        }

        return $existing;
    }

    private function aysa_is_assoc_array($value) {
        if (!is_array($value)) {
            return false;
        }
        if ($value === array()) {
            return false;
        }
        return array_keys($value) !== range(0, count($value) - 1);
    }

    private function aysa_apply_setup_conversation_updates($user_id, $updates_by_step, $project_id = 0) {
        $user_id = intval($user_id);
        $project_id = intval($project_id);
        if (($user_id <= 0 && $project_id <= 0) || !is_array($updates_by_step)) {
            return;
        }

        foreach ($updates_by_step as $step => $data) {
            $step_key = sanitize_key((string) $step);
            if (!is_array($data) || $step_key === '') {
                continue;
            }

            $option_name = 'aysa_wizard_' . $step_key . '_' . $user_id;
            $existing = get_option($option_name, array());
            if (!is_array($existing)) {
                $existing = array();
            }

            $sanitized = $this->sanitize_wizard_data($data);
            if ($project_id > 0) {
                $project_option_name = 'aysa_wizard_' . $step_key . '_project_' . $project_id;
                $project_existing = get_option($project_option_name, array());
                if (!is_array($project_existing)) {
                    $project_existing = array();
                }
                update_option($project_option_name, $sanitized, false);
            }

            if ($user_id > 0) {
                update_option($option_name, $sanitized, false);
            }
        }
    }

    private function aysa_persist_local_setup_state_snapshot($user_id, $local_setup_state, $project_id = 0) {
        $user_id = intval($user_id);
        $project_id = intval($project_id);
        if (($user_id <= 0 && $project_id <= 0) || !is_array($local_setup_state) || empty($local_setup_state)) {
            return;
        }

        $updates_by_step = array();
        foreach ($local_setup_state as $step => $data) {
            $step_key = sanitize_key((string) $step);
            if ($step_key === '' || !is_array($data) || empty($data)) {
                continue;
            }
            $updates_by_step[$step_key] = $this->sanitize_wizard_data($data);
        }

        if (empty($updates_by_step)) {
            return;
        }

        foreach ($updates_by_step as $step_key => $sanitized) {
            if ($step_key === 'general') {
                $project_existing = $project_id > 0 ? get_option('aysa_wizard_general_project_' . $project_id, array()) : array();
                $user_existing = $user_id > 0 ? get_option('aysa_wizard_general_' . $user_id, array()) : array();
                $sanitized = $this->aysa_guard_general_setup_persistence($sanitized, $project_existing, $user_existing);
            }
            if ($project_id > 0) {
                $project_option_name = 'aysa_wizard_' . $step_key . '_project_' . $project_id;
                update_option($project_option_name, $sanitized, false);
            }
            if ($user_id > 0) {
                $user_option_name = 'aysa_wizard_' . $step_key . '_' . $user_id;
                update_option($user_option_name, $sanitized, false);
            }
        }
    }

    private function aysa_sync_setup_state_to_client_rag($project_id, $conversation_language, $local_setup_state, $google_connected = false, $google_skipped = false, $state_meta = array()) {
        $project_id = intval($project_id);
        if ($project_id <= 0 || !is_array($local_setup_state) || empty($local_setup_state)) {
            return false;
        }

        $language = in_array((string) $conversation_language, array('en', 'ro', 'bg'), true) ? (string) $conversation_language : 'en';
        $general = isset($local_setup_state['general']) && is_array($local_setup_state['general']) ? $local_setup_state['general'] : array();
        $website = isset($general['website']) ? $this->aysa_setup_normalize_url((string) $general['website']) : '';
        if ($website === '') {
            $website = $this->aysa_setup_normalize_url(home_url('/'));
        }

        $state = array(
            'meta' => array_merge(array(
                'onboarding_mode' => 'strict_local',
                'google_optional_skipped' => !empty($google_skipped),
            ), is_array($state_meta) ? $state_meta : array()),
        );

        $result = $this->aysa_broker_request('POST', '/v1/conversation/adjustments/sync-setup-context', array(
            'project_id' => $project_id,
            'conversation_language' => $language,
            'website' => $website,
            'local_setup_state' => $local_setup_state,
            'research_snapshot' => array(),
            'ui_context' => array(
                'conversation_language' => $language,
                'available_languages' => array('en', 'ro', 'bg'),
                'phase' => 'agent_orchestration',
                'integrations' => array(
                    'google_connect_available' => true,
                    'google_connected' => !empty($google_connected),
                    'google_skipped' => !empty($google_skipped),
                ),
            ),
            'state' => $state,
        ), 45);

        return !is_wp_error($result);
    }

    private function aysa_adjustments_setup_steps() {
        return array(
            'integrations',
            'company_name',
            'brand_name',
            'support_phone',
            'website',
            'website_type',
            'shipping',
            'payment_method',
            'industry',
            'company_info',
            'objectives',
            'seed_keywords',
            'competitors',
            'seo_settings',
            'tone_of_voice',
        );
    }

    private function aysa_adjustments_is_skip_google_message($message) {
        $normalized = strtolower(trim((string) $message));
        foreach (array(
            'sari peste google',
            'sarim peste google',
            'skip google',
            'continue without google',
            'continue without google for now',
            'continue fara google',
            'continue fără google',
            'continui fara google',
            'continui fără google',
            'skip for now',
            'sari peste momentan',
            'fara google',
            'fără google',
        ) as $pattern) {
            if ($pattern !== '' && strpos($normalized, $pattern) !== false) {
                return true;
            }
        }
        return false;
    }

    private function aysa_adjustments_is_connect_google_message($message) {
        $normalized = mb_strtolower(trim((string) $message));
        foreach (array(
            'connect google',
            'connect with google',
            'conecteaza google',
            'conectează google',
            'conecteaza cu google',
            'conectează cu google',
            'hai sa conectam google',
            'hai să conectăm google',
            'vreau sa conectez google',
            'vreau să conectez google'
        ) as $pattern) {
            if ($pattern !== '' && strpos($normalized, $pattern) !== false) {
                return true;
            }
        }
        return false;
    }

    private function aysa_adjustments_is_continue_message($message) {
        $normalized = mb_strtolower(trim((string) $message));
        foreach (array(
            'continuam',
            'continuăm',
            'continue',
            'continue please',
            'mergem mai departe',
            'hai mai departe',
            'super, si ce fac',
            'super, și ce fac',
            'si ce fac',
            'și ce fac',
            'ce fac mai departe',
            'ce urmeaza',
            'ce urmează',
            'ok',
            'bine',
            'perfect',
            'super'
        ) as $pattern) {
            if ($pattern !== '' && $normalized === $pattern) {
                return true;
            }
        }
        return false;
    }

    private function aysa_setup_fetch_html($url) {
        $resp = wp_remote_get($url, array(
            'timeout' => 15,
            'redirection' => 5,
            'headers' => array(
                'Accept' => 'text/html,application/xhtml+xml',
                'User-Agent' => 'AYSA Setup Autofill/1.0',
            ),
        ));
        if (is_wp_error($resp)) {
            return '';
        }
        $code = intval(wp_remote_retrieve_response_code($resp));
        if ($code < 200 || $code >= 300) {
            return '';
        }
        return (string) wp_remote_retrieve_body($resp);
    }

    private function aysa_setup_extract_json_ld($html) {
        $out = array();
        if (!is_string($html) || $html === '') {
            return $out;
        }
        if (!preg_match_all('#<script[^>]*type=["\']application/ld\\+json["\'][^>]*>(.*?)</script>#is', $html, $matches)) {
            return $out;
        }
        foreach ($matches[1] as $raw) {
            $decoded = json_decode(trim((string) $raw), true);
            if (json_last_error() !== JSON_ERROR_NONE) {
                continue;
            }
            if (is_array($decoded)) {
                if (isset($decoded['@graph']) && is_array($decoded['@graph'])) {
                    foreach ($decoded['@graph'] as $node) {
                        if (is_array($node)) {
                            $out[] = $node;
                        }
                    }
                } else {
                    $out[] = $decoded;
                }
            }
        }
        return $out;
    }

    private function aysa_setup_extract_internal_links($website, $html) {
        $links = array();
        if (!is_string($html) || $html === '') {
            return $links;
        }

        $base_host = strtolower((string) wp_parse_url($website, PHP_URL_HOST));
        if ($base_host === '') {
            return $links;
        }
        $base_scheme = (string) wp_parse_url($website, PHP_URL_SCHEME);
        if ($base_scheme === '') {
            $base_scheme = 'https';
        }

        if (!preg_match_all('#<a[^>]+href=["\']([^"\']+)["\'][^>]*>#i', $html, $matches)) {
            return $links;
        }

        foreach ($matches[1] as $href) {
            $href = trim((string) $href);
            if ($href === '' || strpos($href, '#') === 0 || stripos($href, 'javascript:') === 0 || stripos($href, 'mailto:') === 0 || stripos($href, 'tel:') === 0) {
                continue;
            }

            $absolute = '';
            if (preg_match('#^https?://#i', $href)) {
                $absolute = $href;
            } elseif (strpos($href, '//') === 0) {
                $absolute = $base_scheme . ':' . $href;
            } elseif (strpos($href, '/') === 0) {
                $absolute = rtrim($website, '/') . $href;
            } else {
                $absolute = rtrim($website, '/') . '/' . ltrim($href, '/');
            }

            $host = strtolower((string) wp_parse_url($absolute, PHP_URL_HOST));
            if ($host === '' || $host !== $base_host) {
                continue;
            }

            $path = (string) wp_parse_url($absolute, PHP_URL_PATH);
            if ($path === '') {
                $path = '/';
            }
            $links[] = $base_scheme . '://' . $base_host . $path;
        }

        return array_values(array_unique($links));
    }

    private function aysa_setup_collect_candidate_urls($website, $homepage_html) {
        $urls = array(
            $website,
            rtrim($website, '/') . '/contact',
            rtrim($website, '/') . '/contact-us',
            rtrim($website, '/') . '/despre-noi',
            rtrim($website, '/') . '/about',
            rtrim($website, '/') . '/livrare',
            rtrim($website, '/') . '/shipping',
            rtrim($website, '/') . '/plata',
            rtrim($website, '/') . '/payment',
            rtrim($website, '/') . '/checkout',
            rtrim($website, '/') . '/termeni-si-conditii',
        );

        $priority_tokens = array(
            'contact', 'despre', 'about', 'livrare', 'shipping', 'plata', 'payment', 'checkout', 'termeni', 'faq', 'support'
        );
        $internal_links = $this->aysa_setup_extract_internal_links($website, $homepage_html);
        foreach ($internal_links as $link) {
            $path = strtolower((string) wp_parse_url($link, PHP_URL_PATH));
            foreach ($priority_tokens as $token) {
                if (strpos($path, $token) !== false) {
                    $urls[] = $link;
                    break;
                }
            }
            if (count($urls) >= 12) {
                break;
            }
        }

        return array_values(array_unique($urls));
    }

    private function aysa_setup_extract_general_info($website) {
        $host = (string) wp_parse_url($website, PHP_URL_HOST);
        $host_clean = strtolower(preg_replace('/^www\./', '', $host));
        $default_name = ucwords(str_replace(array('-', '_', '.'), ' ', (string) preg_replace('/\.[a-z]{2,}$/i', '', $host_clean)));

        $homepage_html = $this->aysa_setup_fetch_html($website);
        $candidate_urls = $this->aysa_setup_collect_candidate_urls($website, $homepage_html);

        $all_html_parts = array();
        $json_ld_nodes = array();
        $title = '';
        $description = '';
        $emails = array();
        $phones = array();

        foreach ($candidate_urls as $idx => $url) {
            $html = $idx === 0 ? $homepage_html : $this->aysa_setup_fetch_html($url);
            if (!is_string($html) || $html === '') {
                continue;
            }

            $all_html_parts[] = $html;

            if ($title === '' && preg_match('#<title[^>]*>(.*?)</title>#is', $html, $m)) {
                $title = trim(wp_strip_all_tags((string) $m[1]));
            }
            if ($description === '' && preg_match('#<meta[^>]+name=["\']description["\'][^>]+content=["\']([^"\']+)["\']#i', $html, $m)) {
                $description = trim((string) $m[1]);
            }

            $json_ld_nodes = array_merge($json_ld_nodes, $this->aysa_setup_extract_json_ld($html));

            if (preg_match_all('/[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}/i', $html, $email_matches)) {
                foreach ($email_matches[0] as $email) {
                    $email = strtolower(trim((string) $email));
                    if ($email !== '' && strpos($email, 'example.') === false && strpos($email, '@w3.org') === false) {
                        $emails[] = $email;
                    }
                }
            }
            if (preg_match_all('/(\+?\d[\d\-\s().]{7,}\d)/', $html, $phone_matches)) {
                foreach ($phone_matches[1] as $phone) {
                    $phone = trim((string) $phone);
                    $digits = preg_replace('/\D+/', '', $phone);
                    if (strlen((string) $digits) >= 8 && strlen((string) $digits) <= 15) {
                        $phones[] = $phone;
                    }
                }
            }
        }

        $json_name = '';
        $json_phone = '';
        $json_email = '';
        $json_description = '';
        foreach ($json_ld_nodes as $node) {
            if (!is_array($node)) {
                continue;
            }
            $type = strtolower((string) ($node['@type'] ?? ''));
            if ($type !== '' && strpos($type, 'organization') === false && strpos($type, 'localbusiness') === false && strpos($type, 'store') === false) {
                continue;
            }
            if ($json_name === '' && !empty($node['name'])) {
                $json_name = trim((string) $node['name']);
            }
            if ($json_phone === '' && !empty($node['telephone'])) {
                $json_phone = trim((string) $node['telephone']);
            }
            if ($json_email === '' && !empty($node['email'])) {
                $json_email = trim((string) $node['email']);
            }
            if ($json_description === '' && !empty($node['description'])) {
                $json_description = trim((string) $node['description']);
            }
        }

        $emails = array_values(array_unique($emails));
        $phones = array_values(array_unique($phones));
        $support_email = $json_email !== '' ? $json_email : (!empty($emails) ? (string) $emails[0] : '');
        $support_phone = $json_phone !== '' ? $json_phone : (!empty($phones) ? (string) $phones[0] : '');

        $all_text = strtolower(wp_strip_all_tags(implode("\n", $all_html_parts)));

        $shipping = '';
        if (strpos($all_text, 'livrare gratuita') !== false || strpos($all_text, 'free shipping') !== false) {
            $shipping = 'Free delivery options available';
        } elseif (strpos($all_text, 'livrare') !== false || strpos($all_text, 'shipping') !== false || strpos($all_text, 'curier') !== false) {
            $shipping = 'Delivery by courier/local shipping options available';
        }

        $payment_tokens = array();
        if (strpos($all_text, 'card') !== false) {
            $payment_tokens[] = 'Card';
        }
        if (strpos($all_text, 'paypal') !== false) {
            $payment_tokens[] = 'PayPal';
        }
        if (strpos($all_text, 'ramburs') !== false || strpos($all_text, 'cash on delivery') !== false) {
            $payment_tokens[] = 'Cash on delivery';
        }
        if (strpos($all_text, 'transfer bancar') !== false || strpos($all_text, 'bank transfer') !== false || strpos($all_text, 'op') !== false) {
            $payment_tokens[] = 'Bank transfer';
        }
        $payment_tokens = array_values(array_unique($payment_tokens));
        $payment = !empty($payment_tokens) ? implode(', ', $payment_tokens) : '';

        $website_type = 'business';
        if (strpos($all_text, 'add to cart') !== false || strpos($all_text, 'cos') !== false || strpos($all_text, 'checkout') !== false || strpos($all_text, 'produs') !== false || strpos($all_text, 'woocommerce') !== false) {
            $website_type = 'ecommerce';
        } elseif (strpos($all_text, 'blog') !== false || strpos($all_text, '/blog') !== false) {
            $website_type = 'blog';
        } elseif (strpos($all_text, 'portfolio') !== false) {
            $website_type = 'portfolio';
        }

        $industry = 'retail';
        if (strpos($all_text, 'flori') !== false || strpos($all_text, 'flowers') !== false || strpos($all_text, 'florarie') !== false || strpos($all_text, 'buchet') !== false) {
            $industry = 'florist';
        } elseif (strpos($all_text, 'restaurant') !== false || strpos($all_text, 'hotel') !== false) {
            $industry = 'hospitality';
        } elseif (strpos($all_text, 'clinica') !== false || strpos($all_text, 'medical') !== false) {
            $industry = 'healthcare';
        } elseif (strpos($all_text, 'imobiliare') !== false || strpos($all_text, 'real estate') !== false) {
            $industry = 'real-estate';
        } elseif (strpos($all_text, 'software') !== false || strpos($all_text, 'saas') !== false) {
            $industry = 'technology';
        }

        $company_name = $json_name !== '' ? $json_name : ($title !== '' ? preg_replace('/\s*[\|\-–].*$/u', '', $title) : $default_name);
        $brand = $json_name !== '' ? $json_name : $company_name;
        $company_description = $json_description !== '' ? $json_description : $description;
        $business_information = $company_description !== '' ? $company_description : 'Please review and complete business details.';

        // Keep setup launchable for WooCommerce/e-commerce sites even when the homepage
        // does not expose payment labels clearly enough for string matching.
        if ($payment === '' && $website_type === 'ecommerce') {
            $payment = 'Card, Bank transfer, Cash on delivery';
        }

        return array(
            'id' => 0,
            'project' => array('name' => $default_name),
            'companyName' => $company_name,
            'brand' => $brand,
            'companyDescription' => $company_description,
            'supportPhoneNumber' => $support_phone,
            'supportEmailAddress' => $support_email,
            'websiteUrl' => $website,
            'websiteType' => $website_type,
            'shippingOptions' => $shipping,
            'paymentMethods' => $payment,
            'industry' => $industry,
            'businessInformation' => $business_information,
            'source' => 'setup_general_autofill',
            'scannedUrls' => $candidate_urls,
        );
    }

    private function aysa_test_access_key_option_name() {
        return 'aysa_active_access_key';
    }

    private function aysa_get_effective_access_key($provided_key = '') {
        $provided_key = trim((string) $provided_key);
        if ($provided_key !== '') {
            return $provided_key;
        }
        return trim((string) get_option($this->aysa_test_access_key_option_name(), ''));
    }

    private function aysa_is_access_key_valid_globally(&$validation = null) {
        $stored_key = trim((string) get_option($this->aysa_test_access_key_option_name(), ''));
        $validation = $this->aysa_validate_test_access_key($stored_key, '');
        if (!empty($validation['valid'])) {
            return true;
        }

        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }
        if (!class_exists('Aysa_Auth_Manager')) {
            return false;
        }

        // If the site already has a valid AYSA auth session, promote it to the
        // current WP admin and restore the site-level access key from that auth.
        $auth_status = Aysa_Auth_Manager::get_instance()->is_authenticated();
        $auth_secret_key = is_array($auth_status) ? trim((string) ($auth_status['secret_key'] ?? '')) : '';
        if (empty($auth_status['authenticated']) || $auth_secret_key === '') {
            return false;
        }

        $auth_validation = $this->aysa_validate_test_access_key($auth_secret_key, '');
        if (!empty($auth_validation['valid'])) {
            if ($stored_key !== $auth_secret_key) {
                update_option($this->aysa_test_access_key_option_name(), $auth_secret_key, false);
            }
            if (!empty($auth_validation['project_id'])) {
                update_option('aysa_project_id', intval($auth_validation['project_id']), false);
            }
            if (!empty($auth_validation['website_id'])) {
                update_option('aysa_website_id', intval($auth_validation['website_id']), false);
            }
            $validation = $auth_validation;
            $validation['source'] = 'site_auth_recovery';
            return true;
        }

        // Final fail-open for a site that was already authenticated but the key
        // verifier is temporarily inconsistent. This keeps menus accessible for
        // other admins on the same site instead of forcing reactivation.
        if ($stored_key !== $auth_secret_key) {
            update_option($this->aysa_test_access_key_option_name(), $auth_secret_key, false);
        }
        $validation = array(
            'valid' => true,
            'message' => 'Access key restored from active site authentication.',
            'code' => 'access_key_restored_from_site_auth',
            'host' => (string) wp_parse_url(home_url(), PHP_URL_HOST),
            'source' => 'site_auth_fail_open',
        );
        return true;
    }

    private function aysa_is_setup_profile_completed_effectively(&$reason = null) {
        if ($this->aysa_is_setup_reset_forced()) {
            $reason = array(
                'code' => 'setup_reset_forced',
                'message' => 'Setup was explicitly reset and must start again from the setup flow.',
                'source' => 'forced_reset',
                'step' => 'integrations',
            );
            return false;
        }

        $current_reason = array();
        if ($this->aysa_is_setup_profile_completed_for_current_user($current_reason)) {
            $this->aysa_clear_setup_reset_forced();
            if (!get_option('aysa_setup_completed', false)) {
                update_option('aysa_setup_completed', true, false);
            }
            $reason = array(
                'code' => 'setup_completed_for_current_user',
                'message' => 'Setup is completed for the current authenticated user.',
                'source' => 'current_user',
            );
            return true;
        }

        $site_completed = get_option('aysa_setup_completed', false) ? true : false;
        $project_id = intval(get_option('aysa_project_id', 0));
        $website_id = intval(get_option('aysa_website_id', 0));

        if ($site_completed || $this->aysa_has_site_setup_evidence()) {
            $reason = array(
                'code' => 'setup_completed_for_site',
                'message' => 'Setup is completed for this site based on saved site evidence.',
                'source' => 'site_state',
            );
            return true;
        }

        $reason = is_array($current_reason) ? $current_reason : array();
        return false;
    }

    private function aysa_has_site_setup_evidence() {
        global $wpdb;
        if (!$wpdb || !isset($wpdb->options)) {
            return false;
        }

        $patterns = array(
            'aysa_wizard_general_%',
            'aysa_wizard_general_project_%',
            'aysa_wizard_objectives_%',
            'aysa_wizard_objectives_project_%',
            'aysa_wizard_seo-settings_%',
            'aysa_wizard_seo-settings_project_%',
            'aysa_wizard_competition_%',
            'aysa_wizard_competition_project_%',
            'aysa_wizard_tone-of-voice_%',
            'aysa_wizard_tone-of-voice_project_%',
        );
        foreach ($patterns as $pattern) {
            $option_name = $wpdb->get_var(
                $wpdb->prepare(
                    "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE %s AND option_value IS NOT NULL AND option_value <> '' ORDER BY option_id DESC LIMIT 1",
                    $pattern
                )
            );
            if (is_string($option_name) && $option_name !== '') {
                return true;
            }
        }

        return false;
    }

    private function aysa_get_test_access_keys() {
        // TEMP QA keys until app.aysa.ai account-key system is live.
        return array(
            'AYSA-QA-PLAN-29-2026' => array(
                'key_id' => 'test-key-029',
                'account_id' => 'qa_plan_29',
                'account_label' => 'qa_plan_29',
                'plan_code' => 'starter_29',
                'plan_price_eur' => 29,
            ),
            'AYSA-QA-PLAN-69-2026' => array(
                'key_id' => 'test-key-069',
                'account_id' => 'qa_plan_69',
                'account_label' => 'qa_plan_69',
                'plan_code' => 'growth_69',
                'plan_price_eur' => 69,
            ),
            'AYSA-QA-PLAN-99-2026' => array(
                'key_id' => 'test-key-099',
                'account_id' => 'qa_plan_99',
                'account_label' => 'qa_plan_99',
                'plan_code' => 'scale_99',
                'plan_price_eur' => 99,
            ),
        );
    }

    private function aysa_get_authenticated_external_user_id() {
        if (!class_exists('Aysa_Auth_Manager')) {
            return 0;
        }
        $auth_manager = Aysa_Auth_Manager::get_instance();
        $auth_status = $auth_manager->is_authenticated();
        if (empty($auth_status['authenticated'])) {
            return 0;
        }
        return isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : 0;
    }

    private function aysa_get_effective_setup_user_id() {
        $external_id = $this->aysa_get_authenticated_external_user_id();
        if ($external_id > 0) {
            return $external_id;
        }
        return function_exists('get_current_user_id') ? intval(get_current_user_id()) : 0;
    }

    private function aysa_has_non_empty_value($value) {
        if (is_array($value)) {
            return !empty($value);
        }
        return trim((string) $value) !== '';
    }

    private function aysa_is_setup_profile_completed_for_current_user(&$reason = null) {
        $reason = array();
        $user_id = $this->aysa_get_authenticated_external_user_id();
        if ($user_id <= 0) {
            $reason = array(
                'code' => 'auth_required',
                'step' => 'integrations',
                'message' => 'Authentication is required before completing setup.',
            );
            return false;
        }

        $general = get_option('aysa_wizard_general_' . $user_id, array());
        $objectives = get_option('aysa_wizard_objectives_' . $user_id, array());
        $seo_settings = get_option('aysa_wizard_seo-settings_' . $user_id, array());
        $competition = get_option('aysa_wizard_competition_' . $user_id, array());
        $tone = get_option('aysa_wizard_tone-of-voice_' . $user_id, array());

        $general_required = array(
            'company_name',
            'brand_name',
            'support_phone',
            'website',
            'website_type',
            'shipping',
            'payment_method',
            'industry',
            'company_info',
        );
        foreach ($general_required as $field) {
            if (!$this->aysa_has_non_empty_value(isset($general[$field]) ? $general[$field] : '')) {
                $reason = array(
                    'code' => 'general_incomplete',
                    'step' => 'general',
                    'message' => 'Please complete all required General Information fields.',
                );
                return false;
            }
        }

        if (empty($objectives['objectives']) || !is_array($objectives['objectives'])) {
            $reason = array(
                'code' => 'objectives_incomplete',
                'step' => 'objectives',
                'message' => 'Please select at least one objective.',
            );
            return false;
        }

        $seo_required = array(
            'targeted_search_engine',
            'targeted_country',
            'primary_language',
        );
        foreach ($seo_required as $field) {
            if (!$this->aysa_has_non_empty_value(isset($seo_settings[$field]) ? $seo_settings[$field] : '')) {
                $reason = array(
                    'code' => 'seo_settings_incomplete',
                    'step' => 'seo-settings',
                    'message' => 'Please complete required SEO settings.',
                );
                return false;
            }
        }

        $has_seed_keyword = false;
        if (is_array($competition)) {
            foreach ($competition as $key => $value) {
                if (strpos((string) $key, 'keyword') === 0 && $this->aysa_has_non_empty_value($value)) {
                    $has_seed_keyword = true;
                    break;
                }
            }
        }
        if (!$has_seed_keyword) {
            $reason = array(
                'code' => 'competition_incomplete',
                'step' => 'competition',
                'message' => 'Please add at least one seed keyword.',
            );
            return false;
        }

        $has_competitor = false;
        if (is_array($competition)) {
            foreach ($competition as $key => $value) {
                if (strpos((string) $key, 'competitor') === 0 && $this->aysa_has_non_empty_value($value)) {
                    $has_competitor = true;
                    break;
                }
            }
        }
        if (!$has_competitor) {
            $reason = array(
                'code' => 'competition_incomplete',
                'step' => 'competition',
                'message' => 'Please confirm at least one competitor.',
            );
            return false;
        }

        if (!$this->aysa_has_non_empty_value(isset($tone['tone']) ? $tone['tone'] : '')) {
            $reason = array(
                'code' => 'tone_incomplete',
                'step' => 'tone-of-voice',
                'message' => 'Please choose a tone of voice.',
            );
            return false;
        }

        $project_id = intval($general['project_id'] ?? 0);
        if ($project_id <= 0) {
            $project_id = intval(get_option('aysa_project_id', 0));
        }
        $google_error = '';
        if (!$this->aysa_is_google_connected_for_project($project_id, $google_error)) {
            $reason = array(
                'code' => 'google_not_connected',
                'step' => 'integrations',
                'message' => ($google_error !== '' ? $google_error : 'Google connection is required before setup can be completed.'),
            );
            return false;
        }

        return true;
    }

    private function aysa_is_google_connected_for_project($project_id, &$error_message = '') {
        $error_message = '';
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            $error_message = 'Project ID is missing. Complete Integrations first.';
            return false;
        }

        if (!class_exists('Aysa_Google_OAuth_Manager')) {
            $manager_file = AYSA_PLUGIN_DIR . 'includes/class-aysa-google-oauth-manager.php';
            if (!file_exists($manager_file)) {
                $error_message = 'Google integration manager is missing.';
                return false;
            }
            require_once $manager_file;
        }

        if (!class_exists('Aysa_Google_OAuth_Manager')) {
            $error_message = 'Google integration manager is unavailable.';
            return false;
        }

        $manager = Aysa_Google_OAuth_Manager::get_instance();
        if (!is_object($manager) || !method_exists($manager, 'get_status_for_project')) {
            $error_message = 'Cannot validate Google integration status.';
            return false;
        }

        $status = $manager->get_status_for_project($project_id);
        if (is_wp_error($status)) {
            $error_code = (string) $status->get_error_code();
            // Do not downgrade an otherwise working site to "setup incomplete"
            // just because the OAuth broker had a transient transport/problematic
            // response. Reserve the warning for explicit disconnected states.
            if (in_array($error_code, array('broker_invalid_response', 'broker_http_failed', 'broker_error'), true)) {
                return true;
            }
            $error_message = $status->get_error_message();
            return false;
        }

        if (empty($status['connected'])) {
            $error_message = 'Google Search Console / Analytics is not connected. Connect Google in Integrations.';
            return false;
        }

        return true;
    }

    private function aysa_validate_test_access_key($provided_key = '', $site_host = '') {
        $provided_key = trim((string) $provided_key);
        $valid_keys = $this->aysa_get_test_access_keys();
        if ($provided_key === '') {
            return array(
                'valid' => false,
                'message' => 'Access key is required.',
                'code' => 'access_key_required',
            );
        }

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

        if (
            $this->aysa_setup_is_local_host($site_host)
            && ($provided_key === 'local-dev-key' || $provided_key === 'AYSA-LOCAL-DEV-KEY-2026')
        ) {
            return array(
                'valid' => true,
                'message' => 'Access key accepted in local mode.',
                'code' => 'access_key_local_dev_master',
                'host' => $site_host,
                'account_id' => 'local_dev',
                'account_label' => 'local_dev',
                'key_id' => 'local-dev-master',
                'plan_code' => 'local_dev',
                'plan_price_eur' => 0,
                'source' => 'local_dev_master',
            );
        }

        // QA hardcoded keys remain supported for local testing.
        if (isset($valid_keys[$provided_key])) {
            $key_meta = $valid_keys[$provided_key];
            return array(
                'valid' => true,
                'message' => 'Access key is valid.',
                'host' => $site_host,
                'account_id' => (string) $key_meta['account_id'],
                'account_label' => (string) $key_meta['account_label'],
                'key_id' => (string) $key_meta['key_id'],
                'plan_code' => (string) $key_meta['plan_code'],
                'plan_price_eur' => intval($key_meta['plan_price_eur']),
                'source' => 'qa_static',
            );
        }

        // Real SaaS-issued keys must be validated by API source-of-truth.
        $api_validation = $this->aysa_validate_access_key_via_api($provided_key, $site_host);
        if (!empty($api_validation['valid'])) {
            return $api_validation;
        }

        return array(
            'valid' => false,
            'message' => (string) ($api_validation['message'] ?? 'Invalid access key.'),
            'code' => (string) ($api_validation['code'] ?? 'access_key_invalid'),
            'host' => $site_host,
        );
    }

    private function aysa_validate_access_key_via_api($provided_key = '', $site_host = '') {
        $provided_key = trim((string) $provided_key);
        if ($provided_key === '') {
            return array(
                'valid' => false,
                'message' => 'Access key is required.',
                'code' => 'access_key_required',
            );
        }

        if (!class_exists('Aysa_API_Config')) {
            require_once AYSA_PLUGIN_DIR . 'includes/config/aysa-api-config.php';
        }
        $base_url = class_exists('Aysa_API_Config') ? (string) Aysa_API_Config::get_base_url() : 'https://api.aysa.ai/';
        $api_url = rtrim($base_url, '/') . '/api/License/verify';

        $request_args = array(
            'timeout' => 25,
            'headers' => array(
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
            ),
            'body' => wp_json_encode(array(
                // Keep both legacy and current keys for compatibility with API versions.
                'access_key' => $provided_key,
                'api_key' => $provided_key,
                'domain' => $site_host,
                'site_host' => $site_host,
            )),
        );
        $response = wp_remote_post($api_url, $request_args);
        if (is_wp_error($response)) {
            return array(
                'valid' => false,
                'message' => 'Key verification service unavailable: ' . $response->get_error_message(),
                'code' => 'access_key_verify_unavailable',
            );
        }

        $code = (int) wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);
        $data = json_decode((string) $body, true);
        if (!is_array($data)) {
            return array(
                'valid' => false,
                'message' => 'Key verification returned invalid JSON.',
                'code' => 'access_key_verify_invalid_json',
            );
        }

        if ($code >= 400 || empty($data['ok'])) {
            return array(
                'valid' => false,
                'message' => (string) ($data['message'] ?? $data['error'] ?? ('Key verification failed (HTTP ' . $code . ').')),
                'code' => (string) ($data['code'] ?? 'access_key_verify_failed'),
            );
        }

        if (empty($data['valid'])) {
            return array(
                'valid' => false,
                'message' => (string) ($data['message'] ?? 'Invalid access key.'),
                'code' => (string) ($data['code'] ?? 'access_key_invalid'),
                'host' => (string) ($data['host'] ?? $site_host),
                'domain' => (string) ($data['domain'] ?? ''),
            );
        }

        return array(
            'valid' => true,
            'message' => (string) ($data['message'] ?? 'Access key is valid.'),
            'code' => (string) ($data['code'] ?? 'access_key_valid'),
            'host' => (string) ($data['host'] ?? $site_host),
            'domain' => (string) ($data['domain'] ?? ''),
            'key_id' => (string) ($data['key_id'] ?? ''),
            'key_label' => (string) ($data['key_label'] ?? 'primary'),
            'plan_code' => (string) ($data['plan_code'] ?? ''),
            'project_id' => intval($data['project_id'] ?? 0),
            'website_id' => intval($data['website_id'] ?? 0),
            'source' => (string) ($data['source'] ?? 'api'),
        );
    }

    public function handle_access_key_get_test() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }

        $plan_code = isset($_POST['plan_code']) ? sanitize_key(wp_unslash($_POST['plan_code'])) : 'starter_29';
        $key_map = $this->aysa_get_test_access_keys();
        $key = 'AYSA-QA-PLAN-29-2026';
        foreach ($key_map as $candidate => $meta) {
            if (($meta['plan_code'] ?? '') === $plan_code) {
                $key = $candidate;
                break;
            }
        }
        $validation = $this->aysa_validate_test_access_key($key, isset($_POST['site_host']) ? sanitize_text_field(wp_unslash($_POST['site_host'])) : '');

        wp_send_json_success(array(
            'access_key' => $key,
            'key_id' => (string) ($validation['key_id'] ?? ''),
            'validation' => $validation,
            'available_test_keys' => array(
                array('plan_code' => 'starter_29', 'price_eur' => 29, 'key' => 'AYSA-QA-PLAN-29-2026'),
                array('plan_code' => 'growth_69', 'price_eur' => 69, 'key' => 'AYSA-QA-PLAN-69-2026'),
                array('plan_code' => 'scale_99', 'price_eur' => 99, 'key' => 'AYSA-QA-PLAN-99-2026'),
            ),
            'note' => 'Temporary QA plan keys. Save manually via verify endpoint.',
        ));
    }

    public function handle_access_key_verify() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }

        $access_key = isset($_POST['access_key']) ? sanitize_text_field(wp_unslash($_POST['access_key'])) : '';
        $site_host = isset($_POST['site_host']) ? sanitize_text_field(wp_unslash($_POST['site_host'])) : '';

        $validation = $this->aysa_validate_test_access_key(
            $access_key,
            $site_host
        );

        if (empty($validation['valid'])) {
            wp_send_json_error($validation);
            return;
        }

        update_option(
            $this->aysa_test_access_key_option_name(),
            $access_key,
            false
        );

        if (!empty($validation['project_id'])) {
            update_option('aysa_project_id', intval($validation['project_id']), false);
        }
        if (!empty($validation['website_id'])) {
            update_option('aysa_website_id', intval($validation['website_id']), false);
        }

        $auth_bootstrap = null;
        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }
        if (class_exists('Aysa_Auth_Manager')) {
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $auth_bootstrap = $auth_manager->verify_and_store_secret_key($access_key, $site_host);

            if (is_array($auth_bootstrap) && !empty($auth_bootstrap['success'])) {
                update_option('aysa_user_secret_key', $access_key, false);
                update_option('aysa_user_id', intval($auth_bootstrap['aysa_user_id'] ?? 0), false);
                update_option('aysa_auth_domain', $site_host !== '' ? $site_host : wp_parse_url(home_url(), PHP_URL_HOST), false);
            }
        }

        // Set wallet default plan based on verified key.
        if (!empty($validation['plan_code'])) {
            update_option('aysa_wallet_default_plan', sanitize_key((string) $validation['plan_code']), false);
            if (class_exists('Aysa_Wallet_Manager')) {
                $project_id = intval(get_option('aysa_project_id', 0));
                if ($project_id > 0) {
                    Aysa_Wallet_Manager::get_instance()->apply_plan_to_project(
                        $project_id,
                        sanitize_key((string) $validation['plan_code']),
                        false
                    );
                }
            }
        }

        if (is_array($auth_bootstrap) && empty($auth_bootstrap['success'])) {
            $validation['auth_bootstrap_warning'] = (string) ($auth_bootstrap['error'] ?? 'Authentication bootstrap failed after access key verification.');
        }

        wp_send_json_success($validation);
    }

    public function handle_activation_conversation() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }

        $conversation_language = isset($_POST['conversation_language']) ? sanitize_key(wp_unslash((string) $_POST['conversation_language'])) : 'en';
        if (!in_array($conversation_language, array('en', 'ro', 'bg'), true)) {
            $conversation_language = 'en';
        }

        $user_message = isset($_POST['user_message']) ? sanitize_textarea_field(wp_unslash((string) $_POST['user_message'])) : '';
        $site_host = isset($_POST['site_host']) ? sanitize_text_field(wp_unslash((string) $_POST['site_host'])) : '';

        $result = $this->aysa_broker_request('POST', '/v1/conversation/activation/message', array(
            'conversation_language' => $conversation_language,
            'user_message' => $user_message,
            'site_host' => $site_host,
            'activation_url' => 'https://app.aysa.ai/websites',
        ), 45);

        if (is_wp_error($result)) {
            wp_send_json_error(array('message' => $result->get_error_message()));
            return;
        }
        if (!is_array($result) || empty($result['ok'])) {
            wp_send_json_error(array(
                'message' => isset($result['error']) ? (string) $result['error'] : 'Activation conversation request failed.',
            ));
            return;
        }

        wp_send_json_success($result);
    }

    public function handle_agent8v3_message() {
        $this->handle_agent8v3_proxy('POST', '/v1/agent8v3/message', array(
            'message' => isset($_POST['message']) ? sanitize_textarea_field(wp_unslash((string) $_POST['message'])) : '',
            'state_version' => isset($_POST['state_version']) ? intval(wp_unslash((string) $_POST['state_version'])) : 0,
        ));
    }

    public function handle_agent8v3_action() {
        $action_payload = array();
        if (isset($_POST['payload'])) {
            $decoded_payload = json_decode(wp_unslash((string) $_POST['payload']), true);
            if (is_array($decoded_payload)) {
                $action_payload = $decoded_payload;
            }
        }
        $agent_action = isset($_POST['agent_action']) ? sanitize_key(wp_unslash((string) $_POST['agent_action'])) : '';
        if (
            $agent_action === 'execute_technical_fix_plan'
            && sanitize_key((string) ($action_payload['module'] ?? '')) === 'internal_linking'
        ) {
            $action_payload['technical_internal_linking_snapshot'] = $this->aysa_adjustments_get_technical_internal_linking_snapshot(
                $this->aysa_resolve_project_id_with_local_fallback(0)
            );
        }
        if (
            $agent_action === 'execute_technical_fix_plan'
            && sanitize_key((string) ($action_payload['module'] ?? '')) === 'image_alt'
        ) {
            $action_payload['technical_site_audit'] = $this->aysa_agent8_transport_reduce_technical_site_audit_snapshot(
                $this->aysa_adjustments_get_technical_site_audit_snapshot(
                    $this->aysa_resolve_project_id_with_local_fallback(0)
                )
            );
        }
        if ($agent_action === 'prepare_image_alt_text') {
            $action_payload['website'] = home_url('/');
            $action_payload['target_url'] = home_url('/');
            $action_payload['plugin_projection_target_url'] = home_url('/');
            $action_payload['technical_site_audit'] = $this->aysa_agent8_transport_reduce_technical_site_audit_snapshot(
                $this->aysa_adjustments_get_technical_site_audit_snapshot(
                    $this->aysa_resolve_project_id_with_local_fallback(0)
                )
            );
        }
        if ($agent_action === 'generate_onpage_meta') {
            $project_id = $this->aysa_resolve_project_id_with_local_fallback(0);
            $action_payload = $this->aysa_agent8_prepare_onpage_meta_generation_payload($action_payload, $project_id);
        }
        $this->handle_agent8v3_proxy('POST', '/v1/agent8v3/action', array(
            'action' => $agent_action,
            'payload' => $action_payload,
            'state_version' => isset($_POST['state_version']) ? intval(wp_unslash((string) $_POST['state_version'])) : 0,
        ));
    }

    public function handle_agent8v3_modal() {
        $filters = array();
        if (isset($_POST['filters'])) {
            $decoded_filters = json_decode(wp_unslash((string) $_POST['filters']), true);
            if (is_array($decoded_filters)) {
                $filters = $decoded_filters;
            }
        }
        $modal_key = isset($_POST['modal_key']) ? sanitize_key(wp_unslash((string) $_POST['modal_key'])) : '';
        $payload = array(
            'modal_key' => $modal_key,
            'state_version' => isset($_POST['state_version']) ? intval(wp_unslash((string) $_POST['state_version'])) : 0,
            'page' => isset($_POST['page']) ? max(1, intval(wp_unslash((string) $_POST['page']))) : 1,
            'per_page' => isset($_POST['per_page']) ? max(5, min(100, intval(wp_unslash((string) $_POST['per_page'])))) : 25,
            'filters' => $filters,
        );
        if ($modal_key === 'technical_sitemap_robots') {
            $payload['technical_sitemap_robots_snapshot'] = $this->aysa_technical_build_sitemap_robots_audit($this->aysa_technical_resolve_target_url());
        }
        if ($modal_key === 'technical_internal_linking') {
            $payload['technical_internal_linking_snapshot'] = $this->aysa_adjustments_get_technical_internal_linking_snapshot(
                $this->aysa_resolve_project_id_with_local_fallback(0)
            );
        }
        if ($modal_key === 'technical_image_alt') {
            $payload['technical_site_audit'] = $this->aysa_agent8_transport_reduce_technical_site_audit_snapshot(
                $this->aysa_adjustments_get_technical_site_audit_snapshot(
                    $this->aysa_resolve_project_id_with_local_fallback(0)
                )
            );
        }
        if ($modal_key === 'onpage_seo') {
            $project_id = $this->aysa_resolve_project_id_with_local_fallback(0);
            $payload['onpage_workspace_snapshot'] = $this->aysa_agent8_get_onpage_workspace_snapshot(
                $project_id
            );
            $payload['onpage_execution_snapshot'] = $this->aysa_agent8v3_latest_onpage_execution_snapshot();
            $payload = $this->aysa_agent8_attach_onpage_keyword_mapping_context($payload, $project_id);
        }
        $this->handle_agent8v3_proxy('POST', '/v1/agent8v3/modal', $payload);
    }

    public function handle_agent8v3_approval() {
        $this->handle_agent8v3_proxy('POST', '/v1/agent8v3/approval', array(
            'approval' => isset($_POST['approval']) ? sanitize_key(wp_unslash((string) $_POST['approval'])) : '',
            'state_version' => isset($_POST['state_version']) ? intval(wp_unslash((string) $_POST['state_version'])) : 0,
        ));
    }

    public function handle_agent8v3_state() {
        $project_id = $this->aysa_agent8v3_project_id();
        $site_host = $this->aysa_agent8v3_site_host();
        $query = http_build_query(array(
            'project_id' => $project_id,
            'site_host' => $site_host,
        ), '', '&');
        $this->handle_agent8v3_proxy('GET', '/v1/agent8v3/state' . ($query !== '' ? '?' . $query : ''), array());
    }

    public function handle_agent8v3_reset() {
        $this->handle_agent8v3_proxy('POST', '/v1/agent8v3/reset', array(
            'state_version' => isset($_POST['state_version']) ? intval(wp_unslash((string) $_POST['state_version'])) : 0,
        ));
    }

    public function handle_agent8v3_qa_run() {
        if (!$this->aysa_agent8v3_qa_enabled()) {
            wp_send_json_error(array('message' => 'Agent8 V3 QA is disabled.'));
            return;
        }
        $this->handle_agent8v3_proxy('POST', '/v1/agent8v3/qa/run', array(
            'suite' => isset($_POST['suite']) ? sanitize_key(wp_unslash((string) $_POST['suite'])) : 'all',
            'project_id' => $this->aysa_agent8v3_project_id(),
            'site_host' => $this->aysa_agent8v3_site_host(),
            'wp_project_id' => intval(get_option('aysa_project_id', 0)),
            'plugin_critical_actions' => array(
                'prepare_image_alt_text',
                'execute_technical_fix_plan',
                'refresh_technical_fix_execution_status',
                'start_onpage_seo_run',
                'refresh_onpage_seo_status',
            ),
            'confirm_integration_stress' => !empty($_POST['confirm_integration_stress']),
        ));
    }

    public function handle_agent8v3_qa_clear() {
        if (!$this->aysa_agent8v3_qa_enabled()) {
            wp_send_json_error(array('message' => 'Agent8 V3 QA is disabled.'));
            return;
        }
        $this->handle_agent8v3_proxy('POST', '/v1/agent8v3/qa/clear', array());
    }

    public function handle_agent8v3_qa_log() {
        if (!$this->aysa_agent8v3_qa_enabled()) {
            wp_send_json_error(array('message' => 'Agent8 V3 QA is disabled.'));
            return;
        }
        $events = array();
        if (isset($_POST['events'])) {
            $decoded_events = json_decode(wp_unslash((string) $_POST['events']), true);
            if (is_array($decoded_events)) {
                $events = $decoded_events;
            }
        }
        $this->handle_agent8v3_proxy('POST', '/v1/agent8v3/qa/log', array(
            'events' => $events,
        ));
    }

    public function handle_agent8v3_qa_export() {
        if (!$this->aysa_agent8v3_qa_enabled()) {
            wp_send_json_error(array('message' => 'Agent8 V3 QA is disabled.'));
            return;
        }
        $this->handle_agent8v3_proxy('GET', '/v1/agent8v3/qa/export', array());
    }

    public function handle_agent8v3_qa_legacy_sync() {
        if (!$this->aysa_agent8v3_qa_enabled()) {
            wp_send_json_error(array('message' => 'Agent8 V3 QA is disabled.'));
            return;
        }
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!$this->aysa_current_user_can_workspace()) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }

        $query = http_build_query(array(
            'project_id' => $this->aysa_agent8v3_project_id(),
            'site_host' => $this->aysa_agent8v3_site_host(),
        ));
        $response = $this->aysa_agent8v3_api_request('GET', '/v1/agent8v3/state?' . $query, array());
        if (is_wp_error($response)) {
            wp_send_json_error(array(
                'message' => $response->get_error_message(),
                'code' => $response->get_error_code(),
            ));
            return;
        }
        $state = is_array($response['state'] ?? null) ? $response['state'] : array();
        wp_send_json_success($this->aysa_agent8v3_verify_legacy_sync($state));
    }

    private function handle_agent8v3_proxy($method, $path, $payload) {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!$this->aysa_current_user_can_workspace()) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }

        $method = strtoupper((string) $method);
        $project_id = $this->aysa_agent8v3_project_id();
        $site_host = $this->aysa_agent8v3_site_host();
        $payload = is_array($payload) ? $payload : array();
        if ($method !== 'GET') {
            $payload['project_id'] = $project_id;
            $payload['site_host'] = $site_host;
            $payload['wp_user_id'] = get_current_user_id();
            $payload['wp_project_id'] = intval(get_option('aysa_project_id', 0));
            $payload['return_url'] = admin_url('admin.php?page=aysa-agent8-v3-lab');
            $payload = $this->aysa_agent8v3_maybe_attach_technical_fix_execution_status($payload);
        }

        $response = $this->aysa_agent8v3_api_request($method, (string) $path, $payload);
        if (is_wp_error($response)) {
            $error_payload = array(
                'error' => $response->get_error_code(),
                'message' => $response->get_error_message(),
                'code' => $response->get_error_code(),
            );
            $error_data = $response->get_error_data();
            if (is_array($error_data) && !empty($error_data)) {
                $error_payload['diagnostic'] = $error_data;
            }
            wp_send_json_error($error_payload);
            return;
        }

        if (!empty($response['ok'])) {
            if (!empty($response['legacy_commit_plan']) && is_array($response['legacy_commit_plan'])) {
                $legacy_commit = $this->aysa_agent8v3_apply_legacy_commit_plan($response['legacy_commit_plan'], $payload, $response);
                $response['legacy_commit'] = $legacy_commit;
                if (!isset($response['projection']) || !is_array($response['projection'])) {
                    $response['projection'] = array();
                }
                $response['projection']['legacy_commit'] = array(
                    'status' => (string) ($legacy_commit['status'] ?? 'unknown'),
                    'action' => (string) ($legacy_commit['action'] ?? ''),
                    'payload_hash' => (string) ($legacy_commit['payload_hash'] ?? ''),
                );
                if (($legacy_commit['status'] ?? '') === 'failed') {
                    $legacy_error = sanitize_text_field((string) ($legacy_commit['error'] ?? 'Legacy sync failed.'));
                    $existing_message = isset($response['projection']['assistant_message']) ? (string) $response['projection']['assistant_message'] : '';
                    $response['projection']['assistant_message'] = trim($existing_message . "\n\nLegacy sync failed: " . $legacy_error);
                }
                if (!isset($response['debug']) || !is_array($response['debug'])) {
                    $response['debug'] = array();
                }
                $response['debug']['legacy_commit_status'] = (string) ($legacy_commit['status'] ?? 'unknown');
            }
            if (!empty($response['content_planner_commit_plan']) && is_array($response['content_planner_commit_plan'])) {
                $content_planner_commit = $this->aysa_agent8v3_apply_content_planner_commit_plan($response['content_planner_commit_plan'], $payload);
                $response['content_planner_commit'] = $content_planner_commit;
                if (!isset($response['projection']) || !is_array($response['projection'])) {
                    $response['projection'] = array();
                }
                $response['projection']['content_planner_commit'] = array(
                    'status' => (string) ($content_planner_commit['status'] ?? 'unknown'),
                    'payload_hash' => (string) ($content_planner_commit['payload_hash'] ?? ''),
                    'accepted_count' => intval($content_planner_commit['accepted_count'] ?? 0),
                );
                if (($content_planner_commit['status'] ?? '') === 'failed') {
                    $existing_message = isset($response['projection']['assistant_message']) ? (string) $response['projection']['assistant_message'] : '';
                    $response['projection']['assistant_message'] = trim($existing_message . "\n\nContent Planner sync failed: " . sanitize_text_field((string) ($content_planner_commit['error'] ?? 'unknown_error')));
                }
                if (!isset($response['debug']) || !is_array($response['debug'])) {
                    $response['debug'] = array();
                }
                $response['debug']['content_planner_commit_status'] = (string) ($content_planner_commit['status'] ?? 'unknown');
                unset($response['content_planner_commit_plan']['payload']['rows']);
            }
            if (!empty($response['technical_fix_execution_plan']) && is_array($response['technical_fix_execution_plan'])) {
                $technical_fix_execution = $this->aysa_agent8v3_apply_technical_fix_execution_plan($response['technical_fix_execution_plan'], $payload);
                $response['technical_fix_execution'] = $technical_fix_execution;
                if (!isset($response['projection']) || !is_array($response['projection'])) {
                    $response['projection'] = array();
                }
                $response['projection']['technical_fix_execution'] = array(
                    'status' => (string) ($technical_fix_execution['status'] ?? 'unknown'),
                    'module' => (string) ($technical_fix_execution['module'] ?? ''),
                    'payload_hash' => (string) ($technical_fix_execution['payload_hash'] ?? ''),
                    'applied_count' => intval($technical_fix_execution['applied_count'] ?? 0),
                    'failed_count' => intval($technical_fix_execution['failed_count'] ?? 0),
                    'manual_needed_count' => intval($technical_fix_execution['manual_needed_count'] ?? 0),
                    'queued_count' => intval($technical_fix_execution['queued_count'] ?? 0),
                    'job_id' => (string) ($technical_fix_execution['job_id'] ?? ''),
                    'worker_ref' => (string) ($technical_fix_execution['worker_ref'] ?? ''),
                    'worker_status' => (string) ($technical_fix_execution['worker_status'] ?? ''),
                );
                if (($technical_fix_execution['status'] ?? '') === 'failed') {
                    $existing_message = isset($response['projection']['assistant_message']) ? (string) $response['projection']['assistant_message'] : '';
                    $response['projection']['assistant_message'] = trim($existing_message . "\n\nTechnical fix execution failed: " . sanitize_text_field((string) ($technical_fix_execution['error'] ?? 'unknown_error')));
                }
                if (!isset($response['debug']) || !is_array($response['debug'])) {
                    $response['debug'] = array();
                }
                $response['debug']['technical_fix_execution_status'] = (string) ($technical_fix_execution['status'] ?? 'unknown');
                unset($response['technical_fix_execution_plan']['payload']['fixes']);
                $sync_payload = array(
                    'project_id' => $project_id,
                    'site_host' => $site_host,
                    'wp_user_id' => get_current_user_id(),
                    'wp_project_id' => intval(get_option('aysa_project_id', 0)),
                    'agent_action' => 'refresh_technical_fix_execution_status',
                    'state_version' => intval($response['state']['state_version'] ?? 0),
                    'payload' => array(
                        'module' => (string) ($technical_fix_execution['module'] ?? ''),
                        'technical_fix_execution_result' => $technical_fix_execution,
                    ),
                );
                $sync_response = $this->aysa_agent8v3_api_request('POST', '/v1/agent8v3/action', $sync_payload);
                if (!is_wp_error($sync_response) && !empty($sync_response['ok'])) {
                    $response = $sync_response;
                    $response['technical_fix_execution'] = $technical_fix_execution;
                    if (!isset($response['projection']) || !is_array($response['projection'])) {
                        $response['projection'] = array();
                    }
                    $response['projection']['technical_fix_execution'] = array(
                        'status' => (string) ($technical_fix_execution['status'] ?? 'unknown'),
                        'module' => (string) ($technical_fix_execution['module'] ?? ''),
                        'payload_hash' => (string) ($technical_fix_execution['payload_hash'] ?? ''),
                        'applied_count' => intval($technical_fix_execution['applied_count'] ?? 0),
                        'failed_count' => intval($technical_fix_execution['failed_count'] ?? 0),
                        'manual_needed_count' => intval($technical_fix_execution['manual_needed_count'] ?? 0),
                        'queued_count' => intval($technical_fix_execution['queued_count'] ?? 0),
                        'job_id' => (string) ($technical_fix_execution['job_id'] ?? ''),
                        'worker_ref' => (string) ($technical_fix_execution['worker_ref'] ?? ''),
                        'worker_status' => (string) ($technical_fix_execution['worker_status'] ?? ''),
                    );
                }
            }
            if (!empty($response['onpage_execution_plan']) && is_array($response['onpage_execution_plan'])) {
                if (!isset($response['debug']) || !is_array($response['debug'])) {
                    $response['debug'] = array();
                }
                $response['debug']['onpage_execution_plan_ignored'] = 'deprecated_sync_plan';
                unset($response['onpage_execution_plan']['payload']['accepted_items']);
            }
            $response = $this->aysa_agent8v3_maybe_sync_onpage_projection($response, $payload);
            wp_send_json_success($response);
            return;
        }

        wp_send_json_error(array(
            'error' => isset($response['error']) ? (string) $response['error'] : 'agent8_v3_request_failed',
            'message' => isset($response['error']) ? (string) $response['error'] : 'Agent8 V3 request failed.',
            'payload' => $response,
        ));
    }

    private function aysa_agent8v3_api_request($method, $path, $payload) {
        $cfg = $this->aysa_get_broker_config();
        if (is_wp_error($cfg)) {
            return $cfg;
        }

        $method = strtoupper((string) $method);
        $parts = explode('?', (string) $path, 2);
        $signature_path = $parts[0];
        $body = ($method === 'GET') ? '' : wp_json_encode(is_array($payload) ? $payload : array());
        if (!is_string($body)) {
            $body = '{}';
        }

        $timestamp = (string) time();
        try {
            $nonce = bin2hex(random_bytes(16));
        } catch (Exception $e) {
            $nonce = wp_generate_password(32, false, false);
        }

        $canonical = implode("\n", array($method, $signature_path, hash('sha256', $body), $timestamp, $nonce));
        $headers = array(
            'Accept' => 'application/json',
            'Content-Type' => 'application/json',
            'X-Aysa-Key' => $cfg['key_id'],
            'X-Aysa-Timestamp' => $timestamp,
            'X-Aysa-Nonce' => $nonce,
            'X-Aysa-Signature' => hash_hmac('sha256', $canonical, $cfg['key_secret']),
        );
        $args = array(
            'timeout' => 60,
            'connect_timeout' => 15,
            'connecttimeout' => 15,
            'redirection' => 0,
            'method' => $method,
            'headers' => $headers,
        );
        if ($method !== 'GET') {
            $args['body'] = $body;
        }

        $http_response = wp_remote_request(rtrim((string) $cfg['base_url'], '/') . $path, $args);
        if (is_wp_error($http_response)) {
            return $http_response;
        }

        $raw_body = (string) wp_remote_retrieve_body($http_response);
        $decoded = json_decode($raw_body, true);
        if (!is_array($decoded)) {
            $body_preview = trim(preg_replace('/\s+/', ' ', wp_strip_all_tags($raw_body)));
            $body_preview = preg_replace('/(key|secret|token|signature|password)(["\\\']?\\s*[:=]\\s*["\\\']?)[^\\s"\\\']+/i', '$1$2[redacted]', $body_preview);
            return new WP_Error('agent8v3_invalid_json', 'Agent8 V3 API returned invalid JSON.', array(
                'error_code' => 'agent8v3_invalid_json',
                'http_code' => (int) wp_remote_retrieve_response_code($http_response),
                'body_preview' => substr($body_preview, 0, 500),
            ));
        }
        return $decoded;
    }

    private function aysa_agent8v3_verify_legacy_sync($state) {
        $state = is_array($state) ? $state : array();
        $data = is_array($state['data'] ?? null) ? $state['data'] : array();
        $decisions = is_array($state['decisions'] ?? null) ? $state['decisions'] : array();
        $checks = array();
        $user_id = $this->aysa_agent8v3_legacy_user_id();
        $project_id = intval(get_option('aysa_project_id', 0));

        $activation = is_array($data['activation'] ?? null) ? $data['activation'] : array();
        if (!empty($decisions['activation_verified']) || !empty($activation)) {
            $checks[] = $this->aysa_agent8v3_legacy_sync_check('activation', 'aysa_active_access_key', 'present', true, get_option($this->aysa_test_access_key_option_name(), '') !== '');
            $checks[] = $this->aysa_agent8v3_legacy_sync_check('activation', 'aysa_project_id', 'project_id', intval($activation['project_id'] ?? 0), intval(get_option('aysa_project_id', 0)));
            $checks[] = $this->aysa_agent8v3_legacy_sync_check('activation', 'aysa_website_id', 'website_id', intval($activation['website_id'] ?? 0), intval(get_option('aysa_website_id', 0)));
            $checks[] = $this->aysa_agent8v3_legacy_sync_check('activation', 'aysa_wallet_default_plan', 'plan_code', (string) ($activation['plan_code'] ?? ''), (string) get_option('aysa_wallet_default_plan', ''));
        }

        $business = is_array($data['business_profile'] ?? null) ? $data['business_profile'] : array();
        $profile = is_array($business['confirmed_profile'] ?? null) ? $business['confirmed_profile'] : array();
        if (!empty($profile)) {
            $expected = array(
                'companyName' => (string) ($profile['company_name'] ?? $profile['companyName'] ?? ''),
                'websiteUrl' => (string) ($profile['website'] ?? $profile['websiteUrl'] ?? ''),
                'websiteType' => (string) ($profile['website_type'] ?? $profile['websiteType'] ?? ''),
                'industry' => (string) ($profile['industry'] ?? $profile['business_type'] ?? ''),
                'supportPhoneNumber' => (string) ($profile['support_phone'] ?? ''),
                'supportEmailAddress' => (string) ($profile['support_email'] ?? ''),
            );
            $checks = array_merge($checks, $this->aysa_agent8v3_legacy_sync_option_checks('business_profile', 'general', $expected, $user_id, $project_id));
        }

        $objectives = is_array($data['objectives'] ?? null) ? $data['objectives'] : array();
        if (!empty($decisions['objectives_confirmed']) || !empty($objectives['confirmed'])) {
            $expected = array(
                'objectives' => is_array($objectives['objectives'] ?? null) ? array_values($objectives['objectives']) : array(),
                'custom_objectives' => (string) ($objectives['custom_objectives'] ?? ''),
            );
            $checks = array_merge($checks, $this->aysa_agent8v3_legacy_sync_option_checks('objectives', 'objectives', $expected, $user_id, $project_id));
        }

        $seo = is_array($data['seo_profile'] ?? null) ? $data['seo_profile'] : array();
        $seo_settings = is_array($seo['confirmed_profile'] ?? null) ? $seo['confirmed_profile'] : (is_array($seo['settings'] ?? null) ? $seo['settings'] : array());
        if (!empty($decisions['seo_profile_confirmed']) || !empty($seo['confirmed'])) {
            $checks = array_merge($checks, $this->aysa_agent8v3_legacy_sync_option_checks('seo_profile', 'seo-settings', $seo_settings, $user_id, $project_id));
        }

        $competition = is_array($data['competition_keyword'] ?? null) ? $data['competition_keyword'] : array();
        $proposal = is_array($competition['proposal'] ?? null) ? $competition['proposal'] : array();
        if (!empty($decisions['competition_keyword_confirmed']) || !empty($competition['confirmed'])) {
            $expected = array(
                'keywords' => is_array($proposal['keywords'] ?? null) ? array_values($proposal['keywords']) : array(),
                'competitors' => is_array($proposal['competitors'] ?? null) ? array_values($proposal['competitors']) : array(),
                'seed_keywords' => is_array($proposal['seed_keywords'] ?? null) ? array_values($proposal['seed_keywords']) : array(),
                'excluded_competitors' => is_array($proposal['excluded_competitors'] ?? null) ? array_values($proposal['excluded_competitors']) : array(),
            );
            $checks = array_merge($checks, $this->aysa_agent8v3_legacy_sync_option_checks('competition_keyword', 'competition', $expected, $user_id, $project_id));
        }

        $tone = is_array($data['tone_of_voice'] ?? null) ? $data['tone_of_voice'] : array();
        if (!empty($decisions['tone_of_voice_selected']) || trim((string) ($tone['selected'] ?? '')) !== '') {
            $selected = (string) ($tone['selected'] ?? $decisions['tone_of_voice_selected'] ?? '');
            $checks = array_merge($checks, $this->aysa_agent8v3_legacy_sync_option_checks('tone_of_voice', 'tone-of-voice', array('tone' => $selected, 'selectedTone' => $selected), $user_id, $project_id));
        }

        $setup = is_array($data['setup_profile'] ?? null) ? $data['setup_profile'] : array();
        if (!empty($decisions['setup_completed_committed']) || !empty($setup['legacy_setup_completed'])) {
            $checks = array_merge($checks, $this->aysa_agent8v3_legacy_sync_option_checks('setup_completed', 'setup-completed', array('completed' => true), $user_id, $project_id));
        }

        $checked = count(array_filter($checks, static function ($check) {
            return ($check['status'] ?? '') !== 'skipped';
        }));
        $mismatched = count(array_filter($checks, static function ($check) {
            return ($check['status'] ?? '') === 'mismatch';
        }));
        $skipped = count(array_filter($checks, static function ($check) {
            return ($check['status'] ?? '') === 'skipped';
        }));

        return array(
            'ok' => $mismatched === 0,
            'summary' => array(
                'checked' => $checked,
                'matched' => $checked - $mismatched,
                'mismatched' => $mismatched,
                'skipped' => $skipped,
            ),
            'checks' => $checks,
        );
    }

    private function aysa_agent8v3_legacy_sync_option_checks($stage, $step_key, $expected, $user_id, $project_id) {
        $checks = array();
        $targets = array();
        if ($user_id > 0) {
            $targets[] = 'aysa_wizard_' . $step_key . '_' . $user_id;
        }
        if ($project_id > 0) {
            $targets[] = 'aysa_wizard_' . $step_key . '_project_' . $project_id;
        }
        if (empty($targets)) {
            return array($this->aysa_agent8v3_legacy_sync_check($stage, 'legacy_options', 'target', 'available', 'missing', 'skipped'));
        }
        foreach ($targets as $option_name) {
            $legacy = get_option($option_name, array());
            $legacy = is_array($legacy) ? $legacy : array();
            foreach ($expected as $field => $value) {
                $checks[] = $this->aysa_agent8v3_legacy_sync_check($stage, $option_name, (string) $field, $value, $legacy[$field] ?? null);
            }
        }
        return $checks;
    }

    private function aysa_agent8v3_legacy_sync_check($stage, $option_key, $field, $v3_value, $legacy_value, $forced_status = '') {
        $v3_normalized = $this->aysa_agent8v3_legacy_compare_value($v3_value);
        $legacy_normalized = $this->aysa_agent8v3_legacy_compare_value($legacy_value);
        $status = $forced_status !== '' ? $forced_status : ($v3_normalized === $legacy_normalized ? 'matched' : 'mismatch');
        return array(
            'stage' => sanitize_key((string) $stage),
            'option_key' => sanitize_text_field((string) $option_key),
            'field' => sanitize_text_field((string) $field),
            'v3_value' => $v3_normalized,
            'legacy_value' => $legacy_normalized,
            'status' => $status,
        );
    }

    private function aysa_agent8v3_legacy_compare_value($value) {
        if (is_bool($value)) {
            return $value ? 'true' : 'false';
        }
        if (is_array($value)) {
            $normalized = array_values(array_map('strval', $value));
            sort($normalized);
            return implode('|', $normalized);
        }
        return trim((string) $value);
    }

    private function aysa_agent8v3_legacy_user_id() {
        $auth_manager = class_exists('Aysa_Auth_Manager') ? Aysa_Auth_Manager::get_instance() : null;
        $auth_status = $auth_manager && method_exists($auth_manager, 'is_authenticated') ? $auth_manager->is_authenticated() : array();
        $user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : intval(get_option('aysa_user_id', 0));
        return $user_id > 0 ? $user_id : get_current_user_id();
    }

    private function aysa_agent8v3_apply_legacy_commit_plan($plan, $request_payload, $api_response) {
        $plan = is_array($plan) ? $plan : array();
        $action = sanitize_key((string) ($plan['action'] ?? ''));
        $payload_hash = sanitize_text_field((string) ($plan['payload_hash'] ?? ''));
        $idempotency_key = sanitize_text_field((string) ($plan['idempotency_key'] ?? ''));
        $allowed = array(
            'commit_activation_to_wp',
            'commit_business_profile_to_legacy',
            'commit_objectives_to_legacy',
            'commit_seo_profile_to_legacy',
            'commit_competition_keyword_to_legacy',
            'commit_tone_of_voice_to_legacy',
            'commit_tone_of_voice_and_setup_completed_to_legacy',
            'commit_setup_completed_to_legacy',
        );
        $result = array(
            'action' => $action,
            'payload_hash' => $payload_hash,
            'status' => 'skipped',
            'option_keys' => array(),
        );

        if (!in_array($action, $allowed, true) || $idempotency_key === '') {
            $result['status'] = 'rejected';
            $result['error'] = 'legacy_commit_plan_not_allowlisted';
            $this->aysa_agent8v3_log_legacy_commit($plan, $result);
            return $result;
        }

        $applied = get_option('aysa_agent8v3_legacy_commit_idempotency', array());
        $applied = is_array($applied) ? $applied : array();
        if (!empty($applied[$idempotency_key])) {
            $result['status'] = 'already_synced';
            $result['option_keys'] = is_array($applied[$idempotency_key]['option_keys'] ?? null) ? $applied[$idempotency_key]['option_keys'] : array();
            $this->aysa_agent8v3_log_legacy_commit($plan, $result);
            return $result;
        }

        try {
            if ($action === 'commit_activation_to_wp') {
                $result = $this->aysa_agent8v3_commit_activation_to_legacy($plan, $request_payload);
            } elseif ($action === 'commit_business_profile_to_legacy') {
                $result = $this->aysa_agent8v3_commit_business_profile_to_legacy($plan);
            } elseif ($action === 'commit_objectives_to_legacy') {
                $result = $this->aysa_agent8v3_commit_objectives_to_legacy($plan);
            } elseif ($action === 'commit_seo_profile_to_legacy') {
                $result = $this->aysa_agent8v3_commit_setup_step_to_legacy($plan, 'seo-settings');
            } elseif ($action === 'commit_competition_keyword_to_legacy') {
                $result = $this->aysa_agent8v3_commit_setup_step_to_legacy($plan, 'competition');
            } elseif ($action === 'commit_tone_of_voice_to_legacy') {
                $result = $this->aysa_agent8v3_commit_setup_step_to_legacy($plan, 'tone-of-voice');
            } elseif ($action === 'commit_tone_of_voice_and_setup_completed_to_legacy') {
                $result = $this->aysa_agent8v3_commit_tone_and_setup_completed_to_legacy($plan);
            } elseif ($action === 'commit_setup_completed_to_legacy') {
                $result = $this->aysa_agent8v3_commit_setup_step_to_legacy($plan, 'setup-completed');
            }
            $result['action'] = $action;
            $result['payload_hash'] = $payload_hash;
            $result['status'] = 'synced';
            $applied[$idempotency_key] = array(
                'action' => $action,
                'payload_hash' => $payload_hash,
                'option_keys' => is_array($result['option_keys'] ?? null) ? $result['option_keys'] : array(),
                'timestamp' => current_time('mysql'),
            );
            if (count($applied) > 100) {
                $applied = array_slice($applied, -100, null, true);
            }
            update_option('aysa_agent8v3_legacy_commit_idempotency', $applied, false);
        } catch (Throwable $e) {
            $result['status'] = 'failed';
            $result['error'] = $e->getMessage();
        }

        $this->aysa_agent8v3_log_legacy_commit($plan, $result);
        return $result;
    }

    private function aysa_agent8v3_commit_activation_to_legacy($plan, $request_payload) {
        $payload = is_array($plan['payload'] ?? null) ? $plan['payload'] : array();
        $site_host = sanitize_text_field((string) ($plan['site_host'] ?? $this->aysa_agent8v3_site_host()));
        $access_key = $this->aysa_agent8v3_extract_activation_key_from_payload($request_payload);
        if ($access_key === '') {
            throw new RuntimeException('activation_key_not_available_for_legacy_commit');
        }

        $validation = $this->aysa_validate_test_access_key($access_key, $site_host);
        if (empty($validation['valid'])) {
            throw new RuntimeException((string) ($validation['code'] ?? 'access_key_invalid'));
        }

        $plan_project_id = intval($payload['project_id'] ?? 0);
        $validation_project_id = intval($validation['project_id'] ?? 0);
        if ($plan_project_id > 0 && $validation_project_id > 0 && $plan_project_id !== $validation_project_id) {
            throw new RuntimeException('activation_project_id_mismatch');
        }

        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }
        if (!class_exists('Aysa_Auth_Manager')) {
            throw new RuntimeException('auth_manager_unavailable');
        }
        $auth_manager = Aysa_Auth_Manager::get_instance();
        $auth_bootstrap = $auth_manager->verify_and_store_secret_key($access_key, $site_host);
        if (!is_array($auth_bootstrap) || empty($auth_bootstrap['success'])) {
            throw new RuntimeException('auth_manager_rejected_activation_key');
        }

        update_option($this->aysa_test_access_key_option_name(), $access_key, false);
        if ($validation_project_id > 0) {
            update_option('aysa_project_id', $validation_project_id, false);
        }
        if (!empty($validation['website_id'])) {
            update_option('aysa_website_id', intval($validation['website_id']), false);
        }
        update_option('aysa_user_secret_key', $access_key, false);
        update_option('aysa_user_id', intval($auth_bootstrap['aysa_user_id'] ?? 0), false);
        update_option('aysa_auth_domain', $site_host !== '' ? $site_host : wp_parse_url(home_url(), PHP_URL_HOST), false);

        if (!empty($validation['plan_code'])) {
            update_option('aysa_wallet_default_plan', sanitize_key((string) $validation['plan_code']), false);
            if (class_exists('Aysa_Wallet_Manager')) {
                $project_id = intval(get_option('aysa_project_id', 0));
                if ($project_id > 0) {
                    Aysa_Wallet_Manager::get_instance()->apply_plan_to_project($project_id, sanitize_key((string) $validation['plan_code']), false);
                }
            }
        }

        return array(
            'status' => 'synced',
            'option_keys' => array(
                $this->aysa_test_access_key_option_name(),
                'aysa_project_id',
                'aysa_website_id',
                'aysa_user_secret_key',
                'aysa_user_id',
                'aysa_auth_domain',
                'aysa_wallet_default_plan',
            ),
            'auth_bootstrap' => is_array($auth_bootstrap) && !empty($auth_bootstrap['success']) ? 'success' : 'not_confirmed',
        );
    }

    private function aysa_agent8v3_commit_business_profile_to_legacy($plan) {
        $payload = is_array($plan['payload'] ?? null) ? $plan['payload'] : array();
        $general = is_array($payload['general'] ?? null) ? $payload['general'] : array();
        if (empty($general)) {
            throw new RuntimeException('business_profile_legacy_payload_empty');
        }

        $auth_manager = class_exists('Aysa_Auth_Manager') ? Aysa_Auth_Manager::get_instance() : null;
        $auth_status = $auth_manager && method_exists($auth_manager, 'is_authenticated') ? $auth_manager->is_authenticated() : array();
        $user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : intval(get_option('aysa_user_id', 0));
        if ($user_id <= 0) {
            $user_id = get_current_user_id();
        }

        $project_id = intval(get_option('aysa_project_id', 0));
        $local_setup_state = array('general' => $general);
        $this->aysa_persist_local_setup_state_snapshot($user_id, $local_setup_state, $project_id);

        $option_keys = array();
        if ($user_id > 0) {
            $option_keys[] = 'aysa_wizard_general_' . $user_id;
        }
        if ($project_id > 0) {
            $option_keys[] = 'aysa_wizard_general_project_' . $project_id;
        }

        return array(
            'status' => 'synced',
            'option_keys' => $option_keys,
        );
    }

    private function aysa_agent8v3_commit_objectives_to_legacy($plan) {
        $payload = is_array($plan['payload'] ?? null) ? $plan['payload'] : array();
        $objectives = is_array($payload['objectives'] ?? null) ? array_values(array_intersect($payload['objectives'], array('visitors', 'awareness', 'leads', 'sales'))) : array();
        if (empty($objectives)) {
            throw new RuntimeException('objectives_legacy_payload_empty');
        }

        $project_id = intval(get_option('aysa_project_id', 0));
        $auth_manager = class_exists('Aysa_Auth_Manager') ? Aysa_Auth_Manager::get_instance() : null;
        $auth_status = $auth_manager && method_exists($auth_manager, 'is_authenticated') ? $auth_manager->is_authenticated() : array();
        $user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : intval(get_option('aysa_user_id', 0));
        if ($user_id <= 0) {
            $user_id = get_current_user_id();
        }

        $legacy_objectives = array(
            'objectives' => $objectives,
            'custom_objectives' => sanitize_textarea_field((string) ($payload['custom_objectives'] ?? '')),
        );
        $local_setup_state = array('objectives' => $legacy_objectives);
        $this->aysa_persist_local_setup_state_snapshot($user_id, $local_setup_state, $project_id);

        $option_keys = array();
        if ($user_id > 0) {
            $option_keys[] = 'aysa_wizard_objectives_' . $user_id;
        }
        if ($project_id > 0) {
            $option_keys[] = 'aysa_wizard_objectives_project_' . $project_id;
        }

        return array(
            'status' => 'synced',
            'option_keys' => $option_keys,
        );
    }

    private function aysa_agent8v3_commit_setup_step_to_legacy($plan, $step_key) {
        $step_key = sanitize_key((string) $step_key);
        $allowed_steps = array('seo-settings', 'competition', 'tone-of-voice', 'setup-completed');
        if (!in_array($step_key, $allowed_steps, true)) {
            throw new RuntimeException('setup_step_legacy_commit_not_allowlisted');
        }
        $payload = is_array($plan['payload'] ?? null) ? $plan['payload'] : array();
        $step_payload = is_array($payload[$step_key] ?? null) ? $payload[$step_key] : array();
        if (empty($step_payload)) {
            throw new RuntimeException($step_key . '_legacy_payload_empty');
        }

        $project_id = intval(get_option('aysa_project_id', 0));
        $auth_manager = class_exists('Aysa_Auth_Manager') ? Aysa_Auth_Manager::get_instance() : null;
        $auth_status = $auth_manager && method_exists($auth_manager, 'is_authenticated') ? $auth_manager->is_authenticated() : array();
        $user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : intval(get_option('aysa_user_id', 0));
        if ($user_id <= 0) {
            $user_id = get_current_user_id();
        }

        $this->aysa_persist_local_setup_state_snapshot($user_id, array($step_key => $step_payload), $project_id);

        $option_keys = array();
        if ($user_id > 0) {
            $option_keys[] = 'aysa_wizard_' . $step_key . '_' . $user_id;
        }
        if ($project_id > 0) {
            $option_keys[] = 'aysa_wizard_' . $step_key . '_project_' . $project_id;
        }

        return array(
            'status' => 'synced',
            'option_keys' => $option_keys,
        );
    }

    private function aysa_agent8v3_commit_tone_and_setup_completed_to_legacy($plan) {
        $tone_result = $this->aysa_agent8v3_commit_setup_step_to_legacy($plan, 'tone-of-voice');
        $setup_result = $this->aysa_agent8v3_commit_setup_step_to_legacy($plan, 'setup-completed');
        return array(
            'status' => 'synced',
            'option_keys' => array_values(array_unique(array_merge(
                is_array($tone_result['option_keys'] ?? null) ? $tone_result['option_keys'] : array(),
                is_array($setup_result['option_keys'] ?? null) ? $setup_result['option_keys'] : array()
            ))),
        );
    }

    private function aysa_agent8v3_maybe_sync_onpage_projection($response, $request_payload) {
        if (!is_array($response) || empty($response['ok'])) {
            return $response;
        }

        $agent_action = sanitize_key((string) ($request_payload['action'] ?? $request_payload['agent_action'] ?? ''));
        if (!in_array($agent_action, array('start_onpage_seo_run', 'refresh_onpage_seo_status', 'generate_onpage_meta', 'execute_onpage_plan'), true)) {
            return $response;
        }

        $adapter = is_array($response['onpage_seo_adapter'] ?? null) ? $response['onpage_seo_adapter'] : array();
        $persistable = is_array($adapter['state_persistable'] ?? null) ? $adapter['state_persistable'] : array();
        if (empty($persistable) && is_array($response['state']['data']['onpage_seo'] ?? null)) {
            $persistable = $response['state']['data']['onpage_seo'];
        }

        $projections = is_array($persistable['projection'] ?? null) ? $persistable['projection'] : array();
        if (empty($projections)) {
            return $response;
        }

        $service = $this->aysa_onpage_workspace_service();
        if (!$service || !method_exists($service, 'apply_projection')) {
            return $response;
        }

        $project_id = intval($request_payload['wp_project_id'] ?? get_option('aysa_project_id', 0));
        $run_ids = is_array($persistable['run_ids'] ?? null) ? $persistable['run_ids'] : array();
        $sync_results = array();
        $onpage_execution_snapshot = array();

        foreach ($projections as $run_type => $projection) {
            $has_inventory_rows = is_array($projection) && !empty($projection['inventory_rows']) && is_array($projection['inventory_rows']);
            $has_meta_apply_plan = is_array($projection) && !empty($projection['meta_apply_plan']) && is_array($projection['meta_apply_plan']);
            if (!$has_inventory_rows && !$has_meta_apply_plan) {
                continue;
            }
            $run_id = intval($run_ids[$run_type] ?? $projection['run_id'] ?? 0);
            $sync_results[$run_type] = $service->apply_projection($project_id, $run_id, $projection);
            if ($run_type === 'meta_apply' && is_array($sync_results[$run_type]['meta_apply'] ?? null) && $sync_results[$run_type]['meta_apply'] !== array()) {
                $onpage_execution_snapshot = $sync_results[$run_type]['meta_apply'];
            }
        }

        if (empty($sync_results)) {
            return $response;
        }

        $response['local_onpage_projection_sync'] = $sync_results;
        if (!isset($response['debug']) || !is_array($response['debug'])) {
            $response['debug'] = array();
        }
        $response['debug']['local_onpage_projection_sync'] = implode(',', array_keys($sync_results));
        if ($onpage_execution_snapshot !== array()) {
            if (!isset($response['projection']) || !is_array($response['projection'])) {
                $response['projection'] = array();
            }
            $response['onpage_execution'] = $onpage_execution_snapshot;
            $response['projection']['onpage_execution'] = array(
                'status' => sanitize_key((string) ($onpage_execution_snapshot['status'] ?? 'unknown')),
                'operation' => sanitize_key((string) ($onpage_execution_snapshot['operation'] ?? 'apply_meta')),
                'run_id' => intval($onpage_execution_snapshot['run_id'] ?? 0),
                'payload_hash' => sanitize_text_field((string) ($onpage_execution_snapshot['payload_hash'] ?? '')),
                'accepted_count' => intval($onpage_execution_snapshot['accepted_count'] ?? 0),
                'applied_count' => intval($onpage_execution_snapshot['applied_count'] ?? 0),
                'failed_count' => intval($onpage_execution_snapshot['failed_count'] ?? 0),
                'skipped_count' => intval($onpage_execution_snapshot['skipped_count'] ?? 0),
                'errors' => array_values(array_slice(is_array($onpage_execution_snapshot['errors'] ?? null) ? $onpage_execution_snapshot['errors'] : array(), 0, 20)),
                'item_results' => array_values(array_slice(is_array($onpage_execution_snapshot['item_results'] ?? null) ? $onpage_execution_snapshot['item_results'] : array(), 0, 500)),
            );
            $state_version = intval($response['state']['state_version'] ?? 0);
            if ($state_version > 0) {
                $sync_payload = array(
                    'project_id' => $this->aysa_agent8v3_project_id(),
                    'site_host' => $this->aysa_agent8v3_site_host(),
                    'wp_user_id' => get_current_user_id(),
                    'wp_project_id' => $project_id,
                    'action' => 'refresh_onpage_seo_status',
                    'state_version' => $state_version,
                    'payload' => array(
                        'run_ids' => $run_ids,
                        'onpage_execution_snapshot' => $onpage_execution_snapshot,
                    ),
                );
                $sync_response = $this->aysa_agent8v3_api_request('POST', '/v1/agent8v3/action', $sync_payload);
                if (!is_wp_error($sync_response) && !empty($sync_response['ok'])) {
                    $response = $sync_response;
                    $response['local_onpage_projection_sync'] = $sync_results;
                    $response['onpage_execution'] = $onpage_execution_snapshot;
                    if (!isset($response['projection']) || !is_array($response['projection'])) {
                        $response['projection'] = array();
                    }
                    $response['projection']['onpage_execution'] = array(
                        'status' => sanitize_key((string) ($onpage_execution_snapshot['status'] ?? 'unknown')),
                        'operation' => sanitize_key((string) ($onpage_execution_snapshot['operation'] ?? 'apply_meta')),
                        'run_id' => intval($onpage_execution_snapshot['run_id'] ?? 0),
                        'payload_hash' => sanitize_text_field((string) ($onpage_execution_snapshot['payload_hash'] ?? '')),
                        'accepted_count' => intval($onpage_execution_snapshot['accepted_count'] ?? 0),
                        'applied_count' => intval($onpage_execution_snapshot['applied_count'] ?? 0),
                        'failed_count' => intval($onpage_execution_snapshot['failed_count'] ?? 0),
                        'skipped_count' => intval($onpage_execution_snapshot['skipped_count'] ?? 0),
                        'errors' => array_values(array_slice(is_array($onpage_execution_snapshot['errors'] ?? null) ? $onpage_execution_snapshot['errors'] : array(), 0, 20)),
                        'item_results' => array_values(array_slice(is_array($onpage_execution_snapshot['item_results'] ?? null) ? $onpage_execution_snapshot['item_results'] : array(), 0, 500)),
                    );
                }
            }
        }
        return $response;
    }

    private function aysa_agent8v3_extract_activation_key_from_payload($request_payload) {
        $request_payload = is_array($request_payload) ? $request_payload : array();
        $candidates = array();
        if (isset($request_payload['message'])) {
            $candidates[] = (string) $request_payload['message'];
        }
        if (isset($request_payload['payload']) && is_array($request_payload['payload'])) {
            foreach (array('activation_key', 'access_key', 'value') as $key) {
                if (isset($request_payload['payload'][$key])) {
                    $candidates[] = (string) $request_payload['payload'][$key];
                }
            }
        }
        foreach (array('activation_key', 'access_key', 'value') as $key) {
            if (isset($request_payload[$key])) {
                $candidates[] = (string) $request_payload[$key];
            }
        }
        foreach ($candidates as $candidate) {
            $candidate = trim($candidate);
            if ($candidate === '') {
                continue;
            }
            if (preg_match('/(AYSA-[A-Z0-9-]{8,}|aysa_(?:pk|sk)_[a-z0-9]{16,}|[A-Za-z0-9\\-]{32,})/i', $candidate, $matches)) {
                return sanitize_text_field($matches[1]);
            }
            if (strlen($candidate) >= 12 && strlen($candidate) <= 256 && strpos($candidate, ' ') === false) {
                return sanitize_text_field($candidate);
            }
        }
        return '';
    }

    private function aysa_agent8v3_log_legacy_commit($plan, $result) {
        $events = get_option('aysa_agent8v3_legacy_commits', array());
        $events = is_array($events) ? $events : array();
        array_unshift($events, array(
            'action' => sanitize_key((string) ($result['action'] ?? $plan['action'] ?? '')),
            'state_version' => intval($plan['state_version'] ?? 0),
            'payload_hash' => sanitize_text_field((string) ($result['payload_hash'] ?? $plan['payload_hash'] ?? '')),
            'option_keys' => is_array($result['option_keys'] ?? null) ? array_values(array_map('sanitize_text_field', $result['option_keys'])) : array(),
            'status' => sanitize_key((string) ($result['status'] ?? 'unknown')),
            'error' => isset($result['error']) ? sanitize_text_field((string) $result['error']) : null,
            'timestamp' => current_time('mysql'),
        ));
        update_option('aysa_agent8v3_legacy_commits', array_slice($events, 0, 50), false);
    }

    private function aysa_agent8v3_apply_content_planner_commit_plan($plan, $request_payload) {
        global $wpdb;
        $plan = is_array($plan) ? $plan : array();
        $idempotency_key = sanitize_text_field((string) ($plan['idempotency_key'] ?? ''));
        $payload_hash = sanitize_text_field((string) ($plan['payload_hash'] ?? ''));
        $rows = is_array($plan['payload']['rows'] ?? null) ? $plan['payload']['rows'] : array();
        $result = array(
            'action' => 'approve_content_plan',
            'payload_hash' => $payload_hash,
            'accepted_count' => count($rows),
            'inserted_count' => 0,
            'skipped_count' => 0,
            'status' => 'skipped',
            'option_keys' => array('wp_aysa_content_strategies', 'aysa_agent8v3_content_planner_commit_idempotency'),
        );
        if ($idempotency_key === '' || empty($rows)) {
            $result['status'] = 'rejected';
            $result['error'] = 'content_planner_commit_plan_invalid';
            $this->aysa_agent8v3_log_content_planner_commit($plan, $result);
            return $result;
        }

        $applied = get_option('aysa_agent8v3_content_planner_commit_idempotency', array());
        $applied = is_array($applied) ? $applied : array();
        if (!empty($applied[$idempotency_key])) {
            $result = array_merge($result, is_array($applied[$idempotency_key]) ? $applied[$idempotency_key] : array());
            $result['status'] = 'already_synced';
            $this->aysa_agent8v3_log_content_planner_commit($plan, $result);
            return $result;
        }

        $table = $wpdb->prefix . 'aysa_content_strategies';
        if ($wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table)) !== $table) {
            if (class_exists('Aysa_Content_Strategy_Manager')) {
                $manager = new Aysa_Content_Strategy_Manager();
                if (method_exists($manager, 'create_table')) {
                    $manager->create_table();
                }
            }
        }
        if ($wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table)) !== $table) {
            $result['status'] = 'failed';
            $result['error'] = 'content_strategy_table_missing';
            $this->aysa_agent8v3_log_content_planner_commit($plan, $result);
            return $result;
        }

        $project_id = intval($request_payload['wp_project_id'] ?? get_option('aysa_project_id', 0));
        $plan_batch = 'agent8v3-' . substr($payload_hash, 0, 12);
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $title = sanitize_text_field((string) ($row['title'] ?? ''));
            if ($title === '') {
                continue;
            }
            $primary_keyword = sanitize_text_field((string) ($row['primary_keyword'] ?? ''));
            $target_url = esc_url_raw((string) ($row['target_url'] ?? ''));
            $content_fingerprint = hash('sha256', strtolower($title . '|' . $primary_keyword . '|' . $target_url));
            $existing = $wpdb->get_var($wpdb->prepare(
                "SELECT id FROM {$table} WHERE project_id = %d AND content_fingerprint = %s LIMIT 1",
                $project_id,
                $content_fingerprint
            ));
            if (!empty($existing)) {
                $result['skipped_count']++;
                continue;
            }
            $keywords = array();
            if (is_array($row['keywords'] ?? null)) {
                foreach ($row['keywords'] as $keyword) {
                    $keyword = sanitize_text_field((string) $keyword);
                    if ($keyword !== '') {
                        $keywords[] = $keyword;
                    }
                }
            }
            if ($primary_keyword !== '') {
                array_unshift($keywords, $primary_keyword);
            }
            $keywords = array_values(array_unique($keywords));
            $scheduled_for = $this->aysa_agent8v3_mysql_datetime((string) ($row['scheduled_for'] ?? ''));
            $inserted = $wpdb->insert($table, array(
                'project_id' => $project_id,
                'title' => $title,
                'keywords' => wp_json_encode($keywords),
                'intent' => '',
                'associated_page' => $target_url,
                'strategy_type' => 'agent8v3_research',
                'strategy_meta' => wp_json_encode(array(
                    'agent8v3' => true,
                    'run_id' => intval($plan['run_id'] ?? 0),
                    'content_item_id' => sanitize_text_field((string) ($row['content_item_id'] ?? '')),
                    'source' => sanitize_text_field((string) ($row['source'] ?? '')),
                )),
                'canonical_topic' => $primary_keyword,
                'content_fingerprint' => $content_fingerprint,
                'recommended_page_type' => sanitize_key((string) ($row['content_type'] ?? 'article')),
                'content_type' => sanitize_key((string) ($row['content_type'] ?? 'article')),
                'execution_stage' => 'planned',
                'status' => 'scheduled',
                'scheduled_for' => $scheduled_for,
                'automation_status' => 'queued',
                'automation_attempts' => 0,
                'plan_batch' => $plan_batch,
                'automation_updated_at' => current_time('mysql'),
                'created_at' => current_time('mysql'),
            ));
            if ($inserted) {
                $result['inserted_count']++;
            }
        }

        if ($result['inserted_count'] > 0 && !wp_next_scheduled('aysa_process_planned_content_now')) {
            wp_schedule_single_event(time() + 5, 'aysa_process_planned_content_now');
        }
        $result['status'] = 'synced';
        $applied[$idempotency_key] = array(
            'payload_hash' => $payload_hash,
            'accepted_count' => count($rows),
            'inserted_count' => intval($result['inserted_count']),
            'skipped_count' => intval($result['skipped_count']),
            'option_keys' => $result['option_keys'],
            'timestamp' => current_time('mysql'),
        );
        update_option('aysa_agent8v3_content_planner_commit_idempotency', array_slice($applied, -100, null, true), false);
        $this->aysa_agent8v3_log_content_planner_commit($plan, $result);
        return $result;
    }

    private function aysa_agent8v3_mysql_datetime($value) {
        $timestamp = strtotime((string) $value);
        if (!$timestamp) {
            $timestamp = time() + 300;
        }
        return gmdate('Y-m-d H:i:s', $timestamp);
    }

    private function aysa_agent8v3_log_content_planner_commit($plan, $result) {
        $events = get_option('aysa_agent8v3_content_planner_commits', array());
        $events = is_array($events) ? $events : array();
        array_unshift($events, array(
            'action' => 'approve_content_plan',
            'state_version' => intval($plan['state_version'] ?? 0),
            'accepted_count' => intval($result['accepted_count'] ?? 0),
            'rejected_count' => intval($plan['rejected_count'] ?? 0),
            'payload_hash' => sanitize_text_field((string) ($result['payload_hash'] ?? $plan['payload_hash'] ?? '')),
            'status' => sanitize_key((string) ($result['status'] ?? 'unknown')),
            'error' => isset($result['error']) ? sanitize_text_field((string) $result['error']) : null,
            'timestamp' => current_time('mysql'),
        ));
        update_option('aysa_agent8v3_content_planner_commits', array_slice($events, 0, 50), false);
    }

    private function aysa_agent8v3_apply_onpage_execution_plan($plan, $request_payload) {
        $plan = is_array($plan) ? $plan : array();
        $idempotency_key = sanitize_text_field((string) ($plan['idempotency_key'] ?? ''));
        $payload_hash = sanitize_text_field((string) ($plan['payload_hash'] ?? ''));
        $section = sanitize_key((string) ($plan['section'] ?? ''));
        $operation = sanitize_key((string) ($plan['operation'] ?? ''));
        $items = is_array($plan['payload']['accepted_items'] ?? null) ? $plan['payload']['accepted_items'] : array();
        $result = array(
            'action' => 'execute_onpage_plan',
            'section' => $section,
            'operation' => $operation,
            'payload_hash' => $payload_hash,
            'accepted_count' => count($items),
            'applied_count' => 0,
            'failed_count' => 0,
            'skipped_count' => 0,
            'status' => 'skipped',
            'errors' => array(),
            'item_results' => array(),
            'option_keys' => array('aysa_agent8v3_onpage_execution_idempotency', 'aysa_agent8v3_onpage_executions'),
        );
        if ($idempotency_key === '' || $section !== 'meta_snippets' || $operation !== 'apply_meta' || empty($items)) {
            $result['status'] = 'rejected';
            $result['error'] = 'onpage_execution_plan_invalid';
            $this->aysa_agent8v3_log_onpage_execution($plan, $result);
            return $result;
        }

        $applied = get_option('aysa_agent8v3_onpage_execution_idempotency', array());
        $applied = is_array($applied) ? $applied : array();
        $previous_item_results = array();
        if (!empty($applied[$idempotency_key])) {
            $previous_result = is_array($applied[$idempotency_key]) ? $applied[$idempotency_key] : array();
            $previous_item_results = is_array($previous_result['item_results'] ?? null) ? $previous_result['item_results'] : array();
            $previous_failed = intval($previous_result['failed_count'] ?? 0);
            $previous_skipped = intval($previous_result['skipped_count'] ?? 0);
            if ($previous_failed <= 0 && $previous_skipped <= 0 && intval($previous_result['applied_count'] ?? 0) > 0) {
                $result = array_merge($result, $previous_result);
                $result['status'] = 'already_synced';
                $this->aysa_agent8v3_log_onpage_execution($plan, $result);
                return $result;
            }
        }

        $project_id = intval($request_payload['wp_project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            $result['status'] = 'failed';
            $result['error'] = 'project_id_required';
            $this->aysa_agent8v3_log_onpage_execution($plan, $result);
            return $result;
        }

        $previous_applied = array();
        foreach ($previous_item_results as $previous_item) {
            if (!is_array($previous_item) || (string) ($previous_item['status'] ?? '') !== 'applied') {
                continue;
            }
            $previous_item_id = sanitize_text_field((string) ($previous_item['item_id'] ?? ''));
            if ($previous_item_id !== '') {
                $previous_applied[$previous_item_id] = true;
            }
        }

        foreach ($items as $item) {
            if (!is_array($item)) {
                $result['skipped_count']++;
                $result['item_results'][] = array(
                    'status' => 'skipped',
                    'reason' => 'invalid_item',
                );
                continue;
            }
            $entity_key = sanitize_text_field((string) ($item['entity_key'] ?? ''));
            $item_id = sanitize_text_field((string) ($item['item_id'] ?? $entity_key));
            if ($entity_key === '') {
                $result['skipped_count']++;
                $result['item_results'][] = array(
                    'item_id' => $item_id,
                    'entity_key' => '',
                    'status' => 'skipped',
                    'reason' => 'entity_key_required',
                );
                continue;
            }
            if ($item_id !== '' && isset($previous_applied[$item_id])) {
                $result['skipped_count']++;
                $result['item_results'][] = array(
                    'item_id' => $item_id,
                    'entity_key' => $entity_key,
                    'status' => 'already_synced',
                    'reason' => '',
                );
                continue;
            }
            $apply = $this->aysa_agent8_apply_onpage_entity_operation($project_id, array(
                'entity_key' => $entity_key,
            ), 'apply_meta');
            if (!empty($apply['applied'])) {
                $result['applied_count']++;
                $result['item_results'][] = array(
                    'item_id' => $item_id,
                    'entity_key' => $entity_key,
                    'status' => 'applied',
                    'reason' => '',
                );
            } else {
                $reason = sanitize_key((string) ($apply['reason'] ?? 'onpage_apply_failed'));
                $result['failed_count']++;
                $result['errors'][] = $reason;
                $result['item_results'][] = array(
                    'item_id' => $item_id,
                    'entity_key' => $entity_key,
                    'status' => 'failed',
                    'reason' => $reason,
                );
            }
        }

        $result['status'] = $result['failed_count'] > 0 && $result['applied_count'] === 0 ? 'failed' : 'completed';
        $applied[$idempotency_key] = array(
            'section' => $section,
            'operation' => $operation,
            'payload_hash' => $payload_hash,
            'accepted_count' => count($items),
            'applied_count' => intval($result['applied_count'] ?? 0),
            'failed_count' => intval($result['failed_count'] ?? 0),
            'skipped_count' => intval($result['skipped_count'] ?? 0),
            'errors' => array_values(array_slice(is_array($result['errors'] ?? null) ? $result['errors'] : array(), 0, 20)),
            'item_results' => array_slice($result['item_results'], 0, 100),
            'option_keys' => $result['option_keys'],
            'timestamp' => current_time('mysql'),
        );
        update_option('aysa_agent8v3_onpage_execution_idempotency', array_slice($applied, -100, null, true), false);
        $this->aysa_agent8v3_log_onpage_execution($plan, $result);
        return $result;
    }

    private function aysa_agent8v3_log_onpage_execution($plan, $result) {
        $events = get_option('aysa_agent8v3_onpage_executions', array());
        $events = is_array($events) ? $events : array();
        array_unshift($events, array(
            'action' => 'execute_onpage_plan',
            'section' => sanitize_key((string) ($plan['section'] ?? '')),
            'operation' => sanitize_key((string) ($plan['operation'] ?? '')),
            'accepted_count' => intval($result['accepted_count'] ?? 0),
            'applied_count' => intval($result['applied_count'] ?? 0),
            'failed_count' => intval($result['failed_count'] ?? 0),
            'skipped_count' => intval($result['skipped_count'] ?? 0),
            'payload_hash' => sanitize_text_field((string) ($result['payload_hash'] ?? $plan['payload_hash'] ?? '')),
            'status' => sanitize_key((string) ($result['status'] ?? 'unknown')),
            'error' => isset($result['error']) ? sanitize_text_field((string) $result['error']) : null,
            'errors' => array_values(array_slice(is_array($result['errors'] ?? null) ? $result['errors'] : array(), 0, 20)),
            'item_results' => array_values(array_slice(is_array($result['item_results'] ?? null) ? $result['item_results'] : array(), 0, 500)),
            'timestamp' => current_time('mysql'),
        ));
        update_option('aysa_agent8v3_onpage_executions', array_slice($events, 0, 50), false);
    }

    private function aysa_agent8v3_latest_onpage_execution_snapshot() {
        $events = get_option('aysa_agent8v3_onpage_executions', array());
        $events = is_array($events) ? $events : array();
        return is_array($events[0] ?? null) ? $events[0] : array();
    }

    private function aysa_agent8v3_apply_technical_fix_execution_plan($plan, $request_payload) {
        $plan = is_array($plan) ? $plan : array();
        $idempotency_key = sanitize_text_field((string) ($plan['idempotency_key'] ?? ''));
        $payload_hash = sanitize_text_field((string) ($plan['payload_hash'] ?? ''));
        $module = sanitize_key((string) ($plan['module'] ?? ''));
        $fixes = is_array($plan['payload']['fixes'] ?? null) ? $plan['payload']['fixes'] : array();
        $result = array(
            'action' => 'execute_technical_fix_plan',
            'module' => $module,
            'payload_hash' => $payload_hash,
            'accepted_count' => count($fixes),
            'applied_count' => 0,
            'failed_count' => 0,
            'manual_needed_count' => 0,
            'skipped_count' => 0,
            'status' => 'skipped',
            'errors' => array(),
            'option_keys' => array('aysa_agent8v3_technical_fix_execution_idempotency', 'aysa_agent8v3_technical_fix_executions'),
        );
        if ($idempotency_key === '' || $module === '' || empty($fixes)) {
            $result['status'] = 'rejected';
            $result['error'] = 'technical_fix_execution_plan_invalid';
            $this->aysa_agent8v3_log_technical_fix_execution($plan, $result);
            return $result;
        }

        $applied = get_option('aysa_agent8v3_technical_fix_execution_idempotency', array());
        $applied = is_array($applied) ? $applied : array();
        if (!empty($applied[$idempotency_key])) {
            $result = array_merge($result, is_array($applied[$idempotency_key]) ? $applied[$idempotency_key] : array());
            $result['status'] = 'already_synced';
            $this->aysa_agent8v3_log_technical_fix_execution($plan, $result);
            return $result;
        }

        $project_id = intval($request_payload['wp_project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            $result['status'] = 'failed';
            $result['error'] = 'project_id_required';
            $this->aysa_agent8v3_log_technical_fix_execution($plan, $result);
            return $result;
        }

        if ($module === 'schema') {
            $result = $this->aysa_agent8v3_enqueue_schema_fix_execution_job($project_id, $plan, $fixes, $result);
            $applied[$idempotency_key] = array(
                'module' => $module,
                'payload_hash' => $payload_hash,
                'accepted_count' => count($fixes),
                'applied_count' => intval($result['applied_count'] ?? 0),
                'failed_count' => intval($result['failed_count'] ?? 0),
                'manual_needed_count' => intval($result['manual_needed_count'] ?? 0),
                'queued_count' => intval($result['queued_count'] ?? 0),
                'job_id' => sanitize_text_field((string) ($result['job_id'] ?? '')),
                'worker_ref' => sanitize_text_field((string) ($result['worker_ref'] ?? '')),
                'worker_status' => sanitize_key((string) ($result['worker_status'] ?? '')),
                'option_keys' => $result['option_keys'],
                'timestamp' => current_time('mysql'),
            );
            update_option('aysa_agent8v3_technical_fix_execution_idempotency', array_slice($applied, -100, null, true), false);
            $this->aysa_agent8v3_log_technical_fix_execution($plan, $result);
            return $result;
        }

        if ($module === 'internal_linking') {
            $result = $this->aysa_agent8v3_apply_internal_linking_fix_execution($project_id, $plan, $fixes, $result);
            $applied[$idempotency_key] = array(
                'module' => $module,
                'payload_hash' => $payload_hash,
                'accepted_count' => count($fixes),
                'applied_count' => intval($result['applied_count'] ?? 0),
                'failed_count' => intval($result['failed_count'] ?? 0),
                'manual_needed_count' => intval($result['manual_needed_count'] ?? 0),
                'skipped_count' => intval($result['skipped_count'] ?? 0),
                'option_keys' => $result['option_keys'],
                'timestamp' => current_time('mysql'),
            );
            update_option('aysa_agent8v3_technical_fix_execution_idempotency', array_slice($applied, -100, null, true), false);
            $this->aysa_agent8v3_log_technical_fix_execution($plan, $result);
            return $result;
        }

        foreach ($fixes as $fix) {
            if (!is_array($fix)) {
                continue;
            }
            $operation = sanitize_key((string) ($fix['operation'] ?? ''));
            $execution_result = $this->aysa_agent8v3_execute_technical_fix($project_id, $module, $operation, $fix);
            if (!empty($execution_result['manual_needed'])) {
                $result['manual_needed_count']++;
            } elseif (!empty($execution_result['applied']) || !empty($execution_result['already_done'])) {
                $result['applied_count']++;
            } else {
                $result['failed_count']++;
                $result['errors'][] = array(
                    'fix_id' => sanitize_text_field((string) ($fix['fix_id'] ?? '')),
                    'reason' => sanitize_key((string) ($execution_result['reason'] ?? 'technical_fix_failed')),
                    'message' => sanitize_text_field((string) ($execution_result['message'] ?? '')),
                );
            }
        }

        $result['status'] = $result['failed_count'] > 0 && $result['applied_count'] === 0 ? 'failed' : 'synced';
        $applied[$idempotency_key] = array(
            'module' => $module,
            'payload_hash' => $payload_hash,
            'accepted_count' => count($fixes),
            'applied_count' => intval($result['applied_count']),
            'failed_count' => intval($result['failed_count']),
            'manual_needed_count' => intval($result['manual_needed_count']),
            'option_keys' => $result['option_keys'],
            'timestamp' => current_time('mysql'),
        );
        update_option('aysa_agent8v3_technical_fix_execution_idempotency', array_slice($applied, -100, null, true), false);
        $this->aysa_agent8v3_log_technical_fix_execution($plan, $result);
        return $result;
    }

    private function aysa_agent8v3_maybe_attach_technical_fix_execution_status($payload) {
        $agent_action = is_array($payload) ? (string) ($payload['agent_action'] ?? ($payload['action'] ?? '')) : '';
        if (!is_array($payload) || $agent_action !== 'refresh_technical_fix_execution_status') {
            return $payload;
        }
        $action_payload = isset($payload['payload']) && is_array($payload['payload']) ? $payload['payload'] : array();
        $module = sanitize_key((string) ($action_payload['module'] ?? ''));
        if ($module !== 'schema' || !empty($action_payload['technical_fix_execution_result'])) {
            return $payload;
        }
        $status = $this->aysa_agent8v3_latest_schema_fix_execution_status($module);
        if (!empty($status)) {
            $action_payload['technical_fix_execution_result'] = $status;
            $payload['payload'] = $action_payload;
        }
        return $payload;
    }

    private function aysa_agent8v3_latest_schema_fix_execution_status($module) {
        $module = sanitize_key((string) $module);
        $applied = get_option('aysa_agent8v3_technical_fix_execution_idempotency', array());
        $applied = is_array($applied) ? array_reverse($applied, true) : array();
        foreach ($applied as $entry) {
            if (!is_array($entry) || sanitize_key((string) ($entry['module'] ?? '')) !== $module) {
                continue;
            }
            $job_id = sanitize_text_field((string) ($entry['job_id'] ?? ''));
            if ($job_id === '') {
                continue;
            }
            return $this->aysa_agent8v3_schema_job_execution_status($job_id, $entry);
        }
        return array();
    }

    private function aysa_agent8v3_schema_job_execution_status($job_id, array $entry = array()) {
        $job_id = sanitize_text_field((string) $job_id);
        $job = $this->aysa_technical_schema_read_job($job_id);
        if (is_array($job) && in_array((string) ($job['status'] ?? ''), array('queued', 'running'), true) && intval($job['lock_until'] ?? 0) <= time()) {
            $job = $this->aysa_technical_schema_process_job($job_id, 4);
        }
        if (!is_array($job)) {
            return array(
                'action' => 'execute_technical_fix_plan',
                'module' => 'schema',
                'payload_hash' => sanitize_text_field((string) ($entry['payload_hash'] ?? '')),
                'status' => 'failed',
                'job_id' => $job_id,
                'worker_ref' => sanitize_text_field((string) ($entry['worker_ref'] ?? '')),
                'worker_status' => 'missing',
                'applied_count' => 0,
                'failed_count' => 1,
                'manual_needed_count' => 0,
                'queued_count' => 0,
                'skipped_count' => 0,
                'errors' => array(array('fix_id' => '', 'reason' => 'schema_job_missing', 'message' => 'Schema job not found.')),
            );
        }

        $status = sanitize_key((string) ($job['status'] ?? 'queued'));
        if (in_array($status, array('completed', 'failed', 'cancelled'), true)) {
            $this->aysa_project_jobs_finalize(
                intval($job['project_id'] ?? 0),
                $this->aysa_project_jobs_make_worker_ref('technical_schema', 'schema_job', (string) ($job['job_id'] ?? $job_id)),
                $status === 'completed' ? 'completed' : 'failed',
                $job,
                (string) ($job['last_error'] ?? '')
            );
        }

        return array(
            'action' => 'execute_technical_fix_plan',
            'module' => 'schema',
            'payload_hash' => sanitize_text_field((string) ($entry['payload_hash'] ?? '')),
            'status' => $status,
            'job_id' => $job_id,
            'worker_ref' => $this->aysa_project_jobs_make_worker_ref('technical_schema', 'schema_job', $job_id),
            'worker_status' => $status,
            'applied_count' => intval($job['successful'] ?? 0),
            'failed_count' => intval($job['failed'] ?? 0),
            'manual_needed_count' => intval(($job['blocked'] ?? 0) + ($job['needs_fields'] ?? 0) + ($job['needs_takeover'] ?? 0)),
            'queued_count' => in_array($status, array('queued', 'running'), true) ? intval($job['total'] ?? 0) : 0,
            'skipped_count' => intval($job['skipped'] ?? 0),
            'errors' => !empty($job['last_error']) ? array(array(
                'fix_id' => '',
                'reason' => 'schema_job_error',
                'message' => sanitize_text_field((string) $job['last_error']),
            )) : array(),
        );
    }

    private function aysa_agent8v3_enqueue_schema_fix_execution_job($project_id, $plan, array $fixes, array $base_result) {
        $items = array();
        $scope_lookup = $this->aysa_technical_schema_scope_lookup();
        foreach ($fixes as $fix) {
            if (!is_array($fix)) {
                continue;
            }
            $operation = sanitize_key((string) ($fix['operation'] ?? ''));
            if ($operation !== 'enqueue_schema_unified_graph_job') {
                $base_result['manual_needed_count']++;
                continue;
            }
            $url = esc_url_raw((string) ($fix['url'] ?? $fix['target_url'] ?? ''));
            if ($url === '') {
                $base_result['failed_count']++;
                $base_result['errors'][] = array(
                    'fix_id' => sanitize_text_field((string) ($fix['fix_id'] ?? '')),
                    'reason' => 'schema_url_required',
                    'message' => 'Schema fix is missing URL.',
                );
                continue;
            }
            $canonical_plan = isset($fix['canonical_graph_plan']) && is_array($fix['canonical_graph_plan']) ? $fix['canonical_graph_plan'] : array();
            $schema_type = sanitize_key((string) ($fix['schema_type'] ?? $canonical_plan['primary_schema_type'] ?? $fix['primary_recommended_node'] ?? 'webpage'));
            $item = array(
                'url' => $url,
                'title' => sanitize_text_field((string) ($fix['title'] ?? $url)),
                'schema_type' => $schema_type,
                'primary_recommended_node' => $schema_type,
                'page_family' => sanitize_key((string) ($fix['page_family'] ?? '')),
                'entity_group' => sanitize_key((string) ($fix['entity_group'] ?? '')),
                'agent8v3_fix_id' => sanitize_text_field((string) ($fix['fix_id'] ?? '')),
                'canonical_graph_plan' => $canonical_plan,
            );
            $item = $this->aysa_technical_schema_resolve_scope_item($item, $scope_lookup);
            if (!empty($item['url'])) {
                $items[$item['url']] = $item;
            }
        }

        if (empty($items)) {
            $base_result['status'] = $base_result['failed_count'] > 0 ? 'failed' : 'skipped';
            $base_result['error'] = $base_result['failed_count'] > 0 ? 'schema_job_items_invalid' : 'schema_job_no_executable_items';
            return $base_result;
        }

        $mode = 'rewrite_recommended';
        $job = $this->aysa_technical_schema_create_job($project_id, $mode, 'selection', array_values($items), 'worker');
        $this->aysa_technical_schema_schedule_job((string) $job['job_id'], 1);

        $base_result['status'] = 'queued';
        $base_result['queued_count'] = count($items);
        $base_result['accepted_count'] = count($fixes);
        $base_result['job_id'] = sanitize_text_field((string) ($job['job_id'] ?? ''));
        $base_result['worker_ref'] = $this->aysa_project_jobs_make_worker_ref('technical_schema', 'schema_job', (string) ($job['job_id'] ?? ''));
        $base_result['worker_status'] = sanitize_key((string) ($job['status'] ?? 'queued'));
        $base_result['option_keys'][] = $this->aysa_technical_schema_job_key((string) ($job['job_id'] ?? ''));
        return $base_result;
    }

    private function aysa_agent8v3_apply_internal_linking_fix_execution($project_id, $plan, array $fixes, array $result) {
        $candidates = array();
        $opportunity_ids = array();
        foreach ($fixes as $fix) {
            if (!is_array($fix)) {
                continue;
            }
            $source_url = esc_url_raw((string) ($fix['source_url'] ?? ''));
            $target_url = esc_url_raw((string) ($fix['target_url'] ?? ''));
            $anchor = sanitize_text_field((string) ($fix['anchor_text'] ?? $fix['keyword'] ?? ''));
            if ($source_url === '' || $target_url === '' || $anchor === '') {
                $result['manual_needed_count']++;
                continue;
            }
            $opportunity_id = intval($fix['opportunity_id'] ?? 0);
            if ($opportunity_id > 0) {
                $opportunity_ids[] = $opportunity_id;
            }
            $candidates[] = array(
                'url' => $target_url,
                'target_url' => $target_url,
                'target_title' => sanitize_text_field((string) ($fix['target_title'] ?? '')),
                'suggested_anchor_text' => $anchor,
                'anchor_text' => $anchor,
                'keyword' => sanitize_text_field((string) ($fix['keyword'] ?? $anchor)),
                'safe_to_apply' => true,
                'reason' => sanitize_text_field((string) ($fix['reason'] ?? 'agent8v3_internal_linking')),
                'suggested_source_pages' => array(array(
                    'url' => $source_url,
                    'source_url' => $source_url,
                    'title' => sanitize_text_field((string) ($fix['source_page'] ?? '')),
                    'anchor_text' => $anchor,
                )),
            );
        }

        if (empty($candidates)) {
            $result['status'] = 'rejected';
            $result['error'] = 'empty_fix_plan';
            return $result;
        }

        $package = array(
            'package_id' => 'agent8v3_internal_linking_' . intval($project_id) . '_' . substr(md5((string) ($plan['payload_hash'] ?? wp_json_encode($candidates))), 0, 12),
            'project_id' => intval($project_id),
            'source' => 'agent8v3_technical_fix_plan',
            'total_candidates' => count($candidates),
            'safe_auto_apply_count' => count($candidates),
            'manual_review_needed_count' => intval($result['manual_needed_count'] ?? 0),
            'candidates' => $candidates,
        );
        $apply_result = $this->aysa_technical_site_audit_apply_internal_linking_package($project_id, $package);
        $result['applied_count'] = intval($apply_result['applied_count'] ?? 0);
        $result['skipped_count'] = intval($apply_result['skipped_count'] ?? 0);
        $result['failed_count'] = intval($apply_result['failed_count'] ?? 0);
        $result['manual_needed_count'] += intval($apply_result['manual_needed_count'] ?? 0);
        $result['results'] = is_array($apply_result['results'] ?? null) ? $apply_result['results'] : array();
        $result['errors'] = !empty($apply_result['reason']) ? array(sanitize_text_field((string) $apply_result['reason'])) : array();
        $result['status'] = $result['failed_count'] > 0 && $result['applied_count'] <= 0 ? 'failed' : 'synced';

        $service = $this->aysa_internal_linking_workspace_service();
        if ($service && method_exists($service, 'update_row_action') && $result['applied_count'] > 0) {
            foreach (array_unique($opportunity_ids) as $opportunity_id) {
                $service->update_row_action($project_id, intval($opportunity_id), 'apply');
            }
        }

        return $result;
    }

    private function aysa_agent8v3_execute_technical_fix($project_id, $module, $operation, array $fix) {
        $module = sanitize_key((string) $module);
        $operation = sanitize_key((string) $operation);
        if ($module === 'pagespeed') {
            return array('manual_needed' => true, 'reason' => 'not_executable_yet');
        }
        if ($module === 'sitemap_robots') {
            if ($operation === 'generate_sitemap_index') {
                return $this->aysa_agent8_generate_technical_sitemap($project_id);
            }
            if ($operation === 'apply_robots_policy') {
                return $this->aysa_agent8_restore_technical_robots($project_id);
            }
            if ($operation === 'enforce_sitemap_authority') {
                return $this->aysa_agent8_enforce_aysa_sitemap_authority($project_id);
            }
            return array('manual_needed' => true, 'reason' => 'unsupported_sitemap_robots_operation');
        }
        if (($module === 'schema') && in_array($operation, array('apply_schema', 'disable_schema'), true)) {
            $technical_operation = array(
                'url' => esc_url_raw((string) ($fix['url'] ?? $fix['target_url'] ?? '')),
                'schema_type' => sanitize_key((string) ($fix['schema_type'] ?? 'webpage')),
                'schema_action' => sanitize_key((string) ($fix['schema_action'] ?? 'rewrite_recommended')),
            );
            return $operation === 'disable_schema'
                ? $this->aysa_agent8_disable_technical_schema_operation($project_id, $technical_operation)
                : $this->aysa_agent8_apply_technical_schema_operation($project_id, $technical_operation);
        }
        if (in_array($module, array('redirects', 'site_audit'), true) && $operation === 'apply_redirect') {
            return $this->aysa_agent8_apply_technical_redirect_candidate($project_id, array(
                'source_url' => esc_url_raw((string) ($fix['source_url'] ?? '')),
                'target_url' => esc_url_raw((string) ($fix['target_url'] ?? '')),
            ));
        }
        if ($module === 'redirects' && $operation === 'mark_gone') {
            return $this->aysa_agent8_mark_technical_gone_candidate($project_id, array(
                'source_url' => esc_url_raw((string) ($fix['source_url'] ?? '')),
            ));
        }
        if ($module === 'redirects' && $operation === 'ignore_404') {
            return $this->aysa_agent8_ignore_technical_404_candidate($project_id, array(
                'source_url' => esc_url_raw((string) ($fix['source_url'] ?? '')),
            ));
        }
        if ($module === 'internal_linking' && $operation === 'apply_internal_link') {
            $service = $this->aysa_internal_linking_workspace_service();
            if (!$service || !method_exists($service, 'update_row_action')) {
                return array('applied' => false, 'reason' => 'internal_linking_service_unavailable');
            }
            $opportunity_id = sanitize_text_field((string) ($fix['opportunity_id'] ?? ''));
            if ($opportunity_id === '') {
                return array('applied' => false, 'reason' => 'internal_linking_opportunity_required');
            }
            $row_result = $service->update_row_action($project_id, $opportunity_id, 'apply');
            if (!is_array($row_result) || empty($row_result['ok'])) {
                return array(
                    'applied' => false,
                    'reason' => sanitize_key((string) ($row_result['error'] ?? $row_result['reason'] ?? 'internal_linking_apply_failed')),
                    'message' => sanitize_text_field((string) ($row_result['message'] ?? '')),
                );
            }
            return array('applied' => true, 'operation' => 'apply_internal_link', 'summary' => array('opportunity_id' => $opportunity_id));
        }
        if ($module === 'image_alt' && $operation === 'site_audit_image_alt_apply') {
            $confidence_value = $fix['confidence'] ?? 100;
            $confidence_float = is_numeric($confidence_value) ? (float) $confidence_value : 100;
            $confidence = intval(round($confidence_float > 0 && $confidence_float <= 1 ? $confidence_float * 100 : $confidence_float));
            $row = array(
                'attachment_id' => intval($fix['attachment_id'] ?? $fix['image_id'] ?? 0),
                'image_url' => esc_url_raw((string) ($fix['image_url'] ?? $fix['url'] ?? '')),
                'page_url' => esc_url_raw((string) ($fix['page_url'] ?? $fix['source_url'] ?? '')),
                'decision' => 'rewrite',
                'confidence' => $confidence,
                'proposed_value' => sanitize_text_field((string) ($fix['proposed_value'] ?? $fix['proposed_alt'] ?? '')),
                'old_value' => sanitize_text_field((string) ($fix['old_value'] ?? $fix['current_alt'] ?? '')),
                'is_decorative' => !empty($fix['is_decorative']),
            );
            $package = array(
                'package_id' => 'agent8v3_image_alt_' . intval($project_id) . '_' . substr(md5(wp_json_encode($fix)), 0, 12),
                'project_id' => intval($project_id),
                'total_candidates' => 1,
                'safe_auto_apply_count' => 1,
                'manual_review_needed_count' => 0,
                'items' => array($row),
                'skipped' => array(),
            );
            $result = $this->aysa_technical_site_audit_apply_image_alt_package($project_id, $package);
            if (intval($result['applied_count'] ?? 0) > 0) {
                return array('applied' => true, 'operation' => 'site_audit_image_alt_apply', 'summary' => $result);
            }
            if (intval($result['manual_needed_count'] ?? 0) > 0) {
                return array('manual_needed' => true, 'reason' => 'image_alt_manual_needed', 'summary' => $result);
            }
            return array(
                'applied' => false,
                'reason' => sanitize_key((string) ($result['results'][0]['reason'] ?? 'image_alt_apply_failed')),
                'summary' => $result,
            );
        }
        return array('manual_needed' => true, 'reason' => 'manual_needed');
    }

    private function aysa_agent8v3_log_technical_fix_execution($plan, $result) {
        $events = get_option('aysa_agent8v3_technical_fix_executions', array());
        $events = is_array($events) ? $events : array();
        array_unshift($events, array(
            'action' => 'execute_technical_fix_plan',
            'module' => sanitize_key((string) ($result['module'] ?? $plan['module'] ?? '')),
            'state_version' => intval($plan['state_version'] ?? 0),
            'accepted_count' => intval($result['accepted_count'] ?? 0),
            'applied_count' => intval($result['applied_count'] ?? 0),
            'failed_count' => intval($result['failed_count'] ?? 0),
            'manual_needed_count' => intval($result['manual_needed_count'] ?? 0),
            'payload_hash' => sanitize_text_field((string) ($result['payload_hash'] ?? $plan['payload_hash'] ?? '')),
            'status' => sanitize_key((string) ($result['status'] ?? 'unknown')),
            'error' => isset($result['error']) ? sanitize_text_field((string) $result['error']) : null,
            'timestamp' => current_time('mysql'),
        ));
        update_option('aysa_agent8v3_technical_fix_executions', array_slice($events, 0, 50), false);
    }

    private function aysa_agent8v3_project_id() {
        $wp_project_id = intval(get_option('aysa_project_id', 0));
        if ($wp_project_id <= 0) {
            $stored_key = trim((string) get_option($this->aysa_test_access_key_option_name(), ''));
            $validation = $stored_key !== '' ? $this->aysa_validate_test_access_key($stored_key, $this->aysa_agent8v3_site_host()) : array();
            $wp_project_id = !empty($validation['valid']) && !empty($validation['project_id'])
                ? intval($validation['project_id'])
                : 0;
        }
        return $wp_project_id > 0 ? 'lab_' . $wp_project_id : 'lab_test';
    }

    private function aysa_agent8v3_site_host() {
        $site_host = (string) wp_parse_url(home_url('/'), PHP_URL_HOST);
        $site_host = strtolower(trim($site_host));
        return preg_replace('/^www\./', '', $site_host);
    }

    private function aysa_agent8v3_qa_enabled() {
        if (!current_user_can('manage_options')) {
            return false;
        }
        if (defined('AYSA_AGENT8_V3_QA_ENABLED')) {
            return (bool) AYSA_AGENT8_V3_QA_ENABLED;
        }
        $env_value = getenv('AYSA_AGENT8_V3_QA_ENABLED');
        return in_array(strtolower((string) $env_value), array('1', 'true', 'yes', 'on'), true);
    }

    public function handle_agent8_conversation() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }
        $request = $_POST;
        $request['_agent8_execution_path'] = 'sync';
        try {
            $result = $this->aysa_execute_agent8_conversation($request);
        } catch (Throwable $e) {
            $this->aysa_agent8_bridge_log('Agent8 sync execution threw.', array(
                'task' => 'agent8',
                'run_id' => '',
                'project_id' => isset($request['project_id']) ? intval($request['project_id']) : 0,
                'conversation_id' => isset($request['conversation_id']) ? sanitize_text_field(wp_unslash((string) $request['conversation_id'])) : '',
                'source' => 'sync_handler',
                'error' => $e->getMessage(),
            ));
            wp_send_json_error(array(
                'message' => 'Agent8 bridge execution failed: ' . $e->getMessage(),
                'code' => 'agent8_bridge_exception',
            ));
            return;
        }
        if (is_wp_error($result)) {
            wp_send_json_error(array('message' => $result->get_error_message()));
            return;
        }

        $this->aysa_agent8_bridge_send_json_success($result, array(
            'task' => 'agent8',
            'run_id' => '',
            'project_id' => isset($request['project_id']) ? intval($request['project_id']) : 0,
            'conversation_id' => isset($request['conversation_id']) ? sanitize_text_field(wp_unslash((string) $request['conversation_id'])) : '',
            'source' => 'sync_handler',
        ));
    }

    public function handle_agent9_conversation() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }
        $result = $this->aysa_execute_agent9_conversation($_POST);
        if (is_wp_error($result)) {
            wp_send_json_error(array('message' => $result->get_error_message()));
            return;
        }

        wp_send_json_success($result);
    }

    private function aysa_agent8_storage_key($user_id, $site_host) {
        return 'aysa_agent8_session_' . intval($user_id) . '_' . md5((string) $site_host);
    }

    private function aysa_agent9_storage_key($user_id, $site_host) {
        return 'aysa_agent9_session_' . intval($user_id) . '_' . md5((string) $site_host);
    }

    private function aysa_agent8_normalize_conversation_language($value, $default = 'en') {
        $value = strtolower(str_replace('_', '-', trim((string) $value)));
        if ($value === '') {
            return (string) $default;
        }
        $base = explode('-', $value)[0];
        if (is_string($base) && preg_match('/^[a-z]{2}$/', $base)) {
            return $base;
        }
        return (string) $default;
    }

    private function aysa_agent8_load_session($user_id, $site_host, $language) {
        $storage_key = $this->aysa_agent8_storage_key($user_id, $site_host);
        $stored = get_user_meta($user_id, $storage_key, true);
        if (is_array($stored)) {
            return $stored;
        }

        return array(
            'conversation_id' => '',
            'conversation_language' => (string) $language,
            'phase' => 'conversation',
            'current_step' => 'conversation',
            'module' => '',
            'tab' => '',
            'request_epoch' => 0,
            'state' => array(),
            'updated_at' => current_time('mysql'),
        );
    }

    private function aysa_agent9_load_session($user_id, $site_host, $language) {
        $storage_key = $this->aysa_agent9_storage_key($user_id, $site_host);
        $stored = get_user_meta($user_id, $storage_key, true);
        if (is_array($stored)) {
            return $stored;
        }

        return array(
            'conversation_id' => '',
            'conversation_language' => (string) $language,
            'phase' => 'setup',
            'current_step' => 'bootstrap',
            'module' => 'agent9',
            'tab' => 'setup',
            'state' => array(),
            'updated_at' => current_time('mysql'),
        );
    }

    private function aysa_agent8_save_session($user_id, $site_host, $language, $result, $request_epoch = 0) {
        $storage_key = $this->aysa_agent8_storage_key($user_id, $site_host);
        $stored = get_user_meta($user_id, $storage_key, true);
        $stored = is_array($stored) ? $stored : array();
        $stored_epoch = max(0, intval($stored['request_epoch'] ?? 0));
        $incoming_epoch = max(0, intval($request_epoch));
        if ($incoming_epoch > 0 && $stored_epoch > $incoming_epoch) {
            return;
        }
        $effective_language = isset($result['state']['meta']['language'])
            ? $this->aysa_agent8_normalize_conversation_language($result['state']['meta']['language'], $language)
            : $this->aysa_agent8_normalize_conversation_language($language, 'en');
        update_user_meta($user_id, $storage_key, array(
            'conversation_id' => (string) ($result['conversation_id'] ?? ''),
            'conversation_language' => (string) $effective_language,
            'phase' => sanitize_key((string) ($result['phase'] ?? 'conversation')),
            'current_step' => sanitize_key((string) ($result['current_step'] ?? 'conversation')),
            'module' => sanitize_key((string) ($result['module'] ?? '')),
            'tab' => sanitize_key((string) ($result['tab'] ?? '')),
            'request_epoch' => max($stored_epoch, $incoming_epoch),
            'state' => isset($result['state']) && is_array($result['state']) ? $result['state'] : array(),
            'updated_at' => current_time('mysql'),
        ));
    }

    private function aysa_agent9_save_session($user_id, $site_host, $language, $result) {
        $storage_key = $this->aysa_agent9_storage_key($user_id, $site_host);
        $effective_language = isset($result['state']['meta']['language'])
            ? $this->aysa_agent8_normalize_conversation_language($result['state']['meta']['language'], $language)
            : $this->aysa_agent8_normalize_conversation_language($language, 'en');
        update_user_meta($user_id, $storage_key, array(
            'conversation_id' => (string) ($result['conversation_id'] ?? ''),
            'conversation_language' => (string) $effective_language,
            'phase' => sanitize_key((string) ($result['phase'] ?? 'setup')),
            'current_step' => sanitize_key((string) ($result['current_step'] ?? 'bootstrap')),
            'module' => sanitize_key((string) ($result['module'] ?? 'agent9')),
            'tab' => sanitize_key((string) ($result['tab'] ?? 'setup')),
            'state' => isset($result['state']) && is_array($result['state']) ? $result['state'] : array(),
            'updated_at' => current_time('mysql'),
        ));
    }

    private function aysa_agent8_canonical_state_option_key($domain, $project_id) {
        return 'aysa_agent8_state_' . sanitize_key((string) $domain) . '_project_' . intval($project_id);
    }

    private function aysa_agent8_read_canonical_state($domain, $project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array();
        }

        $stored = get_option($this->aysa_agent8_canonical_state_option_key($domain, $project_id), array());
        return is_array($stored) ? $stored : array();
    }

    private function aysa_agent8_write_canonical_state($domain, $project_id, $payload) {
        $project_id = intval($project_id);
        if ($project_id <= 0 || !is_array($payload)) {
            return false;
        }

        $payload['updated_at'] = current_time('mysql');
        update_option($this->aysa_agent8_canonical_state_option_key($domain, $project_id), $payload, false);
        return true;
    }

    private function aysa_agent8_persist_canonical_project_state_patch($project_id, $patch) {
        $project_id = intval($project_id);
        $patch = is_array($patch) ? $patch : array();
        if ($project_id <= 0 || $patch === array()) {
            return array('applied' => false, 'reason' => 'empty_canonical_project_state_patch');
        }

        $result = array(
            'applied' => true,
            'domains' => array(),
        );

        $setup_state = isset($patch['setup_state']) && is_array($patch['setup_state']) ? $patch['setup_state'] : array();
        if ($setup_state !== array()) {
            $user_id = intval(get_option('aysa_user_id', 0));
            if ($user_id <= 0) {
                $user_id = intval($this->get_user_id());
            }
            if ($user_id > 0) {
                $this->aysa_persist_local_setup_state_snapshot($user_id, $setup_state, $project_id);
                $result['domains']['setup_state'] = array(
                    'stored' => true,
                    'keys' => array_values(array_keys($setup_state)),
                );
            }
        }

        $research_state = isset($patch['research_state']) && is_array($patch['research_state']) ? $patch['research_state'] : array();
        if ($research_state !== array()) {
            $this->aysa_agent8_write_canonical_state('research', $project_id, $research_state);
            $result['domains']['research_state'] = array(
                'stored' => true,
                'keys' => array_values(array_keys($research_state)),
            );
        }

        $technical_state = isset($patch['technical_state']) && is_array($patch['technical_state']) ? $patch['technical_state'] : array();
        if ($technical_state !== array()) {
            $this->aysa_agent8_write_canonical_state('technical', $project_id, $technical_state);
            $result['domains']['technical_state'] = array(
                'stored' => true,
                'keys' => array_values(array_keys($technical_state)),
            );
        }

        $onpage_state = isset($patch['onpage_state']) && is_array($patch['onpage_state']) ? $patch['onpage_state'] : array();
        if ($onpage_state !== array()) {
            $this->aysa_agent8_write_canonical_state('onpage', $project_id, $onpage_state);
            $result['domains']['onpage_state'] = array(
                'stored' => true,
                'keys' => array_values(array_keys($onpage_state)),
            );
        }

        $offpage_state = isset($patch['offpage_state']) && is_array($patch['offpage_state']) ? $patch['offpage_state'] : array();
        if ($offpage_state !== array()) {
            $this->aysa_agent8_write_canonical_state('offpage', $project_id, $offpage_state);
            $result['domains']['offpage_state'] = array(
                'stored' => true,
                'keys' => array_values(array_keys($offpage_state)),
            );
        }

        $monitoring_state = isset($patch['monitoring_state']) && is_array($patch['monitoring_state']) ? $patch['monitoring_state'] : array();
        if ($monitoring_state !== array()) {
            $this->aysa_agent8_write_canonical_state('monitoring', $project_id, $monitoring_state);
            $result['domains']['monitoring_state'] = array(
                'stored' => true,
                'keys' => array_values(array_keys($monitoring_state)),
            );
        }

        if (empty($result['domains'])) {
            $result['applied'] = false;
            $result['reason'] = 'no_supported_canonical_state_domains';
        }

        return $result;
    }

    private function aysa_agent8_clear_session($user_id, $site_host) {
        delete_user_meta($user_id, $this->aysa_agent8_storage_key($user_id, $site_host));
    }

    private function aysa_agent9_clear_session($user_id, $site_host) {
        delete_user_meta($user_id, $this->aysa_agent9_storage_key($user_id, $site_host));
    }

    private function aysa_agent8_validate_contract_response($response_result) {
        $response_result = is_array($response_result) ? $response_result : array();
        $status = sanitize_key((string) ($response_result['contract_status'] ?? ''));
        $issue_code = sanitize_key((string) ($response_result['contract_issue_code'] ?? ''));
        $details = isset($response_result['contract_issue_details']) && is_array($response_result['contract_issue_details'])
            ? $response_result['contract_issue_details']
            : array();

        if ($status === 'invalid' && $issue_code !== '') {
            return array(
                'status' => 'invalid',
                'issue_code' => $issue_code,
                'details' => $details,
            );
        }

        $phase = sanitize_key((string) ($response_result['phase'] ?? 'conversation'));
        $current_step = sanitize_key((string) ($response_result['current_step'] ?? 'conversation'));
        $assistant_message = trim((string) ($response_result['assistant_message'] ?? ''));
        $suggested_actions = isset($response_result['suggested_actions']) && is_array($response_result['suggested_actions'])
            ? array_values($response_result['suggested_actions'])
            : array();
        $approval_request = isset($response_result['approval_request']) && is_array($response_result['approval_request'])
            ? $response_result['approval_request']
            : null;

        if ($approval_request !== null && sanitize_key((string) ($approval_request['action_key'] ?? '')) === '') {
            return array(
                'status' => 'invalid',
                'issue_code' => 'invalid_approval_request',
                'details' => array(
                    'reason' => 'approval_request_missing_action_key',
                ),
            );
        }

        $ui_only_review_action_types = array(
            'open_keyword_review' => true,
            'open_research_competitors_review' => true,
            'open_missing_pages_review' => true,
            'open_content_strategy_review' => true,
            'open_technical_overview' => true,
            'open_technical_site_audit_review' => true,
            'open_technical_pagespeed_review' => true,
            'open_technical_schema_review' => true,
            'open_technical_redirects_review' => true,
            'open_technical_sitemap_robots_review' => true,
            'open_technical_internal_linking_review' => true,
        );

        foreach ($suggested_actions as $index => $action) {
            if (!is_array($action)) {
                return array(
                    'status' => 'invalid',
                    'issue_code' => 'invalid_suggested_action_shape',
                    'details' => array(
                        'reason' => 'suggested_action_not_object',
                        'action_index' => intval($index),
                    ),
                );
            }

            $label = trim((string) ($action['label'] ?? ''));
            $type = sanitize_key((string) ($action['type'] ?? ''));
            $action_key = sanitize_key((string) ($action['action_key'] ?? ''));
            $url = trim((string) ($action['url'] ?? ''));
            $message = trim((string) ($action['message'] ?? ''));
            if ($label === '') {
                return array(
                    'status' => 'invalid',
                    'issue_code' => 'invalid_suggested_action_shape',
                    'details' => array(
                        'reason' => 'suggested_action_missing_label',
                        'action_index' => intval($index),
                    ),
                );
            }
            if ($type === 'open_url') {
                if ($url === '') {
                    return array(
                        'status' => 'invalid',
                        'issue_code' => 'invalid_suggested_action_shape',
                        'details' => array(
                            'reason' => 'open_url_missing_url',
                            'action_index' => intval($index),
                        ),
                    );
                }
            } elseif (isset($ui_only_review_action_types[$type])) {
                // UI-only review command emitted by backend; does not require action_key or message.
            } elseif ($action_key === '' && $message === '') {
                return array(
                    'status' => 'invalid',
                    'issue_code' => 'invalid_suggested_action_shape',
                    'details' => array(
                        'reason' => 'non_url_action_missing_action_key_or_message',
                        'action_index' => intval($index),
                    ),
                );
            }
        }

        if ($this->aysa_agent8_message_expects_cta($assistant_message) && empty($suggested_actions) && $approval_request === null) {
            return array(
                'status' => 'invalid',
                'issue_code' => 'missing_suggested_action',
                'details' => array(
                    'reason' => 'assistant_references_button_but_no_actions_present',
                ),
            );
        }

        $activation_actions_visible = false;
        foreach ($suggested_actions as $action) {
            $type = sanitize_key((string) ($action['type'] ?? ''));
            $action_key = sanitize_key((string) ($action['action_key'] ?? ''));
            $url = trim((string) ($action['url'] ?? ''));
            if ($action_key === 'validate_activation_key' || ($type === 'open_url' && $url === 'https://app.aysa.ai/websites')) {
                $activation_actions_visible = true;
                break;
            }
        }

        if ($activation_actions_visible && !in_array($phase, array('activation', 'preactivation'), true) && !in_array($current_step, array('activation', 'preactivation'), true)) {
            return array(
                'status' => 'invalid',
                'issue_code' => 'activation_action_outside_activation',
                'details' => array(
                    'reason' => 'activation_only_action_visible_after_activation',
                ),
            );
        }

        $pre_research_steps = array(
            'activation' => true,
            'preactivation' => true,
            'integrations' => true,
            'general' => true,
            'objectives' => true,
            'seo-settings' => true,
            'competition' => true,
            'tone-of-voice' => true,
            'research-ready' => true,
            'research_ready' => true,
        );
        $onboarding_steps = array(
            'integrations' => true,
            'general' => true,
            'objectives' => true,
            'seo-settings' => true,
            'competition' => true,
            'tone-of-voice' => true,
        );
        $research_actions = array(
            'start_keyword_research' => true,
            'show_keyword_mapping' => true,
            'show_missing_pages' => true,
            'show_content_strategy' => true,
            'show_publishing_calendar' => true,
            'send_missing_pages_to_plan' => true,
        );
        if (
            isset($pre_research_steps[$phase]) ||
            isset($pre_research_steps[$current_step]) ||
            strpos($phase, 'onboarding') === 0 ||
            isset($onboarding_steps[$current_step])
        ) {
            $offending_keys = array();
            foreach ($suggested_actions as $action) {
                $action_key = sanitize_key((string) ($action['action_key'] ?? ''));
                if ($action_key !== '' && isset($research_actions[$action_key])) {
                    $offending_keys[] = $action_key;
                }
            }
            if (!empty($offending_keys)) {
                $issue_code = (isset($pre_research_steps[$phase]) || isset($pre_research_steps[$current_step]))
                    ? 'pre_research_action_mismatch'
                    : 'onboarding_action_mismatch';
                return array(
                    'status' => 'invalid',
                    'issue_code' => $issue_code,
                    'details' => array(
                        'reason' => 'research_actions_visible_before_research_ready',
                        'offending_action_keys' => implode(',', array_values(array_unique($offending_keys))),
                    ),
                );
            }
        }

        return array(
            'status' => '',
            'issue_code' => '',
            'details' => array(),
        );
    }

    private function aysa_agent8_message_expects_cta($assistant_message) {
        $assistant_message = function_exists('mb_strtolower')
            ? mb_strtolower(trim((string) $assistant_message), 'UTF-8')
            : strtolower(trim((string) $assistant_message));
        if ($assistant_message === '') {
            return false;
        }

        $patterns = array(
            'apasă butonul de mai jos',
            'apasa butonul de mai jos',
            'press the button below',
            'click the button below',
            'натисни бутона по-долу',
        );
        foreach ($patterns as $pattern) {
            if (strpos($assistant_message, $pattern) !== false) {
                return true;
            }
        }

        return false;
    }

    private function aysa_agent8_log_contract_issue($response_result) {
        $response_result = is_array($response_result) ? $response_result : array();
        $entry = array(
            'timestamp' => current_time('mysql'),
            'phase' => sanitize_key((string) ($response_result['phase'] ?? '')),
            'current_step' => sanitize_key((string) ($response_result['current_step'] ?? '')),
            'assistant_message' => trim((string) ($response_result['assistant_message'] ?? '')),
            'suggested_actions' => isset($response_result['suggested_actions']) && is_array($response_result['suggested_actions'])
                ? $response_result['suggested_actions']
                : array(),
            'approval_request' => isset($response_result['approval_request']) && is_array($response_result['approval_request'])
                ? $response_result['approval_request']
                : null,
            'conversation_id' => trim((string) ($response_result['conversation_id'] ?? '')),
            'canonical_project_id' => intval($response_result['canonical_project_id'] ?? 0),
            'contract_issue_code' => sanitize_key((string) ($response_result['contract_issue_code'] ?? '')),
            'contract_issue_details' => isset($response_result['contract_issue_details']) && is_array($response_result['contract_issue_details'])
                ? $response_result['contract_issue_details']
                : array(),
        );

        $option_key = 'aysa_agent8_contract_issue_log';
        $existing = get_option($option_key, array());
        $existing = is_array($existing) ? $existing : array();
        array_unshift($existing, $entry);
        if (count($existing) > 50) {
            $existing = array_slice($existing, 0, 50);
        }
        update_option($option_key, $existing, false);

        aysa_debug_log('[AYSA AGENT8 CONTRACT] ' . wp_json_encode($entry));
    }

    private function aysa_agent8_messages_from_state($state) {
        if (!is_array($state) || empty($state['history']) || !is_array($state['history'])) {
            return array();
        }

        $messages = array();
        foreach ($state['history'] as $item) {
            if (!is_array($item)) {
                continue;
            }
            $role = isset($item['role']) && $item['role'] === 'user' ? 'user' : 'assistant';
            $message = isset($item['message']) ? trim((string) $item['message']) : '';
            if ($message === '') {
                continue;
            }
            $messages[] = array(
                'role' => $role,
                'message' => $message,
            );
        }

        return $messages;
    }

    private function aysa_agent8_apply_billing_usage_hint($wallet_snapshot, $action_result) {
        if (!is_array($wallet_snapshot)) {
            $wallet_snapshot = array();
        }
        if (!is_array($action_result)) {
            return $wallet_snapshot;
        }

        $billing = isset($action_result['billing']) && is_array($action_result['billing']) ? $action_result['billing'] : array();
        $actual_cents = max(0, intval($billing['actual_credits_cents'] ?? 0));
        if ($actual_cents <= 0) {
            return $wallet_snapshot;
        }

        $entries = isset($billing['entries']) && is_array($billing['entries']) ? $billing['entries'] : array();
        $first_entry = isset($entries[0]) && is_array($entries[0]) ? $entries[0] : array();

        $wallet_snapshot['latest_usage_amount_cents'] = $actual_cents;
        if (!empty($first_entry['action_key'])) {
            $wallet_snapshot['latest_usage_action_key'] = sanitize_key((string) $first_entry['action_key']);
        }
        if (!empty($first_entry['reference'])) {
            $wallet_snapshot['latest_usage_reference'] = sanitize_text_field((string) $first_entry['reference']);
        }
        if (empty($wallet_snapshot['latest_usage_at'])) {
            $wallet_snapshot['latest_usage_at'] = current_time('mysql');
        }

        return $wallet_snapshot;
    }

    private function aysa_agent8_apply_top_level_billing_usage_hint($wallet_snapshot, $result) {
        if (!is_array($wallet_snapshot)) {
            $wallet_snapshot = array();
        }
        if (!is_array($result)) {
            return $wallet_snapshot;
        }

        $billing = isset($result['billing']) && is_array($result['billing']) ? $result['billing'] : array();
        $actual_cents = max(0, intval($billing['actual_credits_cents'] ?? 0));
        if ($actual_cents <= 0) {
            return $wallet_snapshot;
        }

        $entries = isset($billing['entries']) && is_array($billing['entries']) ? $billing['entries'] : array();
        $first_entry = isset($entries[0]) && is_array($entries[0]) ? $entries[0] : array();

        $wallet_snapshot['latest_usage_amount_cents'] = $actual_cents;
        if (!empty($first_entry['action_key'])) {
            $wallet_snapshot['latest_usage_action_key'] = sanitize_key((string) $first_entry['action_key']);
        }
        if (!empty($first_entry['reference'])) {
            $wallet_snapshot['latest_usage_reference'] = sanitize_text_field((string) $first_entry['reference']);
        }
        $wallet_snapshot['latest_usage_at'] = current_time('mysql');

        return $wallet_snapshot;
    }

    private function aysa_agent8_local_mutation_skips_billing($local_mutation_result) {
        if (!is_array($local_mutation_result)) {
            return false;
        }

        foreach (array('already_done', 'already_applied', 'already_ignored', 'stale_approval', 'approval_identifiers_missing') as $flag) {
            if (!empty($local_mutation_result[$flag])) {
                return true;
            }
        }

        $code = sanitize_key((string) ($local_mutation_result['code'] ?? $local_mutation_result['reason'] ?? ''));
        return in_array($code, array('already_done', 'already_applied', 'already_ignored', 'stale_approval', 'approval_identifiers_missing', 'redirect_rule_exists', 'gone_rule_exists', 'already_ignored'), true);
    }

    private function aysa_agent8_bill_successful_local_mutation($project_id, $action_result, $local_mutation_result) {
        $project_id = intval($project_id);
        if ($project_id <= 0 || !is_array($action_result) || !is_array($local_mutation_result) || empty($local_mutation_result['applied'])) {
            return array();
        }

        $action_key = sanitize_key((string) ($action_result['action_key'] ?? $local_mutation_result['operation'] ?? ''));
        $cost_cents = $this->aysa_agent8_local_mutation_cost_cents($action_key);
        if ($action_key === '' || $cost_cents <= 0) {
            return array();
        }

        $summary = isset($local_mutation_result['summary']) && is_array($local_mutation_result['summary']) ? $local_mutation_result['summary'] : array();
        $reference = 'agent8:local_mutation:' . $action_key . ':' . md5(wp_json_encode($summary));
        if ($this->aysa_wallet_usage_reference_exists($project_id, $reference)) {
            return array(
                'status' => 'skipped',
                'estimated_credits_cents' => 0,
                'actual_credits_cents' => 0,
                'reason' => 'already_done',
                'entries' => array(),
            );
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            $action_key,
            $reference,
            array(
                'module' => 'agent8_local_mutation',
                'operation' => sanitize_key((string) ($local_mutation_result['operation'] ?? $action_key)),
            ),
            $cost_cents,
            'agent8/local_mutation/' . $action_key
        );

        return array(
            'status' => 'plugin_wallet_tracked',
            'estimated_credits_cents' => $cost_cents,
            'actual_credits_cents' => $cost_cents,
            'entries' => array(array(
                'action_key' => $action_key,
                'billing_mode' => 'plugin_wallet',
                'cost_cents' => $cost_cents,
                'reference' => $reference,
            )),
        );
    }

    private function aysa_agent8_local_mutation_cost_cents($action_key) {
        $action_key = sanitize_key((string) $action_key);
        $costs = array(
            'apply_onpage_meta_batch' => 18,
            'apply_meta' => 18,
            'apply_onpage_rewrite_batch' => 28,
            'apply_rewrite' => 28,
            'apply_onpage_meta_for_page' => 10,
            'apply_onpage_rewrite_for_page' => 20,
            'apply_technical_redirect_candidate' => 12,
            'mark_technical_gone_candidate' => 12,
            'ignore_technical_404_candidate' => 12,
            'apply_technical_redirects_batch' => 18,
            'mark_technical_gone_batch' => 18,
            'ignore_technical_404_batch' => 18,
        );

        return intval($costs[$action_key] ?? 0);
    }

    private function aysa_agent8_runtime_bundle($project_id, $requested_keys = null) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array();
        }

        $setup_user_id = intval($this->aysa_get_effective_setup_user_id());
        $keys = is_array($requested_keys) && !empty($requested_keys)
            ? array_values(array_unique(array_filter(array_map('strval', $requested_keys))))
            : array(
                'setup_snapshot',
                'keyword_mapping_workspace',
                'keyword_mapping_review_payload',
                'research_context_bundle',
                'research_snapshot',
                'research_sitemap_competition',
                'missing_pages_workspace',
                'content_strategy_workspace',
                'existing_missing_page_titles',
                'content_strategy_rows',
                'content_strategy_meta_map',
                'content_strategy_statuses',
                'content_strategy_next',
                'content_strategy_cron_next_run',
                'publishing_calendar_rows',
                'technical_site_audit',
                'technical_pagespeed',
                'technical_errors',
                'technical_sitemap_robots',
                'technical_schema',
                'technical_schema_markup',
                'technical_internal_linking',
                'technical_legacy_urls',
                'onpage_optimization',
                'onpage_workspace',
                'offpage_gbp_overview',
                'offpage_adverlink_orders',
                'offpage_link_exchange_profile',
                'offpage_backlink_audit',
                'offpage_draft_articles',
                'offpage_published_articles',
                'monitoring_rank_tracking',
                'monitoring_ai_overview',
                'monitoring_query_packs',
            );

        $bundle = array();
        foreach ($keys as $key) {
            switch ($key) {
                case 'setup_snapshot':
                    $bundle[$key] = $this->aysa_agent8_get_setup_snapshot($project_id, $setup_user_id);
                    break;
                case 'keyword_mapping_workspace':
                    $bundle[$key] = $this->aysa_keyword_v2_cache_get($project_id, 'keyword_mapping_v2');
                    break;
                case 'keyword_mapping_review_payload':
                    $bundle[$key] = $this->aysa_agent8_get_keyword_mapping_review_payload($project_id);
                    break;
                case 'research_context_bundle':
                    $bundle[$key] = $this->aysa_collect_research_context_bundle($project_id, $setup_user_id);
                    break;
                case 'research_snapshot':
                    $bundle[$key] = $this->aysa_adjustments_get_research_snapshot($project_id, $setup_user_id);
                    break;
                case 'research_sitemap_competition':
                    $bundle[$key] = $this->aysa_agent8_get_research_sitemap_competition_snapshot($project_id);
                    break;
                case 'missing_pages_workspace':
                    $bundle[$key] = $this->aysa_keyword_v2_cache_get($project_id, 'missing_pages_v2');
                    break;
                case 'content_strategy_workspace':
                    $bundle[$key] = $this->aysa_keyword_v2_cache_get($project_id, 'content_strategy_v2');
                    break;
                case 'existing_missing_page_titles':
                    $bundle[$key] = $this->aysa_agent8_get_existing_missing_page_titles($project_id);
                    break;
                case 'content_strategy_rows':
                    $bundle[$key] = $this->aysa_agent8_get_content_strategy_rows($project_id);
                    break;
                case 'content_strategy_meta_map':
                    $bundle[$key] = $this->aysa_agent8_get_content_strategy_meta_map($project_id);
                    break;
                case 'content_strategy_statuses':
                    $bundle[$key] = $this->aysa_agent8_get_content_strategy_status_map($project_id);
                    break;
                case 'content_strategy_next':
                    $bundle[$key] = $this->aysa_agent8_get_content_strategy_next_item($project_id);
                    break;
                case 'content_strategy_cron_next_run':
                    $bundle[$key] = function_exists('wp_next_scheduled') ? wp_next_scheduled('aysa_process_planned_content_now') : null;
                    break;
                case 'publishing_calendar_rows':
                    $bundle[$key] = $this->aysa_agent8_get_publishing_calendar_rows($project_id);
                    break;
                case 'technical_site_audit':
                    $bundle[$key] = $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id);
                    break;
                case 'technical_pagespeed':
                    $bundle[$key] = $this->aysa_adjustments_get_technical_pagespeed_snapshot($project_id);
                    break;
                case 'technical_errors':
                    $bundle[$key] = $this->aysa_adjustments_get_cached_or_idle_technical_errors_snapshot($project_id);
                    break;
                case 'technical_sitemap_robots':
                    $bundle[$key] = $this->aysa_adjustments_get_technical_sitemap_robots_snapshot($project_id);
                    break;
                case 'technical_schema':
                    $bundle[$key] = $this->aysa_adjustments_get_cached_or_idle_technical_schema_snapshot($project_id);
                    break;
                case 'technical_schema_markup':
                    $bundle[$key] = $this->aysa_adjustments_get_technical_schema_markup_snapshot($project_id);
                    break;
                case 'technical_internal_linking':
                    $bundle[$key] = $this->aysa_adjustments_get_technical_internal_linking_snapshot($project_id);
                    break;
                case 'technical_legacy_urls':
                    $bundle[$key] = $this->aysa_agent8_get_technical_legacy_urls();
                    break;
                case 'onpage_optimization':
                    $bundle[$key] = $this->aysa_adjustments_get_onpage_optimization_snapshot($project_id);
                    break;
                case 'onpage_workspace':
                    $bundle[$key] = $this->aysa_agent8_get_onpage_workspace_snapshot($project_id);
                    break;
                case 'offpage_gbp_overview':
                    $bundle[$key] = $this->aysa_agent8_get_offpage_gbp_overview_snapshot($project_id);
                    break;
                case 'offpage_adverlink_orders':
                    $bundle[$key] = $this->aysa_agent8_get_offpage_orders_snapshot($project_id);
                    break;
                case 'offpage_link_exchange_profile':
                    $bundle[$key] = $this->aysa_agent8_get_offpage_link_exchange_profile_snapshot($project_id);
                    break;
                case 'offpage_backlink_audit':
                    $bundle[$key] = $this->aysa_agent8_get_offpage_backlink_audit_snapshot($project_id);
                    break;
                case 'offpage_draft_articles':
                    $bundle[$key] = $this->aysa_agent8_get_offpage_draft_articles_snapshot($project_id);
                    break;
                case 'offpage_published_articles':
                    $bundle[$key] = $this->aysa_agent8_get_offpage_published_articles_snapshot($project_id);
                    break;
                case 'monitoring_rank_tracking':
                    $bundle[$key] = $this->aysa_agent8_get_monitoring_rank_tracking_snapshot($project_id);
                    break;
                case 'monitoring_ai_overview':
                    $bundle[$key] = $this->aysa_agent8_get_monitoring_ai_overview_snapshot($project_id);
                    break;
                case 'monitoring_query_packs':
                    $bundle[$key] = $this->aysa_agent8_get_monitoring_query_packs_snapshot($project_id);
                    break;
            }
        }

        return $this->aysa_agent8_transport_reduce_bundle($bundle);
    }

    private function aysa_agent8_transport_reduce_bundle($bundle) {
        if (!is_array($bundle) || empty($bundle)) {
            return array();
        }

        $reduced = array();
        foreach ($bundle as $key => $value) {
            if ((string) $key === 'technical_site_audit') {
                $reduced[$key] = $this->aysa_agent8_transport_reduce_technical_site_audit_snapshot($value);
                continue;
            }
            $top_level_limit = match ((string) $key) {
                'research_snapshot',
                'research_context_bundle',
                'keyword_mapping_workspace',
                'keyword_mapping_review_payload',
                'missing_pages_workspace',
                'content_strategy_rows',
                'publishing_calendar_rows',
                'offpage_adverlink_orders',
                'offpage_draft_articles',
                'offpage_published_articles',
                'monitoring_rank_tracking',
                'monitoring_ai_overview',
                'monitoring_query_packs' => 20,
                'onpage_workspace' => 500,
                default => 30,
            };
            $reduced[$key] = $this->aysa_agent8_transport_reduce_value($value, 0, $top_level_limit);
        }

        return $reduced;
    }

    private function aysa_agent8_transport_reduce_technical_site_audit_snapshot($snapshot) {
        if (!is_array($snapshot) || empty($snapshot)) {
            return array();
        }

        $reduced = $snapshot;
        if (isset($snapshot['action_bundles']) && is_array($snapshot['action_bundles'])) {
            $reduced['action_bundles'] = array_map(
                array($this, 'aysa_agent8_transport_reduce_site_audit_action_bundle'),
                $snapshot['action_bundles']
            );
        }
        foreach (array('pages', 'exports', 'files', 'overview_rows', 'category_summary', 'page_issue_map', 'export_registry') as $key) {
            if (isset($snapshot[$key]) && is_array($snapshot[$key])) {
                $limit = $key === 'pages' ? 30 : 12;
                $reduced[$key] = $this->aysa_agent8_transport_reduce_value($snapshot[$key], 0, $limit);
            }
        }
        if (isset($snapshot['rows']) && is_array($snapshot['rows'])) {
            $reduced['rows'] = array_map(array($this, 'aysa_agent8_transport_reduce_site_audit_issue_row'), array_slice($snapshot['rows'], 0, 30));
        }
        if (isset($snapshot['issue_groups']) && is_array($snapshot['issue_groups'])) {
            $reduced['issue_groups'] = array_map(function ($group) {
                if (!is_array($group)) {
                    return array();
                }
                return array(
                    'severity' => sanitize_key((string) ($group['severity'] ?? '')),
                    'label' => sanitize_text_field((string) ($group['label'] ?? '')),
                    'count' => intval($group['count'] ?? 0),
                    'issues' => isset($group['issues']) && is_array($group['issues'])
                        ? array_map(array($this, 'aysa_agent8_transport_reduce_site_audit_issue_row'), array_slice($group['issues'], 0, 8))
                        : array(),
                );
            }, array_slice($snapshot['issue_groups'], 0, 4));
        }

        if (isset($snapshot['conclusion']) && is_array($snapshot['conclusion'])) {
            $conclusion = $snapshot['conclusion'];
            if (isset($conclusion['top_issues']) && is_array($conclusion['top_issues'])) {
                $conclusion['top_issues'] = array_map(array($this, 'aysa_agent8_transport_reduce_site_audit_issue_row'), array_slice($conclusion['top_issues'], 0, 12));
            }
            if (isset($conclusion['what_to_do_first']) && is_array($conclusion['what_to_do_first'])) {
                $conclusion['what_to_do_first'] = $this->aysa_agent8_transport_reduce_value($conclusion['what_to_do_first'], 0, 12);
            }
            if (isset($conclusion['recommended_actions']) && is_array($conclusion['recommended_actions'])) {
                $conclusion['recommended_actions'] = array_map(array($this, 'aysa_agent8_transport_reduce_site_audit_action_bundle'), array_slice($conclusion['recommended_actions'], 0, 12));
            }
            $reduced['conclusion'] = $conclusion;
        }

        return $reduced;
    }

    private function aysa_agent8_transport_reduce_site_audit_action_result($action_result) {
        if (!is_array($action_result) || empty($action_result)) {
            return array();
        }

        $reduced = $action_result;
        if (isset($reduced['workspace_snapshot']) && is_array($reduced['workspace_snapshot'])) {
            $reduced['workspace_snapshot'] = $this->aysa_agent8_transport_reduce_technical_site_audit_snapshot($reduced['workspace_snapshot']);
        }
        if (isset($reduced['ui_command']) && is_array($reduced['ui_command'])) {
            $ui_command = $reduced['ui_command'];
            if (isset($ui_command['snapshot']) && is_array($ui_command['snapshot'])) {
                $ui_command['snapshot'] = $this->aysa_agent8_transport_reduce_technical_site_audit_snapshot($ui_command['snapshot']);
            }
            if (isset($ui_command['bundle']) && is_array($ui_command['bundle'])) {
                $ui_command['bundle'] = $this->aysa_agent8_transport_reduce_site_audit_action_bundle($ui_command['bundle']);
            }
            $reduced['ui_command'] = $ui_command;
        }
        if (isset($reduced['canonical_project_state_patch'])) {
            $reduced['canonical_project_state_patch'] = array();
        }

        return $reduced;
    }

    private function aysa_agent8_transport_reduce_site_audit_issue_row($row) {
        if (!is_array($row) || empty($row)) {
            return array();
        }

        $allowed = array(
            'issue_id',
            'key',
            'issue_type',
            'category',
            'severity',
            'priority_score',
            'priority_label',
            'lifecycle_state',
            'title',
            'title_plain',
            'affected_pages_count',
            'affected_pages',
            'affected_display',
            'impact_plain',
            'first_action_plain',
            'source_export',
            'source_file',
        );

        $reduced = array();
        foreach ($allowed as $key) {
            if (array_key_exists($key, $row)) {
                if ($key === 'affected_pages' && is_array($row[$key])) {
                    $reduced[$key] = array_map(array($this, 'aysa_agent8_transport_reduce_site_audit_affected_page'), array_slice($row[$key], 0, 5));
                    continue;
                }
                $limit = $key === 'affected_pages' ? 5 : 12;
                $reduced[$key] = $this->aysa_agent8_transport_reduce_value($row[$key], 0, $limit);
            }
        }

        return $reduced;
    }

    private function aysa_agent8_transport_reduce_site_audit_affected_page($row) {
        if (!is_array($row) || empty($row)) {
            return array();
        }

        $allowed = array('url', 'page_title', 'status_code', 'indexability', 'canonical_url', 'crawl_depth', 'inlinks', 'outlinks');
        $reduced = array();
        foreach ($allowed as $key) {
            if (array_key_exists($key, $row)) {
                $reduced[$key] = $this->aysa_agent8_transport_reduce_value($row[$key], 0, 6);
            }
        }

        return $reduced;
    }

    private function aysa_agent8_transport_reduce_site_audit_action_bundle($bundle) {
        if (!is_array($bundle) || empty($bundle)) {
            return array();
        }

        $reduced = $this->aysa_agent8_transport_reduce_value($bundle, 0, 20);
        if (isset($bundle['metadata_execution_package']) && is_array($bundle['metadata_execution_package'])) {
            $reduced['metadata_execution_package'] = $this->aysa_agent8_transport_reduce_metadata_execution_package($bundle['metadata_execution_package']);
        }
        if (isset($bundle['redirect_execution_package']) && is_array($bundle['redirect_execution_package'])) {
            $reduced['redirect_execution_package'] = $this->aysa_agent8_transport_reduce_value($bundle['redirect_execution_package'], 0, 40);
        }
        if (isset($bundle['internal_linking_execution_package']) && is_array($bundle['internal_linking_execution_package'])) {
            $reduced['internal_linking_execution_package'] = $this->aysa_agent8_transport_reduce_value($bundle['internal_linking_execution_package'], 0, 40);
        }
        if (isset($bundle['image_alt_execution_package']) && is_array($bundle['image_alt_execution_package'])) {
            $reduced['image_alt_execution_package'] = $this->aysa_agent8_transport_reduce_image_alt_execution_package($bundle['image_alt_execution_package']);
        }
        if (isset($bundle['canonical_execution_package']) && is_array($bundle['canonical_execution_package'])) {
            $reduced['canonical_execution_package'] = $this->aysa_agent8_transport_reduce_canonical_execution_package($bundle['canonical_execution_package']);
        }

        return $reduced;
    }

    private function aysa_agent8_transport_reduce_metadata_execution_package($package) {
        if (!is_array($package) || empty($package)) {
            return array();
        }

        $reduced = $package;
        foreach (array('items', 'skipped') as $key) {
            $rows = isset($package[$key]) && is_array($package[$key]) ? $package[$key] : array();
            $reduced[$key] = array_map(array($this, 'aysa_agent8_transport_reduce_metadata_execution_row'), array_slice($rows, 0, 50));
        }
        if (isset($package['count_reconciliation']) && is_array($package['count_reconciliation'])) {
            $reduced['count_reconciliation'] = $this->aysa_agent8_transport_reduce_value($package['count_reconciliation'], 0, 40);
        }

        return $reduced;
    }

    private function aysa_agent8_transport_reduce_metadata_execution_row($row) {
        if (!is_array($row) || empty($row)) {
            return array();
        }

        $allowed = array(
            'url',
            'field',
            'old_value',
            'current_value',
            'new_value',
            'proposed_value',
            'overwrite_decision',
            'overwrite_reason',
            'quality_flags',
            'confidence',
            'post_id',
            'post_title',
            'issue_type',
            'source',
            'source_export',
            'notes',
            'status',
        );

        $reduced = array();
        foreach ($allowed as $key) {
            if (array_key_exists($key, $row)) {
                $reduced[$key] = $this->aysa_agent8_transport_reduce_value($row[$key], 0, 20);
            }
        }

        return $reduced;
    }

    private function aysa_agent8_transport_reduce_image_alt_execution_package($package) {
        if (!is_array($package) || empty($package)) {
            return array();
        }

        $reduced = $package;
        foreach (array('items', 'skipped') as $key) {
            $rows = isset($package[$key]) && is_array($package[$key]) ? $package[$key] : array();
            $reduced[$key] = array_map(array($this, 'aysa_agent8_transport_reduce_image_alt_execution_row'), array_slice($rows, 0, 50));
        }

        return $reduced;
    }

    private function aysa_agent8_transport_reduce_image_alt_execution_row($row) {
        if (!is_array($row) || empty($row)) {
            return array();
        }

        $allowed = array(
            'decision',
            'reason',
            'image_url',
            'attachment_id',
            'page_url',
            'page_title',
            'image_filename',
            'old_value',
            'current_value',
            'existing_alt',
            'proposed_value',
            'image_description',
            'confidence',
            'is_decorative',
            'context_alignment',
            'quality_flags',
            'status',
            'candidate_quality',
        );

        $reduced = array();
        foreach ($allowed as $key) {
            if (array_key_exists($key, $row)) {
                $reduced[$key] = $this->aysa_agent8_transport_reduce_value($row[$key], 0, 20);
            }
        }

        return $reduced;
    }

    private function aysa_agent8_transport_reduce_canonical_execution_package($package) {
        if (!is_array($package) || empty($package)) {
            return array();
        }

        $reduced = $package;
        foreach (array('items', 'skipped') as $key) {
            $rows = isset($package[$key]) && is_array($package[$key]) ? $package[$key] : array();
            $reduced[$key] = array_map(array($this, 'aysa_agent8_transport_reduce_canonical_execution_row'), array_slice($rows, 0, 50));
        }

        return $reduced;
    }

    private function aysa_agent8_transport_reduce_canonical_execution_row($row) {
        if (!is_array($row) || empty($row)) {
            return array();
        }

        $allowed = array(
            'decision',
            'recommended_action',
            'reason',
            'quality_flags',
            'url',
            'page_url',
            'post_id',
            'post_title',
            'current_canonical',
            'live_canonical_url',
            'recommended_target_url',
            'canonical_provider',
            'confidence',
            'status',
        );

        $reduced = array();
        foreach ($allowed as $key) {
            if (array_key_exists($key, $row)) {
                $reduced[$key] = $this->aysa_agent8_transport_reduce_value($row[$key], 0, 20);
            }
        }

        return $reduced;
    }

    private function aysa_agent8_transport_reduce_value($value, $depth = 0, $item_limit = 30) {
        if ($depth >= 6) {
            return is_scalar($value) || $value === null ? $value : array();
        }

        if (is_string($value)) {
            return strlen($value) > 1200 ? substr($value, 0, 1200) : $value;
        }

        if (!is_array($value)) {
            return $value;
        }

        $is_list = array_keys($value) === range(0, count($value) - 1);
        if ($is_list) {
            $value = array_slice($value, 0, max(1, intval($item_limit)));
        }

        $reduced = array();
        $count = 0;
        foreach ($value as $key => $item) {
            if (!$is_list && $count >= 80) {
                break;
            }
            $reduced[$key] = $this->aysa_agent8_transport_reduce_value($item, $depth + 1, $item_limit);
            $count++;
        }

        return $reduced;
    }

    private function aysa_agent8_runtime_bundle_for_request($project_id, $mode, $user_message = '', $action_key = '') {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array();
        }

        $mode = (string) $mode;
        $action_key = (string) $action_key;
        $normalized_message = strtolower(remove_accents((string) $user_message));

        if ($mode === 'action' || $mode === 'approval') {
            return match ($action_key) {
                'show_setup_activation',
                'show_setup_google_connectivity',
                'show_setup_business_profile',
                'show_setup_objectives',
                'show_setup_seo_settings',
                'show_setup_competition',
                'show_setup_tone_of_voice' => $this->aysa_agent8_runtime_bundle($project_id, array('setup_snapshot')),
                'show_research_keyword_discovery' => $this->aysa_agent8_runtime_bundle($project_id, array('research_snapshot', 'setup_snapshot')),
                'refresh_research_keyword_discovery' => $this->aysa_agent8_runtime_bundle($project_id, array('research_snapshot', 'research_context_bundle', 'setup_snapshot')),
                'show_research_competitor_gap' => $this->aysa_agent8_runtime_bundle($project_id, array('research_snapshot', 'research_context_bundle')),
                'refresh_research_competitor_gap' => $this->aysa_agent8_runtime_bundle($project_id, array('research_snapshot', 'research_context_bundle')),
                'show_research_competitor_identification' => $this->aysa_agent8_runtime_bundle($project_id, array('research_snapshot', 'research_context_bundle')),
                'refresh_research_competitor_identification' => $this->aysa_agent8_runtime_bundle($project_id, array('research_snapshot', 'research_context_bundle')),
                'show_research_competitor_backlinks' => $this->aysa_agent8_runtime_bundle($project_id, array('research_snapshot', 'research_context_bundle')),
                'refresh_research_competitor_backlinks' => $this->aysa_agent8_runtime_bundle($project_id, array('research_snapshot', 'research_context_bundle')),
                'show_research_sitemap_competition' => $this->aysa_agent8_runtime_bundle($project_id, array('research_sitemap_competition')),
                'refresh_research_sitemap_competition' => $this->aysa_agent8_runtime_bundle($project_id, array('research_sitemap_competition')),
                'show_found_keywords' => $this->aysa_agent8_runtime_bundle($project_id, array('keyword_mapping_workspace')),
                'show_keyword_mapping' => $this->aysa_agent8_runtime_bundle($project_id, array('keyword_mapping_workspace', 'keyword_mapping_review_payload', 'research_context_bundle')),
                'show_missing_pages',
                'reject_missing_page',
                'accept_missing_page_ai_proposal',
                'choose_missing_page_existing_page',
                'keep_missing_page' => $this->aysa_agent8_runtime_bundle($project_id, array('missing_pages_workspace', 'research_context_bundle')),
                'send_missing_pages_to_plan' => $this->aysa_agent8_runtime_bundle($project_id, array('missing_pages_workspace', 'research_context_bundle', 'existing_missing_page_titles')),
                'show_content_strategy',
                'show_planned_content' => $this->aysa_agent8_runtime_bundle($project_id, array(
                    'research_context_bundle',
                    'content_strategy_rows',
                    'content_strategy_workspace',
                    'content_strategy_meta_map',
                    'content_strategy_statuses',
                    'content_strategy_next',
                    'content_strategy_cron_next_run',
                )),
                'show_publishing_calendar' => $this->aysa_agent8_runtime_bundle($project_id, array('publishing_calendar_rows')),
                'show_technical_overview', 'guide_technical_optimization', 'refresh_technical_optimization' => $this->aysa_agent8_runtime_bundle($project_id, array(
                    'technical_site_audit',
                    'technical_pagespeed',
                    'technical_errors',
                    'technical_schema',
                    'technical_schema_markup',
                    'technical_sitemap_robots',
                    'technical_internal_linking',
                    'technical_legacy_urls',
                )),
                'show_technical_site_audit', 'refresh_technical_site_audit', 'prepare_metadata_remediation', 'prepare_response_code_review', 'prepare_indexability_review', 'prepare_internal_linking_plan' => $this->aysa_agent8_runtime_bundle($project_id, array('technical_site_audit', 'technical_legacy_urls')),
                'show_technical_pagespeed', 'refresh_technical_pagespeed', 'export_technical_pagespeed_pdf', 'ask_technical_pagespeed_recipient', 'email_technical_pagespeed_pdf' => $this->aysa_agent8_runtime_bundle($project_id, array('technical_pagespeed', 'technical_legacy_urls')),
                'show_technical_schema',
                'apply_technical_schema_recommended',
                'disable_technical_schema' => $this->aysa_agent8_runtime_bundle($project_id, array('technical_schema', 'technical_schema_markup', 'technical_legacy_urls')),
                'show_technical_redirects_review',
                'apply_technical_redirect_candidate',
                'mark_technical_gone_candidate',
                'ignore_technical_404_candidate',
                'apply_technical_redirects_batch',
                'mark_technical_gone_batch',
                'ignore_technical_404_batch',
                'show_technical_redirect_conflicts' => $this->aysa_agent8_runtime_bundle($project_id, array('technical_errors', 'technical_legacy_urls')),
                'show_technical_sitemap_robots', 'refresh_technical_sitemap_robots', 'unblock_technical_robots', 'generate_aysa_sitemap', 'save_technical_robots', 'restore_recommended_robots', 'enforce_aysa_sitemap_authority', 'disable_conflicting_sitemap_providers' => $this->aysa_agent8_runtime_bundle($project_id, array('technical_sitemap_robots', 'technical_legacy_urls')),
                'show_technical_internal_linking', 'refresh_technical_internal_linking', 'preview_technical_internal_linking', 'apply_technical_internal_linking' => $this->aysa_agent8_runtime_bundle($project_id, array('technical_internal_linking', 'technical_legacy_urls')),
                'show_onpage_optimization',
                'show_onpage_meta_generation',
                'show_onpage_keyword_association',
                'accept_onpage_keyword_association',
                'reject_onpage_keyword_association',
                'show_onpage_keyword_validation',
                'show_onpage_rewrite_generation',
                'optimize_onpage_pages',
                'generate_onpage_meta',
                'validate_onpage_keywords',
                'decide_onpage_rewrite',
                'generate_onpage_rewrite' => $this->aysa_agent8_runtime_bundle($project_id, array('onpage_optimization', 'onpage_workspace')),
                'show_offpage_google_business_profile',
                'set_offpage_gbp_primary_location',
                'refresh_offpage_gbp_reviews',
                'refresh_offpage_gbp_performance',
                'create_offpage_gbp_post',
                'reply_offpage_gbp_review',
                'upload_offpage_gbp_media',
                'update_offpage_gbp_business_info',
                'update_offpage_gbp_hours',
                'update_offpage_gbp_attributes',
                'update_offpage_gbp_services' => $this->aysa_agent8_runtime_bundle($project_id, array('offpage_gbp_overview')),
                'show_offpage_link_exchange',
                'accept_offpage_exchange_request',
                'pause_offpage_exchange_request' => $this->aysa_agent8_runtime_bundle($project_id, array('offpage_link_exchange_profile', 'offpage_adverlink_orders')),
                'show_link_building_run',
                'approve_link_opportunity',
                'reject_link_opportunity' => $this->aysa_agent8_runtime_bundle($project_id, array('offpage_link_exchange_profile', 'offpage_adverlink_orders', 'offpage_backlink_audit', 'offpage_draft_articles', 'offpage_published_articles')),
                'show_offpage_backlink_audit' => $this->aysa_agent8_runtime_bundle($project_id, array('offpage_backlink_audit')),
                'show_offpage_draft_articles',
                'preview_offpage_draft_article',
                'send_offpage_draft_article_approval_email',
                'approve_offpage_draft_article' => $this->aysa_agent8_runtime_bundle($project_id, array('offpage_draft_articles', 'offpage_adverlink_orders')),
                'show_offpage_published_articles' => $this->aysa_agent8_runtime_bundle($project_id, array('offpage_published_articles', 'offpage_adverlink_orders')),
                'show_monitoring_rank_tracking' => $this->aysa_agent8_runtime_bundle($project_id, array('monitoring_rank_tracking')),
                'refresh_monitoring_rank_tracking' => $this->aysa_agent8_runtime_bundle($project_id, array('monitoring_rank_tracking')),
                'show_monitoring_ai_monitoring' => $this->aysa_agent8_runtime_bundle($project_id, array('monitoring_ai_overview')),
                'refresh_monitoring_ai_monitoring' => $this->aysa_agent8_runtime_bundle($project_id, array('monitoring_ai_overview')),
                'show_monitoring_keyword_overview' => $this->aysa_agent8_runtime_bundle($project_id, array('monitoring_rank_tracking')),
                'refresh_monitoring_keyword_overview' => $this->aysa_agent8_runtime_bundle($project_id, array('monitoring_rank_tracking')),
                'show_monitoring_query_packs' => $this->aysa_agent8_runtime_bundle($project_id, array('monitoring_query_packs', 'monitoring_ai_overview')),
                'save_monitoring_query_pack' => $this->aysa_agent8_runtime_bundle($project_id, array('monitoring_query_packs', 'monitoring_ai_overview')),
                default => array(),
            };
        }

        if ($mode !== 'message' || $normalized_message === '') {
            return array();
        }

        $keys = array();
        if (
            strpos($normalized_message, 'keyword') !== false
            || strpos($normalized_message, 'cautar') !== false
            || strpos($normalized_message, 'mapping') !== false
            || strpos($normalized_message, 'cluster') !== false
        ) {
            $keys[] = 'keyword_mapping_review_payload';
        }
        if (
            strpos($normalized_message, 'missing pages') !== false
            || strpos($normalized_message, 'pagini lipsa') !== false
            || strpos($normalized_message, 'pages lipsa') !== false
            || strpos($normalized_message, 'trimite in plan') !== false
            || strpos($normalized_message, 'trimite missing') !== false
            || strpos($normalized_message, 'send to plan') !== false
            || strpos($normalized_message, 'pune in plan') !== false
        ) {
            $keys[] = 'missing_pages_workspace';
        }
        if (
            strpos($normalized_message, 'trimite in plan') !== false
            || strpos($normalized_message, 'trimite missing') !== false
            || strpos($normalized_message, 'send to plan') !== false
            || strpos($normalized_message, 'pune in plan') !== false
        ) {
            $keys[] = 'existing_missing_page_titles';
        }
        if (
            strpos($normalized_message, 'content strategy') !== false
            || strpos($normalized_message, 'plan editorial') !== false
            || strpos($normalized_message, 'strategie') !== false
            || strpos($normalized_message, 'articole') !== false
        ) {
            $keys = array_merge($keys, array(
                'content_strategy_rows',
                'content_strategy_meta_map',
                'content_strategy_statuses',
                'content_strategy_next',
                'content_strategy_cron_next_run',
            ));
        }
        if (
            strpos($normalized_message, 'calendar') !== false
            || strpos($normalized_message, 'publicare') !== false
            || strpos($normalized_message, 'publishing') !== false
        ) {
            $keys[] = 'publishing_calendar_rows';
        }
        if (
            strpos($normalized_message, 'technical') !== false
            || strpos($normalized_message, 'tehnic') !== false
            || strpos($normalized_message, 'site audit') !== false
            || strpos($normalized_message, 'pagespeed') !== false
            || strpos($normalized_message, 'page speed') !== false
            || strpos($normalized_message, 'sitemap') !== false
            || strpos($normalized_message, 'robots') !== false
            || strpos($normalized_message, 'internal linking') !== false
            || strpos($normalized_message, 'linkuri interne') !== false
        ) {
            $keys = array_merge($keys, array(
                'technical_site_audit',
                'technical_pagespeed',
                'technical_errors',
                'technical_schema',
                'technical_schema_markup',
                'technical_sitemap_robots',
                'technical_internal_linking',
                'technical_legacy_urls',
            ));
        }
        if (
            strpos($normalized_message, 'on page') !== false
            || strpos($normalized_message, 'on-page') !== false
            || strpos($normalized_message, 'onpage') !== false
            || strpos($normalized_message, 'optimizare pagina') !== false
            || strpos($normalized_message, 'optimizare pagini') !== false
            || strpos($normalized_message, 'page update') !== false
            || strpos($normalized_message, 'rescrie pagina') !== false
        ) {
            $keys = array_merge($keys, array(
                'onpage_optimization',
                'onpage_workspace',
            ));
        }
        if (
            strpos($normalized_message, 'google business') !== false
            || strpos($normalized_message, 'gbp') !== false
            || strpos($normalized_message, 'business profile') !== false
            || strpos($normalized_message, 'off page') !== false
            || strpos($normalized_message, 'off-page') !== false
            || strpos($normalized_message, 'offpage') !== false
        ) {
            $keys[] = 'offpage_gbp_overview';
        }
        if (
            strpos($normalized_message, 'exchange') !== false
            || strpos($normalized_message, 'link exchange') !== false
        ) {
            $keys[] = 'offpage_link_exchange_profile';
            $keys[] = 'offpage_adverlink_orders';
        }
        if (
            strpos($normalized_message, 'backlink') !== false
            || strpos($normalized_message, 'audit offpage') !== false
            || strpos($normalized_message, 'backlink audit') !== false
        ) {
            $keys[] = 'offpage_backlink_audit';
        }
        if (
            strpos($normalized_message, 'draft articles') !== false
            || strpos($normalized_message, 'drafturi') !== false
        ) {
            $keys[] = 'offpage_draft_articles';
        }
        if (
            strpos($normalized_message, 'published articles') !== false
            || strpos($normalized_message, 'articole publicate') !== false
        ) {
            $keys[] = 'offpage_published_articles';
        }
        if (
            strpos($normalized_message, 'monitoring') !== false
            || strpos($normalized_message, 'monitorizare') !== false
            || strpos($normalized_message, 'rank tracking') !== false
            || strpos($normalized_message, 'pozitii') !== false
            || strpos($normalized_message, 'poziții') !== false
            || strpos($normalized_message, 'keyword overview') !== false
            || strpos($normalized_message, 'ai monitoring') !== false
            || strpos($normalized_message, 'query pack') !== false
            || strpos($normalized_message, 'query packs') !== false
        ) {
            $keys = array_merge($keys, array(
                'monitoring_rank_tracking',
                'monitoring_ai_overview',
                'monitoring_query_packs',
            ));
        }

        $keys = array_values(array_unique($keys));
        if (empty($keys)) {
            return array();
        }

        return $this->aysa_agent8_runtime_bundle($project_id, $keys);
    }

    private function aysa_agent8_get_onpage_workspace_snapshot($project_id) {
        $service = $this->aysa_onpage_workspace_service();
        if (!$service) {
            return array(
                'summary' => array(),
                'rows' => array(),
                'run' => array(),
                'active_entity_group' => 'page',
            );
        }

        $project_id = intval($project_id);
        $per_page = 100;
        $workspace = null;
        $all_rows = array();
        $seen = array();
        $group_counts = array();
        $entity_groups = array('product', 'ecommerce_category', 'page', 'article', 'other');

        foreach ($entity_groups as $entity_group) {
            $page = 1;
            $group_workspace = $service->build_workspace($project_id, array(
                'entity_group' => $entity_group,
                'page' => $page,
                'per_page' => $per_page,
                'refresh' => false,
            ));
            if (!is_array($group_workspace)) {
                continue;
            }
            if ($workspace === null) {
                $workspace = $group_workspace;
            }

            $pagination = isset($group_workspace['pagination']) && is_array($group_workspace['pagination']) ? $group_workspace['pagination'] : array();
            $pages = max(1, intval($pagination['pages'] ?? 1));
            $group_rows = isset($group_workspace['rows']) && is_array($group_workspace['rows']) ? array_values($group_workspace['rows']) : array();

            while ($page < $pages) {
                $page++;
                $next_workspace = $service->build_workspace($project_id, array(
                    'entity_group' => $entity_group,
                    'page' => $page,
                    'per_page' => $per_page,
                    'refresh' => false,
                ));
                if (!is_array($next_workspace) || !isset($next_workspace['rows']) || !is_array($next_workspace['rows'])) {
                    continue;
                }
                $group_rows = array_merge($group_rows, array_values($next_workspace['rows']));
            }

            $group_counts[$entity_group] = count($group_rows);
            foreach ($group_rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $dedupe_key = (string) ($row['entity_key'] ?? '');
                if ($dedupe_key === '') {
                    $dedupe_key = untrailingslashit((string) ($row['url'] ?? ''));
                }
                if ($dedupe_key !== '' && isset($seen[$dedupe_key])) {
                    continue;
                }
                if ($dedupe_key !== '') {
                    $seen[$dedupe_key] = true;
                }
                $row = $this->aysa_agent8_hydrate_onpage_applied_meta_row($row);
                $all_rows[] = $row;
            }
        }

        if ($workspace === null) {
            return array();
        }

        $workspace['rows'] = $all_rows;
        $workspace['pagination'] = array(
            'page' => 1,
            'per_page' => count($all_rows),
            'total' => count($all_rows),
            'pages' => 1,
        );
        $workspace['agent8_full_snapshot'] = true;
        $workspace['active_entity_group'] = 'all';
        $workspace['agent8_entity_group_counts'] = $group_counts;

        return $workspace;
    }

    private function aysa_agent8_hydrate_onpage_applied_meta_row($row) {
        if (!is_array($row)) {
            return $row;
        }

        $meta_state = sanitize_key((string) ($row['meta_state'] ?? ''));
        $output_state = sanitize_key((string) ($row['output_state'] ?? ''));
        $is_applied = $meta_state === 'applied' || in_array($output_state, array('meta_applied', 'rewrite_applied'), true);
        if (!$is_applied) {
            return $row;
        }

        $entity_id = intval($row['entity_id'] ?? 0);
        if ($entity_id <= 0) {
            return $row;
        }

        $is_term = (string) ($row['object_kind'] ?? 'post') === 'term';
        $read_meta = static function($key) use ($entity_id, $is_term) {
            return trim((string) ($is_term ? get_term_meta($entity_id, $key, true) : get_post_meta($entity_id, $key, true)));
        };

        $title = trim((string) ($row['applied_meta_title'] ?? ''));
        if ($title === '') {
            $title = trim((string) ($row['current_meta_title'] ?? ''));
        }
        if ($title === '') {
            $title = $read_meta('_aysa_seo_title');
        }
        if ($title === '') {
            $title = $read_meta('_aysa_generated_meta_title');
        }

        $description = trim((string) ($row['applied_meta_description'] ?? ''));
        if ($description === '') {
            $description = trim((string) ($row['current_meta_description'] ?? ''));
        }
        if ($description === '') {
            $description = $read_meta('_aysa_seo_description');
        }
        if ($description === '') {
            $description = $read_meta('_aysa_generated_meta_description');
        }

        if ($title !== '') {
            $row['current_meta_title'] = trim((string) ($row['current_meta_title'] ?? '')) !== '' ? $row['current_meta_title'] : $title;
            $row['applied_meta_title'] = trim((string) ($row['applied_meta_title'] ?? '')) !== '' ? $row['applied_meta_title'] : $title;
        }
        if ($description !== '') {
            $row['current_meta_description'] = trim((string) ($row['current_meta_description'] ?? '')) !== '' ? $row['current_meta_description'] : $description;
            $row['applied_meta_description'] = trim((string) ($row['applied_meta_description'] ?? '')) !== '' ? $row['applied_meta_description'] : $description;
        }

        return $row;
    }

    private function aysa_agent8_prepare_onpage_meta_generation_payload($payload, $project_id) {
        $payload = is_array($payload) ? $payload : array();
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return $payload;
        }

        $incoming_batch_info = isset($payload['batch_info']) && is_array($payload['batch_info'])
            ? $payload['batch_info']
            : array();
        $worker_batch_size = 10;
        if (isset($incoming_batch_info['worker_batch_size'])) {
            $worker_batch_size = max(1, min(10, intval($incoming_batch_info['worker_batch_size'])));
        } elseif (isset($incoming_batch_info['batch_size'])) {
            $worker_batch_size = max(1, min(10, intval($incoming_batch_info['batch_size'])));
        } elseif (isset($payload['worker_batch_size'])) {
            $worker_batch_size = max(1, min(10, intval($payload['worker_batch_size'])));
        } elseif (isset($payload['batch_size'])) {
            $worker_batch_size = max(1, min(10, intval($payload['batch_size'])));
        }

        $selection = isset($payload['selection']) && is_array($payload['selection'])
            ? array_values(array_filter(array_map('strval', $payload['selection'])))
            : array();

        $selection = array_values(array_unique($selection));
        $requested_count = count($selection);
        $reported_requested_count = max($requested_count, intval($incoming_batch_info['requested_count'] ?? 0));
        $remaining_count = max(0, $reported_requested_count - count($selection));
        $limit_text = static function($value, $limit) {
            $text = trim(wp_strip_all_tags((string) $value));
            if ($text === '') {
                return '';
            }
            if (function_exists('mb_substr')) {
                return mb_substr($text, 0, intval($limit));
            }
            return substr($text, 0, intval($limit));
        };
        $limit_list = static function($items, $limit) use ($limit_text) {
            if (!is_array($items)) {
                return array();
            }
            $values = array();
            foreach ($items as $item) {
                $value = $limit_text($item, 120);
                if ($value !== '') {
                    $values[] = $value;
                }
                if (count($values) >= intval($limit)) {
                    break;
                }
            }
            return array_values(array_unique($values));
        };
        $compact_context = static function($context) use ($limit_text, $limit_list) {
            return array(
                'entity_key' => (string) ($context['entity_key'] ?? ''),
                'entity_id' => intval($context['entity_id'] ?? 0),
                'entity_group' => (string) ($context['entity_group'] ?? ''),
                'object_kind' => (string) ($context['object_kind'] ?? ''),
                'post_type' => (string) ($context['post_type'] ?? ''),
                'taxonomy' => (string) ($context['taxonomy'] ?? ''),
                'page_type' => (string) ($context['page_type'] ?? ''),
                'url' => (string) ($context['url'] ?? ''),
                'title' => $limit_text($context['title'] ?? '', 300),
                'current_title' => $limit_text($context['current_title'] ?? $context['title'] ?? '', 300),
                'current_h1' => $limit_text($context['current_h1'] ?? $context['h1'] ?? '', 300),
                'current_slug' => $limit_text($context['current_slug'] ?? '', 200),
                'main_keyword' => $limit_text($context['main_keyword'] ?? '', 160),
                'keywords' => $limit_list($context['keywords'] ?? array(), 12),
                'secondary_keywords' => $limit_list($context['secondary_keywords'] ?? array(), 12),
                'mapping_state' => (string) ($context['mapping_state'] ?? ''),
                'meta_state' => (string) ($context['meta_state'] ?? ''),
                'publication_state' => (string) ($context['publication_state'] ?? ''),
                'current_meta_title' => $limit_text($context['current_meta_title'] ?? '', 300),
                'current_meta_description' => $limit_text($context['current_meta_description'] ?? '', 500),
                'content_excerpt' => $limit_text($context['content_excerpt'] ?? '', 700),
                'content_snippet' => $limit_text($context['content_snippet'] ?? '', 900),
                'current_content_text' => $limit_text($context['current_content_text'] ?? '', 1500),
                'language' => (string) ($context['language'] ?? ''),
                'country_code' => (string) ($context['country_code'] ?? ''),
            );
        };
        $seed_entities = array();
        if (isset($payload['seed_projection']['onpage_meta_generation']['entities']) && is_array($payload['seed_projection']['onpage_meta_generation']['entities'])) {
            $seed_entities = $payload['seed_projection']['onpage_meta_generation']['entities'];
        }
        $seed_by_key = array();
        foreach ($seed_entities as $context) {
            if (!is_array($context)) {
                continue;
            }
            $key = trim((string) ($context['entity_key'] ?? ''));
            if ($key !== '') {
                $seed_by_key[$key] = $compact_context($context);
            }
        }

        $eligible = array();
        foreach ($selection as $entity_key) {
            if (isset($seed_by_key[$entity_key])) {
                $eligible[] = $seed_by_key[$entity_key];
            }
        }
        $included_count = count($selection);
        $eligible_count = max($included_count + $remaining_count, intval($incoming_batch_info['eligible_count'] ?? 0));
        unset($payload['selection_meta'], $payload['onpage_workspace_snapshot'], $payload['keyword_mapping_workspace'], $payload['local_keyword_associations'], $payload['onpage_keyword_mapping_diagnostics']);
        $payload['selection'] = array_values($selection);
        $payload['selection_meta'] = $eligible;
        $payload['batch_info'] = array(
            'requested_count' => $requested_count,
            'eligible_count' => $eligible_count,
            'included_count' => $included_count,
            'remaining_count' => $remaining_count,
            'batch_size' => $worker_batch_size,
            'worker_batch_size' => $worker_batch_size,
            'execution_mode' => 'worker_incremental',
        );
        $payload['state_summary'] = array(
            'source' => 'agent8_v3_onpage_meta',
            'eligible_count' => $eligible_count,
            'requested_count' => $reported_requested_count,
            'included_count' => $included_count,
            'remaining_count' => $remaining_count,
            'batch_size' => $worker_batch_size,
            'worker_batch_size' => $worker_batch_size,
            'execution_mode' => 'worker_incremental',
            'requires_mapping_state' => 'mapped',
            'requires_primary_keyword' => true,
        );
        $payload['seed_projection'] = array(
            'onpage_meta_generation' => array(
                'eligible_count' => $eligible_count,
                'included_count' => $included_count,
                'remaining_count' => $remaining_count,
                'worker_batch_size' => $worker_batch_size,
                'execution_mode' => 'worker_incremental',
                'entities' => array_map(static function($context) {
                    return array(
                        'entity_key' => (string) ($context['entity_key'] ?? ''),
                        'url' => (string) ($context['url'] ?? ''),
                        'title' => (string) ($context['title'] ?? ''),
                        'h1' => (string) ($context['current_h1'] ?? ''),
                        'main_keyword' => (string) ($context['main_keyword'] ?? ''),
                        'secondary_keywords' => isset($context['secondary_keywords']) && is_array($context['secondary_keywords']) ? array_values($context['secondary_keywords']) : array(),
                        'mapping_state' => (string) ($context['mapping_state'] ?? ''),
                        'meta_state' => (string) ($context['meta_state'] ?? ''),
                        'publication_state' => (string) ($context['publication_state'] ?? ''),
                        'current_meta_title' => (string) ($context['current_meta_title'] ?? ''),
                        'current_meta_description' => (string) ($context['current_meta_description'] ?? ''),
                    );
                }, $eligible),
            ),
        );

        return $payload;
    }

    private function aysa_agent8_attach_onpage_keyword_mapping_context($payload, $project_id) {
        $payload = is_array($payload) ? $payload : array();
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return $payload;
        }

        $workspace = $this->aysa_keyword_v2_cache_get($project_id, 'keyword_mapping_v2');
        $associations = $this->get_keyword_discovery_local_associations($project_id);
        $payload['keyword_mapping_workspace'] = is_array($workspace) ? $workspace : array();
        $payload['local_keyword_associations'] = is_array($associations) ? array_values($associations) : array();
        $diagnostics = $this->aysa_agent8_onpage_keyword_mapping_diagnostics(
            $project_id,
            $payload['keyword_mapping_workspace'],
            $payload['local_keyword_associations']
        );
        if (isset($payload['onpage_workspace_snapshot']['rows']) && is_array($payload['onpage_workspace_snapshot']['rows'])) {
            $diagnostics['modal_rows_count'] = count($payload['onpage_workspace_snapshot']['rows']);
        }
        $payload['onpage_keyword_mapping_diagnostics'] = $diagnostics;

        return $payload;
    }

    private function aysa_agent8_onpage_keyword_mapping_diagnostics($project_id, $workspace, $associations) {
        $project_id = intval($project_id);
        $workspace = is_array($workspace) ? $workspace : array();
        $associations = is_array($associations) ? $associations : array();
        $clusters = isset($workspace['clusters']) && is_array($workspace['clusters']) ? $workspace['clusters'] : array();
        $canonical = 0;
        $onpage_mapped = 0;
        $resolved = 0;
        $unresolved = array();
        $canonical_urls = array();
        $status_counts = array(
            'mapped' => 0,
            'improve_existing_page' => 0,
            'review' => 0,
            'rejected' => 0,
            'excluded' => 0,
            'unknown' => 0,
        );

        foreach ($clusters as $cluster) {
            if (!is_array($cluster)) {
                continue;
            }
            $mapping_status = sanitize_key((string) ($cluster['mapping_status'] ?? ''));
            $selection_state = sanitize_key((string) ($cluster['selection_state'] ?? ''));
            if ($selection_state === 'excluded') {
                $status_counts['excluded']++;
                continue;
            }
            if ($mapping_status === '') {
                $mapping_status = 'unknown';
            }
            if (!isset($status_counts[$mapping_status])) {
                $status_counts['unknown']++;
            } else {
                $status_counts[$mapping_status]++;
            }
            if (!in_array($mapping_status, array('mapped', 'improve_existing_page', 'review'), true)) {
                continue;
            }
            $target_url = $this->aysa_keyword_mapping_extract_cluster_target_url($cluster);
            if ($target_url === '') {
                continue;
            }
            $keyword_candidates = $this->aysa_keyword_mapping_cluster_keyword_candidates($cluster, $selection_state ?: 'review');
            if (empty($keyword_candidates)) {
                continue;
            }
            $canonical++;
            if (in_array($mapping_status, array('mapped', 'improve_existing_page'), true)) {
                $onpage_mapped++;
            }
            $normalized_url = $this->aysa_keyword_mapping_normalize_url($target_url);
            if ($normalized_url !== '') {
                $canonical_urls[$normalized_url] = true;
            }
            $target = $this->aysa_keyword_mapping_resolve_local_association_target_by_url($target_url);
            if (is_array($target) && !empty($target['associated_on']) && intval($target['id_intern'] ?? 0) > 0) {
                $resolved++;
            } elseif (count($unresolved) < 50) {
                $unresolved[] = $target_url;
            }
        }

        $local_urls = array();
        foreach ($associations as $association) {
            if (!is_array($association)) {
                continue;
            }
            $association_url = (string) ($association['target_url'] ?? $association['link'] ?? '');
            $normalized_url = $this->aysa_keyword_mapping_normalize_url($association_url);
            if ($normalized_url !== '') {
                $local_urls[$normalized_url] = true;
            }
        }
        $stale = 0;
        foreach (array_keys($local_urls) as $local_url) {
            if (!isset($canonical_urls[$local_url])) {
                $stale++;
            }
        }

        return array(
            'project_id' => $project_id,
            'canonical_count' => $canonical,
            'canonical_keyword_mapping_count' => $canonical,
            'legacy_cache_count' => count($clusters),
            'local_associations_count' => count($associations),
            'onpage_mapped_count' => $onpage_mapped,
            'unresolved_url_count' => max(0, $canonical - $resolved),
            'stale_associations_count' => $stale,
            'status_counts' => $status_counts,
            'mapped_clusters' => $canonical,
            'resolved_entities' => $resolved,
            'unresolved_entities' => max(0, $canonical - $resolved),
            'local_associations' => count($associations),
            'unresolved_urls' => $unresolved,
        );
    }

    private function aysa_agent8_runtime_bundle_subset($bundle, $keys) {
        if (!is_array($bundle) || !is_array($keys) || empty($keys)) {
            return array();
        }

        $subset = array();
        foreach ($keys as $key) {
            $key = (string) $key;
            if ($key !== '' && array_key_exists($key, $bundle)) {
                $subset[$key] = $bundle[$key];
            }
        }

        return $subset;
    }

    private function aysa_agent8_get_offpage_gbp_overview_snapshot($project_id) {
        $project_id = intval($project_id);
        $site_host = wp_parse_url(home_url(), PHP_URL_HOST);
        $site_host = is_string($site_host) ? strtolower(trim($site_host)) : '';
        if ($project_id <= 0 || $site_host === '') {
            return array();
        }

        $key = 'aysa_gbp_overview_' . $project_id . '_' . md5($site_host);
        $overview = get_option($key, array());
        return is_array($overview) ? $overview : array();
    }

    private function aysa_agent8_get_offpage_orders_snapshot($project_id) {
        $orders = $this->aysa_get_adverlink_orders($project_id);
        return is_array($orders) ? array_values($orders) : array();
    }

    private function aysa_agent8_get_offpage_link_exchange_profile_snapshot($project_id) {
        $profile = $this->aysa_get_link_exchange_profile($project_id);
        return is_array($profile) ? $profile : array();
    }

    private function aysa_agent8_get_offpage_backlink_audit_snapshot($project_id) {
        $snapshot = get_option($this->aysa_get_backlink_audit_key($project_id), array());
        $last_sync = get_option($this->aysa_get_backlink_audit_sync_key($project_id), array());
        return array(
            'snapshot' => is_array($snapshot) ? $snapshot : array(),
            'last_sync' => is_array($last_sync) ? $last_sync : array(),
        );
    }

    private function aysa_agent8_get_offpage_draft_articles_snapshot($project_id) {
        $orders = $this->aysa_get_adverlink_orders($project_id);
        $items = array();
        foreach ((array) $orders as $order) {
            if (!is_array($order)) {
                continue;
            }
            if (sanitize_key((string) ($order['service_type'] ?? '')) !== 'seo_article') {
                continue;
            }
            $draft_id = intval($order['publisher_article_draft_id'] ?? 0);
            if ($draft_id <= 0) {
                continue;
            }
            $status = strtoupper(trim((string) ($order['status'] ?? '')));
            $draft_status = strtoupper(trim((string) ($order['publisher_article_draft_status'] ?? '')));
            if ($draft_status !== 'APPROVED' && $this->aysa_agent8_is_publisher_draft_approved($project_id, $draft_id)) {
                $draft_status = 'APPROVED';
            }
            if ($draft_status === 'APPROVED') {
                $status = 'APPROVED';
            }
            if (in_array($status, array('COMPLETED', 'PUBLISHED', 'LIVE'), true)) {
                continue;
            }
            $items[] = array(
                'draft_id' => $draft_id,
                'order_ref' => sanitize_text_field((string) ($order['order_ref'] ?? '')),
                'portal_domain' => sanitize_text_field((string) ($order['portal_domain'] ?? '')),
                'status' => $status,
                'draft_status' => $draft_status,
                'target_url' => esc_url_raw((string) ($order['target_url'] ?? '')),
                'created_at' => sanitize_text_field((string) ($order['created_at'] ?? '')),
            );
        }
        return array('items' => array_values($items));
    }

    private function aysa_agent8_get_offpage_published_articles_snapshot($project_id) {
        $orders = $this->aysa_get_adverlink_orders($project_id);
        $items = array();
        foreach ((array) $orders as $order) {
            if (!is_array($order)) {
                continue;
            }
            if (sanitize_key((string) ($order['service_type'] ?? '')) !== 'seo_article') {
                continue;
            }
            $status = strtoupper(trim((string) ($order['status'] ?? '')));
            if (!in_array($status, array('COMPLETED', 'PUBLISHED', 'LIVE'), true)) {
                continue;
            }
            $published_url = '';
            $source = null;
            if (isset($order['raw_track']) && is_array($order['raw_track'])) {
                $source = $order['raw_track'];
            } elseif (isset($order['raw_response']) && is_array($order['raw_response'])) {
                $source = $order['raw_response'];
            }
            if (is_array($source)) {
                $published_url = esc_url_raw((string) $this->aysa_find_first_string_by_keys_recursive($source, array(
                    'published_url', 'publishedUrl', 'insertion_url', 'insertionUrl',
                    'article_url', 'articleUrl', 'placement_url', 'placementUrl',
                    'source_url', 'sourceUrl', 'published_on_url', 'publishedOnUrl'
                )));
            }
            $items[] = array(
                'order_ref' => sanitize_text_field((string) ($order['order_ref'] ?? '')),
                'portal_domain' => sanitize_text_field((string) ($order['portal_domain'] ?? '')),
                'status' => $status,
                'published_url' => $published_url,
                'target_url' => esc_url_raw((string) ($order['target_url'] ?? '')),
                'draft_id' => intval($order['publisher_article_draft_id'] ?? 0),
            );
        }
        return array('items' => array_values($items));
    }

    private function aysa_agent8_get_existing_missing_page_titles($project_id) {
        global $wpdb;
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array();
        }
        $table = $wpdb->prefix . 'aysa_content_strategies';
        $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($exists !== $table) {
            return array();
        }
        $titles = $wpdb->get_col($wpdb->prepare(
            "SELECT title FROM {$table} WHERE project_id = %d AND strategy_type = 'missing_pages' AND COALESCE(automation_status, '') <> 'deleted'",
            $project_id
        ));
        return is_array($titles) ? array_values(array_map('strval', $titles)) : array();
    }

    private function aysa_agent8_get_technical_legacy_urls() {
        $base = admin_url('admin.php?page=aysa-technical-optimization');
        return array(
            'overview' => $base . '&aysa_tab=site-audit',
            'site_audit' => $base . '&aysa_tab=site-audit',
            'pagespeed' => $base . '&aysa_tab=speed',
            'sitemap_robots' => $base . '&aysa_tab=sitemap',
            'internal_linking' => $base . '&aysa_tab=internal-linking',
            'errors' => $base . '&aysa_tab=errors',
            'redirects' => $base . '&aysa_tab=redirects',
            'gone' => $base . '&aysa_tab=gone',
            'schema' => $base . '&aysa_tab=schema',
        );
    }

    private function aysa_agent8_get_keyword_mapping_review_payload($project_id) {
        $workspace = $this->aysa_keyword_v2_cache_get(intval($project_id), 'keyword_mapping_v2');
        if (!is_array($workspace)) {
            return $this->aysa_agent8_build_keyword_review_payload_from_local_snapshot($project_id);
        }

        $clusters = isset($workspace['clusters']) && is_array($workspace['clusters']) ? $workspace['clusters'] : array();
        $mapped = 0;
        $review = 0;
        $not_mapped = 0;
        foreach ($clusters as $cluster) {
            if (!is_array($cluster)) {
                continue;
            }
            $status = (string) ($cluster['mapping_status'] ?? '');
            if ($status === 'mapped') {
                $mapped++;
            } elseif ($status === 'review') {
                $review++;
            } elseif ($status === 'not_mapped') {
                $not_mapped++;
            }
        }

        return array(
            'summary' => array(
                'clusters_total' => count($clusters),
                'keywords_kept' => count($this->aysa_agent8_build_keyword_review_rows($workspace)),
                'mapped' => $mapped,
                'review' => $review,
                'not_mapped' => $not_mapped,
            ),
            'rows' => $this->aysa_agent8_build_keyword_review_rows($workspace),
        );
    }

    private function aysa_agent8_build_keyword_review_payload_from_local_snapshot($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array(
                'summary' => array(
                    'clusters_total' => 0,
                    'keywords_kept' => 0,
                    'mapped' => 0,
                    'review' => 0,
                    'not_mapped' => 0,
                ),
                'rows' => array(),
            );
        }

        $snapshot = get_option($this->aysa_keyword_discovery_snapshot_key($project_id), array());
        $keywords = isset($snapshot['keywords']) && is_array($snapshot['keywords']) ? $snapshot['keywords'] : array();
        $associations = $this->get_keyword_discovery_local_associations($project_id);
        $association_map = array();
        foreach ($associations as $association) {
            if (!is_array($association)) {
                continue;
            }
            $keyword = strtolower(trim((string) ($association['keyword'] ?? '')));
            if ($keyword === '') {
                continue;
            }
            $association_map[$keyword] = array(
                'associated_page' => trim((string) ($association['associatedOn'] ?? '')),
                'value' => trim((string) ($association['value'] ?? '')),
            );
        }

        $rows = array();
        $mapped = 0;
        $review = 0;
        foreach ($keywords as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['keyword'] ?? $row['query'] ?? ''));
            if ($keyword === '') {
                continue;
            }
            $normalized_keyword = strtolower($keyword);
            $association = isset($association_map[$normalized_keyword]) && is_array($association_map[$normalized_keyword])
                ? $association_map[$normalized_keyword]
                : array();
            $associated_page = trim((string) ($association['associated_page'] ?? ''));
            $status = $associated_page !== '' ? 'mapped' : 'review';
            if ($status === 'mapped') {
                $mapped++;
            } else {
                $review++;
            }
            $rows[] = array(
                'row_key' => md5($keyword . '|' . $associated_page),
                'cluster_id' => md5($keyword),
                'keyword' => $keyword,
                'volume' => max(0, intval($row['search_volume'] ?? $row['volume'] ?? 0)),
                'associated_page' => $associated_page,
                'status' => $status,
                'selected' => true,
            );
        }

        usort($rows, static function ($left, $right) {
            $volume_diff = ((int) ($right['volume'] ?? 0)) <=> ((int) ($left['volume'] ?? 0));
            if ($volume_diff !== 0) {
                return $volume_diff;
            }
            return strcmp((string) ($left['keyword'] ?? ''), (string) ($right['keyword'] ?? ''));
        });

        return array(
            'summary' => array(
                'clusters_total' => count($rows),
                'keywords_kept' => count($rows),
                'mapped' => $mapped,
                'review' => $review,
                'not_mapped' => 0,
            ),
            'rows' => array_values($rows),
            'source' => 'local_keyword_snapshot',
        );
    }

    private function aysa_agent8_build_keyword_review_rows($workspace) {
        $clusters = isset($workspace['clusters']) && is_array($workspace['clusters']) ? $workspace['clusters'] : array();
        $rows = array();
        foreach ($clusters as $cluster) {
            if (!is_array($cluster)) {
                continue;
            }

            $cluster_id = trim((string) ($cluster['cluster_id'] ?? ''));
            if ($cluster_id === '') {
                continue;
            }

            $cluster_match_url = $this->aysa_agent8_extract_keyword_associated_page($cluster);
            $keyword_rows = array();
            $primary = isset($cluster['primary_keyword']) && is_array($cluster['primary_keyword']) ? $cluster['primary_keyword'] : array();
            if (!empty($primary)) {
                $keyword_rows[] = $primary;
            }
            $secondary = isset($cluster['secondary_keywords']) && is_array($cluster['secondary_keywords']) ? $cluster['secondary_keywords'] : array();
            foreach ($secondary as $keyword_row) {
                if (is_array($keyword_row)) {
                    $keyword_rows[] = $keyword_row;
                }
            }

            foreach ($keyword_rows as $keyword_row) {
                $keyword = trim((string) ($keyword_row['keyword'] ?? ''));
                if ($keyword === '') {
                    continue;
                }
                $selection_state = trim((string) ($keyword_row['selection_state'] ?? $cluster['selection_state'] ?? 'review'));
                if ($selection_state === 'excluded') {
                    continue;
                }

                $rows[] = array(
                    'row_key' => $cluster_id . ':' . md5($keyword),
                    'cluster_id' => $cluster_id,
                    'keyword' => $keyword,
                    'volume' => max(0, (int) ($keyword_row['search_volume'] ?? 0)),
                    'associated_page' => $this->aysa_agent8_extract_keyword_associated_page($keyword_row) ?: $cluster_match_url,
                    'status' => $selection_state,
                    'selected' => $selection_state !== 'excluded',
                );
            }
        }

        usort($rows, static function ($left, $right) {
            $left_selected = !empty($left['selected']) ? 1 : 0;
            $right_selected = !empty($right['selected']) ? 1 : 0;
            if ($left_selected !== $right_selected) {
                return $right_selected <=> $left_selected;
            }

            $volume_diff = ((int) ($right['volume'] ?? 0)) <=> ((int) ($left['volume'] ?? 0));
            if ($volume_diff !== 0) {
                return $volume_diff;
            }

            return strcmp((string) ($left['keyword'] ?? ''), (string) ($right['keyword'] ?? ''));
        });

        return array_values($rows);
    }

    private function aysa_agent8_extract_keyword_associated_page($row) {
        if (!is_array($row)) {
            return '';
        }
        $matched_page = isset($row['matched_page']) && is_array($row['matched_page']) ? $row['matched_page'] : array();
        $url = trim((string) ($matched_page['url'] ?? ''));
        if ($url !== '') {
            return $url;
        }

        $best_page = isset($row['best_page_match']) && is_array($row['best_page_match']) ? $row['best_page_match'] : array();
        $url = trim((string) ($best_page['url'] ?? ''));
        if ($url !== '') {
            return $url;
        }

        $matches = isset($row['keyword_page_matches']) && is_array($row['keyword_page_matches']) ? $row['keyword_page_matches'] : array();
        if (!empty($matches)) {
            $first = $matches[0] ?? null;
            if (is_array($first)) {
                $url = trim((string) ($first['url'] ?? ''));
                if ($url !== '') {
                    return $url;
                }
            }
        }

        return '';
    }

    private function aysa_agent8_get_content_strategy_rows($project_id) {
        global $wpdb;
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array();
        }
        $plan_batch = $this->aysa_agent8_current_research_plan_batch($project_id);
        $table = $wpdb->prefix . 'aysa_content_strategies';
        $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($exists !== $table) {
            return array();
        }

        if ($plan_batch !== '') {
            $rows = $wpdb->get_results($wpdb->prepare(
                "SELECT *
                 FROM {$table}
                 WHERE project_id = %d
                   AND plan_batch = %s
                   AND COALESCE(automation_status, '') <> 'deleted'
                   AND duplicate_of_strategy_id IS NULL
                   AND superseded_by_strategy_id IS NULL
                 ORDER BY id ASC",
                $project_id,
                $plan_batch
            ), ARRAY_A);
            if (is_array($rows) && $rows !== array()) {
                return $rows;
            }
        }

        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT *
             FROM {$table}
             WHERE project_id = %d
               AND COALESCE(automation_status, '') <> 'deleted'
               AND duplicate_of_strategy_id IS NULL
               AND superseded_by_strategy_id IS NULL
             ORDER BY id ASC",
            $project_id
        ), ARRAY_A);

        return is_array($rows) ? $rows : array();
    }


    private function aysa_agent8_get_content_strategy_meta_map($project_id) {
        global $wpdb;
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array();
        }
        $table = $wpdb->prefix . 'aysa_missing_page_meta';
        $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($exists !== $table) {
            return array();
        }
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT strategy_id, seo_title, h1, meta_description, warning, updated_at
             FROM {$table}
             WHERE project_id = %d",
            $project_id
        ), ARRAY_A);
        $map = array();
        foreach ((array) $rows as $row) {
            $strategy_id = intval($row['strategy_id'] ?? 0);
            if ($strategy_id > 0) {
                $map[$strategy_id] = $row;
            }
        }
        return $map;
    }

    private function aysa_agent8_get_content_strategy_status_map($project_id) {
        global $wpdb;
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array();
        }
        $plan_batch = $this->aysa_agent8_current_research_plan_batch($project_id);
        $table = $wpdb->prefix . 'aysa_content_strategies';
        $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($exists !== $table) {
            return array();
        }
        if ($plan_batch !== '') {
            $rows = $wpdb->get_results($wpdb->prepare(
                "SELECT COALESCE(automation_status, 'none') AS s, COUNT(*) AS c
                 FROM {$table}
                 WHERE project_id = %d
                   AND plan_batch = %s
                 GROUP BY COALESCE(automation_status, 'none')",
                $project_id,
                $plan_batch
            ), ARRAY_A);
            $map = array();
            foreach ((array) $rows as $row) {
                $map[(string) ($row['s'] ?? 'none')] = intval($row['c'] ?? 0);
            }
            if ($map !== array()) {
                return $map;
            }
        }
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT COALESCE(automation_status, 'none') AS s, COUNT(*) AS c
             FROM {$table}
             WHERE project_id = %d
             GROUP BY COALESCE(automation_status, 'none')",
            $project_id
        ), ARRAY_A);
        $map = array();
        foreach ((array) $rows as $row) {
            $map[(string) ($row['s'] ?? 'none')] = intval($row['c'] ?? 0);
        }
        return $map;
    }

    private function aysa_agent8_get_content_strategy_next_item($project_id) {
        global $wpdb;
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return null;
        }
        $plan_batch = $this->aysa_agent8_current_research_plan_batch($project_id);
        $table = $wpdb->prefix . 'aysa_content_strategies';
        $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($exists !== $table) {
            return null;
        }
        if ($plan_batch !== '') {
            $row = $wpdb->get_row($wpdb->prepare(
                "SELECT id, title, scheduled_for
                 FROM {$table}
                 WHERE project_id = %d
                   AND plan_batch = %s
                   AND status = 'scheduled'
                   AND automation_status IN ('queued', 'retry')
                   AND scheduled_for IS NOT NULL
                 ORDER BY scheduled_for ASC
                 LIMIT 1",
                $project_id,
                $plan_batch
            ), ARRAY_A);
            if (is_array($row)) {
                return $row;
            }
        }
        $row = $wpdb->get_row($wpdb->prepare(
            "SELECT id, title, scheduled_for
             FROM {$table}
             WHERE project_id = %d
               AND status = 'scheduled'
               AND automation_status IN ('queued', 'retry')
               AND scheduled_for IS NOT NULL
             ORDER BY scheduled_for ASC
             LIMIT 1",
            $project_id
        ), ARRAY_A);
        return is_array($row) ? $row : null;
    }

    private function aysa_agent8_get_publishing_calendar_rows($project_id) {
        global $wpdb;
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array();
        }
        $plan_batch = $this->aysa_agent8_current_research_plan_batch($project_id);
        $table = $wpdb->prefix . 'aysa_content_strategies';
        $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($exists !== $table) {
            return array();
        }
        if ($plan_batch !== '') {
            $rows = $wpdb->get_results($wpdb->prepare(
                "SELECT id, title, strategy_type, recommended_page_type, content_type, execution_stage, intent, status, post_id, scheduled_for, generated_at, automation_status
                 FROM {$table}
                 WHERE project_id = %d
                   AND plan_batch = %s
                   AND COALESCE(automation_status, '') <> 'deleted'
                   AND duplicate_of_strategy_id IS NULL
                   AND superseded_by_strategy_id IS NULL
                 ORDER BY COALESCE(scheduled_for, created_at) ASC",
                $project_id,
                $plan_batch
            ), ARRAY_A);
            if (is_array($rows) && $rows !== array()) {
                return $rows;
            }
        }
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT id, title, strategy_type, recommended_page_type, content_type, execution_stage, intent, status, post_id, scheduled_for, generated_at, automation_status
             FROM {$table}
             WHERE project_id = %d
               AND COALESCE(automation_status, '') <> 'deleted'
               AND duplicate_of_strategy_id IS NULL
               AND superseded_by_strategy_id IS NULL
             ORDER BY COALESCE(scheduled_for, created_at) ASC",
            $project_id
        ), ARRAY_A);
        return is_array($rows) ? $rows : array();
    }

    private function aysa_agent8_strategy_type_label($strategy_type) {
        $strategy_type = sanitize_key((string) $strategy_type);
        switch ($strategy_type) {
            case 'cluster_titles':
            case 'cluster_based':
                return 'Cluster-based';
            case 'competitor_derived_titles':
            case 'competitor_analysis':
                return 'Competitor Analysis';
            case 'competitor_sitemap':
                return 'Legacy Content From Sitemaps';
            case 'intent_layering':
                return 'Intent Layering';
            case 'seasonal_event':
            case 'seasonal_events':
                return 'Seasonal & Events';
            case 'serp_features':
                return 'SERP Features';
            case 'evergreen_fresh_mix':
            case 'evergreen_fresh':
                return 'Evergreen + Fresh';
            case 'customer_journey':
                return 'Customer Journey';
            case 'local_seo':
                return 'Local SEO';
            case 'authority_building':
                return 'Authority Building';
            case 'persona_based':
                return 'Persona-based';
            case 'missing_pages':
                return 'Missing Pages Promoted To Plan';
            default:
                return ucwords(str_replace(array('_', '-'), ' ', $strategy_type));
        }
    }

    private function aysa_agent8_build_content_strategy_review_command($rows) {
        $rows = is_array($rows) ? $rows : array();
        $groups = array();
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $group_key = sanitize_key((string) ($row['strategy_type'] ?? 'content_strategy'));
            if ($group_key === '') {
                $group_key = 'content_strategy';
            }
            if (!isset($groups[$group_key])) {
                $groups[$group_key] = array(
                    'group_key' => $group_key,
                    'label' => $this->aysa_agent8_strategy_type_label($group_key),
                    'count' => 0,
                    'items' => array(),
                );
            }

            $keywords = array();
            if (isset($row['keywords'])) {
                $decoded = is_string($row['keywords']) ? json_decode($row['keywords'], true) : $row['keywords'];
                if (is_array($decoded)) {
                    $keywords = array_values(array_filter(array_map('strval', $decoded)));
                }
            }

            $groups[$group_key]['items'][] = array(
                'id' => intval($row['id'] ?? 0),
                'title' => trim((string) ($row['title'] ?? '')),
                'keywords' => $keywords,
                'recommended_page_type' => trim((string) ($row['recommended_page_type'] ?? $row['content_type'] ?? 'article')),
                'status' => trim((string) ($row['status'] ?? 'proposed')),
                'automation_status' => trim((string) ($row['automation_status'] ?? '')),
                'execution_stage' => trim((string) ($row['execution_stage'] ?? 'planned')),
                'associated_page' => trim((string) ($row['associated_page'] ?? '')),
                'scheduled_for' => trim((string) ($row['scheduled_for'] ?? '')),
                'generated_at' => trim((string) ($row['generated_at'] ?? '')),
                'published_at' => trim((string) ($row['published_at'] ?? '')),
                'edit_url' => trim((string) ($row['edit_url'] ?? '')),
                'public_url' => trim((string) ($row['public_url'] ?? '')),
            );
            $groups[$group_key]['count']++;
        }

        return array(
            'type' => 'open_content_strategy_review',
            'title' => 'Content Strategy',
            'intro' => 'Aici poți revizui fiecare articol propus, poți refuza itemii care nu sunt potriviți și poți asocia o pagină sau o adresă relevantă înainte să continui.',
            'groups' => array_values($groups),
        );
    }

    private function aysa_agent8_apply_content_strategy_updates($project_id, $updates) {
        global $wpdb;
        $project_id = intval($project_id);
        $updates = is_array($updates) ? $updates : array();
        if ($project_id <= 0 || $updates === array()) {
            return array('applied' => false, 'reason' => 'empty_content_strategy_updates');
        }

        $table = $wpdb->prefix . 'aysa_content_strategies';
        $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($exists !== $table) {
            return array('applied' => false, 'reason' => 'content_strategy_table_missing');
        }

        $updated = 0;
        $rejected = 0;
        $associated = 0;
        foreach ($updates as $item) {
            if (!is_array($item)) {
                continue;
            }
            $strategy_id = intval($item['id'] ?? 0);
            if ($strategy_id <= 0) {
                continue;
            }

            $data = array('automation_updated_at' => current_time('mysql'));
            $format = array('%s');
            if (!empty($item['rejected'])) {
                $data['automation_status'] = 'deleted';
                $format[] = '%s';
                $rejected++;
            } else {
                $data['associated_page'] = sanitize_text_field((string) ($item['associated_page'] ?? ''));
                $format[] = '%s';
                if ($data['associated_page'] !== '') {
                    $associated++;
                }
            }

            $result = $wpdb->update(
                $table,
                $data,
                array(
                    'project_id' => $project_id,
                    'id' => $strategy_id,
                ),
                $format,
                array('%d', '%d')
            );
            if ($result !== false) {
                $updated++;
            }
        }

        $this->aysa_keyword_v2_cache_delete($project_id, 'content_strategy_v2');

        return array(
            'applied' => true,
            'updated' => $updated,
            'rejected' => $rejected,
            'associated' => $associated,
        );
    }

    private function aysa_agent8_apply_local_mutations($project_id, $mutations) {
        $project_id = intval($project_id);
        $mutations = is_array($mutations) ? $mutations : array();
        if ($project_id <= 0 || empty($mutations)) {
            return array('applied' => false, 'reason' => 'empty_mutations');
        }

        $mutation_results = array();
        $handled_mutations = array();
        $collect_mutation_result = static function ($key, $result) use (&$mutation_results, &$handled_mutations) {
            $key = sanitize_key((string) $key);
            if ($key === '') {
                return;
            }
            $mutation_results[$key] = is_array($result)
                ? $result
                : array(
                    'applied' => false,
                    'reason' => 'invalid_mutation_result',
                );
            $handled_mutations[] = $key;
        };

        $tone_mutation = isset($mutations['tone_of_voice']) && is_array($mutations['tone_of_voice'])
            ? $mutations['tone_of_voice']
            : array();
        if ($tone_mutation !== array()) {
            $selected_tone = sanitize_text_field((string) ($tone_mutation['selected_tone'] ?? ''));
            if ($selected_tone === '') {
                $collect_mutation_result('tone_of_voice', array('applied' => false, 'reason' => 'invalid_tone_mutation'));
            } else {
                if (!class_exists('Aysa_Auth_Manager')) {
                    require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
                }

                try {
                    $auth_manager = Aysa_Auth_Manager::get_instance();
                    $existing = $auth_manager->make_authenticated_request('api/ToneOfVoice/ByProject/' . $project_id, 'GET');

                    $existing_id = 0;
                    if (is_array($existing)) {
                        if (!empty($existing['id'])) {
                            $existing_id = intval($existing['id']);
                        } elseif (isset($existing['data']) && is_array($existing['data']) && !empty($existing['data']['id'])) {
                            $existing_id = intval($existing['data']['id']);
                        }
                    }

                    $operation = 'create';
                    $ok = false;
                    if ($existing_id > 0) {
                        $operation = 'update';
                        $ok = $auth_manager->make_authenticated_request(
                            'api/ToneOfVoice/UpdateTone/' . $existing_id,
                            'PATCH',
                            array('selectedTone' => $selected_tone)
                        );
                    }

                    if (!$ok) {
                        $operation = 'create';
                        $ok = $auth_manager->make_authenticated_request(
                            'api/ToneOfVoice',
                            'POST',
                            array(
                                'projectId' => $project_id,
                                'selectedTone' => $selected_tone,
                            )
                        );
                    }

                    if (!$ok) {
                        $collect_mutation_result('tone_of_voice', array('applied' => false, 'reason' => 'tone_api_save_failed'));
                    } else {
                        $collect_mutation_result('tone_of_voice', array(
                            'applied' => true,
                            'tone_saved' => true,
                            'operation' => $operation,
                            'selected_tone' => $selected_tone,
                        ));
                    }
                } catch (Exception $e) {
                    $collect_mutation_result('tone_of_voice', array(
                        'applied' => false,
                        'reason' => 'tone_api_exception',
                        'message' => $e->getMessage(),
                    ));
                }
            }
        }

        $content_strategy_updates = isset($mutations['content_strategy_updates']) && is_array($mutations['content_strategy_updates'])
            ? $mutations['content_strategy_updates']
            : array();
        if ($content_strategy_updates !== array()) {
            $collect_mutation_result(
                'content_strategy_updates',
                $this->aysa_agent8_apply_content_strategy_updates($project_id, $content_strategy_updates)
            );
        }

        $onpage_operation = isset($mutations['onpage_operation']) && is_array($mutations['onpage_operation'])
            ? $mutations['onpage_operation']
            : array();
        if ($onpage_operation !== array()) {
            $operation = sanitize_key((string) ($onpage_operation['operation'] ?? ''));
            $collect_mutation_result('onpage_operation', match ($operation) {
                'accept_onpage_keyword_association' => $this->aysa_agent8_apply_onpage_mapping_operation($project_id, $onpage_operation, 'accept_mapping'),
                'reject_onpage_keyword_association' => $this->aysa_agent8_apply_onpage_mapping_operation($project_id, $onpage_operation, 'reject_mapping'),
                'apply_onpage_meta_for_page' => $this->aysa_agent8_apply_onpage_entity_operation($project_id, $onpage_operation, 'apply_meta'),
                'apply_onpage_rewrite_for_page' => $this->aysa_agent8_apply_onpage_entity_operation($project_id, $onpage_operation, 'apply_rewrite'),
                'skip_onpage_page' => $this->aysa_agent8_apply_onpage_entity_operation($project_id, $onpage_operation, 'skip'),
                'refresh_onpage_page' => $this->aysa_agent8_apply_onpage_entity_operation($project_id, $onpage_operation, 'refresh'),
                'apply_onpage_meta_batch', 'apply_meta' => $this->aysa_agent8_apply_onpage_batch_operation($project_id, $onpage_operation, 'apply_meta'),
                'apply_onpage_rewrite_batch', 'apply_rewrite' => $this->aysa_agent8_apply_onpage_batch_operation($project_id, $onpage_operation, 'apply_rewrite'),
                default => array('applied' => false, 'reason' => 'unsupported_onpage_operation'),
            });
        }

        $offpage_operation = isset($mutations['offpage_operation']) && is_array($mutations['offpage_operation'])
            ? $mutations['offpage_operation']
            : array();
        if ($offpage_operation !== array()) {
            $operation = sanitize_key((string) ($offpage_operation['operation'] ?? ''));
            $collect_mutation_result('offpage_operation', match ($operation) {
                'set_offpage_gbp_primary_location' => $this->aysa_agent8_apply_offpage_gbp_primary_location($project_id, $offpage_operation),
                'refresh_offpage_gbp_reviews' => $this->aysa_agent8_apply_offpage_gbp_reviews_refresh($project_id, $offpage_operation),
                'refresh_offpage_gbp_performance' => $this->aysa_agent8_apply_offpage_gbp_performance_refresh($project_id, $offpage_operation),
                'create_offpage_gbp_post' => $this->aysa_agent8_apply_offpage_gbp_create_post($project_id, $offpage_operation),
                'reply_offpage_gbp_review' => $this->aysa_agent8_apply_offpage_gbp_reply_review($project_id, $offpage_operation),
                'upload_offpage_gbp_media' => $this->aysa_agent8_apply_offpage_gbp_upload_media($project_id, $offpage_operation),
                'update_offpage_gbp_business_info' => $this->aysa_agent8_apply_offpage_gbp_update_business_info($project_id, $offpage_operation),
                'update_offpage_gbp_hours' => $this->aysa_agent8_apply_offpage_gbp_update_hours($project_id, $offpage_operation),
                'update_offpage_gbp_attributes' => $this->aysa_agent8_apply_offpage_gbp_update_attributes($project_id, $offpage_operation),
                'update_offpage_gbp_services' => $this->aysa_agent8_apply_offpage_gbp_update_services($project_id, $offpage_operation),
                'send_offpage_draft_article_approval_email' => $this->aysa_agent8_send_offpage_draft_article_approval_email($project_id, $offpage_operation),
                'recommend_offpage_link_insertion' => $this->aysa_agent8_recommend_offpage_purchase($project_id, $offpage_operation, 'link_insertion'),
                'preview_offpage_link_insertion_order' => $this->aysa_agent8_preview_offpage_purchase($project_id, $offpage_operation, 'link_insertion'),
                'send_offpage_link_insertion_approval_email' => $this->aysa_agent8_send_offpage_purchase_approval_email($project_id, $offpage_operation, 'link_insertion'),
                'place_offpage_link_insertion_order' => $this->aysa_agent8_place_offpage_purchase_order($project_id, $offpage_operation, 'link_insertion'),
                'recommend_offpage_seo_text_ads' => $this->aysa_agent8_recommend_offpage_purchase($project_id, $offpage_operation, 'seo_text_ads'),
                'preview_offpage_seo_text_ads_order' => $this->aysa_agent8_preview_offpage_purchase($project_id, $offpage_operation, 'seo_text_ads'),
                'send_offpage_seo_text_ads_approval_email' => $this->aysa_agent8_send_offpage_purchase_approval_email($project_id, $offpage_operation, 'seo_text_ads'),
                'place_offpage_seo_text_ads_order' => $this->aysa_agent8_place_offpage_purchase_order($project_id, $offpage_operation, 'seo_text_ads'),
                'approve_offpage_draft_article' => $this->aysa_agent8_approve_offpage_draft_article($project_id, array(
                    'draft_id' => intval($offpage_operation['draft_id'] ?? 0),
                )),
                'accept_offpage_exchange_request' => $this->aysa_agent8_start_offpage_async_operation($project_id, 'exchange_request_respond', array(
                    'request_id' => intval($offpage_operation['request_id'] ?? 0),
                    'exchange_ref' => sanitize_text_field((string) ($offpage_operation['exchange_ref'] ?? '')),
                    'decision' => sanitize_text_field((string) ($offpage_operation['decision'] ?? 'accept')),
                    'counterparty_target_url' => esc_url_raw((string) ($offpage_operation['counterparty_target_url'] ?? '')),
                    'counterparty_anchor_text' => sanitize_text_field((string) ($offpage_operation['counterparty_anchor_text'] ?? '')),
                    'counterparty_notes' => sanitize_textarea_field((string) ($offpage_operation['counterparty_notes'] ?? '')),
                )),
                'pause_offpage_exchange_request' => $this->aysa_agent8_start_offpage_async_operation($project_id, 'exchange_change_state', array(
                    'request_id' => intval($offpage_operation['request_id'] ?? 0),
                    'exchange_ref' => sanitize_text_field((string) ($offpage_operation['exchange_ref'] ?? '')),
                    'state_action' => sanitize_text_field((string) ($offpage_operation['state_action'] ?? 'pause')),
                    'reason' => sanitize_textarea_field((string) ($offpage_operation['reason'] ?? 'Paused from Agent8')),
                )),
                default => array('applied' => false, 'reason' => 'unsupported_offpage_operation'),
            });
        }

        $technical_site_audit_state = isset($mutations['technical_site_audit_state']) && is_array($mutations['technical_site_audit_state'])
            ? $mutations['technical_site_audit_state']
            : array();
        if ($technical_site_audit_state !== array()) {
            $collect_mutation_result(
                'technical_site_audit_state',
                $this->aysa_agent8_sync_technical_site_audit_state($project_id, $technical_site_audit_state)
            );
        }

        $technical_pagespeed_state = isset($mutations['technical_pagespeed_state']) && is_array($mutations['technical_pagespeed_state'])
            ? $mutations['technical_pagespeed_state']
            : array();
        if ($technical_pagespeed_state !== array()) {
            $collect_mutation_result(
                'technical_pagespeed_state',
                $this->aysa_agent8_sync_technical_pagespeed_state($project_id, $technical_pagespeed_state)
            );
        }

        $technical_schema_state = isset($mutations['technical_schema_state']) && is_array($mutations['technical_schema_state'])
            ? $mutations['technical_schema_state']
            : array();
        if ($technical_schema_state !== array()) {
            $collect_mutation_result(
                'technical_schema_state',
                $this->aysa_agent8_sync_technical_schema_state($project_id, $technical_schema_state)
            );
        }

        $technical_redirects_state = isset($mutations['technical_redirects_state']) && is_array($mutations['technical_redirects_state'])
            ? $mutations['technical_redirects_state']
            : array();
        if ($technical_redirects_state !== array()) {
            $collect_mutation_result(
                'technical_redirects_state',
                $this->aysa_agent8_sync_technical_redirects_state($project_id, $technical_redirects_state)
            );
        }

        $technical_sitemap_robots_state = isset($mutations['technical_sitemap_robots_state']) && is_array($mutations['technical_sitemap_robots_state'])
            ? $mutations['technical_sitemap_robots_state']
            : array();
        if ($technical_sitemap_robots_state !== array()) {
            $collect_mutation_result(
                'technical_sitemap_robots_state',
                $this->aysa_agent8_sync_technical_sitemap_robots_state($project_id, $technical_sitemap_robots_state)
            );
        }

        $technical_operation = isset($mutations['technical_operation']) && is_array($mutations['technical_operation'])
            ? $mutations['technical_operation']
            : array();
        if ($technical_operation !== array()) {
            $operation = sanitize_key((string) ($technical_operation['operation'] ?? ''));
            $collect_mutation_result('technical_operation', match ($operation) {
                'refresh_technical_site_audit' => array(
                    'applied' => true,
                    'operation' => $operation,
                    'snapshot' => $this->aysa_adjustments_run_technical_site_audit_refresh($project_id, true),
                ),
                'refresh_technical_pagespeed' => array(
                    'applied' => true,
                    'operation' => $operation,
                    'snapshot' => $this->aysa_adjustments_run_technical_pagespeed_refresh($project_id, true),
                ),
                'email_technical_pagespeed_pdf' => $this->aysa_agent8_email_technical_pagespeed_pdf($project_id, $technical_operation),
                'refresh_technical_sitemap_robots' => array(
                    'applied' => true,
                    'operation' => $operation,
                    'snapshot' => $this->aysa_adjustments_run_technical_sitemap_robots_refresh($project_id, true),
                ),
                'unblock_technical_robots' => $this->aysa_agent8_apply_unblock_technical_robots($project_id),
                'generate_aysa_sitemap' => $this->aysa_agent8_generate_technical_sitemap($project_id),
                'save_technical_robots' => $this->aysa_agent8_save_technical_robots($project_id, $technical_operation),
                'restore_recommended_robots' => $this->aysa_agent8_restore_technical_robots($project_id),
                'enforce_aysa_sitemap_authority' => $this->aysa_agent8_enforce_aysa_sitemap_authority($project_id),
                'disable_conflicting_sitemap_providers' => $this->aysa_agent8_enforce_aysa_sitemap_authority($project_id),
                'preview_technical_internal_linking' => array(
                    'applied' => true,
                    'operation' => $operation,
                    'preview' => $this->aysa_adjustments_run_technical_internal_linking_preview($project_id),
                    'preview_package' => $this->aysa_internal_linking_workspace_service()
                        ? $this->aysa_internal_linking_workspace_service()->build_preview_package($project_id)
                        : array(),
                    'snapshot' => $this->aysa_adjustments_get_technical_internal_linking_snapshot($project_id),
                ),
                'apply_technical_internal_linking' => array(
                    'applied' => false,
                    'operation' => $operation,
                    'guarded' => true,
                    'reason' => 'approved_preview_package_required',
                    'message' => 'Apply disabled. Requires approved package.',
                    'snapshot' => $this->aysa_adjustments_get_technical_internal_linking_snapshot($project_id),
                ),
                'apply_technical_schema_recommended' => $this->aysa_agent8_apply_technical_schema_operation($project_id, $technical_operation),
                'disable_technical_schema' => $this->aysa_agent8_disable_technical_schema_operation($project_id, $technical_operation),
                'apply_technical_redirect_candidate' => $this->aysa_agent8_apply_technical_redirect_candidate($project_id, $technical_operation),
                'mark_technical_gone_candidate' => $this->aysa_agent8_mark_technical_gone_candidate($project_id, $technical_operation),
                'ignore_technical_404_candidate' => $this->aysa_agent8_ignore_technical_404_candidate($project_id, $technical_operation),
                'apply_technical_redirects_batch' => $this->aysa_agent8_apply_technical_redirect_batch($project_id, $technical_operation, 'redirect'),
                'mark_technical_gone_batch' => $this->aysa_agent8_apply_technical_redirect_batch($project_id, $technical_operation, 'gone'),
                'ignore_technical_404_batch' => $this->aysa_agent8_apply_technical_redirect_batch($project_id, $technical_operation, 'ignore'),
                default => array('applied' => false, 'reason' => 'unsupported_technical_operation'),
            });
        }

        $monitoring_operation = isset($mutations['monitoring_operation']) && is_array($mutations['monitoring_operation'])
            ? $mutations['monitoring_operation']
            : array();
        if ($monitoring_operation !== array()) {
            $operation = sanitize_key((string) ($monitoring_operation['operation'] ?? ''));
            $collect_mutation_result('monitoring_operation', match ($operation) {
                'refresh_monitoring_rank_tracking' => $this->aysa_agent8_apply_monitoring_rank_refresh($project_id),
                'refresh_monitoring_ai_monitoring' => $this->aysa_agent8_apply_monitoring_ai_refresh($project_id),
                'refresh_monitoring_keyword_overview' => $this->aysa_agent8_apply_monitoring_keyword_overview_refresh($project_id),
                'save_monitoring_query_pack' => $this->aysa_agent8_apply_monitoring_query_pack_save($project_id, $monitoring_operation),
                default => array('applied' => false, 'reason' => 'unsupported_monitoring_operation'),
            });
        }

        $research_operation = isset($mutations['research_operation']) && is_array($mutations['research_operation'])
            ? $mutations['research_operation']
            : array();
        if ($research_operation !== array()) {
            $operation = sanitize_key((string) ($research_operation['operation'] ?? ''));
            $collect_mutation_result('research_operation', match ($operation) {
                'refresh_research_keyword_discovery' => $this->aysa_agent8_apply_research_keyword_discovery_refresh($project_id),
                'refresh_research_competitor_gap' => $this->aysa_agent8_apply_research_competitor_gap_refresh($project_id),
                'refresh_research_competitor_identification' => $this->aysa_agent8_apply_research_competitor_identification_refresh($project_id),
                'refresh_research_competitor_backlinks' => $this->aysa_agent8_apply_research_competitor_backlinks_refresh($project_id),
                'refresh_research_sitemap_competition' => $this->aysa_agent8_apply_research_sitemap_competition_refresh($project_id),
                default => array('applied' => false, 'reason' => 'unsupported_research_operation'),
            });
        }

        $insert_rows = isset($mutations['content_strategy_rows_to_insert']) && is_array($mutations['content_strategy_rows_to_insert'])
            ? $mutations['content_strategy_rows_to_insert']
            : array();
        if ($insert_rows !== array()) {
            $result = $this->aysa_missing_pages_insert_plan_rows($project_id, $insert_rows);
            $this->aysa_keyword_v2_cache_delete($project_id, 'content_strategy_v2');
            $this->aysa_keyword_v2_cache_delete($project_id, 'missing_pages_v2');

            $collect_mutation_result('content_strategy_rows_to_insert', array(
                'applied' => true,
                'inserted' => intval($result['added'] ?? 0),
                'existing' => intval($result['existing'] ?? 0),
                'errors' => intval($result['errors'] ?? 0),
            ));
        }

        $schedule_plan = isset($mutations['content_strategy_schedule_plan']) && is_array($mutations['content_strategy_schedule_plan'])
            ? $mutations['content_strategy_schedule_plan']
            : array();
        if ($schedule_plan !== array()) {
            $collect_mutation_result(
                'content_strategy_schedule_plan',
                $this->aysa_agent8_schedule_content_strategy_plan($project_id, $schedule_plan)
            );
        }

        if ($mutation_results === array()) {
            return array('applied' => false, 'reason' => 'no_supported_mutations');
        }

        if (count($mutation_results) === 1) {
            return reset($mutation_results);
        }

        $applied_any = false;
        $summary = array(
            'processed_mutations' => count($mutation_results),
            'applied_mutations' => 0,
            'failed_mutations' => 0,
        );
        $operations = array();
        $snapshot = array();
        $async_job = array();
        foreach ($mutation_results as $result) {
            if (!empty($result['applied'])) {
                $applied_any = true;
                $summary['applied_mutations']++;
            } else {
                $summary['failed_mutations']++;
            }

            if (!empty($result['operation'])) {
                $operations[] = sanitize_key((string) $result['operation']);
            }
            if (isset($result['summary']) && is_array($result['summary'])) {
                $summary = array_merge($summary, $result['summary']);
            }
            if (isset($result['snapshot']) && is_array($result['snapshot']) && $result['snapshot'] !== array()) {
                $snapshot = $result['snapshot'];
            }
            if (isset($result['async_job']) && is_array($result['async_job']) && $result['async_job'] !== array()) {
                $async_job = $result['async_job'];
            }
        }

        aysa_debug_log('[AYSA AGENT8 MUTATIONS] Processed multiple local mutation blocks: ' . wp_json_encode(array(
            'project_id' => $project_id,
            'handled_mutations' => array_values($handled_mutations),
            'operations' => array_values(array_unique(array_filter($operations))),
            'applied_any' => $applied_any,
        )));

        $aggregate_result = array(
            'applied' => $applied_any,
            'handled_mutations' => array_values($handled_mutations),
            'results' => $mutation_results,
            'summary' => $summary,
        );
        if (!$applied_any) {
            $aggregate_result['reason'] = 'no_mutations_applied';
        }
        if ($snapshot !== array()) {
            $aggregate_result['snapshot'] = $snapshot;
        }
        if ($async_job !== array()) {
            $aggregate_result['async_job'] = $async_job;
        }

        return $aggregate_result;
    }

    private function aysa_agent8_schedule_content_strategy_plan($project_id, $mutation) {
        global $wpdb;
        $project_id = intval($project_id);
        $mutation = is_array($mutation) ? $mutation : array();
        if ($project_id <= 0) {
            return array('applied' => false, 'reason' => 'invalid_project_id');
        }

        $table = $wpdb->prefix . 'aysa_content_strategies';
        $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($exists !== $table) {
            return array('applied' => false, 'reason' => 'content_strategy_table_missing');
        }

        $limit = max(0, intval($mutation['limit'] ?? 0));
        $plan_batch = $this->aysa_agent8_current_research_plan_batch($project_id);
        $where_batch = '';
        $params = array($project_id);
        if ($plan_batch !== '') {
            $where_batch = ' AND plan_batch = %s';
            $params[] = $plan_batch;
        }

        $sql = "SELECT id FROM {$table}
                WHERE project_id = %d
                  {$where_batch}
                  AND post_id IS NULL
                  AND scheduled_for IS NOT NULL
                  AND COALESCE(automation_status, '') NOT IN ('deleted','generated','queued','retry','processing','failed')
                  AND status IN ('proposed','planned','')
                  AND duplicate_of_strategy_id IS NULL
                  AND superseded_by_strategy_id IS NULL
                ORDER BY scheduled_for ASC, id ASC";
        if ($limit > 0) {
            $sql .= ' LIMIT ' . intval($limit);
        }

        $ids = $wpdb->get_col($wpdb->prepare($sql, $params));
        $ids = array_values(array_filter(array_map('intval', is_array($ids) ? $ids : array())));
        if ($ids === array()) {
            return array(
                'applied' => true,
                'scheduled' => 0,
                'queued' => 0,
                'reason' => 'no_dated_proposed_items',
            );
        }

        $placeholders = implode(',', array_fill(0, count($ids), '%d'));
        $update_sql = "UPDATE {$table}
                       SET status = 'scheduled',
                           automation_status = 'queued',
                           automation_attempts = 0,
                           last_error = NULL,
                           automation_updated_at = %s
                       WHERE project_id = %d
                         AND id IN ({$placeholders})";
        $update_params = array_merge(array(current_time('mysql'), $project_id), $ids);
        $updated = $wpdb->query($wpdb->prepare($update_sql, $update_params));

        wp_schedule_single_event(time() + 15, 'aysa_process_planned_content_now');
        $this->aysa_keyword_v2_cache_delete($project_id, 'content_strategy_v2');

        return array(
            'applied' => $updated !== false,
            'scheduled' => intval($updated),
            'queued' => intval($updated),
            'ids' => array_slice($ids, 0, 20),
            'plan_batch' => $plan_batch,
        );
    }

    private function aysa_agent8_current_research_plan_batch($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return '';
        }

        $run_id = intval(get_option($this->aysa_keyword_research_projection_applied_run_option_key($project_id), 0));
        if ($run_id <= 0) {
            return '';
        }

        return 'keyword_research_run_' . $run_id;
    }

    private function aysa_agent8_sync_technical_site_audit_state($project_id, $mutation) {
        $project_id = intval($project_id);
        $mutation = is_array($mutation) ? $mutation : array();
        if ($project_id <= 0) {
            return array('applied' => false, 'reason' => 'invalid_project_id');
        }

        $job_id = sanitize_text_field((string) ($mutation['job_id'] ?? ''));
        $run_id = intval($mutation['run_id'] ?? 0);
        $orchestration = sanitize_key((string) ($mutation['orchestration'] ?? ''));
        if ($job_id === '' && $run_id <= 0) {
            return array('applied' => false, 'reason' => 'missing_job_context');
        }

        $state_key = $this->aysa_technical_site_audit_state_key($project_id);
        $existing = get_option($state_key, array());
        if (!is_array($existing)) {
            $existing = array();
        }

        $is_new_job = ($job_id !== '' && $job_id !== sanitize_text_field((string) ($existing['job_id'] ?? '')))
            || ($run_id > 0 && $run_id !== intval($existing['run_id'] ?? 0));
        $state = array_merge($existing, array(
            'project_id' => $project_id,
            'run_id' => $run_id > 0 ? $run_id : intval($existing['run_id'] ?? 0),
            'job_id' => $job_id,
            'status' => sanitize_key((string) ($mutation['status'] ?? ($existing['status'] ?? 'running'))),
            'target_url' => esc_url_raw((string) ($mutation['target_url'] ?? ($existing['target_url'] ?? ''))),
            'started_at' => sanitize_text_field((string) ($mutation['started_at'] ?? ($existing['started_at'] ?? current_time('mysql')))),
            'updated_at' => current_time('mysql'),
            'provider' => sanitize_text_field((string) ($mutation['provider'] ?? ($existing['provider'] ?? 'aysa_site_audit'))),
            'source' => sanitize_text_field((string) ($mutation['source'] ?? ($existing['source'] ?? 'agent8'))),
            'orchestration' => $orchestration !== '' ? $orchestration : sanitize_key((string) ($existing['orchestration'] ?? 'legacy_polling')),
        ));

        if ($is_new_job) {
            unset($state['kickoff_email_sent_at'], $state['final_email_sent_at'], $state['last_completion_email_status']);
        }

        update_option($state_key, $state, false);
        if ($state['orchestration'] === 'worker_managed') {
            return array(
                'applied' => true,
                'operation' => 'sync_technical_site_audit_state',
                'job_id' => $job_id,
                'run_id' => intval($state['run_id'] ?? 0),
                'status' => (string) ($state['status'] ?? 'queued'),
                'kickoff_email_sent' => false,
            );
        }

        $state = $this->aysa_technical_site_audit_send_kickoff_email_if_needed($project_id, $state);
        $this->aysa_schedule_technical_site_audit_poll($project_id, 120);

        return array(
            'applied' => true,
            'operation' => 'sync_technical_site_audit_state',
            'job_id' => $job_id,
            'run_id' => intval($state['run_id'] ?? 0),
            'status' => (string) ($state['status'] ?? 'running'),
            'kickoff_email_sent' => !empty($state['kickoff_email_sent_at']),
        );
    }

    private function aysa_agent8_sync_technical_pagespeed_state($project_id, $mutation) {
        $project_id = intval($project_id);
        $mutation = is_array($mutation) ? $mutation : array();
        if ($project_id <= 0) {
            return array('applied' => false, 'reason' => 'invalid_project_id');
        }

        $run_id = intval($mutation['run_id'] ?? 0);
        if ($run_id <= 0) {
            return array('applied' => false, 'reason' => 'missing_run_context');
        }

        $state_key = $this->aysa_technical_pagespeed_state_key($project_id);
        $existing = get_option($state_key, array());
        if (!is_array($existing)) {
            $existing = array();
        }

        $state = array_merge($existing, array(
            'project_id' => $project_id,
            'run_id' => $run_id,
            'status' => sanitize_key((string) ($mutation['status'] ?? ($existing['status'] ?? 'queued'))),
            'target_url' => esc_url_raw((string) ($mutation['target_url'] ?? ($existing['target_url'] ?? ''))),
            'started_at' => sanitize_text_field((string) ($mutation['started_at'] ?? ($existing['started_at'] ?? current_time('mysql')))),
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'provider' => sanitize_text_field((string) ($mutation['provider'] ?? ($existing['provider'] ?? 'google_pagespeed_api'))),
            'source' => sanitize_text_field((string) ($mutation['source'] ?? ($existing['source'] ?? 'agent8'))),
            'orchestration' => sanitize_key((string) ($mutation['orchestration'] ?? ($existing['orchestration'] ?? 'worker_managed'))),
            'data' => isset($existing['data']) && is_array($existing['data']) ? $existing['data'] : array(),
        ));

        update_option($state_key, $state, false);

        return array(
            'applied' => true,
            'operation' => 'sync_technical_pagespeed_state',
            'run_id' => intval($state['run_id'] ?? 0),
            'status' => (string) ($state['status'] ?? 'queued'),
        );
    }

    private function aysa_agent8_sync_technical_schema_state($project_id, $mutation) {
        $project_id = intval($project_id);
        $mutation = is_array($mutation) ? $mutation : array();
        if ($project_id <= 0) {
            return array('applied' => false, 'reason' => 'invalid_project_id');
        }

        $run_id = intval($mutation['run_id'] ?? 0);
        if ($run_id <= 0) {
            return array('applied' => false, 'reason' => 'missing_run_context');
        }

        $state_key = $this->aysa_technical_schema_state_key($project_id);
        $existing = get_option($state_key, array());
        if (!is_array($existing)) {
            $existing = array();
        }

        $state = array_merge($existing, array(
            'project_id' => $project_id,
            'run_id' => $run_id,
            'status' => sanitize_key((string) ($mutation['status'] ?? ($existing['status'] ?? 'queued'))),
            'started_at' => sanitize_text_field((string) ($mutation['started_at'] ?? ($existing['started_at'] ?? current_time('mysql')))),
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'provider' => sanitize_text_field((string) ($mutation['provider'] ?? ($existing['provider'] ?? 'wp_local_snapshot'))),
            'source' => sanitize_text_field((string) ($mutation['source'] ?? ($existing['source'] ?? 'agent8'))),
            'orchestration' => sanitize_key((string) ($mutation['orchestration'] ?? ($existing['orchestration'] ?? 'worker_managed'))),
            'schema_markup_snapshot' => isset($existing['schema_markup_snapshot']) && is_array($existing['schema_markup_snapshot'])
                ? $existing['schema_markup_snapshot']
                : array(),
        ));

        update_option($state_key, $state, false);

        return array(
            'applied' => true,
            'operation' => 'sync_technical_schema_state',
            'run_id' => intval($state['run_id'] ?? 0),
            'status' => (string) ($state['status'] ?? 'queued'),
        );
    }

    private function aysa_agent8_sync_technical_redirects_state($project_id, $mutation) {
        $project_id = intval($project_id);
        $mutation = is_array($mutation) ? $mutation : array();
        if ($project_id <= 0) {
            return array('applied' => false, 'reason' => 'invalid_project_id');
        }

        $run_id = intval($mutation['run_id'] ?? 0);
        if ($run_id <= 0) {
            return array('applied' => false, 'reason' => 'missing_run_context');
        }

        $state_key = $this->aysa_technical_redirects_state_key($project_id);
        $existing = get_option($state_key, array());
        if (!is_array($existing)) {
            $existing = array();
        }

        $state = array_merge($existing, array(
            'project_id' => $project_id,
            'run_id' => $run_id,
            'status' => sanitize_key((string) ($mutation['status'] ?? ($existing['status'] ?? 'queued'))),
            'started_at' => sanitize_text_field((string) ($mutation['started_at'] ?? ($existing['started_at'] ?? current_time('mysql')))),
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'provider' => sanitize_text_field((string) ($mutation['provider'] ?? ($existing['provider'] ?? 'wp_local_snapshot'))),
            'source' => sanitize_text_field((string) ($mutation['source'] ?? ($existing['source'] ?? 'agent8'))),
            'orchestration' => sanitize_key((string) ($mutation['orchestration'] ?? ($existing['orchestration'] ?? 'worker_managed'))),
            'snapshot' => isset($existing['snapshot']) && is_array($existing['snapshot']) ? $existing['snapshot'] : array(),
        ));

        update_option($state_key, $state, false);

        return array(
            'applied' => true,
            'operation' => 'sync_technical_redirects_state',
            'run_id' => intval($state['run_id'] ?? 0),
            'status' => (string) ($state['status'] ?? 'queued'),
        );
    }

    private function aysa_agent8_sync_technical_sitemap_robots_state($project_id, $mutation) {
        $project_id = intval($project_id);
        $mutation = is_array($mutation) ? $mutation : array();
        if ($project_id <= 0) {
            return array('applied' => false, 'reason' => 'invalid_project_id');
        }

        $run_id = intval($mutation['run_id'] ?? 0);
        if ($run_id <= 0) {
            return array('applied' => false, 'reason' => 'missing_run_context');
        }

        $state_key = $this->aysa_technical_sitemap_robots_state_key($project_id);
        $existing = get_option($state_key, array());
        if (!is_array($existing)) {
            $existing = array();
        }

        $state = array_merge($existing, array(
            'project_id' => $project_id,
            'run_id' => $run_id,
            'status' => sanitize_key((string) ($mutation['status'] ?? ($existing['status'] ?? 'queued'))),
            'target_url' => esc_url_raw((string) ($mutation['target_url'] ?? ($existing['target_url'] ?? ''))),
            'started_at' => sanitize_text_field((string) ($mutation['started_at'] ?? ($existing['started_at'] ?? current_time('mysql')))),
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'provider' => sanitize_text_field((string) ($mutation['provider'] ?? ($existing['provider'] ?? 'wp_local_snapshot'))),
            'source' => sanitize_text_field((string) ($mutation['source'] ?? ($existing['source'] ?? 'agent8'))),
            'orchestration' => sanitize_key((string) ($mutation['orchestration'] ?? ($existing['orchestration'] ?? 'worker_managed'))),
            'data' => isset($existing['data']) && is_array($existing['data']) ? $existing['data'] : array(),
        ));

        update_option($state_key, $state, false);

        return array(
            'applied' => true,
            'operation' => 'sync_technical_sitemap_robots_state',
            'run_id' => intval($state['run_id'] ?? 0),
            'status' => (string) ($state['status'] ?? 'queued'),
        );
    }

    private function aysa_schedule_technical_site_audit_poll($project_id, $delay_seconds = 120) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return;
        }

        $timestamp = time() + max(30, intval($delay_seconds));
        wp_schedule_single_event($timestamp, 'aysa_technical_site_audit_poll_event', array($project_id));
        if (function_exists('spawn_cron')) {
            spawn_cron();
        }
    }

    private function aysa_technical_site_audit_notification_email($project_id) {
        $project_id = intval($project_id);

        $account_email = sanitize_email((string) get_option('aysa_account_email', ''));
        if ($account_email !== '' && is_email($account_email)) {
            return $account_email;
        }

        $configured_email = sanitize_email((string) get_option('aysa_email_address', ''));
        if ($configured_email !== '' && is_email($configured_email)) {
            return $configured_email;
        }

        $project_general = get_option('aysa_wizard_general_project_' . $project_id, array());
        if (is_array($project_general)) {
            $support_email = sanitize_email((string) ($project_general['support_email'] ?? ''));
            if ($support_email !== '' && is_email($support_email)) {
                return $support_email;
            }
        }

        if (class_exists('Aysa_Google_OAuth_Manager')) {
            try {
                $oauth_manager = Aysa_Google_OAuth_Manager::get_instance();
                if (is_object($oauth_manager) && method_exists($oauth_manager, 'get_status_for_project')) {
                    $oauth_status = $oauth_manager->get_status_for_project($project_id);
                    if (!is_wp_error($oauth_status) && is_array($oauth_status)) {
                        $google_email = sanitize_email((string) ($oauth_status['email'] ?? ''));
                        if ($google_email !== '' && is_email($google_email)) {
                            return $google_email;
                        }
                    }
                }
            } catch (Exception $e) {
                // Fall back to default notification target.
            }
        }

        return $this->get_notification_target_email();
    }

    private function aysa_agent8_communications_table_name() {
        global $wpdb;
        return $wpdb->prefix . 'aysa_agent8_communications';
    }

    private function aysa_agent8_ensure_communications_table() {
        global $wpdb;
        $table = $this->aysa_agent8_communications_table_name();
        $charset_collate = $wpdb->get_charset_collate();
        $sql = "CREATE TABLE $table (
            id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT,
            project_id bigint(20) UNSIGNED NOT NULL,
            conversation_id varchar(128) DEFAULT NULL,
            event_type varchar(80) NOT NULL,
            title varchar(255) NOT NULL,
            message text NOT NULL,
            payload longtext NULL,
            severity varchar(32) DEFAULT 'info',
            importance varchar(32) DEFAULT 'normal',
            channels_required text NULL,
            recipient varchar(191) DEFAULT NULL,
            provider varchar(64) DEFAULT NULL,
            provider_message_id varchar(191) DEFAULT NULL,
            email_status varchar(32) DEFAULT 'not_required',
            in_app_status varchar(32) DEFAULT 'unread',
            last_error text NULL,
            read_at datetime DEFAULT NULL,
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            updated_at datetime DEFAULT CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            KEY project_created (project_id, created_at),
            KEY project_read (project_id, in_app_status),
            KEY email_status (email_status)
        ) $charset_collate;";
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta($sql);
    }

    private function aysa_agent8_send_communication_email($recipient, $subject, $body) {
        if (!class_exists('Aysa_API_Config')) {
            require_once AYSA_PLUGIN_DIR . 'includes/config/aysa-api-config.php';
        }
        $secret_key = trim((string) get_option('aysa_user_secret_key', ''));
        if ($secret_key === '') {
            $secret_key = trim((string) get_option('aysa_active_access_key', ''));
        }
        if (!class_exists('Aysa_API_Config') || $secret_key === '') {
            return array('ok' => false, 'error' => 'postmark_api_context_missing', 'provider' => 'PostmarkMailer');
        }

        $html = '<div style="font-family:Arial,sans-serif;font-size:16px;line-height:1.55;color:#1f2937;">' . nl2br(esc_html((string) $body), false) . '</div>';
        $response = wp_remote_post(rtrim(Aysa_API_Config::get_base_url(), '/') . '/api/Agent8Communication/sendEmail', array(
            'timeout' => 30,
            'headers' => array('Content-Type' => 'application/json'),
            'body' => wp_json_encode(array(
                'secretKey' => $secret_key,
                'domainFromWordpress' => wp_parse_url(home_url(), PHP_URL_HOST),
                'to_email' => sanitize_email((string) $recipient),
                'subject' => sanitize_text_field((string) $subject),
                'text_body' => (string) $body,
                'html_body' => $html,
            )),
        ));
        if (is_wp_error($response)) {
            return array('ok' => false, 'error' => $response->get_error_message(), 'provider' => 'PostmarkMailer');
        }
        $data = json_decode((string) wp_remote_retrieve_body($response), true);
        if (!is_array($data)) {
            return array('ok' => false, 'error' => 'invalid_postmark_api_response', 'provider' => 'PostmarkMailer');
        }
        if (empty($data['provider'])) {
            $data['provider'] = 'PostmarkMailer';
        }
        return $data;
    }

    private function aysa_agent8_create_site_audit_communication_event($project_id, $event_type, $title, $message, $payload = array(), $severity = 'info') {
        global $wpdb;
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array('ok' => false, 'error' => 'missing_project_id');
        }
        $this->aysa_agent8_ensure_communications_table();
        $recipient = $this->aysa_technical_site_audit_notification_email($project_id);
        $email_status = $recipient !== '' && is_email($recipient) ? 'pending' : 'failed';
        $last_error = $email_status === 'failed' ? 'missing_or_invalid_recipient' : '';
        $channels = array('in_app', 'email');
        $wpdb->insert($this->aysa_agent8_communications_table_name(), array(
            'project_id' => $project_id,
            'conversation_id' => '',
            'event_type' => sanitize_key((string) $event_type),
            'title' => sanitize_text_field((string) $title),
            'message' => sanitize_textarea_field((string) $message),
            'payload' => wp_json_encode(is_array($payload) ? $payload : array()),
            'severity' => sanitize_key((string) $severity),
            'importance' => 'important',
            'channels_required' => wp_json_encode($channels),
            'recipient' => sanitize_email((string) $recipient),
            'provider' => 'PostmarkMailer',
            'provider_message_id' => '',
            'email_status' => $email_status,
            'in_app_status' => 'unread',
            'last_error' => $last_error,
            'created_at' => current_time('mysql'),
            'updated_at' => current_time('mysql'),
        ));
        $event_id = intval($wpdb->insert_id);
        if ($event_id > 0 && $email_status === 'pending') {
            $email = $this->aysa_agent8_send_communication_email($recipient, $title, $message);
            $email_status = !empty($email['ok']) ? 'sent' : 'failed';
            $last_error = !empty($email['ok']) ? '' : sanitize_text_field((string) ($email['error'] ?? 'postmark_send_failed'));
            $wpdb->update($this->aysa_agent8_communications_table_name(), array(
                'email_status' => $email_status,
                'provider_message_id' => sanitize_text_field((string) ($email['message_id'] ?? '')),
                'last_error' => $last_error,
                'updated_at' => current_time('mysql'),
            ), array('id' => $event_id));
        }

        return array(
            'ok' => $event_id > 0,
            'event_id' => $event_id,
            'recipient' => $recipient,
            'email_status' => $email_status,
            'last_error' => $last_error,
        );
    }

    private function aysa_technical_site_audit_create_external_communication_event($project_id, $payload = array()) {
        $payload = is_array($payload) ? $payload : array();
        $event_payload = isset($payload['payload']) && is_array($payload['payload']) ? $payload['payload'] : array();
        $event_payload['communication_source'] = 'api_worker_shared_service';
        return $this->aysa_agent8_create_site_audit_communication_event(
            $project_id,
            sanitize_key((string) ($payload['event_type'] ?? 'site_audit_worker_event')),
            sanitize_text_field((string) ($payload['title'] ?? 'Agent8 Site Audit update')),
            sanitize_textarea_field((string) ($payload['message'] ?? 'Site Audit worker update.')),
            $event_payload,
            sanitize_key((string) ($payload['severity'] ?? 'info'))
        );
    }

    private function aysa_technical_site_audit_send_kickoff_email_if_needed($project_id, $state) {
        $project_id = intval($project_id);
        $state = is_array($state) ? $state : array();
        if ($project_id <= 0 || !empty($state['kickoff_email_sent_at'])) {
            return $state;
        }

        $recipient = $this->aysa_technical_site_audit_notification_email($project_id);
        if ($recipient === '' || !is_email($recipient)) {
            return $state;
        }

        $job_id = sanitize_text_field((string) ($state['job_id'] ?? ''));
        $target_url = esc_url_raw((string) ($state['target_url'] ?? home_url('/')));
        $subject = 'AYSA Site Audit started';
        $body_lines = array(
            'AYSA Site Audit has started for your project.',
            '',
            $target_url !== '' ? ('Website: ' . $target_url) : '',
            'In plain terms, we are checking whether Google can crawl the site properly, whether important pages are broken, whether metadata is missing, and whether technical SEO blockers need fixing.',
            'You do not need to wait here. We will update you in Agent 8, and we will send another email when the audit is ready for review.',
            '',
            'Project ID: ' . $project_id,
            'Job ID: ' . $job_id,
            '',
            'Open Agent 8: ' . admin_url('admin.php?page=aysa-agent8'),
        );

        if (wp_mail($recipient, $subject, implode("\n", $body_lines), array('Content-Type: text/plain; charset=UTF-8'))) {
            $state['kickoff_email_sent_at'] = current_time('mysql');
            update_option($this->aysa_technical_site_audit_state_key($project_id), $state, false);
        }

        return $state;
    }

    private function aysa_technical_site_audit_send_final_email_if_needed($project_id, $state) {
        $project_id = intval($project_id);
        $state = is_array($state) ? $state : array();
        if ($project_id <= 0 || !empty($state['final_email_sent_at'])) {
            return $state;
        }

        $status = sanitize_key((string) ($state['status'] ?? ''));
        if (!in_array($status, array('completed', 'failed'), true)) {
            return $state;
        }

        $recipient = $this->aysa_technical_site_audit_notification_email($project_id);
        if ($recipient === '' || !is_email($recipient)) {
            return $state;
        }

        $audit = isset($state['audit_snapshot']) && is_array($state['audit_snapshot']) ? $state['audit_snapshot'] : array();
        $issues = intval($audit['count'] ?? 0);
        $pages = intval($audit['pages_crawled'] ?? 0);
        $health = intval($audit['site_health'] ?? 0);

        if ($status === 'completed') {
            $subject = 'AYSA Site Audit is ready';
            $body_lines = $this->aysa_technical_site_audit_build_popular_final_email_lines($audit, admin_url('admin.php?page=aysa-agent8'));
        } else {
            $subject = 'AYSA Site Audit finished with an issue';
            $body_lines = array(
                'AYSA Site Audit stopped before a usable review snapshot was produced.',
                '',
                'Status: ' . $status,
                'Error: ' . sanitize_text_field((string) ($state['last_error'] ?? 'unknown')),
                '',
                'Open Agent 8: ' . admin_url('admin.php?page=aysa-agent8'),
            );
        }

        if (wp_mail($recipient, $subject, implode("\n", $body_lines), array('Content-Type: text/plain; charset=UTF-8'))) {
            $state['final_email_sent_at'] = current_time('mysql');
            $state['last_completion_email_status'] = $status;
            update_option($this->aysa_technical_site_audit_state_key($project_id), $state, false);
        }

        return $state;
    }

    private function aysa_technical_site_audit_build_popular_final_email_lines($audit, $agent8_url) {
        $audit = is_array($audit) ? $audit : array();
        $issues = intval($audit['count'] ?? 0);
        $pages = intval($audit['pages_crawled'] ?? 0);
        $health = intval($audit['site_health'] ?? 0);
        $top_rows = isset($audit['rows']) && is_array($audit['rows']) ? array_values($audit['rows']) : array();

        $body_lines = array(
            'AYSA Site Audit finished and is ready for review.',
            '',
            'Quick summary:',
            '- Crawled pages: ' . $pages,
            '- Issues found: ' . $issues,
            '- Site health: ' . $health . '%',
            '',
            $this->aysa_technical_site_audit_build_popular_verdict_line($health, $issues),
            '',
            'What is good:',
            '- ' . $this->aysa_technical_site_audit_build_good_line($health, $issues, $pages),
            '',
            'What is not good:',
            '- ' . $this->aysa_technical_site_audit_build_bad_line($top_rows),
            '',
            'What should be done next:',
            '- ' . $this->aysa_technical_site_audit_build_next_step_line($top_rows),
        );

        $issue_lines = array();
        foreach (array_slice($top_rows, 0, 3) as $row) {
            if (!is_array($row)) {
                continue;
            }
            $title = trim((string) ($row['title'] ?? ''));
            if ($title === '') {
                continue;
            }
            $affected = trim((string) ($row['affected'] ?? ''));
            $issue_lines[] = $title . ($affected !== '' ? ' (' . $affected . ')' : '');
        }
        if ($issue_lines !== array()) {
            $body_lines[] = '';
            $body_lines[] = 'Main issues:';
            foreach ($issue_lines as $line) {
                $body_lines[] = '- ' . $line;
            }
        }

        $body_lines[] = '';
        $body_lines[] = 'Open Agent 8: ' . $agent8_url;

        return $body_lines;
    }

    private function aysa_technical_site_audit_build_popular_verdict_line($health, $issues) {
        $health = intval($health);
        $issues = intval($issues);

        if ($health >= 85 && $issues <= 5) {
            return 'Verdict: the technical base looks healthy overall, with only minor cleanup left.';
        }
        if ($health >= 60) {
            return 'Verdict: the site is usable, but technical issues are holding SEO back more than they should.';
        }

        return 'Verdict: there are serious technical issues that can limit crawling, indexing, or visibility.';
    }

    private function aysa_technical_site_audit_build_good_line($health, $issues, $pages) {
        $health = intval($health);
        $issues = intval($issues);
        $pages = intval($pages);

        if ($health >= 85 && $issues <= 5) {
            return 'Google should be able to understand and crawl most of the site without major technical blockers.';
        }
        if ($pages > 0) {
            return 'We now have a full technical snapshot, so the problems are clear and can be prioritized properly.';
        }

        return 'The audit collected enough data to show where the main interventions are needed.';
    }

    private function aysa_technical_site_audit_build_bad_line($rows) {
        $rows = is_array($rows) ? $rows : array();
        $top = isset($rows[0]) && is_array($rows[0]) ? $rows[0] : array();
        $title = trim((string) ($top['title'] ?? ''));
        $affected = trim((string) ($top['affected'] ?? ''));

        if ($title !== '') {
            return $title . ($affected !== '' ? ' affects ' . $affected . '.' : '.');
        }

        return 'There are technical issues that can confuse Google or weaken important pages.';
    }

    private function aysa_technical_site_audit_build_next_step_line($rows) {
        $rows = is_array($rows) ? $rows : array();
        $top = isset($rows[0]) && is_array($rows[0]) ? $rows[0] : array();
        $title = strtolower(trim((string) ($top['title'] ?? '')));

        if ($title !== '') {
            if (strpos($title, 'meta') !== false) {
                return 'Start with missing or duplicated metadata on the most important commercial pages.';
            }
            if (strpos($title, 'internal') !== false || strpos($title, 'inlink') !== false) {
                return 'Strengthen internal linking to important pages so Google can discover and support them better.';
            }
            if (strpos($title, 'h1') !== false || strpos($title, 'h2') !== false) {
                return 'Clean up the heading structure in the templates and keep only useful sections.';
            }
            if (strpos($title, '4xx') !== false || strpos($title, '5xx') !== false || strpos($title, 'broken') !== false) {
                return 'Repair or redirect broken URLs first so they stop wasting traffic and crawl budget.';
            }
            if (strpos($title, 'canonical') !== false) {
                return 'Review canonical tags so the site does not send mixed indexing signals to Google.';
            }
            if (strpos($title, 'noindex') !== false) {
                return 'Review noindex directives and keep blocked only the pages that truly should stay out of Google.';
            }
        }

        return 'Open Site Audit in Agent 8 and fix first the issues that affect the most pages or block Google directly.';
    }

    public function handle_technical_site_audit_poll_event($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return;
        }

        $state_key = $this->aysa_technical_site_audit_state_key($project_id);
        $state = get_option($state_key, array());
        if (!is_array($state)) {
            $state = array();
        }

        $job_id = sanitize_text_field((string) ($state['job_id'] ?? ''));
        if ($job_id === '') {
            return;
        }

        $status_result = $this->aysa_broker_request('POST', '/v1/technical/screamingfrog/status', array('job_id' => $job_id), 45);
        if (is_wp_error($status_result)) {
            $this->aysa_schedule_technical_site_audit_poll($project_id, 180);
            return;
        }

        if (is_array($status_result)) {
            $state = array_merge($state, array(
                'project_id' => $project_id,
                'job_id' => $job_id,
                'status' => (string) ($status_result['status'] ?? ($state['status'] ?? 'unknown')),
                'updated_at' => current_time('mysql'),
                'job_dir' => (string) ($status_result['job_dir'] ?? ($state['job_dir'] ?? '')),
                'mode' => (string) ($status_result['mode'] ?? ($state['mode'] ?? 'baseline_full')),
                'config_path' => (string) ($status_result['config_path'] ?? ($state['config_path'] ?? '')),
                'export_tabs' => (string) ($status_result['export_tabs'] ?? ($state['export_tabs'] ?? '')),
                'bulk_exports' => (string) ($status_result['bulk_exports'] ?? ($state['bulk_exports'] ?? '')),
                'reports' => (string) ($status_result['reports'] ?? ($state['reports'] ?? '')),
                'artifacts' => isset($status_result['artifacts']) && is_array($status_result['artifacts']) ? $status_result['artifacts'] : array(),
            ));
            update_option($state_key, $state, false);
        }

        $status = sanitize_key((string) ($state['status'] ?? 'unknown'));
        if (in_array($status, array('queued', 'running', 'unknown'), true)) {
            $this->aysa_schedule_technical_site_audit_poll($project_id, 180);
            return;
        }

        if ($status === 'completed') {
            $results = $this->aysa_broker_request('POST', '/v1/technical/screamingfrog/results', array(
                'job_id' => $job_id,
                'preview_rows' => 50,
            ), 60);
            if (is_array($results)) {
                $state = array_merge($state, array(
                    'status' => (string) ($results['status'] ?? $status),
                    'updated_at' => current_time('mysql'),
                    'mode' => (string) ($results['mode'] ?? ($state['mode'] ?? 'baseline_full')),
                    'config_path' => (string) ($results['config_path'] ?? ($state['config_path'] ?? '')),
                    'export_tabs' => (string) ($results['export_tabs'] ?? ($state['export_tabs'] ?? '')),
                    'bulk_exports' => (string) ($results['bulk_exports'] ?? ($state['bulk_exports'] ?? '')),
                    'reports' => (string) ($results['reports'] ?? ($state['reports'] ?? '')),
                    'result_count' => count(isset($results['files']) && is_array($results['files']) ? $results['files'] : array()),
                    'latest_output_dir' => (string) ($results['latest_output_dir'] ?? ''),
                    'audit_snapshot' => isset($results['audit']) && is_array($results['audit']) ? $results['audit'] : array(),
                    'result_files' => isset($results['files']) && is_array($results['files']) ? $results['files'] : array(),
                ));
                $version_id = $this->aysa_sf_site_audit_persist_version($project_id, $state);
                if ($version_id > 0) {
                    $latest_version = $this->aysa_sf_site_audit_get_latest_version_row($project_id);
                    if (is_array($latest_version) && !empty($latest_version)) {
                        $state['version_id'] = intval($latest_version['id'] ?? 0);
                        $state['version_number'] = intval($latest_version['version_number'] ?? 0);
                    }
                }
            }
        }

        update_option($state_key, $state, false);
        $state = $this->aysa_technical_site_audit_send_final_email_if_needed($project_id, $state);
        $this->aysa_project_jobs_finalize(
            $project_id,
            $this->aysa_project_jobs_make_worker_ref('technical_site_audit', 'site_audit', (string) ($state['job_id'] ?? '')),
            (string) ($state['status'] ?? 'failed'),
            isset($state['audit_snapshot']) && is_array($state['audit_snapshot']) ? $state['audit_snapshot'] : $state,
            (string) ($state['last_error'] ?? '')
        );
    }

    private function aysa_agent8_refresh_action_result_after_local_mutations($response_result, $project_id) {
        $response_result = is_array($response_result) ? $response_result : array();
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return $response_result;
        }

        $action_result = isset($response_result['action_result']) && is_array($response_result['action_result'])
            ? $response_result['action_result']
            : array();
        $action_key = sanitize_key((string) ($action_result['action_key'] ?? ''));
        if (in_array($action_key, array(
            'recommend_offpage_link_insertion',
            'send_offpage_link_insertion_approval_email',
            'place_offpage_link_insertion_order',
            'recommend_offpage_seo_text_ads',
            'send_offpage_seo_text_ads_approval_email',
            'place_offpage_seo_text_ads_order',
        ), true)) {
            $local_mutation_result = isset($response_result['local_mutation_result']) && is_array($response_result['local_mutation_result'])
                ? $response_result['local_mutation_result']
                : array();
            if (!empty($local_mutation_result['assistant_message'])) {
                $response_result['assistant_message'] = (string) $local_mutation_result['assistant_message'];
            }
            if (isset($local_mutation_result['suggested_actions']) && is_array($local_mutation_result['suggested_actions'])) {
                $response_result['suggested_actions'] = $local_mutation_result['suggested_actions'];
            }
            if (isset($local_mutation_result['summary']) && is_array($local_mutation_result['summary'])) {
                $action_result['summary'] = array_merge(
                    isset($action_result['summary']) && is_array($action_result['summary']) ? $action_result['summary'] : array(),
                    $local_mutation_result['summary']
                );
            }
            if (isset($local_mutation_result['projection']) && is_array($local_mutation_result['projection'])) {
                $action_result['projection'] = $local_mutation_result['projection'];
            }
            if (!empty($local_mutation_result['payment_link'])) {
                $response_result['suggested_actions'][] = array(
                    'type' => 'open_url',
                    'label' => 'Open payment link',
                    'url' => esc_url_raw((string) $local_mutation_result['payment_link']),
                );
            }
            $response_result['action_result'] = $action_result;
            return $response_result;
        }
        if ($action_key === 'send_missing_pages_to_plan' || $action_key === 'update_content_strategy_items' || $action_key === 'schedule_publishing_plan') {
            $content_strategy_rows = $this->aysa_agent8_get_content_strategy_rows($project_id);
            $content_strategy_meta_map = $this->aysa_agent8_get_content_strategy_meta_map($project_id);
            $content_strategy_statuses = $this->aysa_agent8_get_content_strategy_status_map($project_id);
            $content_strategy_next = $this->aysa_agent8_get_content_strategy_next_item($project_id);
            $publishing_calendar_rows = $this->aysa_agent8_get_publishing_calendar_rows($project_id);

            $action_result['workspace_snapshot'] = array(
                'content_strategy_rows' => $content_strategy_rows,
                'content_strategy_statuses' => $content_strategy_statuses,
                'content_strategy_next' => $content_strategy_next,
                'publishing_calendar_rows' => $publishing_calendar_rows,
            );
            if ($action_key === 'update_content_strategy_items') {
                $action_result['summary'] = array(
                    'items' => count($content_strategy_rows),
                    'statuses' => $content_strategy_statuses,
                    'next' => $content_strategy_next,
                );
                $action_result['projection'] = array(
                    'type' => 'content_strategy',
                    'summary' => $action_result['summary'],
                );
                $action_result['ui_command'] = $this->aysa_agent8_build_content_strategy_review_command($content_strategy_rows);
            } elseif ($action_key === 'schedule_publishing_plan') {
                $mutation_result = isset($response_result['local_mutation_result']) && is_array($response_result['local_mutation_result'])
                    ? $response_result['local_mutation_result']
                    : array();
                $action_result['summary'] = array(
                    'scheduled' => intval($mutation_result['scheduled'] ?? 0),
                    'queued' => intval($mutation_result['queued'] ?? 0),
                    'items' => count($content_strategy_rows),
                    'statuses' => $content_strategy_statuses,
                    'next' => $content_strategy_next,
                );
                $action_result['projection'] = array(
                    'type' => 'publishing_plan_schedule_result',
                    'summary' => $action_result['summary'],
                    'publishing_calendar' => array(
                        'events' => count($publishing_calendar_rows),
                    ),
                );
            } else {
                $action_result['projection'] = array(
                    'type' => 'plan_mutation_result',
                    'summary' => isset($action_result['summary']) && is_array($action_result['summary']) ? $action_result['summary'] : array(),
                    'content_strategy' => array(
                        'items' => count($content_strategy_rows),
                        'statuses' => $content_strategy_statuses,
                        'next' => $content_strategy_next,
                    ),
                    'publishing_calendar' => array(
                        'events' => count($publishing_calendar_rows),
                    ),
                );
            }
            $action_result['refreshed_runtime_bundle'] = array(
                'content_strategy_rows' => $content_strategy_rows,
                'content_strategy_meta_map' => $content_strategy_meta_map,
                'content_strategy_statuses' => $content_strategy_statuses,
                'content_strategy_next' => $content_strategy_next,
                'publishing_calendar_rows' => $publishing_calendar_rows,
            );

            $response_result['action_result'] = $action_result;
            return $response_result;
        }

        if (in_array($action_key, array(
            'refresh_research_keyword_discovery',
            'refresh_research_competitor_gap',
            'refresh_research_competitor_identification',
            'refresh_research_competitor_backlinks',
            'refresh_research_sitemap_competition',
            'set_offpage_gbp_primary_location',
            'refresh_offpage_gbp_reviews',
            'refresh_offpage_gbp_performance',
            'create_offpage_gbp_post',
            'reply_offpage_gbp_review',
            'upload_offpage_gbp_media',
            'update_offpage_gbp_business_info',
            'update_offpage_gbp_hours',
            'update_offpage_gbp_attributes',
            'update_offpage_gbp_services',
            'accept_offpage_exchange_request',
            'pause_offpage_exchange_request',
            'approve_offpage_draft_article',
            'accept_onpage_keyword_association',
            'reject_onpage_keyword_association',
            'refresh_technical_pagespeed',
            'email_technical_pagespeed_pdf',
            'refresh_technical_sitemap_robots',
            'unblock_technical_robots',
            'generate_aysa_sitemap',
            'save_technical_robots',
            'restore_recommended_robots',
            'enforce_aysa_sitemap_authority',
            'disable_conflicting_sitemap_providers',
            'preview_technical_internal_linking',
            'apply_technical_internal_linking',
            'apply_technical_schema_recommended',
            'disable_technical_schema',
            'apply_technical_redirect_candidate',
            'mark_technical_gone_candidate',
            'ignore_technical_404_candidate',
            'apply_technical_redirects_batch',
            'mark_technical_gone_batch',
            'ignore_technical_404_batch',
            'refresh_monitoring_rank_tracking',
            'refresh_monitoring_ai_monitoring',
            'refresh_monitoring_keyword_overview',
            'save_monitoring_query_pack',
            'apply_onpage_meta_batch',
            'apply_onpage_rewrite_batch',
        ), true)) {
            $local_mutation_result = isset($response_result['local_mutation_result']) && is_array($response_result['local_mutation_result'])
                ? $response_result['local_mutation_result']
                : array();
            $snapshot = isset($local_mutation_result['snapshot']) && is_array($local_mutation_result['snapshot'])
                ? $local_mutation_result['snapshot']
                : array();
            if ($snapshot !== array()) {
                $action_result['workspace_snapshot'] = $snapshot;
                $action_result['summary'] = array_merge(
                    isset($action_result['summary']) && is_array($action_result['summary']) ? $action_result['summary'] : array(),
                    isset($local_mutation_result['summary']) && is_array($local_mutation_result['summary']) ? $local_mutation_result['summary'] : array()
                );
                if (in_array($action_key, array('apply_onpage_meta_batch', 'apply_onpage_rewrite_batch'), true)) {
                    $summary = isset($action_result['summary']) && is_array($action_result['summary']) ? $action_result['summary'] : array();
                    $is_rewrite = $action_key === 'apply_onpage_rewrite_batch';
                    $response_result['assistant_message'] = 'Applied ' . ($is_rewrite ? 'rewrites' : 'meta') . ' to ' . intval($summary['applied_count'] ?? 0)
                        . ' pages out of ' . intval($summary['attempted_count'] ?? ($summary['selected'] ?? 0))
                        . ' attempted. Skipped: ' . intval($summary['skipped_count'] ?? 0)
                        . ', failed: ' . intval($summary['failed_count'] ?? 0)
                        . '. Remaining eligible ' . ($is_rewrite ? 'rewrites' : 'meta') . ': ' . intval($summary['remaining_eligible_count'] ?? 0) . '.';
                }
                if (in_array($action_key, array(
                    'refresh_research_keyword_discovery',
                    'refresh_research_competitor_gap',
                    'refresh_research_competitor_identification',
                    'refresh_research_competitor_backlinks',
                    'refresh_research_sitemap_competition',
                ), true)) {
                    $action_result['refreshed_runtime_bundle'] = array(
                        'research_snapshot' => $this->aysa_adjustments_get_research_snapshot($project_id),
                        'research_sitemap_competition' => $this->aysa_agent8_get_research_sitemap_competition_snapshot($project_id),
                    );
                    if (!empty($local_mutation_result['async_job']) && is_array($local_mutation_result['async_job'])) {
                        $action_result['async_job'] = $local_mutation_result['async_job'];
                    }
                } elseif (in_array($action_key, array(
                    'set_offpage_gbp_primary_location',
                    'refresh_offpage_gbp_reviews',
                    'refresh_offpage_gbp_performance',
                    'create_offpage_gbp_post',
                    'reply_offpage_gbp_review',
                    'upload_offpage_gbp_media',
                    'update_offpage_gbp_business_info',
                    'update_offpage_gbp_hours',
                    'update_offpage_gbp_attributes',
                    'update_offpage_gbp_services',
                    'accept_offpage_exchange_request',
                    'pause_offpage_exchange_request',
                    'approve_offpage_draft_article',
                ), true)) {
                    $action_result['refreshed_runtime_bundle'] = $this->aysa_agent8_offpage_runtime_bundle_for_action($project_id, $action_key);
                    if (!empty($local_mutation_result['async_job']) && is_array($local_mutation_result['async_job'])) {
                        $action_result['async_job'] = $local_mutation_result['async_job'];
                    }
                } elseif (in_array($action_key, array(
                    'refresh_monitoring_rank_tracking',
                    'refresh_monitoring_ai_monitoring',
                    'refresh_monitoring_keyword_overview',
                    'save_monitoring_query_pack',
                ), true)) {
                    $action_result['refreshed_runtime_bundle'] = array(
                        'monitoring_rank_tracking' => $this->aysa_agent8_get_monitoring_rank_tracking_snapshot($project_id),
                        'monitoring_ai_overview' => $this->aysa_agent8_get_monitoring_ai_overview_snapshot($project_id),
                        'monitoring_query_packs' => $this->aysa_agent8_get_monitoring_query_packs_snapshot($project_id),
                    );
                    if (!empty($local_mutation_result['async_job']) && is_array($local_mutation_result['async_job'])) {
                        $action_result['async_job'] = $local_mutation_result['async_job'];
                    }
                } else {
                    $action_result['refreshed_runtime_bundle'] = array(
                        'technical_snapshot' => $snapshot,
                    );
                }
                $response_result['action_result'] = $action_result;
            }
            return $response_result;
        }

        return $response_result;
    }

    private function aysa_agent8_apply_technical_schema_operation($project_id, array $technical_operation) {
        $project_id = intval($project_id);
        $url = $this->aysa_adjustments_normalize_schema_url((string) ($technical_operation['url'] ?? ''));
        $schema_type = sanitize_key((string) ($technical_operation['schema_type'] ?? 'webpage'));
        $schema_action = sanitize_key((string) ($technical_operation['schema_action'] ?? 'rewrite_recommended'));
        if ($project_id <= 0 || $url === '') {
            return array('applied' => false, 'reason' => 'schema_target_required');
        }

        $item = array(
            'url' => $url,
            'schema_type' => $schema_type !== '' ? $schema_type : 'webpage',
            'primary_recommended_node' => $schema_type !== '' ? $schema_type : 'webpage',
        );
        $result = $this->aysa_technical_schema_run_action($project_id, $schema_action, $item);
        if (is_wp_error($result)) {
            return array(
                'applied' => false,
                'reason' => sanitize_key((string) $result->get_error_code()),
                'message' => (string) $result->get_error_message(),
            );
        }

        return array(
            'applied' => true,
            'operation' => 'apply_technical_schema_recommended',
            'summary' => array(
                'url' => $url,
                'schema_type' => $schema_type,
                'schema_action' => $schema_action,
            ),
            'snapshot' => $this->aysa_adjustments_get_cached_or_idle_technical_schema_snapshot($project_id),
        );
    }

    private function aysa_agent8_disable_technical_schema_operation($project_id, array $technical_operation) {
        $project_id = intval($project_id);
        $url = $this->aysa_adjustments_normalize_schema_url((string) ($technical_operation['url'] ?? ''));
        $schema_type = sanitize_key((string) ($technical_operation['schema_type'] ?? ''));
        if ($project_id <= 0 || $url === '') {
            return array('applied' => false, 'reason' => 'schema_target_required');
        }

        $result = $this->aysa_technical_schema_execute_disable($project_id, $url, $schema_type);
        if (is_wp_error($result)) {
            return array(
                'applied' => false,
                'reason' => sanitize_key((string) $result->get_error_code()),
                'message' => (string) $result->get_error_message(),
            );
        }

        return array(
            'applied' => true,
            'operation' => 'disable_technical_schema',
            'summary' => array(
                'url' => $url,
                'schema_type' => $schema_type,
            ),
            'snapshot' => $this->aysa_adjustments_get_cached_or_idle_technical_schema_snapshot($project_id),
        );
    }

    private function aysa_agent8_apply_technical_redirect_candidate($project_id, array $technical_operation) {
        $project_id = intval($project_id);
        $source = $this->aysa_normalize_redirect_source((string) ($technical_operation['source_url'] ?? ''));
        $target = $this->aysa_normalize_redirect_destination((string) ($technical_operation['target_url'] ?? ''));
        if ($project_id <= 0 || $source === '' || $target === '') {
            return array('applied' => false, 'reason' => 'redirect_target_required');
        }

        $result = $this->aysa_add_htaccess_redirect_rule($source, $target);
        if (empty($result['ok'])) {
            return array(
                'applied' => false,
                'reason' => sanitize_key((string) ($result['code'] ?? 'redirect_apply_failed')),
                'message' => (string) ($result['message'] ?? 'Redirect apply failed.'),
            );
        }

        return array(
            'applied' => empty($result['already_done']),
            'already_done' => !empty($result['already_done']),
            'operation' => 'apply_technical_redirect_candidate',
            'summary' => array(
                'source_url' => $source,
                'target_url' => $target,
                'already_done' => !empty($result['already_done']),
            ),
            'snapshot' => $this->aysa_adjustments_get_technical_errors_snapshot($project_id),
        );
    }

    private function aysa_agent8_apply_onpage_mapping_operation($project_id, array $onpage_operation, $row_action) {
        $project_id = intval($project_id);
        $entity_key = sanitize_text_field((string) ($onpage_operation['entity_key'] ?? ''));
        $row_action = sanitize_key((string) $row_action);
        if ($project_id <= 0 || $entity_key === '' || !in_array($row_action, array('accept_mapping', 'reject_mapping'), true)) {
            return array('applied' => false, 'reason' => 'onpage_mapping_target_required');
        }

        $service = $this->aysa_onpage_workspace_service();
        if (!$service) {
            return array('applied' => false, 'reason' => 'onpage_workspace_service_unavailable');
        }

        $result = $row_action === 'accept_mapping'
            ? $service->accept_mapping_review($project_id, $entity_key)
            : $service->reject_mapping_review($project_id, $entity_key);
        if (!is_array($result) || empty($result['ok'])) {
            return array(
                'applied' => false,
                'reason' => sanitize_key((string) ($result['reason'] ?? 'onpage_mapping_apply_failed')),
                'message' => (string) ($result['message'] ?? 'On-page mapping operation failed.'),
            );
        }

        return array(
            'applied' => true,
            'operation' => $row_action === 'accept_mapping' ? 'accept_onpage_keyword_association' : 'reject_onpage_keyword_association',
            'summary' => array(
                'entity_key' => $entity_key,
                'row_action' => $row_action,
            ),
            'snapshot' => $this->aysa_agent8_get_onpage_workspace_snapshot($project_id),
        );
    }

    private function aysa_agent8_apply_onpage_entity_operation($project_id, array $onpage_operation, $row_action) {
        $project_id = intval($project_id);
        $entity_key = sanitize_text_field((string) ($onpage_operation['entity_key'] ?? ''));
        $row_action = sanitize_key((string) $row_action);
        if ($project_id <= 0 || $entity_key === '' || !in_array($row_action, array('apply_meta', 'apply_rewrite', 'skip', 'refresh'), true)) {
            return array('applied' => false, 'reason' => 'onpage_entity_target_required');
        }

        $service = $this->aysa_onpage_workspace_service();
        if (!$service) {
            return array('applied' => false, 'reason' => 'onpage_workspace_service_unavailable');
        }

        $result = match ($row_action) {
            'apply_meta' => $service->apply_meta_output($project_id, $entity_key),
            'apply_rewrite' => $service->apply_rewrite_output($project_id, $entity_key),
            'skip' => $service->skip_entity($project_id, $entity_key),
            'refresh' => $service->refresh_entity($project_id, $entity_key),
            default => array('ok' => false, 'error' => 'unsupported_row_action'),
        };
        if (!is_array($result) || empty($result['ok'])) {
            return array(
                'applied' => false,
                'reason' => sanitize_key((string) ($result['error'] ?? 'onpage_entity_apply_failed')),
                'message' => (string) ($result['message'] ?? 'On-page entity operation failed.'),
            );
        }

        return array(
            'applied' => true,
            'operation' => $row_action,
            'summary' => array(
                'entity_key' => $entity_key,
                'row_action' => $row_action,
            ),
            'snapshot' => $this->aysa_agent8_get_onpage_workspace_snapshot($project_id),
        );
    }

    private function aysa_agent8_apply_onpage_batch_operation($project_id, array $onpage_operation, $operation) {
        $project_id = intval($project_id);
        $operation = sanitize_key((string) $operation);
        if ($project_id <= 0 || !in_array($operation, array('apply_meta', 'apply_rewrite'), true)) {
            return array('applied' => false, 'reason' => 'onpage_batch_target_required');
        }

        $service = $this->aysa_onpage_workspace_service();
        if (!$service) {
            return array('applied' => false, 'reason' => 'onpage_workspace_service_unavailable');
        }

        $selection = isset($onpage_operation['selection']) && is_array($onpage_operation['selection'])
            ? array_values(array_filter(array_map('sanitize_text_field', $onpage_operation['selection'])))
            : array();
        $filters = isset($onpage_operation['filters']) && is_array($onpage_operation['filters'])
            ? $onpage_operation['filters']
            : array();
        $entity_group = sanitize_key((string) ($onpage_operation['entity_group'] ?? 'page'));
        $batch_size_raw = $onpage_operation['batch_size'] ?? null;
        $batch_size = is_string($batch_size_raw) && strtolower($batch_size_raw) === 'all'
            ? 'all'
            : max(0, intval($batch_size_raw));
        $batch_mode = sanitize_key((string) ($onpage_operation['batch_mode'] ?? 'first_eligible'));

        $result = $service->batch_apply_operation($project_id, $entity_group, $filters, $selection, $operation, array(
            'batch_size' => $batch_size,
            'batch_mode' => $batch_mode !== '' ? $batch_mode : 'first_eligible',
        ));
        if (!is_array($result) || empty($result['ok'])) {
            return array(
                'applied' => false,
                'reason' => sanitize_key((string) ($result['error'] ?? 'onpage_batch_apply_failed')),
                'message' => (string) ($result['message'] ?? 'On-page batch operation failed.'),
            );
        }

        return array(
            'applied' => true,
            'operation' => $operation,
            'summary' => array(
                'selected' => intval($result['selected'] ?? 0),
                'eligible' => intval($result['eligible'] ?? 0),
                'applied_count' => intval($result['applied'] ?? 0),
                'failed_count' => intval($result['failed'] ?? 0),
                'skipped_count' => intval($result['skipped'] ?? 0),
                'attempted_count' => intval($result['attempted_count'] ?? ($result['selected'] ?? 0)),
                'remaining_eligible_count' => intval($result['remaining_eligible_count'] ?? 0),
            ),
            'snapshot' => $this->aysa_agent8_get_onpage_workspace_snapshot($project_id),
        );
    }

    private function aysa_agent8_apply_offpage_gbp_primary_location($project_id, array $offpage_operation) {
        $project_id = intval($project_id);
        $location_name = sanitize_text_field((string) ($offpage_operation['location_name'] ?? ''));
        if ($project_id <= 0 || $location_name === '') {
            return array('applied' => false, 'reason' => 'gbp_location_required');
        }

        $site_host = wp_parse_url(home_url(), PHP_URL_HOST);
        $site_host = is_string($site_host) ? strtolower(trim($site_host)) : '';
        if ($site_host === '') {
            return array('applied' => false, 'reason' => 'site_host_required');
        }

        $result = $this->aysa_broker_request('POST', '/v1/offpage/gbp/location/set-primary', array(
            'project_id' => $project_id,
            'site_host' => $site_host,
            'location_name' => $location_name,
        ), 45);
        if (is_wp_error($result)) {
            return array(
                'applied' => false,
                'reason' => 'gbp_primary_location_failed',
                'message' => $this->aysa_humanize_broker_error($result, 'Could not set primary GBP location.'),
            );
        }

        $overview = $this->aysa_broker_request('POST', '/v1/offpage/gbp/overview', array(
            'project_id' => $project_id,
            'site_host' => $site_host,
        ), 60);
        if (!is_wp_error($overview) && is_array($overview)) {
            update_option('aysa_gbp_overview_' . $project_id . '_' . md5($site_host), $overview, false);
        } else {
            $overview = $this->aysa_agent8_get_offpage_gbp_overview_snapshot($project_id);
        }

        return array(
            'applied' => true,
            'operation' => 'set_offpage_gbp_primary_location',
            'summary' => array(
                'location_name' => $location_name,
            ),
            'snapshot' => is_array($overview) ? $overview : array(),
        );
    }

    private function aysa_agent8_apply_offpage_gbp_reviews_refresh($project_id, array $offpage_operation) {
        return $this->aysa_agent8_apply_offpage_gbp_direct_proxy(
            $project_id,
            '/v1/offpage/gbp/reviews/list',
            array(
                'location_name' => sanitize_text_field((string) ($offpage_operation['location_name'] ?? '')),
                'force_refresh' => 1,
            ),
            'refresh_offpage_gbp_reviews'
        );
    }

    private function aysa_agent8_apply_offpage_gbp_performance_refresh($project_id, array $offpage_operation) {
        return $this->aysa_agent8_apply_offpage_gbp_direct_proxy(
            $project_id,
            '/v1/offpage/gbp/performance/get',
            array(
                'location_name' => sanitize_text_field((string) ($offpage_operation['location_name'] ?? '')),
                'force_refresh' => 1,
            ),
            'refresh_offpage_gbp_performance'
        );
    }

    private function aysa_agent8_apply_offpage_gbp_create_post($project_id, array $offpage_operation) {
        $summary = sanitize_textarea_field((string) ($offpage_operation['summary'] ?? ''));
        if ($summary === '') {
            return array('applied' => false, 'reason' => 'gbp_post_summary_required');
        }

        return $this->aysa_agent8_apply_offpage_gbp_direct_proxy(
            $project_id,
            '/v1/offpage/gbp/posts/create',
            array(
                'location_name' => sanitize_text_field((string) ($offpage_operation['location_name'] ?? '')),
                'post' => array(
                    'summary' => $summary,
                ),
            ),
            'create_offpage_gbp_post',
            array(
                'post_preview' => $summary,
            )
        );
    }

    private function aysa_agent8_apply_offpage_gbp_reply_review($project_id, array $offpage_operation) {
        $review_name = sanitize_text_field((string) ($offpage_operation['review_name'] ?? ''));
        $comment = sanitize_textarea_field((string) ($offpage_operation['comment'] ?? ''));
        if ($review_name === '' || $comment === '') {
            return array('applied' => false, 'reason' => $review_name === '' ? 'review_name_required' : 'review_comment_required');
        }

        return $this->aysa_agent8_apply_offpage_gbp_direct_proxy(
            $project_id,
            '/v1/offpage/gbp/reviews/reply',
            array(
                'review_name' => $review_name,
                'comment' => $comment,
            ),
            'reply_offpage_gbp_review',
            array(
                'review_name' => $review_name,
            )
        );
    }

    private function aysa_agent8_apply_offpage_gbp_upload_media($project_id, array $offpage_operation) {
        $media_url = esc_url_raw((string) ($offpage_operation['media_url'] ?? ''));
        if ($media_url === '') {
            return array('applied' => false, 'reason' => 'media_url_required');
        }

        return $this->aysa_agent8_apply_offpage_gbp_direct_proxy(
            $project_id,
            '/v1/offpage/gbp/media/upload',
            array(
                'location_name' => sanitize_text_field((string) ($offpage_operation['location_name'] ?? '')),
                'media' => array(
                    'sourceUrl' => $media_url,
                ),
            ),
            'upload_offpage_gbp_media',
            array(
                'media_url' => $media_url,
            )
        );
    }

    private function aysa_agent8_apply_offpage_gbp_update_business_info($project_id, array $offpage_operation) {
        $patch = isset($offpage_operation['patch']) && is_array($offpage_operation['patch']) ? $offpage_operation['patch'] : array();
        if ($patch === array()) {
            return array('applied' => false, 'reason' => 'gbp_patch_required');
        }

        return $this->aysa_agent8_apply_offpage_gbp_direct_proxy(
            $project_id,
            '/v1/offpage/gbp/location/update',
            array(
                'location_name' => sanitize_text_field((string) ($offpage_operation['location_name'] ?? '')),
                'patch' => $patch,
            ),
            'update_offpage_gbp_business_info'
        );
    }

    private function aysa_agent8_apply_offpage_gbp_update_hours($project_id, array $offpage_operation) {
        $regular = isset($offpage_operation['regularHours']) && is_array($offpage_operation['regularHours']) ? $offpage_operation['regularHours'] : array();
        $special = isset($offpage_operation['specialHours']) && is_array($offpage_operation['specialHours']) ? $offpage_operation['specialHours'] : array();
        if ($regular === array() && $special === array()) {
            return array('applied' => false, 'reason' => 'gbp_hours_required');
        }

        return $this->aysa_agent8_apply_offpage_gbp_direct_proxy(
            $project_id,
            '/v1/offpage/gbp/location/hours',
            array(
                'location_name' => sanitize_text_field((string) ($offpage_operation['location_name'] ?? '')),
                'regularHours' => $regular,
                'specialHours' => $special,
            ),
            'update_offpage_gbp_hours'
        );
    }

    private function aysa_agent8_apply_offpage_gbp_update_attributes($project_id, array $offpage_operation) {
        $attributes = isset($offpage_operation['attributes']) && is_array($offpage_operation['attributes']) ? array_values($offpage_operation['attributes']) : array();
        if ($attributes === array()) {
            return array('applied' => false, 'reason' => 'gbp_attributes_required');
        }

        return $this->aysa_agent8_apply_offpage_gbp_direct_proxy(
            $project_id,
            '/v1/offpage/gbp/location/attributes',
            array(
                'location_name' => sanitize_text_field((string) ($offpage_operation['location_name'] ?? '')),
                'attributes' => $attributes,
            ),
            'update_offpage_gbp_attributes',
            array(
                'count' => count($attributes),
            )
        );
    }

    private function aysa_agent8_apply_offpage_gbp_update_services($project_id, array $offpage_operation) {
        $service_items = isset($offpage_operation['serviceItems']) && is_array($offpage_operation['serviceItems']) ? array_values($offpage_operation['serviceItems']) : array();
        if ($service_items === array()) {
            return array('applied' => false, 'reason' => 'gbp_services_required');
        }

        return $this->aysa_agent8_apply_offpage_gbp_direct_proxy(
            $project_id,
            '/v1/offpage/gbp/location/services',
            array(
                'location_name' => sanitize_text_field((string) ($offpage_operation['location_name'] ?? '')),
                'serviceItems' => $service_items,
            ),
            'update_offpage_gbp_services',
            array(
                'count' => count($service_items),
            )
        );
    }

    private function aysa_agent8_apply_offpage_gbp_direct_proxy($project_id, $route, array $extra_payload, $operation, array $extra_summary = array()) {
        $project_id = intval($project_id);
        $route = (string) $route;
        $operation = sanitize_key((string) $operation);
        $location_name = sanitize_text_field((string) ($extra_payload['location_name'] ?? ''));
        if ($project_id <= 0 || $route === '' || $location_name === '') {
            return array('applied' => false, 'reason' => 'gbp_location_required');
        }

        $site_host = wp_parse_url(home_url(), PHP_URL_HOST);
        $site_host = is_string($site_host) ? strtolower(trim($site_host)) : '';
        if ($site_host === '') {
            return array('applied' => false, 'reason' => 'site_host_required');
        }

        $result = $this->aysa_broker_request('POST', $route, array_merge(array(
            'project_id' => $project_id,
            'site_host' => $site_host,
        ), $extra_payload), 60);
        if (is_wp_error($result)) {
            return array(
                'applied' => false,
                'reason' => $operation . '_failed',
                'message' => $this->aysa_humanize_broker_error($result, 'Could not execute GBP action.'),
            );
        }

        $overview = $this->aysa_broker_request('POST', '/v1/offpage/gbp/overview', array(
            'project_id' => $project_id,
            'site_host' => $site_host,
        ), 60);
        if (!is_wp_error($overview) && is_array($overview)) {
            update_option('aysa_gbp_overview_' . $project_id . '_' . md5($site_host), $overview, false);
        } else {
            $overview = $this->aysa_agent8_get_offpage_gbp_overview_snapshot($project_id);
        }

        return array(
            'applied' => true,
            'operation' => $operation,
            'summary' => array_merge(array(
                'location_name' => $location_name,
            ), $extra_summary),
            'snapshot' => is_array($overview) ? $overview : array(),
        );
    }

    private function aysa_agent8_send_offpage_draft_article_approval_email($project_id, array $offpage_operation) {
        $draft_id = intval($offpage_operation['draft_id'] ?? 0);
        if ($draft_id <= 0) {
            return array('applied' => false, 'reason' => 'missing_draft_id');
        }

        $draft = array();
        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/publisher-articles/get', array(
            'draft_id' => $draft_id,
        ), 60);
        if (!is_wp_error($broker_result) && isset($broker_result['draft']) && is_array($broker_result['draft'])) {
            $draft = $broker_result['draft'];
        }

        $current_user = wp_get_current_user();
        $recipient = ($current_user && !empty($current_user->user_email)) ? sanitize_email((string) $current_user->user_email) : '';
        if ($recipient === '' || !is_email($recipient)) {
            $recipient = sanitize_email((string) get_option('admin_email', ''));
        }
        if ($recipient === '' || !is_email($recipient)) {
            return array('applied' => false, 'reason' => 'missing_recipient');
        }

        $title = sanitize_text_field((string) ($draft['title'] ?? 'Adverlink article draft'));
        $excerpt = trim(wp_strip_all_tags((string) ($draft['excerpt'] ?? '')));
        $portal_domain = sanitize_text_field((string) ($offpage_operation['portal_domain'] ?? ($draft['portal_domain'] ?? '')));
        $target_url = esc_url_raw((string) ($offpage_operation['target_url'] ?? ($draft['target_url'] ?? '')));
        $order_ref = sanitize_text_field((string) ($offpage_operation['order_ref'] ?? ($draft['order_ref'] ?? '')));
        $agent8_url = admin_url('admin.php?page=aysa-agent8');

        $subject = 'Article ready for approval: ' . $title;
        $body_lines = array(
            'An Adverlink article is ready for your review in Agent8.',
            '',
            'Article title: ' . $title,
            'Portal/domain: ' . ($portal_domain !== '' ? $portal_domain : '-'),
            'Target URL: ' . ($target_url !== '' ? $target_url : '-'),
            'Order ref: ' . ($order_ref !== '' ? $order_ref : '-'),
            'Draft ID: ' . $draft_id,
        );
        if ($excerpt !== '') {
            $body_lines[] = '';
            $body_lines[] = 'Short summary:';
            $body_lines[] = $excerpt;
        }
        $body_lines[] = '';
        $body_lines[] = 'Open Agent8 to preview and approve the article:';
        $body_lines[] = $agent8_url;
        $body_lines[] = '';
        $body_lines[] = 'Important: this email does not approve or publish anything. Approval must happen inside Agent8/admin. Approving this changes Adverlink state and may cost credits.';

        $email = $this->aysa_agent8_send_communication_email($recipient, $subject, implode("\n", $body_lines));
        if (empty($email['ok'])) {
            return array(
                'applied' => false,
                'reason' => 'email_failed',
                'provider' => $email['provider'] ?? 'PostmarkMailer',
                'error' => $email['error'] ?? 'unknown_error',
            );
        }

        return array(
            'applied' => true,
            'operation' => 'send_offpage_draft_article_approval_email',
            'draft_id' => $draft_id,
            'recipient' => $recipient,
            'provider' => $email['provider'] ?? 'PostmarkMailer',
        );
    }

    private function aysa_agent8_offpage_purchase_setup_user_id() {
        $setup_user_id = intval(get_option('aysa_user_id', 0));
        if ($setup_user_id <= 0) {
            $setup_user_id = intval($this->get_user_id());
        }
        if ($setup_user_id <= 0) {
            $setup_user_id = intval($this->aysa_get_authenticated_external_user_id());
        }
        if ($setup_user_id <= 0) {
            $setup_user_id = get_current_user_id();
        }
        return intval($setup_user_id);
    }

    private function aysa_agent8_recommend_offpage_purchase($project_id, array $offpage_operation, $flow) {
        $language = sanitize_key((string) ($offpage_operation['language'] ?? 'en'));
        $setup_user_id = $this->aysa_agent8_offpage_purchase_setup_user_id();
        $legacy = $flow === 'seo_text_ads'
            ? $this->aysa_adjustments_recommend_seo_text_ads_portals($project_id, $setup_user_id, $language)
            : $this->aysa_adjustments_recommend_link_insertion_portals($project_id, $setup_user_id, $language);
        $candidates = isset($legacy['state_meta']['offpage_purchase_candidates']) && is_array($legacy['state_meta']['offpage_purchase_candidates'])
            ? array_values($legacy['state_meta']['offpage_purchase_candidates'])
            : array();
        $keyword = sanitize_text_field((string) ($legacy['state_meta']['offpage_purchase_keyword'] ?? ''));
        $suggested_actions = array();
        foreach (array_slice($candidates, 0, 3) as $candidate) {
            if (!is_array($candidate)) {
                continue;
            }
            $payload = $this->aysa_agent8_normalize_offpage_purchase_payload($candidate, $flow, $keyword);
            if (empty($payload['portal_id']) || empty($payload['portal_domain'])) {
                continue;
            }
            $suggested_actions[] = array(
                'type' => 'action',
                'action_key' => $flow === 'seo_text_ads' ? 'preview_offpage_seo_text_ads_order' : 'preview_offpage_link_insertion_order',
                'label' => 'Preview ' . $payload['portal_domain'],
                'action_payload' => $payload,
            );
        }

        return array(
            'applied' => true,
            'operation' => 'recommend_offpage_' . $flow,
            'assistant_message' => (string) ($legacy['assistant_message'] ?? ''),
            'suggested_actions' => $suggested_actions,
            'summary' => array(
                'flow' => $flow,
                'candidates' => count($candidates),
            ),
            'projection' => array(
                'type' => $flow === 'seo_text_ads' ? 'offpage_seo_text_ads' : 'offpage_link_insertion',
                'title' => $flow === 'seo_text_ads' ? 'SEO Text Ads' : 'Link Insertion',
                'summary' => array(
                    'status' => 'recommendations_ready',
                    'candidates' => array_slice(array_map(function ($candidate) use ($flow, $keyword) {
                        return is_array($candidate) ? $this->aysa_agent8_normalize_offpage_purchase_payload($candidate, $flow, $keyword) : array();
                    }, $candidates), 0, 3),
                ),
            ),
        );
    }

    private function aysa_agent8_preview_offpage_purchase($project_id, array $offpage_operation, $flow) {
        $payload = $this->aysa_agent8_normalize_offpage_purchase_payload($offpage_operation, $flow, sanitize_text_field((string) ($offpage_operation['keyword'] ?? '')));
        if (empty($payload['portal_id']) || empty($payload['portal_domain'])) {
            return array('applied' => false, 'reason' => 'missing_purchase_candidate');
        }
        return array(
            'applied' => true,
            'operation' => 'preview_offpage_' . $flow . '_order',
            'summary' => $payload,
            'projection' => array(
                'type' => $flow === 'seo_text_ads' ? 'offpage_seo_text_ads' : 'offpage_link_insertion',
                'title' => $flow === 'seo_text_ads' ? 'SEO Text Ads' : 'Link Insertion',
                'summary' => array_merge($payload, array('status' => 'preview_ready')),
            ),
        );
    }

    private function aysa_agent8_send_offpage_purchase_approval_email($project_id, array $offpage_operation, $flow) {
        $payload = $this->aysa_agent8_normalize_offpage_purchase_payload($offpage_operation, $flow, sanitize_text_field((string) ($offpage_operation['keyword'] ?? '')));
        if (empty($payload['portal_domain'])) {
            return array('applied' => false, 'reason' => 'missing_purchase_candidate');
        }

        $current_user = wp_get_current_user();
        $recipient = ($current_user && !empty($current_user->user_email)) ? sanitize_email((string) $current_user->user_email) : '';
        if ($recipient === '' || !is_email($recipient)) {
            $recipient = sanitize_email((string) get_option('admin_email', ''));
        }
        if ($recipient === '' || !is_email($recipient)) {
            return array('applied' => false, 'reason' => 'missing_recipient');
        }

        $label = $flow === 'seo_text_ads' ? 'SEO Text Ads' : 'Link Insertion';
        $cost = intval($flow === 'seo_text_ads' ? ($payload['monthly_cost_cents'] ?? 0) : ($payload['estimated_cost_cents'] ?? 0));
        $billing = $flow === 'seo_text_ads' ? 'monthly' : 'one-time';
        $agent8_url = admin_url('admin.php?page=aysa-agent8');
        $subject = $label . ' offer ready for approval: ' . $payload['portal_domain'];
        $body_lines = array(
            'An off-page placement offer is ready for your review in Agent8.',
            '',
            'Service: ' . $label,
            'Portal/domain: ' . ($payload['portal_domain'] !== '' ? $payload['portal_domain'] : '-'),
            'Target URL: ' . ($payload['target_url'] !== '' ? $payload['target_url'] : '-'),
            'Keyword/anchor: ' . ($payload['keyword'] !== '' ? $payload['keyword'] : '-'),
            'Billing model: ' . $billing,
            'Estimated cost: ' . $cost . ' credits',
            '',
            'Open Agent8 to preview and approve the order:',
            $agent8_url,
            '',
            'Important: this email does not create an order and does not consume credits. Placing the order must happen inside Agent8/admin and changes Adverlink state.',
        );

        $email = $this->aysa_agent8_send_communication_email($recipient, $subject, implode("\n", $body_lines));
        if (empty($email['ok'])) {
            return array(
                'applied' => false,
                'reason' => 'email_failed',
                'provider' => $email['provider'] ?? 'PostmarkMailer',
                'error' => $email['error'] ?? 'unknown_error',
            );
        }

        return array(
            'applied' => true,
            'operation' => 'send_offpage_' . $flow . '_approval_email',
            'recipient' => $recipient,
            'provider' => $email['provider'] ?? 'PostmarkMailer',
            'summary' => array_merge($payload, array('status' => 'approval_email_sent')),
        );
    }

    private function aysa_agent8_place_offpage_purchase_order($project_id, array $offpage_operation, $flow) {
        $language = sanitize_key((string) ($offpage_operation['language'] ?? 'en'));
        $setup_user_id = $this->aysa_agent8_offpage_purchase_setup_user_id();
        $payload = $this->aysa_agent8_normalize_offpage_purchase_payload($offpage_operation, $flow, sanitize_text_field((string) ($offpage_operation['keyword'] ?? '')));
        $legacy = $flow === 'seo_text_ads'
            ? $this->aysa_adjustments_place_seo_text_ads_order($project_id, $setup_user_id, $payload, $language)
            : $this->aysa_adjustments_place_link_insertion_order($project_id, $setup_user_id, $payload, $language);

        return array(
            'applied' => true,
            'operation' => 'place_offpage_' . $flow . '_order',
            'assistant_message' => (string) ($legacy['assistant_message'] ?? ''),
            'payment_link' => isset($legacy['payment_link']) ? esc_url_raw((string) $legacy['payment_link']) : '',
            'required_credits' => intval($legacy['required_credits'] ?? 0),
            'available_credits' => intval($legacy['available_credits'] ?? 0),
            'missing_credits' => intval($legacy['missing_credits'] ?? 0),
            'suggested_actions' => !empty($legacy['payment_link']) ? array(
                array(
                    'type' => 'open_url',
                    'label' => 'Open payment link',
                    'url' => esc_url_raw((string) $legacy['payment_link']),
                ),
            ) : array(),
            'summary' => array_merge($payload, array(
                'status' => !empty($legacy['payment_link']) ? 'payment_required' : 'order_submitted',
                'payment_link' => isset($legacy['payment_link']) ? esc_url_raw((string) $legacy['payment_link']) : '',
            )),
            'projection' => array(
                'type' => $flow === 'seo_text_ads' ? 'offpage_seo_text_ads' : 'offpage_link_insertion',
                'title' => $flow === 'seo_text_ads' ? 'SEO Text Ads' : 'Link Insertion',
                'summary' => array(
                    'status' => !empty($legacy['payment_link']) ? 'payment_required' : 'order_submitted',
                    'payment_link' => isset($legacy['payment_link']) ? esc_url_raw((string) $legacy['payment_link']) : '',
                ),
            ),
        );
    }

    private function aysa_agent8_normalize_offpage_purchase_payload(array $source, $flow, $keyword) {
        $flow = $flow === 'seo_text_ads' ? 'seo_text_ads' : 'link_insertion';
        $keyword = sanitize_text_field((string) ($source['keyword'] ?? $keyword));
        $cost_key = $flow === 'seo_text_ads' ? 'monthly_cost_cents' : 'estimated_cost_cents';
        $cost = max(0, intval($source[$cost_key] ?? $source['estimated_cost_cents'] ?? $source['monthly_cost_cents'] ?? 0));
        return array(
            'portal_id' => sanitize_text_field((string) ($source['portal_id'] ?? $source['id'] ?? '')),
            'portal_domain' => sanitize_text_field((string) ($source['portal_domain'] ?? $source['domain'] ?? '')),
            'domain' => sanitize_text_field((string) ($source['portal_domain'] ?? $source['domain'] ?? '')),
            'authority' => floatval($source['authority'] ?? 0),
            'score' => floatval($source['score'] ?? 0),
            'service_type' => $flow === 'seo_text_ads' ? 'seo_ads_text' : 'link_insertion',
            'billing_type' => $flow === 'seo_text_ads' ? 'monthly' : 'one_time',
            'target_url' => esc_url_raw((string) ($source['target_url'] ?? home_url('/'))),
            'keyword' => $keyword,
            'estimated_cost_cents' => $flow === 'seo_text_ads' ? 0 : $cost,
            'monthly_cost_cents' => $flow === 'seo_text_ads' ? $cost : 0,
            'included_link_eligible' => isset($source['included_link_eligible']) ? (bool) $source['included_link_eligible'] : null,
        );
    }

    private function aysa_agent8_start_offpage_async_operation($project_id, $task, array $input) {
        $project_id = intval($project_id);
        $task = sanitize_key((string) $task);
        if ($project_id <= 0 || $task === '' || !in_array($task, $this->aysa_offpage_async_supported_tasks(), true)) {
            return array('applied' => false, 'reason' => 'invalid_offpage_async_task');
        }

        $request = $this->aysa_offpage_async_prepare_request($task, array_merge($input, array(
            'project_id' => $project_id,
        )));
        if (is_wp_error($request)) {
            return array(
                'applied' => false,
                'reason' => sanitize_key((string) $request->get_error_code()),
                'message' => (string) $request->get_error_message(),
            );
        }

        $request_hash = (string) ($request['request_hash'] ?? '');
        $existing = $this->aysa_offpage_async_get_status($project_id, $task);
        if (
            is_array($existing) &&
            !empty($existing['run_id']) &&
            in_array((string) ($existing['status'] ?? ''), array('queued', 'running'), true) &&
            (string) ($existing['request_hash'] ?? '') === $request_hash
        ) {
            return array(
                'applied' => true,
                'operation' => $task,
                'summary' => array(
                    'status' => (string) ($existing['status'] ?? 'queued'),
                    'run_id' => (string) ($existing['run_id'] ?? ''),
                ),
                'snapshot' => $this->aysa_agent8_offpage_snapshot_for_task($project_id, $task),
                'async_job' => $existing,
            );
        }

        $run_id = uniqid('offpage_', true);
        $status = array(
            'task' => $task,
            'project_id' => $project_id,
            'run_id' => $run_id,
            'request_hash' => $request_hash,
            'status' => 'queued',
            'result' => null,
            'error' => '',
            'started_at' => time(),
            'updated_at' => time(),
            'finished_at' => 0,
        );
        $this->aysa_offpage_async_save_status($project_id, $task, $status);
        $this->aysa_offpage_async_schedule_event(array(
            'task' => $task,
            'project_id' => $project_id,
            'run_id' => $run_id,
            'request_hash' => $request_hash,
            'request' => $request,
        ), 1);

        return array(
            'applied' => true,
            'operation' => $task,
            'summary' => array(
                'status' => 'queued',
                'run_id' => $run_id,
            ),
            'snapshot' => $this->aysa_agent8_offpage_snapshot_for_task($project_id, $task),
            'async_job' => $status,
        );
    }

    private function aysa_agent8_approve_offpage_draft_article($project_id, array $input) {
        $project_id = intval($project_id);
        $draft_id = intval($input['draft_id'] ?? 0);
        if ($project_id <= 0 || $draft_id <= 0) {
            return array('applied' => false, 'reason' => 'invalid_draft_id');
        }

        $reference = 'offpage_approve_publisher_article_draft_' . $draft_id;
        if ($this->aysa_wallet_usage_reference_exists($project_id, $reference) || $this->aysa_agent8_is_publisher_draft_approved($project_id, $draft_id)) {
            $this->aysa_agent8_mark_publisher_draft_approved($project_id, $draft_id);
            return array(
                'applied' => true,
                'operation' => 'publisher_draft_approve',
                'summary' => array(
                    'status' => 'already_approved',
                    'draft_id' => $draft_id,
                ),
                'snapshot' => $this->aysa_agent8_get_offpage_draft_articles_snapshot($project_id),
            );
        }

        $request_hash = md5($project_id . '|approve|' . $draft_id);
        $existing = $this->aysa_offpage_async_get_status($project_id, 'publisher_draft_approve');
        if (
            is_array($existing) &&
            !empty($existing['run_id']) &&
            in_array((string) ($existing['status'] ?? ''), array('queued', 'running'), true) &&
            (string) ($existing['request_hash'] ?? '') === $request_hash
        ) {
            return array(
                'applied' => true,
                'operation' => 'publisher_draft_approve',
                'summary' => array(
                    'status' => 'already_queued',
                    'run_id' => (string) ($existing['run_id'] ?? ''),
                ),
                'snapshot' => $this->aysa_agent8_get_offpage_draft_articles_snapshot($project_id),
                'async_job' => $existing,
            );
        }

        return $this->aysa_agent8_start_offpage_async_operation($project_id, 'publisher_draft_approve', array(
            'draft_id' => $draft_id,
        ));
    }

    private function aysa_agent8_is_publisher_draft_approved($project_id, $draft_id) {
        $project_id = intval($project_id);
        $draft_id = intval($draft_id);
        if ($project_id <= 0 || $draft_id <= 0) {
            return false;
        }

        foreach ($this->aysa_get_adverlink_orders($project_id) as $order) {
            if (!is_array($order) || intval($order['publisher_article_draft_id'] ?? 0) !== $draft_id) {
                continue;
            }
            $draft_status = strtoupper(trim((string) ($order['publisher_article_draft_status'] ?? '')));
            if ($draft_status === 'APPROVED') {
                return true;
            }
        }

        $status = $this->aysa_offpage_async_get_status($project_id, 'publisher_draft_approve');
        if (!is_array($status) || (string) ($status['status'] ?? '') !== 'completed') {
            return false;
        }
        $request_hash = md5($project_id . '|approve|' . $draft_id);
        if ((string) ($status['request_hash'] ?? '') !== $request_hash) {
            return false;
        }
        $result = isset($status['result']) && is_array($status['result']) ? $status['result'] : array();
        $approved_status = strtoupper($this->aysa_find_first_string_by_keys_recursive($result, array('status', 'draft_status')));
        return $approved_status === 'APPROVED';
    }

    private function aysa_agent8_mark_publisher_draft_approved($project_id, $draft_id, array $draft = array()) {
        $project_id = intval($project_id);
        $draft_id = intval($draft_id);
        if ($project_id <= 0 || $draft_id <= 0) {
            return;
        }

        $orders = $this->aysa_get_adverlink_orders($project_id);
        $changed = false;
        foreach ($orders as $idx => $order) {
            if (!is_array($order) || intval($order['publisher_article_draft_id'] ?? 0) !== $draft_id) {
                continue;
            }
            $orders[$idx]['publisher_article_draft_status'] = 'approved';
            $orders[$idx]['publisher_article_draft_updated_at'] = sanitize_text_field((string) ($draft['updated_at'] ?? current_time('mysql')));
            $changed = true;
        }
        if ($changed) {
            $this->aysa_save_adverlink_orders($project_id, $orders);
        }
    }

    private function aysa_agent8_offpage_snapshot_for_task($project_id, $task) {
        $project_id = intval($project_id);
        $task = sanitize_key((string) $task);
        return match ($task) {
            'publisher_draft_approve' => $this->aysa_agent8_get_offpage_draft_articles_snapshot($project_id),
            'exchange_request_respond', 'exchange_change_state' => array(
                'profile' => $this->aysa_agent8_get_offpage_link_exchange_profile_snapshot($project_id),
                'orders' => $this->aysa_agent8_get_offpage_orders_snapshot($project_id),
            ),
            default => array(),
        };
    }

    private function aysa_agent8_offpage_runtime_bundle_for_action($project_id, $action_key) {
        $project_id = intval($project_id);
        $action_key = sanitize_key((string) $action_key);
        $full = $this->aysa_agent8_runtime_bundle($project_id);

        return match ($action_key) {
            'set_offpage_gbp_primary_location',
            'refresh_offpage_gbp_reviews',
            'refresh_offpage_gbp_performance',
            'create_offpage_gbp_post' => $this->aysa_agent8_runtime_bundle_subset($full, array('offpage_gbp_overview')),
            'reply_offpage_gbp_review',
            'upload_offpage_gbp_media',
            'update_offpage_gbp_business_info',
            'update_offpage_gbp_hours',
            'update_offpage_gbp_attributes',
            'update_offpage_gbp_services' => $this->aysa_agent8_runtime_bundle_subset($full, array('offpage_gbp_overview')),
            'accept_offpage_exchange_request', 'pause_offpage_exchange_request' => $this->aysa_agent8_runtime_bundle_subset($full, array('offpage_link_exchange_profile', 'offpage_adverlink_orders')),
            'preview_offpage_draft_article',
            'send_offpage_draft_article_approval_email',
            'approve_offpage_draft_article' => $this->aysa_agent8_runtime_bundle_subset($full, array('offpage_draft_articles', 'offpage_adverlink_orders')),
            'approve_link_opportunity',
            'reject_link_opportunity' => $this->aysa_agent8_runtime_bundle_subset($full, array('offpage_link_exchange_profile', 'offpage_adverlink_orders', 'offpage_backlink_audit', 'offpage_draft_articles', 'offpage_published_articles')),
            default => array(),
        };
    }

    private function aysa_agent8_mark_technical_gone_candidate($project_id, array $technical_operation) {
        $project_id = intval($project_id);
        $source = $this->aysa_normalize_redirect_source((string) ($technical_operation['source_url'] ?? ''));
        if ($project_id <= 0 || $source === '') {
            return array('applied' => false, 'reason' => 'gone_target_required');
        }

        $result = $this->aysa_add_htaccess_gone_rule($source);
        if (empty($result['ok'])) {
            return array(
                'applied' => false,
                'reason' => sanitize_key((string) ($result['code'] ?? 'gone_apply_failed')),
                'message' => (string) ($result['message'] ?? 'Gone apply failed.'),
            );
        }

        return array(
            'applied' => empty($result['already_done']),
            'already_done' => !empty($result['already_done']),
            'operation' => 'mark_technical_gone_candidate',
            'summary' => array(
                'source_url' => $source,
                'redirect_removed' => (int) (!empty($result['redirect_removed'])),
                'already_done' => !empty($result['already_done']),
            ),
            'snapshot' => $this->aysa_adjustments_get_technical_errors_snapshot($project_id),
        );
    }

    private function aysa_agent8_ignore_technical_404_candidate($project_id, array $technical_operation) {
        global $wpdb;

        $project_id = intval($project_id);
        $source = $this->aysa_normalize_redirect_source((string) ($technical_operation['source_url'] ?? ''));
        if ($project_id <= 0 || $source === '') {
            return array('applied' => false, 'reason' => 'ignore_target_required');
        }

        $table_name = $wpdb->prefix . 'aysa_404_errors';
        $variants = array_values(array_unique(array_filter(array(
            $source,
            untrailingslashit($source),
            trailingslashit($source),
        ))));
        if ($variants === array()) {
            return array('applied' => false, 'reason' => 'ignore_target_required');
        }

        $placeholders = implode(',', array_fill(0, count($variants), '%s'));
        $query = $wpdb->prepare(
            "SELECT id FROM {$table_name} WHERE is_ignored = 0 AND url IN ({$placeholders})",
            ...$variants
        );
        $ids = $query ? $wpdb->get_col($query) : array();
        $ids = array_values(array_filter(array_map('intval', (array) $ids)));
        if ($ids === array()) {
            return array(
                'applied' => false,
                'already_done' => true,
                'reason' => 'already_ignored',
                'message' => 'This 404 is no longer active or was already ignored.',
                'snapshot' => $this->aysa_adjustments_get_technical_errors_snapshot($project_id),
            );
        }

        $updated = 0;
        foreach ($ids as $error_id) {
            $result = $wpdb->update(
                $table_name,
                array('is_ignored' => 1),
                array('id' => $error_id),
                array('%d'),
                array('%d')
            );
            if ($result === false) {
                return array('applied' => false, 'reason' => '404_ignore_failed');
            }
            $updated += intval($result);
        }

        return array(
            'applied' => $updated > 0,
            'operation' => 'ignore_technical_404_candidate',
            'summary' => array(
                'source_url' => $source,
                'ignored_rows' => $updated,
            ),
            'snapshot' => $this->aysa_adjustments_get_technical_errors_snapshot($project_id),
        );
    }

    private function aysa_agent8_apply_technical_redirect_batch($project_id, array $technical_operation, $mode) {
        $project_id = intval($project_id);
        $mode = sanitize_key((string) $mode);
        $selection = isset($technical_operation['selection']) && is_array($technical_operation['selection'])
            ? array_values($technical_operation['selection'])
            : array();
        if ($project_id <= 0 || $selection === array()) {
            return array('applied' => false, 'reason' => 'technical_redirect_batch_selection_required');
        }

        $applied = 0;
        $failed = 0;
        $already_done = 0;
        foreach ($selection as $item) {
            if (!is_array($item)) {
                continue;
            }
            $result = match ($mode) {
                'redirect' => $this->aysa_agent8_apply_technical_redirect_candidate($project_id, $item),
                'gone' => $this->aysa_agent8_mark_technical_gone_candidate($project_id, $item),
                'ignore' => $this->aysa_agent8_ignore_technical_404_candidate($project_id, $item),
                default => array('applied' => false, 'reason' => 'unsupported_batch_mode'),
            };
            if (!empty($result['applied'])) {
                $applied++;
            } elseif (!empty($result['already_done'])) {
                $already_done++;
            } else {
                $failed++;
            }
        }

        return array(
            'applied' => $applied > 0,
            'operation' => 'technical_redirect_batch_' . $mode,
            'summary' => array(
                'selected_entity_count' => count($selection),
                'applied_count' => $applied,
                'already_done_count' => $already_done,
                'failed_count' => $failed,
            ),
            'snapshot' => $this->aysa_adjustments_get_technical_errors_snapshot($project_id),
        );
    }

    private function aysa_agent8_email_technical_pagespeed_pdf($project_id, array $technical_operation) {
        $project_id = intval($project_id);
        $recipient = sanitize_email((string) ($technical_operation['recipient_email'] ?? ''));
        $download_url = esc_url_raw((string) ($technical_operation['download_url'] ?? ''));
        $file_name = sanitize_text_field((string) ($technical_operation['file_name'] ?? 'aysa-pagespeed-report.pdf'));
        $summary = isset($technical_operation['summary']) && is_array($technical_operation['summary']) ? $technical_operation['summary'] : array();
        if ($project_id <= 0 || $recipient === '' || !is_email($recipient) || $download_url === '') {
            return array('applied' => false, 'reason' => 'pagespeed_email_payload_required');
        }

        $snapshot = $this->aysa_adjustments_get_technical_pagespeed_snapshot($project_id);
        $subject = 'AYSA PageSpeed AI Report';
        $body_lines = array(
            'The latest PageSpeed AI report is ready.',
            '',
            'Website: ' . sanitize_text_field((string) ($snapshot['url'] ?? home_url('/'))),
            'Overall score: ' . intval($summary['overall_score'] ?? $snapshot['overall_score'] ?? 0),
            'Desktop score: ' . intval($summary['desktop_score'] ?? $snapshot['desktop_score'] ?? 0),
            'Mobile score: ' . intval($summary['mobile_score'] ?? $snapshot['mobile_score'] ?? 0),
            'Verdict: ' . sanitize_text_field((string) ($summary['verdict'] ?? '')),
            '',
            'Download report: ' . $download_url,
        );

        if (!wp_mail($recipient, $subject, implode("\n", $body_lines), array('Content-Type: text/plain; charset=UTF-8'))) {
            return array('applied' => false, 'reason' => 'pagespeed_email_send_failed');
        }

        $state_key = $this->aysa_technical_pagespeed_state_key($project_id);
        $state = get_option($state_key, array());
        if (!is_array($state)) {
            $state = array();
        }
        $state['last_report_email'] = array(
            'recipient_email' => $recipient,
            'download_url' => $download_url,
            'file_name' => $file_name,
            'sent_at' => current_time('mysql'),
        );
        update_option($state_key, $state, false);

        return array(
            'applied' => true,
            'operation' => 'email_technical_pagespeed_pdf',
            'summary' => $state['last_report_email'],
            'snapshot' => $this->aysa_adjustments_get_technical_pagespeed_snapshot($project_id),
        );
    }

    private function aysa_agent8_generate_technical_sitemap($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array('applied' => false, 'reason' => 'invalid_project_id');
        }

        $target_url = $this->aysa_technical_resolve_target_url();
        $generation = $this->aysa_technical_generate_own_sitemap($target_url);
        $audit = $this->aysa_technical_build_sitemap_robots_audit($target_url);
        update_option($this->aysa_technical_sitemap_robots_state_key($project_id), array(
            'project_id' => $project_id,
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'data' => $audit,
        ), false);

        return array(
            'applied' => true,
            'operation' => 'generate_aysa_sitemap',
            'summary' => array(
                'entries_count' => intval($generation['entries_count'] ?? 0),
                'mode' => sanitize_text_field((string) ($generation['mode'] ?? 'virtual')),
            ),
            'snapshot' => $this->aysa_adjustments_get_technical_sitemap_robots_snapshot($project_id),
        );
    }

    private function aysa_agent8_save_technical_robots($project_id, array $technical_operation) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array('applied' => false, 'reason' => 'invalid_project_id');
        }
        $target_url = $this->aysa_technical_resolve_target_url();
        $content = isset($technical_operation['content']) && is_string($technical_operation['content']) && trim($technical_operation['content']) !== ''
            ? (string) $technical_operation['content']
            : $this->aysa_technical_robots_default_content($target_url, $this->aysa_technical_preferred_sitemap_url($target_url));
        $saved = $this->aysa_technical_save_robots_content($content);
        if (is_wp_error($saved)) {
            return array('applied' => false, 'reason' => 'robots_save_failed', 'message' => $saved->get_error_message());
        }
        $audit = $this->aysa_technical_build_sitemap_robots_audit($target_url);
        update_option($this->aysa_technical_sitemap_robots_state_key($project_id), array(
            'project_id' => $project_id,
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'data' => $audit,
        ), false);
        return array(
            'applied' => true,
            'operation' => 'save_technical_robots',
            'snapshot' => $this->aysa_adjustments_get_technical_sitemap_robots_snapshot($project_id),
        );
    }

    private function aysa_agent8_restore_technical_robots($project_id) {
        return $this->aysa_agent8_save_technical_robots($project_id, array());
    }

    private function aysa_agent8_enforce_aysa_sitemap_authority($project_id) {
        $project_id = intval($project_id);
        $sitemap = $this->aysa_agent8_generate_technical_sitemap($project_id);
        if (empty($sitemap['applied'])) {
            return $sitemap;
        }
        $robots = $this->aysa_agent8_restore_technical_robots($project_id);
        if (empty($robots['applied'])) {
            return $robots;
        }
        return array(
            'applied' => true,
            'operation' => 'enforce_aysa_sitemap_authority',
            'summary' => array(
                'sitemap_authority' => 'aysa',
            ),
            'snapshot' => $this->aysa_adjustments_get_technical_sitemap_robots_snapshot($project_id),
        );
    }

    private function aysa_agent8_apply_unblock_technical_robots($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array('applied' => false, 'reason' => 'invalid_project_id');
        }

        $target_url = $this->aysa_technical_resolve_target_url();
        $content = $this->aysa_technical_robots_default_content($target_url, $this->aysa_technical_preferred_sitemap_url($target_url));
        $saved = $this->aysa_technical_save_robots_content($content);
        if (is_wp_error($saved)) {
            return array(
                'applied' => false,
                'reason' => 'robots_save_failed',
                'message' => $saved->get_error_message(),
            );
        }

        $audit = $this->aysa_technical_build_sitemap_robots_audit($target_url);
        update_option($this->aysa_technical_sitemap_robots_state_key($project_id), array(
            'project_id' => $project_id,
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'data' => $audit,
        ), false);

        return array(
            'applied' => true,
            'operation' => 'unblock_technical_robots',
            'snapshot' => $this->aysa_adjustments_get_technical_sitemap_robots_snapshot($project_id),
        );
    }

    private function aysa_agent8_build_technical_overview_command($site_audit, $pagespeed, $sitemap_robots, $internal_linking, $technical_errors = array(), $technical_schema = array()) {
        return array(
            'type' => 'open_technical_overview',
            'title' => 'Technical Optimization',
            'intro' => 'Aici vezi starea tehnică agregată, ce se poate rezolva automat și ce rămâne în review.',
            'sections' => array(
                array(
                    'title' => 'Site Audit',
                    'summary' => array(
                        'Status' => sanitize_key((string) ($site_audit['status'] ?? 'idle')),
                        'Issues' => (string) intval($site_audit['count'] ?? 0),
                        'Pages crawled' => (string) intval($site_audit['pages_crawled'] ?? 0),
                    ),
                ),
                array(
                    'title' => 'PageSpeed',
                    'summary' => array(
                        'Status' => sanitize_key((string) ($pagespeed['status'] ?? 'idle')),
                        'Overall score' => (string) intval($pagespeed['overall_score'] ?? 0),
                        'Mobile score' => (string) intval($pagespeed['mobile_score'] ?? 0),
                    ),
                ),
                array(
                    'title' => 'Sitemap + Robots',
                    'summary' => array(
                        'Status' => sanitize_key((string) ($sitemap_robots['status'] ?? 'idle')),
                        'Errors' => (string) intval($sitemap_robots['errors'] ?? 0),
                        'Warnings' => (string) intval($sitemap_robots['warnings'] ?? 0),
                        'Primary sitemap' => sanitize_text_field((string) ($sitemap_robots['primary_sitemap'] ?? '-')),
                    ),
                ),
                array(
                    'title' => 'Errors Hub',
                    'summary' => array(
                        'Redirect ready' => (string) intval($technical_errors['summary']['redirect_ready'] ?? 0),
                        'Gone candidates' => (string) intval($technical_errors['summary']['gone_candidates'] ?? 0),
                        'Review required' => (string) intval($technical_errors['summary']['review_required'] ?? 0),
                    ),
                ),
                array(
                    'title' => 'Schema Markup',
                    'summary' => array(
                        'Status' => sanitize_key((string) ($technical_schema['status'] ?? 'idle')),
                        'Pages' => (string) intval($technical_schema['summary']['total_pages'] ?? 0),
                        'Issues' => (string) intval($technical_schema['summary']['issue_count'] ?? 0),
                    ),
                ),
                array(
                    'title' => 'Internal Linking',
                    'summary' => array(
                        'Status' => sanitize_key((string) ($internal_linking['status'] ?? 'idle')),
                        'Keywords linked' => (string) intval($internal_linking['count'] ?? 0),
                        'Preview links to add' => (string) intval($internal_linking['preview_links_to_add'] ?? 0),
                    ),
                ),
            ),
        );
    }

    private function aysa_agent8_build_technical_site_audit_command($snapshot) {
        return array(
            'type' => 'open_technical_site_audit_review',
            'title' => 'Technical Site Audit',
            'intro' => 'Aici vezi auditul tehnic salvat: overview, problemele principale, paginile crawl-ate și statisticile importante.',
            'snapshot' => array(
                'overview_rows' => isset($snapshot['overview_rows']) && is_array($snapshot['overview_rows']) ? $snapshot['overview_rows'] : array(),
                'conclusion' => isset($snapshot['conclusion']) && is_array($snapshot['conclusion']) ? $snapshot['conclusion'] : array(),
                'rows' => array_slice(isset($snapshot['rows']) && is_array($snapshot['rows']) ? $snapshot['rows'] : array(), 0, 20),
                'pages' => array_slice(isset($snapshot['pages']) && is_array($snapshot['pages']) ? $snapshot['pages'] : array(), 0, 20),
                'exports' => isset($snapshot['exports']) && is_array($snapshot['exports']) ? $snapshot['exports'] : array(),
                'status' => sanitize_key((string) ($snapshot['status'] ?? 'idle')),
                'updated_at' => sanitize_text_field((string) ($snapshot['updated_at'] ?? '')),
                'version_number' => intval($snapshot['version_number'] ?? 0),
                'history_count' => intval($snapshot['history_count'] ?? 0),
                'mode' => sanitize_text_field((string) ($snapshot['mode'] ?? 'baseline_full')),
            ),
        );
    }

    private function aysa_agent8_build_technical_pagespeed_command($snapshot) {
        return array(
            'type' => 'open_technical_pagespeed_review',
            'title' => 'PageSpeed',
            'intro' => 'Aici vezi scorurile curente de viteză și cele mai importante oportunități de optimizare.',
            'snapshot' => array(
                'overview_rows' => isset($snapshot['overview_rows']) && is_array($snapshot['overview_rows']) ? $snapshot['overview_rows'] : array(),
                'metrics_rows' => isset($snapshot['metrics_rows']) && is_array($snapshot['metrics_rows']) ? $snapshot['metrics_rows'] : array(),
                'rows' => array_slice(isset($snapshot['rows']) && is_array($snapshot['rows']) ? $snapshot['rows'] : array(), 0, 12),
                'priority_actions' => isset($snapshot['priority_actions']) && is_array($snapshot['priority_actions']) ? $snapshot['priority_actions'] : array(),
                'status' => sanitize_key((string) ($snapshot['status'] ?? 'idle')),
                'updated_at' => sanitize_text_field((string) ($snapshot['updated_at'] ?? '')),
            ),
        );
    }

    private function aysa_agent8_build_technical_sitemap_robots_command($snapshot) {
        return array(
            'type' => 'open_technical_sitemap_robots_review',
            'title' => 'Sitemap + Robots',
            'intro' => 'Aici vezi verificările pentru sitemap și robots.txt, plus fișierul robots curent.',
            'snapshot' => array(
                'overview_rows' => isset($snapshot['overview_rows']) && is_array($snapshot['overview_rows']) ? $snapshot['overview_rows'] : array(),
                'rows' => array_slice(isset($snapshot['rows']) && is_array($snapshot['rows']) ? $snapshot['rows'] : array(), 0, 20),
                'sitemaps' => isset($snapshot['sitemaps']) && is_array($snapshot['sitemaps']) ? $snapshot['sitemaps'] : array(),
                'robots_rows' => isset($snapshot['robots_rows']) && is_array($snapshot['robots_rows']) ? $snapshot['robots_rows'] : array(),
                'status' => sanitize_key((string) ($snapshot['status'] ?? 'idle')),
                'updated_at' => sanitize_text_field((string) ($snapshot['updated_at'] ?? '')),
            ),
        );
    }

    private function aysa_agent8_build_technical_internal_linking_command($snapshot) {
        return array(
            'type' => 'open_technical_internal_linking_review',
            'title' => 'Internal Linking',
            'intro' => 'Aici vezi rezumatul actual pentru internal linking și preview-ul ultimului pass pregătit.',
            'snapshot' => array(
                'overview_rows' => isset($snapshot['overview_rows']) && is_array($snapshot['overview_rows']) ? $snapshot['overview_rows'] : array(),
                'rows' => array_slice(isset($snapshot['rows']) && is_array($snapshot['rows']) ? $snapshot['rows'] : array(), 0, 20),
                'preview_rows' => array_slice(isset($snapshot['preview_rows']) && is_array($snapshot['preview_rows']) ? $snapshot['preview_rows'] : array(), 0, 20),
                'preview_summary' => sanitize_text_field((string) ($snapshot['preview_summary'] ?? '')),
                'status' => sanitize_key((string) ($snapshot['status'] ?? 'idle')),
            ),
        );
    }

    private function aysa_store_verified_access_key($validation, $access_key, $site_host) {
        update_option($this->aysa_test_access_key_option_name(), (string) $access_key, false);

        if (!empty($validation['project_id'])) {
            update_option('aysa_project_id', intval($validation['project_id']), false);
        }
        if (!empty($validation['website_id'])) {
            update_option('aysa_website_id', intval($validation['website_id']), false);
        }

        $auth_bootstrap = null;
        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }
        if (class_exists('Aysa_Auth_Manager')) {
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $auth_bootstrap = $auth_manager->verify_and_store_secret_key((string) $access_key, (string) $site_host);

            if (is_array($auth_bootstrap) && !empty($auth_bootstrap['success'])) {
                update_option('aysa_user_secret_key', (string) $access_key, false);
                update_option('aysa_user_id', intval($auth_bootstrap['aysa_user_id'] ?? 0), false);
                update_option('aysa_auth_domain', $site_host !== '' ? $site_host : wp_parse_url(home_url(), PHP_URL_HOST), false);
            }
        }

        if (!empty($validation['plan_code'])) {
            update_option('aysa_wallet_default_plan', sanitize_key((string) $validation['plan_code']), false);
            if (class_exists('Aysa_Wallet_Manager')) {
                $project_id = intval(get_option('aysa_project_id', 0));
                if ($project_id > 0) {
                    Aysa_Wallet_Manager::get_instance()->apply_plan_to_project(
                        $project_id,
                        sanitize_key((string) $validation['plan_code']),
                        false
                    );
                }
            }
        }
    }

    private function aysa_clear_activation_binding($wp_user_id = 0) {
        delete_option($this->aysa_test_access_key_option_name());
        delete_option('aysa_active_access_key');
        delete_option('aysa_user_secret_key');
        delete_option('aysa_user_id');
        delete_option('aysa_auth_domain');
        delete_option('aysa_project_id');
        delete_option('aysa_website_id');

        global $wpdb;
        $domain = (string) wp_parse_url(home_url(), PHP_URL_HOST);
        $domain = strtolower(preg_replace('/^www\./', '', trim($domain)));
        if ($domain !== '') {
            $table_name = $wpdb->prefix . 'aysa_user_auth';
            $wpdb->delete(
                $table_name,
                array('domain' => $domain),
                array('%s')
            );
        }

        if (!class_exists('Aysa_Auth_Manager')) {
            $auth_manager_file = AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
            if (file_exists($auth_manager_file)) {
                require_once $auth_manager_file;
            }
        }

        if (class_exists('Aysa_Auth_Manager')) {
            $auth_manager = Aysa_Auth_Manager::get_instance();
            if (is_object($auth_manager) && method_exists($auth_manager, 'logout')) {
                $auth_manager->logout($wp_user_id > 0 ? intval($wp_user_id) : get_current_user_id());
            }
        }
    }

    public function maybe_cleanup_retired_agent7_runtime() {
        if (!is_admin() || wp_doing_ajax()) {
            return;
        }
        if (!$this->aysa_current_user_can_workspace()) {
            return;
        }
        if (get_option('aysa_agent7_retired_cleanup_done', '0') === '1') {
            return;
        }

        global $wpdb;
        if (isset($wpdb->usermeta)) {
            $wpdb->query(
                $wpdb->prepare(
                    "DELETE FROM {$wpdb->usermeta} WHERE meta_key LIKE %s",
                    'aysa_agent7_local_%'
                )
            );
        }
        if (isset($wpdb->options)) {
            $wpdb->query(
                $wpdb->prepare(
                    "DELETE FROM {$wpdb->options} WHERE option_name LIKE %s",
                    'aysa_agent7_%'
                )
            );
        }

        $project_id = intval(get_option('aysa_project_id', 0));
        $site_host = (string) wp_parse_url(home_url('/'), PHP_URL_HOST);
        if ($project_id > 0) {
            $this->aysa_broker_request('POST', '/v1/conversation/apprentice/reset', array(
                'project_id' => $project_id,
                'canonical_project_id' => $project_id,
                'wp_project_id' => $project_id,
                'site_host' => $site_host,
                'channels' => array('agent7', 'agent7_v2'),
            ), 20);
        }

        update_option('aysa_agent7_retired_cleanup_done', '1', false);
    }

    public function handle_insight_banner_dismiss() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }
        if (!$this->check_authentication(false)) {
            wp_send_json_error(array('message' => 'Authentication required.'));
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval(wp_unslash((string) $_POST['project_id'])) : intval(get_option('aysa_project_id', 0));
        $banner_id = isset($_POST['banner_id']) ? sanitize_key(wp_unslash((string) $_POST['banner_id'])) : '';
        $dismiss_version = isset($_POST['dismiss_version']) ? sanitize_text_field(wp_unslash((string) $_POST['dismiss_version'])) : 'v1';

        if ($project_id <= 0 || $banner_id === '') {
            wp_send_json_error(array('message' => 'Invalid dismiss payload.'));
            return;
        }

        $result = $this->aysa_broker_request('POST', '/v1/conversation/insight-banner/dismiss', array(
            'project_id' => $project_id,
            'banner_id' => $banner_id,
            'dismiss_version' => $dismiss_version !== '' ? $dismiss_version : 'v1',
        ), 20);

        if (is_wp_error($result)) {
            wp_send_json_error(array('message' => $result->get_error_message()));
            return;
        }
        if (!is_array($result) || empty($result['ok'])) {
            wp_send_json_error(array(
                'message' => isset($result['error']) ? (string) $result['error'] : 'Insight banner dismiss failed.',
            ));
            return;
        }

        wp_send_json_success($result);
    }

    public function handle_activation_reset_test() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }

        delete_option($this->aysa_test_access_key_option_name());
        delete_option('aysa_active_access_key');
        delete_option('aysa_user_secret_key');
        delete_option('aysa_user_id');
        delete_option('aysa_auth_domain');

        global $wpdb;
        $domain = (string) wp_parse_url(home_url(), PHP_URL_HOST);
        $domain = strtolower(preg_replace('/^www\./', '', trim($domain)));
        if ($domain !== '') {
            $table_name = $wpdb->prefix . 'aysa_user_auth';
            $wpdb->delete(
                $table_name,
                array('domain' => $domain),
                array('%s')
            );
        }

        wp_send_json_success(array(
            'message' => 'Activation test state cleared.',
            'host' => $domain,
        ));
    }
    
    /**
     * Sanitize wizard data before saving
     */
    private function sanitize_wizard_data($data) {
        $sanitized = [];
        
        foreach ($data as $key => $value) {
            $clean_key = sanitize_key((string) $key);
            if (is_array($value)) {
                $sanitized[$clean_key] = array_map(static function ($item) {
                    return sanitize_text_field((string) $item);
                }, $value);
            } else {
                $sanitized[$clean_key] = sanitize_text_field((string) $value);
            }
        }
        
        return $sanitized;
    }

    /**
     * Register all the hooks related to the admin area functionality
     * of the plugin.
     */
    private function register_hooks() {
        // Add menu items
        add_action('admin_menu', array($this, 'add_plugin_admin_menu'));
        
        // Add AJAX handlers
        add_action('wp_ajax_aysa_save_wizard_data', array($this, 'handle_save_wizard_data'));
        add_action('wp_ajax_aysa_get_wizard_data', array($this, 'handle_get_wizard_data'));
        add_action('wp_ajax_aysa_save_project_id', array($this, 'save_project_id'));
        
        // Register stylesheets and scripts
        add_action('admin_enqueue_scripts', array($this, 'enqueue_styles'));
        add_action('admin_enqueue_scripts', array($this, 'enqueue_scripts'));
        add_action('admin_enqueue_scripts', array($this, 'enqueue_credit_analytics_scripts'));
        

    }

    private function get_app_aysa_base_url() {
        $base = trim((string) get_option('aysa_wallet_payment_base_url', 'https://app.aysa.ai/pay'));
        if ($base === '') {
            $base = 'https://app.aysa.ai/pay';
        }
        $base = preg_replace('#/pay/?$#', '', $base);
        if (!is_string($base) || trim($base) === '') {
            return 'https://app.aysa.ai';
        }
        return rtrim($base, '/');
    }

    private function get_current_plugin_domain() {
        $domain = isset($_SERVER['HTTP_HOST']) ? strtolower(trim((string) $_SERVER['HTTP_HOST'])) : '';
        if (strpos($domain, 'www.') === 0) {
            $domain = substr($domain, 4);
        }
        return $domain;
    }

    private function get_plugin_notifications_secret_key() {
        if (class_exists('Aysa_Auth_Manager')) {
            try {
                $auth = Aysa_Auth_Manager::get_instance();
                $status = $auth->is_authenticated();
                $token = is_array($status) ? trim((string) ($status['secret_key'] ?? '')) : '';
                if ($token !== '') {
                    return $token;
                }
            } catch (Exception $e) {
                // Fall through to the same site-level token sources used by wallet sync.
            }
        }

        $token = trim((string) get_option('aysa_user_secret_key', ''));
        if ($token !== '') {
            return $token;
        }

        $authData = get_option('aysa_activator_auth_data', array());
        if (is_array($authData)) {
            $token = trim((string) ($authData['secret_key'] ?? ''));
            if ($token !== '') {
                return $token;
            }
        }

        return '';
    }

    private function make_app_aysa_notifications_request($path) {
        $secretKey = $this->get_plugin_notifications_secret_key();
        $domain = $this->get_current_plugin_domain();

        if ($secretKey === '' || $domain === '') {
            return array(
                'ok' => false,
                'message' => 'Authentication required.',
            );
        }

        $url = $this->get_app_aysa_base_url() . '/' . ltrim((string) $path, '/');
        $response = wp_remote_get($url, array(
            'timeout' => 20,
            'headers' => array(
                'Accept' => 'application/json',
                'Authorization' => 'Bearer ' . $secretKey,
                'X-AYSA-Plugin-Domain' => $domain,
            ),
        ));

        if (is_wp_error($response)) {
            return array(
                'ok' => false,
                'message' => $response->get_error_message(),
            );
        }

        $code = intval(wp_remote_retrieve_response_code($response));
        $body = json_decode((string) wp_remote_retrieve_body($response), true);

        if ($code < 200 || $code >= 300 || !is_array($body)) {
            return array(
                'ok' => false,
                'message' => 'Notification service unavailable.',
            );
        }

        return $body;
    }

    public function handle_notifications_fetch() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_notifications_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }

        if (!$this->check_authentication(false)) {
            wp_send_json_error(array('message' => 'Authentication required.'));
            return;
        }

        $this->clear_cached_notification_notice();
        $result = $this->make_app_aysa_notifications_request('/bridge/plugin-notifications');
        if (empty($result['ok'])) {
            wp_send_json_error(array('message' => (string) ($result['message'] ?? 'Failed to load notifications.')));
            return;
        }

        wp_send_json_success($result);
    }

    public function handle_notifications_mark_read() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_notifications_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }

        if (!$this->check_authentication(false)) {
            wp_send_json_error(array('message' => 'Authentication required.'));
            return;
        }

        $notificationId = intval($_POST['notification_id'] ?? 0);
        if ($notificationId <= 0) {
            wp_send_json_error(array('message' => 'Invalid notification.'));
            return;
        }

        $result = $this->make_app_aysa_notifications_request('/bridge/plugin-notifications/' . $notificationId . '/read');
        if (empty($result['ok'])) {
            wp_send_json_error(array('message' => (string) ($result['message'] ?? 'Failed to update notification.')));
            return;
        }

        $this->clear_cached_notification_notice();
        wp_send_json_success($result);
    }

    public function handle_notifications_mark_all_read() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_notifications_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }

        if (!$this->check_authentication(false)) {
            wp_send_json_error(array('message' => 'Authentication required.'));
            return;
        }

        $result = $this->make_app_aysa_notifications_request('/bridge/plugin-notifications/read-all');
        if (empty($result['ok'])) {
            wp_send_json_error(array('message' => (string) ($result['message'] ?? 'Failed to update notifications.')));
            return;
        }

        $this->clear_cached_notification_notice();
        wp_send_json_success($result);
    }

    /**
     * Save project ID to WordPress options
     */
    public function save_project_id() {
        // Check for nonce security
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_save_project_id_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }
        
        // Get project ID and name from POST data
        $project_id = isset($_POST['project_id']) ? absint(wp_unslash((string) $_POST['project_id'])) : 0;
        $project_name = isset($_POST['project_name']) ? sanitize_text_field(wp_unslash((string) $_POST['project_name'])) : '';
        
        // Save to WordPress options
        update_option('aysa_project_id', $project_id);
        update_option('aysa_project_name', $project_name);
        
        // Send success response
        wp_send_json_success(array('message' => 'Project ID saved successfully.'));
    }

    /**
     * Get saved wizard data for a specific step.
     */
    public function handle_get_wizard_data() {
        $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (empty($nonce) || (!wp_verify_nonce($nonce, 'aysa_wizard_nonce') && !wp_verify_nonce($nonce, 'aysa_nonce'))) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }

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

        if (!$this->check_authentication(false)) {
            wp_send_json_error(array('message' => 'Authentication required'));
            return;
        }

        $step = isset($_POST['step']) ? sanitize_text_field(wp_unslash((string) $_POST['step'])) : '';
        if ($step === '') {
            wp_send_json_error(array('message' => 'Step is required'));
            return;
        }

        $auth_manager = Aysa_Auth_Manager::get_instance();
        $auth_status = $auth_manager->is_authenticated();
        $user_id = isset($auth_status['aysa_user_id']) ? intval($auth_status['aysa_user_id']) : 0;
        if ($user_id <= 0) {
            wp_send_json_error(array('message' => 'Invalid authentication context'));
            return;
        }

        $option_name = 'aysa_wizard_' . $step . '_' . $user_id;
        $saved_data = get_option($option_name, array());
        if (!is_array($saved_data)) {
            $saved_data = array();
        }

        wp_send_json_success(array('step' => $step, 'data' => $saved_data));
    }

    /**
     * AJAX handler to fetch WooCommerce categories
     */
    public function get_woocommerce_categories() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error('Security check failed');
            return;
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        // Check if WooCommerce is active
        if (!class_exists('WooCommerce')) {
            wp_send_json_error('WooCommerce is not active');
            return;
        }
        
        // Get all product categories
        $categories = get_terms(array(
            'taxonomy' => 'product_cat',
            'hide_empty' => false,
        ));
        
        // Format the categories for the response
        $formatted_categories = array();
        
        if (!is_wp_error($categories) && !empty($categories)) {
            foreach ($categories as $category) {
                $formatted_categories[] = array(
                    'id' => $category->term_id,
                    'name' => $category->name,
                    'slug' => $category->slug,
                    'count' => $category->count
                );
            }
        }
        
        wp_send_json_success($formatted_categories);
    }

    /**
     * Handle adding .htaccess redirect
     */
    private function aysa_get_redirects_table_name() {
        global $wpdb;

        return $wpdb->prefix . 'aysa_redirects';
    }

    private function aysa_redirects_table_exists() {
        global $wpdb;

        $table_name = $this->aysa_get_redirects_table_name();
        $found = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table_name));

        return is_string($found) && $found === $table_name;
    }

    private function aysa_normalize_redirect_path($value) {
        $value = trim((string) $value);
        if ($value === '') {
            return '';
        }

        if (preg_match('#^https?://#i', $value)) {
            $parsed = wp_parse_url($value, PHP_URL_PATH);
            $value = is_string($parsed) ? $parsed : '/';
        }

        if ($value === '') {
            $value = '/';
        }

        if (strpos($value, '/') !== 0) {
            $value = '/' . $value;
        }

        return $value;
    }

    private function aysa_parse_htaccess_redirect_rules() {
        $htaccess_file = ABSPATH . '.htaccess';
        if (!file_exists($htaccess_file) || !is_readable($htaccess_file)) {
            return array();
        }

        $content = file_get_contents($htaccess_file);
        if (!is_string($content) || $content === '') {
            return array();
        }

        $section_start = '# BEGIN Aysa Redirects';
        $section_end = '# END Aysa Redirects';
        if (strpos($content, $section_start) === false || strpos($content, $section_end) === false) {
            return array();
        }

        $pattern = '/' . preg_quote($section_start, '/') . '(.*?)' . preg_quote($section_end, '/') . '/s';
        if (!preg_match($pattern, $content, $matches)) {
            return array();
        }

        $rows = array();
        $lines = preg_split('/\r\n|\r|\n/', (string) ($matches[1] ?? ''));
        foreach ($lines as $line) {
            $line = trim((string) $line);
            if ($line === '' || stripos($line, 'RedirectMatch 301 ') !== 0) {
                continue;
            }

            if (!preg_match('/^RedirectMatch\s+301\s+\^(.+?)\$\s+(.+)$/i', $line, $parts)) {
                continue;
            }

            $source = stripslashes((string) $parts[1]);
            $source = preg_replace('/\\\\([\\/])/', '$1', $source);
            $source = $this->aysa_normalize_redirect_path($source);
            $target = trim((string) $parts[2]);
            if ($source === '' || $target === '') {
                continue;
            }

            $rows[$source] = array(
                'source_url' => $source,
                'target_url' => $target,
                'redirect_type' => '301',
                'is_active' => 1,
                'hit_count' => 0,
                'created_at' => '',
                'scope' => '.htaccess',
                'local_id' => null,
            );
        }

        return $rows;
    }

    private function aysa_get_local_redirect_rows() {
        global $wpdb;

        $rows = $this->aysa_parse_htaccess_redirect_rules();
        if (!$this->aysa_redirects_table_exists()) {
            return array_values($rows);
        }

        $table_name = $this->aysa_get_redirects_table_name();
        $db_rows = $wpdb->get_results("SELECT id, source_url, target_url, redirect_type, is_active, hit_count, created_at FROM {$table_name} ORDER BY created_at DESC, id DESC");
        if (!is_array($db_rows)) {
            return array_values($rows);
        }

        foreach ($db_rows as $db_row) {
            $source = $this->aysa_normalize_redirect_path((string) ($db_row->source_url ?? ''));
            if ($source === '') {
                continue;
            }

            $target = (string) ($db_row->target_url ?? '');
            if ($target === '') {
                continue;
            }

            if (!isset($rows[$source])) {
                $rows[$source] = array(
                    'source_url' => $source,
                    'target_url' => $target,
                    'redirect_type' => (string) ($db_row->redirect_type ?? '301'),
                    'is_active' => intval($db_row->is_active ?? 1),
                    'hit_count' => intval($db_row->hit_count ?? 0),
                    'created_at' => (string) ($db_row->created_at ?? ''),
                    'scope' => 'database',
                    'local_id' => intval($db_row->id ?? 0),
                );
                continue;
            }

            $rows[$source]['local_id'] = intval($db_row->id ?? 0);
            $rows[$source]['hit_count'] = intval($db_row->hit_count ?? 0);
            $rows[$source]['created_at'] = (string) ($db_row->created_at ?? '');
            $rows[$source]['redirect_type'] = (string) ($db_row->redirect_type ?? $rows[$source]['redirect_type']);
            $rows[$source]['is_active'] = intval($db_row->is_active ?? $rows[$source]['is_active']);
            if ($rows[$source]['scope'] !== '.htaccess') {
                $rows[$source]['scope'] = 'database';
            } else {
                $rows[$source]['scope'] = 'database + .htaccess';
            }
        }

        $visible_rows = array();
        foreach ($rows as $row) {
            if ($this->aysa_should_hide_redirect_from_ui($row)) {
                continue;
            }
            $visible_rows[] = $row;
        }

        return array_values($visible_rows);
    }

    /**
     * @param array<string,mixed> $row
     */
    private function aysa_should_hide_redirect_from_ui(array $row): bool {
        $needles = array(
            '/wp-admin/',
            '/wp-login.php',
            '/admin-ajax.php',
            '/xmlrpc.php',
            '/api/',
            'api/aysapluginuser/',
            'api/seo/',
            'api/redirect',
            'api/goneurl',
            'rest_route=',
            'wc-ajax=',
        );

        $haystacks = array(
            strtolower((string) ($row['source_url'] ?? '')),
            strtolower((string) ($row['target_url'] ?? '')),
        );

        foreach ($haystacks as $haystack) {
            if ($haystack === '') {
                continue;
            }
            foreach ($needles as $needle) {
                if (strpos($haystack, $needle) !== false) {
                    return true;
                }
            }
        }

        return false;
    }

    public function handle_get_local_redirects() {
        if (!check_ajax_referer('aysa_nonce', 'nonce', false)) {
            wp_send_json_error('Security check failed.');
            return;
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error('Permission denied.');
            return;
        }

        wp_send_json_success(array(
            'redirects' => $this->aysa_get_local_redirect_rows(),
        ));
    }

    public function handle_delete_local_redirect() {
        global $wpdb;

        if (!check_ajax_referer('aysa_nonce', 'nonce', false)) {
            wp_send_json_error('Security check failed.');
            return;
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error('Permission denied.');
            return;
        }

        $source = isset($_POST['source']) ? sanitize_text_field(wp_unslash((string) $_POST['source'])) : '';
        $source = $this->aysa_normalize_redirect_path($source);
        $local_id = isset($_POST['local_id']) ? intval($_POST['local_id']) : 0;
        if ($source === '' && $local_id <= 0) {
            wp_send_json_error('Redirect source is missing.');
            return;
        }

        if ($source !== '') {
            $result = $this->aysa_remove_htaccess_redirect_rule($source);
            if (empty($result['ok'])) {
                wp_send_json_error((string) ($result['message'] ?? 'Failed to remove redirect from .htaccess.'));
                return;
            }
        }

        if ($this->aysa_redirects_table_exists()) {
            $table_name = $this->aysa_get_redirects_table_name();
            if ($local_id > 0) {
                $wpdb->delete($table_name, array('id' => $local_id), array('%d'));
            } elseif ($source !== '') {
                $wpdb->delete($table_name, array('source_url' => $source), array('%s'));
            }
        }

        wp_send_json_success(array(
            'message' => 'Redirect removed successfully.',
        ));
    }

    private function aysa_remove_htaccess_redirect_rule($source) {
        $source = $this->aysa_normalize_redirect_path($source);
        if ($source === '') {
            return array(
                'ok' => false,
                'message' => 'Source URL is missing.',
            );
        }

        $htaccess_file = ABSPATH . '.htaccess';
        if (!file_exists($htaccess_file)) {
            return array(
                'ok' => false,
                'message' => 'The .htaccess file does not exist in the WordPress root directory.',
            );
        }
        if (!is_writable($htaccess_file)) {
            return array(
                'ok' => false,
                'message' => 'The .htaccess file is not writable. Please check file permissions.',
            );
        }

        $htaccess_content = file_get_contents($htaccess_file);
        if (!is_string($htaccess_content)) {
            return array(
                'ok' => false,
                'message' => 'Could not read the .htaccess file.',
            );
        }

        $section_start = '# BEGIN Aysa Redirects';
        $section_end = '# END Aysa Redirects';
        $redirect_pattern = 'RedirectMatch 301 \^' . preg_quote(preg_quote($source, '/'), '/') . '\$';

        if (strpos($htaccess_content, $section_start) === false) {
            return array(
                'ok' => false,
                'message' => 'Redirect rule not found in .htaccess.',
            );
        }

        $pattern = '/' . preg_quote($section_start, '/') . '(.*?)' . preg_quote($section_end, '/') . '/s';
        if (!preg_match($pattern, $htaccess_content, $matches)) {
            return array(
                'ok' => false,
                'message' => 'Redirect rule not found in .htaccess.',
            );
        }

        $existing_content = (string) ($matches[1] ?? '');
        $modified_content = preg_replace('/[\r\n]*' . $redirect_pattern . '.*[\r\n]*/i', "\n", $existing_content);
        if ($modified_content === null || $modified_content === $existing_content) {
            return array(
                'ok' => false,
                'message' => 'Redirect rule not found in .htaccess.',
            );
        }

        $new_content = $section_start . $modified_content . $section_end;
        $htaccess_content = preg_replace($pattern, $new_content, $htaccess_content);
        if (!is_string($htaccess_content) || file_put_contents($htaccess_file, $htaccess_content) === false) {
            return array(
                'ok' => false,
                'message' => 'Failed to write to .htaccess file.',
            );
        }

        return array(
            'ok' => true,
            'message' => 'Redirect removed successfully from .htaccess file.',
        );
    }

    public function handle_add_htaccess_redirect() {
        // Check for nonce security
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error('Security check failed.');
            return;
        }
        
        // Get redirect data from POST
        $source = isset($_POST['source']) ? sanitize_text_field(wp_unslash((string) $_POST['source'])) : '';
        $destination = isset($_POST['destination']) ? sanitize_text_field(wp_unslash((string) $_POST['destination'])) : '';

        $result = $this->aysa_add_htaccess_redirect_rule($source, $destination);
        if (empty($result['ok'])) {
            wp_send_json_error((string) ($result['message'] ?? 'Failed to add redirect.'));
            return;
        }

        $project_id = $this->aysa_resolve_project_id_with_local_fallback(0);
        if ($project_id > 0) {
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_add_htaccess_redirect',
                'add_htaccess_redirect',
                array(
                    'module' => 'technical_optimization',
                    'source' => $source,
                    'destination' => $destination,
                ),
                12,
                '/v1/technical/redirects/htaccess/add'
            );
        }

        wp_send_json_success(array(
            'message' => (string) ($result['message'] ?? 'Redirect added successfully to .htaccess file.'),
            'redirect_rule' => (string) ($result['redirect_rule'] ?? '')
        ));
    }

    /**
     * Handle adding .htaccess gone URL
     */
    private function aysa_add_htaccess_gone_rule($gone_url) {
        $gone_url = sanitize_text_field((string) $gone_url);
        if ($gone_url === '') {
            return array(
                'ok' => false,
                'message' => 'Gone URL is missing.',
            );
        }

        if (strpos($gone_url, '/') !== 0) {
            $gone_url = '/' . $gone_url;
        }

        $htaccess_file = ABSPATH . '.htaccess';
        if (!file_exists($htaccess_file)) {
            return array(
                'ok' => false,
                'message' => 'The .htaccess file does not exist in the WordPress root directory.',
            );
        }

        if (!is_writable($htaccess_file)) {
            return array(
                'ok' => false,
                'message' => 'The .htaccess file is not writable. Please check file permissions.',
            );
        }

        $htaccess_content = file_get_contents($htaccess_file);
        if (!is_string($htaccess_content)) {
            return array(
                'ok' => false,
                'message' => 'Could not read the .htaccess file.',
            );
        }

        $section_start = '# BEGIN Aysa Redirects';
        $section_end = '# END Aysa Redirects';

        $gone_rule = 'RedirectMatch 410 ^' . preg_quote($gone_url, '/') . '$';
        $redirect_exists = false;

        if (strpos($htaccess_content, $section_start) !== false) {
            $pattern = '/' . preg_quote($section_start, '/') . '(.*?)' . preg_quote($section_end, '/') . '/s';
            if (preg_match($pattern, $htaccess_content, $matches)) {
                $existing_content = (string) ($matches[1] ?? '');

                if (strpos($existing_content, $gone_rule) !== false) {
                    return array(
                        'ok' => true,
                        'already_done' => true,
                        'message' => 'This gone rule already exists in .htaccess.',
                        'code' => 'gone_rule_exists',
                    );
                }

                $redirect_pattern = 'RedirectMatch 301 \^' . preg_quote(preg_quote($gone_url, '/'), '/') . '\$';
                $redirect_exists = preg_match('/' . $redirect_pattern . '/i', $existing_content) === 1;
                $cleaned_content = preg_replace('/[\r\n]*' . $redirect_pattern . '.*[\r\n]*/i', "\n", $existing_content);
                $cleaned_content = is_string($cleaned_content) ? $cleaned_content : $existing_content;

                $new_content = $section_start . "\n" . $cleaned_content . $gone_rule . "\n" . $section_end;
                $htaccess_content = preg_replace($pattern, $new_content, $htaccess_content);
            }
        } else {
            $new_section = $section_start . "\n" . $gone_rule . "\n" . $section_end . "\n\n";
            $htaccess_content = $new_section . $htaccess_content;
        }

        if (!is_string($htaccess_content) || file_put_contents($htaccess_file, $htaccess_content) === false) {
            return array(
                'ok' => false,
                'message' => 'Failed to write to .htaccess file.',
                'code' => 'htaccess_write_failed',
            );
        }

        return array(
            'ok' => true,
            'message' => 'Gone rule added successfully to .htaccess file.',
            'gone_rule' => $gone_rule,
            'redirect_removed' => $redirect_exists,
            'gone_url' => $gone_url,
        );
    }

    public function handle_add_htaccess_gone() {
        // Check for nonce security
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error('Security check failed.');
            return;
        }
        
        // Get gone URL from POST
        $gone_url = isset($_POST['url']) ? sanitize_text_field(wp_unslash((string) $_POST['url'])) : '';
        
        // Validate data
        if (empty($gone_url)) {
            wp_send_json_error('Gone URL is missing.');
            return;
        }
        
        $result = $this->aysa_add_htaccess_gone_rule($gone_url);
        if (empty($result['ok'])) {
            wp_send_json_error((string) ($result['message'] ?? 'Failed to add gone rule.'));
            return;
        }

        $gone_url = (string) ($result['gone_url'] ?? $gone_url);
        $gone_rule = (string) ($result['gone_rule'] ?? '');
        $redirect_exists = !empty($result['redirect_removed']);

        $project_id = $this->aysa_resolve_project_id_with_local_fallback(0);
        if ($project_id > 0) {
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_add_htaccess_gone',
                'add_htaccess_gone',
                array(
                    'module' => 'technical_optimization',
                    'url' => $gone_url,
                    'redirect_removed' => (int) $redirect_exists,
                ),
                12,
                '/v1/technical/gone/htaccess/add'
            );
        }
        
        // Dacă am șters un redirect 301, încercăm să-l ștergem și din API
        $api_sync_msg = '';
        if ($redirect_exists) {
            // Primul pas: obținem toate redirect-urile din API
            $api_url = rtrim(AYSA_API_BASE_URL, '/') . '/api/Redirect';
            $response = wp_remote_get($api_url);
            
            if (!is_wp_error($response)) {
                $redirects = json_decode(wp_remote_retrieve_body($response), true);
                
                // Construim URL-ul complet pentru comparație
                $site_url = site_url();
                $full_url = strpos($gone_url, 'http') === 0 ? $gone_url : $site_url . $gone_url;
                
                // Căutăm redirect-ul care are sourceUrl-ul nostru
                foreach ($redirects as $redirect) {
                    if (isset($redirect['sourceUrl']) && 
                        (trailingslashit($redirect['sourceUrl']) === trailingslashit($full_url) || 
                         trailingslashit($redirect['sourceUrl']) === trailingslashit($gone_url))) {
                        
                        // Am găsit un redirect cu același sourceUrl, îl ștergem din API
                        $delete_url = $api_url . '/' . $redirect['id'];
                        $delete_response = wp_remote_request($delete_url, array(
                            'method' => 'DELETE',
                            'timeout' => 30,
                        ));
                        
                        if (!is_wp_error($delete_response) && wp_remote_retrieve_response_code($delete_response) < 400) {
                            $api_sync_msg = ' Am șters și redirect-ul 301 din baza de date.';
                        } else {
                            $api_sync_msg = ' Am încercat să șterg redirect-ul 301 din baza de date, dar a apărut o eroare.';
                        }
                        
                        break; // Am găsit și procesat redirect-ul, ieșim din buclă
                    }
                }
            } else {
                // Eroare la comunicarea cu API-ul
                $api_sync_msg = ' Nu am putut comunica cu API-ul pentru a sincroniza baza de date.';
            }
        }
        
        wp_send_json_success(array(
            'message' => 'Gone rule added successfully to .htaccess file.' . $api_sync_msg,
            'gone_rule' => $gone_rule,
            'redirect_removed' => $redirect_exists
        ));
    }

    /**
     * Handle removing .htaccess redirect
     */
    public function handle_remove_htaccess_redirect() {
        // Check for nonce security
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error('Security check failed.');
            return;
        }
        
        // Get source URL from POST
        $source = isset($_POST['source']) ? sanitize_text_field(wp_unslash((string) $_POST['source'])) : '';
        $result = $this->aysa_remove_htaccess_redirect_rule($source);
        if (empty($result['ok'])) {
            wp_send_json_error((string) ($result['message'] ?? 'Redirect rule not found in .htaccess.'));
            return;
        }

        $project_id = $this->aysa_resolve_project_id_with_local_fallback(0);
        if ($project_id > 0) {
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_remove_htaccess_redirect',
                'remove_htaccess_redirect',
                array(
                    'module' => 'technical_optimization',
                    'source' => $source,
                ),
                12,
                '/v1/technical/redirects/htaccess/remove'
            );
        }

        wp_send_json_success(array(
            'message' => (string) ($result['message'] ?? 'Redirect removed successfully from .htaccess file.'),
        ));
    }
    
    /**
     * Handle removing .htaccess gone URL
     */
    public function handle_remove_htaccess_gone() {
        // Check for nonce security
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error('Security check failed');
            return;
        }
        
        // Get gone URL from POST
        $gone_url = isset($_POST['url']) ? sanitize_text_field(wp_unslash((string) $_POST['url'])) : '';
        
        // Validate data
        if (empty($gone_url)) {
            wp_send_json_error('Gone URL is missing.');
            return;
        }
        
        // Make sure URL starts with a slash
        if (strpos($gone_url, '/') !== 0) {
            $gone_url = '/' . $gone_url;
        }
        
        // Get the path to the root .htaccess file
        $htaccess_file = ABSPATH . '.htaccess';
        
        // Check if file exists and is writable
        if (!file_exists($htaccess_file)) {
            wp_send_json_error('The .htaccess file does not exist in the WordPress root directory.');
            return;
        }
        
        if (!is_writable($htaccess_file)) {
            wp_send_json_error('The .htaccess file is not writable. Please check file permissions.');
            return;
        }
        
        // Get current .htaccess content
        $htaccess_content = file_get_contents($htaccess_file);
        
        // Check if our section exists
        $aysa_section_start = '# BEGIN Aysa Redirects';
        $aysa_section_end = '# END Aysa Redirects';
        
        // Pattern to match any 410 gone rule for this URL
        $gone_pattern = 'RedirectMatch 410 \^' . preg_quote(preg_quote($gone_url, '/'), '/') . '\$';
        
        if (strpos($htaccess_content, $aysa_section_start) !== false) {
            // Section exists, find and remove the gone rule
            $pattern = '/' . preg_quote($aysa_section_start, '/') . '(.*?)' . preg_quote($aysa_section_end, '/') . '/s';
            if (preg_match($pattern, $htaccess_content, $matches)) {
                $existing_content = $matches[1];
                
                // Use regex to remove the line containing our gone rule
                $modified_content = preg_replace('/[\r\n]*' . $gone_pattern . '.*[\r\n]*/i', "\n", $existing_content);
                
                // Update the section with the modified content
                $new_content = $aysa_section_start . $modified_content . $aysa_section_end;
                $htaccess_content = preg_replace($pattern, $new_content, $htaccess_content);
                
                // Write the updated content back to .htaccess
                $result = file_put_contents($htaccess_file, $htaccess_content);
                
                if ($result === false) {
                    wp_send_json_error('Failed to write to .htaccess file.');
                    return;
                }

                $project_id = $this->aysa_resolve_project_id_with_local_fallback(0);
                if ($project_id > 0) {
                    $this->aysa_wallet_track_action_usage(
                        $project_id,
                        'aysa_remove_htaccess_gone',
                        'remove_htaccess_gone',
                        array(
                            'module' => 'technical_optimization',
                            'url' => $gone_url,
                        ),
                        12,
                        '/v1/technical/gone/htaccess/remove'
                    );
                }
                
                wp_send_json_success(array(
                    'message' => 'Gone rule removed successfully from .htaccess file.'
                ));
                return;
            }
        }
        
        // If we got here, either the section doesn't exist or the gone rule wasn't found
        wp_send_json_error('Gone rule not found in .htaccess.');
    }

    /**
     * AJAX: Return all posts, pages, products, categories, tags for redirect suggestions
     */
    public function ajax_get_redirect_suggestions() {
        $provided_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash((string) $_POST['nonce'])) : '';
        if (!wp_verify_nonce($provided_nonce, 'aysa_nonce')) {
            wp_send_json_error('Security check failed');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('No permission');
            return;
        }
        $type = isset($_POST['type']) ? sanitize_text_field(wp_unslash((string) $_POST['type'])) : '';
        $results = array();
        if ($type === 'post' || $type === '') {
            $posts = get_posts(array('post_type' => 'post', 'numberposts' => 100));
            foreach ($posts as $post) {
                $results[] = array('title' => $post->post_title, 'url' => get_permalink($post->ID));
            }
        }
        if ($type === 'page' || $type === '') {
            $pages = get_posts(array('post_type' => 'page', 'numberposts' => 100));
            foreach ($pages as $page) {
                $results[] = array('title' => $page->post_title, 'url' => get_permalink($page->ID));
            }
        }
        if (($type === 'product' || $type === '') && post_type_exists('product')) {
            $products = get_posts(array('post_type' => 'product', 'numberposts' => 100));
            foreach ($products as $prod) {
                $results[] = array('title' => $prod->post_title, 'url' => get_permalink($prod->ID));
            }
        }
        if ($type === 'category' || $type === '') {
            $cats = get_categories(array('hide_empty' => false, 'number' => 100));
            foreach ($cats as $cat) {
                $results[] = array('title' => $cat->name, 'url' => get_category_link($cat->term_id));
            }
        }
        if ($type === 'tag' || $type === '') {
            $tags = get_tags(array('hide_empty' => false, 'number' => 100));
            foreach ($tags as $tag) {
                $results[] = array('title' => $tag->name, 'url' => get_tag_link($tag->term_id));
            }
        }
        // Remove duplicates by URL
        $results = array_values(array_unique($results, SORT_REGULAR));

        $project_id = $this->aysa_resolve_project_id_with_local_fallback(0);
        if ($project_id > 0) {
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_get_redirect_suggestions',
                'get_redirect_suggestions',
                array(
                    'module' => 'technical_optimization',
                    'type' => $type === '' ? 'all' : $type,
                    'result_count' => count($results),
                ),
                12,
                '/v1/technical/redirects/suggestions'
            );
        }
        wp_send_json_success($results);
    }
    
    /**
     * AJAX handler for saving plugin settings
     */
    public function handle_save_settings() {
        // Check nonce
        check_ajax_referer('aysa_settings_nonce', 'nonce');
        
        // Check user permissions
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'You do not have permission to save settings.'));
            return;
        }
        
        // Get and sanitize settings
        $settings = isset($_POST['settings']) ? wp_unslash($_POST['settings']) : array();
        
        // Sanitize and save project ID
        if (isset($settings['project_id'])) {
            $project_id = intval($settings['project_id']);
            if ($project_id > 0) {
                update_option('aysa_project_id', $project_id);
            }
        }

        if (array_key_exists('adverlink_api_key', $settings)) {
            update_option('aysa_adverlink_api_key', sanitize_text_field((string) $settings['adverlink_api_key']));
        }

        if (array_key_exists('adverlink_advertiser_org_id', $settings)) {
            update_option('aysa_adverlink_advertiser_org_id', sanitize_text_field((string) $settings['adverlink_advertiser_org_id']));
        }
        
        // Sanitize and save auto scan setting
        if (isset($settings['auto_scan'])) {
            $auto_scan = sanitize_text_field((string) $settings['auto_scan']);
            $allowed_values = array('daily', 'weekly', 'monthly', 'disabled');
            if (in_array($auto_scan, $allowed_values)) {
                update_option('aysa_auto_scan', $auto_scan);
            }
        }
        
        // Sanitize and save notification settings
        if (isset($settings['notify_scan'])) {
            update_option('aysa_notify_scan', (bool) $settings['notify_scan']);
        }
        
        if (isset($settings['notify_issues'])) {
            update_option('aysa_notify_issues', (bool) $settings['notify_issues']);
        }
        
        if (isset($settings['notify_updates'])) {
            update_option('aysa_notify_updates', (bool) $settings['notify_updates']);
        }
        
        // Sanitize and save uninstall table deletion setting
        if (isset($settings['delete_tables_on_uninstall'])) {
            update_option('aysa_delete_tables_on_uninstall', (bool) $settings['delete_tables_on_uninstall']);
        }
        
        // Return success
        wp_send_json_success(array('message' => 'Settings saved successfully.'));
    }
    
    public function handle_get_feature_stats() {
        check_ajax_referer('aysa_automation_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'You do not have permission to perform this action.'));
            return;
        }
        
        $feature_type = isset($_POST['feature_type']) ? sanitize_text_field(wp_unslash((string) $_POST['feature_type'])) : '';
        
        global $wpdb;
        $audit_table = $wpdb->prefix . 'aysa_audit_log';
        
        $total_suggestions = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$audit_table} WHERE action_type = %s AND is_automated = 0",
            $feature_type . '_suggestion'
        ));
        
        $accepted_suggestions = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$audit_table} WHERE action_type = %s AND is_automated = 0",
            $feature_type . '_accepted'
        ));
        
        $acceptance_rate = $total_suggestions > 0 ? round(($accepted_suggestions / $total_suggestions) * 100) : 0;
        
        wp_send_json_success(array(
            'acceptance_rate' => $acceptance_rate,
            'suggestions_count' => $total_suggestions
        ));
    }
    
    public function handle_check_acceptance_rate() {
        check_ajax_referer('aysa_automation_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'You do not have permission to perform this action.'));
            return;
        }
        
        $feature_type = isset($_POST['feature_type']) ? sanitize_text_field(wp_unslash((string) $_POST['feature_type'])) : '';
        
        global $wpdb;
        $audit_table = $wpdb->prefix . 'aysa_audit_log';
        
        $total_suggestions = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$audit_table} WHERE action_type = %s AND is_automated = 0",
            $feature_type . '_suggestion'
        ));
        
        $accepted_suggestions = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$audit_table} WHERE action_type = %s AND is_automated = 0",
            $feature_type . '_accepted'
        ));
        
        $acceptance_rate = $total_suggestions > 0 ? ($accepted_suggestions / $total_suggestions) * 100 : 0;
        $meets_threshold = $acceptance_rate >= 85;
        
        wp_send_json_success(array(
            'meets_threshold' => $meets_threshold,
            'acceptance_rate' => round($acceptance_rate)
        ));
    }
    
    public function handle_save_automation_setting() {
        check_ajax_referer('aysa_automation_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'You do not have permission to perform this action.'));
            return;
        }
        
        $setting_name = isset($_POST['setting_name']) ? sanitize_text_field(wp_unslash((string) $_POST['setting_name'])) : '';
        $setting_value = isset($_POST['setting_value']) ? wp_unslash($_POST['setting_value']) : '';
        
        if (is_bool($setting_value) || $setting_value === 'true' || $setting_value === 'false') {
            $setting_value = filter_var($setting_value, FILTER_VALIDATE_BOOLEAN);
        }
        
        update_option('aysa_' . $setting_name, $setting_value);
        
        wp_send_json_success(array('message' => 'Setting saved successfully.'));
    }
    
    public function handle_save_qa_settings() {
        check_ajax_referer('aysa_automation_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'You do not have permission to perform this action.'));
            return;
        }
        
        $settings = isset($_POST['settings']) ? wp_unslash($_POST['settings']) : array();
        
        foreach ($settings as $key => $value) {
            $sanitized_key = sanitize_text_field($key);
            
            if (in_array($sanitized_key, ['auto_approve_threshold', 'suggest_threshold', 'manual_review_threshold'])) {
                $sanitized_value = floatval($value);
            } else {
                $sanitized_value = filter_var($value, FILTER_VALIDATE_BOOLEAN);
            }
            
            update_option('aysa_' . $sanitized_key, $sanitized_value);
        }
        
        wp_send_json_success(array('message' => 'QA settings saved successfully.'));
    }
    
    public function handle_save_reporting_settings() {
        check_ajax_referer('aysa_settings_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'You do not have permission to save settings.'));
            return;
        }
        
        if (isset($_POST['aysa_reporting_mode'])) {
            update_option('aysa_reporting_mode', sanitize_text_field(wp_unslash((string) $_POST['aysa_reporting_mode'])));
        }
        if (isset($_POST['aysa_reporting_endpoint'])) {
            update_option('aysa_reporting_endpoint', esc_url_raw(wp_unslash((string) $_POST['aysa_reporting_endpoint'])));
        }
        if (isset($_POST['aysa_reporting_enabled'])) {
            update_option('aysa_reporting_enabled', 1);
        } else {
            update_option('aysa_reporting_enabled', 0);
        }
        
        wp_send_json_success(array('message' => 'Reporting settings saved successfully.'));
    }
    
    public function handle_submit_feedback() {
        check_ajax_referer('aysa_feedback_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'You do not have permission to submit feedback.'));
            return;
        }
        
        $config = Aysa_Automation_Config::get_feedback_config();
        
        $feedback_data = array(
            'category' => isset($_POST['category']) ? sanitize_text_field(wp_unslash((string) $_POST['category'])) : '',
            'message' => isset($_POST['message']) ? sanitize_textarea_field(wp_unslash((string) $_POST['message'])) : '',
            'site_url' => isset($_POST['site_url']) ? sanitize_url(wp_unslash((string) $_POST['site_url'])) : '',
            'plugin_version' => isset($_POST['plugin_version']) ? sanitize_text_field(wp_unslash((string) $_POST['plugin_version'])) : '',
            'user_id' => isset($_POST['user_id']) ? intval(wp_unslash((string) $_POST['user_id'])) : 0,
            'timestamp' => current_time('mysql'),
            'user_agent' => isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash((string) $_SERVER['HTTP_USER_AGENT'])) : '',
            'wp_version' => get_bloginfo('version'),
            'php_version' => PHP_VERSION
        );
        
        $response = wp_remote_post($config['endpoint'], array(
            'timeout' => $config['timeout'],
            'headers' => array(
                'Content-Type' => 'application/json',
                'User-Agent' => 'Aysa-Plugin/' . (defined('AYSA_VERSION') ? AYSA_VERSION : '1.0.0')
            ),
            'body' => json_encode($feedback_data)
        ));
        
        if (is_wp_error($response)) {
            aysa_debug_log('Aysa Feedback Error: ' . $response->get_error_message());
            wp_send_json_error(array('message' => 'Failed to send feedback. Please try again later.'));
        } else {
            $response_code = wp_remote_retrieve_response_code($response);
            if ($response_code >= 200 && $response_code < 300) {
                wp_send_json_success(array('message' => 'Feedback sent successfully.'));
            } else {
                aysa_debug_log('Aysa Feedback HTTP Error: ' . $response_code);
                wp_send_json_error(array('message' => 'Server error. Please try again later.'));
            }
        }
    }

    /**
     * Handle social media content generation AJAX request
     */
    public function handle_generate_social_content() {
        // Check nonce for security
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_task_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed.'));
            return;
        }

        // Check user capabilities
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array('message' => 'Insufficient permissions.'));
            return;
        }

        $platforms = isset($_POST['platforms']) ? $_POST['platforms'] : (isset($_POST['platform']) ? array($_POST['platform']) : array());
        $appname = sanitize_text_field($_POST['appname'] ?? ''); // For future API integration
        
        if (empty($platforms)) {
            wp_send_json_error(array('message' => 'At least one platform is required.'));
            return;
        }

        $platform_limits = array(
            'facebook' => 63206,
            'linkedin' => 3000,
            'x' => 280,
            'instagram' => 2200,
            'youtube' => 5000,
            'pinterest' => 500
        );

        $results = array();
        
        foreach ($platforms as $platform) {
            $platform = sanitize_text_field($platform);
            
            if (!isset($platform_limits[$platform])) {
                continue; // Skip invalid platforms
            }

            $limit = $platform_limits[$platform];
            $content = $this->generate_platform_content($platform, $limit);
            
            $results[] = array(
                'platform' => $platform,
                'content' => $content,
                'character_count' => strlen($content),
                'character_limit' => $limit
            );
        }

        if (count($results) === 1 && isset($_POST['platform'])) {
            wp_send_json_success(array(
                'content' => $results[0]['content'],
                'platform' => $results[0]['platform'],
                'character_count' => $results[0]['character_count'],
                'character_limit' => $results[0]['character_limit']
            ));
        } else {
            wp_send_json_success(array(
                'results' => $results,
                'appname' => $appname // Echo back for future use
            ));
        }
    }

    /**
     * Generate platform-specific lorem ipsum content
     */
    private function generate_platform_content($platform, $limit) {
        $base_content = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.";
        
        $platform_specific = array(
            'facebook' => array(
                'prefix' => "🚀 Exciting news! ",
                'suffix' => "\n\n#Marketing #Business #Growth #Success #Innovation #DigitalMarketing #SocialMedia #Entrepreneur #Leadership #Technology",
                'cta' => "\n\n👉 What do you think? Share your thoughts in the comments!"
            ),
            'linkedin' => array(
                'prefix' => "Professional insight: ",
                'suffix' => "\n\n#LinkedIn #Professional #Business #Career #Industry #Networking #Leadership #Innovation #Strategy #Growth",
                'cta' => "\n\nWhat's your experience with this? I'd love to hear your thoughts."
            ),
            'x' => array(
                'prefix' => "🔥 ",
                'suffix' => " #trending #viral #tech",
                'cta' => ""
            ),
            'instagram' => array(
                'prefix' => "✨ ",
                'suffix' => "\n\n#Instagram #Lifestyle #Inspiration #Creative #Beautiful #Amazing #Love #Follow #PhotoOfTheDay #InstaGood",
                'cta' => "\n\n💬 Tag a friend who needs to see this!"
            ),
            'youtube' => array(
                'prefix' => "🎥 New content alert! ",
                'suffix' => "\n\n#YouTube #Content #Video #Subscribe #Like #Share #Creator #Entertainment #Educational #Tutorial",
                'cta' => "\n\n👍 Don't forget to like and subscribe for more content!"
            ),
            'pinterest' => array(
                'prefix' => "📌 ",
                'suffix' => " #Pinterest #DIY #Ideas #Inspiration #Creative",
                'cta' => ""
            )
        );

        $config = $platform_specific[$platform];
        $content = $config['prefix'] . $base_content . $config['cta'] . $config['suffix'];

        if (strlen($content) > $limit) {
            $available_space = $limit - strlen($config['prefix']) - strlen($config['suffix']) - strlen($config['cta']);
            $trimmed_base = substr($base_content, 0, $available_space - 3) . "...";
            $content = $config['prefix'] . $trimmed_base . $config['cta'] . $config['suffix'];
        }

        return $content;
    }
    
    
    public function handle_get_scheduled_tasks() {
        if (class_exists('Aysa_Scheduler')) {
            $scheduler = new Aysa_Scheduler();
            $scheduler->handle_get_scheduled_tasks();
        } else {
            wp_send_json_error('Scheduler not available');
        }
    }
    
    public function handle_get_pending_domain_authority_reviews() {
        aysa_debug_log('[AYSA DOMAIN AUTHORITY] Received request for pending reviews');
        check_ajax_referer('aysa_automation_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            aysa_debug_log('[AYSA DOMAIN AUTHORITY] Unauthorized user attempted to access pending reviews');
            aysa_debug_log('[AYSA DOMAIN AUTHORITY] Unauthorized user attempted to schedule task');
            wp_send_json_error('Insufficient permissions');
        }
        
        $automation_config = get_option('aysa_automation_config', array());
        if (!isset($automation_config['domain_authority_automation']) || !$automation_config['domain_authority_automation']) {
            aysa_debug_log('[AYSA DOMAIN AUTHORITY] Task scheduling rejected - automation not enabled');
            wp_send_json_error('Domain Authority automation is disabled');
        }
        
        $project_id = intval($_POST['project_id']);
        $mean_backlinks = intval($_POST['mean_backlinks']);
        $mean_authority = intval($_POST['mean_authority']);
        $competitor_data = json_decode(stripslashes($_POST['competitor_data']), true);
        
        aysa_debug_log('[AYSA DOMAIN AUTHORITY] Task data received - Project: ' . $project_id . ', Backlinks: ' . $mean_backlinks . ', Authority: ' . $mean_authority);
        
        if (!$project_id || !$mean_backlinks || !$mean_authority) {
            aysa_debug_log('[AYSA DOMAIN AUTHORITY] Invalid task data provided');
            wp_send_json_error('Invalid task data');
        }
        
        $task_data = array(
            'title' => "Get {$mean_backlinks} links with {$mean_authority}+ authority",
            'description' => 'Domain authority optimization task based on competitor analysis',
            'target_backlinks' => $mean_backlinks,
            'target_authority' => $mean_authority,
            'competitor_data' => $competitor_data,
            'project_id' => $project_id,
            'created_at' => current_time('mysql')
        );
        
        aysa_debug_log('[AYSA DOMAIN AUTHORITY] Prepared task data: ' . json_encode($task_data));
        
        $queue_manager = new Aysa_Queue_Manager();
        $queue_manager->init();
        
        aysa_debug_log('[AYSA DOMAIN AUTHORITY] Adding task to queue');
        $result = $queue_manager->add_job('domain_authority_processor', $task_data, 5);
        
        if (is_wp_error($result)) {
            aysa_debug_log('[AYSA DOMAIN AUTHORITY] Failed to add task to queue: ' . $result->get_error_message());
            wp_send_json_error($result->get_error_message());
        }
        
        aysa_debug_log('[AYSA DOMAIN AUTHORITY] Task scheduled successfully with job ID: ' . $result);
        wp_send_json_success(array(
            'job_id' => $result,
            'message' => 'Domain Authority task scheduled successfully'
        ));
    }
    
    public function handle_schedule_association_task() {
        aysa_debug_log('[AYSA DOMAIN AUTHORITY] Received association task scheduling request');
        
        if (!wp_verify_nonce($_POST['nonce'], 'aysa_automation_nonce')) {
            aysa_debug_log('[AYSA DOMAIN AUTHORITY] Invalid nonce for association task scheduling');
            wp_send_json_error('Invalid nonce');
        }
        
        if (!current_user_can('manage_options')) {
            aysa_debug_log('[AYSA DOMAIN AUTHORITY] Unauthorized user attempted to schedule association task');
            wp_send_json_error('Insufficient permissions');
        }
        
        $automation_config = get_option('aysa_automation_config', array());
        if (!isset($automation_config['domain_authority_automation']) || !$automation_config['domain_authority_automation']) {
            aysa_debug_log('[AYSA DOMAIN AUTHORITY] Association task scheduling rejected - automation not enabled');
            wp_send_json_error('Domain Authority automation is disabled');
        }
        
        $project_id = intval($_POST['project_id']);
        $association_count = intval($_POST['association_count']);
        
        aysa_debug_log('[AYSA DOMAIN AUTHORITY] Association task data received - Project: ' . $project_id . ', Count: ' . $association_count);
        
        if (!$project_id || !$association_count) {
            aysa_debug_log('[AYSA DOMAIN AUTHORITY] Invalid association task data provided');
            wp_send_json_error('Invalid task data');
        }
        
        $task_data = array(
            'title' => "Associate {$association_count} keywords with pages/products/categories",
            'description' => 'Keyword association task based on available unassociated keywords',
            'association_count' => $association_count,
            'project_id' => $project_id,
            'created_at' => current_time('mysql')
        );
        
        aysa_debug_log('[AYSA DOMAIN AUTHORITY] Prepared association task data: ' . json_encode($task_data));
        
        $queue_manager = new Aysa_Queue_Manager();
        $queue_manager->init();
        
        aysa_debug_log('[AYSA DOMAIN AUTHORITY] Adding association task to queue');
        $result = $queue_manager->add_job('association_processor', $task_data, 5);
        
        if (is_wp_error($result)) {
            aysa_debug_log('[AYSA DOMAIN AUTHORITY] Failed to add association task to queue: ' . $result->get_error_message());
            wp_send_json_error($result->get_error_message());
        }
        
        aysa_debug_log('[AYSA DOMAIN AUTHORITY] Association task scheduled successfully with job ID: ' . $result);
        wp_send_json_success(array(
            'job_id' => $result,
            'message' => 'Association task scheduled successfully'
        ));
    }
    
    public function handle_add_task_to_queue() {
        aysa_debug_log('[AYSA AUTOMATION] Received task queue addition request');
        
        if (!wp_verify_nonce($_POST['nonce'], 'aysa_automation_nonce')) {
            aysa_debug_log('[AYSA AUTOMATION] Invalid nonce for task queue addition');
            wp_send_json_error('Invalid nonce');
        }
        
        if (!current_user_can('manage_options')) {
            aysa_debug_log('[AYSA AUTOMATION] Unauthorized user attempted to add task to queue');
            wp_send_json_error('Insufficient permissions');
        }
        
        $task_id = sanitize_text_field($_POST['task_id']);
        $task_title = sanitize_text_field($_POST['task_title'] ?? '');
        $processor = sanitize_text_field($_POST['processor'] ?? 'association_processor');
        $priority = intval($_POST['priority'] ?? 5);
        $task_data_json = $_POST['task_data'] ?? '';
        
        aysa_debug_log('[AYSA AUTOMATION] Task queue data received - ID: ' . $task_id . ', Title: ' . $task_title . ', Processor: ' . $processor);
        
        if (!$task_id) {
            aysa_debug_log('[AYSA AUTOMATION] Invalid task ID provided');
            wp_send_json_error('Invalid task ID');
        }
        
        $task_data = array(
            'task_id' => $task_id,
            'title' => $task_title,
            'processor_type' => $processor,
            'source' => 'dotnet_api',
            'created_at' => current_time('mysql')
        );
        
        if (!empty($task_data_json)) {
            $parsed_task_data = json_decode(stripslashes($task_data_json), true);
            if ($parsed_task_data) {
                $task_data = array_merge($task_data, $parsed_task_data);
                aysa_debug_log('[AYSA AUTOMATION] Merged task data from .NET Core API');
            }
        }
        
        aysa_debug_log('[AYSA AUTOMATION] Prepared task data for queue: ' . json_encode($task_data));
        
        $queue_manager = new Aysa_Queue_Manager();
        $queue_manager->init();
        
        aysa_debug_log('[AYSA AUTOMATION] Adding task to automation queue with processor: ' . $processor);
        $result = $queue_manager->add_job($processor, $task_data, $priority);
        
        if (is_wp_error($result)) {
            aysa_debug_log('[AYSA AUTOMATION] Failed to add task to queue: ' . $result->get_error_message());
            wp_send_json_error($result->get_error_message());
        }
        
        aysa_debug_log('[AYSA AUTOMATION] Task added to queue successfully with job ID: ' . $result);
        wp_send_json_success(array(
            'job_id' => $result,
            'task_id' => $task_id,
            'message' => 'Task added to automation queue successfully'
        ));
    }
    
    public function handle_add_pending_review() {
        check_ajax_referer('aysa_automation_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $review_data = $_POST['review_data'] ?? array();
        
        if (empty($review_data)) {
            wp_send_json_error('No review data provided');
            return;
        }
        
        $existing_reviews = get_option('aysa_pending_dotnet_reviews', array());
        $existing_reviews[$review_data['task_id']] = $review_data;
        
        $result = update_option('aysa_pending_dotnet_reviews', $existing_reviews);
        
        if ($result) {
            wp_send_json_success('Task added to pending reviews');
        } else {
            wp_send_json_error('Failed to add task to pending reviews');
        }
    }
    
    public function handle_get_pending_dotnet_reviews() {
        check_ajax_referer('aysa_automation_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $pending_reviews = get_option('aysa_pending_dotnet_reviews', array());
        
        wp_send_json_success($pending_reviews);
    }
    
    public function handle_approve_dotnet_task() {
        check_ajax_referer('aysa_automation_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $task_id = $_POST['task_id'] ?? '';
        if (empty($task_id)) {
            wp_send_json_error('No task ID provided');
            return;
        }
        
        $pending_reviews = get_option('aysa_pending_dotnet_reviews', array());
        if (isset($pending_reviews[$task_id])) {
            $task_data = $pending_reviews[$task_id];
            unset($pending_reviews[$task_id]);
            update_option('aysa_pending_dotnet_reviews', $pending_reviews);
            
            $queue_manager = new Aysa_Queue_Manager();
            $queue_manager->init();
            $result = $queue_manager->add_job('research_processor', $task_data['task_data'], 3);
            
            wp_send_json_success('Task approved and added to queue');
        } else {
            wp_send_json_error('Task not found in pending reviews');
        }
    }
    
    public function handle_reject_dotnet_task() {
        check_ajax_referer('aysa_automation_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $task_id = $_POST['task_id'] ?? '';
        if (empty($task_id)) {
            wp_send_json_error('No task ID provided');
            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);
            wp_send_json_success('Task rejected and removed from pending reviews');
        } else {
            wp_send_json_error('Task not found in pending reviews');
        }
    }
    
    public function handle_update_status() {
        check_ajax_referer('aysa_nonce', 'nonce');
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        
        $association_id = intval($_POST['association_id'] ?? 0);
        $new_status = sanitize_text_field($_POST['status'] ?? '');
        
        if (!$association_id || !$new_status) {
            wp_send_json_error('Invalid association ID or status');
            return;
        }
        
        // For now, we'll store the status in WordPress options
        // In a real implementation, this would update a database table
        $association_statuses = get_option('aysa_association_statuses', array());
        $association_statuses[$association_id] = $new_status;
        
        $result = update_option('aysa_association_statuses', $association_statuses);
        
        if ($result) {
            wp_send_json_success(array(
                'message' => 'Status updated successfully',
                'association_id' => $association_id,
                'new_status' => $new_status
            ));
        } else {
            wp_send_json_error('Failed to update status');
        }
    }
    
    /**
     * Handle getting link orders
     */
    public function handle_get_link_orders() {
        // Check nonce
        if (!wp_verify_nonce($_POST['nonce'], 'aysa_orders_nonce')) {
            wp_send_json_error('Security check failed');
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
        }
        
        $project_id = intval($_POST['project_id']);
        $status_filter = sanitize_text_field($_POST['status_filter']);
        
        if (!$project_id) {
            wp_send_json_error('Invalid project ID');
        }
        
        global $wpdb;
        $table_name = $wpdb->prefix . 'aysa_link_purchases';
        $existing_order = $wpdb->get_row($wpdb->prepare("SELECT id, project_id, status FROM {$table_name} WHERE id = %d", $order_id));
        $project_id = intval($existing_order->project_id ?? 0);
        if ($project_id <= 0) {
            $project_id = $this->aysa_resolve_project_id_with_local_fallback(0);
        }
        
        // Build query based on status filter
        $where_clause = "WHERE project_id = %d";
        $params = array($project_id);
        
        if ($status_filter !== 'all') {
            $where_clause .= " AND status = %s";
            $params[] = $status_filter;
        }
        
        $query = "SELECT * FROM {$table_name} {$where_clause} ORDER BY created_at DESC";
        
        $orders = $wpdb->get_results($wpdb->prepare($query, $params));
        
        if ($orders === false) {
            aysa_debug_log('[AYSA ORDERS] Database error: ' . $wpdb->last_error);
            wp_send_json_error('Database error occurred');
        }
        
        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_get_link_orders',
            'offpage_get_link_orders',
            array(
                'module' => 'offpage_legacy',
                'status_filter' => $status_filter,
                'count' => is_array($orders) ? count($orders) : 0,
            ),
            2,
            'wp_ajax/aysa_get_link_orders'
        );

        wp_send_json_success($orders);
    }
    
    /**
     * Handle getting single order details
     */
    public function handle_get_order_details() {
        // Check nonce
        if (!wp_verify_nonce($_POST['nonce'], 'aysa_orders_nonce')) {
            wp_send_json_error('Security check failed');
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
        }
        
        $order_id = intval($_POST['order_id']);
        
        if (!$order_id) {
            wp_send_json_error('Invalid order ID');
        }
        
        global $wpdb;
        $table_name = $wpdb->prefix . 'aysa_link_purchases';
        
        $query = "SELECT * FROM {$table_name} WHERE id = %d";
        $order = $wpdb->get_row($wpdb->prepare($query, $order_id));
        
        if (!$order) {
            wp_send_json_error('Order not found');
        }
        
        $project_id = intval($order->project_id ?? 0);
        if ($project_id <= 0) {
            $project_id = $this->aysa_resolve_project_id_with_local_fallback(0);
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_get_order_details',
            'offpage_get_order_details_' . $order_id,
            array(
                'module' => 'offpage_legacy',
                'order_id' => $order_id,
            ),
            1,
            'wp_ajax/aysa_get_order_details'
        );

        wp_send_json_success($order);
    }
    
    /**
     * Handle retrying link order
     */
    public function handle_retry_link_order() {
        // Check nonce
        if (!wp_verify_nonce($_POST['nonce'], 'aysa_orders_nonce')) {
            wp_send_json_error('Security check failed');
        }
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
        }
        
        $order_id = intval($_POST['order_id']);
        
        if (!$order_id) {
            wp_send_json_error('Invalid order ID');
        }
        
        global $wpdb;
        $table_name = $wpdb->prefix . 'aysa_link_purchases';
        
        // Update order status to pending to retry
        $result = $wpdb->update(
            $table_name,
            array(
                'status' => 'pending',
                'updated_at' => current_time('mysql')
            ),
            array('id' => $order_id),
            array('%s', '%s'),
            array('%d')
        );
        
        if ($result === false) {
            aysa_debug_log('[AYSA ORDERS] Database update error: ' . $wpdb->last_error);
            wp_send_json_error('Failed to update order status');
        }
        
        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_retry_link_order',
            'offpage_retry_link_order_' . $order_id,
            array(
                'module' => 'offpage_legacy',
                'order_id' => $order_id,
                'previous_status' => (string) ($existing_order->status ?? ''),
            ),
            8,
            'wp_ajax/aysa_retry_link_order'
        );

        wp_send_json_success('Order marked for retry');
    }
    
    /**
     * Enqueue credit analytics scripts and styles
     */
    public function enqueue_credit_analytics_scripts($hook = '') {
        $screen = function_exists('get_current_screen') ? get_current_screen() : null;
        $page = isset($_GET['page']) ? sanitize_key((string) $_GET['page']) : '';
        $is_settings_page = ($page === 'aysa-settings')
            || ($screen && isset($screen->id) && is_string($screen->id) && strpos($screen->id, 'aysa-settings') !== false)
            || ($screen && isset($screen->base) && is_string($screen->base) && strpos($screen->base, 'aysa-settings') !== false);

        if (!$is_settings_page) {
            return;
        }
        
        // Enqueue Chart.js for the credit breakdown chart
        wp_enqueue_script(
            'chart-js',
            AYSA_PLUGIN_URL . 'assets/js/vendor/chartjs-stub.js',
            array(),
            AYSA_VERSION,
            true
        );
        
        // Enqueue credit analytics CSS
        wp_enqueue_style(
            'aysa-credit-analytics',
            AYSA_PLUGIN_URL . 'assets/css/aysa-credit-analytics.css',
            array(),
            AYSA_VERSION,
            'all'
        );
        
        // Enqueue credit analytics JavaScript
        wp_enqueue_script(
            'aysa-credit-analytics',
            AYSA_PLUGIN_URL . 'assets/js/aysa-credit-analytics.js',
            array('jquery', 'chart-js'),
            AYSA_VERSION,
            true
        );
        
        // Localize script with AJAX data
        wp_localize_script(
            'aysa-credit-analytics',
            'aysa_credit_analytics',
            array(
                'ajax_url' => admin_url('admin-ajax.php'),
                'nonce' => wp_create_nonce('aysa_credit_analytics_nonce'),
                'api_base_url' => $this->get_api_base_url()
            )
        );
    }
    
    /**
     * Get API base URL from settings
     */
    private function get_api_base_url() {
        if (class_exists('Aysa_API_Config')) {
            return rtrim((string) Aysa_API_Config::get_api_base_url(), '/');
        }
        return rtrim((string) AYSA_API_BASE_URL, '/');
    }

    /**
     * Handle AJAX request to get WordToLink API URL
     */
    public function handle_get_wordtolink_api_url() {
        // Verify nonce
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }

        // Get parameters
        $user_id = sanitize_text_field($_POST['user_id'] ?? '');
        $url_part = sanitize_text_field($_POST['url_part'] ?? '');
        $project_id = $this->aysa_resolve_project_id_with_local_fallback(intval($_POST['project_id'] ?? 0));

        if (empty($user_id) || empty($url_part)) {
            wp_send_json_error('Missing required parameters');
            return;
        }

        // Use the centralized API configuration
        if (!class_exists('Aysa_API_Config')) {
            require_once AYSA_PLUGIN_DIR . 'includes/config/aysa-api-config.php';
        }

        try {
            // Get the full API URL using the centralized configuration
            $api_url = Aysa_API_Config::get_endpoint_url_with_params(
                'wordtolink_search',
                array(
                    'userId' => $user_id,
                    'urlPart' => $url_part
                )
            );

            if ($api_url) {
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_get_wordtolink_api_url',
                    'offpage_get_wordtolink_api_url',
                    array(
                        'module' => 'offpage_legacy',
                        'user_id' => $user_id,
                        'url_part' => $url_part,
                    ),
                    1,
                    'wp_ajax/aysa_get_wordtolink_api_url'
                );

                wp_send_json_success(array(
                    'api_url' => $api_url,
                    'environment' => Aysa_API_Config::get_environment(),
                    'base_url' => Aysa_API_Config::get_api_base_url()
                ));
            } else {
                wp_send_json_error('Failed to generate API URL');
            }
        } catch (Exception $e) {
            wp_send_json_error('Error: ' . $e->getMessage());
        }
    }

    /**
     * Handle AJAX request: list link building portals from Adverlink marketplace API.
     */
    public function handle_adverlink_list_portals() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $creds = $this->aysa_get_adverlink_credentials();
        $api_key = $creds['api_key'];
        $advertiser_org_id = $creds['org_id'];
        $service_type = sanitize_text_field((string) ($_POST['service_type'] ?? ''));
        $billing_type = sanitize_text_field((string) ($_POST['billing_type'] ?? ''));
        $per_page = max(1, min(200, intval($_POST['per_page'] ?? 20)));
        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        $force_refresh = !empty($_POST['force_refresh']) && $_POST['force_refresh'] !== '0';

        $credentials_check = $this->aysa_validate_adverlink_marketplace_credentials($creds);
        if (is_wp_error($credentials_check)) {
            wp_send_json_error($this->aysa_format_adverlink_marketplace_error($credentials_check));
            return;
        }

        $cache_key = 'aysa_adverlink_portals_' . md5(
            implode('|', array(
                $project_id,
                $service_type,
                $billing_type,
                $per_page
            ))
        );
        if (!$force_refresh) {
            $cached = get_transient($cache_key);
            if (is_array($cached) && isset($cached['items'])) {
                wp_send_json_success(array(
                    'items' => $cached['items'],
                    'raw' => $cached['raw'],
                    'cached' => true
                ));
                return;
            }
        }

        $decoded = $this->aysa_broker_request('POST', '/adverlink/v1/public/portals', array(
            'project_id' => $project_id,
            'api_key' => $api_key,
            'advertiser_org_id' => $advertiser_org_id,
            'service_type' => $service_type,
            'billing_type' => $billing_type,
            'per_page' => $per_page,
        ), 45);

        if (is_wp_error($decoded)) {
            wp_send_json_error(array(
                'code' => 'adverlink_api_error',
                'message' => $decoded->get_error_message()
            ));
            return;
        }

        $items = array();
        if (is_array($decoded)) {
            if (isset($decoded['data']) && is_array($decoded['data'])) {
                $items = $decoded['data'];
            } elseif (isset($decoded['items']) && is_array($decoded['items'])) {
                $items = $decoded['items'];
            } elseif (isset($decoded['portals']) && is_array($decoded['portals'])) {
                $items = $decoded['portals'];
            } elseif (array_keys($decoded) === range(0, count($decoded) - 1)) {
                $items = $decoded;
            }
        }

        set_transient($cache_key, array('items' => $items, 'raw' => $decoded), 15 * MINUTE_IN_SECONDS);
        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_adverlink_list_portals',
            'offpage_list_portals_' . $project_id,
            array(
                'module' => 'offpage',
                'cached' => false,
                'service_type' => $service_type,
                'billing_type' => $billing_type,
                'items_count' => count($items),
            ),
            0,
            'wp_ajax/aysa_adverlink_list_portals'
        );
        wp_send_json_success(array(
            'items' => $items,
            'raw' => $decoded,
            'cached' => false
        ));
    }

    /**
     * Handle AJAX request: run backlink audit against configured API endpoint (DataForSEO proxy style).
     */
    public function handle_adverlink_backlink_audit() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $domain = sanitize_text_field((string) ($_POST['domain'] ?? ''));
        $limit = max(1, min(500, intval($_POST['limit'] ?? 50)));
        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        $force_refresh = !empty($_POST['force_refresh']) && intval($_POST['force_refresh']) === 1;

        if (empty($domain)) {
            wp_send_json_error('Target domain is required.');
            return;
        }

        $result = $this->aysa_adjustments_run_offpage_backlink_audit($project_id, $domain, $limit, $force_refresh);
        if (is_wp_error($result)) {
            wp_send_json_error($result->get_error_message());
            return;
        }

        wp_send_json_success($result);
    }

    public function handle_adverlink_get_backlink_audit() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $snapshot = get_option($this->aysa_get_backlink_audit_key($project_id), array());
        $last_sync = get_option($this->aysa_get_backlink_audit_sync_key($project_id), array());
        if (!is_array($snapshot) || empty($snapshot)) {
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_adverlink_get_backlink_audit',
                'offpage_get_backlink_audit_empty_' . $project_id,
                array(
                    'module' => 'offpage',
                    'items_count' => 0,
                ),
                0,
                'wp_ajax/aysa_adverlink_get_backlink_audit'
            );
            wp_send_json_success(array(
                'items' => array(),
                'snapshot' => array(),
                'last_sync' => array(),
            ));
            return;
        }

        wp_send_json_success(array(
            'items' => isset($snapshot['items']) && is_array($snapshot['items']) ? $snapshot['items'] : array(),
            'snapshot' => $snapshot,
            'last_sync' => is_array($last_sync) ? $last_sync : array(),
        ));
    }

    private function aysa_adjustments_run_offpage_backlink_audit($project_id, $domain, $limit = 50, $force_refresh = false) {
        $project_id = intval($project_id);
        $domain = sanitize_text_field((string) $domain);
        $limit = max(1, min(500, intval($limit)));
        $force_refresh = !empty($force_refresh);

        if ($project_id <= 0) {
            return new WP_Error('invalid_project', 'Invalid project id');
        }
        if ($domain === '') {
            return new WP_Error('missing_domain', 'Target domain is required.');
        }

        if (!$force_refresh) {
            $cached_snapshot = get_option($this->aysa_get_backlink_audit_domain_key($project_id, $domain), array());
            $cached_items = (is_array($cached_snapshot) && isset($cached_snapshot['items']) && is_array($cached_snapshot['items']))
                ? $cached_snapshot['items']
                : array();
            if (!empty($cached_items)) {
                return array(
                    'items' => $cached_items,
                    'raw' => array(),
                    'provider' => (string) ($cached_snapshot['provider'] ?? 'local_cache'),
                    'generated_at' => (string) ($cached_snapshot['generated_at'] ?? ''),
                    'cached' => true,
                );
            }
        }

        $broker_result = $this->aysa_broker_request('POST', '/v1/dataforseo/backlink-audit', array(
            'target' => $domain,
            'domain' => $domain,
            'limit' => $limit,
            'project_id' => $project_id,
        ), 90);

        if (is_wp_error($broker_result)) {
            $debug_message = 'Backlink audit broker failed: ' . $broker_result->get_error_message();
            $message = 'The live backlink provider was unavailable, so the audit used the backup analysis flow.';
            if (defined('AYSA_DEBUG') && AYSA_DEBUG) {
                $message .= ' ' . $debug_message;
            }
            $fallback_link_issues = $this->aysa_fetch_adverlink_link_issues($project_id);
            if (is_wp_error($fallback_link_issues)) {
                return new WP_Error('backlink_audit_failed', $message);
            }

            $issues_items = (is_array($fallback_link_issues) && isset($fallback_link_issues['items']) && is_array($fallback_link_issues['items']))
                ? $fallback_link_issues['items']
                : array();
            $normalized_fallback = array();
            foreach ($issues_items as $it) {
                if (!is_array($it)) {
                    continue;
                }
                $normalized_fallback[] = array(
                    'source_url' => (string) ($it['published_on_url'] ?? ''),
                    'target_url' => (string) ($it['linked_to_url'] ?? ''),
                    'anchor_text' => (string) ($it['published_keyword'] ?? ''),
                    'referring_domain' => (string) ($it['portal_domain'] ?? ''),
                    'status' => (string) ($it['status'] ?? ''),
                    'health' => (string) ($it['status'] ?? ''),
                    'issue_type' => (string) ($it['issue_type'] ?? ''),
                    'issue_message' => (string) ($it['issue_summary'] ?? $it['issue_message'] ?? ''),
                    'first_seen' => (string) ($it['detected_at'] ?? ''),
                    'last_seen' => (string) ($it['detected_at'] ?? ''),
                    'risk' => 'high',
                );
            }

            $snapshot = array(
                'domain' => $domain,
                'provider' => 'link_issues_fallback',
                'generated_at' => current_time('mysql'),
                'count' => count($normalized_fallback),
                'items' => $normalized_fallback,
                'fallback_reason' => $message,
            );
            update_option($this->aysa_get_backlink_audit_key($project_id), $snapshot, false);
            update_option($this->aysa_get_backlink_audit_domain_key($project_id, $domain), $snapshot, false);
            update_option($this->aysa_get_backlink_audit_sync_key($project_id), array(
                'synced_at' => current_time('mysql'),
                'domain' => $domain,
                'count' => count($normalized_fallback),
                'provider' => 'link_issues_fallback',
            ), false);
            if ($force_refresh) {
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_adverlink_backlink_audit',
                    'offpage_backlink_audit_' . md5($domain),
                    array(
                        'module' => 'offpage',
                        'domain' => $domain,
                        'fallback' => true,
                        'items_count' => count($normalized_fallback),
                        'force_refresh' => 1,
                    ),
                    9,
                    'wp_ajax/aysa_adverlink_backlink_audit'
                );
            }

            return array(
                'items' => $normalized_fallback,
                'raw' => array(),
                'fallback' => true,
                'fallback_source' => 'link_issues',
                'fallback_reason' => $message,
                'generated_at' => $snapshot['generated_at'],
            );
        }

        $items = (isset($broker_result['items']) && is_array($broker_result['items'])) ? $broker_result['items'] : array();
        $snapshot = array(
            'domain' => $domain,
            'provider' => (string) ($broker_result['provider'] ?? 'api.aysa.ai'),
            'generated_at' => current_time('mysql'),
            'count' => count($items),
            'items' => $items,
        );
        update_option($this->aysa_get_backlink_audit_key($project_id), $snapshot, false);
        update_option($this->aysa_get_backlink_audit_domain_key($project_id, $domain), $snapshot, false);
        update_option($this->aysa_get_backlink_audit_sync_key($project_id), array(
            'synced_at' => current_time('mysql'),
            'domain' => $domain,
            'count' => count($items),
            'provider' => (string) ($broker_result['provider'] ?? 'api.aysa.ai'),
        ), false);
        if ($force_refresh) {
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_adverlink_backlink_audit',
                'offpage_backlink_audit_' . md5($domain),
                array(
                    'module' => 'offpage',
                    'domain' => $domain,
                    'fallback' => false,
                    'items_count' => count($items),
                    'force_refresh' => 1,
                ),
                9,
                'wp_ajax/aysa_adverlink_backlink_audit'
            );
        }

        return array(
            'items' => $items,
            'raw' => (isset($broker_result['raw']) && is_array($broker_result['raw'])) ? $broker_result['raw'] : array(),
            'provider' => (string) ($broker_result['provider'] ?? 'api.aysa.ai'),
            'generated_at' => $snapshot['generated_at'],
        );
    }

    public function handle_competitor_backlinks_gap_insights() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $home_host = $this->aysa_monitoring_extract_host((string) home_url('/'));
        $resolved_context = $this->aysa_monitoring_resolve_context($home_host !== '' ? $home_host : 'florariebragadiru.ro');
        $target = strtolower(trim((string) ($resolved_context['target'] ?? '')));
        if ($target !== '') {
            $target = $this->aysa_monitoring_extract_host($target);
        }
        if ($target === '') {
            $target = $home_host;
        }
        if ($target === '') {
            $target = $this->aysa_monitoring_extract_host((string) ($_POST['target'] ?? ''));
        }
        if ($target === '') {
            wp_send_json_error('Target domain is required.');
            return;
        }

        $competitors = array();
        $seed_data = $this->aysa_keyword_strategy_extract_seed_data(get_current_user_id());
        if (isset($seed_data['competitors']) && is_array($seed_data['competitors'])) {
            $competitors = $seed_data['competitors'];
        }
        if (empty($competitors)) {
            $shared_user_id = intval(get_option('aysa_user_id', 0));
            if ($shared_user_id > 0) {
                $shared_seed_data = $this->aysa_keyword_strategy_extract_seed_data($shared_user_id);
                if (isset($shared_seed_data['competitors']) && is_array($shared_seed_data['competitors'])) {
                    $competitors = $shared_seed_data['competitors'];
                }
            }
        }

        $normalized_competitors = array();
        foreach ($competitors as $raw) {
            $host = $this->aysa_monitoring_extract_host((string) $raw);
            if ($host === '') {
                $host = strtolower(trim((string) $raw));
            }
            $host = preg_replace('/^www\./', '', $host);
            if ($host === '' || $host === $target) {
                continue;
            }
            if (!preg_match('/^[a-z0-9.-]+\.[a-z]{2,}$/', $host)) {
                continue;
            }
            $normalized_competitors[$host] = true;
        }
        $competitor_domains = array_keys($normalized_competitors);
        if (empty($competitor_domains)) {
            wp_send_json_error('No competitors configured in Setup SEO Profile.');
            return;
        }

        $limit = max(50, min(300, intval($_POST['limit'] ?? 150)));
        $domains = array_merge(array($target), $competitor_domains);

        $force_refresh = !empty($_POST['force_refresh']) && intval($_POST['force_refresh']) === 1;

        $profiles = array();
        $errors = array();
        foreach ($domains as $domain) {
            $cached_domain = get_option($this->aysa_get_backlink_audit_domain_key($project_id, $domain), array());
            $cached_items = (is_array($cached_domain) && isset($cached_domain['items']) && is_array($cached_domain['items']))
                ? $cached_domain['items']
                : array();

            if (!$force_refresh) {
                $profiles[$domain] = !empty($cached_items)
                    ? $this->aysa_backlinks_build_profile($domain, $cached_items)
                    : $this->aysa_backlinks_empty_profile($domain);
                continue;
            }

            $broker_result = $this->aysa_broker_request('POST', '/v1/dataforseo/backlink-audit', array(
                'target' => $domain,
                'domain' => $domain,
                'limit' => $limit,
                'project_id' => $project_id,
            ), 180);
            if (is_wp_error($broker_result)) {
                $errors[] = array('domain' => $domain, 'error' => $broker_result->get_error_message());
                $profiles[$domain] = !empty($cached_items)
                    ? $this->aysa_backlinks_build_profile($domain, $cached_items)
                    : $this->aysa_backlinks_empty_profile($domain);
                continue;
            }

            $items = (isset($broker_result['items']) && is_array($broker_result['items'])) ? $broker_result['items'] : array();
            $snapshot = array(
                'domain' => $domain,
                'provider' => (string) ($broker_result['provider'] ?? 'dataforseo'),
                'generated_at' => current_time('mysql'),
                'count' => count($items),
                'items' => $items,
            );
            update_option($this->aysa_get_backlink_audit_domain_key($project_id, $domain), $snapshot, false);
            $profiles[$domain] = $this->aysa_backlinks_build_profile($domain, $items);
        }

        $target_profile = isset($profiles[$target]) ? $profiles[$target] : $this->aysa_backlinks_empty_profile($target);
        $competitor_profiles = array();
        foreach ($competitor_domains as $domain) {
            $competitor_profiles[] = isset($profiles[$domain]) ? $profiles[$domain] : $this->aysa_backlinks_empty_profile($domain);
        }

        $brand_terms = $this->aysa_backlinks_extract_brand_terms($target, get_current_user_id());
        $benchmark = $this->aysa_backlinks_build_benchmark($competitor_profiles, $brand_terms);
        $gap = $this->aysa_backlinks_build_gap($target_profile, $benchmark, $brand_terms);

        // Local cached reads should not consume credits.
        if ($force_refresh) {
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_competitor_backlinks_gap_insights',
                'competitor_backlinks_gap_insights',
                array(
                    'module' => 'competitor_backlinks',
                    'target' => $target,
                    'competitors_count' => count($competitors),
                    'errors_count' => count($errors),
                    'force_refresh' => 1,
                ),
                45,
                '/v1/backlinks/gap-insights'
            );
        }

        wp_send_json_success(array(
            'target' => $target,
            'project_id' => $project_id,
            'profiles' => $profiles,
            'target_profile' => $target_profile,
            'competitor_profiles' => $competitor_profiles,
            'benchmark' => $benchmark,
            'gap' => $gap,
            'brand_terms' => $brand_terms,
            'errors' => $errors,
            'generated_at' => current_time('mysql'),
        ));
    }

    public function handle_competitor_backlinks_get_cached() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $domains_raw = isset($_POST['domains']) ? wp_unslash($_POST['domains']) : '[]';
        $domains = json_decode((string) $domains_raw, true);
        if (!is_array($domains)) {
            $domains = array();
        }

        $result = array();
        foreach ($domains as $raw) {
            $domain = $this->aysa_monitoring_extract_host((string) $raw);
            if ($domain === '') {
                $domain = strtolower(trim((string) $raw));
            }
            $domain = preg_replace('/^www\./', '', $domain);
            if ($domain === '' || !preg_match('/^[a-z0-9.-]+\.[a-z]{2,}$/', $domain)) {
                continue;
            }

            $snapshot = get_option($this->aysa_get_backlink_audit_domain_key($project_id, $domain), array());
            $items = (is_array($snapshot) && isset($snapshot['items']) && is_array($snapshot['items']))
                ? $snapshot['items']
                : array();

            $result[$domain] = array(
                'items' => $items,
                'count' => count($items),
                'generated_at' => is_array($snapshot) ? (string) ($snapshot['generated_at'] ?? '') : '',
                'provider' => is_array($snapshot) ? (string) ($snapshot['provider'] ?? '') : '',
            );
        }

        wp_send_json_success(array(
            'project_id' => $project_id,
            'domains' => $result,
        ));
    }

    private function aysa_backlinks_empty_profile($domain) {
        return array(
            'domain' => (string) $domain,
            'total_backlinks' => 0,
            'ref_domains' => 0,
            'dofollow_count' => 0,
            'dofollow_ratio' => 0,
            'avg_authority' => 0,
            'high_authority_links' => 0,
            'top_anchors' => array(),
            'keyword_cloud' => array(),
        );
    }

    private function aysa_backlinks_build_profile($domain, $items) {
        $total = is_array($items) ? count($items) : 0;
        $ref_domains = array();
        $anchor_counts = array();
        $term_counts = array();
        $dofollow = 0;
        $authority_sum = 0.0;
        $authority_count = 0;
        $high_authority = 0;

        if (is_array($items)) {
            foreach ($items as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $ref = strtolower(trim((string) ($row['referring_domain'] ?? $row['source_domain'] ?? $row['domain_from'] ?? '')));
                if ($ref !== '') {
                    $ref_domains[$ref] = true;
                }

                $anchor = trim((string) ($row['anchor_text'] ?? $row['anchor'] ?? ''));
                if ($anchor !== '') {
                    $key = mb_strtolower($anchor, 'UTF-8');
                    $anchor_counts[$key] = intval($anchor_counts[$key] ?? 0) + 1;
                    $terms = preg_split('/[^\p{L}\p{N}]+/u', $key);
                    if (is_array($terms)) {
                        foreach ($terms as $term) {
                            $term = trim((string) $term);
                            if (mb_strlen($term, 'UTF-8') < 3) {
                                continue;
                            }
                            if (in_array($term, array('the', 'and', 'for', 'cu', 'din', 'care', 'sunt', 'sau', 'www', 'com', 'ro'), true)) {
                                continue;
                            }
                            $term_counts[$term] = intval($term_counts[$term] ?? 0) + 1;
                        }
                    }
                }

                $is_dofollow = !empty($row['dofollow']);
                if ($is_dofollow) {
                    $dofollow++;
                }

                $authority = intval($row['domain_rank'] ?? 0);
                if ($authority > 0) {
                    $authority_sum += $authority;
                    $authority_count++;
                    if ($authority >= 50) {
                        $high_authority++;
                    }
                }
            }
        }

        arsort($anchor_counts);
        arsort($term_counts);

        $top_anchors = array();
        foreach (array_slice($anchor_counts, 0, 15, true) as $anchor => $count) {
            $top_anchors[] = array('anchor' => $anchor, 'count' => intval($count));
        }
        $keyword_cloud = array();
        foreach (array_slice($term_counts, 0, 20, true) as $term => $count) {
            $keyword_cloud[] = array('term' => $term, 'count' => intval($count));
        }

        return array(
            'domain' => (string) $domain,
            'total_backlinks' => $total,
            'ref_domains' => count($ref_domains),
            'dofollow_count' => $dofollow,
            'dofollow_ratio' => $total > 0 ? round(($dofollow / $total) * 100, 1) : 0,
            'avg_authority' => $authority_count > 0 ? round($authority_sum / $authority_count, 1) : 0,
            'high_authority_links' => $high_authority,
            'top_anchors' => $top_anchors,
            'keyword_cloud' => $keyword_cloud,
        );
    }

    private function aysa_backlinks_build_benchmark($competitor_profiles, $brand_terms = array()) {
        $count = is_array($competitor_profiles) ? count($competitor_profiles) : 0;
        if ($count === 0) {
            return array(
                'ref_domains' => 0,
                'total_backlinks' => 0,
                'avg_authority' => 0,
                'dofollow_ratio' => 0,
                'high_authority_links' => 0,
                'keyword_cloud' => array(),
                'top_anchors' => array(),
            );
        }

        $sum_ref = 0;
        $sum_total = 0;
        $sum_auth = 0.0;
        $sum_dofollow_ratio = 0.0;
        $sum_high = 0;
        $anchor_counts = array();
        $term_counts = array();

        foreach ($competitor_profiles as $p) {
            if (!is_array($p)) {
                continue;
            }
            $sum_ref += intval($p['ref_domains'] ?? 0);
            $sum_total += intval($p['total_backlinks'] ?? 0);
            $sum_auth += floatval($p['avg_authority'] ?? 0);
            $sum_dofollow_ratio += floatval($p['dofollow_ratio'] ?? 0);
            $sum_high += intval($p['high_authority_links'] ?? 0);

            $anchors = isset($p['top_anchors']) && is_array($p['top_anchors']) ? $p['top_anchors'] : array();
            foreach ($anchors as $a) {
                if (!is_array($a)) {
                    continue;
                }
                $k = trim((string) ($a['anchor'] ?? ''));
                if ($k === '') {
                    continue;
                }
                if ($this->aysa_backlinks_text_has_brand($k, $brand_terms)) {
                    continue;
                }
                $anchor_counts[$k] = intval($anchor_counts[$k] ?? 0) + intval($a['count'] ?? 0);
            }
            $cloud = isset($p['keyword_cloud']) && is_array($p['keyword_cloud']) ? $p['keyword_cloud'] : array();
            foreach ($cloud as $kw) {
                if (!is_array($kw)) {
                    continue;
                }
                $k = trim((string) ($kw['term'] ?? ''));
                if ($k === '') {
                    continue;
                }
                if ($this->aysa_backlinks_text_has_brand($k, $brand_terms)) {
                    continue;
                }
                $term_counts[$k] = intval($term_counts[$k] ?? 0) + intval($kw['count'] ?? 0);
            }
        }

        arsort($anchor_counts);
        arsort($term_counts);
        $top_anchors = array();
        foreach (array_slice($anchor_counts, 0, 15, true) as $anchor => $cnt) {
            $top_anchors[] = array('anchor' => $anchor, 'count' => intval($cnt));
        }
        $keyword_cloud = array();
        foreach (array_slice($term_counts, 0, 20, true) as $term => $cnt) {
            $keyword_cloud[] = array('term' => $term, 'count' => intval($cnt));
        }

        return array(
            'ref_domains' => intval(round($sum_ref / max(1, $count))),
            'total_backlinks' => intval(round($sum_total / max(1, $count))),
            'avg_authority' => round($sum_auth / max(1, $count), 1),
            'dofollow_ratio' => round($sum_dofollow_ratio / max(1, $count), 1),
            'high_authority_links' => intval(round($sum_high / max(1, $count))),
            'top_anchors' => $top_anchors,
            'keyword_cloud' => $keyword_cloud,
        );
    }

    private function aysa_backlinks_build_gap($target_profile, $benchmark, $brand_terms = array()) {
        $target_ref = intval($target_profile['ref_domains'] ?? 0);
        $target_total = intval($target_profile['total_backlinks'] ?? 0);
        $target_high = intval($target_profile['high_authority_links'] ?? 0);
        $target_anchor_set = array();
        if (isset($target_profile['top_anchors']) && is_array($target_profile['top_anchors'])) {
            foreach ($target_profile['top_anchors'] as $a) {
                if (is_array($a) && !empty($a['anchor'])) {
                    $target_anchor_set[(string) $a['anchor']] = true;
                }
            }
        }

        $bench_ref = intval($benchmark['ref_domains'] ?? 0);
        $bench_total = intval($benchmark['total_backlinks'] ?? 0);
        $bench_high = intval($benchmark['high_authority_links'] ?? 0);

        $need_ref = max(0, $bench_ref - $target_ref);
        $need_total = max(0, $bench_total - $target_total);
        $need_high = max(0, $bench_high - $target_high);

        $missing_anchors = array();
        $bench_anchors = isset($benchmark['top_anchors']) && is_array($benchmark['top_anchors']) ? $benchmark['top_anchors'] : array();
        foreach ($bench_anchors as $a) {
            if (!is_array($a) || empty($a['anchor'])) {
                continue;
            }
            $anchor = (string) $a['anchor'];
            if ($this->aysa_backlinks_text_has_brand($anchor, $brand_terms)) {
                continue;
            }
            if (!isset($target_anchor_set[$anchor])) {
                $missing_anchors[] = $a;
            }
            if (count($missing_anchors) >= 12) {
                break;
            }
        }
        $recommended_anchor_cloud = $this->aysa_backlinks_build_recommended_anchor_cloud(
            isset($benchmark['keyword_cloud']) && is_array($benchmark['keyword_cloud']) ? $benchmark['keyword_cloud'] : array(),
            $need_ref,
            $brand_terms
        );

        return array(
            'needed_ref_domains' => $need_ref,
            'needed_total_backlinks' => $need_total,
            'needed_high_authority_links' => $need_high,
            'target_dofollow_ratio' => floatval($benchmark['dofollow_ratio'] ?? 0),
            'plan_90_days' => array(
                'high_authority' => intval(ceil($need_ref * 0.25)),
                'medium_authority' => intval(ceil($need_ref * 0.45)),
                'baseline_authority' => intval(max(0, $need_ref - ceil($need_ref * 0.25) - ceil($need_ref * 0.45))),
            ),
            'missing_anchor_opportunities' => $missing_anchors,
            'recommended_anchor_cloud' => $recommended_anchor_cloud,
            'conclusion' => sprintf(
                'To match competitor benchmark, target +%d referring domains (+%d high-authority links) and +%d backlinks, while moving dofollow ratio toward %.1f%%.',
                $need_ref,
                $need_high,
                $need_total,
                floatval($benchmark['dofollow_ratio'] ?? 0)
            ),
        );
    }

    private function aysa_backlinks_extract_brand_terms($target_domain, $user_id) {
        $terms = array();
        $target_domain = strtolower(trim((string) $target_domain));
        if ($target_domain !== '') {
            $parts = preg_split('/[^a-z0-9]+/i', $target_domain);
            if (is_array($parts)) {
                foreach ($parts as $part) {
                    $part = strtolower(trim((string) $part));
                    if ($part !== '' && strlen($part) >= 3) {
                        $terms[$part] = true;
                    }
                }
            }
        }

        $candidate_users = array(intval($user_id));
        $shared_user_id = intval(get_option('aysa_user_id', 0));
        if ($shared_user_id > 0 && !in_array($shared_user_id, $candidate_users, true)) {
            $candidate_users[] = $shared_user_id;
        }
        foreach ($candidate_users as $uid) {
            $general = get_option('aysa_wizard_general_' . intval($uid), array());
            if (!is_array($general)) {
                continue;
            }
            $raw_values = array(
                (string) ($general['brand'] ?? ''),
                (string) ($general['company_name'] ?? ''),
                (string) ($general['website'] ?? ''),
            );
            foreach ($raw_values as $raw) {
                $raw = strtolower(trim($raw));
                if ($raw === '') {
                    continue;
                }
                $parts = preg_split('/[^\p{L}\p{N}]+/u', $raw);
                if (!is_array($parts)) {
                    continue;
                }
                foreach ($parts as $p) {
                    $p = strtolower(trim((string) $p));
                    if ($p !== '' && mb_strlen($p, 'UTF-8') >= 3) {
                        $terms[$p] = true;
                    }
                }
            }
        }

        return array_keys($terms);
    }

    private function aysa_backlinks_text_has_brand($text, $brand_terms) {
        $normalized = mb_strtolower(trim((string) $text), 'UTF-8');
        if ($normalized === '') {
            return false;
        }
        if (!is_array($brand_terms) || empty($brand_terms)) {
            return false;
        }
        foreach ($brand_terms as $term) {
            $term = mb_strtolower(trim((string) $term), 'UTF-8');
            if ($term === '' || mb_strlen($term, 'UTF-8') < 3) {
                continue;
            }
            if (mb_strpos($normalized, $term, 0, 'UTF-8') !== false) {
                return true;
            }
        }
        return false;
    }

    private function aysa_backlinks_build_recommended_anchor_cloud($keyword_cloud, $needed_links, $brand_terms = array()) {
        $needed = max(0, intval($needed_links));
        if ($needed === 0 || !is_array($keyword_cloud) || empty($keyword_cloud)) {
            return array();
        }

        $pool = array();
        foreach ($keyword_cloud as $item) {
            if (!is_array($item)) {
                continue;
            }
            $term = trim((string) ($item['term'] ?? ''));
            if ($term === '' || $this->aysa_backlinks_text_has_brand($term, $brand_terms)) {
                continue;
            }
            $count = max(1, intval($item['count'] ?? 0));
            $pool[] = array('term' => $term, 'count' => $count);
            if (count($pool) >= 12) {
                break;
            }
        }
        if (empty($pool)) {
            return array();
        }

        $total_weight = 0;
        foreach ($pool as $p) {
            $total_weight += intval($p['count']);
        }
        if ($total_weight <= 0) {
            $total_weight = count($pool);
        }

        $recommended = array();
        $allocated = 0;
        foreach ($pool as $idx => $p) {
            $share = intval(round(($p['count'] / $total_weight) * $needed));
            if ($share <= 0) {
                $share = 1;
            }
            $recommended[$idx] = array(
                'term' => $p['term'],
                'weight' => intval($p['count']),
                'needed_links' => $share,
            );
            $allocated += $share;
        }

        while ($allocated > $needed) {
            foreach ($recommended as $idx => $item) {
                if ($allocated <= $needed) {
                    break;
                }
                if (intval($item['needed_links']) > 1) {
                    $recommended[$idx]['needed_links'] = intval($item['needed_links']) - 1;
                    $allocated--;
                }
            }
            if ($needed === 0) {
                break;
            }
        }
        while ($allocated < $needed) {
            $recommended[0]['needed_links'] = intval($recommended[0]['needed_links']) + 1;
            $allocated++;
        }

        return array_values($recommended);
    }

    public function handle_monitoring_rank_plan() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        $context = $this->aysa_monitoring_resolve_context((string) ($_POST['target'] ?? ''));
        $associations_raw = isset($_POST['associations']) ? wp_unslash($_POST['associations']) : '';
        $associations = json_decode((string) $associations_raw, true);
        if (!is_array($associations)) {
            $associations = array();
        }

        $payload = array(
            'project_id' => $project_id,
            'target' => $context['target'],
            'se_domain' => $context['se_domain'],
            'location_code' => $context['location_code'],
            'language_code' => $context['language_code'],
            'associations' => $associations,
        );
        $result = $this->aysa_broker_request('POST', '/v1/dataforseo/rank-tracking/plan-enriched', $payload, 60);
        if (is_wp_error($result)) {
            wp_send_json_error('Rank plan failed: ' . $result->get_error_message());
            return;
        }
        if (is_array($result) && isset($result['rows']) && is_array($result['rows'])) {
            $persist = $this->aysa_monitoring_persist_rows($project_id, $context['target'], $result['rows']);
            $result['persist_debug'] = $persist;
        }
        wp_send_json_success($result);
    }

    private function aysa_monitoring_extract_overview_rows($result) {
        if (!is_array($result)) {
            return array();
        }
        if (isset($result['rows']) && is_array($result['rows'])) {
            return $result['rows'];
        }
        if (isset($result['results']) && is_array($result['results'])) {
            return $result['results'];
        }
        if (isset($result['data']) && is_array($result['data'])) {
            if (isset($result['data']['rows']) && is_array($result['data']['rows'])) {
                return $result['data']['rows'];
            }
            if (isset($result['data']['results']) && is_array($result['data']['results'])) {
                return $result['data']['results'];
            }
            if (array_keys($result['data']) === range(0, count($result['data']) - 1)) {
                return $result['data'];
            }
        }
        return array();
    }

    private function aysa_adjustments_create_wallet_topup_order($project_id, $credits_cents, $language) {
        $project_id = intval($project_id);
        $credits_cents = intval($credits_cents);

        if ($project_id <= 0 || $credits_cents < 500) {
            return array(
                'module' => 'billing_wallet',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I need a valid top-up package before I can generate the payment link.',
                    'Am nevoie de un pachet valid de top-up înainte să pot genera linkul de plată.',
                    'Нужен ми е валиден top-up пакет, преди да мога да генерирам линк за плащане.'
                ),
            );
        }

        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 array(
                'module' => 'billing_wallet',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'The wallet manager is not available right now.',
                    'Managerul wallet-ului nu este disponibil acum.',
                    'Wallet manager-ът не е наличен в момента.'
                ),
            );
        }

        $wallet_manager = Aysa_Wallet_Manager::get_instance();
        $order = $wallet_manager->create_topup_order($project_id, $credits_cents, get_current_user_id());
        if (is_wp_error($order)) {
            $message = $order->get_error_message();
            if ($message === 'billing_company_missing') {
                $message = $this->aysa_adjustments_tr(
                    $language,
                    'I cannot generate the payment link yet because billing company details are missing in AYSA.',
                    'Nu pot genera încă linkul de plată pentru că lipsesc datele companiei de facturare din AYSA.',
                    'Не мога още да генерирам линк за плащане, защото липсват данните за фактуриране в AYSA.'
                );
            }
            return array(
                'module' => 'billing_wallet',
                'assistant_message' => $message,
            );
        }

        $payment_link = is_array($order) ? trim((string) ($order['payment_link'] ?? '')) : '';
        $amount_eur = number_format($credits_cents / 100, 2, '.', '');

        return array(
            'module' => 'billing_wallet',
            'assistant_message' => $this->aysa_adjustments_tr(
                $language,
                'I generated the Stripe payment link for the ' . $amount_eur . ' EUR top-up. I am opening it now, and after payment the credits will be added to your AYSA wallet automatically.',
                'Am generat linkul Stripe de plată pentru top-up-ul de ' . $amount_eur . ' EUR. Îl deschid acum, iar după plată creditele intră automat în wallet-ul tău AYSA.',
                'Генерирах Stripe линк за плащане за top-up от ' . $amount_eur . ' EUR. Отварям го сега и след плащане кредитите ще влязат автоматично в AYSA wallet-а ти.'
            ),
            'payment_link' => $payment_link,
        );
    }

    private function aysa_adjustments_create_wallet_supplement_order($project_id, $credits_cents, $language, $context = 'order', $module = 'billing_wallet', $meta = array()) {
        $project_id = intval($project_id);
        $credits_cents = intval($credits_cents);
        $meta = is_array($meta) ? $meta : array();

        if ($project_id <= 0 || $credits_cents < 1) {
            return array(
                'module' => $module,
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I need a valid supplemental amount before I can generate the payment link.',
                    'Am nevoie de o sumă suplimentară validă înainte să pot genera linkul de plată.',
                    'Нужна ми е валидна допълнителна сума, преди да мога да генерирам линк за плащане.'
                ),
            );
        }

        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 array(
                'module' => $module,
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'The wallet manager is not available right now.',
                    'Managerul wallet-ului nu este disponibil acum.',
                    'Wallet manager-ът не е наличен в момента.'
                ),
            );
        }

        $service_type = match ($context) {
            'seo_article_order' => 'seo_article',
            'professional_article_order' => 'professional_article',
            'link_insertion_order' => 'link_insertion',
            'seo_text_ads_order' => 'seo_ads_text',
            default => '',
        };
        $portal_domain = sanitize_text_field((string) ($meta['portal_domain'] ?? ''));
        $invoice_label = match ($context) {
            'seo_article_order' => 'SEO article',
            'professional_article_order' => 'Professional article',
            'link_insertion_order' => 'Link insertion',
            'seo_text_ads_order' => 'SEO Text Ads',
            default => 'Off-page supplement',
        };
        if ($portal_domain !== '') {
            $invoice_label .= ' - ' . $portal_domain;
        }

        $wallet_manager = Aysa_Wallet_Manager::get_instance();
        $order = $wallet_manager->create_supplement_order($project_id, $credits_cents, get_current_user_id(), array(
            'service_type' => $service_type,
            'portal_id' => sanitize_text_field((string) ($meta['portal_id'] ?? '')),
            'portal_domain' => $portal_domain,
            'context' => $context,
            'invoice_label' => $invoice_label,
        ));
        if (is_wp_error($order)) {
            $message = $order->get_error_message();
            if ($message === 'billing_company_missing') {
                $message = $this->aysa_adjustments_tr(
                    $language,
                    'I cannot generate the payment link yet because billing company details are missing in AYSA.',
                    'Nu pot genera încă linkul de plată pentru că lipsesc datele companiei de facturare din AYSA.',
                    'Не мога още да генерирам линк за плащане, защото липсват данните за фактуриране в AYSA.'
                );
            }
            return array(
                'module' => $module,
                'assistant_message' => $message,
            );
        }

        $payment_link = is_array($order) ? trim((string) ($order['payment_link'] ?? '')) : '';
        $amount_eur = number_format($credits_cents / 100, 2, '.', '');

        return array(
            'module' => $module,
            'assistant_message' => $this->aysa_adjustments_tr(
                $language,
                'I generated the Stripe payment link for the missing amount of ' . $amount_eur . ' EUR. I am opening it now, and after payment you can continue the off-page order.',
                'Am generat linkul Stripe de plată pentru diferența de ' . $amount_eur . ' EUR. Îl deschid acum, iar după plată poți continua comanda off-page.',
                'Генерирах Stripe линк за плащане за липсващата сума от ' . $amount_eur . ' EUR. Отварям го сега и след плащане можеш да продължиш off-page поръчката.'
            ),
            'payment_link' => $payment_link,
            'order_type' => 'offpage_supplement',
        );
    }

    private function aysa_build_insufficient_credits_payment_response($project_id, $required_credits, $available_credits, $language, $context = 'order', $module = 'billing_wallet', $meta = array()) {
        $project_id = intval($project_id);
        $required_credits = max(0, intval($required_credits));
        $available_credits = max(0, intval($available_credits));
        $missing_credits = max(0, $required_credits - $available_credits);
        $meta = is_array($meta) ? $meta : array();

        if ($project_id <= 0 || $missing_credits <= 0) {
            return array(
                'module' => $module,
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I could not calculate the missing credits for this payment.',
                    'Nu am putut calcula creditele lipsă pentru această plată.',
                    'Не успях да изчисля липсващите кредити за това плащане.'
                ),
            );
        }

        $topup_contexts = array('seo_article_order', 'professional_article_order', 'link_insertion_order', 'seo_text_ads_order');
        if (in_array((string) $context, $topup_contexts, true)) {
            $topup = $this->aysa_adjustments_create_wallet_supplement_order($project_id, $missing_credits, $language, $context, $module, $meta);
        } else {
            $topup = $this->aysa_adjustments_create_wallet_topup_order($project_id, $missing_credits, $language);
        }
        $payment_link = trim((string) ($topup['payment_link'] ?? ''));
        $amount_eur = number_format($missing_credits / 100, 2, '.', '');

        $purpose_text = match ($context) {
            'seo_article_order' => $this->aysa_adjustments_tr($language, 'SEO article order', 'comanda de articol SEO', 'поръчката за SEO статия'),
            'professional_article_order' => $this->aysa_adjustments_tr($language, 'professional article order', 'comanda de articol profesional', 'поръчката за професионална статия'),
            'link_insertion_order' => $this->aysa_adjustments_tr($language, 'link insertion order', 'comanda de inserare link', 'поръчката за link insertion'),
            'seo_text_ads_order' => $this->aysa_adjustments_tr($language, 'SEO Text Ads order', 'comanda SEO Text Ads', 'поръчката за SEO Text Ads'),
            default => $this->aysa_adjustments_tr($language, 'order', 'comandă', 'поръчка'),
        };

        if ($payment_link === '') {
            return array(
                'module' => $module,
                'assistant_message' => trim((string) ($topup['assistant_message'] ?? '')),
                'required_credits' => $required_credits,
                'available_credits' => $available_credits,
                'missing_credits' => $missing_credits,
            );
        }

        return array(
            'module' => $module,
            'assistant_message' => match ($language) {
                'ro' => 'Nu ai suficiente credite pentru ' . $purpose_text . '. Îți lipsesc ' . $missing_credits . ' credite (' . $amount_eur . ' EUR). Am generat linkul Stripe de plată. După plată, poți relua comanda din interfață.',
                'bg' => 'Нямаш достатъчно кредити за ' . $purpose_text . '. Липсват ти ' . $missing_credits . ' кредита (' . $amount_eur . ' EUR). Генерирах Stripe линка за плащане. След плащането можеш да повториш поръчката от интерфейса.',
                default => 'You do not have enough credits for this ' . $purpose_text . '. You are missing ' . $missing_credits . ' credits (' . $amount_eur . ' EUR). I generated the Stripe payment link. After payment, you can retry the order from the interface.',
            },
            'payment_link' => $payment_link,
            'required_credits' => $required_credits,
            'available_credits' => $available_credits,
            'missing_credits' => $missing_credits,
            'order_type' => trim((string) ($topup['order_type'] ?? 'topup')),
        );
    }

    private function aysa_get_wallet_manager_instance() {
        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 null;
        }

        return Aysa_Wallet_Manager::get_instance();
    }

    private function aysa_build_agent_wallet_snapshot($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array();
        }

        $wallet_snapshot = array();
        $wallet_manager = $this->aysa_get_wallet_manager_instance();
        if ($wallet_manager && method_exists($wallet_manager, 'get_wallet_snapshot')) {
            $wallet_snapshot = $wallet_manager->get_wallet_snapshot($project_id, array(
                'allow_remote' => true,
                'force_remote' => true,
            ));
            $resolved_project_id = $this->aysa_resolve_remote_wallet_project_id($project_id);
            if ($resolved_project_id > 0 && $resolved_project_id !== $project_id) {
                $resolved_snapshot = $wallet_manager->get_wallet_snapshot($resolved_project_id, array(
                    'allow_remote' => true,
                    'force_remote' => true,
                ));
                if (is_array($resolved_snapshot) && !empty($resolved_snapshot)) {
                    $wallet_snapshot = $resolved_snapshot;
                }
            }
        }

        $actual_credits = intval($wallet_snapshot['balance_cents'] ?? 0);
        $reserved_credits = intval($wallet_snapshot['reserved_links_cents'] ?? 0);
        $available_credits = intval($wallet_snapshot['spendable_cents'] ?? max(0, $actual_credits - $reserved_credits));

        return array(
            'actual_credits' => intval($actual_credits),
            'reserved_credits' => intval($reserved_credits),
            'available_credits' => intval($available_credits),
            'balance_cents' => intval($actual_credits),
            'spendable_cents' => intval($available_credits),
            'reserved_links_cents' => intval($reserved_credits),
            'project_id' => intval($wallet_snapshot['project_id'] ?? $project_id),
            'sot_source' => sanitize_key((string) ($wallet_snapshot['sot_source'] ?? '')),
            'latest_usage_amount_cents' => intval($wallet_snapshot['latest_usage_amount_cents'] ?? 0),
            'latest_usage_action_key' => sanitize_key((string) ($wallet_snapshot['latest_usage_action_key'] ?? '')),
            'latest_usage_reference' => sanitize_text_field((string) ($wallet_snapshot['latest_usage_reference'] ?? '')),
            'latest_usage_at' => sanitize_text_field((string) ($wallet_snapshot['latest_usage_at'] ?? '')),
        );
    }

    private function aysa_resolve_agent8_canonical_project_id($project_id, $site_host = '', $key_validation = array()) {
        $project_id = intval($project_id);
        $key_validation = is_array($key_validation) ? $key_validation : array();

        $candidate = intval($key_validation['canonical_project_id'] ?? 0);
        if ($candidate > 0) {
            return $candidate;
        }

        $candidate = intval($key_validation['project_id'] ?? 0);
        if ($candidate > 0) {
            return $candidate;
        }

        $candidate = intval($key_validation['website_id'] ?? 0);
        if ($candidate > 0) {
            return $candidate;
        }

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

        if ($site_host !== '' && !$this->aysa_setup_is_local_host($site_host)) {
            if (!class_exists('Aysa_API_Config')) {
                require_once AYSA_PLUGIN_DIR . 'includes/config/aysa-api-config.php';
            }

            $endpoint = '';
            if (class_exists('Aysa_API_Config') && method_exists('Aysa_API_Config', 'get_endpoint_url')) {
                $endpoint = (string) Aysa_API_Config::get_endpoint_url('project_by_domain');
            }

            if ($endpoint !== '') {
                $url = rtrim($endpoint, '/') . '/' . rawurlencode($site_host);
                $response = wp_remote_get($url, array(
                    'timeout' => 10,
                    'redirection' => 2,
                ));
                if (!is_wp_error($response)) {
                    $code = intval(wp_remote_retrieve_response_code($response));
                    if ($code >= 200 && $code < 300) {
                        $decoded = json_decode((string) wp_remote_retrieve_body($response), true);
                        if (is_array($decoded)) {
                            $data = isset($decoded['data']) && is_array($decoded['data']) ? $decoded['data'] : $decoded;
                            $candidate = intval($data['project_id'] ?? $data['projectId'] ?? 0);
                            if ($candidate > 0) {
                                return $candidate;
                            }
                            $candidate = intval($data['website_id'] ?? $data['websiteId'] ?? 0);
                            if ($candidate > 0) {
                                return $candidate;
                            }
                        }
                    }
                }
            }
        }

        return $project_id;
    }

    private function aysa_resolve_remote_wallet_project_id($project_id) {
        $project_id = intval($project_id);
        $host = '';

        if (class_exists('Aysa_Auth_Manager')) {
            $auth_status = Aysa_Auth_Manager::get_instance()->is_authenticated();
            $host = trim((string) ($auth_status['domain'] ?? ''));
        }

        if ($host === '') {
            $host = (string) wp_parse_url(home_url('/'), PHP_URL_HOST);
        }

        $host = strtolower(trim((string) preg_replace('/^www\./i', '', $host)));
        if ($host === '' || $this->aysa_setup_is_local_host($host)) {
            return $project_id;
        }

        if (!class_exists('Aysa_API_Config')) {
            require_once AYSA_PLUGIN_DIR . 'includes/config/aysa-api-config.php';
        }

        $endpoint = '';
        if (class_exists('Aysa_API_Config') && method_exists('Aysa_API_Config', 'get_endpoint_url')) {
            $endpoint = (string) Aysa_API_Config::get_endpoint_url('project_by_domain');
        }
        if ($endpoint === '') {
            return $project_id;
        }

        $url = rtrim($endpoint, '/') . '/' . rawurlencode($host);
        $response = wp_remote_get($url, array(
            'timeout' => 10,
            'redirection' => 2,
        ));
        if (is_wp_error($response)) {
            return $project_id;
        }

        $code = intval(wp_remote_retrieve_response_code($response));
        if ($code < 200 || $code >= 300) {
            return $project_id;
        }

        $decoded = json_decode((string) wp_remote_retrieve_body($response), true);
        if (!is_array($decoded)) {
            return $project_id;
        }

        $data = isset($decoded['data']) && is_array($decoded['data']) ? $decoded['data'] : $decoded;
        $remote_project_id = intval($data['project_id'] ?? $data['projectId'] ?? 0);
        if ($remote_project_id <= 0) {
            $remote_project_id = intval($data['website_id'] ?? $data['websiteId'] ?? 0);
        }

        return $remote_project_id > 0 ? $remote_project_id : $project_id;
    }

    private function aysa_is_included_link_eligible_service($service_type) {
        return in_array((string) $service_type, array('seo_article', 'professional_article', 'link_insertion', 'seo_ads_text'), true);
    }

    private function aysa_monitoring_extract_assoc_for_keyword($associations, $keyword) {
        $keyword = strtolower(trim((string) $keyword));
        if ($keyword === '' || !is_array($associations)) {
            return array();
        }
        foreach ($associations as $assoc) {
            if (!is_array($assoc)) {
                continue;
            }
            $kw = strtolower(trim((string) ($assoc['keyword'] ?? '')));
            if ($kw === $keyword) {
                return $assoc;
            }
        }
        return array();
    }

    private function aysa_monitoring_extract_overview_string($row, $keys) {
        if (!is_array($row)) {
            return '';
        }
        foreach ($keys as $key) {
            if (!array_key_exists($key, $row)) {
                continue;
            }
            $value = $row[$key];
            if (is_string($value) && trim($value) !== '') {
                return trim($value);
            }
        }
        return '';
    }

    private function aysa_monitoring_extract_overview_numeric($row, $keys) {
        if (!is_array($row)) {
            return null;
        }
        foreach ($keys as $key) {
            if (!array_key_exists($key, $row)) {
                continue;
            }
            $value = $row[$key];
            if ($value === null || $value === '') {
                continue;
            }
            if (is_numeric($value)) {
                return floatval($value);
            }
            if (is_string($value)) {
                $normalized = str_replace(',', '.', preg_replace('/[^0-9,\.\-]/', '', $value));
                if ($normalized !== '' && is_numeric($normalized)) {
                    return floatval($normalized);
                }
            }
        }
        return null;
    }

    private function aysa_monitoring_normalize_overview_rows($rows, $associations) {
        $normalized = array();
        if (!is_array($rows)) {
            return $normalized;
        }

        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }

            $keyword = $this->aysa_monitoring_extract_overview_string($row, array('keyword', 'keyword_value', 'query'));
            if ($keyword === '' && isset($row['keyword_data']) && is_array($row['keyword_data'])) {
                $keyword = $this->aysa_monitoring_extract_overview_string($row['keyword_data'], array('keyword', 'keyword_value', 'query'));
            }
            if ($keyword === '') {
                continue;
            }

            $assoc = $this->aysa_monitoring_extract_assoc_for_keyword($associations, $keyword);

            $intent = $this->aysa_monitoring_extract_overview_string($row, array('intent', 'search_intent', 'main_intent'));
            if ($intent === '' && isset($row['search_intent_info']) && is_array($row['search_intent_info'])) {
                $intent = $this->aysa_monitoring_extract_overview_string($row['search_intent_info'], array('main_intent', 'intent'));
            }

            $volume = $this->aysa_monitoring_extract_overview_numeric($row, array('search_volume', 'volume'));
            if (is_null($volume) && isset($row['keyword_info']) && is_array($row['keyword_info'])) {
                $volume = $this->aysa_monitoring_extract_overview_numeric($row['keyword_info'], array('search_volume', 'volume'));
            }

            $competition = $this->aysa_monitoring_extract_overview_numeric($row, array('kd', 'keyword_difficulty', 'competition'));
            if (is_null($competition) && isset($row['keyword_info']) && is_array($row['keyword_info'])) {
                $competition = $this->aysa_monitoring_extract_overview_numeric($row['keyword_info'], array('competition'));
            }

            $traffic = $this->aysa_monitoring_extract_overview_numeric($row, array('traffic', 'clicks', 'gsc_clicks'));
            $traffic_pct = $this->aysa_monitoring_extract_overview_numeric($row, array('traffic_pct', 'traffic_percent'));
            $position = $this->aysa_monitoring_extract_overview_numeric($row, array('position', 'rank', 'rank_group', 'last_position'));
            $position_absolute = $this->aysa_monitoring_extract_overview_numeric($row, array('position_absolute', 'rank_absolute'));
            $url = $this->aysa_monitoring_extract_overview_string($row, array('url', 'target_url', 'found_url', 'link'));
            $ctr = $this->aysa_monitoring_extract_overview_numeric($row, array('ctr', 'gsc_ctr'));
            $impressions = $this->aysa_monitoring_extract_overview_numeric($row, array('impressions', 'gsc_impressions'));
            $cpc = $this->aysa_monitoring_extract_overview_numeric($row, array('cpc', 'cpc_usd', 'cpcUsd'));
            if (is_null($cpc) && isset($row['keyword_info']) && is_array($row['keyword_info'])) {
                $cpc = $this->aysa_monitoring_extract_overview_numeric($row['keyword_info'], array('cpc', 'cpc_usd', 'cpcUsd'));
            }

            $item_types = array();
            if (isset($row['serp_features']) && is_array($row['serp_features'])) {
                $item_types = array_values(array_filter(array_map('strval', $row['serp_features']), function($v) { return trim($v) !== ''; }));
            } elseif (isset($row['item_types']) && is_array($row['item_types'])) {
                $item_types = array_values(array_filter(array_map('strval', $row['item_types']), function($v) { return trim($v) !== ''; }));
            } elseif (isset($row['item_types']) && is_string($row['item_types']) && trim($row['item_types']) !== '') {
                $item_types = array_values(array_filter(array_map('trim', explode(',', (string) $row['item_types'])), function($v) { return $v !== ''; }));
            }

            $normalized[] = array(
                'keyword' => $keyword,
                'associated_on' => (string) ($row['associated_on'] ?? $assoc['associated_on'] ?? 'other'),
                'id_intern' => intval($row['id_intern'] ?? $assoc['id_intern'] ?? 0),
                'link' => $url !== '' ? $url : (string) ($assoc['link'] ?? ''),
                'position' => $position,
                'position_absolute' => $position_absolute,
                'ctr' => $ctr,
                'search_volume' => is_null($volume) ? null : intval(round($volume)),
                'gsc_clicks' => is_null($traffic) ? null : intval(round($traffic)),
                'gsc_impressions' => is_null($impressions) ? null : intval(round($impressions)),
                'item_types' => $item_types,
                'intent' => $intent,
                'kd' => $competition,
                'cpc' => $cpc,
                'traffic_pct' => $traffic_pct,
            );
        }

        $total_traffic = 0.0;
        $has_explicit_traffic_pct = false;
        foreach ($normalized as $row) {
            $clicks = isset($row['gsc_clicks']) ? intval($row['gsc_clicks']) : 0;
            $total_traffic += max(0, $clicks);
            if (isset($row['traffic_pct']) && !is_null($row['traffic_pct'])) {
                $has_explicit_traffic_pct = true;
            }
        }

        if (!$has_explicit_traffic_pct && $total_traffic > 0) {
            foreach ($normalized as $i => $row) {
                $clicks = isset($row['gsc_clicks']) ? intval($row['gsc_clicks']) : 0;
                $normalized[$i]['traffic_pct'] = round(($clicks / $total_traffic) * 100, 4);
            }
        }

        return $normalized;
    }

    private function aysa_keyword_discovery_extract_metrics_rows($result) {
        if (!is_array($result)) {
            return array();
        }
        if (isset($result['rows']) && is_array($result['rows'])) {
            return $result['rows'];
        }
        if (isset($result['results']) && is_array($result['results'])) {
            return $result['results'];
        }
        if (isset($result['data']) && is_array($result['data'])) {
            if (isset($result['data']['rows']) && is_array($result['data']['rows'])) {
                return $result['data']['rows'];
            }
            if (isset($result['data']['results']) && is_array($result['data']['results'])) {
                return $result['data']['results'];
            }
            if (isset($result['data']['items']) && is_array($result['data']['items'])) {
                return $result['data']['items'];
            }
            if (array_keys($result['data']) === range(0, count($result['data']) - 1)) {
                return $result['data'];
            }
        }
        if (isset($result['items']) && is_array($result['items'])) {
            return $result['items'];
        }
        return array();
    }

    private function aysa_keyword_discovery_normalize_metrics_rows($rows) {
        $metrics = array();
        if (!is_array($rows)) {
            return $metrics;
        }

        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = $this->aysa_monitoring_extract_overview_string($row, array('keyword', 'keyword_value', 'query'));
            if ($keyword === '' && isset($row['keyword_data']) && is_array($row['keyword_data'])) {
                $keyword = $this->aysa_monitoring_extract_overview_string($row['keyword_data'], array('keyword', 'keyword_value', 'query'));
            }
            if ($keyword === '') {
                continue;
            }

            $intent = $this->aysa_monitoring_extract_overview_string($row, array('intent', 'search_intent', 'main_intent'));
            if ($intent === '' && isset($row['search_intent_info']) && is_array($row['search_intent_info'])) {
                $intent = $this->aysa_monitoring_extract_overview_string($row['search_intent_info'], array('main_intent', 'intent'));
            }

            $volume = $this->aysa_monitoring_extract_overview_numeric($row, array('search_volume', 'volume'));
            if (is_null($volume) && isset($row['keyword_info']) && is_array($row['keyword_info'])) {
                $volume = $this->aysa_monitoring_extract_overview_numeric($row['keyword_info'], array('search_volume', 'volume'));
            }

            $kd = $this->aysa_monitoring_extract_overview_numeric($row, array('kd', 'keyword_difficulty', 'competition'));
            if (is_null($kd) && isset($row['keyword_info']) && is_array($row['keyword_info'])) {
                $kd = $this->aysa_monitoring_extract_overview_numeric($row['keyword_info'], array('keyword_difficulty', 'competition'));
            }

            $cpc = $this->aysa_monitoring_extract_overview_numeric($row, array('cpc', 'cpc_usd', 'cpcUsd'));
            if (is_null($cpc) && isset($row['keyword_info']) && is_array($row['keyword_info'])) {
                $cpc = $this->aysa_monitoring_extract_overview_numeric($row['keyword_info'], array('cpc', 'cpc_usd', 'cpcUsd'));
            }

            $traffic = $this->aysa_monitoring_extract_overview_numeric($row, array('traffic', 'clicks', 'gsc_clicks'));
            $traffic_pct = $this->aysa_monitoring_extract_overview_numeric($row, array('traffic_pct', 'traffic_percent'));

            $metrics[strtolower(trim($keyword))] = array(
                'intent' => $intent !== '' ? $intent : null,
                'search_volume' => is_null($volume) ? null : intval(round($volume)),
                'kd' => is_null($kd) ? null : round(floatval($kd), 4),
                'cpc' => is_null($cpc) ? null : round(floatval($cpc), 4),
                'traffic' => is_null($traffic) ? null : intval(round($traffic)),
                'traffic_pct' => is_null($traffic_pct) ? null : round(floatval($traffic_pct), 4),
            );
        }

        return $metrics;
    }

    private function aysa_keyword_discovery_normalize_intent($intent) {
        $value = strtolower(trim((string) $intent));
        if ($value === '') {
            return '';
        }
        if ($value === 'informational intent') {
            return 'informational';
        }
        if ($value === 'navigational intent') {
            return 'navigational';
        }
        if ($value === 'commercial intent') {
            return 'commercial';
        }
        if ($value === 'transactional intent') {
            return 'transactional';
        }
        return $value;
    }

    private function aysa_keyword_discovery_normalize_intent_probabilities($value) {
        if (is_string($value) && trim($value) !== '') {
            $decoded = json_decode($value, true);
            if (is_array($decoded)) {
                $value = $decoded;
            }
        }

        if (!is_array($value)) {
            return array();
        }

        $normalized = array();
        $target_keys = array('informational', 'navigational', 'commercial', 'transactional');

        foreach ($target_keys as $key) {
            if (!array_key_exists($key, $value)) {
                continue;
            }
            $raw = $value[$key];
            if ($raw === null || $raw === '' || !is_numeric($raw)) {
                continue;
            }
            $normalized[$key] = round(floatval($raw), 6);
        }

        if (!empty($normalized)) {
            return $normalized;
        }

        foreach ($value as $k => $v) {
            if (is_array($v)) {
                if (isset($v['probability']) && is_numeric($v['probability'])) {
                    $kk = $this->aysa_keyword_discovery_normalize_intent($k);
                    if (in_array($kk, $target_keys, true)) {
                        $normalized[$kk] = round(floatval($v['probability']), 6);
                    }
                }
                continue;
            }
            if (!is_numeric($v)) {
                continue;
            }
            $kk = $this->aysa_keyword_discovery_normalize_intent($k);
            if (in_array($kk, $target_keys, true)) {
                $normalized[$kk] = round(floatval($v), 6);
            }
        }

        if (!empty($normalized)) {
            return $normalized;
        }

        foreach ($value as $row) {
            if (!is_array($row)) {
                continue;
            }
            $name = $this->aysa_keyword_discovery_normalize_intent(
                $row['type'] ?? $row['intent'] ?? $row['main_intent'] ?? ''
            );
            $prob = $row['probability'] ?? $row['score'] ?? null;
            if ($name === '' || !in_array($name, $target_keys, true) || !is_numeric($prob)) {
                continue;
            }
            $normalized[$name] = round(floatval($prob), 6);
        }

        return $normalized;
    }

    private function aysa_keyword_discovery_fetch_keyword_intent_bulk($project_id, $keywords, $context) {
        $project_id = intval($project_id);
        if ($project_id <= 0 || !is_array($keywords) || empty($keywords)) {
            return array();
        }

        $language_code = trim((string) ($context['language_code'] ?? 'ro'));
        if ($language_code === '') {
            $language_code = 'ro';
        }

        $result_map = array();
        $chunks = array_chunk(array_values($keywords), 1000);
        $paths = array(
            '/v1/keywords/intent/bulk',
            '/v1/dataforseo/keywords/intent/bulk',
            '/v1/dataforseo/search-intent/bulk',
        );

        foreach ($chunks as $chunk) {
            if (empty($chunk)) {
                continue;
            }
            $payload = array(
                'project_id' => $project_id,
                'language_code' => $language_code,
                'keywords' => array_values($chunk),
            );

            $rows = array();
            foreach ($paths as $path) {
                $result = $this->aysa_broker_request('POST', $path, $payload, 120);
                if (is_wp_error($result)) {
                    continue;
                }
                $rows = $this->aysa_keyword_discovery_extract_metrics_rows($result);
                if (!empty($rows)) {
                    break;
                }
            }

            if (empty($rows)) {
                continue;
            }

            foreach ($rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $keyword = $this->aysa_monitoring_extract_overview_string($row, array('keyword', 'keyword_value', 'query'));
                if ($keyword === '' && isset($row['keyword_data']) && is_array($row['keyword_data'])) {
                    $keyword = $this->aysa_monitoring_extract_overview_string($row['keyword_data'], array('keyword', 'keyword_value', 'query'));
                }
                if ($keyword === '') {
                    continue;
                }

                $intent = $this->aysa_monitoring_extract_overview_string($row, array('main_intent', 'intent', 'search_intent'));
                $intent_probs = $this->aysa_keyword_discovery_normalize_intent_probabilities(
                    $row['intent_probabilities'] ?? $row['probabilities'] ?? array()
                );

                if (isset($row['search_intent_info']) && is_array($row['search_intent_info'])) {
                    if ($intent === '') {
                        $intent = $this->aysa_monitoring_extract_overview_string($row['search_intent_info'], array('main_intent', 'intent'));
                    }
                    if (empty($intent_probs)) {
                        $intent_probs = $this->aysa_keyword_discovery_normalize_intent_probabilities(
                            $row['search_intent_info']['probabilities'] ?? $row['search_intent_info']
                        );
                    }
                }

                $intent = $this->aysa_keyword_discovery_normalize_intent($intent);
                if ($intent === '' && !empty($intent_probs)) {
                    $best_key = '';
                    $best_val = -INF;
                    foreach ($intent_probs as $k => $v) {
                        if (floatval($v) > $best_val) {
                            $best_key = (string) $k;
                            $best_val = floatval($v);
                        }
                    }
                    if ($best_key !== '') {
                        $intent = $best_key;
                    }
                }

                if ($intent === '' && empty($intent_probs)) {
                    continue;
                }

                $result_map[strtolower(trim($keyword))] = array(
                    'intent' => ($intent !== '' ? $intent : null),
                    'intent_probabilities' => $intent_probs,
                    'intent_source' => 'bulk_intent',
                    'intent_updated_at' => current_time('mysql'),
                );
            }
        }

        return $result_map;
    }

    private function aysa_keyword_discovery_fetch_keyword_metrics($project_id, $keywords, $context) {
        $project_id = intval($project_id);
        if ($project_id <= 0 || !is_array($keywords) || empty($keywords)) {
            return array();
        }

        $payload = array(
            'project_id' => $project_id,
            'target' => (string) ($context['target'] ?? ''),
            'se_domain' => (string) ($context['se_domain'] ?? 'google.com'),
            'location_code' => intval($context['location_code'] ?? 2840),
            'language_code' => (string) ($context['language_code'] ?? 'en'),
            'max_keywords' => max(1, min(1000, count($keywords))),
            'keywords' => array_values($keywords),
        );

        $paths = array(
            '/v1/keywords/overview',
            '/v1/dataforseo/keywords/overview',
        );

        foreach ($paths as $path) {
            $result = $this->aysa_broker_request('POST', $path, $payload, 120);
            if (is_wp_error($result)) {
                continue;
            }
            $rows = $this->aysa_keyword_discovery_extract_metrics_rows($result);
            $normalized = $this->aysa_keyword_discovery_normalize_metrics_rows($rows);
            if (!empty($normalized)) {
                return $normalized;
            }
        }

        // Fallback to monitoring overview endpoint on the same broker when dedicated keyword-overview paths return empty.
        $associations = array();
        foreach ($keywords as $kw) {
            $keyword = trim((string) $kw);
            if ($keyword === '') {
                continue;
            }
            $associations[] = array(
                'keyword' => $keyword,
                'associated_on' => 'other',
                'id_intern' => 0,
                'link' => '',
            );
        }

        if (!empty($associations)) {
            $overview_payload = array(
                'project_id' => $project_id,
                'target' => (string) ($context['target'] ?? ''),
                'se_domain' => (string) ($context['se_domain'] ?? 'google.com'),
                'location_code' => intval($context['location_code'] ?? 2840),
                'language_code' => (string) ($context['language_code'] ?? 'en'),
                'max_keywords' => max(1, min(1000, count($associations))),
                'include_serp_info' => false,
                'associations' => $associations,
            );
            $overview_result = $this->aysa_broker_request('POST', '/v1/monitoring/keywords/overview', $overview_payload, 120);
            if (!is_wp_error($overview_result)) {
                $overview_rows = $this->aysa_monitoring_extract_overview_rows($overview_result);
                $overview_rows = $this->aysa_monitoring_normalize_overview_rows($overview_rows, $associations);
                $normalized = $this->aysa_keyword_discovery_normalize_metrics_rows($overview_rows);
                if (!empty($normalized)) {
                    return $normalized;
                }
            }
        }

        // Last fallback: existing keyword research endpoint (already used in plugin flows).
        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }
        $auth_manager = Aysa_Auth_Manager::get_instance();
        $fallback_metrics = array();
        $chunks = array_chunk(array_values($keywords), 200);
        $location_name = (string) ($context['location_name'] ?? 'Romania');
        if ($location_name === '') {
            $location_name = 'Romania';
        }
        $language_name = (string) ($context['language_name'] ?? 'Romanian');
        if ($language_name === '') {
            $language_name = 'Romanian';
        }

        foreach ($chunks as $chunk) {
            if (empty($chunk)) {
                continue;
            }
            $query = http_build_query(array(
                'keywords' => implode(',', $chunk),
                'locationName' => $location_name,
                'languageName' => $language_name,
                'projectId' => $project_id,
                'limit' => count($chunk),
            ));
            $response = $auth_manager->make_authenticated_request('api/DataForSeo/keywords-research-and-analysis?' . $query, 'GET');
            $rows = $this->normalize_aysa_collection_response($response);
            if (empty($rows) && is_array($response)) {
                if (isset($response['data']) && is_array($response['data'])) {
                    $rows = $this->normalize_aysa_collection_response($response['data']);
                } elseif (isset($response['results']) && is_array($response['results'])) {
                    $rows = array_values($response['results']);
                }
            }

            if (empty($rows)) {
                continue;
            }

            foreach ($rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $keyword = $this->aysa_monitoring_extract_overview_string($row, array('keyword', 'keyword_value', 'query'));
                if ($keyword === '') {
                    continue;
                }
                $intent = $this->aysa_monitoring_extract_overview_string($row, array('intent', 'search_intent', 'main_intent'));
                $volume = $this->aysa_monitoring_extract_overview_numeric($row, array('search_volume', 'searchVolume', 'volume'));
                $kd = $this->aysa_monitoring_extract_overview_numeric($row, array('keyword_difficulty', 'difficulty', 'kd', 'competition'));
                $cpc = $this->aysa_monitoring_extract_overview_numeric($row, array('cpc', 'cpc_usd', 'cpcUsd'));
                $traffic = $this->aysa_monitoring_extract_overview_numeric($row, array('traffic', 'clicks', 'gsc_clicks'));
                $traffic_pct = $this->aysa_monitoring_extract_overview_numeric($row, array('traffic_pct', 'trafficPercent', 'traffic_percent'));

                $fallback_metrics[strtolower(trim($keyword))] = array(
                    'intent' => ($intent !== '' ? $intent : null),
                    'search_volume' => is_null($volume) ? null : intval(round($volume)),
                    'kd' => is_null($kd) ? null : round(floatval($kd), 4),
                    'cpc' => is_null($cpc) ? null : round(floatval($cpc), 4),
                    'traffic' => is_null($traffic) ? null : intval(round($traffic)),
                    'traffic_pct' => is_null($traffic_pct) ? null : round(floatval($traffic_pct), 4),
                );
            }
        }

        if (!empty($fallback_metrics)) {
            return $fallback_metrics;
        }

        return array();
    }

    private function aysa_monitoring_keywords_async_status_key($project_id) {
        return 'aysa_monitoring_keywords_async_' . intval($project_id);
    }

    private function aysa_monitoring_keywords_async_get_status($project_id) {
        $status = get_option($this->aysa_monitoring_keywords_async_status_key($project_id), array());
        return is_array($status) ? $status : array();
    }

    private function aysa_monitoring_keywords_async_save_status($project_id, $status) {
        update_option($this->aysa_monitoring_keywords_async_status_key($project_id), is_array($status) ? $status : array(), false);
    }

    private function aysa_monitoring_keywords_async_output_status($status) {
        if (!is_array($status)) {
            return array(
                'status' => 'idle',
                'result' => null,
                'error' => '',
            );
        }
        $clean = $status;
        unset($clean['_request']);
        return $clean;
    }

    private function aysa_monitoring_keywords_prepare_request($input) {
        $project_id = intval($input['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            return new WP_Error('invalid_project', 'Invalid project id');
        }

        $context = $this->aysa_monitoring_resolve_context((string) ($input['target'] ?? ''));
        $associations_raw = isset($input['associations']) ? wp_unslash($input['associations']) : '';
        $associations = json_decode((string) $associations_raw, true);
        $associations = $this->aysa_monitoring_normalize_associations($associations);
        if (empty($associations)) {
            return new WP_Error('missing_keywords', 'No tracked keywords provided.');
        }

        $payload = array(
            'project_id' => $project_id,
            'target' => $context['target'],
            'se_domain' => $context['se_domain'],
            'location_code' => $context['location_code'],
            'language_code' => $context['language_code'],
            'associations' => $associations,
            'max_keywords' => max(1, min(500, count($associations))),
            'include_serp_info' => true,
        );

        return array(
            'project_id' => $project_id,
            'domain' => (string) ($context['target'] ?? ''),
            'target' => (string) ($context['target'] ?? ''),
            'request_hash' => md5(wp_json_encode($payload)),
            'payload' => $payload,
            'associations' => $associations,
        );
    }

    private function aysa_monitoring_keywords_execute_request($request) {
        if (!is_array($request)) {
            return new WP_Error('invalid_request', 'Invalid keywords overview request.');
        }

        $project_id = intval($request['project_id'] ?? 0);
        $target = (string) ($request['target'] ?? '');
        $payload = isset($request['payload']) && is_array($request['payload']) ? $request['payload'] : array();
        $associations = isset($request['associations']) && is_array($request['associations']) ? $request['associations'] : array();
        if ($project_id <= 0 || $target === '' || empty($payload) || empty($associations)) {
            return new WP_Error('invalid_request', 'Incomplete keywords overview request.');
        }

        $rows = array();
        $result = $this->aysa_broker_request('POST', '/v1/monitoring/keywords/overview', $payload, 120);
        if (!is_wp_error($result)) {
            $rows = $this->aysa_monitoring_extract_overview_rows($result);
            $rows = $this->aysa_monitoring_normalize_overview_rows($rows, $associations);
        }

        if (empty($rows)) {
            $metrics_result = $this->aysa_broker_request('POST', '/v1/keywords/overview', $payload, 120);
            if (!is_wp_error($metrics_result)) {
                $metrics = $this->aysa_keyword_discovery_normalize_metrics_rows(
                    $this->aysa_keyword_discovery_extract_metrics_rows($metrics_result)
                );
                foreach ($associations as $association) {
                    $keyword = trim((string) ($association['keyword'] ?? ''));
                    if ($keyword === '') {
                        continue;
                    }
                    $metric = $metrics[strtolower($keyword)] ?? array();
                    $rows[] = array(
                        'keyword' => $keyword,
                        'intent' => (string) ($metric['intent'] ?? ''),
                        'search_volume' => $metric['search_volume'] ?? null,
                        'kd' => $metric['kd'] ?? null,
                        'traffic' => $metric['traffic'] ?? null,
                        'traffic_pct' => $metric['traffic_pct'] ?? null,
                        'link' => (string) ($association['link'] ?? ''),
                        'item_types' => '',
                        'tracked_at' => current_time('mysql'),
                        '_isRefreshed' => !empty($metric),
                    );
                }
            }
        }

        if (empty($rows)) {
            $error_message = is_wp_error($result) ? $result->get_error_message() : 'empty_keywords_overview';
            return new WP_Error('keywords_overview_failed', 'Keywords overview failed: ' . $error_message);
        }

        $persist_info = array('inserted' => 0, 'failed' => 0, 'last_error' => '');
        if (!empty($rows)) {
            $persist_info = $this->aysa_monitoring_persist_rows($project_id, $target, $rows);
        }

        return array(
            'project_id' => $project_id,
            'target' => $target,
            'rows' => $rows,
            'count' => count($rows),
            'persisted_rows' => intval($persist_info['inserted'] ?? 0),
        );
    }

    public function handle_monitoring_keywords_async_start() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $request = $this->aysa_monitoring_keywords_prepare_request($_POST);
        if (is_wp_error($request)) {
            wp_send_json_error($request->get_error_message());
            return;
        }

        $project_id = intval($request['project_id'] ?? 0);
        $job = $this->aysa_project_jobs_enqueue(
            $project_id,
            'monitoring',
            'keywords_overview_refresh',
            array(
                'request' => $request,
            ),
            array(
                'dedupe_key' => (string) ($request['request_hash'] ?? ''),
                'input_summary' => array(
                    'domain' => (string) ($request['domain'] ?? ''),
                ),
            )
        );
        $response = array(
            'project_id' => $project_id,
            'run_id' => (string) ($job['job_id'] ?? ''),
            'status' => (string) ($job['status'] ?? 'queued'),
            'result' => isset($job['result_payload']) ? $job['result_payload'] : null,
            'error' => (string) ($job['error_summary'] ?? ''),
            'worker_ref' => (string) ($job['worker_ref'] ?? ''),
            'worker_status' => (string) ($job['worker_status'] ?? ''),
            'blocked_by' => isset($job['blocked_by']) && is_array($job['blocked_by']) ? array_values($job['blocked_by']) : array(),
            'project_job_rack' => $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id)),
        );
        wp_send_json_success($response);
    }

    public function handle_monitoring_keywords_async_status() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($this->aysa_resolve_project_id_with_local_fallback());
        if ($project_id <= 0) {
            wp_send_json_error('Invalid monitoring keywords async status request.');
            return;
        }

        $status = $this->aysa_monitoring_keywords_async_get_status($project_id);
        if (empty($status)) {
            $job = $this->aysa_project_jobs_find_latest_job($project_id, 'monitoring', 'keywords_overview_refresh', array('queued', 'running', 'completed', 'failed'));
            if (is_array($job)) {
                wp_send_json_success(array(
                    'project_id' => $project_id,
                    'run_id' => (string) ($job['job_id'] ?? ''),
                    'status' => (string) ($job['status'] ?? 'idle'),
                    'result' => isset($job['result_payload']) ? $job['result_payload'] : null,
                    'error' => (string) ($job['error_summary'] ?? ''),
                    'worker_ref' => (string) ($job['worker_ref'] ?? ''),
                    'worker_status' => (string) ($job['worker_status'] ?? ''),
                    'project_job_rack' => $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id)),
                ));
                return;
            }
            wp_send_json_success(array(
                'project_id' => $project_id,
                'status' => 'idle',
                'result' => null,
                'error' => '',
                'project_job_rack' => $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id)),
            ));
            return;
        }

        $requested_run_id = (string) ($_POST['run_id'] ?? '');
        if ($requested_run_id !== '' && (string) ($status['run_id'] ?? '') !== $requested_run_id) {
            wp_send_json_success(array(
                'project_id' => $project_id,
                'status' => 'idle',
                'result' => null,
                'error' => '',
            ));
            return;
        }

        $state = (string) ($status['status'] ?? 'idle');
        $updated_at = intval($status['updated_at'] ?? 0);
        if (in_array($state, array('queued', 'running'), true) && ($updated_at <= 0 || (time() - $updated_at) > 20) && !empty($status['_request'])) {
            $this->run_monitoring_keywords_async_event(array(
                'project_id' => $project_id,
                'run_id' => (string) ($status['run_id'] ?? ''),
                'request_hash' => (string) ($status['request_hash'] ?? ''),
                'request' => is_array($status['_request']) ? $status['_request'] : array(),
            ));
            $status = $this->aysa_monitoring_keywords_async_get_status($project_id);
        }

        $output = $this->aysa_monitoring_keywords_async_output_status($status);
        $output['project_job_rack'] = $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id));
        wp_send_json_success($output);
    }

    public function run_monitoring_keywords_async_event($payload) {
        if (!is_array($payload)) {
            return;
        }

        $project_id = intval($payload['project_id'] ?? 0);
        $run_id = (string) ($payload['run_id'] ?? '');
        $request_hash = (string) ($payload['request_hash'] ?? '');
        $request = isset($payload['request']) && is_array($payload['request']) ? $payload['request'] : array();
        if ($project_id <= 0 || $run_id === '' || $request_hash === '') {
            return;
        }

        $status = $this->aysa_monitoring_keywords_async_get_status($project_id);
        if (
            empty($status) ||
            (string) ($status['run_id'] ?? '') !== $run_id ||
            (string) ($status['request_hash'] ?? '') !== $request_hash
        ) {
            return;
        }

        $status['status'] = 'running';
        $status['updated_at'] = time();
        $status['error'] = '';
        $this->aysa_monitoring_keywords_async_save_status($project_id, $status);

        $result = $this->aysa_monitoring_keywords_execute_request($request);
        $status['updated_at'] = time();
        $status['finished_at'] = time();
        unset($status['_request']);
        if (is_wp_error($result)) {
            $status['status'] = 'failed';
            $status['result'] = null;
            $status['error'] = $result->get_error_message();
        } else {
            $status['status'] = 'completed';
            $status['result'] = $result;
            $status['error'] = '';
        }
        $this->aysa_monitoring_keywords_async_save_status($project_id, $status);
        $this->aysa_project_jobs_finalize(
            $project_id,
            $this->aysa_project_jobs_make_worker_ref('monitoring_keywords_async', 'keywords_overview_refresh', $run_id),
            (string) $status['status'],
            is_array($status['result'] ?? null) ? $status['result'] : null,
            (string) ($status['error'] ?? '')
        );
    }

    public function handle_monitoring_keywords_overview() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $request = $this->aysa_monitoring_keywords_prepare_request($_POST);
        if (is_wp_error($request)) {
            wp_send_json_error($request->get_error_message());
            return;
        }

        $project_id = intval($request['project_id'] ?? 0);
        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_monitoring_keywords_overview',
            'monitoring_keywords_overview',
            array(
                'module' => 'monitoring_analytics',
                'source' => 'manual_refresh',
            ),
            60,
            '/v1/monitoring/keywords/overview'
        );

        $result = $this->aysa_monitoring_keywords_execute_request($request);
        if (is_wp_error($result)) {
            wp_send_json_error($result->get_error_message());
            return;
        }

        wp_send_json_success($result);
    }

    public function handle_monitoring_get_associations() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }
        // Do not charge credits for passive page-load bootstrap calls.

        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }

        try {
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $raw = $auth_manager->make_authenticated_request('api/Association?projectId=' . $project_id . '&pageSize=10000', 'GET');
            $rows = $this->normalize_aysa_collection_response($raw);
            if (!is_array($rows)) {
                $rows = array();
            }

            $tracked = array();
            $seen = array();
            foreach ($rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $is_tracked = false;
                if (array_key_exists('isTracked', $row)) {
                    $is_tracked = !empty($row['isTracked']);
                } elseif (array_key_exists('IsTracked', $row)) {
                    $is_tracked = !empty($row['IsTracked']);
                }
                if (!$is_tracked) {
                    continue;
                }

                $keyword = '';
                if (isset($row['keyword'])) {
                    $keyword = trim((string) $row['keyword']);
                } elseif (isset($row['Keyword'])) {
                    $keyword = trim((string) $row['Keyword']);
                }
                if ($keyword === '') {
                    continue;
                }

                $assoc_on = (string) ($row['associatedOn'] ?? $row['AssociatedOn'] ?? 'other');
                $id_intern = intval($row['idIntern'] ?? $row['IdIntern'] ?? 0);
                $link = (string) ($row['link'] ?? $row['Link'] ?? '');
                if ($link === '') {
                    $link = $this->aysa_monitoring_resolve_association_link(
                        $assoc_on,
                        $id_intern,
                        (string) ($row['value'] ?? $row['Value'] ?? '')
                    );
                }
                $dedupe = strtolower($keyword . '|' . $assoc_on . '|' . $id_intern);
                if (isset($seen[$dedupe])) {
                    continue;
                }
                $seen[$dedupe] = true;

                $tracked[] = array(
                    'id' => intval($row['id'] ?? $row['Id'] ?? 0),
                    'keyword' => $keyword,
                    'associated_on' => $assoc_on,
                    'id_intern' => $id_intern,
                    'is_main' => !empty($row['isMain']) || !empty($row['IsMain']),
                    'value' => (string) ($row['value'] ?? $row['Value'] ?? ''),
                    'link' => $link,
                );
            }

            // Local-first fallback: include tracked associations from local DB so Monitoring always has testable keywords.
            $local_rows = $this->get_keyword_discovery_local_associations($project_id);
            $max_local_fallback = 120;
            $added_local = 0;
            foreach ($local_rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                if ($added_local >= $max_local_fallback) {
                    break;
                }
                $keyword = trim((string) ($row['keyword'] ?? ''));
                $assoc_on = (string) ($row['associatedOn'] ?? $row['associated_on'] ?? 'other');
                $id_intern = intval($row['idIntern'] ?? $row['id_intern'] ?? 0);
                if ($keyword === '') {
                    continue;
                }
                $dedupe = strtolower($keyword . '|' . $assoc_on . '|' . $id_intern);
                if (isset($seen[$dedupe])) {
                    continue;
                }
                $link = $this->aysa_monitoring_resolve_association_link(
                    $assoc_on,
                    $id_intern,
                    (string) ($row['value'] ?? '')
                );
                if ($link === '') {
                    continue;
                }
                $seen[$dedupe] = true;
                $tracked[] = array(
                    'id' => intval($row['id'] ?? 0),
                    'keyword' => $keyword,
                    'associated_on' => $assoc_on,
                    'id_intern' => $id_intern,
                    'is_main' => false,
                    'value' => (string) ($row['value'] ?? ''),
                    'link' => $link,
                );
                $added_local++;
            }

            wp_send_json_success(array(
                'project_id' => $project_id,
                'count' => count($tracked),
                'associations' => $tracked,
            ));
        } catch (Exception $e) {
            wp_send_json_error('Failed to fetch associations: ' . $e->getMessage());
        }
    }

    private function aysa_monitoring_resolve_association_link($associated_on, $id_intern, $value = '') {
        $value = trim((string) $value);
        if ($value !== '' && preg_match('#^https?://#i', $value)) {
            return $value;
        }

        $target = $this->resolve_keyword_discovery_entity_target((string) $associated_on, intval($id_intern));
        if (!is_array($target)) {
            return '';
        }

        $kind = (string) ($target['kind'] ?? '');
        $entity_id = intval($target['id'] ?? 0);
        if ($kind === 'post' && $entity_id > 0) {
            $permalink = get_permalink($entity_id);
            return is_string($permalink) ? $permalink : '';
        }
        if ($kind === 'term' && $entity_id > 0) {
            $term_link = get_term_link($entity_id);
            return is_string($term_link) ? $term_link : '';
        }
        $home = home_url('/');
        if (is_string($home) && preg_match('#^https?://#i', $home)) {
            return $home;
        }
        return '';
    }

    public function handle_onpage_get_associations() {
        $nonce = isset($_POST['nonce']) ? (string) $_POST['nonce'] : '';
        $nonce_ok = ($nonce !== '') && (
            wp_verify_nonce($nonce, 'aysa_nonce') ||
            wp_verify_nonce($nonce, 'aysa_metabox_nonce')
        );
        if (!$nonce_ok) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $local_rows = $this->get_keyword_discovery_local_associations($project_id);
        if (!empty($local_rows)) {
            if (!$this->is_keyword_discovery_entity_meta_synced($project_id)) {
                $this->sync_keyword_discovery_associations_batch_to_entity_meta($local_rows);
                $this->mark_keyword_discovery_entity_meta_synced($project_id);
            }
            wp_send_json_success(array(
                'project_id' => $project_id,
                'associations' => array_values($local_rows),
                'count' => count($local_rows),
                'source' => 'local_db',
            ));
            return;
        }

        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }

        try {
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $raw = $auth_manager->make_authenticated_request(
                'api/Association?projectId=' . $project_id . '&pageSize=10000&pageNumber=1&sortOrder=asc',
                'GET'
            );
            $rows = $this->normalize_aysa_collection_response($raw);
            if (!is_array($rows)) {
                $rows = array();
            }

            $rows = $this->merge_keyword_discovery_associations(
                $rows,
                $local_rows
            );

            // Persist fetched rows locally so next calls remain local-first.
            if (!empty($rows)) {
                $this->save_keyword_discovery_local_associations($project_id, $rows);
            }

            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_onpage_get_associations',
                'onpage_get_associations:' . $project_id,
                array(
                    'module' => 'on_page_optimization',
                    'source' => 'remote_bootstrap',
                    'association_count' => count($rows),
                ),
                5,
                '/api/Association'
            );

            wp_send_json_success(array(
                'project_id' => $project_id,
                'associations' => array_values($rows),
                'count' => count($rows),
                'source' => 'remote_bootstrap',
            ));
        } catch (Exception $e) {
            wp_send_json_success(array(
                'project_id' => $project_id,
                'associations' => array_values($local_rows),
                'count' => count($local_rows),
                'source' => 'local_fallback',
                'warning' => 'Remote association API unavailable: ' . $e->getMessage(),
            ));
        }
    }

    public function handle_monitoring_get_project_id() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($this->aysa_resolve_project_id_with_local_fallback());
        if ($project_id <= 0) {
            wp_send_json_error('Project ID not configured');
            return;
        }
        wp_send_json_success(array(
            'project_id' => $project_id,
        ));
    }

    public function handle_get_project_by_domain_broker() {
        $nonce = isset($_POST['nonce']) ? (string) $_POST['nonce'] : '';
        $nonce_ok = ($nonce !== '') && (
            wp_verify_nonce($nonce, 'aysa_nonce') ||
            wp_verify_nonce($nonce, 'aysa_metabox_nonce') ||
            wp_verify_nonce($nonce, 'aysa-ajax-nonce') ||
            wp_verify_nonce($nonce, 'aysa_wizard_nonce') ||
            wp_verify_nonce($nonce, 'aysa_internal_linking_nonce')
        );
        if (!$nonce_ok) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $domain = isset($_POST['domain']) ? sanitize_text_field((string) wp_unslash($_POST['domain'])) : '';
        if ($domain === '') {
            $domain = (string) wp_parse_url(home_url('/'), PHP_URL_HOST);
        }
        $domain = strtolower(trim($domain));
        $domain = preg_replace('#^https?://#i', '', $domain);
        $domain = trim((string) $domain, "/ \t\n\r\0\x0B");
        if ($domain === '') {
            wp_send_json_error('Invalid domain');
            return;
        }

        $project_id = intval(get_option('aysa_project_id', 0));
        $source = 'local_option';
        $warning = '';
        $host = (string) wp_parse_url(home_url('/'), PHP_URL_HOST);
        $is_local = ($host === 'localhost' || $host === '127.0.0.1' || substr((string) $host, -10) === '.localhost');

        if ($project_id <= 0 && $is_local) {
            $project_id = defined('AYSA_LOCAL_DEV_PROJECT_ID') ? intval(AYSA_LOCAL_DEV_PROJECT_ID) : 22;
            $source = 'local_dev_fallback';
        }

        $paths = array(
            '/v1/project/by-domain',
            '/v1/projects/by-domain',
            '/v1/project/resolve-domain',
        );
        $payload = array(
            'domain' => $domain,
            'host' => $domain,
            'site_url' => home_url('/'),
        );
        foreach ($paths as $path) {
            $result = $this->aysa_broker_request('POST', $path, $payload, 20);
            if (is_wp_error($result)) {
                $warning = $this->aysa_humanize_broker_error($result, 'Broker unavailable');
                continue;
            }
            $candidate = 0;
            if (is_array($result)) {
                $candidate = intval($result['project_id'] ?? $result['projectId'] ?? $result['id'] ?? 0);
                if ($candidate <= 0 && !empty($result['data']) && is_array($result['data'])) {
                    $candidate = intval($result['data']['project_id'] ?? $result['data']['projectId'] ?? $result['data']['id'] ?? 0);
                }
            }
            if ($candidate > 0) {
                $project_id = $candidate;
                $source = 'broker';
                update_option('aysa_project_id', $project_id, false);
                break;
            }
        }

        if ($project_id <= 0) {
            wp_send_json_error('Project ID could not be resolved for this domain.');
            return;
        }

        wp_send_json_success(array(
            'project_id' => $project_id,
            'domain' => $domain,
            'source' => $source,
            'warning' => $warning,
        ));
    }

    public function handle_monitoring_get_context() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $context = $this->aysa_monitoring_resolve_context((string) ($_POST['target'] ?? ''));
        wp_send_json_success($context);
    }

    public function handle_monitoring_get_ai_overview() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $payload = $this->aysa_monitoring_build_ai_payload();
        $result = $this->aysa_broker_request('POST', '/v1/monitoring/ai/overview', $payload, 120);
        if (is_wp_error($result)) {
            $fallback = $this->aysa_monitoring_ai_get_local_overview($payload['project_id'], $payload['domain']);
            if (!empty($fallback)) {
                wp_send_json_success($fallback);
                return;
            }
            wp_send_json_error('AI overview failed: ' . $result->get_error_message());
            return;
        }

        $snapshot = isset($result['snapshot']) && is_array($result['snapshot']) ? $result['snapshot'] : array();
        if (!empty($snapshot)) {
            $this->aysa_monitoring_ai_persist_snapshot_summary($payload['project_id'], $payload['domain'], $snapshot);
        }
        wp_send_json_success($result);
    }

    public function handle_monitoring_refresh_ai_snapshot() {
        $this->handle_monitoring_start_ai_refresh();
    }

    public function handle_monitoring_start_ai_refresh() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $payload = $this->aysa_monitoring_build_ai_payload();
        $project_id = intval($payload['project_id'] ?? 0);
        $domain = (string) ($payload['domain'] ?? '');
        if ($project_id <= 0 || $domain === '') {
            wp_send_json_error('AI refresh cannot start without project and domain context.');
            return;
        }

        $job = $this->aysa_project_jobs_enqueue(
            $project_id,
            'monitoring',
            'ai_refresh',
            $payload,
            array(
                'dedupe_key' => md5(wp_json_encode($payload)),
                'input_summary' => array(
                    'domain' => $domain,
                ),
            )
        );
        wp_send_json_success(array(
            'project_id' => $project_id,
            'domain' => $domain,
            'run_id' => (string) ($job['job_id'] ?? ''),
            'status' => (string) ($job['status'] ?? 'queued'),
            'error' => (string) ($job['error_summary'] ?? ''),
            'worker_ref' => (string) ($job['worker_ref'] ?? ''),
            'worker_status' => (string) ($job['worker_status'] ?? ''),
            'project_job_rack' => $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id)),
        ));
    }

    public function handle_monitoring_get_ai_refresh_status() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $payload = $this->aysa_monitoring_build_ai_payload();
        $project_id = intval($payload['project_id'] ?? 0);
        $domain = (string) ($payload['domain'] ?? '');
        $status_key = $this->aysa_monitoring_ai_refresh_status_option_key($project_id, $domain);
        $existing = get_option($status_key, array());
        if (!is_array($existing)) {
            $existing = array();
        }
        $payload['advance'] = 0;
        $result = $this->aysa_broker_request('POST', '/v1/monitoring/ai/refresh/status', $payload, 60);
        if (!is_wp_error($result) && is_array($result)) {
            $snapshot = isset($result['snapshot']) && is_array($result['snapshot']) ? $result['snapshot'] : array();
            if (!empty($snapshot)) {
                $this->aysa_monitoring_ai_persist_snapshot_summary($project_id, $domain, $snapshot);
            }
            $status = array(
                'ok' => true,
                'project_id' => $project_id,
                'domain' => $domain,
                'status' => (string) ($result['status'] ?? 'idle'),
                'message' => (string) ($result['message'] ?? 'No AI refresh in progress.'),
                'updated_at' => gmdate('c'),
                'snapshot_id' => intval($result['snapshot_id'] ?? 0),
                'error' => '',
                'processed_questions' => intval($result['processed_questions'] ?? 0),
                'total_questions' => intval($result['total_questions'] ?? 0),
                'processed_sources' => intval($result['processed_sources'] ?? 0),
                'total_sources' => intval($result['total_sources'] ?? 0),
                'partial_errors' => isset($result['partial_errors']) && is_array($result['partial_errors']) ? array_values($result['partial_errors']) : array(),
                'query_pack' => isset($result['query_pack']) && is_array($result['query_pack']) ? array_values($result['query_pack']) : array(),
                'overview_questions' => isset($result['overview_questions']) && is_array($result['overview_questions']) ? array_values($result['overview_questions']) : array(),
                'snapshot' => $snapshot,
                'history' => isset($result['history']) && is_array($result['history']) ? array_values($result['history']) : array(),
            );
            $status['billed_provider_milli_cents'] = intval($existing['billed_provider_milli_cents'] ?? 0);
            $status = $this->aysa_monitoring_ai_finalize_status_payload($status, $existing);
            if (
                ($status['status'] ?? '') === 'running'
                && $this->aysa_monitoring_ai_refresh_needs_rescue($status, $existing)
            ) {
                $rescue_payload = $payload;
                $rescue_payload['advance'] = 1;
                $rescue_result = $this->aysa_broker_request('POST', '/v1/monitoring/ai/refresh/status', $rescue_payload, 60);
                if (!is_wp_error($rescue_result) && is_array($rescue_result)) {
                    $status = $this->aysa_monitoring_ai_build_status_from_broker_result($project_id, $domain, $rescue_result, $status);
                    $status['last_rescue_at'] = gmdate('c');
                } else {
                    $status['message'] = 'AI refresh looks stalled. Rescue step scheduled and worker will retry.';
                    $status['last_rescue_at'] = gmdate('c');
                }
            }
            update_option($status_key, $status, false);
            if (($status['status'] ?? '') === 'running') {
                $this->aysa_monitoring_schedule_ai_refresh_event($payload, 5);
            } elseif (in_array((string) ($status['status'] ?? ''), array('completed', 'failed'), true)) {
                $this->aysa_project_jobs_finalize(
                    $project_id,
                    (string) ($status['orchestrator_worker_ref'] ?? $this->aysa_project_jobs_make_worker_ref('monitoring_ai_async', 'ai_refresh', (string) md5($project_id . '|' . strtolower($domain)))),
                    (string) $status['status'],
                    is_array($status['snapshot'] ?? null) ? $status['snapshot'] : null,
                    (string) ($status['error'] ?? '')
                );
            }
            $status['project_job_rack'] = $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id));
            wp_send_json_success($status);
            return;
        }

        $status = get_option($status_key, array());
        if (!is_array($status) || empty($status)) {
            $status = array(
                'ok' => true,
                'project_id' => $project_id,
                'domain' => $domain,
                'status' => 'idle',
                'message' => 'No AI refresh in progress.',
                'updated_at' => gmdate('c'),
            );
        }

        $status_value = (string) ($status['status'] ?? 'idle');
        $fallback = $this->aysa_monitoring_ai_get_local_overview($project_id, $domain);
        if (!empty($fallback)) {
            if (!in_array($status_value, array('queued', 'running'), true) || intval($status['snapshot_id'] ?? 0) > 0) {
                $status['snapshot'] = $fallback['snapshot'] ?? null;
            }
            $status['history'] = $fallback['history'] ?? array();
        }

        if (($status['status'] ?? '') === 'running') {
            $status['message'] = 'AI refresh is still running in background.';
            $this->aysa_monitoring_schedule_ai_refresh_event($payload, 5);
        } elseif (in_array((string) ($status['status'] ?? ''), array('completed', 'failed'), true)) {
            $this->aysa_project_jobs_finalize(
                $project_id,
                (string) ($status['orchestrator_worker_ref'] ?? $this->aysa_project_jobs_make_worker_ref('monitoring_ai_async', 'ai_refresh', (string) md5($project_id . '|' . strtolower($domain)))),
                (string) $status['status'],
                is_array($status['snapshot'] ?? null) ? $status['snapshot'] : null,
                (string) ($status['error'] ?? '')
            );
        }

        if (($status['status'] ?? 'idle') === 'idle') {
            $job = $this->aysa_project_jobs_find_latest_job($project_id, 'monitoring', 'ai_refresh', array('queued', 'running', 'completed', 'failed'));
            if (is_array($job)) {
                $status = array(
                    'project_id' => $project_id,
                    'domain' => $domain,
                    'status' => (string) ($job['status'] ?? 'idle'),
                    'error' => (string) ($job['error_summary'] ?? ''),
                    'worker_ref' => (string) ($job['worker_ref'] ?? ''),
                );
            }
        }

        $status['project_job_rack'] = $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id));
        wp_send_json_success($status);
    }

    public function handle_monitoring_get_ai_snapshot_history() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $payload = $this->aysa_monitoring_build_ai_payload();
        $payload['limit'] = intval($_POST['limit'] ?? 12);
        $result = $this->aysa_broker_request('POST', '/v1/monitoring/ai/history', $payload, 120);
        if (is_wp_error($result)) {
            wp_send_json_success(array(
                'ok' => true,
                'project_id' => intval($payload['project_id']),
                'domain' => (string) $payload['domain'],
                'history' => $this->aysa_monitoring_ai_get_local_history($payload['project_id'], $payload['domain'], $payload['limit']),
            ));
            return;
        }
        wp_send_json_success($result);
    }

    public function handle_monitoring_get_ai_snapshot_detail() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $payload = $this->aysa_monitoring_build_ai_payload();
        $payload['snapshot_id'] = intval($_POST['snapshot_id'] ?? 0);
        $payload['item_id'] = intval($_POST['item_id'] ?? 0);
        $result = $this->aysa_broker_request('POST', '/v1/monitoring/ai/detail', $payload, 120);
        if (is_wp_error($result)) {
            wp_send_json_error('AI detail failed: ' . $result->get_error_message());
            return;
        }
        wp_send_json_success($result);
    }

    private function aysa_monitoring_ai_async_supported_tasks() {
        return array('overview', 'history', 'detail');
    }

    private function aysa_monitoring_ai_async_status_key($project_id, $task) {
        return 'aysa_monitoring_ai_async_' . sanitize_key((string) $task) . '_' . intval($project_id);
    }

    private function aysa_monitoring_ai_async_get_status($project_id, $task) {
        $raw = get_option($this->aysa_monitoring_ai_async_status_key($project_id, $task), array());
        return is_array($raw) ? $raw : array();
    }

    private function aysa_monitoring_ai_async_save_status($project_id, $task, $status) {
        update_option($this->aysa_monitoring_ai_async_status_key($project_id, $task), is_array($status) ? $status : array(), false);
    }

    private function aysa_monitoring_ai_async_schedule_event($payload, $delay_seconds = 1) {
        if (!is_array($payload)) {
            return;
        }
        $timestamp = time() + max(1, intval($delay_seconds));
        wp_schedule_single_event($timestamp, 'aysa_monitoring_ai_async_event', array($payload));
        if (function_exists('spawn_cron')) {
            spawn_cron();
        }
    }

    private function aysa_monitoring_ai_async_prepare_request($task, $input) {
        $payload = $this->aysa_monitoring_build_ai_payload();
        $project_id = intval($payload['project_id'] ?? 0);
        $domain = (string) ($payload['domain'] ?? '');
        if ($project_id <= 0 || $domain === '') {
            return new WP_Error('invalid_context', 'AI monitoring async request requires project and domain context.');
        }

        $request = array(
            'task' => $task,
            'project_id' => $project_id,
            'domain' => $domain,
            'payload' => $payload,
        );

        if ($task === 'history') {
            $request['payload']['limit'] = intval($input['limit'] ?? 12);
        } elseif ($task === 'detail') {
            $request['payload']['snapshot_id'] = intval($input['snapshot_id'] ?? 0);
            $request['payload']['item_id'] = intval($input['item_id'] ?? 0);
            if (intval($request['payload']['snapshot_id']) <= 0 && intval($request['payload']['item_id']) <= 0) {
                return new WP_Error('invalid_detail_request', 'AI detail requires snapshot_id or item_id.');
            }
        }

        $request['request_hash'] = md5(wp_json_encode(array(
            'task' => $task,
            'payload' => $request['payload'],
        )));

        return $request;
    }

    private function aysa_monitoring_ai_async_execute($request) {
        $task = sanitize_key((string) ($request['task'] ?? ''));
        $payload = isset($request['payload']) && is_array($request['payload']) ? $request['payload'] : array();
        $project_id = intval($request['project_id'] ?? ($payload['project_id'] ?? 0));
        $domain = (string) ($request['domain'] ?? ($payload['domain'] ?? ''));

        switch ($task) {
            case 'overview':
                $result = $this->aysa_broker_request('POST', '/v1/monitoring/ai/overview', $payload, 120);
                if (is_wp_error($result)) {
                    $fallback = $this->aysa_monitoring_ai_get_local_overview($project_id, $domain);
                    if (!empty($fallback)) {
                        return $fallback;
                    }
                    return $result;
                }
                $snapshot = isset($result['snapshot']) && is_array($result['snapshot']) ? $result['snapshot'] : array();
                if (!empty($snapshot)) {
                    $this->aysa_monitoring_ai_persist_snapshot_summary($project_id, $domain, $snapshot);
                }
                return $result;

            case 'history':
                $result = $this->aysa_broker_request('POST', '/v1/monitoring/ai/history', $payload, 120);
                if (is_wp_error($result)) {
                    return array(
                        'ok' => true,
                        'project_id' => $project_id,
                        'domain' => $domain,
                        'history' => $this->aysa_monitoring_ai_get_local_history($project_id, $domain, intval($payload['limit'] ?? 12)),
                    );
                }
                return $result;

            case 'detail':
                $result = $this->aysa_broker_request('POST', '/v1/monitoring/ai/detail', $payload, 120);
                if (is_wp_error($result)) {
                    return $result;
                }
                return $result;
        }

        return new WP_Error('unsupported_task', 'Unsupported monitoring AI async task.');
    }

    private function aysa_monitoring_ai_async_output_status($status) {
        if (!is_array($status)) {
            return array();
        }
        $clean = $status;
        unset($clean['_request']);
        return $clean;
    }

    private function aysa_monitoring_rank_refresh_start_queued($payload) {
        $project_id = intval($payload['project_id'] ?? 0);
        $associations = isset($payload['associations']) && is_array($payload['associations']) ? array_values($payload['associations']) : array();
        if ($project_id <= 0 || empty($associations)) {
            return new WP_Error('invalid_monitoring_rank_request', 'No tracked keywords provided.');
        }

        $existing_status = $this->aysa_monitoring_get_refresh_status($project_id);
        if (($existing_status['status'] ?? '') === 'running') {
            return array(
                'status' => 'running',
                'worker_ref' => $this->aysa_project_jobs_make_worker_ref('monitoring_rank_refresh', 'rank_refresh', (string) ($existing_status['run_id'] ?? '')),
                'worker_status' => (string) ($existing_status['status'] ?? ''),
                'result_summary' => array('already_running' => true),
                'started_at' => intval($existing_status['started_at'] ?? time()),
            );
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_monitoring_rank_refresh_start_background',
            'monitoring_rank_refresh_start_background',
            array(
                'module' => 'monitoring_analytics',
                'source' => 'background_refresh',
            ),
            120,
            '/v1/dataforseo/rank-tracking/refresh-start-background'
        );

        $batch_size = intval($payload['batch_size'] ?? 10);
        if ($batch_size < 5) {
            $batch_size = 5;
        } elseif ($batch_size > 10) {
            $batch_size = 10;
        }

        $context = isset($payload['context']) && is_array($payload['context'])
            ? $payload['context']
            : $this->aysa_monitoring_resolve_context((string) ($payload['target'] ?? ''));
        $run_id = uniqid('mon_', true);
        $now = time();

        $queue = array(
            'run_id' => $run_id,
            'project_id' => $project_id,
            'context' => $context,
            'associations' => $associations,
            'cursor' => 0,
            'batch_size' => $batch_size,
            'current_retry' => 0,
            'created_at' => $now,
        );
        update_option($this->aysa_monitoring_refresh_queue_key($project_id), $queue, false);

        $status = array(
            'run_id' => $run_id,
            'status' => 'running',
            'total' => count($associations),
            'processed' => 0,
            'refreshed' => 0,
            'failed' => 0,
            'deferred' => 0,
            'last_error' => '',
            'started_at' => $now,
            'updated_at' => $now,
            'finished_at' => 0,
        );
        $this->aysa_monitoring_save_refresh_status($project_id, $status);
        $this->aysa_monitoring_ensure_refresh_cron_tick($project_id);
        $this->aysa_monitoring_schedule_background_refresh($project_id, 1);

        return array(
            'status' => 'running',
            'worker_ref' => $this->aysa_project_jobs_make_worker_ref('monitoring_rank_refresh', 'rank_refresh', $run_id),
            'worker_status' => 'running',
            'result_summary' => array('tracked_keywords' => count($associations)),
            'started_at' => $now,
        );
    }

    private function aysa_monitoring_ai_refresh_start_queued($payload) {
        $project_id = intval($payload['project_id'] ?? 0);
        $domain = (string) ($payload['domain'] ?? '');
        if ($project_id <= 0 || $domain === '') {
            return new WP_Error('invalid_monitoring_ai_request', 'AI refresh cannot start without project and domain context.');
        }

        $status_key = $this->aysa_monitoring_ai_refresh_status_option_key($project_id, $domain);
        $result = $this->aysa_broker_request('POST', '/v1/monitoring/ai/refresh/start', $payload, 60);
        if (is_wp_error($result)) {
            return $result;
        }
        if (!is_array($result) || empty($result['ok'])) {
            return new WP_Error('monitoring_ai_refresh_failed', isset($result['error']) ? (string) $result['error'] : 'AI refresh failed.');
        }

        $snapshot = isset($result['snapshot']) && is_array($result['snapshot']) ? $result['snapshot'] : array();
        if (!empty($snapshot)) {
            $this->aysa_monitoring_ai_persist_snapshot_summary($project_id, $domain, $snapshot);
        }

        $worker_ref = $this->aysa_project_jobs_make_worker_ref('monitoring_ai_async', 'ai_refresh', (string) md5($project_id . '|' . strtolower($domain)));
        $status = array(
            'ok' => true,
            'project_id' => $project_id,
            'domain' => $domain,
            'status' => (string) ($result['status'] ?? 'running'),
            'message' => (string) ($result['message'] ?? 'AI refresh started.'),
            'started_at' => gmdate('c'),
            'updated_at' => gmdate('c'),
            'snapshot_id' => intval($result['snapshot_id'] ?? 0),
            'error' => '',
            'processed_questions' => intval($result['processed_questions'] ?? 0),
            'total_questions' => intval($result['total_questions'] ?? 0),
            'processed_sources' => intval($result['processed_sources'] ?? 0),
            'total_sources' => intval($result['total_sources'] ?? 0),
            'partial_errors' => isset($result['partial_errors']) && is_array($result['partial_errors']) ? array_values($result['partial_errors']) : array(),
            'query_pack' => isset($result['query_pack']) && is_array($result['query_pack']) ? array_values($result['query_pack']) : array(),
            'overview_questions' => isset($result['overview_questions']) && is_array($result['overview_questions']) ? array_values($result['overview_questions']) : array(),
            'snapshot' => $snapshot,
            'history' => isset($result['history']) && is_array($result['history']) ? array_values($result['history']) : array(),
            'billed_provider_milli_cents' => 0,
            'orchestrator_worker_ref' => $worker_ref,
        );
        $status = $this->aysa_monitoring_ai_finalize_status_payload($status, array());
        update_option($status_key, $status, false);
        $this->aysa_monitoring_schedule_ai_refresh_event($payload, 1);

        return array(
            'status' => in_array((string) ($status['status'] ?? ''), array('queued', 'running'), true) ? 'running' : (string) ($status['status'] ?? 'running'),
            'worker_ref' => $worker_ref,
            'worker_status' => (string) ($status['status'] ?? 'running'),
            'result_summary' => array('domain' => $domain),
            'started_at' => time(),
        );
    }

    public function handle_monitoring_ai_async_start() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $task = sanitize_key((string) ($_POST['task'] ?? ''));
        if (!in_array($task, $this->aysa_monitoring_ai_async_supported_tasks(), true)) {
            wp_send_json_error('Unsupported monitoring AI task.');
            return;
        }

        $request = $this->aysa_monitoring_ai_async_prepare_request($task, $_POST);
        if (is_wp_error($request)) {
            wp_send_json_error($request->get_error_message());
            return;
        }

        $project_id = intval($request['project_id'] ?? 0);
        $request_hash = (string) ($request['request_hash'] ?? '');
        $existing = $this->aysa_monitoring_ai_async_get_status($project_id, $task);
        if (
            is_array($existing) &&
            !empty($existing['run_id']) &&
            in_array((string) ($existing['status'] ?? ''), array('queued', 'running'), true) &&
            (string) ($existing['request_hash'] ?? '') === $request_hash
        ) {
            wp_send_json_success($this->aysa_monitoring_ai_async_output_status($existing));
            return;
        }

        $run_id = uniqid('mon_ai_', true);
        $status = array(
            'task' => $task,
            'project_id' => $project_id,
            'domain' => (string) ($request['domain'] ?? ''),
            'run_id' => $run_id,
            'request_hash' => $request_hash,
            'status' => 'queued',
            'result' => null,
            'error' => '',
            'started_at' => time(),
            'updated_at' => time(),
            'finished_at' => 0,
            '_request' => $request,
        );
        $this->aysa_monitoring_ai_async_save_status($project_id, $task, $status);
        $this->aysa_monitoring_ai_async_schedule_event(array(
            'task' => $task,
            'project_id' => $project_id,
            'run_id' => $run_id,
            'request_hash' => $request_hash,
            'request' => $request,
        ), 1);

        wp_send_json_success($this->aysa_monitoring_ai_async_output_status($status));
    }

    public function handle_monitoring_ai_async_status() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $task = sanitize_key((string) ($_POST['task'] ?? ''));
        $project_id = intval($this->aysa_resolve_project_id_with_local_fallback());
        if ($project_id <= 0 || !in_array($task, $this->aysa_monitoring_ai_async_supported_tasks(), true)) {
            wp_send_json_error('Invalid monitoring AI async status request.');
            return;
        }

        $status = $this->aysa_monitoring_ai_async_get_status($project_id, $task);
        if (empty($status)) {
            wp_send_json_success(array(
                'task' => $task,
                'project_id' => $project_id,
                'status' => 'idle',
                'result' => null,
                'error' => '',
            ));
            return;
        }

        $requested_run_id = (string) ($_POST['run_id'] ?? '');
        if ($requested_run_id !== '' && (string) ($status['run_id'] ?? '') !== $requested_run_id) {
            wp_send_json_success(array(
                'task' => $task,
                'project_id' => $project_id,
                'status' => 'idle',
                'result' => null,
                'error' => '',
            ));
            return;
        }

        $state = (string) ($status['status'] ?? 'idle');
        $updated_at = intval($status['updated_at'] ?? 0);
        if (in_array($state, array('queued', 'running'), true) && ($updated_at <= 0 || (time() - $updated_at) > 20) && !empty($status['_request'])) {
            $this->run_monitoring_ai_async_event(array(
                'task' => $task,
                'project_id' => $project_id,
                'run_id' => (string) ($status['run_id'] ?? ''),
                'request_hash' => (string) ($status['request_hash'] ?? ''),
                'request' => is_array($status['_request']) ? $status['_request'] : array(),
            ));
            $status = $this->aysa_monitoring_ai_async_get_status($project_id, $task);
        }

        wp_send_json_success($this->aysa_monitoring_ai_async_output_status($status));
    }

    public function run_monitoring_ai_async_event($payload) {
        if (!is_array($payload)) {
            return;
        }

        $task = sanitize_key((string) ($payload['task'] ?? ''));
        $project_id = intval($payload['project_id'] ?? 0);
        $run_id = (string) ($payload['run_id'] ?? '');
        $request_hash = (string) ($payload['request_hash'] ?? '');
        $request = isset($payload['request']) && is_array($payload['request']) ? $payload['request'] : array();
        if ($project_id <= 0 || $run_id === '' || $request_hash === '' || !in_array($task, $this->aysa_monitoring_ai_async_supported_tasks(), true)) {
            return;
        }

        $status = $this->aysa_monitoring_ai_async_get_status($project_id, $task);
        if (
            empty($status) ||
            (string) ($status['run_id'] ?? '') !== $run_id ||
            (string) ($status['request_hash'] ?? '') !== $request_hash
        ) {
            return;
        }

        $status['status'] = 'running';
        $status['updated_at'] = time();
        $status['error'] = '';
        $this->aysa_monitoring_ai_async_save_status($project_id, $task, $status);

        $result = $this->aysa_monitoring_ai_async_execute($request);
        $status['updated_at'] = time();
        $status['finished_at'] = time();
        unset($status['_request']);
        if (is_wp_error($result)) {
            $status['status'] = 'failed';
            $status['result'] = null;
            $status['error'] = $result->get_error_message();
        } else {
            $status['status'] = 'completed';
            $status['result'] = $result;
            $status['error'] = '';
        }
        $this->aysa_monitoring_ai_async_save_status($project_id, $task, $status);
    }

    public function handle_monitoring_save_ai_query_pack() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $payload = $this->aysa_monitoring_build_ai_payload();
        $raw_query_pack = isset($_POST['query_pack']) ? wp_unslash($_POST['query_pack']) : '[]';
        $query_pack = json_decode((string) $raw_query_pack, true);
        if (!is_array($query_pack)) {
            wp_send_json_error('Invalid query pack payload.');
            return;
        }
        $payload['query_pack'] = $query_pack;

        $result = $this->aysa_broker_request('POST', '/v1/monitoring/ai/query-pack', $payload, 120);
        if (is_wp_error($result)) {
            wp_send_json_error('AI query pack save failed: ' . $result->get_error_message());
            return;
        }

        $this->aysa_wallet_track_action_usage(
            intval($payload['project_id'] ?? 0),
            'aysa_monitoring_save_ai_query_pack',
            'monitoring_ai_query_pack_save',
            array(
                'module' => 'monitoring_analytics',
                'source' => 'manual_save',
                'query_count' => count($query_pack),
            ),
            10,
            '/v1/monitoring/ai/query-pack'
        );

        wp_send_json_success($result);
    }

    private function aysa_sitemap_competition_build_payload() {
        $project_id = intval($this->aysa_resolve_project_id_with_local_fallback(intval(get_option('aysa_project_id', 0))));
        $target_host = $this->aysa_monitoring_extract_host((string) home_url('/'));
        if ($target_host === '') {
            $target_host = 'florariebragadiru.ro';
        }

        return array(
            'project_id' => $project_id,
            'target_host' => $target_host,
            'competitors' => $this->aysa_sitemap_competition_resolve_competitors($project_id),
        );
    }

    private function aysa_sitemap_competition_resolve_competitors($project_id) {
        $project_id = intval($project_id);
        $domains = array();

        $seed_user_id = get_current_user_id();
        $seed_data = $this->aysa_keyword_strategy_extract_seed_data($seed_user_id);
        $seed_competitors = isset($seed_data['competitors']) && is_array($seed_data['competitors']) ? $seed_data['competitors'] : array();
        foreach ($seed_competitors as $competitor) {
            $host = $this->aysa_monitoring_extract_host((string) $competitor);
            if ($host !== '') {
                $domains[$host] = true;
            }
        }

        $cached_identification = $this->aysa_competitor_identification_get_cached_payload($project_id);
        $cached_data = isset($cached_identification['data']) && is_array($cached_identification['data']) ? $cached_identification['data'] : array();
        $unique_competitors = isset($cached_data['unique_competitors']) && is_array($cached_data['unique_competitors'])
            ? $cached_data['unique_competitors']
            : array();
        foreach ($unique_competitors as $row) {
            if (is_array($row)) {
                $host = $this->aysa_monitoring_extract_host((string) ($row['domain'] ?? $row['host'] ?? $row['competitor'] ?? ''));
            } else {
                $host = $this->aysa_monitoring_extract_host((string) $row);
            }
            if ($host !== '') {
                $domains[$host] = true;
            }
        }

        $home_host = $this->aysa_monitoring_extract_host((string) home_url('/'));
        if ($home_host !== '') {
            unset($domains[$home_host]);
        }

        if (empty($domains)) {
            global $wpdb;
            $runs_table = $this->aysa_sitemap_competition_runs_table();
            $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $runs_table));
            if ($exists === $runs_table) {
                $previous = $wpdb->get_var($wpdb->prepare(
                    "SELECT competitors_json
                     FROM {$runs_table}
                     WHERE project_id = %d
                       AND competitors_json IS NOT NULL
                       AND competitors_json <> ''
                       AND competitors_json <> '[]'
                     ORDER BY id DESC
                     LIMIT 1",
                    $project_id
                ));
                $decoded = json_decode((string) $previous, true);
                if (is_array($decoded)) {
                    foreach ($decoded as $competitor) {
                        $host = $this->aysa_monitoring_extract_host((string) $competitor);
                        if ($host !== '' && $host !== $home_host) {
                            $domains[$host] = true;
                        }
                    }
                }
            }
        }

        return array_values(array_keys($domains));
    }

    private function aysa_sitemap_competition_normalize_freshness_signal($item) {
        if (!is_array($item)) {
            return '';
        }
        foreach (array('updated_signal', 'publication_signal', 'lastmod') as $key) {
            $value = trim((string) ($item[$key] ?? ''));
            if ($value !== '') {
                return $value;
            }
        }
        return '';
    }

    private function aysa_sitemap_competition_local_inventory() {
        $posts = get_posts(array(
            'post_type' => array('post', 'page'),
            'post_status' => 'publish',
            'posts_per_page' => 300,
            'orderby' => 'modified',
            'order' => 'DESC',
            'suppress_filters' => true,
        ));

        $inventory = array();
        foreach ($posts as $post) {
            if (!$post instanceof WP_Post) {
                continue;
            }
            $inventory[] = array(
                'post_id' => intval($post->ID),
                'title' => get_the_title($post),
                'url' => get_permalink($post),
                'slug' => $post->post_name,
                'modified_gmt' => (string) $post->post_modified_gmt,
                'tokens' => $this->aysa_sitemap_competition_tokens(get_the_title($post) . ' ' . $post->post_name),
            );
        }

        return $inventory;
    }

    private function aysa_sitemap_competition_tokens($value) {
        $value = strtolower(remove_accents((string) $value));
        $value = preg_replace('/[^a-z0-9]+/i', ' ', $value);
        $parts = preg_split('/\s+/', trim((string) $value)) ?: array();
        $parts = array_values(array_filter($parts, function($part) {
            return strlen((string) $part) >= 3;
        }));
        return array_values(array_unique($parts));
    }

    private function aysa_sitemap_competition_similarity($left_tokens, $right_tokens) {
        $left = is_array($left_tokens) ? array_values(array_unique($left_tokens)) : array();
        $right = is_array($right_tokens) ? array_values(array_unique($right_tokens)) : array();
        if (empty($left) || empty($right)) {
            return 0.0;
        }
        $intersection = array_intersect($left, $right);
        $union = array_unique(array_merge($left, $right));
        if (empty($union)) {
            return 0.0;
        }
        return count($intersection) / count($union);
    }

    private function aysa_sitemap_competition_build_todo_rows($items) {
        $local_inventory = $this->aysa_sitemap_competition_local_inventory();
        $todo_rows = array();

        foreach ($items as $item) {
            if (!is_array($item) || (string) ($item['content_type'] ?? '') !== 'blog_post') {
                continue;
            }
            $title = trim((string) ($item['title'] ?? $item['meta_title'] ?? ''));
            $topic = $title !== '' ? $title : trim((string) ($item['slug'] ?? ''));
            $tokens = $this->aysa_sitemap_competition_tokens($topic . ' ' . (string) ($item['path'] ?? ''));
            $best = null;
            $bestScore = 0.0;
            foreach ($local_inventory as $local) {
                $score = $this->aysa_sitemap_competition_similarity($tokens, $local['tokens']);
                if ($score > $bestScore) {
                    $bestScore = $score;
                    $best = $local;
                }
            }

            $local_status = 'missing';
            $suggested_action = 'create article';
            if ($best !== null && $bestScore >= 0.75) {
                $local_status = 'covered';
                $suggested_action = 'review manually';
                $competitorTs = strtotime($this->aysa_sitemap_competition_normalize_freshness_signal($item));
                $localTs = strtotime((string) ($best['modified_gmt'] ?? ''));
                if ($competitorTs && $localTs && $competitorTs > $localTs) {
                    $local_status = 'needs_refresh';
                    $suggested_action = 'update existing article';
                }
            } elseif ($best !== null && $bestScore >= 0.4) {
                $local_status = 'partially_covered';
                $suggested_action = 'update existing article';
            }

            $todo_rows[] = array(
                'competitor_source' => (string) ($item['competitor'] ?? ''),
                'competitor_url' => (string) ($item['url'] ?? ''),
                'competitor_title' => $title,
                'estimated_topic' => $topic,
                'freshness_signal' => $this->aysa_sitemap_competition_normalize_freshness_signal($item),
                'local_status' => $local_status,
                'suggested_action' => $suggested_action,
                'matched_post_id' => intval($best['post_id'] ?? 0),
                'matched_url' => (string) ($best['url'] ?? ''),
                'score' => round($bestScore, 4),
            );
        }

        return $todo_rows;
    }

    private function aysa_sitemap_competition_persist_broker_result($project_id, $target_host, $result, $preferred_local_run_id = 0) {
        global $wpdb;
        $project_id = intval($project_id);
        $target_host = strtolower(trim((string) $target_host));
        if ($project_id <= 0 || $target_host === '' || !is_array($result)) {
            return array();
        }

        $this->aysa_sitemap_competition_maybe_create_tables();
        $runs_table = $this->aysa_sitemap_competition_runs_table();
        $items_table = $this->aysa_sitemap_competition_items_table();
        $todo_table = $this->aysa_sitemap_competition_todo_table();

        $run = isset($result['run']) && is_array($result['run']) ? $result['run'] : array();
        if (empty($run)) {
            return array();
        }
        $remote_run_id = intval($run['run_id'] ?? 0);
        $items = isset($result['items']) && is_array($result['items']) ? array_values($result['items']) : array();
        $todo_rows = $this->aysa_sitemap_competition_build_todo_rows($items);

        $existing_local_id = intval($preferred_local_run_id);
        if ($existing_local_id <= 0 && $remote_run_id > 0) {
            $existing_local_id = intval($wpdb->get_var($wpdb->prepare(
                "SELECT id FROM {$runs_table} WHERE project_id = %d AND target_host = %s AND remote_run_id = %d LIMIT 1",
                $project_id,
                $target_host,
                $remote_run_id
            )));
        }

        $overview = isset($run['overview']) && is_array($run['overview']) ? $run['overview'] : array();
        $summary = isset($run['competitor_summaries']) && is_array($run['competitor_summaries']) ? $run['competitor_summaries'] : array();
        $data = array(
            'project_id' => $project_id,
            'target_host' => $target_host,
            'remote_run_id' => $remote_run_id,
            'status' => sanitize_text_field((string) ($run['status'] ?? '')),
            'competitors_json' => wp_json_encode(isset($run['competitors']) && is_array($run['competitors']) ? $run['competitors'] : array()),
            'summary_json' => wp_json_encode($summary),
            'overview_json' => wp_json_encode($overview),
            'run_json' => wp_json_encode($run),
            'started_at' => !empty($run['started_at']) ? gmdate('Y-m-d H:i:s', strtotime((string) $run['started_at'])) : current_time('mysql'),
            'completed_at' => !empty($run['completed_at']) ? gmdate('Y-m-d H:i:s', strtotime((string) $run['completed_at'])) : null,
            'created_at' => current_time('mysql'),
            'updated_at' => current_time('mysql'),
        );

        if ($existing_local_id > 0) {
            $wpdb->update($runs_table, $data, array('id' => $existing_local_id));
            $local_run_id = $existing_local_id;
        } else {
            $wpdb->insert($runs_table, $data);
            $local_run_id = intval($wpdb->insert_id);
        }

        $wpdb->delete($items_table, array('local_run_id' => $local_run_id));
        foreach ($items as $item) {
            if (!is_array($item)) {
                continue;
            }
            $wpdb->insert($items_table, array(
                'local_run_id' => $local_run_id,
                'project_id' => $project_id,
                'target_host' => $target_host,
                'competitor_host' => (string) ($item['competitor'] ?? ''),
                'final_url' => (string) ($item['url'] ?? ''),
                'page_title' => (string) ($item['title'] ?? ''),
                'meta_title' => (string) ($item['meta_title'] ?? ''),
                'content_type' => (string) ($item['content_type'] ?? 'unknown'),
                'freshness_signal' => $this->aysa_sitemap_competition_normalize_freshness_signal($item),
                'slug' => (string) ($item['slug'] ?? ''),
                'path' => (string) ($item['path'] ?? ''),
                'fetch_status' => (string) ($item['fetch_status'] ?? ''),
                'item_json' => wp_json_encode($item),
                'created_at' => current_time('mysql'),
            ));
        }

        $wpdb->delete($todo_table, array('local_run_id' => $local_run_id));
        foreach ($todo_rows as $todo_row) {
            $wpdb->insert($todo_table, array(
                'local_run_id' => $local_run_id,
                'project_id' => $project_id,
                'target_host' => $target_host,
                'competitor_host' => (string) ($todo_row['competitor_source'] ?? ''),
                'competitor_url' => (string) ($todo_row['competitor_url'] ?? ''),
                'competitor_title' => (string) ($todo_row['competitor_title'] ?? ''),
                'estimated_topic' => (string) ($todo_row['estimated_topic'] ?? ''),
                'freshness_signal' => (string) ($todo_row['freshness_signal'] ?? ''),
                'local_status' => (string) ($todo_row['local_status'] ?? 'missing'),
                'suggested_action' => (string) ($todo_row['suggested_action'] ?? 'review manually'),
                'matched_post_id' => intval($todo_row['matched_post_id'] ?? 0),
                'matched_url' => (string) ($todo_row['matched_url'] ?? ''),
                'todo_json' => wp_json_encode($todo_row),
                'created_at' => current_time('mysql'),
            ));
        }

        $payload = array(
            'project_id' => $project_id,
            'target_host' => $target_host,
            'updated_at' => current_time('mysql'),
            'competitors' => isset($run['competitors']) && is_array($run['competitors']) ? $run['competitors'] : array(),
            'inventory_summary' => isset($overview['inventory_summary']) && is_array($overview['inventory_summary']) ? $overview['inventory_summary'] : array(),
            'content_inventory_summary' => isset($overview['inventory_summary']) && is_array($overview['inventory_summary']) ? $overview['inventory_summary'] : array(),
            'todo_summary' => $this->aysa_sitemap_competition_build_todo_summary($todo_rows),
            'todo_items' => array_slice($todo_rows, 0, 20),
        );
        $this->aysa_sitemap_competition_sync_context_to_broker($payload);

        return $this->aysa_sitemap_competition_get_local_overview($project_id, $target_host);
    }

    private function aysa_sitemap_competition_get_local_run_row($local_run_id) {
        global $wpdb;
        $runs_table = $this->aysa_sitemap_competition_runs_table();
        $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$runs_table} WHERE id = %d LIMIT 1", intval($local_run_id)), ARRAY_A);
        return is_array($row) ? $row : array();
    }

    private function aysa_sitemap_competition_update_local_run($local_run_id, array $patch = array()) {
        global $wpdb;
        $runs_table = $this->aysa_sitemap_competition_runs_table();
        $row = $this->aysa_sitemap_competition_get_local_run_row($local_run_id);
        if (!is_array($row) || empty($row)) {
            return;
        }

        $run_json = json_decode((string) ($row['run_json'] ?? '{}'), true);
        if (!is_array($run_json)) {
            $run_json = array();
        }
        $overview = json_decode((string) ($row['overview_json'] ?? '{}'), true);
        if (!is_array($overview)) {
            $overview = array();
        }

        $status = isset($patch['status']) ? (string) $patch['status'] : (string) ($row['status'] ?? '');
        if ($status !== '') {
            $run_json['status'] = $status;
            $overview['status'] = $status;
        }

        if (array_key_exists('error', $patch)) {
            $error_message = (string) $patch['error'];
            if ($error_message !== '') {
                $run_json['error'] = $error_message;
                $overview['error'] = $error_message;
            } else {
                unset($run_json['error'], $overview['error']);
            }
        }

        foreach (array('phase', 'attempt', 'recovery_count', 'last_recovered_at', 'progress_label', 'last_worker_error') as $key) {
            if (array_key_exists($key, $patch)) {
                $value = $patch[$key];
                if ($value === null || $value === '') {
                    unset($run_json[$key], $overview[$key]);
                } else {
                    $run_json[$key] = $value;
                    $overview[$key] = $value;
                }
            }
        }

        $data = array(
            'status' => $status,
            'run_json' => wp_json_encode($run_json),
            'overview_json' => wp_json_encode($overview),
            'updated_at' => current_time('mysql'),
        );

        if (array_key_exists('remote_run_id', $patch)) {
            $data['remote_run_id'] = intval($patch['remote_run_id']);
        }

        if (array_key_exists('completed_at', $patch)) {
            $data['completed_at'] = $patch['completed_at'];
        } elseif (in_array($status, array('completed', 'partial', 'failed'), true)) {
            $data['completed_at'] = current_time('mysql');
        } else {
            $data['completed_at'] = null;
        }

        $wpdb->update($runs_table, $data, array('id' => intval($local_run_id)));
    }

    private function aysa_sitemap_competition_create_local_run($project_id, $target_host, $payload) {
        global $wpdb;
        $this->aysa_sitemap_competition_maybe_create_tables();
        $runs_table = $this->aysa_sitemap_competition_runs_table();
        $wpdb->insert($runs_table, array(
            'project_id' => intval($project_id),
            'target_host' => (string) $target_host,
            'remote_run_id' => 0,
            'status' => 'queued',
            'competitors_json' => wp_json_encode(isset($payload['competitors']) && is_array($payload['competitors']) ? $payload['competitors'] : array()),
            'summary_json' => wp_json_encode(array()),
            'overview_json' => wp_json_encode(array(
                'status' => 'queued',
                'competitor_count' => count(isset($payload['competitors']) && is_array($payload['competitors']) ? $payload['competitors'] : array()),
                'inventory_summary' => array(),
                'competitor_summaries' => array(),
            )),
            'run_json' => wp_json_encode(array(
                'status' => 'queued',
                'phase' => 'dispatch',
                'attempt' => 0,
                'recovery_count' => 0,
                'progress_label' => 'Queued',
                'competitors' => isset($payload['competitors']) && is_array($payload['competitors']) ? $payload['competitors'] : array(),
            )),
            'started_at' => current_time('mysql'),
            'completed_at' => null,
            'created_at' => current_time('mysql'),
            'updated_at' => current_time('mysql'),
        ));
        return intval($wpdb->insert_id);
    }

    private function aysa_sitemap_competition_update_local_run_status($local_run_id, $status, $error_message = '') {
        $this->aysa_sitemap_competition_update_local_run($local_run_id, array(
            'status' => (string) $status,
            'error' => (string) $error_message,
        ));
    }

    private function aysa_sitemap_competition_schedule_scan_event($payload, $delay_seconds = 1) {
        if (!is_array($payload)) {
            return;
        }
        if (!wp_next_scheduled('aysa_sitemap_competition_scan_event', array($payload))) {
            wp_schedule_single_event(time() + max(1, intval($delay_seconds)), 'aysa_sitemap_competition_scan_event', array($payload));
        }
        if (function_exists('spawn_cron')) {
            spawn_cron();
        }
    }

    private function aysa_sitemap_competition_start_queued_scan($payload) {
        if (!is_array($payload)) {
            return new WP_Error('invalid_sitemap_payload', 'Invalid sitemap competition payload.');
        }
        $project_id = intval($payload['project_id'] ?? 0);
        $target_host = (string) ($payload['target_host'] ?? '');
        if ($project_id <= 0 || $target_host === '') {
            return new WP_Error('invalid_sitemap_payload', 'Invalid sitemap competition payload.');
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_sitemap_competition_start_scan',
            'sitemap_competition_start_scan',
            array(
                'module' => 'research',
                'tab' => 'sitemap_competition',
                'target_host' => $target_host,
                'competitors_count' => isset($payload['competitors']) && is_array($payload['competitors']) ? count($payload['competitors']) : 0,
                'source' => 'project_orchestrator',
            ),
            45,
            '/v1/research/sitemap-competition/start'
        );

        $local_run_id = $this->aysa_sitemap_competition_create_local_run($project_id, $target_host, $payload);
        $event_payload = array(
            'project_id' => $project_id,
            'target_host' => $target_host,
            'competitors' => isset($payload['competitors']) && is_array($payload['competitors']) ? array_values($payload['competitors']) : array(),
            'local_run_id' => $local_run_id,
            'phase' => 'dispatch',
            'attempt' => 1,
        );
        $this->aysa_sitemap_competition_schedule_scan_event($event_payload, 1);

        return array(
            'queued' => true,
            'local_run_id' => $local_run_id,
        );
    }

    private function aysa_sitemap_competition_maybe_recover_local_run($project_id, $target_host, $local) {
        if (!is_array($local) || empty($local['run']) || !is_array($local['run'])) {
            return $local;
        }

        $run = $local['run'];
        $local_run_id = intval($run['local_run_id'] ?? 0);
        $status = (string) ($run['status'] ?? '');
        if ($local_run_id <= 0 || !in_array($status, array('queued', 'running'), true)) {
            return $local;
        }

        $updated_at_raw = (string) ($run['updated_at'] ?? '');
        $updated_at_ts = $updated_at_raw !== '' ? strtotime($updated_at_raw) : 0;
        if ($updated_at_ts > 0 && (time() - $updated_at_ts) <= 30) {
            return $local;
        }

        $run_meta = is_array($run['overview'] ?? null) ? $run['overview'] : array();
        $recovery_count = intval($run_meta['recovery_count'] ?? 0);
        $phase = (string) ($run_meta['phase'] ?? (($status === 'queued') ? 'dispatch' : 'advance'));
        if ($recovery_count >= 3) {
            $this->aysa_sitemap_competition_update_local_run($local_run_id, array(
                'status' => 'failed',
                'error' => 'Sitemap competition worker stalled and was stopped. Please retry.',
                'phase' => $phase,
                'last_worker_error' => 'stale_stop',
            ));
            return $this->aysa_sitemap_competition_get_local_overview($project_id, $target_host);
        }

        $retry_payload = array(
            'project_id' => intval($project_id),
            'target_host' => (string) $target_host,
            'competitors' => isset($run['competitors']) && is_array($run['competitors']) ? array_values($run['competitors']) : array(),
            'local_run_id' => $local_run_id,
            'phase' => $phase,
            'attempt' => 1,
            'remote_run_id' => intval($run['remote_run_id'] ?? 0),
        );

        $this->aysa_sitemap_competition_update_local_run($local_run_id, array(
            'status' => 'queued',
            'error' => '',
            'phase' => $phase,
            'recovery_count' => $recovery_count + 1,
            'last_recovered_at' => current_time('mysql'),
            'progress_label' => $phase === 'dispatch' ? 'Retrying broker handoff' : 'Retrying worker advance',
        ));
        $this->aysa_sitemap_competition_schedule_scan_event($retry_payload, 1);

        return $this->aysa_sitemap_competition_get_local_overview($project_id, $target_host);
    }

    private function aysa_sitemap_competition_build_todo_summary($todo_rows) {
        $summary = array(
            'covered' => 0,
            'partially_covered' => 0,
            'missing' => 0,
            'needs_refresh' => 0,
            'total' => 0,
        );
        foreach ((array) $todo_rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $status = (string) ($row['local_status'] ?? 'missing');
            if (!isset($summary[$status])) {
                continue;
            }
            $summary[$status]++;
            $summary['total']++;
        }
        return $summary;
    }

    private function aysa_sitemap_competition_build_local_inventory_rows($local_run_id) {
        global $wpdb;
        $items_table = $this->aysa_sitemap_competition_items_table();
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT competitor_host, content_type, COUNT(*) AS total
             FROM {$items_table}
             WHERE local_run_id = %d
             GROUP BY competitor_host, content_type
             ORDER BY competitor_host ASC, total DESC",
            intval($local_run_id)
        ), ARRAY_A);
        return is_array($rows) ? array_values($rows) : array();
    }

    private function aysa_sitemap_competition_sync_context_to_broker($payload) {
        if (!is_array($payload)) {
            return;
        }
        $result = $this->aysa_broker_request('POST', '/v1/project-context/sitemap-competition/upsert', $payload, 20);
        if (is_wp_error($result)) {
            error_log('AYSA sitemap competition context broker sync failed: ' . $result->get_error_message());
        }
    }

    private function aysa_sitemap_competition_get_local_overview($project_id, $target_host) {
        global $wpdb;
        $runs_table = $this->aysa_sitemap_competition_runs_table();
        $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $runs_table));
        if ($exists !== $runs_table) {
            return array();
        }

        $row = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$runs_table} WHERE project_id = %d AND target_host = %s ORDER BY id DESC LIMIT 1",
            intval($project_id),
            (string) $target_host
        ), ARRAY_A);
        if (!is_array($row)) {
            return array();
        }

        $local_run_id = intval($row['id'] ?? 0);
        $items_table = $this->aysa_sitemap_competition_items_table();
        $todo_table = $this->aysa_sitemap_competition_todo_table();

        $todo_rows = $wpdb->get_results($wpdb->prepare(
            "SELECT todo_json FROM {$todo_table} WHERE local_run_id = %d ORDER BY id ASC LIMIT 200",
            $local_run_id
        ), ARRAY_A);
        $todos = array();
        foreach ((array) $todo_rows as $todo_row) {
            $decoded = json_decode((string) ($todo_row['todo_json'] ?? '{}'), true);
            if (is_array($decoded)) {
                $todos[] = $decoded;
            }
        }

        $history_rows = $wpdb->get_results($wpdb->prepare(
            "SELECT id, remote_run_id, status, overview_json, started_at, completed_at, updated_at FROM {$runs_table} WHERE project_id = %d AND target_host = %s ORDER BY id DESC LIMIT 10",
            intval($project_id),
            (string) $target_host
        ), ARRAY_A);
        $history = array();
        foreach ((array) $history_rows as $history_row) {
            $overview = json_decode((string) ($history_row['overview_json'] ?? '{}'), true);
            $history[] = array(
                'local_run_id' => intval($history_row['id'] ?? 0),
                'remote_run_id' => intval($history_row['remote_run_id'] ?? 0),
                'status' => (string) ($history_row['status'] ?? ''),
                'overview' => is_array($overview) ? $overview : array(),
                'article_count' => intval($overview['inventory_summary']['blog_post'] ?? $overview['inventory_summary']['article'] ?? 0),
                'started_at' => (string) ($history_row['started_at'] ?? ''),
                'completed_at' => (string) ($history_row['completed_at'] ?? ''),
                'updated_at' => (string) ($history_row['updated_at'] ?? ''),
            );
        }

        $item_count = intval($wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$items_table} WHERE local_run_id = %d",
            $local_run_id
        )));
        $overview = json_decode((string) ($row['overview_json'] ?? '{}'), true);
        if (!is_array($overview)) {
            $overview = array();
        }
        $inventory_summary = isset($overview['inventory_summary']) && is_array($overview['inventory_summary']) ? $overview['inventory_summary'] : array();
        $inventory_total = 0;
        foreach ($inventory_summary as $count) {
            $inventory_total += max(0, intval($count));
        }

        return array(
            'ok' => true,
            'project_id' => intval($project_id),
            'target_host' => (string) $target_host,
            'run' => array(
                'local_run_id' => $local_run_id,
                'remote_run_id' => intval($row['remote_run_id'] ?? 0),
                'status' => (string) ($row['status'] ?? ''),
                'competitors' => json_decode((string) ($row['competitors_json'] ?? '[]'), true),
                'overview' => $overview,
                'summary' => json_decode((string) ($row['summary_json'] ?? '[]'), true),
                'started_at' => (string) ($row['started_at'] ?? ''),
                'completed_at' => (string) ($row['completed_at'] ?? ''),
                'updated_at' => (string) ($row['updated_at'] ?? ''),
                'item_count' => $inventory_total > 0 ? $inventory_total : $item_count,
            ),
            'inventory_rows' => $this->aysa_sitemap_competition_build_local_inventory_rows($local_run_id),
            'todo_rows' => $todos,
            'todo_summary' => $this->aysa_sitemap_competition_build_todo_summary($todos),
            'history' => $history,
        );
    }

    private function aysa_sitemap_competition_get_local_detail($project_id, $target_host, $local_run_id = 0) {
        global $wpdb;
        $runs_table = $this->aysa_sitemap_competition_runs_table();
        $items_table = $this->aysa_sitemap_competition_items_table();
        $todo_table = $this->aysa_sitemap_competition_todo_table();

        if ($local_run_id <= 0) {
            $local_run_id = intval($wpdb->get_var($wpdb->prepare(
                "SELECT id FROM {$runs_table} WHERE project_id = %d AND target_host = %s ORDER BY id DESC LIMIT 1",
                intval($project_id),
                (string) $target_host
            )));
        }
        if ($local_run_id <= 0) {
            return array();
        }

        $item_rows = $wpdb->get_results($wpdb->prepare(
            "SELECT item_json FROM {$items_table} WHERE local_run_id = %d ORDER BY competitor_host ASC, final_url ASC LIMIT 1000",
            $local_run_id
        ), ARRAY_A);
        $items = array();
        foreach ((array) $item_rows as $item_row) {
            $decoded = json_decode((string) ($item_row['item_json'] ?? '{}'), true);
            if (is_array($decoded)) {
                $items[] = $decoded;
            }
        }

        $todo_rows = $wpdb->get_results($wpdb->prepare(
            "SELECT todo_json FROM {$todo_table} WHERE local_run_id = %d ORDER BY id ASC LIMIT 500",
            $local_run_id
        ), ARRAY_A);
        $todos = array();
        foreach ((array) $todo_rows as $todo_row) {
            $decoded = json_decode((string) ($todo_row['todo_json'] ?? '{}'), true);
            if (is_array($decoded)) {
                $todos[] = $decoded;
            }
        }

        return array(
            'ok' => true,
            'local_run_id' => $local_run_id,
            'items' => $items,
            'todo_rows' => $todos,
            'unknown_rows' => array_values(array_filter($items, function($item) {
                return is_array($item) && (string) ($item['content_type'] ?? '') === 'unknown';
            })),
        );
    }

    public function handle_sitemap_competition_get_overview() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $payload = $this->aysa_sitemap_competition_build_payload();
        $result = $this->aysa_broker_request('POST', '/v1/research/sitemap-competition/overview', $payload, 60);
        if (is_wp_error($result)) {
            $fallback = $this->aysa_sitemap_competition_get_local_overview($payload['project_id'], $payload['target_host']);
            if (!empty($fallback)) {
                wp_send_json_success($fallback);
                return;
            }
            wp_send_json_error('Sitemap competition overview failed: ' . $result->get_error_message());
            return;
        }

        $local = $this->aysa_sitemap_competition_get_local_overview($payload['project_id'], $payload['target_host']);
        if (!empty($local)) {
            $result['local'] = $local;
        }
        wp_send_json_success($result);
    }

    public function handle_sitemap_competition_start_scan() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $payload = $this->aysa_sitemap_competition_build_payload();
        $job = $this->aysa_project_jobs_enqueue(
            intval($payload['project_id'] ?? 0),
            'research',
            'sitemap_competition_scan',
            array(
                'project_id' => intval($payload['project_id'] ?? 0),
                'target_host' => (string) ($payload['target_host'] ?? ''),
                'competitors' => isset($payload['competitors']) && is_array($payload['competitors']) ? array_values($payload['competitors']) : array(),
            ),
            array(
                'dedupe_key' => md5(wp_json_encode(array(
                    'project_id' => intval($payload['project_id'] ?? 0),
                    'target_host' => (string) ($payload['target_host'] ?? ''),
                    'competitors' => isset($payload['competitors']) && is_array($payload['competitors']) ? array_values($payload['competitors']) : array(),
                ))),
                'input_summary' => array(
                    'target_host' => (string) ($payload['target_host'] ?? ''),
                    'competitors_count' => isset($payload['competitors']) && is_array($payload['competitors']) ? count($payload['competitors']) : 0,
                ),
            )
        );
        if (is_wp_error($job)) {
            wp_send_json_error($job->get_error_message());
            return;
        }

        $job_row = isset($job['job']) && is_array($job['job']) ? $job['job'] : array();
        $local_run_id = 0;
        if (!empty($job_row['worker_ref'])) {
            $parts = explode(':', (string) $job_row['worker_ref']);
            $local_run_id = intval(end($parts));
        }
        $local = $this->aysa_sitemap_competition_get_local_overview(intval($payload['project_id'] ?? 0), (string) ($payload['target_host'] ?? ''));
        wp_send_json_success(array(
            'ok' => true,
            'queued' => (string) ($job_row['status'] ?? '') === 'queued',
            'local_run_id' => $local_run_id,
            'local' => $local,
            'project_job_rack' => isset($job['rack']) && is_array($job['rack']) ? $job['rack'] : array(),
        ));
    }

    public function handle_sitemap_competition_get_status() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $payload = $this->aysa_sitemap_competition_build_payload();
        $local = $this->aysa_sitemap_competition_get_local_overview($payload['project_id'], $payload['target_host']);
        $local = $this->aysa_sitemap_competition_maybe_recover_local_run($payload['project_id'], $payload['target_host'], $local);

        if (!empty($local)) {
            $local['project_job_rack'] = $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state(intval($payload['project_id'] ?? 0)));
            wp_send_json_success($local);
            return;
        }
        $job = $this->aysa_project_jobs_find_latest_job(intval($payload['project_id'] ?? 0), 'research', 'sitemap_competition_scan', array('queued', 'running', 'completed', 'failed'));
        if (is_array($job)) {
            wp_send_json_success(array(
                'ok' => true,
                'project_id' => intval($payload['project_id']),
                'target_host' => (string) $payload['target_host'],
                'status' => (string) ($job['status'] ?? 'idle'),
                'run' => array(
                    'local_run_id' => 0,
                    'status' => (string) ($job['status'] ?? 'idle'),
                    'remote_run_id' => 0,
                    'overview' => array(),
                ),
                'project_job_rack' => $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state(intval($payload['project_id'] ?? 0))),
            ));
            return;
        }
        wp_send_json_success(array(
            'ok' => true,
            'project_id' => intval($payload['project_id']),
            'target_host' => (string) $payload['target_host'],
            'status' => 'idle',
            'run' => null,
            'project_job_rack' => $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state(intval($payload['project_id'] ?? 0))),
        ));
    }

    public function handle_sitemap_competition_get_detail() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $payload = $this->aysa_sitemap_competition_build_payload();
        $local_run_id = intval($_POST['local_run_id'] ?? 0);
        $remote_run_id = intval($_POST['remote_run_id'] ?? 0);
        $broker_payload = $payload;
        if ($remote_run_id > 0) {
            $broker_payload['run_id'] = $remote_run_id;
        }
        $result = $this->aysa_broker_request('POST', '/v1/research/sitemap-competition/detail', $broker_payload, 120);
        if (!is_wp_error($result) && is_array($result) && isset($result['run'])) {
            $local = $this->aysa_sitemap_competition_get_local_detail($payload['project_id'], $payload['target_host'], $local_run_id);
            if (!empty($local)) {
                $result['local'] = $local;
            }
            wp_send_json_success($result);
            return;
        }

        $local = $this->aysa_sitemap_competition_get_local_detail($payload['project_id'], $payload['target_host'], $local_run_id);
        if (!empty($local)) {
            wp_send_json_success($local);
            return;
        }

        wp_send_json_error('Sitemap competition detail failed.');
    }

    public function handle_sitemap_competition_get_history() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $payload = $this->aysa_sitemap_competition_build_payload();
        $result = $this->aysa_broker_request('POST', '/v1/research/sitemap-competition/history', $payload, 60);
        if (is_wp_error($result)) {
            $local = $this->aysa_sitemap_competition_get_local_overview($payload['project_id'], $payload['target_host']);
            if (!empty($local)) {
                wp_send_json_success(array('history' => $local['history']));
                return;
            }
            wp_send_json_error('Sitemap competition history failed: ' . $result->get_error_message());
            return;
        }

        $local = $this->aysa_sitemap_competition_get_local_overview($payload['project_id'], $payload['target_host']);
        if (!empty($local)) {
            $result['local_history'] = $local['history'];
        }
        wp_send_json_success($result);
    }

    public function run_sitemap_competition_scan_event($payload) {
        if (!is_array($payload)) {
            return;
        }

        $project_id = intval($payload['project_id'] ?? 0);
        $target_host = strtolower(trim((string) ($payload['target_host'] ?? '')));
        $local_run_id = intval($payload['local_run_id'] ?? 0);
        $attempt = intval($payload['attempt'] ?? 1);
        $phase = (string) ($payload['phase'] ?? 'dispatch');
        if ($project_id <= 0 || $target_host === '' || $local_run_id <= 0) {
            return;
        }

        $broker_payload = array(
            'project_id' => $project_id,
            'target_host' => $target_host,
            'competitors' => isset($payload['competitors']) && is_array($payload['competitors']) ? array_values($payload['competitors']) : array(),
        );
        if ($phase === 'dispatch') {
            $this->aysa_sitemap_competition_update_local_run($local_run_id, array(
                'status' => 'queued',
                'error' => '',
                'phase' => 'dispatch',
                'attempt' => $attempt,
                'progress_label' => 'Starting broker run',
            ));
            $start = $this->aysa_broker_request('POST', '/v1/research/sitemap-competition/start', $broker_payload, 45);
            if (is_wp_error($start) || !is_array($start) || empty($start['run'])) {
                if ($attempt < 3) {
                    $this->aysa_sitemap_competition_update_local_run($local_run_id, array(
                        'status' => 'queued',
                        'error' => '',
                        'phase' => 'dispatch',
                        'attempt' => $attempt,
                        'last_worker_error' => is_wp_error($start) ? $start->get_error_message() : 'Broker did not create a sitemap competition run.',
                        'progress_label' => 'Retrying broker handoff',
                    ));
                    $retry_payload = $payload;
                    $retry_payload['attempt'] = $attempt + 1;
                    $this->aysa_sitemap_competition_schedule_scan_event($retry_payload, 10);
                    return;
                }
                $this->aysa_sitemap_competition_update_local_run($local_run_id, array(
                    'status' => 'failed',
                    'error' => is_wp_error($start) ? $start->get_error_message() : 'Broker did not create a sitemap competition run.',
                    'phase' => 'dispatch',
                    'attempt' => $attempt,
                ));
                $this->aysa_project_jobs_finalize(
                    $project_id,
                    $this->aysa_project_jobs_make_worker_ref('sitemap_competition', 'sitemap_competition_scan', (string) $local_run_id),
                    'failed',
                    null,
                    is_wp_error($start) ? $start->get_error_message() : 'Broker did not create a sitemap competition run.'
                );
                return;
            }

            $local = $this->aysa_sitemap_competition_persist_broker_result($project_id, $target_host, $start, $local_run_id);
            $remote_run_id = 0;
            if (is_array($local) && isset($local['run']) && is_array($local['run'])) {
                $remote_run_id = intval($local['run']['remote_run_id'] ?? 0);
            }
            if ($remote_run_id <= 0 && is_array($start['run'] ?? null)) {
                $remote_run_id = intval($start['run']['run_id'] ?? 0);
            }
            if ($remote_run_id <= 0) {
                if ($attempt < 3) {
                    $this->aysa_sitemap_competition_update_local_run($local_run_id, array(
                        'status' => 'queued',
                        'error' => '',
                        'phase' => 'dispatch',
                        'attempt' => $attempt,
                        'last_worker_error' => 'Broker did not return remote_run_id.',
                        'progress_label' => 'Retrying broker handoff',
                    ));
                    $retry_payload = $payload;
                    $retry_payload['attempt'] = $attempt + 1;
                    $this->aysa_sitemap_competition_schedule_scan_event($retry_payload, 10);
                    return;
                }
                $this->aysa_sitemap_competition_update_local_run($local_run_id, array(
                    'status' => 'failed',
                    'error' => 'Broker did not return remote_run_id.',
                    'phase' => 'dispatch',
                    'attempt' => $attempt,
                ));
                $this->aysa_project_jobs_finalize(
                    $project_id,
                    $this->aysa_project_jobs_make_worker_ref('sitemap_competition', 'sitemap_competition_scan', (string) $local_run_id),
                    'failed',
                    null,
                    'Broker did not return remote_run_id.'
                );
                return;
            }

            $retry_payload = $payload;
            $retry_payload['phase'] = 'advance';
            $retry_payload['remote_run_id'] = $remote_run_id;
            $retry_payload['attempt'] = 1;
            $this->aysa_sitemap_competition_update_local_run($local_run_id, array(
                'status' => 'running',
                'error' => '',
                'phase' => 'advance',
                'attempt' => 1,
                'remote_run_id' => $remote_run_id,
                'progress_label' => 'Broker run started',
            ));
            $this->aysa_sitemap_competition_schedule_scan_event($retry_payload, 5);
            return;
        }

        $remote_run_id = intval($payload['remote_run_id'] ?? 0);
        if ($remote_run_id <= 0) {
            $local = $this->aysa_sitemap_competition_get_local_overview($project_id, $target_host);
            if (is_array($local) && isset($local['run']) && is_array($local['run'])) {
                $remote_run_id = intval($local['run']['remote_run_id'] ?? 0);
            }
        }
        if ($remote_run_id <= 0) {
            $this->aysa_sitemap_competition_update_local_run($local_run_id, array(
                'status' => 'failed',
                'error' => 'Missing remote_run_id for sitemap competition worker.',
                'phase' => 'advance',
                'attempt' => $attempt,
            ));
            $this->aysa_project_jobs_finalize(
                $project_id,
                $this->aysa_project_jobs_make_worker_ref('sitemap_competition', 'sitemap_competition_scan', (string) $local_run_id),
                'failed',
                null,
                'Missing remote_run_id for sitemap competition worker.'
            );
            return;
        }

        $this->aysa_sitemap_competition_update_local_run($local_run_id, array(
            'status' => 'running',
            'error' => '',
            'phase' => 'advance',
            'attempt' => $attempt,
            'remote_run_id' => $remote_run_id,
            'progress_label' => 'Advancing broker run',
        ));
        $advance = $this->aysa_broker_request('POST', '/v1/research/sitemap-competition/advance', array_merge($broker_payload, array(
            'run_id' => $remote_run_id,
        )), 180);
        if (is_wp_error($advance) || !is_array($advance) || empty($advance['run'])) {
            if ($attempt < 6) {
                $this->aysa_sitemap_competition_update_local_run($local_run_id, array(
                    'status' => 'running',
                    'error' => '',
                    'phase' => 'advance',
                    'attempt' => $attempt,
                    'remote_run_id' => $remote_run_id,
                    'last_worker_error' => is_wp_error($advance) ? $advance->get_error_message() : 'Broker advance request failed.',
                    'progress_label' => 'Retrying broker advance',
                ));
                $retry_payload = $payload;
                $retry_payload['phase'] = 'advance';
                $retry_payload['remote_run_id'] = $remote_run_id;
                $retry_payload['attempt'] = $attempt + 1;
                $this->aysa_sitemap_competition_schedule_scan_event($retry_payload, 20);
                return;
            }
            $this->aysa_sitemap_competition_update_local_run($local_run_id, array(
                'status' => 'failed',
                'error' => is_wp_error($advance) ? $advance->get_error_message() : 'Broker advance request failed.',
                'phase' => 'advance',
                'attempt' => $attempt,
                'remote_run_id' => $remote_run_id,
            ));
            $this->aysa_project_jobs_finalize(
                $project_id,
                $this->aysa_project_jobs_make_worker_ref('sitemap_competition', 'sitemap_competition_scan', (string) $local_run_id),
                'failed',
                null,
                is_wp_error($advance) ? $advance->get_error_message() : 'Broker advance request failed.'
            );
            return;
        }

        $local = $this->aysa_sitemap_competition_persist_broker_result($project_id, $target_host, $advance, $local_run_id);
        $run_status = '';
        if (is_array($local) && isset($local['run']) && is_array($local['run'])) {
            $run_status = (string) ($local['run']['status'] ?? '');
        }
        if ($run_status === 'running' && $attempt < 12) {
            $this->aysa_sitemap_competition_update_local_run($local_run_id, array(
                'status' => 'running',
                'error' => '',
                'phase' => 'advance',
                'attempt' => $attempt,
                'remote_run_id' => $remote_run_id,
                'progress_label' => 'Broker run still processing',
            ));
            $retry_payload = $payload;
            $retry_payload['phase'] = 'advance';
            $retry_payload['remote_run_id'] = $remote_run_id;
            $retry_payload['attempt'] = $attempt + 1;
            $this->aysa_sitemap_competition_schedule_scan_event($retry_payload, 20);
            return;
        }
        if ($run_status === '') {
            $run_status = 'completed';
        }
        $this->aysa_sitemap_competition_update_local_run($local_run_id, array(
            'status' => $run_status,
            'error' => '',
            'phase' => 'completed',
            'attempt' => $attempt,
            'remote_run_id' => $remote_run_id,
            'progress_label' => ucfirst($run_status),
        ));
        if (in_array($run_status, array('completed', 'failed'), true)) {
            $this->aysa_project_jobs_finalize(
                $project_id,
                $this->aysa_project_jobs_make_worker_ref('sitemap_competition', 'sitemap_competition_scan', (string) $local_run_id),
                $run_status === 'failed' ? 'failed' : 'completed',
                array(
                    'local_run_id' => $local_run_id,
                    'remote_run_id' => $remote_run_id,
                    'status' => $run_status,
                ),
                $run_status === 'failed' ? (string) ($local['run']['error'] ?? '') : ''
            );
        }
    }

    public function run_monitoring_ai_refresh_event($payload) {
        if (!is_array($payload)) {
            return;
        }

        $project_id = intval($payload['project_id'] ?? 0);
        $domain = strtolower(trim((string) ($payload['domain'] ?? '')));
        if ($project_id <= 0 || $domain === '') {
            return;
        }

        $payload['advance'] = 1;
        $status_key = $this->aysa_monitoring_ai_refresh_status_option_key($project_id, $domain);
        $result = $this->aysa_broker_request('POST', '/v1/monitoring/ai/refresh/status', $payload, 60);

        if (is_wp_error($result) || !is_array($result)) {
            $status = get_option($status_key, array());
            if (!is_array($status)) {
                $status = array();
            }
            $status['ok'] = false;
            $status['project_id'] = $project_id;
            $status['domain'] = $domain;
            $status['status'] = (($status['status'] ?? '') === 'completed') ? 'completed' : 'running';
            $status['error'] = is_wp_error($result) ? $result->get_error_message() : 'Unknown AI refresh worker error.';
            $status['message'] = 'AI refresh worker will retry after a transient error.';
            $status['updated_at'] = gmdate('c');
            update_option($status_key, $status, false);
            $this->aysa_monitoring_schedule_ai_refresh_event($payload, 20);
            return;
        }

        $existing = get_option($status_key, array());
        if (!is_array($existing)) {
            $existing = array();
        }
        $status = $this->aysa_monitoring_ai_build_status_from_broker_result($project_id, $domain, $result, $existing);
        update_option($status_key, $status, false);

        if (($status['status'] ?? '') === 'running') {
            $this->aysa_monitoring_schedule_ai_refresh_event($payload, 15);
        }
    }

    private function aysa_monitoring_ai_build_status_from_broker_result($project_id, $domain, $result, $existing = array()) {
        $snapshot = isset($result['snapshot']) && is_array($result['snapshot']) ? $result['snapshot'] : array();
        if (!empty($snapshot)) {
            $this->aysa_monitoring_ai_persist_snapshot_summary($project_id, $domain, $snapshot);
        }

        if (!is_array($existing)) {
            $existing = array();
        }
        $billed_provider_milli_cents = intval($existing['billed_provider_milli_cents'] ?? 0);
        $snapshot_cost_usd = 0.0;
        if (isset($snapshot['summary']) && is_array($snapshot['summary'])) {
            $snapshot_cost_usd = floatval($snapshot['summary']['provider_cost_usd'] ?? 0);
        }
        $provider_milli = $this->aysa_cost_usd_to_milli_cents($snapshot_cost_usd);
        $provider_delta_milli = max(0, $provider_milli - $billed_provider_milli_cents);
        $billed_cents = intval(ceil(($provider_delta_milli * 5) / 1000));
        if ($billed_cents > 0) {
            $this->aysa_wallet_track_server_usage(
                intval($project_id),
                '/v1/monitoring/ai/refresh/status',
                $billed_cents,
                'monitoring_ai_refresh_background',
                array(
                    'module' => 'monitoring_ai',
                    'source' => 'background_refresh',
                    'snapshot_id' => intval($result['snapshot_id'] ?? 0),
                    'provider_cost_milli_cents' => $provider_delta_milli,
                    'pricing_multiplier' => 5,
                )
            );
            $billed_provider_milli_cents = $provider_milli;
        }

        $status = array(
            'ok' => true,
            'project_id' => intval($project_id),
            'domain' => (string) $domain,
            'status' => (string) ($result['status'] ?? 'idle'),
            'message' => (string) ($result['message'] ?? 'No AI refresh in progress.'),
            'updated_at' => gmdate('c'),
            'snapshot_id' => intval($result['snapshot_id'] ?? 0),
            'error' => '',
            'processed_questions' => intval($result['processed_questions'] ?? 0),
            'total_questions' => intval($result['total_questions'] ?? 0),
            'processed_sources' => intval($result['processed_sources'] ?? 0),
            'total_sources' => intval($result['total_sources'] ?? 0),
            'partial_errors' => isset($result['partial_errors']) && is_array($result['partial_errors']) ? array_values($result['partial_errors']) : array(),
            'query_pack' => isset($result['query_pack']) && is_array($result['query_pack']) ? array_values($result['query_pack']) : array(),
            'overview_questions' => isset($result['overview_questions']) && is_array($result['overview_questions']) ? array_values($result['overview_questions']) : array(),
            'snapshot' => $snapshot,
            'history' => isset($result['history']) && is_array($result['history']) ? array_values($result['history']) : array(),
            'billed_provider_milli_cents' => $billed_provider_milli_cents,
        );

        return $this->aysa_monitoring_ai_finalize_status_payload($status, $existing);
    }

    private function aysa_monitoring_ai_finalize_status_payload($status, $existing = array()) {
        if (!is_array($status)) {
            $status = array();
        }
        if (!is_array($existing)) {
            $existing = array();
        }

        $signature = implode('|', array(
            intval($status['snapshot_id'] ?? 0),
            intval($status['processed_questions'] ?? 0),
            intval($status['processed_sources'] ?? 0),
            trim((string) ($status['snapshot']['updated_at'] ?? '')),
        ));
        $existing_signature = (string) ($existing['progress_signature'] ?? '');
        $last_progress_at = (string) ($existing['last_progress_at'] ?? '');
        if ($signature === '' || $signature !== $existing_signature) {
            $last_progress_at = gmdate('c');
        } elseif ($last_progress_at === '') {
            $last_progress_at = gmdate('c');
        }

        $status['progress_signature'] = $signature;
        $status['last_progress_at'] = $last_progress_at;

        return $status;
    }

    private function aysa_monitoring_ai_refresh_needs_rescue($status, $existing = array()) {
        if (!is_array($status) || !is_array($existing)) {
            return false;
        }
        if ((string) ($status['status'] ?? '') !== 'running') {
            return false;
        }
        if (intval($status['snapshot_id'] ?? 0) <= 0) {
            return false;
        }
        if (intval($existing['snapshot_id'] ?? 0) !== intval($status['snapshot_id'] ?? 0)) {
            return false;
        }
        if ((string) ($existing['progress_signature'] ?? '') !== (string) ($status['progress_signature'] ?? '')) {
            return false;
        }

        $last_progress_at = trim((string) ($status['last_progress_at'] ?? ''));
        if ($last_progress_at === '') {
            return false;
        }
        $last_progress_ts = strtotime($last_progress_at);
        if ($last_progress_ts === false) {
            return false;
        }

        $rescue_cooldown_at = trim((string) ($existing['last_rescue_at'] ?? ''));
        if ($rescue_cooldown_at !== '') {
            $rescue_cooldown_ts = strtotime($rescue_cooldown_at);
            if ($rescue_cooldown_ts !== false && $rescue_cooldown_ts > (time() - 45)) {
                return false;
            }
        }

        return $last_progress_ts < (time() - 45);
    }

    private function aysa_monitoring_schedule_ai_refresh_event($payload, $delay_seconds = 1) {
        if (!is_array($payload)) {
            return;
        }

        $project_id = intval($payload['project_id'] ?? 0);
        $domain = strtolower(trim((string) ($payload['domain'] ?? '')));
        if ($project_id <= 0 || $domain === '') {
            return;
        }

        $event_payload = array(
            'project_id' => $project_id,
            'domain' => $domain,
            'target' => (string) ($payload['target'] ?? $domain),
            'brand' => (string) ($payload['brand'] ?? ''),
            'brand_name' => (string) ($payload['brand_name'] ?? ''),
            'company_name' => (string) ($payload['company_name'] ?? ''),
            'industry' => (string) ($payload['industry'] ?? ''),
            'website_type' => (string) ($payload['website_type'] ?? ''),
            'location_code' => intval($payload['location_code'] ?? 2840),
            'language_code' => (string) ($payload['language_code'] ?? 'en'),
            'country_iso_code' => (string) ($payload['country_iso_code'] ?? 'US'),
            'seed_keywords' => isset($payload['seed_keywords']) && is_array($payload['seed_keywords']) ? array_values($payload['seed_keywords']) : array(),
        );
        if (isset($payload['query_pack']) && is_array($payload['query_pack'])) {
            $event_payload['query_pack'] = array_values($payload['query_pack']);
        }

        if (!wp_next_scheduled('aysa_monitoring_ai_refresh_event', array($event_payload))) {
            wp_schedule_single_event(time() + max(1, intval($delay_seconds)), 'aysa_monitoring_ai_refresh_event', array($event_payload));
        }
        if (function_exists('spawn_cron')) {
            spawn_cron();
        }
    }

    private function aysa_monitoring_ai_refresh_status_option_key($project_id, $domain) {
        $project_id = intval($project_id);
        $domain = strtolower(trim((string) $domain));
        return 'aysa_monitoring_ai_refresh_' . $project_id . '_' . md5($domain);
    }

    private function aysa_monitoring_build_ai_payload() {
        $project_id = intval($this->aysa_resolve_project_id_with_local_fallback());
        if ($project_id <= 0) {
            return array(
                'project_id' => 0,
                'domain' => '',
            );
        }

        $context = $this->aysa_monitoring_resolve_context((string) ($_POST['target'] ?? ''));
        $user_id = get_current_user_id();
        $general = get_option('aysa_wizard_general_' . intval($user_id), array());
        if (!is_array($general)) {
            $general = array();
        }
        $seed_data = $this->aysa_keyword_strategy_extract_seed_data($user_id);
        $brand = trim((string) ($general['brand_name'] ?? $general['brand'] ?? get_option('aysa_brand_name', '')));
        $company_name = trim((string) ($general['company_name'] ?? get_option('aysa_company_name', get_bloginfo('name'))));
        if ($brand === '') {
            $brand = $company_name !== '' ? $company_name : $context['target'];
        }
        $industry = trim((string) ($general['industry'] ?? get_option('aysa_industry', '')));
        $website_type = trim((string) ($general['website_type'] ?? get_option('aysa_website_type', '')));
        $country_iso = $this->aysa_monitoring_country_iso_from_location_code(intval($context['location_code'] ?? 2840));
        $query_pack = array();
        if (isset($_POST['query_pack'])) {
            $decoded_query_pack = json_decode((string) wp_unslash($_POST['query_pack']), true);
            if (is_array($decoded_query_pack)) {
                $query_pack = $decoded_query_pack;
            }
        }

        $payload = array(
            'project_id' => $project_id,
            'domain' => (string) ($context['target'] ?? ''),
            'target' => (string) ($context['target'] ?? ''),
            'brand' => $brand,
            'brand_name' => $brand,
            'company_name' => $company_name,
            'industry' => $industry,
            'website_type' => $website_type,
            'location_code' => intval($context['location_code'] ?? 2840),
            'language_code' => strtolower((string) ($context['language_code'] ?? 'en')),
            'country_iso_code' => $country_iso,
            'seed_keywords' => isset($seed_data['seed_keywords']) && is_array($seed_data['seed_keywords'])
                ? array_values($seed_data['seed_keywords'])
                : array(),
        );
        if (!empty($query_pack)) {
            $payload['query_pack'] = $query_pack;
        }
        return $payload;
    }

    private function aysa_monitoring_country_iso_from_location_code($location_code) {
        $location_code = intval($location_code);
        return match ($location_code) {
            2642 => 'RO',
            2276 => 'DE',
            2250 => 'FR',
            2380 => 'IT',
            2724 => 'ES',
            2826 => 'GB',
            default => 'US',
        };
    }

    public function handle_monitoring_rank_latest_from_db() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        global $wpdb;
        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }
        $context = $this->aysa_monitoring_resolve_context((string) ($_POST['target'] ?? ''));
        $associations_raw = isset($_POST['associations']) ? wp_unslash($_POST['associations']) : '';
        $associations = json_decode((string) $associations_raw, true);
        if (!is_array($associations)) {
            $associations = array();
        }
        $table = $this->aysa_monitoring_history_table();
        $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table));
        if ($table_exists !== $table) {
            wp_send_json_success(array(
                'rows' => array(),
                'source' => 'db',
                'count' => 0,
            ));
            return;
        }

        $query_latest_rows = function() use ($wpdb, $table, $project_id, $context) {
            $sql = "
            SELECT h.*
            FROM {$table} h
            INNER JOIN (
                SELECT MAX(id) AS max_id
                FROM {$table}
                WHERE project_id = %d
                  AND target_host = %s
                  AND (
                        (position IS NOT NULL AND position > 0)
                     OR (position_absolute IS NOT NULL AND position_absolute > 0)
                     OR (ctr IS NOT NULL AND ctr > 0)
                     OR (search_volume IS NOT NULL AND search_volume > 0)
                     OR (bounce_rate IS NOT NULL AND bounce_rate > 0)
                     OR (sessions IS NOT NULL AND sessions > 0)
                     OR (gsc_clicks IS NOT NULL AND gsc_clicks > 0)
                     OR (gsc_impressions IS NOT NULL AND gsc_impressions > 0)
                     OR (intent IS NOT NULL AND intent <> '')
                     OR (traffic IS NOT NULL AND traffic > 0)
                     OR (traffic_pct IS NOT NULL AND traffic_pct > 0)
                     OR (kd IS NOT NULL AND kd > 0)
                     OR (target_url IS NOT NULL AND target_url <> '')
                     OR (item_types IS NOT NULL AND item_types <> '')
                     OR (check_url IS NOT NULL AND check_url <> '')
                     OR (matched_item_type IS NOT NULL AND matched_item_type <> '')
                  )
                GROUP BY keyword, associated_on, id_intern
            ) latest ON latest.max_id = h.id
            ORDER BY h.keyword ASC
        ";
            return $wpdb->get_results($wpdb->prepare($sql, $project_id, $context['target']), ARRAY_A);
        };

        $rows = $query_latest_rows();
        if (!is_array($rows)) {
            $rows = array();
        }

        $needs_broker_fallback = empty($rows);
        if (!$needs_broker_fallback && !empty($associations)) {
            $refreshedKeywords = array();
            foreach ($rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $kw = trim((string) ($row['keyword'] ?? ''));
                if ($kw !== '') {
                    $refreshedKeywords[strtolower($kw)] = true;
                }
            }
            foreach ($associations as $assoc) {
                if (!is_array($assoc)) {
                    continue;
                }
                $kw = trim((string) ($assoc['keyword'] ?? ''));
                if ($kw !== '' && !isset($refreshedKeywords[strtolower($kw)])) {
                    $needs_broker_fallback = true;
                    break;
                }
            }
        }

        if ($needs_broker_fallback && !empty($associations)) {
            $payload = array(
                'project_id' => $project_id,
                'target' => $context['target'],
                'se_domain' => $context['se_domain'],
                'location_code' => $context['location_code'],
                'language_code' => $context['language_code'],
                'associations' => $associations,
            );
            $plan_result = $this->aysa_broker_request('POST', '/v1/dataforseo/rank-tracking/plan-enriched', $payload, 60);
            if (!is_wp_error($plan_result) && is_array($plan_result) && isset($plan_result['rows']) && is_array($plan_result['rows'])) {
                $this->aysa_monitoring_persist_rows($project_id, $context['target'], $plan_result['rows']);
                $rows = $query_latest_rows();
                if (!is_array($rows)) {
                    $rows = array();
                }
            }
        }

        $out = array();
        foreach ($rows as $row) {
            $latest_pos = is_null($row['position']) ? null : floatval($row['position']);
            $prev_pos = $wpdb->get_var($wpdb->prepare(
                "SELECT position
                 FROM {$table}
                 WHERE project_id = %d
                   AND target_host = %s
                   AND keyword = %s
                   AND associated_on = %s
                   AND id_intern = %d
                 ORDER BY id DESC
                 LIMIT 1 OFFSET 1",
                $project_id,
                $context['target'],
                (string) $row['keyword'],
                (string) $row['associated_on'],
                intval($row['id_intern'])
            ));
            $prev_pos = is_null($prev_pos) ? null : floatval($prev_pos);
            $delta = null;
            if (!is_null($latest_pos) && !is_null($prev_pos) && $prev_pos > 0) {
                $delta = $prev_pos - $latest_pos;
            }
	            $out[] = array(
	                'id' => intval($row['id']),
	                'keyword' => (string) $row['keyword'],
	                'associated_on' => (string) $row['associated_on'],
	                'id_intern' => intval($row['id_intern']),
	                'link' => (string) $row['target_url'],
	                'rank_group' => $latest_pos,
	                'rank_absolute' => is_null($row['position_absolute']) ? null : floatval($row['position_absolute']),
	                'position' => $latest_pos,
	                'position_absolute' => is_null($row['position_absolute']) ? null : floatval($row['position_absolute']),
	                'previous_position' => $prev_pos,
	                'position_delta' => $delta,
	                'ctr' => is_null($row['ctr']) ? null : floatval($row['ctr']),
                    'intent' => (string) ($row['intent'] ?? ''),
                    'traffic' => is_null($row['traffic']) ? null : intval($row['traffic']),
                    'traffic_pct' => is_null($row['traffic_pct']) ? null : floatval($row['traffic_pct']),
                    'kd' => is_null($row['kd']) ? null : floatval($row['kd']),
	                'search_volume' => is_null($row['search_volume']) ? null : intval($row['search_volume']),
	                'bounce_rate' => is_null($row['bounce_rate']) ? null : floatval($row['bounce_rate']),
	                'sessions' => is_null($row['sessions']) ? null : intval($row['sessions']),
	                'gsc_clicks' => is_null($row['gsc_clicks']) ? null : intval($row['gsc_clicks']),
	                'gsc_impressions' => is_null($row['gsc_impressions']) ? null : intval($row['gsc_impressions']),
	                'item_types' => (string) ($row['item_types'] ?? ''),
	                'check_url' => (string) ($row['check_url'] ?? ''),
	                'matched_item_type' => (string) ($row['matched_item_type'] ?? ''),
	                'last_checked_at' => (string) $row['tracked_at'],
	            );
        }

        wp_send_json_success(array(
            'rows' => $out,
            'source' => 'db',
            'count' => count($out),
        ));
    }

    public function handle_monitoring_rank_refresh_due() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }
        $context = $this->aysa_monitoring_resolve_context((string) ($_POST['target'] ?? ''));
        $max_keywords = intval($_POST['max_keywords'] ?? 30);
        $force = !empty($_POST['force']) ? true : false;
        $associations_raw = isset($_POST['associations']) ? wp_unslash($_POST['associations']) : '';
        $associations = json_decode((string) $associations_raw, true);
        if (!is_array($associations)) {
            $associations = array();
        }

        $payload = array(
            'project_id' => $project_id,
            'target' => $context['target'],
            'se_domain' => $context['se_domain'],
            'location_code' => $context['location_code'],
            'language_code' => $context['language_code'],
            'max_keywords' => max(1, min(100, $max_keywords)),
            'force' => $force,
            'associations' => $associations,
        );

        $result = $this->aysa_broker_request('POST', '/v1/dataforseo/rank-tracking/refresh-due', $payload, 120);
        if (is_wp_error($result)) {
            wp_send_json_error('Rank refresh failed: ' . $result->get_error_message());
            return;
        }

        // Bill only executed provider work; do not charge for DB/cache reads or request bootstrap.
        $provider_milli = $this->aysa_cost_usd_to_milli_cents($result['cost'] ?? 0);
        $billed_cents = intval(ceil(($provider_milli * 5) / 1000));
        if ($billed_cents > 0) {
            $this->aysa_wallet_track_server_usage(
                $project_id,
                '/v1/dataforseo/rank-tracking/refresh-due',
                $billed_cents,
                'monitoring_rank_refresh_due',
                array(
                    'module' => 'monitoring_analytics',
                    'source' => 'manual_refresh',
                    'provider_cost_milli_cents' => $provider_milli,
                    'pricing_multiplier' => 5,
                )
            );
        }

        // First persist directly from refresh response (authoritative execution output).
        $persisted_from_refresh = 0;
        if (is_array($result) && isset($result['results']) && is_array($result['results']) && !empty($result['results'])) {
            $refresh_rows = array();
            foreach ($result['results'] as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $keyword = trim((string) ($row['keyword'] ?? ''));
                if ($keyword === '') {
                    continue;
                }
                $refresh_rows[] = array(
                    'keyword' => $keyword,
                    'associated_on' => (string) ($row['associated_on'] ?? 'other'),
                    'id_intern' => intval($row['id_intern'] ?? 0),
                    'link' => (string) ($row['found_url'] ?? ''),
                    'position' => isset($row['position']) ? $row['position'] : null,
                    'position_absolute' => isset($row['position_absolute']) ? $row['position_absolute'] : null,
                    'status_code' => isset($row['status_code']) ? intval($row['status_code']) : null,
                    'status_message' => (string) ($row['status_message'] ?? ''),
                    'item_types' => isset($row['item_types']) ? $row['item_types'] : array(),
                    'check_url' => (string) ($row['check_url'] ?? ''),
                    'matched_item_type' => (string) ($row['matched_item_type'] ?? ''),
                    'last_checked_at' => (string) ($row['checked_at'] ?? ''),
                );
            }
            if (!empty($refresh_rows)) {
                $persist_info = $this->aysa_monitoring_persist_rows($project_id, $context['target'], $refresh_rows);
                $persisted_from_refresh = intval($persist_info['inserted'] ?? 0);
                $result['persist_debug_refresh'] = $persist_info;
            }
        }

        // Persist updated rank rows locally after manual refresh (or cron equivalent),
        // so regular page loads can work strictly from local DB cache.
        $plan_payload = array(
            'project_id' => $project_id,
            'target' => $context['target'],
            'se_domain' => $context['se_domain'],
            'location_code' => $context['location_code'],
            'language_code' => $context['language_code'],
            'associations' => $associations,
        );
        $plan_result = $this->aysa_broker_request('POST', '/v1/dataforseo/rank-tracking/plan-enriched', $plan_payload, 60);
        if (!is_wp_error($plan_result) && is_array($plan_result) && isset($plan_result['rows']) && is_array($plan_result['rows'])) {
            $persist_plan = $this->aysa_monitoring_persist_rows($project_id, $context['target'], $plan_result['rows']);
            $result['persist_debug_plan'] = $persist_plan;
            $result['persisted_rows'] = max($persisted_from_refresh, intval($persist_plan['inserted'] ?? 0));
        } else {
            $result['persisted_rows'] = $persisted_from_refresh;
        }
        $result['persisted_from_refresh'] = $persisted_from_refresh;

        wp_send_json_success($result);
    }

    private function aysa_monitoring_refresh_status_key($project_id) {
        return 'aysa_monitoring_rank_refresh_status_' . intval($project_id);
    }

    private function aysa_monitoring_refresh_queue_key($project_id) {
        return 'aysa_monitoring_rank_refresh_queue_' . intval($project_id);
    }

    private function aysa_monitoring_refresh_lock_key($project_id) {
        return 'aysa_monitoring_rank_refresh_lock_' . intval($project_id);
    }

    private function aysa_monitoring_get_refresh_status($project_id) {
        $status = get_option($this->aysa_monitoring_refresh_status_key($project_id), array());
        if (!is_array($status)) {
            $status = array();
        }

        return array(
            'run_id' => (string) ($status['run_id'] ?? ''),
            'status' => (string) ($status['status'] ?? 'idle'),
            'total' => intval($status['total'] ?? 0),
            'processed' => intval($status['processed'] ?? 0),
            'refreshed' => intval($status['refreshed'] ?? 0),
            'failed' => intval($status['failed'] ?? 0),
            'deferred' => intval($status['deferred'] ?? 0),
            'last_error' => (string) ($status['last_error'] ?? ''),
            'started_at' => intval($status['started_at'] ?? 0),
            'updated_at' => intval($status['updated_at'] ?? 0),
            'finished_at' => intval($status['finished_at'] ?? 0),
        );
    }

    private function aysa_monitoring_save_refresh_status($project_id, $status) {
        if (!is_array($status)) {
            return;
        }
        update_option($this->aysa_monitoring_refresh_status_key($project_id), $status, false);
    }

    private function aysa_monitoring_clear_refresh_queue($project_id) {
        delete_option($this->aysa_monitoring_refresh_queue_key($project_id));
    }

    private function aysa_monitoring_normalize_associations($associations) {
        $normalized = array();
        if (!is_array($associations)) {
            return $normalized;
        }

        foreach ($associations as $assoc) {
            if (!is_array($assoc)) {
                continue;
            }
            $keyword = trim((string) ($assoc['keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }
            $normalized[] = array(
                'keyword' => $keyword,
                'associated_on' => (string) ($assoc['associated_on'] ?? 'other'),
                'id_intern' => intval($assoc['id_intern'] ?? 0),
                'link' => (string) ($assoc['link'] ?? ''),
            );
        }

        return $normalized;
    }

    private function aysa_monitoring_schedule_background_refresh($project_id, $delay_seconds = 1) {
        $timestamp = time() + max(1, intval($delay_seconds));
        wp_schedule_single_event($timestamp, 'aysa_monitoring_rank_refresh_background_event', array(intval($project_id)));
        if (function_exists('spawn_cron')) {
            spawn_cron();
        }
    }

    private function aysa_monitoring_ensure_refresh_cron_tick($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return;
        }
        if (!wp_next_scheduled('aysa_monitoring_rank_refresh_cron_tick', array($project_id))) {
            wp_schedule_event(time() + 60, 'aysa_every_minute', 'aysa_monitoring_rank_refresh_cron_tick', array($project_id));
        }
        if (function_exists('spawn_cron')) {
            spawn_cron();
        }
    }

    private function aysa_monitoring_clear_refresh_cron_tick($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return;
        }
        while (false !== ($ts = wp_next_scheduled('aysa_monitoring_rank_refresh_cron_tick', array($project_id)))) {
            wp_unschedule_event($ts, 'aysa_monitoring_rank_refresh_cron_tick', array($project_id));
        }
    }

    private function aysa_monitoring_refresh_batch_and_persist($project_id, $context, $associations) {
        $project_id = intval($project_id);
        $context = is_array($context) ? $context : array();
        $associations = $this->aysa_monitoring_normalize_associations($associations);
        if ($project_id <= 0 || empty($associations)) {
            return array(
                'ok' => false,
                'inserted' => 0,
                'provider_cost_milli_cents' => 0,
                'error' => 'Invalid refresh batch payload.'
            );
        }

        $persisted_from_overview = 0;
        $overview_payload = array(
            'project_id' => $project_id,
            'target' => (string) ($context['target'] ?? ''),
            'se_domain' => (string) ($context['se_domain'] ?? 'google.com'),
            'location_code' => intval($context['location_code'] ?? 2840),
            'language_code' => (string) ($context['language_code'] ?? 'en'),
            'max_keywords' => count($associations),
            'include_serp_info' => true,
            'associations' => $associations,
        );
        $overview_result = $this->aysa_broker_request('POST', '/v1/monitoring/keywords/overview', $overview_payload, 120);
        if (!is_wp_error($overview_result)) {
            $overview_rows = $this->aysa_monitoring_extract_overview_rows($overview_result);
            $overview_rows = $this->aysa_monitoring_normalize_overview_rows($overview_rows, $associations);
            if (!empty($overview_rows)) {
                $persist_overview = $this->aysa_monitoring_persist_rows($project_id, (string) ($context['target'] ?? ''), $overview_rows);
                $persisted_from_overview = intval($persist_overview['inserted'] ?? 0);
            }
        }

        $payload = array(
            'project_id' => $project_id,
            'target' => (string) ($context['target'] ?? ''),
            'se_domain' => (string) ($context['se_domain'] ?? 'google.com'),
            'location_code' => intval($context['location_code'] ?? 2840),
            'language_code' => (string) ($context['language_code'] ?? 'en'),
            'max_keywords' => count($associations),
            'force' => true,
            'associations' => $associations,
        );

        $plan_payload = array(
            'project_id' => $project_id,
            'target' => (string) ($context['target'] ?? ''),
            'se_domain' => (string) ($context['se_domain'] ?? 'google.com'),
            'location_code' => intval($context['location_code'] ?? 2840),
            'language_code' => (string) ($context['language_code'] ?? 'en'),
            'associations' => $associations,
        );

        $result = $this->aysa_broker_request('POST', '/v1/dataforseo/rank-tracking/refresh-due', $payload, 120);
        if (is_wp_error($result)) {
            $err_code = (string) $result->get_error_code();
            $is_transient_broker_error = in_array($err_code, array('broker_invalid_response', 'broker_http_failed', 'broker_error'), true);
            // Graceful fallback: keep successful overview persistence instead of marking the entire batch failed.
            if ($persisted_from_overview > 0) {
                return array(
                    'ok' => true,
                    'inserted' => $persisted_from_overview,
                    'queued' => false,
                    'provider_cost_milli_cents' => 0,
                    'error' => '',
                );
            }
            if ($is_transient_broker_error) {
                // Try fallback planning data even when refresh-due fails.
                $plan_result = $this->aysa_broker_request('POST', '/v1/dataforseo/rank-tracking/plan-enriched', $plan_payload, 60);
                if (!is_wp_error($plan_result) && is_array($plan_result) && isset($plan_result['rows']) && is_array($plan_result['rows'])) {
                    $persist_plan = $this->aysa_monitoring_persist_rows($project_id, (string) ($context['target'] ?? ''), $plan_result['rows']);
                    $inserted = intval($persist_plan['inserted'] ?? 0);
                    if ($inserted > 0) {
                        return array(
                            'ok' => true,
                            'inserted' => $inserted,
                            'queued' => false,
                            'provider_cost_milli_cents' => $this->aysa_cost_usd_to_milli_cents($plan_result['cost'] ?? 0),
                            'error' => '',
                        );
                    }
                }
                return array(
                    'ok' => true,
                    'inserted' => 0,
                    'queued' => true,
                    'provider_cost_milli_cents' => 0,
                    'error' => $result->get_error_message(),
                );
            }
            return array(
                'ok' => false,
                'inserted' => $persisted_from_overview,
                'queued' => false,
                'provider_cost_milli_cents' => 0,
                'error' => $result->get_error_message(),
            );
        }

        $persisted_from_refresh = 0;
        $all_queued = false;
        if (is_array($result) && isset($result['results']) && is_array($result['results']) && !empty($result['results'])) {
            $refresh_rows = array();
            $all_queued = true;
            foreach ($result['results'] as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $keyword = trim((string) ($row['keyword'] ?? ''));
                if ($keyword === '') {
                    continue;
                }
                $status_code = isset($row['status_code']) ? intval($row['status_code']) : null;
                if ($status_code !== 40602) {
                    $all_queued = false;
                }
                $refresh_rows[] = array(
                    'keyword' => $keyword,
                    'associated_on' => (string) ($row['associated_on'] ?? 'other'),
                    'id_intern' => intval($row['id_intern'] ?? 0),
                    'link' => (string) ($row['found_url'] ?? ''),
                    'position' => isset($row['position']) ? $row['position'] : null,
                    'position_absolute' => isset($row['position_absolute']) ? $row['position_absolute'] : null,
                    'status_code' => $status_code,
                    'status_message' => (string) ($row['status_message'] ?? ''),
                    'item_types' => isset($row['item_types']) ? $row['item_types'] : array(),
                    'check_url' => (string) ($row['check_url'] ?? ''),
                    'matched_item_type' => (string) ($row['matched_item_type'] ?? ''),
                    'last_checked_at' => (string) ($row['checked_at'] ?? ''),
                );
            }
            if (!empty($refresh_rows)) {
                $persist_info = $this->aysa_monitoring_persist_rows($project_id, (string) ($context['target'] ?? ''), $refresh_rows);
                $persisted_from_refresh = intval($persist_info['inserted'] ?? 0);
            }
        }

        $plan_result = $this->aysa_broker_request('POST', '/v1/dataforseo/rank-tracking/plan-enriched', $plan_payload, 60);
        if (!is_wp_error($plan_result) && is_array($plan_result) && isset($plan_result['rows']) && is_array($plan_result['rows'])) {
            $persist_plan = $this->aysa_monitoring_persist_rows($project_id, (string) ($context['target'] ?? ''), $plan_result['rows']);
            return array(
                'ok' => true,
                'inserted' => max($persisted_from_overview, $persisted_from_refresh, intval($persist_plan['inserted'] ?? 0)),
                'queued' => ($all_queued && max($persisted_from_overview, $persisted_from_refresh, intval($persist_plan['inserted'] ?? 0)) <= 0),
                'provider_cost_milli_cents' => max(
                    $this->aysa_cost_usd_to_milli_cents($result['cost'] ?? 0),
                    $this->aysa_cost_usd_to_milli_cents($plan_result['cost'] ?? 0)
                ),
                'error' => '',
            );
        }

        return array(
            'ok' => true,
            'inserted' => max($persisted_from_overview, $persisted_from_refresh),
            'queued' => ($all_queued && max($persisted_from_overview, $persisted_from_refresh) <= 0),
            'provider_cost_milli_cents' => $this->aysa_cost_usd_to_milli_cents($result['cost'] ?? 0),
            'error' => '',
        );
    }

    public function handle_monitoring_rank_refresh_start_background() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }
        $associations_raw = isset($_POST['associations']) ? wp_unslash($_POST['associations']) : '';
        $associations = json_decode((string) $associations_raw, true);
        $associations = $this->aysa_monitoring_normalize_associations($associations);
        if (empty($associations)) {
            wp_send_json_error('No tracked keywords provided.');
            return;
        }

        $batch_size = intval($_POST['batch_size'] ?? 20);
        if ($batch_size < 5) {
            $batch_size = 5;
        } elseif ($batch_size > 10) {
            $batch_size = 10;
        }
        $context = $this->aysa_monitoring_resolve_context((string) ($_POST['target'] ?? ''));
        $job = $this->aysa_project_jobs_enqueue(
            $project_id,
            'monitoring',
            'rank_refresh',
            array(
                'project_id' => $project_id,
                'associations' => array_values($associations),
                'batch_size' => $batch_size,
                'context' => $context,
                'target' => (string) ($_POST['target'] ?? ''),
            ),
            array(
                'dedupe_key' => md5(wp_json_encode(array($project_id, $associations, $batch_size, (string) ($_POST['target'] ?? '')))),
                'input_summary' => array(
                    'tracked_keywords' => count($associations),
                ),
            )
        );

        wp_send_json_success(array(
            'started' => in_array((string) ($job['status'] ?? ''), array('queued', 'running', 'completed'), true),
            'run_id' => (string) ($job['job_id'] ?? ''),
            'status' => $this->aysa_monitoring_get_refresh_status($project_id),
            'project_job_status' => array(
                'status' => (string) ($job['status'] ?? 'queued'),
                'worker_ref' => (string) ($job['worker_ref'] ?? ''),
                'worker_status' => (string) ($job['worker_status'] ?? ''),
                'blocked_by' => isset($job['blocked_by']) && is_array($job['blocked_by']) ? array_values($job['blocked_by']) : array(),
            ),
            'project_job_rack' => $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id)),
        ));
    }

    public function handle_monitoring_rank_refresh_status() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $status = $this->aysa_monitoring_get_refresh_status($project_id);
        $queue = get_option($this->aysa_monitoring_refresh_queue_key($project_id), array());
        if (!is_array($queue)) {
            $queue = array();
        }

        $next_run = wp_next_scheduled('aysa_monitoring_rank_refresh_background_event', array($project_id));
        $status_is_running = (($status['status'] ?? 'idle') === 'running');
        $updated_at = intval($status['updated_at'] ?? 0);
        $stale_for = ($updated_at > 0) ? (time() - $updated_at) : PHP_INT_MAX;

        // Fallback worker tick: if WP-Cron is not executing, advance one batch from status polling.
        if ($status_is_running && ($next_run === false || $stale_for > 20)) {
            $this->handle_monitoring_rank_refresh_background_event($project_id);
            $status = $this->aysa_monitoring_get_refresh_status($project_id);
            $queue = get_option($this->aysa_monitoring_refresh_queue_key($project_id), array());
            if (!is_array($queue)) {
                $queue = array();
            }
            $next_run = wp_next_scheduled('aysa_monitoring_rank_refresh_background_event', array($project_id));
        }

        if (empty($status)) {
            $job = $this->aysa_project_jobs_find_latest_job($project_id, 'monitoring', 'rank_refresh', array('queued', 'running', 'completed', 'failed'));
            if (is_array($job)) {
                wp_send_json_success(array(
                    'run_id' => (string) ($job['job_id'] ?? ''),
                    'status' => (string) ($job['status'] ?? 'idle'),
                    'worker_ref' => (string) ($job['worker_ref'] ?? ''),
                    'worker_status' => (string) ($job['worker_status'] ?? ''),
                    'project_job_rack' => $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id)),
                ));
                return;
            }
        }

        $status['pending'] = max(0, intval($status['total']) - intval($status['processed']));
        $status['queue_cursor'] = intval($queue['cursor'] ?? 0);
        $status['queue_batch_size'] = intval($queue['batch_size'] ?? 0);
        $status['is_running'] = (($status['status'] ?? 'idle') === 'running');
        $status['next_run'] = $next_run;
        $status['project_job_rack'] = $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id));

        wp_send_json_success($status);
    }

    public function handle_monitoring_rank_refresh_background_event($project_id = 0) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return;
        }

        $lock_key = $this->aysa_monitoring_refresh_lock_key($project_id);
        if (get_transient($lock_key)) {
            $this->aysa_monitoring_schedule_background_refresh($project_id, 20);
            return;
        }
        set_transient($lock_key, 1, 180);

        try {
            $queue = get_option($this->aysa_monitoring_refresh_queue_key($project_id), array());
            if (!is_array($queue) || empty($queue['associations']) || !isset($queue['run_id'])) {
                $this->aysa_monitoring_clear_refresh_cron_tick($project_id);
                return;
            }

            $status = $this->aysa_monitoring_get_refresh_status($project_id);
            if (($status['status'] ?? '') !== 'running') {
                $this->aysa_monitoring_clear_refresh_cron_tick($project_id);
                return;
            }

            $cursor = max(0, intval($queue['cursor'] ?? 0));
            $batch_size = intval($queue['batch_size'] ?? 20);
            if ($batch_size < 5) {
                $batch_size = 5;
            } elseif ($batch_size > 10) {
                $batch_size = 10;
            }

            $associations = is_array($queue['associations']) ? $queue['associations'] : array();
            $batch = array_slice($associations, $cursor, $batch_size);

            if (empty($batch)) {
                $status['status'] = 'completed';
                $status['updated_at'] = time();
                $status['finished_at'] = time();
                $this->aysa_monitoring_save_refresh_status($project_id, $status);
                $this->aysa_monitoring_clear_refresh_queue($project_id);
                $this->aysa_monitoring_clear_refresh_cron_tick($project_id);
                $this->aysa_project_jobs_finalize(
                    $project_id,
                    $this->aysa_project_jobs_make_worker_ref('monitoring_rank_refresh', 'rank_refresh', (string) ($status['run_id'] ?? '')),
                    'completed',
                    array(
                        'processed' => intval($status['processed'] ?? 0),
                        'refreshed' => intval($status['refreshed'] ?? 0),
                    ),
                    ''
                );
                return;
            }

            $context = is_array($queue['context'] ?? null)
                ? $queue['context']
                : $this->aysa_monitoring_resolve_context('');
            $result = $this->aysa_monitoring_refresh_batch_and_persist($project_id, $context, $batch);
            if (!empty($result['queued'])) {
                $queue['current_retry'] = intval($queue['current_retry'] ?? 0) + 1;
                $status['updated_at'] = time();
                $status['last_error'] = (string) ($result['error'] ?? '');
                if ($queue['current_retry'] > 5) {
                    $current_batch_size = intval($queue['batch_size'] ?? 20);
                    if ($current_batch_size > 5) {
                        $queue['batch_size'] = max(5, intval(floor($current_batch_size / 2)));
                        $queue['current_retry'] = 0;
                        $status['last_error'] = 'Broker delayed responses; reducing batch size to ' . intval($queue['batch_size']) . '.';
                    } else {
                        $status['deferred'] = intval($status['deferred'] ?? 0) + count($batch);
                        $status['processed'] = min(intval($status['total']), intval($status['processed']) + count($batch));
                        $status['last_error'] = 'Batch stayed queued for too long and was deferred.';
                        $queue['cursor'] = $cursor + count($batch);
                        $queue['current_retry'] = 0;
                    }
                }
                $this->aysa_monitoring_save_refresh_status($project_id, $status);
                update_option($this->aysa_monitoring_refresh_queue_key($project_id), $queue, false);
                $this->aysa_monitoring_schedule_background_refresh($project_id, 20);
                return;
            }

            $status['processed'] = min(intval($status['total']), intval($status['processed']) + count($batch));
            if (!empty($result['ok'])) {
                $status['refreshed'] = intval($status['refreshed']) + intval($result['inserted'] ?? 0);
            } else {
                $status['failed'] = intval($status['failed']) + count($batch);
                $status['last_error'] = (string) ($result['error'] ?? 'Unknown refresh error');
            }
            $status['updated_at'] = time();
            $this->aysa_monitoring_save_refresh_status($project_id, $status);

            $queue['cursor'] = $cursor + count($batch);
            $queue['current_retry'] = 0;
            update_option($this->aysa_monitoring_refresh_queue_key($project_id), $queue, false);
            $provider_milli = max(0, intval($result['provider_cost_milli_cents'] ?? 0));
            $billed_cents = intval(ceil(($provider_milli * 5) / 1000));
            if ($billed_cents > 0) {
                $this->aysa_wallet_track_server_usage(
                    $project_id,
                    '/v1/dataforseo/rank-tracking/refresh-due',
                    $billed_cents,
                    'monitoring_rank_refresh_batch',
                    array(
                        'module' => 'monitoring_analytics',
                        'source' => 'background_refresh',
                        'run_id' => (string) ($queue['run_id'] ?? ''),
                        'batch_count' => count($batch),
                        'cursor_from' => $cursor,
                        'cursor_to' => intval($queue['cursor']),
                        'result' => !empty($result['ok']) ? 'ok' : 'failed',
                        'provider_cost_milli_cents' => $provider_milli,
                        'pricing_multiplier' => 5,
                    )
                );
            }

            if ($queue['cursor'] >= count($associations)) {
                $status['status'] = 'completed';
                $status['updated_at'] = time();
                $status['finished_at'] = time();
                $this->aysa_monitoring_save_refresh_status($project_id, $status);
                $this->aysa_monitoring_clear_refresh_queue($project_id);
                $this->aysa_monitoring_clear_refresh_cron_tick($project_id);
                $this->aysa_project_jobs_finalize(
                    $project_id,
                    $this->aysa_project_jobs_make_worker_ref('monitoring_rank_refresh', 'rank_refresh', (string) ($status['run_id'] ?? '')),
                    'completed',
                    array(
                        'processed' => intval($status['processed'] ?? 0),
                        'refreshed' => intval($status['refreshed'] ?? 0),
                    ),
                    ''
                );
                return;
            }

            $this->aysa_monitoring_schedule_background_refresh($project_id, 15);
        } finally {
            delete_transient($lock_key);
        }
    }

    public function register_aysa_cron_schedules($schedules) {
        if (!is_array($schedules)) {
            $schedules = array();
        }
        if (!isset($schedules['aysa_every_minute'])) {
            $schedules['aysa_every_minute'] = array(
                'interval' => 60,
                'display' => 'AYSA Every Minute',
            );
        }
        if (!isset($schedules['aysa_every_fifteen_minutes'])) {
            $schedules['aysa_every_fifteen_minutes'] = array(
                'interval' => 15 * 60,
                'display' => 'AYSA Every Fifteen Minutes',
            );
        }
        return $schedules;
    }

public function aysa_monitoring_history_maybe_create_table() {
        // Always run dbDelta to keep schema in sync when columns are added.
        $this->aysa_monitoring_history_create_table();
    }

    public function aysa_monitoring_ai_history_maybe_create_table() {
        $this->aysa_monitoring_ai_history_create_table();
    }

    private function aysa_monitoring_ai_persist_snapshot_summary($project_id, $target_host, $snapshot) {
        global $wpdb;
        $project_id = intval($project_id);
        $target_host = strtolower(trim((string) $target_host));
        if ($project_id <= 0 || $target_host === '' || !is_array($snapshot)) {
            return false;
        }

        $this->aysa_monitoring_ai_history_maybe_create_table();
        $table = $this->aysa_monitoring_ai_history_table();
        $summary = isset($snapshot['summary']) && is_array($snapshot['summary']) ? $snapshot['summary'] : array();
        $query_pack = isset($snapshot['query_pack']) && is_array($snapshot['query_pack']) ? $snapshot['query_pack'] : array();
        $snapshot_id = intval($snapshot['snapshot_id'] ?? 0);
        $snapshot_at = trim((string) ($snapshot['snapshot_at'] ?? ''));
        if ($snapshot_at === '') {
            $snapshot_at = current_time('mysql');
        } else {
            $ts = strtotime($snapshot_at);
            $snapshot_at = $ts ? gmdate('Y-m-d H:i:s', $ts) : current_time('mysql');
        }

        $existing_id = 0;
        if ($snapshot_id > 0) {
            $existing_id = intval($wpdb->get_var($wpdb->prepare(
                "SELECT id FROM {$table} WHERE project_id = %d AND target_host = %s AND snapshot_id = %d LIMIT 1",
                $project_id,
                $target_host,
                $snapshot_id
            )));
        }

        $data = array(
            'project_id' => $project_id,
            'target_host' => $target_host,
            'snapshot_id' => $snapshot_id,
            'status' => sanitize_text_field((string) ($snapshot['status'] ?? '')),
            'brand_name' => sanitize_text_field((string) ($snapshot['brand'] ?? '')),
            'summary_json' => wp_json_encode($summary),
            'query_pack_json' => wp_json_encode($query_pack),
            'snapshot_json' => wp_json_encode($snapshot),
            'snapshot_at' => $snapshot_at,
            'created_at' => current_time('mysql'),
        );
        $formats = array('%d', '%s', '%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s');

        if ($existing_id > 0) {
            $wpdb->update($table, $data, array('id' => $existing_id), $formats, array('%d'));
            $this->aysa_monitoring_update_client_rag_context($project_id, $target_host);
            return $existing_id;
        }

        $wpdb->insert($table, $data, $formats);
        $insert_id = intval($wpdb->insert_id);
        $this->aysa_monitoring_update_client_rag_context($project_id, $target_host);
        return $insert_id;
    }

    private function aysa_monitoring_rag_context_option_key($project_id) {
        return 'aysa_monitoring_client_rag_context_' . intval($project_id);
    }

    private function aysa_monitoring_update_client_rag_context($project_id, $target_host) {
        $project_id = intval($project_id);
        $target_host = strtolower(trim((string) $target_host));
        if ($project_id <= 0 || $target_host === '') {
            return;
        }

        $rank_rows = $this->aysa_adjustments_get_monitoring_latest_rows($project_id, $target_host);
        $rank_summary = array();
        foreach (array_slice(is_array($rank_rows) ? $rank_rows : array(), 0, 25) as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }
            $position = isset($row['position']) && is_numeric($row['position']) ? intval(round((float) $row['position'])) : null;
            $entry = array(
                'keyword' => $keyword,
                'position' => $position,
                'previous_position' => isset($row['previous_position']) && is_numeric($row['previous_position']) ? intval(round((float) $row['previous_position'])) : null,
                'position_delta' => isset($row['position_delta']) && is_numeric($row['position_delta']) ? floatval($row['position_delta']) : null,
                'target_url' => (string) ($row['link'] ?? ''),
                'last_checked_at' => (string) ($row['last_checked_at'] ?? ''),
                'intent' => (string) ($row['intent'] ?? ''),
            );
            $rank_summary[] = $entry;
        }

        $ai_overview = $this->aysa_monitoring_ai_get_local_overview($project_id, $target_host);
        $ai_snapshot = isset($ai_overview['snapshot']) && is_array($ai_overview['snapshot']) ? $ai_overview['snapshot'] : array();
        $ai_questions = isset($ai_snapshot['questions']) && is_array($ai_snapshot['questions']) ? $ai_snapshot['questions'] : array();
        $ai_summary = array();
        foreach (array_slice($ai_questions, 0, 12) as $question) {
            if (!is_array($question)) {
                continue;
            }
            $ai_summary[] = array(
                'question' => (string) ($question['text'] ?? $question['question'] ?? ''),
                'type' => (string) ($question['type'] ?? ''),
                'visibility_status' => (string) ($question['visibility_status'] ?? ''),
                'our_position' => isset($question['our_position']) ? $question['our_position'] : null,
                'context_summary' => (string) ($question['context_summary'] ?? ''),
                'snapshot_at' => (string) ($ai_snapshot['snapshot_at'] ?? ''),
            );
        }

        $payload = array(
            'project_id' => $project_id,
            'target_host' => $target_host,
            'updated_at' => current_time('mysql'),
            'rank_monitoring' => $rank_summary,
            'ai_monitoring' => $ai_summary,
        );

        $this->aysa_monitoring_sync_client_context_to_broker($payload);
        update_option($this->aysa_monitoring_rag_context_option_key($project_id), $payload, false);
    }

    private function aysa_monitoring_sync_client_context_to_broker($payload) {
        if (!is_array($payload)) {
            return;
        }

        $project_id = intval($payload['project_id'] ?? 0);
        $target_host = strtolower(trim((string) ($payload['target_host'] ?? '')));
        if ($project_id <= 0 || $target_host === '') {
            return;
        }

        $result = $this->aysa_broker_request('POST', '/v1/project-context/monitoring/upsert', $payload, 20);
        if (is_wp_error($result)) {
            error_log('AYSA monitoring context broker sync failed for project ' . $project_id . ': ' . $result->get_error_message());
        }
    }

    private function aysa_monitoring_ai_get_local_overview($project_id, $target_host) {
        $history = $this->aysa_monitoring_ai_get_local_history($project_id, $target_host, 12);
        if (empty($history)) {
            return array();
        }

        $latest = $history[0];
        $snapshot = isset($latest['_snapshot']) && is_array($latest['_snapshot']) ? $latest['_snapshot'] : array();
        $clean_history = array_map(function($row) {
            if (is_array($row) && isset($row['_snapshot'])) {
                unset($row['_snapshot']);
            }
            return $row;
        }, $history);

        return array(
            'ok' => true,
            'project_id' => intval($project_id),
            'domain' => (string) $target_host,
            'snapshot' => $snapshot,
            'history' => $clean_history,
        );
    }

    private function aysa_monitoring_ai_get_local_history($project_id, $target_host, $limit = 12) {
        global $wpdb;
        $project_id = intval($project_id);
        $target_host = strtolower(trim((string) $target_host));
        if ($project_id <= 0 || $target_host === '') {
            return array();
        }

        $table = $this->aysa_monitoring_ai_history_table();
        $exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table));
        if ($exists !== $table) {
            return array();
        }

        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$table} WHERE project_id = %d AND target_host = %s ORDER BY snapshot_at DESC LIMIT %d",
            $project_id,
            $target_host,
            max(1, min(30, intval($limit)))
        ), ARRAY_A);
        if (!is_array($rows)) {
            return array();
        }

        $history = array();
        foreach ($rows as $row) {
            $summary = json_decode((string) ($row['summary_json'] ?? '{}'), true);
            $query_pack = json_decode((string) ($row['query_pack_json'] ?? '[]'), true);
            $snapshot = json_decode((string) ($row['snapshot_json'] ?? '{}'), true);
            $history[] = array(
                'snapshot_id' => intval($row['snapshot_id'] ?? 0),
                'project_id' => intval($row['project_id'] ?? 0),
                'domain' => (string) ($row['target_host'] ?? ''),
                'brand' => (string) ($row['brand_name'] ?? ''),
                'status' => (string) ($row['status'] ?? ''),
                'snapshot_at' => gmdate('c', strtotime((string) ($row['snapshot_at'] ?? 'now'))),
                'query_pack' => is_array($query_pack) ? $query_pack : array(),
                'summary' => is_array($summary) ? $summary : array(),
                '_snapshot' => is_array($snapshot) ? $snapshot : array(),
            );
        }

        return $history;
    }

    private function aysa_monitoring_resolve_context($requested_target = '') {
        $configured_target = (string) get_option('aysa_domain', '');
        if ($configured_target === '') {
            $configured_target = (string) get_option('aysa_auth_domain', '');
        }
        if ($configured_target === '') {
            $configured_target = (string) home_url();
        }

        $effective_target = $requested_target !== '' ? $requested_target : $configured_target;
        $host = $this->aysa_monitoring_extract_host($effective_target);
        if ($host === '' || $this->aysa_monitoring_is_local_host($host)) {
            $host = $this->aysa_monitoring_extract_host($configured_target);
        }
        if ($host === '' || $this->aysa_monitoring_is_local_host($host)) {
            $host = $this->aysa_monitoring_extract_host((string) home_url());
        }

        $lang_raw = (string) get_option('aysa_language', get_locale());
        $language_code = strtolower(substr($lang_raw, 0, 2));
        if (!preg_match('/^[a-z]{2}$/', $language_code)) {
            $language_code = 'en';
        }

        $country_raw = (string) get_option('aysa_targeted_country', '');
        if ($country_raw === '') {
            $country_raw = (string) get_option('targeted_country', '');
        }
        if ($country_raw === '') {
            $country_raw = (string) get_option('aysa_country', '');
        }
        if ($country_raw === '') {
            $country_raw = (string) get_option('country', '');
        }

        $tld = strtolower((string) preg_replace('/^.*\.([a-z]{2,})$/', '$1', $host));
        if ($country_raw === '') {
            if ($tld === 'ro') {
                $country_raw = 'Romania';
            } elseif ($tld === 'de') {
                $country_raw = 'Germany';
            } elseif ($tld === 'fr') {
                $country_raw = 'France';
            } elseif ($tld === 'it') {
                $country_raw = 'Italy';
            } elseif ($tld === 'es') {
                $country_raw = 'Spain';
            } elseif ($tld === 'uk' || $tld === 'co.uk') {
                $country_raw = 'United Kingdom';
            } elseif ($tld === 'com') {
                $country_raw = 'United States';
            }
        }

        $location_map = array(
            'romania' => 2642,
            'ro' => 2642,
            'germany' => 2276,
            'de' => 2276,
            'france' => 2250,
            'fr' => 2250,
            'italy' => 2380,
            'it' => 2380,
            'spain' => 2724,
            'es' => 2724,
            'united kingdom' => 2826,
            'uk' => 2826,
            'united states' => 2840,
            'us' => 2840,
        );
        $country_key = strtolower(trim($country_raw));
        $location_code = isset($location_map[$country_key]) ? intval($location_map[$country_key]) : 2840;

	        if ($tld === 'ro') {
	            $se_domain = 'google.ro';
	            if ($language_code === 'en') {
	                $language_code = 'ro';
	            }
	            $location_code = 2642;
        } elseif ($tld === 'de') {
            $se_domain = 'google.de';
        } elseif ($tld === 'fr') {
            $se_domain = 'google.fr';
        } elseif ($tld === 'it') {
            $se_domain = 'google.it';
        } elseif ($tld === 'es') {
            $se_domain = 'google.es';
        } elseif ($tld === 'uk') {
            $se_domain = 'google.co.uk';
	        } else {
	            $se_domain = 'google.com';
	        }
        return array(
            'target' => $host,
            'se_domain' => $se_domain,
            'location_code' => $location_code,
            'language_code' => $language_code,
        );
    }

    private function aysa_monitoring_extract_host($value) {
        $value = trim((string) $value);
        if ($value === '') {
            return '';
        }
        if (!preg_match('#^[a-z][a-z0-9+.-]*://#i', $value)) {
            $value = 'https://' . ltrim($value, '/');
        }
        $host = wp_parse_url($value, PHP_URL_HOST);
        if (!is_string($host) || $host === '') {
            return '';
        }
        return strtolower(preg_replace('/^www\./', '', $host));
    }

    private function aysa_monitoring_is_local_host($host) {
        $host = strtolower(trim((string) $host));
        return $host === 'localhost' || $host === '127.0.0.1' || substr($host, -10) === '.localhost';
    }

private function aysa_monitoring_history_table() {
        global $wpdb;
        return $wpdb->prefix . 'aysa_monitoring_rank_history';
    }

    private function aysa_monitoring_ai_history_table() {
        global $wpdb;
        return $wpdb->prefix . 'aysa_monitoring_ai_history';
    }

    private function aysa_sitemap_competition_runs_table() {
        global $wpdb;
        return $wpdb->prefix . 'aysa_sitemap_competition_runs';
    }

    private function aysa_sitemap_competition_items_table() {
        global $wpdb;
        return $wpdb->prefix . 'aysa_sitemap_competition_items';
    }

    private function aysa_sitemap_competition_todo_table() {
        global $wpdb;
        return $wpdb->prefix . 'aysa_sitemap_competition_todo';
    }

    private function aysa_monitoring_history_create_table() {
        global $wpdb;
        $table = $this->aysa_monitoring_history_table();
        $charset_collate = $wpdb->get_charset_collate();
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
	        $sql = "CREATE TABLE {$table} (
	            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
	            project_id BIGINT UNSIGNED NOT NULL,
	            target_host VARCHAR(191) NOT NULL,
	            keyword VARCHAR(191) NOT NULL,
	            associated_on VARCHAR(40) NOT NULL DEFAULT 'other',
	            id_intern BIGINT UNSIGNED NOT NULL DEFAULT 0,
	            target_url TEXT NULL,
	            position DECIMAL(10,4) NULL,
	            position_absolute DECIMAL(10,4) NULL,
	            ctr DECIMAL(10,4) NULL,
	            search_volume INT NULL,
	            bounce_rate DECIMAL(10,4) NULL,
	            sessions INT NULL,
	            gsc_clicks INT NULL,
            gsc_impressions INT NULL,
            intent VARCHAR(32) NULL,
            traffic INT NULL,
            traffic_pct DECIMAL(10,4) NULL,
            kd DECIMAL(10,4) NULL,
            item_types TEXT NULL,
	            check_url TEXT NULL,
	            matched_item_type VARCHAR(64) NULL,
	            source VARCHAR(20) NOT NULL DEFAULT 'api',
	            tracked_at DATETIME NOT NULL,
	            created_at DATETIME NOT NULL,
            PRIMARY KEY (id),
            KEY project_target (project_id, target_host),
            KEY keyword_idx (keyword),
            KEY tracked_at_idx (tracked_at),
            KEY kw_assoc_entity (project_id, keyword, associated_on, id_intern)
        ) {$charset_collate};";
        dbDelta($sql);
    }

    private function aysa_monitoring_ai_history_create_table() {
        global $wpdb;
        $table = $this->aysa_monitoring_ai_history_table();
        $charset_collate = $wpdb->get_charset_collate();
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        $sql = "CREATE TABLE {$table} (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
            project_id BIGINT UNSIGNED NOT NULL,
            target_host VARCHAR(191) NOT NULL,
            snapshot_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
            status VARCHAR(32) NOT NULL DEFAULT '',
            brand_name VARCHAR(191) NOT NULL DEFAULT '',
            summary_json LONGTEXT NOT NULL,
            query_pack_json LONGTEXT NOT NULL,
            snapshot_json LONGTEXT NOT NULL,
            snapshot_at DATETIME NOT NULL,
            created_at DATETIME NOT NULL,
            PRIMARY KEY (id),
            KEY project_target_snapshot (project_id, target_host, snapshot_at),
            KEY snapshot_id_idx (snapshot_id)
        ) {$charset_collate};";
        dbDelta($sql);
    }

    public function aysa_sitemap_competition_maybe_create_tables() {
        global $wpdb;
        $charset_collate = $wpdb->get_charset_collate();
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';

        $runs = $this->aysa_sitemap_competition_runs_table();
        $items = $this->aysa_sitemap_competition_items_table();
        $todo = $this->aysa_sitemap_competition_todo_table();

        dbDelta("CREATE TABLE {$runs} (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
            project_id BIGINT UNSIGNED NOT NULL,
            target_host VARCHAR(191) NOT NULL,
            remote_run_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
            status VARCHAR(32) NOT NULL DEFAULT '',
            competitors_json LONGTEXT NOT NULL,
            summary_json LONGTEXT NOT NULL,
            overview_json LONGTEXT NOT NULL,
            run_json LONGTEXT NOT NULL,
            started_at DATETIME NULL,
            completed_at DATETIME NULL,
            created_at DATETIME NOT NULL,
            updated_at DATETIME NOT NULL,
            PRIMARY KEY (id),
            KEY project_target_idx (project_id, target_host),
            KEY remote_run_idx (remote_run_id)
        ) {$charset_collate};");

        dbDelta("CREATE TABLE {$items} (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
            local_run_id BIGINT UNSIGNED NOT NULL,
            project_id BIGINT UNSIGNED NOT NULL,
            target_host VARCHAR(191) NOT NULL,
            competitor_host VARCHAR(191) NOT NULL,
            final_url TEXT NOT NULL,
            page_title TEXT NULL,
            meta_title TEXT NULL,
            content_type VARCHAR(32) NOT NULL DEFAULT 'unknown',
            freshness_signal VARCHAR(191) NULL,
            slug VARCHAR(191) NULL,
            path TEXT NULL,
            fetch_status VARCHAR(32) NOT NULL DEFAULT '',
            item_json LONGTEXT NOT NULL,
            created_at DATETIME NOT NULL,
            PRIMARY KEY (id),
            KEY local_run_idx (local_run_id),
            KEY competitor_idx (competitor_host(120)),
            KEY content_type_idx (content_type)
        ) {$charset_collate};");

        dbDelta("CREATE TABLE {$todo} (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
            local_run_id BIGINT UNSIGNED NOT NULL,
            project_id BIGINT UNSIGNED NOT NULL,
            target_host VARCHAR(191) NOT NULL,
            competitor_host VARCHAR(191) NOT NULL,
            competitor_url TEXT NOT NULL,
            competitor_title TEXT NULL,
            estimated_topic TEXT NULL,
            freshness_signal VARCHAR(191) NULL,
            local_status VARCHAR(32) NOT NULL DEFAULT 'missing',
            suggested_action VARCHAR(32) NOT NULL DEFAULT 'review',
            matched_post_id BIGINT UNSIGNED NOT NULL DEFAULT 0,
            matched_url TEXT NULL,
            todo_json LONGTEXT NOT NULL,
            created_at DATETIME NOT NULL,
            PRIMARY KEY (id),
            KEY local_run_idx (local_run_id),
            KEY status_idx (local_status)
        ) {$charset_collate};");
    }

    private function aysa_monitoring_extract_numeric($row, $keys) {
        if (!is_array($row)) {
            return null;
        }
        foreach ($keys as $key) {
            if (!array_key_exists($key, $row)) {
                continue;
            }
            $val = $row[$key];
            if ($val === null || $val === '') {
                continue;
            }
            if (is_numeric($val)) {
                return floatval($val);
            }
            if (is_string($val)) {
                $normalized = str_replace(',', '.', preg_replace('/[^0-9,\.\-]/', '', $val));
                if ($normalized !== '' && is_numeric($normalized)) {
                    return floatval($normalized);
                }
            }
        }
        return null;
    }

    private function aysa_monitoring_persist_rows($project_id, $target_host, $rows) {
        global $wpdb;
        if (!is_array($rows) || empty($rows)) {
            return array('inserted' => 0, 'failed' => 0, 'last_error' => '');
        }
        $this->aysa_monitoring_history_maybe_create_table();
        $table = $this->aysa_monitoring_history_table();
        $now = current_time('mysql');
        $inserted = 0;
        $failed = 0;
        $last_error = '';

        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }
	            // Keep Position as organic group rank first, then generic fallbacks.
	            $position = $this->aysa_monitoring_extract_numeric($row, array(
	                'position', 'rank_group', 'last_position', 'rank', 'pos'
	            ));
	            $position_absolute = $this->aysa_monitoring_extract_numeric($row, array(
	                'position_absolute', 'rank_absolute'
	            ));
	            // Prefer Google CTR when present.
            $ctr = $this->aysa_monitoring_extract_numeric($row, array('gsc_ctr', 'ctr'));
            $intent = trim((string) ($row['intent'] ?? $row['search_intent'] ?? ''));
            $traffic = $this->aysa_monitoring_extract_numeric($row, array('traffic', 'gsc_clicks', 'clicks', 'sessions'));
            $traffic_pct = $this->aysa_monitoring_extract_numeric($row, array('traffic_pct', 'traffic_percent'));
            $kd = $this->aysa_monitoring_extract_numeric($row, array('kd', 'keyword_difficulty', 'competition'));
	            $search_volume = $this->aysa_monitoring_extract_numeric($row, array('search_volume', 'searchVolume', 'volume'));
	            // Prefer GA bounce rate when present.
	            $bounce_rate = $this->aysa_monitoring_extract_numeric($row, array('ga_bounce_rate', 'bounce_rate', 'bounceRate'));
	            $sessions = $this->aysa_monitoring_extract_numeric($row, array('sessions'));
	            $gsc_clicks = $this->aysa_monitoring_extract_numeric($row, array('gsc_clicks', 'clicks'));
	            $gsc_impressions = $this->aysa_monitoring_extract_numeric($row, array('gsc_impressions', 'impressions'));
            $itemTypesArray = [];
            if (isset($row['item_types']) && is_array($row['item_types'])) {
                $itemTypesArray = array_values(array_filter(array_map('strval', $row['item_types']), fn($v) => $v !== ''));
            } elseif (isset($row['item_types']) && is_string($row['item_types']) && trim($row['item_types']) !== '') {
                $itemTypesArray = array_values(array_filter(array_map('trim', explode(',', (string) $row['item_types'])), fn($v) => $v !== ''));
            }
            $itemTypesJson = null;
            if ($itemTypesArray) {
                $encoded = json_encode($itemTypesArray);
                if ($encoded !== false) {
                    $itemTypesJson = $encoded;
                }
            }
	            $check_url = (string) ($row['check_url'] ?? '');
	            $matched_item_type = (string) ($row['matched_item_type'] ?? '');
            $status_code = isset($row['status_code']) ? intval($row['status_code']) : null;

            // Do not persist queue/incomplete responses as latest history rows.
            if ($status_code === 40602) {
                continue;
            }

            $target_url = (string) ($row['link'] ?? $row['found_url'] ?? $row['url'] ?? '');
            $hasMeaningfulData = (
                (!is_null($position) && floatval($position) > 0) ||
                (!is_null($position_absolute) && floatval($position_absolute) > 0) ||
                (!is_null($ctr) && floatval($ctr) > 0) ||
                (!is_null($search_volume) && floatval($search_volume) > 0) ||
                (!is_null($bounce_rate) && floatval($bounce_rate) > 0) ||
                (!is_null($sessions) && floatval($sessions) > 0) ||
                (!is_null($gsc_clicks) && floatval($gsc_clicks) > 0) ||
                (!is_null($gsc_impressions) && floatval($gsc_impressions) > 0) ||
                ($intent !== '') ||
                (!is_null($traffic) && floatval($traffic) > 0) ||
                (!is_null($traffic_pct) && floatval($traffic_pct) > 0) ||
                (!is_null($kd) && floatval($kd) > 0) ||
                $target_url !== '' ||
                $itemTypesJson !== null ||
                $check_url !== '' ||
                $matched_item_type !== ''
            );
            if (!$hasMeaningfulData) {
                continue;
            }

            $ok = $wpdb->insert(
                $table,
                array(
                    'project_id' => intval($project_id),
                    'target_host' => (string) $target_host,
                    'keyword' => $keyword,
                    'associated_on' => (string) ($row['associated_on'] ?? $row['associatedOn'] ?? 'other'),
	                    'id_intern' => intval($row['id_intern'] ?? $row['idIntern'] ?? 0),
	                    'target_url' => $target_url,
	                    'position' => is_null($position) ? null : $position,
	                    'position_absolute' => is_null($position_absolute) ? null : $position_absolute,
	                    'ctr' => is_null($ctr) ? null : $ctr,
	                    'search_volume' => is_null($search_volume) ? null : intval(round($search_volume)),
	                    'bounce_rate' => is_null($bounce_rate) ? null : $bounce_rate,
	                    'sessions' => is_null($sessions) ? null : intval(round($sessions)),
	                    'gsc_clicks' => is_null($gsc_clicks) ? null : intval(round($gsc_clicks)),
	                    'gsc_impressions' => is_null($gsc_impressions) ? null : intval(round($gsc_impressions)),
                    'intent' => $intent !== '' ? $intent : null,
                    'traffic' => is_null($traffic) ? null : intval(round($traffic)),
                    'traffic_pct' => is_null($traffic_pct) ? null : $traffic_pct,
                    'kd' => is_null($kd) ? null : $kd,
                    'item_types' => $itemTypesJson,
	                    'check_url' => $check_url !== '' ? $check_url : null,
	                    'matched_item_type' => $matched_item_type !== '' ? $matched_item_type : null,
	                    'source' => 'api',
	                    'tracked_at' => $now,
	                    'created_at' => $now,
	                ),
	                array(
	                    '%d', '%s', '%s', '%s', '%d', '%s',
	                    '%f', '%f', '%f', '%d', '%f', '%d', '%d', '%d',
                    '%s', '%d', '%f', '%f',
	                    '%s', '%s', '%s', '%s', '%s', '%s'
	                )
	            );
            if ($ok === false) {
                $failed++;
                if (!empty($wpdb->last_error)) {
                    $last_error = (string) $wpdb->last_error;
                }
            } else {
                $inserted++;
            }
        }
        if ($inserted > 0) {
            $this->aysa_monitoring_update_client_rag_context($project_id, $target_host);
        }

        return array(
            'inserted' => $inserted,
            'failed' => $failed,
            'last_error' => $last_error,
        );
    }

    private function aysa_get_project_credit_balance($project_id, $force_remote = false) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return 0;
        }

        if (class_exists('Aysa_Wallet_Manager')) {
            try {
                $wallet = Aysa_Wallet_Manager::get_instance()->get_wallet_snapshot($project_id, array(
                    'allow_remote' => true,
                    'force_remote' => !empty($force_remote),
                ));
                if (is_array($wallet) && isset($wallet['balance_cents'])) {
                    return intval($wallet['balance_cents']);
                }
            } catch (Exception $e) {
                // keep backward-compat fallback below
            }
        }

        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }

        try {
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $resp = $auth_manager->make_authenticated_request('api/AysaCredits/get-total-credits/' . $project_id, 'GET');
            if (is_array($resp)) {
                if (isset($resp['totalCredits'])) {
                    return intval($resp['totalCredits']);
                }
                if (isset($resp['data']['totalCredits'])) {
                    return intval($resp['data']['totalCredits']);
                }
                if (isset($resp['credits'])) {
                    return intval($resp['credits']);
                }
            }
            if (is_numeric($resp)) {
                return intval($resp);
            }
        } catch (Exception $e) {
            // fallback below
        }

        return intval(get_option('aysa_user_credits', 0));
    }

    private function aysa_get_adverlink_orders_key($project_id) {
        return 'aysa_adverlink_orders_' . intval($project_id);
    }

    private function aysa_get_reserved_credits_key($project_id) {
        return 'aysa_adverlink_reserved_credits_' . intval($project_id);
    }

    private function aysa_get_adverlink_link_issues_key($project_id) {
        return 'aysa_adverlink_link_issues_' . intval($project_id);
    }

    private function aysa_get_adverlink_link_issues_sync_key($project_id) {
        return 'aysa_adverlink_link_issues_last_sync_' . intval($project_id);
    }

    private function aysa_get_backlink_audit_key($project_id) {
        return 'aysa_backlink_audit_' . intval($project_id);
    }

    private function aysa_get_backlink_audit_sync_key($project_id) {
        return 'aysa_backlink_audit_last_sync_' . intval($project_id);
    }

    private function aysa_get_backlink_audit_domain_key($project_id, $domain) {
        $normalized = strtolower(trim((string) $domain));
        $normalized = preg_replace('/[^a-z0-9.-]/', '', $normalized);
        if ($normalized === '') {
            $normalized = 'unknown';
        }
        return 'aysa_backlink_audit_domain_' . intval($project_id) . '_' . md5($normalized);
    }

    private function aysa_get_article_draft_key($project_id, $portal_id) {
        $project_part = intval($project_id);
        $portal_part = substr(md5((string) $portal_id), 0, 16);
        return 'aysa_adverlink_article_draft_' . $project_part . '_' . $portal_part;
    }

    private function aysa_get_adverlink_credentials() {
        $api_key = defined('AYSA_ADVERLINK_API_KEY')
            ? (string) AYSA_ADVERLINK_API_KEY
            : (string) get_option('aysa_adverlink_api_key', '');

        $org_id = defined('AYSA_ADVERLINK_ADVERTISER_ORG_ID')
            ? (string) AYSA_ADVERLINK_ADVERTISER_ORG_ID
            : (string) get_option('aysa_adverlink_advertiser_org_id', '');

        return array(
            'api_key' => trim($api_key),
            'org_id' => trim($org_id)
        );
    }

    private function aysa_detect_adverlink_credential_type($api_key) {
        $api_key = trim((string) $api_key);
        if ($api_key === '') {
            return 'missing';
        }
        if (stripos($api_key, 'atk_') === 0) {
            return 'advertiser_api_key';
        }
        if (stripos($api_key, 'advlk_pk_') === 0 || stripos($api_key, 'advlk_sk_') === 0) {
            return 'plugin_token';
        }
        return 'unknown';
    }

    private function aysa_validate_adverlink_marketplace_credentials(array $creds) {
        $api_key = trim((string) ($creds['api_key'] ?? ''));
        if ($api_key === '') {
            return new WP_Error(
                'adverlink_missing_credentials',
                'Off-Page credentials are missing. Configure marketplace advertiser API key in AYSA settings.'
            );
        }

        $type = $this->aysa_detect_adverlink_credential_type($api_key);
        if ($type !== 'advertiser_api_key') {
            return new WP_Error(
                'adverlink_wrong_credential_type',
                'Received ' . $type . '. Off-Page marketplace public routes require advertiser API key (atk_...).'
            );
        }

        return true;
    }

    private function aysa_format_adverlink_marketplace_error($error, $fallback = 'Marketplace request failed.') {
        if (is_wp_error($error)) {
            return array(
                'code' => $error->get_error_code(),
                'message' => $error->get_error_message(),
            );
        }

        if (is_array($error)) {
            return array(
                'code' => sanitize_text_field((string) ($error['code'] ?? 'marketplace_error')),
                'message' => sanitize_text_field((string) ($error['message'] ?? $fallback)),
            );
        }

        return array(
            'code' => 'marketplace_error',
            'message' => $fallback,
        );
    }

    private function aysa_get_adverlink_orders($project_id) {
        $orders = get_option($this->aysa_get_adverlink_orders_key($project_id), array());
        return is_array($orders) ? $orders : array();
    }

    private function aysa_get_link_exchange_profile($project_id = 0) {
        $project_id = intval($project_id);
        $setup_user_id = intval($this->aysa_get_authenticated_external_user_id());
        if ($setup_user_id <= 0) {
            $setup_user_id = get_current_user_id();
        }

        $context = $this->aysa_keyword_strategy_get_context($setup_user_id);
        $seed_data = $this->aysa_keyword_strategy_extract_seed_data($setup_user_id);
        $general = get_option('aysa_wizard_general_' . intval($setup_user_id), array());
        if (!is_array($general)) {
            $general = array();
        }

        $industry = sanitize_text_field((string) ($context['industry'] ?? $general['industry'] ?? ''));
        $website_type = sanitize_text_field((string) ($context['website_type'] ?? $general['website_type'] ?? ''));
        $country = sanitize_text_field((string) ($context['targeted_country'] ?? 'Romania'));
        $language = sanitize_text_field((string) ($context['primary_language'] ?? 'Romanian'));

        $topics = array();
        if (!empty($seed_data['seed_keywords']) && is_array($seed_data['seed_keywords'])) {
            foreach ($seed_data['seed_keywords'] as $keyword) {
                $keyword = sanitize_text_field((string) $keyword);
                if ($keyword !== '') {
                    $topics[] = $keyword;
                }
            }
        }
        $industry_hints = $this->aysa_adjustments_get_industry_keyword_hints($industry, $website_type);
        foreach ($industry_hints as $hint) {
            $hint = sanitize_text_field((string) $hint);
            if ($hint !== '') {
                $topics[] = $hint;
            }
        }
        $topics = array_values(array_unique(array_filter($topics, static function ($value) {
            return trim((string) $value) !== '';
        })));

        $authority_value = 0;
        if ($project_id > 0) {
            $authority_value = intval(get_option('aysa_domain_authority_score_' . $project_id, 0));
        }

        return array(
            'site_url' => esc_url_raw(home_url('/')),
            'site_domain' => preg_replace('/^www\./', '', wp_parse_url(home_url('/'), PHP_URL_HOST) ?: ''),
            'website_type' => $website_type,
            'industry' => $industry,
            'language' => $language,
            'country' => $country,
            'topics' => $topics,
            'authority_value' => max(0, $authority_value),
        );
    }

    private function aysa_exchange_insert_link_into_post($post_id, $target_url, $anchor_text, $exchange_ref = '') {
        $post = get_post($post_id);
        if (!($post instanceof WP_Post)) {
            return new WP_Error('exchange_source_missing', 'Selected source content was not found.');
        }
        if ($post->post_status !== 'publish') {
            return new WP_Error('exchange_source_not_publish', 'Selected source content must be published before activation.');
        }

        $content = (string) $post->post_content;
        if ($content === '') {
            return new WP_Error('exchange_source_empty', 'Selected source content has no editable body content.');
        }

        $target_url = esc_url_raw((string) $target_url);
        $anchor_text = sanitize_text_field((string) $anchor_text);
        if ($target_url === '' || $anchor_text === '') {
            return new WP_Error('exchange_target_invalid', 'Target URL and anchor text are required.');
        }

        $normalized_content = strtolower(html_entity_decode($content, ENT_QUOTES | ENT_HTML5, 'UTF-8'));
        $normalized_target = strtolower(html_entity_decode($target_url, ENT_QUOTES | ENT_HTML5, 'UTF-8'));
        $already_present = strpos($normalized_content, $normalized_target) !== false;

        $source_url = get_permalink($post);
        if (!$already_present) {
            $link_html = sprintf(
                '<a href="%s" class="aysa-exchange-link" rel="noopener sponsored" target="_blank" title="%s" data-aysa-exchange-ref="%s">%s</a>',
                esc_url($target_url),
                esc_attr($anchor_text),
                esc_attr((string) $exchange_ref),
                esc_html($anchor_text)
            );
            $partner_box = sprintf(
                '<div class="aysa-exchange-partners-box" data-aysa-exchange-ref="%s" style="margin:24px 0 0;padding:16px 18px;border:1px solid #d7dce2;border-radius:12px;background:#f8fafc;"><strong>Parteneri:</strong> %s</div>',
                esc_attr((string) $exchange_ref),
                $link_html
            );
            $updated_content = rtrim($content) . "\n\n" . $partner_box;
            if (!is_string($updated_content) || trim($updated_content) === '') {
                return new WP_Error('exchange_insert_failed', 'Could not prepare the reciprocal link insertion.');
            }

            $update_result = wp_update_post(array(
                'ID' => intval($post->ID),
                'post_content' => $updated_content,
            ), true);
            if (is_wp_error($update_result)) {
                return new WP_Error('exchange_post_update_failed', $update_result->get_error_message());
            }
        }

        return array(
            'source_post_id' => intval($post->ID),
            'source_url' => esc_url_raw((string) $source_url),
            'already_present' => $already_present,
        );
    }

    private function aysa_exchange_remove_link_from_post($post_id, $exchange_ref = '') {
        $post = get_post($post_id);
        if (!($post instanceof WP_Post)) {
            return new WP_Error('exchange_source_missing', 'Selected source content was not found.');
        }

        $content = (string) $post->post_content;
        if ($content === '' || $exchange_ref === '') {
            return array(
                'source_post_id' => intval($post->ID),
                'removed' => false,
            );
        }

        $pattern = '/\s*<div class="aysa-exchange-partners-box"[^>]*data-aysa-exchange-ref="' . preg_quote($exchange_ref, '/') . '"[^>]*>.*?<\/div>\s*/is';
        $updated_content = preg_replace($pattern, "\n\n", $content, -1, $count);
        if (!is_string($updated_content)) {
            return new WP_Error('exchange_remove_failed', 'Could not prepare reciprocal link removal.');
        }
        if ((int) $count <= 0) {
            return array(
                'source_post_id' => intval($post->ID),
                'removed' => false,
            );
        }

        $update_result = wp_update_post(array(
            'ID' => intval($post->ID),
            'post_content' => trim($updated_content),
        ), true);
        if (is_wp_error($update_result)) {
            return new WP_Error('exchange_post_update_failed', $update_result->get_error_message());
        }

        return array(
            'source_post_id' => intval($post->ID),
            'removed' => true,
        );
    }

    private function aysa_exchange_activation_grace_hours() {
        $hours = intval(apply_filters('aysa_exchange_activation_grace_hours', 48));
        return max(1, $hours);
    }

    private function aysa_exchange_activation_reminder_hours() {
        $hours = intval(apply_filters('aysa_exchange_activation_reminder_hours', 24));
        return max(1, $hours);
    }

    private function aysa_get_exchange_notification_state($project_id) {
        $state = get_option('aysa_exchange_notification_state_' . intval($project_id), array());
        return is_array($state) ? $state : array();
    }

    private function aysa_exchange_notification_sent($project_id, $key) {
        $state = $this->aysa_get_exchange_notification_state($project_id);
        return !empty($state[(string) $key]);
    }

    private function aysa_mark_exchange_notification_sent($project_id, $key) {
        $state = $this->aysa_get_exchange_notification_state($project_id);
        $state[(string) $key] = gmdate('c');
        update_option('aysa_exchange_notification_state_' . intval($project_id), $state, false);
    }

    private function aysa_exchange_send_activation_email($project_id, array $request, $reminder_type = 'activation') {
        $recipient = $this->get_notification_target_email();
        if ($recipient === '' || !is_email($recipient)) {
            return false;
        }

        $viewer_role = (string) ($request['viewer_role'] ?? '');
        $counterparty_domain = $viewer_role === 'requester'
            ? (string) ($request['counterparty_site_domain'] ?? '')
            : (string) ($request['requester_site_domain'] ?? '');
        $target_url = $viewer_role === 'requester'
            ? (string) ($request['requester_target_url'] ?? '')
            : (string) ($request['counterparty_target_url'] ?? '');
        $anchor_text = $viewer_role === 'requester'
            ? (string) ($request['requester_anchor_text'] ?? '')
            : (string) ($request['counterparty_anchor_text'] ?? '');
        $exchange_ref = (string) ($request['exchange_ref'] ?? '');
        $offpage_url = admin_url('admin.php?page=aysa-off-page-optimization');
        $is_ro = stripos((string) get_locale(), 'ro') === 0;

        if ($is_ro) {
            $subject = $reminder_type === 'reminder_24h'
                ? 'Reminder Link Exchange: activeaza linkul reciproc'
                : 'Link Exchange: cealalta parte a activat, activeaza si tu';
            $body_lines = array(
                'Exchange ref: ' . $exchange_ref,
                'Partener: ' . $counterparty_domain,
                'Ancora ceruta: ' . $anchor_text,
                'URL tinta: ' . $target_url,
                '',
                $reminder_type === 'reminder_24h'
                    ? 'Reminder: cealalta parte a activat deja. Activeaza si tu linkul reciproc din plugin.'
                    : 'Cealalta parte a activat deja linkul. Intra in plugin si activeaza si tu linkul reciproc.',
                'Deschide pluginul: ' . $offpage_url,
            );
        } else {
            $subject = $reminder_type === 'reminder_24h'
                ? 'Link Exchange reminder: activate your reciprocal link'
                : 'Link Exchange: the counterparty activated, now activate yours';
            $body_lines = array(
                'Exchange ref: ' . $exchange_ref,
                'Counterparty: ' . $counterparty_domain,
                'Required anchor: ' . $anchor_text,
                'Target URL: ' . $target_url,
                '',
                $reminder_type === 'reminder_24h'
                    ? 'Reminder: the counterparty already activated their side. Please activate your reciprocal link in the plugin.'
                    : 'The counterparty has already activated their side. Please activate your reciprocal link in the plugin.',
                'Open plugin: ' . $offpage_url,
            );
        }

        return (bool) wp_mail($recipient, $subject, implode("\n", $body_lines), array('Content-Type: text/plain; charset=UTF-8'));
    }

    private function aysa_exchange_find_latest_remote_activation_event(array $request) {
        $timeline = isset($request['timeline']) && is_array($request['timeline']) ? $request['timeline'] : array();
        if (empty($timeline)) {
            return null;
        }

        $viewer_role = (string) ($request['viewer_role'] ?? '');
        $other_domain = $viewer_role === 'requester'
            ? (string) ($request['counterparty_site_domain'] ?? '')
            : (string) ($request['requester_site_domain'] ?? '');
        for ($i = count($timeline) - 1; $i >= 0; $i--) {
            $event = $timeline[$i];
            if (!is_array($event) || (string) ($event['event_type'] ?? '') !== 'link_activated') {
                continue;
            }
            $payload = isset($event['payload']) && is_array($event['payload']) ? $event['payload'] : array();
            if ((string) ($payload['by'] ?? '') === $other_domain) {
                return $event;
            }
        }

        return null;
    }

    private function aysa_exchange_score_source_candidate($post, $target_url, $anchor_text) {
        if (!($post instanceof WP_Post)) {
            return array('eligible' => false, 'score' => 0, 'reasons' => array('Invalid source content.'));
        }

        $title = sanitize_text_field((string) get_the_title($post));
        $excerpt = sanitize_text_field(wp_strip_all_tags((string) ($post->post_excerpt ?: '')));
        $content = sanitize_text_field(wp_strip_all_tags((string) $post->post_content));
        $haystack = trim($title . ' ' . $excerpt . ' ' . wp_trim_words($content, 120, ''));

        $anchor_tokens = $this->aysa_exchange_tokenize_for_semantics($anchor_text);
        $target_tokens = $this->aysa_exchange_tokenize_for_semantics($target_url);
        $source_tokens = $this->aysa_exchange_tokenize_for_semantics($haystack);
        $title_tokens = $this->aysa_exchange_tokenize_for_semantics($title);

        $anchor_overlap = array_values(array_intersect($anchor_tokens, $source_tokens));
        $target_overlap = array_values(array_intersect($target_tokens, $source_tokens));
        $title_anchor_overlap = array_values(array_intersect($anchor_tokens, $title_tokens));

        $score = 0;
        $reasons = array();
        if (!empty($title_anchor_overlap)) {
            $score += 35;
            $reasons[] = 'Anchor aligns with the page title.';
        }
        if (!empty($anchor_overlap)) {
            $score += min(35, 10 + (count($anchor_overlap) * 8));
            $reasons[] = 'Anchor terms appear in the source content.';
        } else {
            $reasons[] = 'Anchor terms do not appear in the source content.';
        }
        if (!empty($target_overlap)) {
            $score += min(25, 8 + (count($target_overlap) * 6));
            $reasons[] = 'Target topic overlaps with the source content.';
        } else {
            $reasons[] = 'Target topic is weakly represented in the source content.';
        }
        if ((string) $post->post_type === 'product') {
            $score += 5;
        }

        return array(
            'eligible' => $score >= 35 && (!empty($anchor_overlap) || !empty($title_anchor_overlap)),
            'score' => max(0, min(100, intval($score))),
            'reasons' => $reasons,
        );
    }

    private function aysa_exchange_tokenize_for_semantics($value) {
        $value = strtolower((string) $value);
        $value = preg_replace('#https?://#', ' ', $value);
        $value = preg_replace('#[^a-z0-9ăâîșşțţ]+#iu', ' ', $value);
        $parts = preg_split('/\s+/', trim((string) $value));
        if (!is_array($parts)) {
            return array();
        }

        $tokens = array();
        $stop = array('si', 'și', 'de', 'la', 'in', 'în', 'cu', 'for', 'the', 'and', 'sau', 'ori', 'din', 'pe', 'un', 'una', 'unei', 'o', 'a', 'to');
        foreach ($parts as $part) {
            $part = trim((string) $part);
            if ($part === '' || mb_strlen($part) < 3 || in_array($part, $stop, true)) {
                continue;
            }
            $tokens[$part] = true;
        }
        return array_values(array_keys($tokens));
    }

    private function aysa_validate_exchange_request_access($request) {
        if (!is_array($request) || empty($request)) {
            return new WP_Error('exchange_request_missing', 'Exchange request not found.');
        }

        return true;
    }

    private function aysa_get_broker_config() {
        $default_base_url = class_exists('Aysa_API_Config') ? rtrim((string) Aysa_API_Config::get_api_base_url(), '/') : 'https://api.aysa.ai';
        $default_key_id = 'wp_plugin';
        $default_key_secret = 'kQ4v9dH2uR6xZ1mP8sT3fY7nB5cL0eW4';
        $base_url = defined('AYSA_OAUTH_BROKER_BASE_URL') ? AYSA_OAUTH_BROKER_BASE_URL : $default_base_url;
        $key_id = defined('AYSA_OAUTH_BROKER_KEY_ID') ? AYSA_OAUTH_BROKER_KEY_ID : $default_key_id;
        $key_secret = defined('AYSA_OAUTH_BROKER_KEY_SECRET') ? AYSA_OAUTH_BROKER_KEY_SECRET : $default_key_secret;

        $base_url = rtrim((string) $base_url, '/');
        $key_id = trim((string) $key_id);
        $key_secret = trim((string) $key_secret);

        if ($base_url === '' || $key_id === '' || $key_secret === '') {
            return new WP_Error('broker_config_missing', 'Broker config missing. Set AYSA_OAUTH_BROKER_BASE_URL, AYSA_OAUTH_BROKER_KEY_ID and AYSA_OAUTH_BROKER_KEY_SECRET.');
        }

        return array(
            'base_url' => $base_url,
            'key_id' => $key_id,
            'key_secret' => $key_secret,
        );
    }

    private function aysa_broker_request_async($method, $path, $payload = array(), $timeout = 1) {
        $cfg = $this->aysa_get_broker_config();
        if (is_wp_error($cfg)) {
            return $cfg;
        }

        $method = strtoupper((string) $method);
        $path = $this->aysa_broker_normalize_path((string) $path);
        $url = $cfg['base_url'] . $path;
        $body = ($method === 'POST') ? wp_json_encode($payload) : '';
        if ($body === false) {
            $body = '{}';
        }

        $timestamp = (string) time();
        try {
            $nonce = bin2hex(random_bytes(16));
        } catch (Exception $e) {
            $nonce = wp_generate_password(32, false, false);
        }

        $body_hash = hash('sha256', $body);
        $canonical = implode("\n", array($method, $path, $body_hash, $timestamp, $nonce));
        $signature = hash_hmac('sha256', $canonical, $cfg['key_secret']);

        $args = array(
            'timeout' => max(1, min(10, intval($timeout))),
            'blocking' => false,
            'method' => $method,
            'headers' => array(
                'Content-Type' => 'application/json',
                'X-Aysa-Key' => $cfg['key_id'],
                'X-Aysa-Timestamp' => $timestamp,
                'X-Aysa-Nonce' => $nonce,
                'X-Aysa-Signature' => $signature,
            ),
        );

        if ($method === 'POST') {
            $args['body'] = $body;
        }

        $resp = wp_remote_request($url, $args);
        if (is_wp_error($resp)) {
            return $resp;
        }

        return array('ok' => true);
    }

    private function aysa_should_queue_setup_competition_suggestion($mode, $current_step, $user_message) {
        if ((string) $mode !== 'message' || (string) $current_step !== 'competition') {
            return false;
        }

        if (!empty($_POST['async_competition_suggestion'])) {
            return true;
        }

        $normalized = strtolower(trim((string) $user_message));
        if ($normalized === '') {
            return false;
        }

        if (
            strpos($normalized, 'sugereaza tu') !== false
            || strpos($normalized, 'sugerează tu') !== false
            || strpos($normalized, 'suggest for me') !== false
            || strpos($normalized, 'suggest them for me') !== false
        ) {
            return true;
        }

        return (bool) preg_match('/^(nu stiu|nu știu|i do not know|idk)\b/u', $normalized);
    }

    private function aysa_setup_competition_async_ack($language) {
        $lang = in_array((string) $language, array('ro', 'bg'), true) ? (string) $language : 'en';
        return match ($lang) {
            'ro' => 'Perfect. Acum relaxează-te, bea o cafea sau ieși puțin la aer. Eu pornesc research-ul pentru seed keywords și competitori, lucrez în fundal pentru tine și te anunț când e gata aici în WordPress, iar dacă am emailul disponibil îți trimit și email cu link direct spre plugin.',
            'bg' => 'Чудесно. Сега се отпусни, изпий едно кафе или излез за малко. Аз стартирам research-а за seed keywords и конкуренти, работя във фонов режим за теб и ще те уведомя тук в WordPress, а ако имам имейла ти, ще изпратя и имейл с директен линк към plugin-а.',
            default => 'Perfect. Now relax, grab a coffee, or take a short walk. I am starting the seed keyword and competitor research in the background, I will work on it for you, and I will notify you here in WordPress and by email with a direct plugin link if an email is available.',
        };
    }

    private function aysa_broker_request($method, $path, $payload = array(), $timeout = 30) {
        $cfg = $this->aysa_get_broker_config();
        if (is_wp_error($cfg)) {
            return $cfg;
        }

        $method = strtoupper((string) $method);
        $path = $this->aysa_broker_normalize_path((string) $path);
        $url = $cfg['base_url'] . $path;
        $body = ($method === 'POST') ? wp_json_encode($payload) : '';
        if ($body === false) {
            $body = '{}';
        }

        $timestamp = (string) time();
        try {
            $nonce = bin2hex(random_bytes(16));
        } catch (Exception $e) {
            $nonce = wp_generate_password(32, false, false);
        }

        $body_hash = hash('sha256', $body);
        $canonical = implode("\n", array($method, $path, $body_hash, $timestamp, $nonce));
        $signature = hash_hmac('sha256', $canonical, $cfg['key_secret']);

        $args = array(
            'timeout' => max(10, min(120, intval($timeout))),
            'method' => $method,
            'headers' => array(
                'Content-Type' => 'application/json',
                'X-Aysa-Key' => $cfg['key_id'],
                'X-Aysa-Timestamp' => $timestamp,
                'X-Aysa-Nonce' => $nonce,
                'X-Aysa-Signature' => $signature,
            ),
        );

        if ($method === 'POST') {
            $args['body'] = $body;
        }

        $resp = wp_remote_request($url, $args);
        if (is_wp_error($resp)) {
            return new WP_Error('broker_http_failed', 'Broker request failed: ' . $resp->get_error_message());
        }

        $code = wp_remote_retrieve_response_code($resp);
        $raw_body = (string) wp_remote_retrieve_body($resp);
        $content_type = (string) wp_remote_retrieve_header($resp, 'content-type');
        $decoded = json_decode($raw_body, true);
        if (!is_array($decoded)) {
            $preview = trim(preg_replace('/\s+/', ' ', wp_strip_all_tags($raw_body)));
            if ($preview !== '') {
                $preview = substr($preview, 0, 280);
            }
            $message = 'Broker returned invalid JSON response. [code=' . intval($code) . ']';
            if ($content_type !== '') {
                $message .= ' [content-type=' . $content_type . ']';
            }
            if ($preview !== '') {
                $message .= ' ' . $preview;
            }
            return new WP_Error('broker_invalid_response', $message);
        }

        if ($code < 200 || $code >= 300 || (isset($decoded['ok']) && !$decoded['ok'])) {
            $message = isset($decoded['error']) ? (string) $decoded['error'] : 'Broker returned an error.';
            return new WP_Error('broker_error', $message . ' [code=' . intval($code) . ']');
        }

        $usage = isset($decoded['usage']) && is_array($decoded['usage']) ? $decoded['usage'] : array();
        if (!empty($usage)) {
            $project_id = 0;
            if (is_array($payload) && isset($payload['project_id'])) {
                $project_id = intval($payload['project_id']);
            }
            if ($project_id <= 0) {
                $project_id = intval(get_option('aysa_project_id', 0));
            }

            $action_key = $this->aysa_usage_action_key_from_path($path);
            $reference = ltrim((string) $path, '/');
            $this->aysa_wallet_log_endpoint_usage(
                $project_id,
                (string) $path,
                $usage,
                $action_key,
                $reference,
                array(
                    'http_method' => $method,
                    'response_code' => intval($code),
                )
            );
        }

        return $decoded;
    }

    private function aysa_broker_normalize_path($path) {
        $path = '/' . ltrim((string) $path, '/');
        if ($path !== '/' && substr($path, -1) !== '/') {
            $path .= '/';
        }
        return $path;
    }

    private function aysa_humanize_broker_error($error, $fallback = 'Broker temporarily unavailable. Please retry later.') {
        if (!is_wp_error($error)) {
            return (string) $fallback;
        }

        $code = (string) $error->get_error_code();
        $message = (string) $error->get_error_message();
        if ($code === 'broker_http_failed' && stripos($message, 'cURL error 7') !== false) {
            return 'API broker is temporarily unavailable. Please retry in a moment.';
        }
        if ($code === 'broker_invalid_response') {
            return 'API broker returned an invalid response. Please retry; if it persists, investigate the live API service.';
        }
        if (stripos($message, '/usr/bin/expect') !== false && stripos($message, 'not found') !== false) {
            return 'Site Audit is temporarily unavailable because the API server is missing the crawler dependency. Install the missing package on api.aysa.ai, then retry.';
        }
        if (stripos($message, 'permission denied (publickey)') !== false) {
            return 'Site Audit cannot start because the crawler host rejects the current SSH credentials. Configure valid key-based SSH access for the crawler host, then retry.';
        }

        return ($message !== '') ? $message : (string) $fallback;
    }

    private function aysa_should_use_local_site_audit_fallback($error) {
        if (!is_wp_error($error)) {
            return false;
        }

        $message = strtolower((string) $error->get_error_message());
        if ($message === '') {
            return false;
        }

        $needles = array(
            'permission denied (publickey)',
            '/usr/bin/expect',
            'broker_http_failed',
            'local api broker is offline',
            'failed to start site audit',
            'could not start site audit',
        );

        foreach ($needles as $needle) {
            if (strpos($message, strtolower($needle)) !== false) {
                return true;
            }
        }

        return false;
    }

    private function aysa_local_site_audit_collect_urls($target_url, $limit = 40) {
        $limit = max(10, min(120, intval($limit)));
        $urls = array();
        $home = home_url('/');
        if ($home !== '') {
            $urls[$this->aysa_local_site_audit_normalize_url($home)] = array(
                'url' => $home,
                'title' => get_bloginfo('name'),
            );
        }

        $public_types = get_post_types(array(
            'public' => true,
        ), 'names');
        unset($public_types['attachment']);

        $posts = get_posts(array(
            'post_type' => array_values($public_types),
            'post_status' => 'publish',
            'posts_per_page' => $limit,
            'orderby' => 'date',
            'order' => 'DESC',
            'fields' => 'ids',
            'no_found_rows' => true,
            'suppress_filters' => false,
        ));

        foreach ($posts as $post_id) {
            $url = get_permalink($post_id);
            if (!$url) {
                continue;
            }
            $normalized = $this->aysa_local_site_audit_normalize_url($url);
            if ($normalized === '') {
                continue;
            }
            $urls[$normalized] = array(
                'url' => $url,
                'title' => get_the_title($post_id),
                'post_id' => intval($post_id),
            );
            if (count($urls) >= $limit) {
                break;
            }
        }

        if ($target_url !== '') {
            $normalizedTarget = $this->aysa_local_site_audit_normalize_url($target_url);
            if ($normalizedTarget !== '') {
                $urls = array($normalizedTarget => array(
                    'url' => $target_url,
                    'title' => isset($urls[$normalizedTarget]['title']) ? $urls[$normalizedTarget]['title'] : '',
                )) + $urls;
            }
        }

        return array_values(array_slice($urls, 0, $limit, true));
    }

    private function aysa_local_site_audit_normalize_url($url) {
        $url = trim((string) $url);
        if ($url === '') {
            return '';
        }

        $parts = wp_parse_url($url);
        if (!is_array($parts) || empty($parts['host'])) {
            return '';
        }

        $scheme = !empty($parts['scheme']) ? strtolower((string) $parts['scheme']) : 'https';
        $host = strtolower((string) $parts['host']);
        $path = isset($parts['path']) ? (string) $parts['path'] : '/';
        if ($path === '') {
            $path = '/';
        }
        $path = '/' . ltrim($path, '/');
        if ($path !== '/') {
            $path = untrailingslashit($path);
        }

        return $scheme . '://' . $host . $path;
    }

    private function aysa_local_site_audit_extract_html_metrics($html, $base_url) {
        $metrics = array(
            'title' => '',
            'meta_description' => '',
            'canonical' => '',
            'indexability' => 'Indexable',
            'h1_count' => 0,
            'h2_count' => 0,
            'internal_links' => array(),
        );

        if (!is_string($html) || trim($html) === '') {
            return $metrics;
        }

        if (!class_exists('DOMDocument')) {
            return $metrics;
        }

        $dom = new DOMDocument();
        $previous = libxml_use_internal_errors(true);
        $loaded = $dom->loadHTML($html);
        libxml_clear_errors();
        libxml_use_internal_errors($previous);

        if (!$loaded) {
            return $metrics;
        }

        $titles = $dom->getElementsByTagName('title');
        if ($titles->length > 0) {
            $metrics['title'] = trim((string) $titles->item(0)->textContent);
        }

        $base_host = wp_parse_url($base_url, PHP_URL_HOST);
        $metaTags = $dom->getElementsByTagName('meta');
        foreach ($metaTags as $meta) {
            $name = strtolower(trim((string) $meta->getAttribute('name')));
            $content = trim((string) $meta->getAttribute('content'));
            if ($name === 'description' && $metrics['meta_description'] === '') {
                $metrics['meta_description'] = $content;
            }
            if ($name === 'robots' && stripos($content, 'noindex') !== false) {
                $metrics['indexability'] = 'Non-Indexable';
            }
        }

        $links = $dom->getElementsByTagName('link');
        foreach ($links as $link) {
            $rel = strtolower(trim((string) $link->getAttribute('rel')));
            $href = trim((string) $link->getAttribute('href'));
            if ($rel === 'canonical' && $metrics['canonical'] === '') {
                $metrics['canonical'] = $href;
            }
        }

        $metrics['h1_count'] = $dom->getElementsByTagName('h1')->length;
        $metrics['h2_count'] = $dom->getElementsByTagName('h2')->length;

        $anchors = $dom->getElementsByTagName('a');
        foreach ($anchors as $anchor) {
            $href = trim((string) $anchor->getAttribute('href'));
            if ($href === '' || strpos($href, '#') === 0 || stripos($href, 'mailto:') === 0 || stripos($href, 'tel:') === 0) {
                continue;
            }
            $absolute = $href;
            if (strpos($href, 'http://') !== 0 && strpos($href, 'https://') !== 0) {
                $absolute = home_url('/' . ltrim($href, '/'));
            }
            $absolute = $this->aysa_local_site_audit_normalize_url($absolute);
            if ($absolute === '') {
                continue;
            }
            $host = wp_parse_url($absolute, PHP_URL_HOST);
            if ($base_host && $host && strtolower((string) $host) === strtolower((string) $base_host)) {
                $metrics['internal_links'][] = $absolute;
            }
        }

        $metrics['internal_links'] = array_values(array_unique($metrics['internal_links']));

        return $metrics;
    }

    private function aysa_local_site_audit_build($project_id, $target_url, $job_id) {
        $targets = $this->aysa_local_site_audit_collect_urls($target_url, 40);
        $pages = array();
        $inlinkMap = array();
        $titleCounts = array();
        $metaMissing = array();
        $canonicalMissing = array();
        $multipleH1 = array();
        $errorPages = array();
        $nonIndexable = 0;
        $depthGt3 = 0;

        foreach ($targets as $target) {
            $url = isset($target['url']) ? (string) $target['url'] : '';
            $normalizedUrl = $this->aysa_local_site_audit_normalize_url($url);
            if ($normalizedUrl === '') {
                continue;
            }

            $response = wp_remote_get($url, array(
                'timeout' => 12,
                'redirection' => 3,
                'headers' => array(
                    'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
                    'User-Agent' => 'AYSA Site Audit/1.0',
                ),
            ));

            $statusCode = 0;
            $body = '';
            if (!is_wp_error($response)) {
                $statusCode = intval(wp_remote_retrieve_response_code($response));
                $body = (string) wp_remote_retrieve_body($response);
            }

            $metrics = $this->aysa_local_site_audit_extract_html_metrics($body, $url);
            $title = $metrics['title'] !== '' ? $metrics['title'] : (string) ($target['title'] ?? '');
            $indexability = (string) ($metrics['indexability'] ?? 'Indexable');
            $path = (string) wp_parse_url($normalizedUrl, PHP_URL_PATH);
            $depth = 0;
            if ($path !== '' && $path !== '/') {
                $segments = array_values(array_filter(explode('/', trim($path, '/'))));
                $depth = count($segments);
            }

            $pages[$normalizedUrl] = array(
                'url' => $url,
                'title' => $title,
                'status_code' => $statusCode > 0 ? $statusCode : 0,
                'indexability' => $indexability,
                'crawl_depth' => $depth,
                'inlinks' => 0,
            );

            if ($title !== '') {
                $titleKey = strtolower(trim($title));
                $titleCounts[$titleKey] = isset($titleCounts[$titleKey]) ? intval($titleCounts[$titleKey]) + 1 : 1;
            }
            if ($metrics['meta_description'] === '') {
                $metaMissing[] = $normalizedUrl;
            }
            if ($metrics['canonical'] === '') {
                $canonicalMissing[] = $normalizedUrl;
            }
            if (intval($metrics['h1_count']) > 1) {
                $multipleH1[] = $normalizedUrl;
            }
            if ($statusCode >= 400) {
                $errorPages[] = $normalizedUrl;
            }
            if (stripos($indexability, 'non-indexable') !== false) {
                $nonIndexable++;
            }
            if ($depth > 3) {
                $depthGt3++;
            }

            foreach ((array) $metrics['internal_links'] as $linkedUrl) {
                if (!isset($inlinkMap[$linkedUrl])) {
                    $inlinkMap[$linkedUrl] = 0;
                }
                $inlinkMap[$linkedUrl]++;
            }
        }

        $singleInlink = 0;
        $statusBuckets = array(
            '2xx' => 0,
            '3xx' => 0,
            '4xx' => 0,
            '5xx' => 0,
            'other' => 0,
        );
        foreach ($pages as $normalizedUrl => &$page) {
            $page['inlinks'] = intval($inlinkMap[$normalizedUrl] ?? 0);
            if ($page['inlinks'] <= 1) {
                $singleInlink++;
            }
            $status = intval($page['status_code']);
            if ($status >= 200 && $status < 300) {
                $statusBuckets['2xx']++;
            } elseif ($status >= 300 && $status < 400) {
                $statusBuckets['3xx']++;
            } elseif ($status >= 400 && $status < 500) {
                $statusBuckets['4xx']++;
            } elseif ($status >= 500 && $status < 600) {
                $statusBuckets['5xx']++;
            } else {
                $statusBuckets['other']++;
            }
        }
        unset($page);

        $pagesList = array_values($pages);
        $pagesCrawled = count($pagesList);
        $warnings = count($metaMissing) + count($canonicalMissing) + count($multipleH1) + $depthGt3 + $singleInlink;
        $errors = count($errorPages);
        $siteHealth = 100;
        if ($pagesCrawled > 0) {
            $severityScore = ($errors * 3) + ($warnings * 1.5);
            $siteHealth = max(1, intval(round(100 - (($severityScore / max(1, $pagesCrawled)) * 100))));
        }

        $duplicateTitleUrls = array();
        foreach ($pagesList as $page) {
            $titleKey = strtolower(trim((string) ($page['title'] ?? '')));
            if ($titleKey !== '' && intval($titleCounts[$titleKey] ?? 0) > 1) {
                $duplicateTitleUrls[] = (string) ($page['url'] ?? '');
            }
        }

        $issues = array();
        $issues = array_merge($issues, $this->aysa_local_site_audit_issue_from_urls(
            'error',
            'http_status_4xx_5xx',
            'Pages with 4xx/5xx status',
            'Broken or failing URLs found in the local crawl.',
            $errorPages,
            $pagesList
        ));
        $issues = array_merge($issues, $this->aysa_local_site_audit_issue_from_urls(
            'warning',
            'missing_meta_descriptions',
            'Pages missing meta descriptions',
            'Add unique meta descriptions to improve search snippets.',
            $metaMissing,
            $pagesList
        ));
        $issues = array_merge($issues, $this->aysa_local_site_audit_issue_from_urls(
            'warning',
            'missing_canonicals',
            'Pages missing canonical tags',
            'Add canonical tags to clarify the preferred URL.',
            $canonicalMissing,
            $pagesList
        ));
        $issues = array_merge($issues, $this->aysa_local_site_audit_issue_from_urls(
            'notice',
            'multiple_h1',
            'Pages with multiple H1 headings',
            'Review heading structure and keep one primary H1 per page.',
            $multipleH1,
            $pagesList
        ));
        $issues = array_merge($issues, $this->aysa_local_site_audit_issue_from_urls(
            'warning',
            'duplicate_titles',
            'Pages with duplicate titles',
            'Use unique page titles to avoid cannibalization and weak click-through.',
            $duplicateTitleUrls,
            $pagesList
        ));

        $resultFiles = array(
            array('filename' => 'internal-pages-local.csv', 'type' => 'csv', 'row_count' => $pagesCrawled, 'updated_at' => current_time('mysql'), 'size_bytes' => 0),
            array('filename' => 'page-titles-local.csv', 'type' => 'csv', 'row_count' => max(0, count($duplicateTitleUrls)), 'updated_at' => current_time('mysql'), 'size_bytes' => 0),
            array('filename' => 'meta-descriptions-local.csv', 'type' => 'csv', 'row_count' => max(0, count($metaMissing)), 'updated_at' => current_time('mysql'), 'size_bytes' => 0),
            array('filename' => 'canonicals-local.csv', 'type' => 'csv', 'row_count' => max(0, count($canonicalMissing)), 'updated_at' => current_time('mysql'), 'size_bytes' => 0),
            array('filename' => 'directives-local.csv', 'type' => 'csv', 'row_count' => $nonIndexable, 'updated_at' => current_time('mysql'), 'size_bytes' => 0),
            array('filename' => 'links-local.csv', 'type' => 'csv', 'row_count' => $singleInlink, 'updated_at' => current_time('mysql'), 'size_bytes' => 0),
        );

        return array(
            'state' => array(
                'project_id' => intval($project_id),
                'job_id' => (string) $job_id,
                'target_url' => (string) $target_url,
                'status' => 'completed',
                'provider' => 'local_wordpress',
                'started_at' => current_time('mysql'),
                'updated_at' => current_time('mysql'),
                'result_count' => count($resultFiles),
                'job_dir' => 'local-wordpress://site-audit/' . sanitize_key((string) $job_id),
                'latest_output_dir' => 'local-wordpress://site-audit/' . sanitize_key((string) $job_id),
                'audit_snapshot' => array(
                    'overview' => array(
                        'pages_crawled' => $pagesCrawled,
                        'site_health' => $siteHealth,
                        'errors' => $errors,
                        'warnings' => $warnings,
                        'notices' => max(0, count($issues) - $errors),
                        'status_codes' => $statusBuckets,
                        'indexable_pages' => max(0, $pagesCrawled - $nonIndexable),
                        'non_indexable_pages' => $nonIndexable,
                        'deep_pages_over_3' => $depthGt3,
                        'single_inlink_pages' => $singleInlink,
                    ),
                    'issues' => array_values($issues),
                    'pages' => array_values($pagesList),
                    'statistics' => array(
                        'exports' => array(
                            'internal' => $pagesCrawled,
                            'page_titles' => count($duplicateTitleUrls),
                            'meta_descriptions' => count($metaMissing),
                            'canonicals' => count($canonicalMissing),
                            'directives' => $nonIndexable,
                            'links' => $singleInlink,
                        ),
                    ),
                ),
                'result_files' => $resultFiles,
            ),
        );
    }

    private function aysa_local_site_audit_issue_from_urls($severity, $key, $title, $description, $urls, $pagesList) {
        $urls = array_values(array_unique(array_filter(array_map('strval', (array) $urls))));
        if (empty($urls)) {
            return array();
        }

        $pageMap = array();
        foreach ((array) $pagesList as $page) {
            if (!is_array($page) || empty($page['url'])) {
                continue;
            }
            $pageMap[$this->aysa_local_site_audit_normalize_url((string) $page['url'])] = $page;
        }

        $samples = array();
        foreach ($urls as $url) {
            $normalized = $this->aysa_local_site_audit_normalize_url($url);
            $page = isset($pageMap[$normalized]) ? $pageMap[$normalized] : array('url' => $url);
            $samples[] = array(
                'url' => (string) ($page['url'] ?? $url),
                'title' => (string) ($page['title'] ?? ''),
                'status_code' => intval($page['status_code'] ?? 0),
                'indexability' => (string) ($page['indexability'] ?? ''),
                'crawl_depth' => intval($page['crawl_depth'] ?? 0),
                'inlinks' => intval($page['inlinks'] ?? 0),
            );
            if (count($samples) >= 80) {
                break;
            }
        }

        return array(
            array(
                'severity' => sanitize_key((string) $severity),
                'key' => sanitize_key((string) $key),
                'title' => sanitize_text_field((string) $title),
                'affected_pages' => count($urls),
                'description' => sanitize_text_field((string) $description),
                'details' => array(
                    'source' => 'local_wordpress',
                    'filename' => sanitize_key((string) $key) . '.csv',
                    'sample_rows' => $samples,
                    'sample_columns' => array('url', 'title', 'status_code', 'indexability', 'crawl_depth', 'inlinks'),
                ),
            ),
        );
    }

    private function aysa_technical_site_audit_state_key($project_id) {
        return 'aysa_technical_site_audit_state_' . intval($project_id);
    }

    private function aysa_technical_site_audit_issue_lifecycle_key($project_id) {
        return 'aysa_technical_site_audit_issue_lifecycle_' . intval($project_id);
    }

    private function aysa_technical_site_audit_issue_id($key, $title, $category) {
        $base = sanitize_key((string) $key);
        if ($base === '') {
            $base = sanitize_key((string) $category . '_' . substr(md5((string) $title), 0, 10));
        }
        return $base;
    }

    private function aysa_technical_site_audit_get_issue_lifecycle_map($project_id) {
        $project_id = $this->aysa_resolve_technical_project_id(intval($project_id));
        $state = get_option($this->aysa_technical_site_audit_issue_lifecycle_key($project_id), array());
        return is_array($state) ? $state : array();
    }

    private function aysa_technical_site_audit_update_issue_lifecycle($project_id, $issue_id, $lifecycle_state) {
        $project_id = $this->aysa_resolve_technical_project_id(intval($project_id));
        $issue_id = sanitize_text_field((string) $issue_id);
        $lifecycle_state = sanitize_key((string) $lifecycle_state);
        if ($project_id <= 0 || $issue_id === '') {
            return array('applied' => false, 'reason' => 'invalid_issue_lifecycle_context');
        }
        if (!in_array($lifecycle_state, array('open', 'accepted', 'ignored', 'resolved', 'verified'), true)) {
            return array('applied' => false, 'reason' => 'invalid_lifecycle_state');
        }

        $state = $this->aysa_technical_site_audit_get_issue_lifecycle_map($project_id);
        if ($lifecycle_state === 'open') {
            unset($state[$issue_id]);
        } else {
            $state[$issue_id] = array(
                'state' => $lifecycle_state,
                'updated_at' => current_time('mysql'),
                'updated_at_unix' => time(),
            );
        }
        update_option($this->aysa_technical_site_audit_issue_lifecycle_key($project_id), $state, false);

        return array(
            'applied' => true,
            'issue_id' => $issue_id,
            'lifecycle_state' => $lifecycle_state,
        );
    }

    private function aysa_resolve_technical_project_id($project_id = 0, $site_host = '') {
        $hint_project_id = intval($project_id);
        $site_host = strtolower(trim((string) $site_host));
        if ($site_host === '') {
            $site_host = (string) wp_parse_url(home_url('/'), PHP_URL_HOST);
            $site_host = strtolower(trim((string) $site_host));
        }

        $key_validation = null;
        if ($this->aysa_is_access_key_valid_globally($key_validation)) {
            $candidate = $hint_project_id;
            $canonical_project_id = intval($this->aysa_resolve_agent8_canonical_project_id(
                $candidate,
                $site_host,
                is_array($key_validation) ? $key_validation : array()
            ));
            if ($canonical_project_id > 0) {
                return $canonical_project_id;
            }
        }

        return max(0, $hint_project_id);
    }

    private function aysa_technical_site_audit_snapshot_has_data($snapshot) {
        if (!is_array($snapshot) || empty($snapshot)) {
            return false;
        }

        $overview_rows = isset($snapshot['overview_rows']) && is_array($snapshot['overview_rows']) ? $snapshot['overview_rows'] : array();
        $issue_rows = isset($snapshot['issue_rows']) && is_array($snapshot['issue_rows'])
            ? $snapshot['issue_rows']
            : (isset($snapshot['rows']) && is_array($snapshot['rows']) ? $snapshot['rows'] : array());
        $page_rows = isset($snapshot['page_rows']) && is_array($snapshot['page_rows'])
            ? $snapshot['page_rows']
            : (isset($snapshot['pages']) && is_array($snapshot['pages']) ? $snapshot['pages'] : array());
        $file_rows = isset($snapshot['file_rows']) && is_array($snapshot['file_rows'])
            ? $snapshot['file_rows']
            : (isset($snapshot['files']) && is_array($snapshot['files']) ? $snapshot['files'] : array());

        return $overview_rows !== array()
            || $issue_rows !== array()
            || $page_rows !== array()
            || $file_rows !== array()
            || intval($snapshot['pages_crawled'] ?? 0) > 0
            || intval($snapshot['count'] ?? 0) > 0;
    }

    private function aysa_technical_site_audit_merge_local_state($project_id, $incoming_state, $reset_local_fields = false) {
        $project_id = $this->aysa_resolve_technical_project_id(intval($project_id));
        $incoming_state = is_array($incoming_state) ? $incoming_state : array();
        if ($project_id <= 0) {
            return $incoming_state;
        }

        if ($reset_local_fields) {
            unset($incoming_state['action_bundles']);
            return $incoming_state;
        }

        $current_state = get_option($this->aysa_technical_site_audit_state_key($project_id), array());
        if (!is_array($current_state) || empty($current_state)) {
            $latest_version = $this->aysa_sf_site_audit_get_latest_version_row($project_id);
            if (is_array($latest_version) && !empty($latest_version)) {
                $current_state = $this->aysa_sf_site_audit_build_state_from_version_row($latest_version);
            }
        }
        if (!is_array($current_state) || empty($current_state)) {
            return $incoming_state;
        }

        if (isset($current_state['action_bundles']) && is_array($current_state['action_bundles']) && !empty($current_state['action_bundles'])) {
            $incoming_state['action_bundles'] = $current_state['action_bundles'];
        }

        return $incoming_state;
    }

    public function aysa_sf_site_audit_versions_maybe_create_table() {
        $this->aysa_sf_site_audit_versions_create_table();
    }

    private function aysa_sf_site_audit_versions_table_name() {
        global $wpdb;
        return $wpdb->prefix . 'aysa_sf_site_audit_versions';
    }

    private function aysa_sf_site_audit_versions_create_table() {
        global $wpdb;

        static $ready = false;
        if ($ready) {
            return;
        }

        $table = $this->aysa_sf_site_audit_versions_table_name();
        $charset_collate = $wpdb->get_charset_collate();
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';

        $sql = "CREATE TABLE IF NOT EXISTS {$table} (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
            project_id BIGINT UNSIGNED NOT NULL,
            version_number INT UNSIGNED NOT NULL,
            job_id VARCHAR(191) NOT NULL,
            mode VARCHAR(64) NOT NULL DEFAULT 'baseline_full',
            provider VARCHAR(64) NOT NULL DEFAULT 'screamingfrog',
            status VARCHAR(32) NOT NULL DEFAULT 'completed',
            target_url TEXT NULL,
            job_dir TEXT NULL,
            latest_output_dir TEXT NULL,
            result_count INT UNSIGNED NOT NULL DEFAULT 0,
            pages_crawled INT UNSIGNED NOT NULL DEFAULT 0,
            issue_count INT UNSIGNED NOT NULL DEFAULT 0,
            site_health INT UNSIGNED NOT NULL DEFAULT 0,
            snapshot_json LONGTEXT NOT NULL,
            files_json LONGTEXT NOT NULL,
            state_json LONGTEXT NOT NULL,
            created_at DATETIME NOT NULL,
            updated_at DATETIME NOT NULL,
            completed_at DATETIME NULL,
            PRIMARY KEY (id),
            UNIQUE KEY project_version_unique (project_id, version_number),
            UNIQUE KEY project_job_unique (project_id, job_id),
            KEY project_id_idx (project_id),
            KEY status_idx (status),
            KEY completed_at_idx (completed_at),
            KEY updated_at_idx (updated_at)
        ) {$charset_collate};";

        dbDelta($sql);
        $ready = true;
    }

    private function aysa_sf_site_audit_version_history_count($project_id) {
        global $wpdb;

        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return 0;
        }

        $this->aysa_sf_site_audit_versions_create_table();
        $table = $this->aysa_sf_site_audit_versions_table_name();
        $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($exists !== $table) {
            return 0;
        }

        return intval($wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$table} WHERE project_id = %d",
            $project_id
        )));
    }

    private function aysa_sf_site_audit_get_latest_version_row($project_id) {
        global $wpdb;

        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return null;
        }

        $this->aysa_sf_site_audit_versions_create_table();
        $table = $this->aysa_sf_site_audit_versions_table_name();
        $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($exists !== $table) {
            return null;
        }

        $row = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$table} WHERE project_id = %d ORDER BY version_number DESC LIMIT 1",
            $project_id
        ), ARRAY_A);

        return is_array($row) ? $row : null;
    }

    private function aysa_sf_site_audit_decode_json_field($value, $default = array()) {
        if (!is_string($value) || $value === '') {
            return $default;
        }
        $decoded = json_decode($value, true);
        return is_array($decoded) ? $decoded : $default;
    }

    private function aysa_sf_site_audit_build_state_from_version_row($row) {
        if (!is_array($row) || empty($row)) {
            return array();
        }

        $state = $this->aysa_sf_site_audit_decode_json_field($row['state_json'] ?? '', array());
        $audit = $this->aysa_sf_site_audit_decode_json_field($row['snapshot_json'] ?? '', array());
        $files = $this->aysa_sf_site_audit_decode_json_field($row['files_json'] ?? '', array());

        $state['project_id'] = intval($row['project_id'] ?? ($state['project_id'] ?? 0));
        $state['job_id'] = (string) ($row['job_id'] ?? ($state['job_id'] ?? ''));
        $state['status'] = (string) ($row['status'] ?? ($state['status'] ?? 'completed'));
        $state['mode'] = (string) ($row['mode'] ?? ($state['mode'] ?? 'baseline_full'));
        $state['provider'] = (string) ($row['provider'] ?? ($state['provider'] ?? 'screamingfrog'));
        $state['target_url'] = (string) ($row['target_url'] ?? ($state['target_url'] ?? ''));
        $state['job_dir'] = (string) ($row['job_dir'] ?? ($state['job_dir'] ?? ''));
        $state['latest_output_dir'] = (string) ($row['latest_output_dir'] ?? ($state['latest_output_dir'] ?? ''));
        $state['result_count'] = intval($row['result_count'] ?? ($state['result_count'] ?? 0));
        $state['audit_snapshot'] = $audit;
        $state['result_files'] = is_array($files) ? array_values($files) : array();
        $state['version_id'] = intval($row['id'] ?? 0);
        $state['version_number'] = intval($row['version_number'] ?? 0);
        $state['version_created_at'] = (string) ($row['created_at'] ?? '');
        $state['version_completed_at'] = (string) ($row['completed_at'] ?? '');

        return $state;
    }

    private function aysa_sf_site_audit_persist_version($project_id, $state) {
        global $wpdb;

        $project_id = intval($project_id);
        if ($project_id <= 0 || !is_array($state) || empty($state)) {
            return 0;
        }

        $job_id = sanitize_text_field((string) ($state['job_id'] ?? ''));
        if ($job_id === '') {
            return 0;
        }

        $audit = !empty($state['audit_snapshot']) && is_array($state['audit_snapshot']) ? $state['audit_snapshot'] : array();
        $files = !empty($state['result_files']) && is_array($state['result_files']) ? array_values($state['result_files']) : array();
        if (empty($audit) && empty($files)) {
            return 0;
        }

        $this->aysa_sf_site_audit_versions_create_table();
        $table = $this->aysa_sf_site_audit_versions_table_name();
        $exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($exists !== $table) {
            return 0;
        }

        $overview = !empty($audit['overview']) && is_array($audit['overview']) ? $audit['overview'] : array();
        $created_at = current_time('mysql');
        $completed_at = null;
        $status = sanitize_key((string) ($state['status'] ?? 'completed'));
        if ($status === 'completed') {
            $completed_at = current_time('mysql');
        }

        $snapshot_json = wp_json_encode($audit);
        $files_json = wp_json_encode($files);
        $state_json = wp_json_encode($state);
        if ($snapshot_json === false || $files_json === false || $state_json === false) {
            return 0;
        }

        $existing = $wpdb->get_row($wpdb->prepare(
            "SELECT id, version_number, created_at, completed_at FROM {$table} WHERE project_id = %d AND job_id = %s LIMIT 1",
            $project_id,
            $job_id
        ), ARRAY_A);

        if (is_array($existing) && !empty($existing['id'])) {
            $wpdb->update(
                $table,
                array(
                    'mode' => sanitize_text_field((string) ($state['mode'] ?? 'baseline_full')),
                    'provider' => sanitize_text_field((string) ($state['provider'] ?? 'screamingfrog')),
                    'status' => $status !== '' ? $status : 'completed',
                    'target_url' => esc_url_raw((string) ($state['target_url'] ?? '')),
                    'job_dir' => sanitize_text_field((string) ($state['job_dir'] ?? '')),
                    'latest_output_dir' => sanitize_text_field((string) ($state['latest_output_dir'] ?? '')),
                    'result_count' => count($files),
                    'pages_crawled' => intval($overview['pages_crawled'] ?? 0),
                    'issue_count' => count(!empty($audit['issues']) && is_array($audit['issues']) ? $audit['issues'] : array()),
                    'site_health' => intval($overview['site_health'] ?? 0),
                    'snapshot_json' => $snapshot_json,
                    'files_json' => $files_json,
                    'state_json' => $state_json,
                    'updated_at' => current_time('mysql'),
                    'completed_at' => $completed_at ?: ($existing['completed_at'] ?? null),
                ),
                array('id' => intval($existing['id'])),
                array('%s','%s','%s','%s','%s','%s','%d','%d','%d','%d','%s','%s','%s','%s','%s'),
                array('%d')
            );

            return intval($existing['id']);
        }

        $next_version = intval($wpdb->get_var($wpdb->prepare(
            "SELECT COALESCE(MAX(version_number), 0) + 1 FROM {$table} WHERE project_id = %d",
            $project_id
        )));
        if ($next_version <= 0) {
            $next_version = 1;
        }

        $wpdb->insert(
            $table,
            array(
                'project_id' => $project_id,
                'version_number' => $next_version,
                'job_id' => $job_id,
                'mode' => sanitize_text_field((string) ($state['mode'] ?? 'baseline_full')),
                'provider' => sanitize_text_field((string) ($state['provider'] ?? 'screamingfrog')),
                'status' => $status !== '' ? $status : 'completed',
                'target_url' => esc_url_raw((string) ($state['target_url'] ?? '')),
                'job_dir' => sanitize_text_field((string) ($state['job_dir'] ?? '')),
                'latest_output_dir' => sanitize_text_field((string) ($state['latest_output_dir'] ?? '')),
                'result_count' => count($files),
                'pages_crawled' => intval($overview['pages_crawled'] ?? 0),
                'issue_count' => count(!empty($audit['issues']) && is_array($audit['issues']) ? $audit['issues'] : array()),
                'site_health' => intval($overview['site_health'] ?? 0),
                'snapshot_json' => $snapshot_json,
                'files_json' => $files_json,
                'state_json' => $state_json,
                'created_at' => $created_at,
                'updated_at' => $created_at,
                'completed_at' => $completed_at,
            ),
            array('%d','%d','%s','%s','%s','%s','%s','%s','%s','%d','%d','%d','%d','%s','%s','%s','%s','%s','%s')
        );

        return intval($wpdb->insert_id);
    }

    private function aysa_adjustments_build_technical_site_audit_conclusion($project_id, $state, $overview, $issues, $pages, $files, $statistics, $history_count, $recommended_actions = array()) {
        $project_id = intval($project_id);
        $version_number = intval($state['version_number'] ?? 0);
        $pages_crawled = intval($overview['pages_crawled'] ?? 0);
        $site_health = intval($overview['site_health'] ?? 0);
        $errors = intval($overview['errors'] ?? 0);
        $warnings = intval($overview['warnings'] ?? 0);
        $indexable_pages = intval($overview['indexable_pages'] ?? 0);
        $non_indexable_pages = intval($overview['non_indexable_pages'] ?? 0);
        $deep_pages = intval($overview['deep_pages_over_3'] ?? 0);
        $thin_linking_pages = intval($overview['single_inlink_pages'] ?? 0);
        $status_codes = !empty($overview['status_codes']) && is_array($overview['status_codes']) ? $overview['status_codes'] : array();
        $status_4xx = intval($status_codes['4xx'] ?? 0);
        $status_5xx = intval($status_codes['5xx'] ?? 0);
        $artifact_count = count(is_array($files) ? $files : array());
        $issue_count = count(is_array($issues) ? $issues : array());
        $mode = sanitize_text_field((string) ($state['mode'] ?? 'baseline_full'));
        $updated_at = sanitize_text_field((string) ($state['updated_at'] ?? ''));
        $version_label = $version_number > 0 ? ('v' . $version_number) : 'current';

        $normalized_issues = is_array($issues) ? array_values($issues) : array();
        usort($normalized_issues, function($left, $right) {
            $left_rank = intval($left['priority_rank'] ?? 99);
            $right_rank = intval($right['priority_rank'] ?? 99);
            if ($left_rank !== $right_rank) {
                return ($left_rank < $right_rank) ? -1 : 1;
            }
            $left_count = intval($left['affected_pages'] ?? 0);
            $right_count = intval($right['affected_pages'] ?? 0);
            if ($left_count === $right_count) {
                return 0;
            }
            return ($left_count > $right_count) ? -1 : 1;
        });

        $top_issue = array();
        if (is_array($issues) && !empty($issues)) {
            $top_issue = is_array($normalized_issues[0] ?? null) ? $normalized_issues[0] : array();
        }

        $status = 'healthy';
        if ($status_5xx > 0 || $errors > 0) {
            $status = 'critical';
        } elseif ($status_4xx > 0 || $warnings > 0 || $issue_count > 0 || $deep_pages > 0 || $thin_linking_pages > 0) {
            $status = 'attention';
        }

        $status_label_map = array(
            'healthy' => 'Healthy',
            'attention' => 'Needs attention',
            'critical' => 'Critical',
        );
        $status_label = $status_label_map[$status] ?? 'Needs attention';

        $main_problem = '';
        if (!empty($top_issue['title_plain'])) {
            $main_problem = sanitize_text_field((string) $top_issue['title_plain']);
        } elseif ($pages_crawled <= 0) {
            $main_problem = 'The plugin does not have enough crawl data loaded yet to explain the baseline clearly.';
        } else {
            $main_problem = 'The current crawl shows technical issues that need prioritised cleanup before the next remediation pass.';
        }

        if ($status === 'critical') {
            $business_impact = 'Critical crawl or indexing failures can stop important pages from being accessed or ranked.';
        } elseif (!empty($top_issue['impact_plain'])) {
            $business_impact = sanitize_text_field((string) $top_issue['impact_plain']);
        } elseif ($pages_crawled > 0) {
            $business_impact = 'The current issues reduce crawl efficiency or search visibility across the site.';
        } else {
            $business_impact = 'Load the audit data first so the plugin can explain the real SEO impact.';
        }

        $summary = sprintf(
            '%s: %s',
            $status_label,
            $main_problem
        );

        $highlights = array();
        if ($artifact_count > 0) {
            $highlights[] = sprintf(
                '%d audit artifacts are stored for %s and ready for review.',
                $artifact_count,
                $version_label
            );
        }
        if ($indexable_pages > 0 || $non_indexable_pages > 0) {
            $highlights[] = sprintf(
                'Indexability split: %d indexable pages and %d non-indexable pages.',
                $indexable_pages,
                $non_indexable_pages
            );
        }
        if (!empty($top_issue['title_plain'])) {
            $highlights[] = sprintf(
                'Top issue: %s (%d affected pages).',
                sanitize_text_field((string) $top_issue['title_plain']),
                intval($top_issue['affected_pages'] ?? 0)
            );
        }
        if ($deep_pages > 0) {
            $highlights[] = sprintf('%d pages sit deeper than crawl depth 3.', $deep_pages);
        }
        if ($thin_linking_pages > 0) {
            $highlights[] = sprintf('%d pages currently have one or fewer inlinks.', $thin_linking_pages);
        }
        if ($updated_at !== '') {
            $highlights[] = sprintf('Last synced in plugin on %s.', $updated_at);
        }

        $priority_actions = array();
        if ($status_5xx > 0 || $errors > 0) {
            $priority_actions[] = sprintf('Investigate %d critical crawl/server issues before using this baseline operationally.', max($status_5xx, $errors));
        }
        if ($status_4xx > 0) {
            $priority_actions[] = sprintf('Review %d internal URLs returning 4xx and remove or repair their internal links.', $status_4xx);
        }
        if (!empty($top_issue['first_action_plain'])) {
            $priority_actions[] = sanitize_text_field((string) $top_issue['first_action_plain']);
        }
        if ($deep_pages > 0) {
            $priority_actions[] = 'Flatten internal architecture for deeper URLs so key commercial pages are reachable within three clicks.';
        }
        if ($thin_linking_pages > 0) {
            $priority_actions[] = 'Strengthen internal linking for pages with weak inlink support before the next crawl comparison.';
        }
        if (empty($priority_actions)) {
            $priority_actions[] = 'Use this baseline as the comparison point for the next remediation crawl and track deltas by version.';
        }
        $priority_actions = array_values(array_slice(array_unique($priority_actions), 0, 4));
        $what_to_do_first = array_values(array_slice($priority_actions, 0, 3));

        $top_issue_cards = array();
        foreach (array_slice($normalized_issues, 0, 3) as $issue) {
            $top_issue_cards[] = array(
                'title' => sanitize_text_field((string) ($issue['title_plain'] ?? $issue['title'] ?? '')),
                'priority_label' => sanitize_text_field((string) ($issue['priority_label'] ?? 'Medium')),
                'affected_pages' => intval($issue['affected_pages'] ?? 0),
                'affected_display' => sanitize_text_field((string) ($issue['affected_display'] ?? '0 pages')),
                'impact_plain' => sanitize_text_field((string) ($issue['impact_plain'] ?? 'This can limit crawl quality or search visibility.')),
                'first_action_plain' => sanitize_text_field((string) ($issue['first_action_plain'] ?? 'Open details and review the affected URLs first.')),
            );
        }

        return array(
            'status' => $status,
            'status_label' => $status_label,
            'summary' => sanitize_text_field($summary),
            'main_problem' => sanitize_text_field($main_problem),
            'business_impact' => sanitize_text_field($business_impact),
            'highlights' => array_values(array_slice(array_map('sanitize_text_field', $highlights), 0, 6)),
            'priority_actions' => array_values(array_map('sanitize_text_field', $priority_actions)),
            'what_to_do_first' => array_values(array_map('sanitize_text_field', $what_to_do_first)),
            'top_issues' => $top_issue_cards,
            'recommended_actions' => is_array($recommended_actions) ? array_values($recommended_actions) : array(),
            'version_label' => sanitize_text_field($version_label),
            'history_count' => max(0, intval($history_count)),
            'artifact_count' => $artifact_count,
            'mode' => $mode,
            'project_id' => $project_id,
            'updated_at' => $updated_at,
        );
    }

    private function aysa_technical_site_audit_start_queued($payload) {
        $project_id = intval($payload['project_id'] ?? 0);
        $target_url = isset($payload['url']) ? esc_url_raw((string) $payload['url']) : '';
        if ($project_id <= 0) {
            return new WP_Error('invalid_technical_site_audit_project', 'Invalid project id.');
        }
        if ($target_url === '') {
            $target_url = $this->aysa_technical_resolve_target_url();
        }

        $broker_payload = array(
            'project_id' => $project_id,
            'url' => $target_url,
            'job_id' => isset($payload['job_id']) ? sanitize_text_field((string) $payload['job_id']) : ('wp_' . $project_id . '_' . gmdate('Ymd_His')),
            'mode' => (string) ($payload['mode'] ?? 'baseline_full'),
        );

        $result = $this->aysa_broker_request('POST', '/v1/technical/screamingfrog/start', $broker_payload, 90);
        if (is_wp_error($result)) {
            return $result;
        }
        if (!is_array($result) || empty($result['ok'])) {
            return new WP_Error('technical_site_audit_start_failed', is_array($result) ? (string) ($result['error'] ?? 'technical_audit_start_failed') : 'technical_audit_start_failed');
        }

        $state = array(
            'project_id' => $project_id,
            'job_id' => (string) ($result['job_id'] ?? ''),
            'target_url' => (string) ($result['target_url'] ?? $target_url),
            'status' => 'running',
            'started_at' => current_time('mysql'),
            'updated_at' => current_time('mysql'),
            'mode' => (string) ($result['mode'] ?? 'baseline_full'),
            'config_path' => (string) ($result['config_path'] ?? ''),
            'export_tabs' => (string) ($result['export_tabs'] ?? ''),
            'bulk_exports' => (string) ($result['bulk_exports'] ?? ''),
            'reports' => (string) ($result['reports'] ?? ''),
            'result_count' => 0,
            'billing_strategy' => 'site_audit_fixed_500_v1',
            'billing_status' => 'pending',
        );
        $state = $this->aysa_technical_site_audit_merge_local_state($project_id, $state, true);
        update_option($this->aysa_technical_site_audit_state_key($project_id), $state, false);

        return array(
            'status' => 'running',
            'worker_ref' => $this->aysa_project_jobs_make_worker_ref('technical_site_audit', 'site_audit', (string) ($state['job_id'] ?? '')),
            'worker_status' => 'running',
            'result_summary' => array(),
            'started_at' => time(),
        );
    }

    private function aysa_technical_pagespeed_state_key($project_id) {
        return 'aysa_technical_pagespeed_state_' . intval($project_id);
    }

    private function aysa_technical_schema_state_key($project_id) {
        return 'aysa_technical_schema_state_' . intval($project_id);
    }

    private function aysa_technical_redirects_state_key($project_id) {
        return 'aysa_technical_redirects_state_' . intval($project_id);
    }

    private function aysa_technical_sitemap_robots_state_key($project_id) {
        return 'aysa_technical_sitemap_robots_state_' . intval($project_id);
    }

    private function aysa_technical_sitemap_generation_state_key() {
        return 'aysa_technical_sitemap_generation_state';
    }

    private function aysa_technical_internal_linking_preview_state_key($project_id) {
        return 'aysa_technical_internal_linking_preview_state_' . intval($project_id);
    }

    private function aysa_technical_robots_file_path() {
        return trailingslashit(ABSPATH) . 'robots.txt';
    }

    private function aysa_technical_sitemap_file_path() {
        return trailingslashit(ABSPATH) . 'sitemap_index.xml';
    }

    private function aysa_technical_preferred_sitemap_url($target_url = '') {
        $base = trim((string) $target_url);
        if ($base === '') {
            $base = home_url('/');
        }
        return untrailingslashit($base) . '/sitemap_index.xml';
    }

    private function aysa_technical_fetch_sitemap_candidates($target_url) {
        $target_url = untrailingslashit((string) $target_url);
        $candidates = array(
            array(
                'url' => $this->aysa_technical_preferred_sitemap_url($target_url),
                'provider' => 'aysa',
                'provider_label' => 'AYSA Sitemap',
            ),
            array(
                'url' => $target_url . '/sitemap.xml',
                'provider' => 'legacy_sitemap',
                'provider_label' => 'Compatibility sitemap',
            ),
            array(
                'url' => $target_url . '/wp-sitemap.xml',
                'provider' => 'wp_core_sitemap',
                'provider_label' => 'Core sitemap endpoint',
            ),
        );
        $rows = array();
        foreach ($candidates as $candidate) {
            $candidate_url = (string) ($candidate['url'] ?? '');
            $resp = wp_remote_get($candidate_url, array(
                'timeout' => 12,
                'redirection' => 0,
                'headers' => array(
                    'Accept' => 'application/xml,text/xml,*/*',
                ),
            ));
            if (is_wp_error($resp)) {
                $rows[] = array(
                    'url' => $candidate_url,
                    'provider' => (string) ($candidate['provider'] ?? ''),
                    'provider_label' => (string) ($candidate['provider_label'] ?? ''),
                    'reachable' => false,
                    'status_code' => 0,
                    'entries_count' => '',
                    'type' => '',
                    'message' => $resp->get_error_message(),
                );
                continue;
            }
            $status_code = intval(wp_remote_retrieve_response_code($resp));
            $body = (string) wp_remote_retrieve_body($resp);
            $location = (string) wp_remote_retrieve_header($resp, 'location');
            $is_reachable = ($status_code >= 200 && $status_code < 300);
            $is_xml = (stripos($body, '<urlset') !== false) || (stripos($body, '<sitemapindex') !== false) || (stripos($body, '<?xml') !== false);
            $entry_count = '';
            $map_type = '';
            if ($is_xml && $is_reachable) {
                if (stripos($body, '<urlset') !== false) {
                    $map_type = 'urlset';
                    $entry_count = substr_count(strtolower($body), '<url>');
                } elseif (stripos($body, '<sitemapindex') !== false) {
                    $map_type = 'index';
                    $entry_count = substr_count(strtolower($body), '<sitemap>');
                }
            } elseif ($status_code >= 300 && $status_code < 400 && $location !== '') {
                $map_type = 'redirect';
            }
            $rows[] = array(
                'url' => $candidate_url,
                'provider' => (string) ($candidate['provider'] ?? ''),
                'provider_label' => (string) ($candidate['provider_label'] ?? ''),
                'reachable' => ($is_reachable && $is_xml),
                'status_code' => $status_code,
                'entries_count' => $entry_count,
                'type' => $map_type,
                'location' => $location,
                'message' => ($status_code >= 300 && $status_code < 400 && $location !== '')
                    ? ('Redirects to ' . $location)
                    : ($is_xml ? '' : 'Endpoint is reachable but not valid XML sitemap.'),
            );
        }
        return $rows;
    }

    private function aysa_technical_load_robots_data($target_url, $primary_sitemap = '') {
        $file_path = $this->aysa_technical_robots_file_path();
        $file_exists = file_exists($file_path);
        $content = '';
        $source = 'virtual-default';
        $source_label = 'WordPress virtual default';

        if ($file_exists) {
            $content = (string) @file_get_contents($file_path);
            $source = 'file';
            $source_label = 'Physical robots.txt';
        } else {
            $lines = array(
                'User-agent: *',
                'Disallow:',
            );
            if ($primary_sitemap !== '') {
                $lines[] = 'Sitemap: ' . $primary_sitemap;
            } else {
                $lines[] = 'Sitemap: ' . untrailingslashit((string) $target_url) . '/wp-sitemap.xml';
            }
            $content = implode("\n", $lines) . "\n";
        }

        $writable = false;
        if ($file_exists) {
            $writable = is_writable($file_path);
        } else {
            $writable = is_writable(dirname($file_path));
        }

        return array(
            'content' => $content,
            'source' => $source,
            'source_label' => $source_label,
            'path' => $file_path,
            'exists' => $file_exists,
            'writable' => $writable,
            'last_modified' => $file_exists ? @date_i18n('Y-m-d H:i:s', intval(@filemtime($file_path))) : '',
        );
    }

    private function aysa_technical_detect_active_sitemap_providers() {
        $providers = array();
        $active_plugins = (array) get_option('active_plugins', array());
        if (in_array('wordpress-seo/wp-seo.php', $active_plugins, true) || class_exists('WPSEO_Options')) {
            $providers[] = 'Yoast SEO';
        }
        if (in_array('seo-by-rank-math/rank-math.php', $active_plugins, true) || class_exists('RankMath')) {
            $providers[] = 'Rank Math';
        }
        if (in_array('all-in-one-seo-pack/all_in_one_seo_pack.php', $active_plugins, true) || class_exists('AIOSEO\\Plugin\\Common\\Main')) {
            $providers[] = 'All in One SEO';
        }
        $providers[] = 'WordPress Core';
        return array_values(array_unique($providers));
    }

    private function aysa_technical_post_is_noindex($post_id) {
        $post_id = intval($post_id);
        if ($post_id <= 0) {
            return false;
        }
        $robots_meta = get_post_meta($post_id, 'aysa_robots_meta', true);
        if (is_array($robots_meta)) {
            if (!empty($robots_meta['noindex'])) {
                return true;
            }
            if (isset($robots_meta['index']) && strtolower((string) $robots_meta['index']) === 'noindex') {
                return true;
            }
        }
        $yoast_noindex = (string) get_post_meta($post_id, '_yoast_wpseo_meta-robots-noindex', true);
        if ($yoast_noindex === '1' || strtolower($yoast_noindex) === 'noindex') {
            return true;
        }
        $yoast_robots = strtolower((string) get_post_meta($post_id, '_yoast_wpseo_robots', true));
        return ($yoast_robots !== '' && strpos($yoast_robots, 'noindex') !== false);
    }

    private function aysa_technical_term_is_noindex($term_id) {
        $term_id = intval($term_id);
        if ($term_id <= 0) {
            return false;
        }
        $robots_meta = get_term_meta($term_id, 'aysa_robots_meta', true);
        if (is_array($robots_meta)) {
            if (!empty($robots_meta['noindex'])) {
                return true;
            }
            if (isset($robots_meta['index']) && strtolower((string) $robots_meta['index']) === 'noindex') {
                return true;
            }
        }
        $yoast_noindex = (string) get_term_meta($term_id, '_yoast_wpseo_meta-robots-noindex', true);
        return ($yoast_noindex === '1' || strtolower($yoast_noindex) === 'noindex');
    }

    private function aysa_technical_sitemap_url_is_excluded($url) {
        $path = wp_parse_url((string) $url, PHP_URL_PATH);
        $path = strtolower(is_string($path) ? $path : '');
        if ($path === '') {
            return false;
        }
        foreach (array('/cart/', '/checkout/', '/my-account/', '/account/', '/search/') as $blocked) {
            if (strpos($path, $blocked) !== false) {
                return true;
            }
        }
        $query = wp_parse_url((string) $url, PHP_URL_QUERY);
        return is_string($query) && stripos($query, 'add-to-cart=') !== false;
    }

    private function aysa_technical_sitemap_push_entry(&$groups, &$seen, $group_key, $group_label, $loc, $lastmod = '') {
        $loc = esc_url_raw((string) $loc);
        if ($loc === '' || isset($seen[$loc]) || $this->aysa_technical_sitemap_url_is_excluded($loc)) {
            return;
        }
        $seen[$loc] = true;
        if (!isset($groups[$group_key]) || !is_array($groups[$group_key])) {
            $groups[$group_key] = array(
                'key' => sanitize_key((string) $group_key),
                'label' => sanitize_text_field((string) $group_label),
                'entries' => array(),
            );
        }
        $row = array('loc' => $loc);
        if (is_string($lastmod) && trim($lastmod) !== '') {
            $row['lastmod'] = trim($lastmod);
        }
        $groups[$group_key]['entries'][] = $row;
    }

    private function aysa_technical_collect_sitemap_groups() {
        $groups = array();
        $seen = array();

        $home_lastmod = get_lastpostmodified('GMT');
        $this->aysa_technical_sitemap_push_entry($groups, $seen, 'pages', 'Pages', home_url('/'), $home_lastmod ? gmdate('c', strtotime($home_lastmod . ' GMT')) : gmdate('c'));

        $post_types = get_post_types(array('public' => true), 'objects');
        foreach ($post_types as $post_type => $obj) {
            if (empty($obj->publicly_queryable) || $post_type === 'attachment') {
                continue;
            }
            $ids = get_posts(array(
                'post_type' => $post_type,
                'post_status' => 'publish',
                'fields' => 'ids',
                'numberposts' => 10000,
                'orderby' => 'modified',
                'order' => 'DESC',
                'suppress_filters' => false,
            ));
            $group_key = sanitize_key($post_type);
            $group_label = !empty($obj->labels->name) ? (string) $obj->labels->name : ucfirst((string) $post_type);
            foreach ((array) $ids as $post_id) {
                $post_id = intval($post_id);
                if ($post_id <= 0 || $this->aysa_technical_post_is_noindex($post_id)) {
                    continue;
                }
                $permalink = get_permalink($post_id);
                if (!is_string($permalink) || $permalink === '') {
                    continue;
                }
                $this->aysa_technical_sitemap_push_entry($groups, $seen, $group_key, $group_label, $permalink, (string) get_post_modified_time('c', true, $post_id));
            }
        }

        $taxonomies = get_taxonomies(array('public' => true), 'objects');
        foreach ($taxonomies as $taxonomy => $obj) {
            if (in_array($taxonomy, array('post_format', 'nav_menu', 'link_category'), true)) {
                continue;
            }
            $terms = get_terms(array(
                'taxonomy' => $taxonomy,
                'hide_empty' => true,
                'number' => 10000,
            ));
            if (is_wp_error($terms)) {
                continue;
            }
            $group_key = sanitize_key($taxonomy);
            $group_label = !empty($obj->labels->name) ? (string) $obj->labels->name : ucfirst((string) $taxonomy);
            foreach ((array) $terms as $term) {
                if (!($term instanceof WP_Term) || $this->aysa_technical_term_is_noindex($term->term_id)) {
                    continue;
                }
                $term_link = get_term_link($term);
                if (is_wp_error($term_link) || !is_string($term_link) || $term_link === '') {
                    continue;
                }
                $this->aysa_technical_sitemap_push_entry($groups, $seen, $group_key, $group_label, $term_link, '');
            }
        }

        return array_values(array_filter($groups, static function($group) {
            return is_array($group) && !empty($group['entries']);
        }));
    }

    private function aysa_technical_collect_sitemap_entries() {
        $entries = array();
        foreach ($this->aysa_technical_collect_sitemap_groups() as $group) {
            foreach ((array) ($group['entries'] ?? array()) as $entry) {
                if (is_array($entry)) {
                    $entries[] = $entry;
                }
            }
        }
        return $entries;
    }

    private function aysa_technical_build_sitemap_urlset_xml(array $entries) {
        $lines = array(
            '<?xml version="1.0" encoding="UTF-8"?>',
            '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
        );
        foreach ($entries as $entry) {
            $lines[] = '  <url>';
            $lines[] = '    <loc>' . esc_html((string) ($entry['loc'] ?? '')) . '</loc>';
            if (!empty($entry['lastmod'])) {
                $lines[] = '    <lastmod>' . esc_html((string) $entry['lastmod']) . '</lastmod>';
            }
            $lines[] = '  </url>';
        }
        $lines[] = '</urlset>';

        return array(
            'xml' => implode("\n", $lines) . "\n",
            'entries_count' => count($entries),
        );
    }

    private function aysa_technical_sitemap_child_slug($group_key, $page = 1) {
        $group_key = sanitize_key((string) $group_key);
        $page = max(1, intval($page));
        return $page > 1 ? $group_key . '-sitemap-' . $page . '.xml' : $group_key . '-sitemap.xml';
    }

    private function aysa_technical_build_sitemap_index_xml($target_url = '') {
        $target_url = $target_url !== '' ? untrailingslashit((string) $target_url) : untrailingslashit(home_url('/'));
        $groups = $this->aysa_technical_collect_sitemap_groups();
        $children = array();
        $entries_count = 0;
        foreach ($groups as $group) {
            $entries = array_values((array) ($group['entries'] ?? array()));
            $chunks = array_chunk($entries, 1000);
            foreach ($chunks as $page => $chunk) {
                $lastmod = '';
                foreach ($chunk as $entry) {
                    if (!empty($entry['lastmod']) && (string) $entry['lastmod'] > $lastmod) {
                        $lastmod = (string) $entry['lastmod'];
                    }
                }
                $children[] = array(
                    'key' => sanitize_key((string) ($group['key'] ?? 'sitemap')),
                    'label' => sanitize_text_field((string) ($group['label'] ?? 'Sitemap')),
                    'url' => $target_url . '/' . $this->aysa_technical_sitemap_child_slug((string) ($group['key'] ?? 'sitemap'), $page + 1),
                    'entries_count' => count($chunk),
                    'lastmod' => $lastmod !== '' ? $lastmod : gmdate('c'),
                    'page' => $page + 1,
                );
                $entries_count += count($chunk);
            }
        }

        $lines = array(
            '<?xml version="1.0" encoding="UTF-8"?>',
            '<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">',
        );
        foreach ($children as $child) {
            $lines[] = '  <sitemap>';
            $lines[] = '    <loc>' . esc_html((string) ($child['url'] ?? '')) . '</loc>';
            $lines[] = '    <lastmod>' . esc_html((string) ($child['lastmod'] ?? gmdate('c'))) . '</lastmod>';
            $lines[] = '  </sitemap>';
        }
        $lines[] = '</sitemapindex>';

        return array(
            'xml' => implode("\n", $lines) . "\n",
            'entries_count' => $entries_count,
            'children_count' => count($children),
            'children' => $children,
        );
    }

    private function aysa_technical_build_sitemap_xml($target_url = '') {
        return $this->aysa_technical_build_sitemap_index_xml($target_url);
    }

    private function aysa_technical_generate_own_sitemap($target_url = '') {
        $target_url = trim((string) $target_url);
        if ($target_url === '') {
            $target_url = home_url('/');
        }

        $built = $this->aysa_technical_build_sitemap_xml($target_url);
        $file_path = $this->aysa_technical_sitemap_file_path();
        $mode = 'virtual';
        $saved = false;

        if (file_exists($file_path) ? is_writable($file_path) : (is_dir(dirname($file_path)) && is_writable(dirname($file_path)))) {
            $bytes = @file_put_contents($file_path, (string) ($built['xml'] ?? ''));
            if ($bytes !== false) {
                @chmod($file_path, 0644);
                $mode = 'physical';
                $saved = true;
            }
        }
        $legacy_file_path = trailingslashit(ABSPATH) . 'sitemap.xml';
        if ($legacy_file_path !== $file_path && file_exists($legacy_file_path) && is_writable($legacy_file_path)) {
            @unlink($legacy_file_path);
        }

        $state = array(
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'url' => $this->aysa_technical_preferred_sitemap_url($target_url),
            'entries_count' => intval($built['entries_count'] ?? 0),
            'children_count' => intval($built['children_count'] ?? 0),
            'children' => is_array($built['children'] ?? null) ? $built['children'] : array(),
            'mode' => $mode,
            'saved' => $saved,
        );
        update_option($this->aysa_technical_sitemap_generation_state_key(), $state, false);
        return $state;
    }

    public function maybe_serve_aysa_technical_sitemap() {
        $request_uri = isset($_SERVER['REQUEST_URI']) ? (string) $_SERVER['REQUEST_URI'] : '';
        if ($request_uri === '') {
            return;
        }
        $path = wp_parse_url($request_uri, PHP_URL_PATH);
        $path = is_string($path) ? untrailingslashit($path) : '';
        if ($path === '') {
            $path = '/';
        }
        if ($path === '/sitemap.xml' || $path === '/wp-sitemap.xml') {
            wp_safe_redirect($this->aysa_technical_preferred_sitemap_url(home_url('/')), 301);
            exit;
        }

        if ($path === '/sitemap_index.xml') {
            $built = $this->aysa_technical_build_sitemap_index_xml(home_url('/'));
            status_header(200);
            nocache_headers();
            header('Content-Type: application/xml; charset=UTF-8');
            echo (string) ($built['xml'] ?? '');
            exit;
        }

        if (preg_match('#^/([a-z0-9_-]+)-sitemap(?:-([0-9]+))?\.xml$#', $path, $matches)) {
            $group_key = sanitize_key((string) ($matches[1] ?? ''));
            $page = max(1, intval($matches[2] ?? 1));
            foreach ($this->aysa_technical_collect_sitemap_groups() as $group) {
                if (sanitize_key((string) ($group['key'] ?? '')) !== $group_key) {
                    continue;
                }
                $chunks = array_chunk(array_values((array) ($group['entries'] ?? array())), 1000);
                $entries = isset($chunks[$page - 1]) && is_array($chunks[$page - 1]) ? $chunks[$page - 1] : array();
                if ($entries === array()) {
                    break;
                }
                $built = $this->aysa_technical_build_sitemap_urlset_xml($entries);
                status_header(200);
                nocache_headers();
                header('Content-Type: application/xml; charset=UTF-8');
                echo (string) ($built['xml'] ?? '');
                exit;
            }
        }
    }

    public function filter_aysa_technical_robots_txt($output, $public) {
        $content = (string) $output;
        $sitemap_url = $this->aysa_technical_preferred_sitemap_url(home_url('/'));
        if (trim($content) === '') {
            return $this->aysa_technical_robots_default_content(home_url('/'), $sitemap_url);
        }
        if (stripos($content, 'Sitemap:') === false) {
            $content = rtrim($content) . "\nSitemap: " . $sitemap_url . "\n";
        }
        return $content;
    }

    private function aysa_technical_robots_default_content($target_url, $primary_sitemap = '') {
        $sitemap_url = $primary_sitemap !== '' ? $primary_sitemap : $this->aysa_technical_preferred_sitemap_url($target_url);
        $lines = array(
            'User-agent: *',
            'Disallow: /wp-admin/',
            'Allow: /wp-admin/admin-ajax.php',
            'Disallow: /cart/',
            'Disallow: /checkout/',
            'Disallow: /my-account/',
            'Disallow: /?add-to-cart=',
            '',
            'Sitemap: ' . $sitemap_url,
        );
        return implode("\n", $lines) . "\n";
    }

    private function aysa_technical_save_robots_content($content) {
        $file_path = $this->aysa_technical_robots_file_path();
        $content = (string) $content;
        $content = str_replace("\r\n", "\n", $content);
        $content = str_replace("\r", "\n", $content);
        $content = str_replace("\0", '', $content);
        $lines = explode("\n", $content);
        $clean_lines = array();
        foreach ($lines as $line) {
            $clean_lines[] = rtrim((string) $line, " \t");
        }
        $normalized = trim(implode("\n", $clean_lines)) . "\n";
        if ($normalized === "\n") {
            return new WP_Error('robots_empty', 'robots.txt content cannot be empty.');
        }
        if (strlen($normalized) > 120000) {
            return new WP_Error('robots_too_large', 'robots.txt content is too large.');
        }

        if (file_exists($file_path)) {
            if (!is_writable($file_path)) {
                return new WP_Error('robots_not_writable', 'robots.txt is not writable.');
            }
        } else {
            $dir = dirname($file_path);
            if (!is_dir($dir) || !is_writable($dir)) {
                return new WP_Error('robots_not_writable', 'Cannot create robots.txt in WordPress root (directory not writable).');
            }
        }

        $bytes = @file_put_contents($file_path, $normalized);
        if ($bytes === false) {
            return new WP_Error('robots_write_failed', 'Failed to write robots.txt.');
        }
        return true;
    }

    private function aysa_technical_build_sitemap_robots_audit($target_url) {
        $sitemaps = $this->aysa_technical_fetch_sitemap_candidates($target_url);
        $generation = get_option($this->aysa_technical_sitemap_generation_state_key(), array());
        if (!is_array($generation)) {
            $generation = array();
        }
        $primary_sitemap = '';
        foreach ($sitemaps as $row) {
            if (!empty($row['reachable']) && !empty($row['url']) && (string) ($row['provider'] ?? '') === 'aysa') {
                $primary_sitemap = (string) $row['url'];
                break;
            }
        }
        if ($primary_sitemap === '') {
            foreach ($sitemaps as $row) {
                if (!empty($row['reachable']) && !empty($row['url'])) {
                    $primary_sitemap = (string) $row['url'];
                    break;
                }
            }
        }
        if ($primary_sitemap === '') {
            $primary_sitemap = $this->aysa_technical_preferred_sitemap_url($target_url);
        }

        $robots = $this->aysa_technical_load_robots_data($target_url, $primary_sitemap);
        $robots_content = (string) ($robots['content'] ?? '');
        $normalized = strtolower($robots_content);
        $wordpress_visibility_enabled = ((string) get_option('blog_public', '1') !== '0');
        $providers = $this->aysa_technical_detect_active_sitemap_providers();
        $preferred_sitemap = $this->aysa_technical_preferred_sitemap_url($target_url);
        $generation_mode = sanitize_text_field((string) ($generation['mode'] ?? 'not_generated'));
        $generation_entries = max(0, intval($generation['entries_count'] ?? 0));
        $generation_saved = !empty($generation['saved']);
        $legacy_conflicts = array();
        $legacy_redirected = array();

        $checks = array();
        $checks[] = array(
            'id' => 'wp_visibility',
            'label' => 'WordPress search visibility is enabled',
            'status' => $wordpress_visibility_enabled ? 'pass' : 'error',
            'message' => $wordpress_visibility_enabled ? 'WordPress is not discouraging search engines.' : 'WordPress currently discourages search engines from indexing the site.',
        );
        $has_user_agent_star = (bool) preg_match('/^\s*user-agent:\s*\*\s*$/im', $robots_content);
        $checks[] = array(
            'id' => 'ua_star',
            'label' => 'Has User-agent: *',
            'status' => $has_user_agent_star ? 'pass' : 'error',
            'message' => $has_user_agent_star ? 'Global crawler directive is present.' : 'Add `User-agent: *` to apply rules for major crawlers.',
        );

        $has_disallow_all = (bool) preg_match('/^\s*disallow:\s*\/\s*$/im', $robots_content);
        $checks[] = array(
            'id' => 'disallow_all',
            'label' => 'No global Disallow /',
            'status' => $has_disallow_all ? 'error' : 'pass',
            'message' => $has_disallow_all ? 'Global `Disallow: /` blocks the whole site from crawling.' : 'No full-site crawl blocking rule detected.',
        );

        $has_sitemap = (bool) preg_match('/^\s*sitemap:\s*https?:\/\/\S+\s*$/im', $robots_content);
        $sitemap_matches_primary = stripos($normalized, strtolower('Sitemap: ' . $preferred_sitemap)) !== false;
        $checks[] = array(
            'id' => 'sitemap_directive',
            'label' => 'robots.txt points to AYSA sitemap',
            'status' => $has_sitemap ? ($sitemap_matches_primary ? 'pass' : 'warning') : 'warning',
            'message' => !$has_sitemap
                ? 'Add a `Sitemap:` directive in robots.txt.'
                : ($sitemap_matches_primary ? 'robots.txt references the AYSA sitemap.' : 'Sitemap directive found, but it does not point to the AYSA sitemap URL.'),
        );

        $danger_patterns = array('/product/', '/category/', '/blog/');
        $danger_hits = array();
        foreach ($danger_patterns as $pattern) {
            if (stripos($normalized, 'disallow: ' . strtolower($pattern)) !== false) {
                $danger_hits[] = $pattern;
            }
        }
        $checks[] = array(
            'id' => 'critical_sections',
            'label' => 'Critical sections are crawlable',
            'status' => empty($danger_hits) ? 'pass' : 'warning',
            'message' => empty($danger_hits)
                ? 'No broad disallow rules detected for key SEO sections.'
                : ('Potentially restrictive rules found for: ' . implode(', ', $danger_hits)),
        );

        $reachable_sitemaps = 0;
        $aysa_reachable = false;
        foreach ($sitemaps as $sitemap_row) {
            if (!empty($sitemap_row['reachable'])) {
                $reachable_sitemaps++;
            }
            if ((string) ($sitemap_row['provider'] ?? '') === 'aysa' && !empty($sitemap_row['reachable'])) {
                $aysa_reachable = true;
            }
            if ((string) ($sitemap_row['provider'] ?? '') !== 'aysa') {
                $legacy_label = (string) ($sitemap_row['provider_label'] ?? $sitemap_row['url']);
                $is_redirect = ((string) ($sitemap_row['type'] ?? '') === 'redirect');
                $redirect_location = untrailingslashit((string) ($sitemap_row['location'] ?? ''));
                if ($is_redirect && $redirect_location === untrailingslashit($preferred_sitemap)) {
                    $legacy_redirected[] = $legacy_label;
                } elseif (!empty($sitemap_row['reachable']) || $is_redirect) {
                    $legacy_conflicts[] = $legacy_label;
                }
            }
        }
        $checks[] = array(
            'id' => 'aysa_sitemap_reachable',
            'label' => 'AYSA sitemap endpoint is reachable',
            'status' => $aysa_reachable ? 'pass' : 'error',
            'message' => $aysa_reachable
                ? 'AYSA sitemap index is available on /sitemap_index.xml.'
                : 'AYSA sitemap index is not reachable yet. Generate it and verify that /sitemap_index.xml returns valid XML.',
        );
        $checks[] = array(
            'id' => 'legacy_sitemaps',
            'label' => 'Other sitemap endpoints are not competing',
            'status' => empty($legacy_conflicts) ? 'pass' : 'warning',
            'message' => empty($legacy_conflicts)
                ? (empty($legacy_redirected) ? 'No competing sitemap endpoint is active.' : 'Compatibility sitemap endpoints redirect safely to AYSA sitemap.')
                : 'One or more sitemap endpoints still respond directly and should be redirected to AYSA sitemap.',
        );
        $checks[] = array(
            'id' => 'sitemap_generation',
            'label' => 'AYSA sitemap is generated from WordPress content',
            'status' => ($generation_entries > 0 && $aysa_reachable) ? 'pass' : 'warning',
            'message' => ($generation_entries > 0)
                ? ('AYSA sitemap contains ' . $generation_entries . ' entries and is served in `' . $generation_mode . '` mode' . ($generation_saved ? '.' : ' (virtual fallback).'))
                : 'AYSA sitemap has not been generated yet. Use `Generate AYSA Sitemap` first.',
        );
        $checks[] = array(
            'id' => 'robots_writable',
            'label' => 'robots.txt is editable',
            'status' => !empty($robots['writable']) ? 'pass' : 'warning',
            'message' => !empty($robots['writable']) ? 'WordPress can update robots.txt.' : 'robots.txt is not writable from WordPress root.',
        );
        $checks[] = array(
            'id' => 'seo_plugin_sitemaps_disabled',
            'label' => 'Conflicting sitemap endpoints are controlled',
            'status' => empty($legacy_conflicts) ? 'pass' : 'warning',
            'message' => empty($legacy_conflicts)
                ? 'Other sitemap endpoints are not competing with AYSA sitemap.'
                : 'One or more sitemap endpoints still respond directly. Keep only AYSA sitemap active.',
        );

        $error_count = 0;
        $warning_count = 0;
        $pass_count = 0;
        foreach ($checks as $check) {
            $status = (string) ($check['status'] ?? '');
            if ($status === 'error') {
                $error_count++;
            } elseif ($status === 'warning') {
                $warning_count++;
            } elseif ($status === 'pass') {
                $pass_count++;
            }
        }

        $total = max(1, count($checks));
        $robots_health = intval(round(($pass_count / $total) * 100));
        $sitemap_health = intval(round(((($aysa_reachable ? 1 : 0) + (empty($legacy_conflicts) ? 1 : 0) + ($generation_entries > 0 ? 1 : 0)) / 3) * 100));

        return array(
            'generated_at' => current_time('mysql'),
            'target_url' => $target_url,
            'primary_sitemap' => $primary_sitemap,
            'robots' => $robots,
            'sitemaps' => $sitemaps,
            'checks' => $checks,
            'generation' => array(
                'mode' => $generation_mode,
                'entries_count' => $generation_entries,
                'children_count' => intval($generation['children_count'] ?? 0),
                'children' => is_array($generation['children'] ?? null) ? $generation['children'] : array(),
                'updated_at' => sanitize_text_field((string) ($generation['updated_at'] ?? '')),
                'url' => esc_url_raw((string) ($generation['url'] ?? $preferred_sitemap)),
                'saved' => $generation_saved,
            ),
            'providers' => $providers,
            'summary' => array(
                'errors' => $error_count,
                'warnings' => $warning_count,
                'passes' => $pass_count,
                'robots_health' => $robots_health . '/100',
                'sitemap_health' => $sitemap_health . '/100',
                'wordpress_visibility' => $wordpress_visibility_enabled ? 'enabled' : 'blocked',
                'sitemap_mode' => $generation_mode,
                'providers_label' => implode(', ', $providers),
                'legacy_conflicts' => count($legacy_conflicts),
                'sitemap_entries' => $generation_entries,
                'legacy_redirected' => count($legacy_redirected),
                'reachable_sitemaps' => $reachable_sitemaps,
            ),
        );
    }

    private function aysa_resolve_project_id_with_local_fallback($project_id = 0) {
        $project_id = intval($project_id);
        if ($project_id > 0) {
            return $project_id;
        }

        $project_id = intval(get_option('aysa_current_project_id', 0));
        if ($project_id > 0) {
            return $project_id;
        }

        $project_id = intval(get_option('aysa_project_id', 0));
        if ($project_id > 0) {
            return $project_id;
        }

        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')) {
            $wallet_project_id = intval(Aysa_Wallet_Manager::get_instance()->get_project_id());
            if ($wallet_project_id > 0) {
                return $wallet_project_id;
            }
        }

        return max(0, intval($project_id));
    }

    private function aysa_verify_technical_audit_nonce() {
        $nonce = isset($_POST['nonce']) ? (string) $_POST['nonce'] : '';
        if ($nonce === '') {
            $nonce = isset($_POST['_ajax_nonce']) ? (string) $_POST['_ajax_nonce'] : '';
        }
        if ($nonce === '') {
            return false;
        }

        $allowed_actions = array(
            'aysa_nonce',
            'aysa_metabox_nonce',
            'aysa-ajax-nonce',
            'aysa_internal_linking_nonce',
            'aysa_schema_nonce',
            'aysa_technical_audit_nonce',
            'aysa_content_strategy_nonce',
        );
        foreach ($allowed_actions as $action) {
            if (wp_verify_nonce($nonce, $action)) {
                return true;
            }
        }
        return false;
    }

    private function aysa_technical_resolve_target_url($project_id = 0) {
        $project_id = $this->aysa_resolve_technical_project_id(intval($project_id));
        if ($project_id > 0) {
            $project_general = get_option('aysa_wizard_general_project_' . $project_id, array());
            if (is_array($project_general)) {
                $website = $this->aysa_setup_normalize_url((string) ($project_general['website'] ?? ''));
                if ($website !== '') {
                    return rtrim($website, '/');
                }
            }
        }

        $site_url = (string) site_url('/');
        return rtrim($site_url, '/');
    }

    private function aysa_wallet_track_server_usage($project_id, $endpoint, $cost_cents, $reference = '', $meta = array()) {
        $project_id = intval($project_id);
        $cost_cents = intval($cost_cents);
        if ($project_id <= 0 || $cost_cents <= 0) {
            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();
        $meta_payload = is_array($meta) ? $meta : array();
        $meta_payload['server_side'] = true;
        $meta_payload['user_id'] = get_current_user_id();
        $event = array(
            'endpoint' => (string) $endpoint,
            'cost_cents' => $cost_cents,
            'meta' => $meta_payload,
            'ts' => time(),
        );

        // Keep UX non-blocking: usage is always queued remotely; local debit is best-effort.
        $wallet->enqueue_usage_event($project_id, (string) $endpoint, $cost_cents, $meta_payload, intval($event['ts']));
        $wallet->apply_credit_delta(
            $project_id,
            -abs($cost_cents),
            'usage',
            'wp_plugin_server',
            (string) $reference,
            null,
            $meta_payload
        );

        if (method_exists($wallet, 'push_usage_events_now')) {
            $wallet->push_usage_events_now($project_id, array($event));
        } elseif (method_exists($wallet, 'flush_usage_queue')) {
            $wallet->flush_usage_queue($project_id);
        }
    }

    private function aysa_cost_usd_to_milli_cents($costUsd) {
        if (!is_numeric($costUsd)) {
            return 0;
        }
        $val = (float) $costUsd;
        if ($val <= 0) {
            return 0;
        }
        return max(0, intval(round($val * 100000)));
    }

    private function aysa_wallet_track_action_usage($project_id, $action_key, $reference = '', $meta = array(), $fallback_cost_cents = 0, $endpoint = '') {
        $project_id = intval($project_id);
        if ($project_id <= 0 || trim((string) $action_key) === '') {
            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')) {
            // Fallback for older wallet manager versions.
            $this->aysa_wallet_track_server_usage($project_id, $endpoint, intval($fallback_cost_cents), $reference, $meta);
            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
        );
    }

    private function aysa_technical_site_audit_billed_cents_from_pages($pages_crawled) {
        $pages_crawled = max(0, intval($pages_crawled));
        return 500;
    }

    private function aysa_wallet_usage_reference_exists($project_id, $reference) {
        global $wpdb;

        $project_id = intval($project_id);
        $reference = trim((string) $reference);
        if ($project_id <= 0 || $reference === '') {
            return false;
        }

        $table = $wpdb->prefix . 'aysa_wallet_ledger';
        $existing_id = $wpdb->get_var(
            $wpdb->prepare(
                "SELECT id
                 FROM {$table}
                 WHERE project_id = %d
                   AND entry_type = %s
                   AND reference = %s
                 ORDER BY id DESC
                 LIMIT 1",
                $project_id,
                'usage',
                $reference
            )
        );

        return intval($existing_id) > 0;
    }

    private function aysa_technical_site_audit_bill_if_needed($project_id, array $state, array $audit_payload = array()) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return $state;
        }

        $billing_strategy = sanitize_key((string) ($state['billing_strategy'] ?? ''));
        if ($billing_strategy !== 'site_audit_fixed_500_v1') {
            return $state;
        }

        $job_id = sanitize_text_field((string) ($state['job_id'] ?? ''));
        if ($job_id === '') {
            return $state;
        }

        $billed_job_id = sanitize_text_field((string) ($state['billing_job_id'] ?? ''));
        if ($billed_job_id === $job_id && intval($state['billing_cents'] ?? 0) > 0) {
            return $state;
        }

        $snapshot = !empty($state['audit_snapshot']) && is_array($state['audit_snapshot']) ? $state['audit_snapshot'] : array();
        $pages_crawled = max(
            intval($audit_payload['pages_crawled'] ?? 0),
            intval($snapshot['pages_crawled'] ?? 0)
        );
        $status = sanitize_key((string) ($state['status'] ?? ''));
        if ($pages_crawled <= 0 && !in_array($status, array('completed', 'failed'), true)) {
            return $state;
        }

        $billed_cents = $this->aysa_technical_site_audit_billed_cents_from_pages($pages_crawled);
        $billing_reference = 'technical_site_audit_start:' . $job_id;
        if ($this->aysa_wallet_usage_reference_exists($project_id, $billing_reference)) {
            $state['billing_job_id'] = $job_id;
            $state['billing_cents'] = $billed_cents;
            $state['billing_pages_crawled'] = $pages_crawled;
            $state['billing_billed_at'] = current_time('mysql');
            $state['billing_status'] = 'billed';
            return $state;
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_technical_site_audit_start',
            $billing_reference,
            array(
                'module' => 'technical_optimization',
                'target_url' => (string) ($state['target_url'] ?? ''),
                'job_id' => $job_id,
                'provider' => (string) ($state['provider'] ?? 'screamingfrog'),
                'pages_crawled' => $pages_crawled,
                'billing_rule' => 'fixed_500_site_audit_only',
                'override_cost_cents' => $billed_cents,
            ),
            $billed_cents,
            '/v1/technical/screamingfrog/start'
        );

        $state['billing_job_id'] = $job_id;
        $state['billing_cents'] = $billed_cents;
        $state['billing_pages_crawled'] = $pages_crawled;
        $state['billing_billed_at'] = current_time('mysql');
        $state['billing_status'] = 'billed';

        return $state;
    }

    private function aysa_wallet_log_endpoint_usage($project_id, $endpoint, $usage = array(), $action_key = '', $reference = '', $meta = array()) {
        $project_id = intval($project_id);
        $endpoint = (string) $endpoint;
        if ($project_id <= 0 || $endpoint === '' || !is_array($usage) || empty($usage)) {
            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, 'log_endpoint_usage')) {
            return;
        }

        $billed_cost_cents = 0;
        if (method_exists($wallet, 'get_action_cost_cents')) {
            $billed_cost_cents = intval($wallet->get_action_cost_cents($action_key, 0));
        }

        $wallet->log_endpoint_usage(
            $project_id,
            $endpoint,
            $usage,
            sanitize_key((string) $action_key),
            (string) $reference,
            $billed_cost_cents,
            is_array($meta) ? $meta : array()
        );
    }

    private function aysa_wallet_bill_provider_usage($project_id, $endpoint, $usage = array(), $reference = '', $meta = array(), $pricing_multiplier = 5) {
        $project_id = intval($project_id);
        if ($project_id <= 0 || !is_array($usage) || empty($usage)) {
            return 0;
        }

        $provider_milli = 0;
        if (isset($usage['provider_cost_milli_cents'])) {
            $provider_milli = max(0, intval($usage['provider_cost_milli_cents']));
        } elseif (isset($usage['provider_cost_usd'])) {
            $provider_milli = $this->aysa_cost_usd_to_milli_cents($usage['provider_cost_usd']);
        } elseif (isset($usage['cost_usd'])) {
            $provider_milli = $this->aysa_cost_usd_to_milli_cents($usage['cost_usd']);
        }
        if ($provider_milli <= 0) {
            return 0;
        }

        $multiplier = max(1, intval($pricing_multiplier));
        $billed_cents = intval(ceil(($provider_milli * $multiplier) / 1000));
        if ($billed_cents <= 0) {
            $billed_cents = 1;
        }

        $meta_payload = is_array($meta) ? $meta : array();
        $meta_payload['provider_cost_milli_cents'] = $provider_milli;
        $meta_payload['pricing_multiplier'] = $multiplier;
        $meta_payload['provider'] = (string) ($usage['provider'] ?? 'openai');
        $meta_payload['model'] = (string) ($usage['model'] ?? '');
        $meta_payload['input_tokens'] = intval($usage['input_tokens'] ?? ($usage['prompt_tokens'] ?? 0));
        $meta_payload['output_tokens'] = intval($usage['output_tokens'] ?? ($usage['completion_tokens'] ?? 0));
        $meta_payload['total_tokens'] = intval($usage['total_tokens'] ?? 0);

        $this->aysa_wallet_track_server_usage(
            $project_id,
            (string) $endpoint,
            $billed_cents,
            (string) $reference,
            $meta_payload
        );

        return $billed_cents;
    }

    private function aysa_usage_action_key_from_path($path) {
        $path = rtrim((string) $path, '/');
        $map = array(
            '/v1/content-strategy/generate' => 'aysa_content_strategy_generate',
            '/v1/content/strategy/generate' => 'aysa_content_strategy_generate',
            '/v1/content/missing-page/meta' => 'aysa_missing_page_generate_meta',
            '/v1/content/seo-suggestion' => 'aysa_onpage_generate_suggestion',
            '/v1/setup/general/autofill' => 'aysa_setup_general_autofill',
            '/v1/conversation/setup/message' => 'aysa_setup_conversation_message',
            '/v1/conversation/adjustments/message' => 'aysa_adjustments_conversation_message',
            '/v1/conversation/monitoring/message' => 'aysa_monitoring_conversation_message',
            '/v1/technical/screamingfrog/start' => 'aysa_technical_site_audit_start',
            '/v1/technical/screamingfrog/status' => 'aysa_technical_site_audit_status',
            '/v1/technical/screamingfrog/results' => 'aysa_technical_site_audit_results',
            '/v1/technical/pagespeed/summary' => 'aysa_technical_pagespeed_summary',
            '/v1/keywords/intent/bulk' => 'aysa_keyword_discovery_intent_batch',
            '/v1/keywords/suggestions' => 'aysa_keyword_discovery_keyword_suggestions_refresh',
            '/v1/keywords/overview' => 'aysa_monitoring_keywords_overview',
            '/v1/keywords/gap' => 'aysa_competitor_gap_keywords_gap',
            '/v1/competitors/identify/from-seeds' => 'aysa_competitor_identification_seed_competitors',
            '/adverlink/v1/public/portals' => 'aysa_adverlink_list_portals',
            '/adverlink/v1/public/orders/link-issues' => 'aysa_adverlink_get_link_issues',
            '/adverlink/v1/public/orders/create' => 'aysa_adverlink_create_order',
            '/adverlink/v1/public/orders/track' => 'aysa_adverlink_track_order',
            '/adverlink/v1/public/orders/renew' => 'aysa_adverlink_subscription_renew',
            '/adverlink/v1/exchange/portals' => 'aysa_adverlink_exchange_list_portals',
            '/adverlink/v1/exchange/requests/create' => 'aysa_adverlink_exchange_create_request',
            '/adverlink/v1/exchange/requests/list' => 'aysa_adverlink_exchange_list_requests',
            '/adverlink/v1/exchange/requests/get' => 'aysa_adverlink_exchange_get_request',
            '/adverlink/v1/exchange/requests/respond' => 'aysa_adverlink_exchange_respond_request',
            '/adverlink/v1/exchange/requests/activate' => 'aysa_adverlink_exchange_activate_link',
            '/adverlink/v1/exchange/requests/publication' => 'aysa_adverlink_exchange_submit_publication',
            '/adverlink/v1/exchange/requests/verify' => 'aysa_adverlink_exchange_refresh_verification',
            '/adverlink/v1/exchange/requests/state' => 'aysa_adverlink_exchange_change_state',
        );
        if (isset($map[$path])) {
            return $map[$path];
        }
        if (strpos($path, '/blog-content/') === 0) {
            return 'aysa_content_generate_article';
        }
        return '';
    }

    public function handle_technical_site_audit_start() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_technical_schema_resolve_project_id($project_id);
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $target_url = isset($_POST['target_url']) ? esc_url_raw((string) wp_unslash($_POST['target_url'])) : '';
        if ($target_url === '') {
            $target_url = $this->aysa_technical_resolve_target_url($project_id);
        }

        $job = $this->aysa_project_jobs_enqueue(
            $project_id,
            'technical',
            'site_audit',
            array(
                'project_id' => $project_id,
                'url' => $target_url,
                'job_id' => 'wp_' . $project_id . '_' . gmdate('Ymd_His'),
                'mode' => 'baseline_full',
            ),
            array(
                'dedupe_key' => md5(wp_json_encode(array($project_id, $target_url, 'baseline_full'))),
                'input_summary' => array(
                    'target_url' => $target_url,
                ),
            )
        );
        if (is_wp_error($job)) {
            wp_send_json_error($job->get_error_message());
            return;
        }

        $state = get_option($this->aysa_technical_site_audit_state_key($project_id), array());
        if (!is_array($state)) {
            $state = array();
        }
        $state = array_merge($state, array(
            'project_id' => $project_id,
            'job_id' => sanitize_text_field((string) ($job['job_id'] ?? '')),
            'status' => sanitize_key((string) ($job['status'] ?? 'queued')),
            'target_url' => esc_url_raw($target_url),
            'updated_at' => current_time('mysql'),
            'provider' => 'aysa_site_audit',
            'source' => 'legacy_plugin',
            'orchestration' => 'project_jobs',
        ));
        update_option($this->aysa_technical_site_audit_state_key($project_id), $state, false);

        wp_send_json_success(array(
            'state' => $state,
            'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
            'project_job_status' => array(
                'run_id' => (string) ($job['job_id'] ?? ''),
                'status' => (string) ($job['status'] ?? 'queued'),
                'worker_ref' => (string) ($job['worker_ref'] ?? ''),
                'worker_status' => (string) ($job['worker_status'] ?? ''),
                'error' => (string) ($job['error_summary'] ?? ''),
            ),
            'project_job_rack' => $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id)),
        ));
    }

    public function handle_technical_site_audit_status() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_technical_schema_resolve_project_id($project_id);

        $state_key = $this->aysa_technical_site_audit_state_key($project_id);
        $state = get_option($state_key, array());
        if (!is_array($state)) {
            $state = array();
        }
        $local_snapshot = $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id);
        $has_local_snapshot = $this->aysa_technical_site_audit_snapshot_has_data($local_snapshot);
        $force_refresh = !empty($_POST['refresh']) && (string) $_POST['refresh'] !== '0';
        $cached_audit = (!empty($state['audit_snapshot']) && is_array($state['audit_snapshot'])) ? $state['audit_snapshot'] : array();
        $cached_files = (!empty($state['result_files']) && is_array($state['result_files'])) ? $state['result_files'] : array();

        $job_id = isset($_POST['job_id']) ? sanitize_text_field((string) wp_unslash($_POST['job_id'])) : '';
        if ($job_id === '' && !empty($state['job_id'])) {
            $job_id = sanitize_text_field((string) $state['job_id']);
        }
        if (!$force_refresh) {
            $cached_state = array_merge(array(
                'project_id' => $project_id,
                'job_id' => $job_id,
                'status' => $job_id === '' ? 'idle' : (string) ($state['status'] ?? 'unknown'),
            ), $state);
            wp_send_json_success(array(
                'state' => $cached_state,
                'cached' => true,
                'snapshot' => $local_snapshot,
                'audit' => $cached_audit,
                'files' => $cached_files,
            ));
            return;
        }

        if (($state['provider'] ?? '') === 'local_wordpress') {
            $cached_state = array_merge(array(
                'project_id' => $project_id,
                'job_id' => $job_id,
                'status' => (string) ($state['status'] ?? 'completed'),
            ), $state);
            wp_send_json_success(array(
                'state' => $cached_state,
                'cached' => true,
                'snapshot' => $local_snapshot,
                'audit' => $cached_audit,
                'files' => $cached_files,
            ));
            return;
        }

        if ($job_id === '') {
            wp_send_json_success(array(
                'state' => array(
                    'project_id' => $project_id,
                    'job_id' => '',
                    'status' => $has_local_snapshot ? (string) ($state['status'] ?? 'completed') : 'idle',
                ),
                'cached' => true,
                'snapshot' => $local_snapshot,
                'audit' => $cached_audit,
                'files' => $cached_files,
            ));
            return;
        }

        $result = $this->aysa_broker_request('POST', '/v1/technical/screamingfrog/status', array('job_id' => $job_id), 45);
        if (is_wp_error($result)) {
            $fallback_state = array_merge(array(
                'project_id' => $project_id,
                'job_id' => $job_id,
                'status' => (string) ($state['status'] ?? 'unknown'),
            ), $state);
            wp_send_json_success(array(
                'state' => $fallback_state,
                'cached' => true,
                'stale' => true,
                'warning' => $this->aysa_humanize_broker_error($result, 'Broker unavailable') . ' — showing cached local snapshot.',
                'snapshot' => $local_snapshot,
                'audit' => $cached_audit,
                'files' => $cached_files,
            ));
            return;
        }

        $status = is_array($result) ? (string) ($result['status'] ?? 'unknown') : 'unknown';
        $state = array_merge($state, array(
            'project_id' => $project_id,
            'job_id' => $job_id,
            'status' => $status,
            'updated_at' => current_time('mysql'),
            'job_dir' => is_array($result) ? (string) ($result['job_dir'] ?? '') : '',
            'mode' => is_array($result) ? (string) ($result['mode'] ?? ($state['mode'] ?? 'baseline_full')) : (string) ($state['mode'] ?? 'baseline_full'),
            'config_path' => is_array($result) ? (string) ($result['config_path'] ?? ($state['config_path'] ?? '')) : (string) ($state['config_path'] ?? ''),
            'export_tabs' => is_array($result) ? (string) ($result['export_tabs'] ?? ($state['export_tabs'] ?? '')) : (string) ($state['export_tabs'] ?? ''),
            'bulk_exports' => is_array($result) ? (string) ($result['bulk_exports'] ?? ($state['bulk_exports'] ?? '')) : (string) ($state['bulk_exports'] ?? ''),
            'reports' => is_array($result) ? (string) ($result['reports'] ?? ($state['reports'] ?? '')) : (string) ($state['reports'] ?? ''),
            'artifacts' => is_array($result) && isset($result['artifacts']) && is_array($result['artifacts']) ? $result['artifacts'] : array(),
        ));
        $state = $this->aysa_technical_site_audit_merge_local_state($project_id, $state);
        update_option($state_key, $state, false);
        if (in_array((string) ($state['status'] ?? ''), array('completed', 'failed'), true)) {
            $this->aysa_project_jobs_finalize(
                $project_id,
                $this->aysa_project_jobs_make_worker_ref('technical_site_audit', 'site_audit', (string) ($state['job_id'] ?? '')),
                (string) ($state['status'] ?? 'failed'),
                isset($state['audit_snapshot']) && is_array($state['audit_snapshot']) ? $state['audit_snapshot'] : $state,
                (string) ($state['last_error'] ?? '')
            );
        }

        wp_send_json_success(array(
            'state' => $state,
            'broker' => $result,
            'cached' => false,
            'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
            'audit' => $cached_audit,
            'files' => $cached_files,
            'project_job_rack' => $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id)),
        ));
    }

    public function handle_technical_site_audit_results() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_technical_schema_resolve_project_id($project_id);
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $state_key = $this->aysa_technical_site_audit_state_key($project_id);
        $state = get_option($state_key, array());
        if (!is_array($state)) {
            $state = array();
        }
        $local_snapshot = $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id);
        $has_local_snapshot = $this->aysa_technical_site_audit_snapshot_has_data($local_snapshot);
        $force_refresh = !empty($_POST['refresh']) && (string) $_POST['refresh'] !== '0';
        $cached_audit = (!empty($state['audit_snapshot']) && is_array($state['audit_snapshot'])) ? $state['audit_snapshot'] : array();
        $cached_files = (!empty($state['result_files']) && is_array($state['result_files'])) ? $state['result_files'] : array();

        $job_id = isset($_POST['job_id']) ? sanitize_text_field((string) wp_unslash($_POST['job_id'])) : '';
        if ($job_id === '' && !empty($state['job_id'])) {
            $job_id = sanitize_text_field((string) $state['job_id']);
        }
        if (!$force_refresh && (!empty($cached_audit) || !empty($cached_files))) {
            $cached_state = array_merge(array(
                'project_id' => $project_id,
                'job_id' => $job_id,
                'status' => (string) ($state['status'] ?? ($job_id === '' ? 'idle' : 'unknown')),
            ), $state);
            $cached_state = $this->aysa_technical_site_audit_bill_if_needed($project_id, $cached_state, $cached_audit);
            if ($cached_state !== $state) {
                update_option($state_key, $cached_state, false);
            }
            wp_send_json_success(array(
                'state' => $cached_state,
                'cached' => true,
                'snapshot' => $local_snapshot,
                'audit' => $cached_audit,
                'files' => $cached_files,
            ));
            return;
        }

        if (($state['provider'] ?? '') === 'local_wordpress') {
            $cached_state = array_merge(array(
                'project_id' => $project_id,
                'job_id' => $job_id,
                'status' => (string) ($state['status'] ?? 'completed'),
            ), $state);
            wp_send_json_success(array(
                'state' => $cached_state,
                'cached' => true,
                'snapshot' => $local_snapshot,
                'audit' => $cached_audit,
                'files' => $cached_files,
            ));
            return;
        }

        if ($job_id === '') {
            if ($has_local_snapshot) {
                wp_send_json_success(array(
                    'state' => array_merge(array(
                        'project_id' => $project_id,
                        'job_id' => '',
                        'status' => (string) ($state['status'] ?? 'completed'),
                    ), $state),
                    'cached' => true,
                    'snapshot' => $local_snapshot,
                    'audit' => $cached_audit,
                    'files' => $cached_files,
                ));
                return;
            }
            wp_send_json_error('Missing job id');
            return;
        }

        $preview_rows = isset($_POST['preview_rows']) ? intval($_POST['preview_rows']) : 20;
        if ($preview_rows < 1) {
            $preview_rows = 20;
        }
        if ($preview_rows > 100) {
            $preview_rows = 100;
        }

        $result = $this->aysa_broker_request('POST', '/v1/technical/screamingfrog/results', array(
            'job_id' => $job_id,
            'preview_rows' => $preview_rows,
        ), 60);
        if (is_wp_error($result)) {
            if (!empty($cached_audit) || !empty($cached_files)) {
                $fallback_state = array_merge(array(
                    'project_id' => $project_id,
                    'job_id' => $job_id,
                    'status' => (string) ($state['status'] ?? 'unknown'),
                ), $state);
                wp_send_json_success(array(
                    'state' => $fallback_state,
                    'cached' => true,
                    'stale' => true,
                    'warning' => $this->aysa_humanize_broker_error($result, 'Broker unavailable') . ' — showing cached local snapshot.',
                    'snapshot' => $local_snapshot,
                    'audit' => $cached_audit,
                    'files' => $cached_files,
                ));
                return;
            }
            wp_send_json_error($this->aysa_humanize_broker_error($result, 'Could not load site audit results.'));
            return;
        }

        $files = is_array($result) && isset($result['files']) && is_array($result['files']) ? $result['files'] : array();
        $audit_payload = is_array($result) && isset($result['audit']) && is_array($result['audit']) ? $result['audit'] : array();
        $state = array_merge($state, array(
            'project_id' => $project_id,
            'job_id' => $job_id,
            'status' => is_array($result) ? (string) ($result['status'] ?? 'unknown') : 'unknown',
            'updated_at' => current_time('mysql'),
            'mode' => is_array($result) ? (string) ($result['mode'] ?? ($state['mode'] ?? 'baseline_full')) : (string) ($state['mode'] ?? 'baseline_full'),
            'config_path' => is_array($result) ? (string) ($result['config_path'] ?? ($state['config_path'] ?? '')) : (string) ($state['config_path'] ?? ''),
            'export_tabs' => is_array($result) ? (string) ($result['export_tabs'] ?? ($state['export_tabs'] ?? '')) : (string) ($state['export_tabs'] ?? ''),
            'bulk_exports' => is_array($result) ? (string) ($result['bulk_exports'] ?? ($state['bulk_exports'] ?? '')) : (string) ($state['bulk_exports'] ?? ''),
            'reports' => is_array($result) ? (string) ($result['reports'] ?? ($state['reports'] ?? '')) : (string) ($state['reports'] ?? ''),
            'result_count' => count($files),
            'latest_output_dir' => is_array($result) ? (string) ($result['latest_output_dir'] ?? '') : '',
            'audit_snapshot' => $audit_payload,
            'result_files' => $files,
        ));
        $state = $this->aysa_technical_site_audit_merge_local_state($project_id, $state);
        $state = $this->aysa_technical_site_audit_bill_if_needed($project_id, $state, $audit_payload);
        $version_id = $this->aysa_sf_site_audit_persist_version($project_id, $state);
        if ($version_id > 0) {
            $latest_version = $this->aysa_sf_site_audit_get_latest_version_row($project_id);
            if (is_array($latest_version) && !empty($latest_version)) {
                $state['version_id'] = intval($latest_version['id'] ?? 0);
                $state['version_number'] = intval($latest_version['version_number'] ?? 0);
            }
        }
        update_option($state_key, $state, false);

        wp_send_json_success(array(
            'state' => $state,
            'snapshot' => $this->aysa_adjustments_get_technical_site_audit_snapshot($project_id),
            'broker' => $result,
            'cached' => false,
            'audit' => $audit_payload,
            'files' => $files,
        ));
    }

    public function handle_technical_pagespeed_summary() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $refresh = !empty($_POST['refresh']) && (string) $_POST['refresh'] !== '0';
        $target_url = isset($_POST['target_url']) ? esc_url_raw((string) wp_unslash($_POST['target_url'])) : '';
        if ($target_url === '') {
            $target_url = $this->aysa_technical_resolve_target_url();
        }

        $state_key = $this->aysa_technical_pagespeed_state_key($project_id);
        $cached = get_option($state_key, array());
        if (!is_array($cached)) {
            $cached = array();
        }

        $max_age_seconds = 6 * HOUR_IN_SECONDS;
        if (!$refresh && !empty($cached['updated_at_unix']) && (time() - intval($cached['updated_at_unix']) < $max_age_seconds) && !empty($cached['data']) && is_array($cached['data'])) {
            wp_send_json_success(array(
                'cached' => true,
                'data' => $cached['data'],
                'updated_at' => $cached['updated_at'],
            ));
            return;
        }

        $payload = array(
            'project_id' => $project_id,
            'url' => $target_url,
            'force_refresh' => $refresh,
        );
        $result = $this->aysa_broker_request('POST', '/v1/technical/pagespeed/summary', $payload, 90);
        if (is_wp_error($result)) {
            $broker_error = $result;
            $fallback = $this->aysa_pagespeed_summary_fallback($target_url);
            if (is_wp_error($fallback)) {
                $legacy = $this->aysa_pagespeed_summary_from_legacy_endpoints($project_id, $target_url, $refresh);
                if (!is_wp_error($legacy)) {
                    $legacy['warning'] = $broker_error->get_error_message() . ' | ' . $fallback->get_error_message();
                    $result = $legacy;
                } else {
                $result = array(
                    'ok' => true,
                    'requested_at' => gmdate('c'),
                    'url' => $target_url,
                    'overview' => array(
                        'pageSpeedDesktop' => 0,
                        'pageSpeedMobile' => 0,
                        'mobileFriendly' => 0,
                        'indexability' => 90,
                        'requestedAt' => gmdate('c'),
                        'overallScore' => 0,
                    ),
                    'desktop' => array(
                        'strategy' => 'desktop',
                        'requestedAt' => gmdate('c'),
                        'metrics' => array(),
                        'opportunities' => array(),
                    ),
                    'mobile' => array(
                        'strategy' => 'mobile',
                        'requestedAt' => gmdate('c'),
                        'metrics' => array(),
                        'opportunities' => array(),
                    ),
                    'conclusion' => array(
                        'overall_score' => 0,
                        'status' => 'needs_improvement',
                        'summary' => 'Google Page Speed data is temporarily unavailable. Retry after connectivity/API access is restored.',
                        'priority_actions' => array(),
                    ),
                    'warning' => $broker_error->get_error_message() . ' | ' . $fallback->get_error_message() . ' | ' . $legacy->get_error_message(),
                );
                }
            } else {
                $result = $fallback;
            }
        }

        // If the fresh result is degraded (0 scores due to quota/outage), prefer last valid cached dataset.
        if ($this->aysa_pagespeed_is_degraded_result($result) && !empty($cached['data']) && is_array($cached['data']) && !$this->aysa_pagespeed_is_degraded_result($cached['data'])) {
            $cached_data = $cached['data'];
            $warning = isset($result['warning']) ? (string) $result['warning'] : 'Live fetch failed; using last valid cached data.';
            $cached_data['warning'] = $warning;
            wp_send_json_success(array(
                'cached' => true,
                'stale' => true,
                'data' => $cached_data,
                'updated_at' => isset($cached['updated_at']) ? $cached['updated_at'] : '',
            ));
            return;
        }

        $state = array(
            'project_id' => $project_id,
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'data' => $result,
        );
        update_option($state_key, $state, false);
        // Bill only for explicit manual refresh actions, not passive tab/view loads.
        if ($refresh) {
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_technical_pagespeed_summary',
                'technical_pagespeed_summary_manual_refresh',
                array(
                    'module' => 'technical_optimization',
                    'source' => 'live',
                    'target_url' => $target_url,
                    'refresh' => 1,
                ),
                48,
                '/v1/technical/pagespeed/summary'
            );
        }

        wp_send_json_success(array(
            'cached' => false,
            'data' => $result,
            'updated_at' => $state['updated_at'],
        ));
    }

    public function handle_export_pagespeed_pdf() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error(array('message' => 'Invalid nonce'));
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(array('message' => 'Access denied'));
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        if ($project_id <= 0) {
            wp_send_json_error(array('message' => 'Invalid project id'));
            return;
        }

        $snapshot = $this->aysa_adjustments_get_technical_pagespeed_snapshot($project_id);
        if (empty($snapshot['has_data'])) {
            $snapshot = $this->aysa_adjustments_run_technical_pagespeed_refresh($project_id, false);
        }
        if (!is_array($snapshot) || empty($snapshot['has_data'])) {
            wp_send_json_error(array('message' => 'PageSpeed data is not available yet. Refresh the analysis first.'));
            return;
        }

        $state = get_option($this->aysa_technical_pagespeed_state_key($project_id), array());
        if (!is_array($state)) {
            $state = array();
        }
        $state_data = isset($state['data']) && is_array($state['data']) ? $state['data'] : array();
        $raw_payload = isset($state_data['raw']) && is_array($state_data['raw']) ? $state_data['raw'] : array();

        $site_url = isset($snapshot['url']) && is_string($snapshot['url']) && trim($snapshot['url']) !== ''
            ? trim((string) $snapshot['url'])
            : $this->aysa_technical_resolve_target_url();
        $site_host = parse_url($site_url, PHP_URL_HOST);
        if (!is_string($site_host) || trim($site_host) === '') {
            $site_host = parse_url(home_url('/'), PHP_URL_HOST);
        }

        $lang_raw = (string) get_option('aysa_language', get_locale());
        $language_code = strtolower(substr($lang_raw, 0, 2));
        if (!in_array($language_code, array('ro', 'en'), true)) {
            $language_code = 'en';
        }

        $broker_payload = array(
            'project_id' => $project_id,
            'site_url' => $site_url,
            'site_host' => is_string($site_host) ? trim($site_host) : '',
            'brand_name' => sanitize_text_field((string) get_bloginfo('name')),
            'language' => $language_code,
            'force_regenerate' => !empty($_POST['force_regenerate']),
            'snapshot' => $snapshot,
            'raw_payload' => $raw_payload,
        );

        $job = $this->aysa_project_jobs_enqueue(
            $project_id,
            'technical',
            'pagespeed_report',
            $broker_payload,
            array(
                'dedupe_key' => md5(wp_json_encode(array($project_id, $site_url, !empty($_POST['force_regenerate'])))),
                'input_summary' => array(
                    'site_url' => $site_url,
                    'site_host' => is_string($site_host) ? trim($site_host) : '',
                ),
            )
        );

        wp_send_json_success(array(
            'job_id' => (string) ($job['job_id'] ?? ''),
            'status' => (string) ($job['status'] ?? 'queued'),
            'progress_label' => sanitize_text_field((string) (($job['result_summary']['progress_label'] ?? 'Queued'))),
            'worker_ref' => (string) ($job['worker_ref'] ?? ''),
            'worker_status' => (string) ($job['worker_status'] ?? ''),
            'project_job_rack' => $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id)),
        ));
    }

    public function handle_export_pagespeed_pdf_status() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error(array('message' => 'Invalid nonce'));
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(array('message' => 'Access denied'));
            return;
        }

        $job_id = isset($_POST['job_id']) ? sanitize_text_field((string) wp_unslash($_POST['job_id'])) : '';
        if ($job_id === '') {
            wp_send_json_error(array('message' => 'Missing job id'));
            return;
        }

        $result = $this->aysa_broker_request('POST', '/v1/technical/pagespeed/report/status', array(
            'job_id' => $job_id,
        ), 30);
        if (is_wp_error($result)) {
            error_log('[aysa_pagespeed_pdf] status_wp_error ' . wp_json_encode(array(
                'job_id' => $job_id,
                'message' => $result->get_error_message(),
            )));
            wp_send_json_error(array('message' => $result->get_error_message()));
            return;
        }
        if (!is_array($result) || empty($result['ok'])) {
            error_log('[aysa_pagespeed_pdf] status_broker_error ' . wp_json_encode(array(
                'job_id' => $job_id,
                'result' => $result,
            )));
            wp_send_json_error(array(
                'message' => isset($result['error']) ? (string) $result['error'] : 'PageSpeed report status failed.',
                'result' => $result,
            ));
            return;
        }

        $normalized_status = sanitize_key((string) ($result['status'] ?? 'queued'));
        if (in_array($normalized_status, array('completed', 'failed'), true)) {
            $this->aysa_project_jobs_finalize(
                intval($_POST['project_id'] ?? get_option('aysa_project_id', 0)),
                $this->aysa_project_jobs_make_worker_ref('pagespeed_report', 'pagespeed_report', sanitize_text_field((string) ($result['job_id'] ?? $job_id))),
                $normalized_status,
                isset($result['summary']) && is_array($result['summary']) ? $result['summary'] : $result,
                sanitize_text_field((string) ($result['error'] ?? ''))
            );
        }

        wp_send_json_success(array(
            'job_id' => sanitize_text_field((string) ($result['job_id'] ?? $job_id)),
            'status' => sanitize_text_field((string) ($result['status'] ?? 'queued')),
            'progress_label' => sanitize_text_field((string) ($result['progress_label'] ?? '')),
            'download_url' => esc_url_raw((string) ($result['download_url'] ?? '')),
            'file_name' => sanitize_text_field((string) ($result['file_name'] ?? 'aysa-pagespeed-report.pdf')),
            'summary' => isset($result['summary']) && is_array($result['summary']) ? $result['summary'] : array(),
            'updated_at' => sanitize_text_field((string) ($result['updated_at'] ?? '')),
            'error' => sanitize_text_field((string) ($result['error'] ?? '')),
            'project_job_rack' => $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state(intval($_POST['project_id'] ?? get_option('aysa_project_id', 0)))),
        ));
    }

    public function handle_pagespeed_report_email() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error(array('message' => 'Invalid nonce'));
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error(array('message' => 'Access denied'));
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        $recipient = isset($_POST['recipient_email']) ? sanitize_email((string) wp_unslash($_POST['recipient_email'])) : '';
        if ($project_id <= 0) {
            wp_send_json_error(array('message' => 'Invalid project id'));
            return;
        }
        if ($recipient === '' || !is_email($recipient)) {
            wp_send_json_error(array('message' => 'Valid recipient email required.'));
            return;
        }

        $snapshot = $this->aysa_adjustments_get_technical_pagespeed_snapshot($project_id);
        if (empty($snapshot['has_data'])) {
            wp_send_json_error(array('message' => 'PageSpeed snapshot missing. Refresh PageSpeed first.'));
            return;
        }

        $site_url = isset($snapshot['url']) && is_string($snapshot['url']) && trim($snapshot['url']) !== ''
            ? trim((string) $snapshot['url'])
            : $this->aysa_technical_resolve_target_url();
        $site_host = parse_url($site_url, PHP_URL_HOST);
        if (!is_string($site_host) || trim($site_host) === '') {
            $site_host = parse_url(home_url('/'), PHP_URL_HOST);
        }

        $lang_raw = (string) get_option('aysa_language', get_locale());
        $language_code = strtolower(substr($lang_raw, 0, 2));
        if (!in_array($language_code, array('ro', 'en'), true)) {
            $language_code = 'en';
        }

        $report = $this->aysa_broker_request('POST', '/v1/technical/pagespeed/report', array(
            'project_id' => $project_id,
            'site_url' => $site_url,
            'site_host' => is_string($site_host) ? trim($site_host) : '',
            'brand_name' => sanitize_text_field((string) get_bloginfo('name')),
            'language' => $language_code,
            'snapshot' => $snapshot,
        ), 60);
        if (is_wp_error($report) || !is_array($report) || empty($report['ok'])) {
            wp_send_json_error(array(
                'message' => is_wp_error($report) ? $report->get_error_message() : 'Could not generate the PageSpeed report for email.',
            ));
            return;
        }

        $download_url = esc_url_raw((string) ($report['download_url'] ?? ''));
        $subject = 'AYSA PageSpeed AI Report';
        $body_lines = array(
            'The latest PageSpeed AI report is ready.',
            '',
            'Website: ' . $site_url,
            'Overall score: ' . intval($report['summary']['overall_score'] ?? 0),
            'Desktop score: ' . intval($report['summary']['desktop_score'] ?? 0),
            'Mobile score: ' . intval($report['summary']['mobile_score'] ?? 0),
            'Verdict: ' . sanitize_text_field((string) ($report['summary']['verdict'] ?? '')),
            '',
            'Download report: ' . $download_url,
            'Generated at: ' . sanitize_text_field((string) ($report['created_at'] ?? current_time('mysql'))),
        );

        if (!wp_mail($recipient, $subject, implode("\n", $body_lines), array('Content-Type: text/plain; charset=UTF-8'))) {
            wp_send_json_error(array('message' => 'Email send failed.'));
            return;
        }

        $state_key = $this->aysa_technical_pagespeed_state_key($project_id);
        $state = get_option($state_key, array());
        if (!is_array($state)) {
            $state = array();
        }
        $state['last_report_email'] = array(
            'recipient_email' => $recipient,
            'download_url' => $download_url,
            'file_name' => sanitize_text_field((string) ($report['file_name'] ?? 'aysa-pagespeed-report.pdf')),
            'sent_at' => current_time('mysql'),
        );
        update_option($state_key, $state, false);

        wp_send_json_success(array(
            'recipient_email' => $recipient,
            'download_url' => $download_url,
            'file_name' => sanitize_text_field((string) ($report['file_name'] ?? 'aysa-pagespeed-report.pdf')),
            'sent_at' => sanitize_text_field((string) ($state['last_report_email']['sent_at'] ?? current_time('mysql'))),
        ));
    }

    public function handle_technical_sitemap_robots_audit() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $refresh = !empty($_POST['refresh']) && (string) $_POST['refresh'] !== '0';
        $target_url = isset($_POST['target_url']) ? esc_url_raw((string) wp_unslash($_POST['target_url'])) : '';
        if ($target_url === '') {
            $target_url = $this->aysa_technical_resolve_target_url();
        }

        $state_key = $this->aysa_technical_sitemap_robots_state_key($project_id);
        $cached = get_option($state_key, array());
        if (!is_array($cached)) {
            $cached = array();
        }

        $max_age_seconds = 6 * HOUR_IN_SECONDS;
        if (
            !$refresh &&
            !empty($cached['updated_at_unix']) &&
            (time() - intval($cached['updated_at_unix']) < $max_age_seconds) &&
            !empty($cached['data']) &&
            is_array($cached['data'])
        ) {
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_technical_sitemap_robots_audit',
                'technical_sitemap_robots_audit',
                array(
                    'module' => 'technical_optimization',
                    'source' => 'cache',
                    'target_url' => $target_url,
                ),
                24,
                '/v1/technical/sitemap-robots/audit'
            );
            wp_send_json_success(array(
                'cached' => true,
                'data' => $cached['data'],
                'updated_at' => isset($cached['updated_at']) ? $cached['updated_at'] : '',
            ));
            return;
        }

        $audit = $this->aysa_technical_build_sitemap_robots_audit($target_url);
        $state = array(
            'project_id' => $project_id,
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'data' => $audit,
        );
        update_option($state_key, $state, false);
        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_technical_sitemap_robots_audit',
            'technical_sitemap_robots_audit',
            array(
                'module' => 'technical_optimization',
                'source' => 'live',
                'target_url' => $target_url,
            ),
            24,
            '/v1/technical/sitemap-robots/audit'
        );

        wp_send_json_success(array(
            'cached' => false,
            'data' => $audit,
            'updated_at' => $state['updated_at'],
        ));
    }

    public function handle_technical_schema_snapshot() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_technical_schema_resolve_project_id($project_id);
        $snapshot = $this->aysa_schema_inventory_get_ui_snapshot($project_id);
        $refresh_job = $this->aysa_technical_schema_get_current_refresh_job($project_id);
        if ($refresh_job && in_array((string) ($refresh_job['status'] ?? ''), array('queued', 'running'), true)) {
            $snapshot['status'] = 'reconciling';
            $snapshot['job'] = $this->aysa_technical_schema_get_job_response($refresh_job);
        }
        $snapshot['resolved_project_id'] = $project_id;
        $snapshot['snapshot_updated_at'] = current_time('mysql');

        wp_send_json_success(array(
            'snapshot' => $snapshot,
        ));
    }

    private function aysa_technical_schema_start_queued_job($payload) {
        $project_id = intval($payload['project_id'] ?? 0);
        $mode = sanitize_key((string) ($payload['mode'] ?? 'validate'));
        $scope = sanitize_key((string) ($payload['scope'] ?? 'selection'));
        $items = isset($payload['items']) && is_array($payload['items']) ? $payload['items'] : array();
        if ($project_id <= 0 || empty($items)) {
            return new WP_Error('invalid_technical_schema_request', 'No valid schema rows were selected.');
        }

        $job = $this->aysa_technical_schema_create_job($project_id, $mode, $scope, $items, 'worker');
        $this->aysa_technical_schema_schedule_job((string) $job['job_id'], 1);

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_technical_schema_job_start',
            'technical_schema_job_start',
            array(
                'module' => 'technical_schema',
                'mode' => $mode,
                'scope' => $scope,
                'selected_count' => count($items),
                'job_id' => (string) ($job['job_id'] ?? ''),
            ),
            8,
            '/v1/technical/schema/job/start'
        );

        return array(
            'status' => sanitize_key((string) ($job['status'] ?? 'queued')),
            'worker_ref' => $this->aysa_project_jobs_make_worker_ref('technical_schema', 'schema_job', (string) ($job['job_id'] ?? '')),
            'worker_status' => sanitize_key((string) ($job['status'] ?? 'queued')),
            'result_summary' => array('selected_count' => count($items)),
            'started_at' => time(),
        );
    }

    public function handle_technical_schema_refresh() {
        $this->handle_technical_schema_refresh_start();
    }

    public function handle_technical_schema_rewrite_start() {
        $_POST['mode'] = 'rewrite_recommended';
        $_POST['scope'] = 'selection';
        $this->handle_technical_schema_job_start();
    }

    public function handle_technical_schema_rewrite_status() {
        $this->handle_technical_schema_job_status();
    }

    public function handle_technical_sitemap_generate() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $target_url = isset($_POST['target_url']) ? esc_url_raw((string) wp_unslash($_POST['target_url'])) : '';
        if ($target_url === '') {
            $target_url = $this->aysa_technical_resolve_target_url();
        }

        $generation = $this->aysa_technical_generate_own_sitemap($target_url);
        $audit = $this->aysa_technical_build_sitemap_robots_audit($target_url);
        $state = array(
            'project_id' => $project_id,
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'data' => $audit,
        );
        update_option($this->aysa_technical_sitemap_robots_state_key($project_id), $state, false);
        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_technical_sitemap_generate',
            'technical_sitemap_generate',
            array(
                'module' => 'technical_optimization',
                'target_url' => $target_url,
                'mode' => sanitize_text_field((string) ($generation['mode'] ?? '')),
                'entries_count' => intval($generation['entries_count'] ?? 0),
            ),
            18,
            '/v1/technical/sitemap/generate'
        );

        wp_send_json_success(array(
            'generated' => true,
            'generation' => $generation,
            'data' => $audit,
            'updated_at' => $state['updated_at'],
        ));
    }

    public function handle_technical_robots_save() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $target_url = isset($_POST['target_url']) ? esc_url_raw((string) wp_unslash($_POST['target_url'])) : '';
        if ($target_url === '') {
            $target_url = $this->aysa_technical_resolve_target_url();
        }
        $robots_content = isset($_POST['robots_content']) ? (string) wp_unslash($_POST['robots_content']) : '';
        $saved = $this->aysa_technical_save_robots_content($robots_content);
        if (is_wp_error($saved)) {
            wp_send_json_error($saved->get_error_message());
            return;
        }

        $audit = $this->aysa_technical_build_sitemap_robots_audit($target_url);
        $state = array(
            'project_id' => $project_id,
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'data' => $audit,
        );
        update_option($this->aysa_technical_sitemap_robots_state_key($project_id), $state, false);
        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_technical_robots_save',
            'technical_robots_save',
            array(
                'module' => 'technical_optimization',
                'target_url' => $target_url,
            ),
            12,
            '/v1/technical/robots/save'
        );

        wp_send_json_success(array(
            'saved' => true,
            'data' => $audit,
            'updated_at' => $state['updated_at'],
        ));
    }

    public function handle_technical_robots_restore_default() {
        if (!$this->aysa_verify_technical_audit_nonce()) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        $project_id = $this->aysa_resolve_project_id_with_local_fallback($project_id);
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $target_url = isset($_POST['target_url']) ? esc_url_raw((string) wp_unslash($_POST['target_url'])) : '';
        if ($target_url === '') {
            $target_url = $this->aysa_technical_resolve_target_url();
        }

        $sitemaps = $this->aysa_technical_fetch_sitemap_candidates($target_url);
        $primary_sitemap = '';
        foreach ($sitemaps as $row) {
            if (!empty($row['reachable']) && !empty($row['url'])) {
                $primary_sitemap = (string) $row['url'];
                break;
            }
        }

        $content = $this->aysa_technical_robots_default_content($target_url, $primary_sitemap);
        $saved = $this->aysa_technical_save_robots_content($content);
        if (is_wp_error($saved)) {
            wp_send_json_error($saved->get_error_message());
            return;
        }

        $audit = $this->aysa_technical_build_sitemap_robots_audit($target_url);
        $state = array(
            'project_id' => $project_id,
            'updated_at' => current_time('mysql'),
            'updated_at_unix' => time(),
            'data' => $audit,
        );
        update_option($this->aysa_technical_sitemap_robots_state_key($project_id), $state, false);
        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_technical_robots_restore_default',
            'technical_robots_restore_default',
            array(
                'module' => 'technical_optimization',
                'target_url' => $target_url,
            ),
            12,
            '/v1/technical/robots/restore-default'
        );

        wp_send_json_success(array(
            'restored' => true,
            'data' => $audit,
            'updated_at' => $state['updated_at'],
        ));
    }

    private function aysa_pagespeed_is_degraded_result($result) {
        if (!is_array($result)) {
            return true;
        }
        $overview = isset($result['overview']) && is_array($result['overview']) ? $result['overview'] : array();
        $desktop = intval($overview['pageSpeedDesktop'] ?? 0);
        $mobile = intval($overview['pageSpeedMobile'] ?? 0);
        $overall = intval($overview['overallScore'] ?? 0);
        $has_metrics = !empty($result['desktop']['metrics']) || !empty($result['mobile']['metrics']);
        if ($has_metrics) {
            return false;
        }
        return ($desktop <= 0 && $mobile <= 0 && $overall <= 0);
    }

    private function aysa_pagespeed_summary_fallback($target_url) {
        $desktop_raw = $this->aysa_pagespeed_fetch_strategy($target_url, 'desktop');
        if (is_wp_error($desktop_raw)) {
            return $desktop_raw;
        }
        $mobile_raw = $this->aysa_pagespeed_fetch_strategy($target_url, 'mobile');
        if (is_wp_error($mobile_raw)) {
            return $mobile_raw;
        }

        $desktop = $this->aysa_pagespeed_normalize_strategy($desktop_raw, 'desktop');
        $mobile = $this->aysa_pagespeed_normalize_strategy($mobile_raw, 'mobile');
        $desktop_score = intval(round(floatval($desktop['metrics']['performance']['score_percent'] ?? 0)));
        $mobile_score = intval(round(floatval($mobile['metrics']['performance']['score_percent'] ?? 0)));
        $overall = intval(round(($desktop_score + $mobile_score) / 2));

        $priority = array();
        $source_pool = array_merge(
            is_array($desktop['opportunities']) ? $desktop['opportunities'] : array(),
            is_array($mobile['opportunities']) ? $mobile['opportunities'] : array()
        );
        usort($source_pool, static function($a, $b) {
            return intval($b['impact'] ?? 0) <=> intval($a['impact'] ?? 0);
        });
        foreach ($source_pool as $row) {
            if (!is_array($row)) {
                continue;
            }
            $id = sanitize_key((string) ($row['id'] ?? ''));
            if ($id === '' || isset($priority[$id])) {
                continue;
            }
            $priority[$id] = array(
                'id' => $id,
                'title' => (string) ($row['title'] ?? $id),
                'displayValue' => (string) ($row['displayValue'] ?? ''),
                'impact' => intval($row['impact'] ?? 0),
            );
            if (count($priority) >= 5) {
                break;
            }
        }

        $summary = 'Good baseline with optimization room.';
        $status = 'good';
        if ($overall >= 90) {
            $summary = 'Excellent speed profile on desktop and mobile.';
            $status = 'excellent';
        } elseif ($overall < 70) {
            $summary = 'Performance needs urgent improvements, especially on mobile.';
            $status = $overall < 50 ? 'critical' : 'needs_improvement';
        }

        return array(
            'ok' => true,
            'requested_at' => gmdate('c'),
            'url' => $target_url,
            'overview' => array(
                'pageSpeedDesktop' => $desktop_score,
                'pageSpeedMobile' => $mobile_score,
                'mobileFriendly' => $mobile_score,
                'indexability' => 90,
                'requestedAt' => gmdate('c'),
                'overallScore' => $overall,
            ),
            'desktop' => $desktop,
            'mobile' => $mobile,
            'conclusion' => array(
                'overall_score' => $overall,
                'status' => $status,
                'summary' => $summary,
                'priority_actions' => array_values($priority),
            ),
        );
    }

    private function aysa_pagespeed_fetch_strategy($target_url, $strategy = 'desktop') {
        $query = array(
            'url' => (string) $target_url,
            'strategy' => (string) $strategy,
            'category' => 'PERFORMANCE',
        );
        $api_key = $this->aysa_get_pagespeed_api_key();
        if ($api_key !== '') {
            $query['key'] = $api_key;
        }
        $endpoint = 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed?' . http_build_query($query);
        $resp = wp_remote_get($endpoint, array(
            'timeout' => 45,
            'headers' => array('Accept' => 'application/json'),
        ));
        if (is_wp_error($resp)) {
            return $resp;
        }
        $code = intval(wp_remote_retrieve_response_code($resp));
        $body = (string) wp_remote_retrieve_body($resp);
        $decoded = json_decode($body, true);
        if (!is_array($decoded)) {
            return new WP_Error('pagespeed_invalid_json', 'Invalid JSON from Google PageSpeed API.');
        }
        if ($code < 200 || $code >= 300 || isset($decoded['error'])) {
            $msg = isset($decoded['error']['message']) ? (string) $decoded['error']['message'] : 'Google PageSpeed API error.';
            return new WP_Error('pagespeed_http_error', $msg);
        }
        return $decoded;
    }

    private function aysa_pagespeed_normalize_strategy($raw, $strategy = 'desktop') {
        if (!is_array($raw)) {
            return array(
                'strategy' => (string) $strategy,
                'requestedAt' => gmdate('c'),
                'metrics' => array(),
                'opportunities' => array(),
            );
        }
        $lhr = (isset($raw['lighthouseResult']) && is_array($raw['lighthouseResult'])) ? $raw['lighthouseResult'] : array();
        $categories = (isset($lhr['categories']) && is_array($lhr['categories'])) ? $lhr['categories'] : array();
        $audits = (isset($lhr['audits']) && is_array($lhr['audits'])) ? $lhr['audits'] : array();
        $perf_score = floatval($categories['performance']['score'] ?? 0) * 100;
        $metrics = array(
            'performance' => array(
                'score' => round(floatval($categories['performance']['score'] ?? 0), 4),
                'score_percent' => round($perf_score, 0),
            ),
            'firstContentfulPaint' => isset($audits['first-contentful-paint']['numericValue']) ? floatval($audits['first-contentful-paint']['numericValue']) : null,
            'largestContentfulPaint' => isset($audits['largest-contentful-paint']['numericValue']) ? floatval($audits['largest-contentful-paint']['numericValue']) : null,
            'totalBlockingTime' => isset($audits['total-blocking-time']['numericValue']) ? floatval($audits['total-blocking-time']['numericValue']) : null,
            'cumulativeLayoutShift' => isset($audits['cumulative-layout-shift']['numericValue']) ? floatval($audits['cumulative-layout-shift']['numericValue']) : null,
            'speedIndex' => isset($audits['speed-index']['numericValue']) ? floatval($audits['speed-index']['numericValue']) : null,
            'interactive' => isset($audits['interactive']['numericValue']) ? floatval($audits['interactive']['numericValue']) : null,
            'totalByteWeight' => isset($audits['total-byte-weight']['numericValue']) ? floatval($audits['total-byte-weight']['numericValue']) : null,
        );

        $opportunities = array();
        foreach ($audits as $id => $audit) {
            if (!is_array($audit)) {
                continue;
            }
            $savings_ms = isset($audit['details']['overallSavingsMs']) ? floatval($audit['details']['overallSavingsMs']) : 0;
            $savings_bytes = isset($audit['details']['overallSavingsBytes']) ? floatval($audit['details']['overallSavingsBytes']) : 0;
            $score = isset($audit['score']) ? floatval($audit['score']) : 1.0;
            if ($savings_ms <= 0 && $savings_bytes <= 0 && $score >= 0.9) {
                continue;
            }
            $impact = intval(round($savings_ms + ($savings_bytes / 1024)));
            $opportunities[] = array(
                'id' => (string) $id,
                'title' => (string) ($audit['title'] ?? $id),
                'description' => (string) ($audit['description'] ?? ''),
                'displayValue' => (string) ($audit['displayValue'] ?? 'Needs improvement'),
                'savings_ms' => $savings_ms,
                'savings_bytes' => $savings_bytes,
                'score' => $score,
                'impact' => $impact,
            );
        }
        usort($opportunities, static function($a, $b) {
            return intval($b['impact'] ?? 0) <=> intval($a['impact'] ?? 0);
        });
        $opportunities = array_slice($opportunities, 0, 12);

        return array(
            'strategy' => (string) $strategy,
            'requestedAt' => gmdate('c'),
            'metrics' => $metrics,
            'opportunities' => $opportunities,
        );
    }

    private function aysa_get_pagespeed_api_key() {
        if (defined('AYSA_PAGESPEED_API_KEY')) {
            $val = trim((string) AYSA_PAGESPEED_API_KEY);
            if ($val !== '') {
                return $val;
            }
        }
        $opt = get_option('aysa_pagespeed_api_key', '');
        if (is_string($opt)) {
            $opt = trim($opt);
            if ($opt !== '') {
                return $opt;
            }
        }

        // Local dev convenience: reuse broker config key when available.
        $candidate_paths = array(
            trailingslashit(ABSPATH) . '../../../api_projects/api.aysa.ai/.secure/config.php',
            trailingslashit(ABSPATH) . '../../api_projects/api.aysa.ai/.secure/config.php',
        );
        foreach ($candidate_paths as $path) {
            $real = realpath($path);
            if (!$real || !is_readable($real)) {
                continue;
            }
            $cfg = @include $real;
            if (!is_array($cfg)) {
                continue;
            }
            $key = isset($cfg['google']['pagespeed_api_key']) ? trim((string) $cfg['google']['pagespeed_api_key']) : '';
            if ($key !== '') {
                return $key;
            }
        }
        return '';
    }

    private function aysa_pagespeed_summary_from_legacy_endpoints($project_id, $target_url, $refresh = false) {
        if (!class_exists('Aysa_API_Config')) {
            return new WP_Error('pagespeed_legacy_missing_config', 'Aysa_API_Config is not available.');
        }

        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return new WP_Error('pagespeed_legacy_missing_project', 'Missing project id for legacy PageSpeed endpoint.');
        }

        if ($refresh) {
            $this->aysa_pagespeed_legacy_post($project_id, $target_url, 'desktop');
            $this->aysa_pagespeed_legacy_post($project_id, $target_url, 'mobile');
            $this->aysa_pagespeed_legacy_post_overview($project_id, $target_url);
        }

        $overview_url = Aysa_API_Config::get_endpoint_url_with_params('pagespeed_overview_get', array('projectId' => $project_id));
        $metrics_url = Aysa_API_Config::get_endpoint_url_with_params('pagespeed_metrics_get', array('projectId' => $project_id));
        if (!$overview_url && !$metrics_url) {
            return new WP_Error('pagespeed_legacy_missing_endpoint', 'Legacy PageSpeed endpoints are not configured.');
        }

        $overview_rows = $overview_url ? $this->aysa_pagespeed_legacy_get_json($overview_url) : array();
        $metrics_rows = $metrics_url ? $this->aysa_pagespeed_legacy_get_json($metrics_url) : array();

        $overview_record = $this->aysa_pagespeed_pick_latest_overview($overview_rows);
        $desktop_record = $this->aysa_pagespeed_pick_latest_metrics_by_strategy($metrics_rows, 'desktop');
        $mobile_record = $this->aysa_pagespeed_pick_latest_metrics_by_strategy($metrics_rows, 'mobile');

        if (!$desktop_record && !$mobile_record && $overview_record && isset($overview_record['metrics']) && is_array($overview_record['metrics'])) {
            $desktop_record = $overview_record;
            $mobile_record = $overview_record;
        }
        if (!$desktop_record && !$mobile_record) {
            return new WP_Error('pagespeed_legacy_empty', 'No legacy PageSpeed records are available yet.');
        }
        if (!$desktop_record && $mobile_record) {
            $desktop_record = $mobile_record;
        }
        if (!$mobile_record && $desktop_record) {
            $mobile_record = $desktop_record;
        }

        $desktop = $this->aysa_pagespeed_normalize_legacy_metrics_record($desktop_record, 'desktop');
        $mobile = $this->aysa_pagespeed_normalize_legacy_metrics_record($mobile_record, 'mobile');

        $desktop_score = intval(round(floatval($desktop['metrics']['performance']['score_percent'] ?? 0)));
        $mobile_score = intval(round(floatval($mobile['metrics']['performance']['score_percent'] ?? 0)));

        $overview = is_array($overview_record) ? $overview_record : array();
        $overall = isset($overview['overallScore']) ? intval($overview['overallScore']) : intval(round(($desktop_score + $mobile_score) / 2));
        $mobile_friendly = isset($overview['mobileFriendly']) ? intval($overview['mobileFriendly']) : $mobile_score;
        $indexability = isset($overview['indexability']) ? intval($overview['indexability']) : 90;

        $priority = array();
        $pool = array_merge(
            is_array($desktop['opportunities']) ? $desktop['opportunities'] : array(),
            is_array($mobile['opportunities']) ? $mobile['opportunities'] : array()
        );
        usort($pool, static function($a, $b) {
            return intval($b['impact'] ?? 0) <=> intval($a['impact'] ?? 0);
        });
        foreach ($pool as $row) {
            if (!is_array($row)) {
                continue;
            }
            $id = sanitize_key((string) ($row['id'] ?? ''));
            if ($id === '' || isset($priority[$id])) {
                continue;
            }
            $priority[$id] = array(
                'id' => $id,
                'title' => (string) ($row['title'] ?? $id),
                'displayValue' => (string) ($row['displayValue'] ?? ''),
                'impact' => intval($row['impact'] ?? 0),
            );
            if (count($priority) >= 5) {
                break;
            }
        }

        $summary = 'Good baseline with optimization room.';
        $status = 'good';
        if ($overall >= 90) {
            $summary = 'Excellent speed profile on desktop and mobile.';
            $status = 'excellent';
        } elseif ($overall < 70) {
            $summary = 'Performance needs urgent improvements, especially on mobile.';
            $status = $overall < 50 ? 'critical' : 'needs_improvement';
        }

        return array(
            'ok' => true,
            'requested_at' => gmdate('c'),
            'url' => $target_url,
            'overview' => array(
                'pageSpeedDesktop' => $desktop_score,
                'pageSpeedMobile' => $mobile_score,
                'mobileFriendly' => $mobile_friendly,
                'indexability' => $indexability,
                'requestedAt' => (string) ($overview['requestedAt'] ?? gmdate('c')),
                'overallScore' => $overall,
            ),
            'desktop' => $desktop,
            'mobile' => $mobile,
            'conclusion' => array(
                'overall_score' => $overall,
                'status' => $status,
                'summary' => $summary,
                'priority_actions' => array_values($priority),
            ),
            'source' => 'legacy_pagespeed_api',
        );
    }

    private function aysa_pagespeed_legacy_get_json($url) {
        if (!is_string($url) || trim($url) === '') {
            return array();
        }
        $resp = wp_remote_get($url, array(
            'timeout' => 30,
            'headers' => array(
                'Accept' => 'application/json',
            ),
        ));
        if (is_wp_error($resp)) {
            return array();
        }
        $body = (string) wp_remote_retrieve_body($resp);
        $decoded = json_decode($body, true);
        if (is_array($decoded)) {
            return $decoded;
        }
        return array();
    }

    private function aysa_pagespeed_legacy_post($project_id, $target_url, $strategy = 'desktop') {
        $post_url = class_exists('Aysa_API_Config') ? Aysa_API_Config::get_endpoint_url('pagespeed_metrics') : false;
        if (!$post_url) {
            return;
        }
        wp_remote_post($post_url, array(
            'timeout' => 45,
            'headers' => array(
                'Accept' => 'application/json',
                'Content-Type' => 'application/json',
            ),
            'body' => wp_json_encode(array(
                'projectId' => intval($project_id),
                'url' => (string) $target_url,
                'strategy' => (string) $strategy,
            )),
        ));
    }

    private function aysa_pagespeed_legacy_post_overview($project_id, $target_url) {
        $post_url = class_exists('Aysa_API_Config') ? Aysa_API_Config::get_endpoint_url('pagespeed_overview') : false;
        if (!$post_url) {
            return;
        }
        wp_remote_post($post_url, array(
            'timeout' => 45,
            'headers' => array(
                'Accept' => 'application/json',
                'Content-Type' => 'application/json',
            ),
            'body' => wp_json_encode(array(
                'projectId' => intval($project_id),
                'url' => (string) $target_url,
            )),
        ));
    }

    private function aysa_pagespeed_pick_latest_overview($data) {
        $rows = $this->aysa_pagespeed_as_row_list($data);
        $best = null;
        $best_ts = 0;
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            if (!isset($row['pageSpeedDesktop']) && !isset($row['pageSpeedMobile']) && !isset($row['overallScore'])) {
                continue;
            }
            $ts = strtotime((string) ($row['requestedAt'] ?? ''));
            if ($ts === false) {
                $ts = 0;
            }
            if ($best === null || $ts >= $best_ts) {
                $best = $row;
                $best_ts = $ts;
            }
        }
        return is_array($best) ? $best : null;
    }

    private function aysa_pagespeed_pick_latest_metrics_by_strategy($data, $strategy = 'desktop') {
        $rows = $this->aysa_pagespeed_as_row_list($data);
        $best = null;
        $best_ts = 0;
        foreach ($rows as $row) {
            if (!is_array($row) || !isset($row['metrics']) || !is_array($row['metrics'])) {
                continue;
            }
            $row_strategy = strtolower((string) ($row['strategy'] ?? ''));
            if ($row_strategy !== '' && $row_strategy !== strtolower((string) $strategy)) {
                continue;
            }
            $ts = strtotime((string) ($row['requestedAt'] ?? ''));
            if ($ts === false) {
                $ts = 0;
            }
            if ($best === null || $ts >= $best_ts) {
                $best = $row;
                $best_ts = $ts;
            }
        }
        return is_array($best) ? $best : null;
    }

    private function aysa_pagespeed_as_row_list($data) {
        if (!is_array($data)) {
            return array();
        }
        if (isset($data[0]) && is_array($data[0])) {
            return $data;
        }
        return array($data);
    }

    private function aysa_pagespeed_normalize_legacy_metrics_record($record, $strategy = 'desktop') {
        if (!is_array($record)) {
            return array(
                'strategy' => (string) $strategy,
                'requestedAt' => gmdate('c'),
                'metrics' => array(
                    'performance' => array(
                        'score' => 0,
                        'score_percent' => 0,
                    ),
                ),
                'opportunities' => array(),
            );
        }
        $record['strategy'] = (string) ($record['strategy'] ?? $strategy);
        $record['requestedAt'] = (string) ($record['requestedAt'] ?? gmdate('c'));
        if (!isset($record['metrics']) || !is_array($record['metrics'])) {
            $record['metrics'] = array();
        }
        if (!isset($record['metrics']['performance']) || !is_array($record['metrics']['performance'])) {
            $record['metrics']['performance'] = array();
        }
        $score = $record['metrics']['performance']['score'] ?? null;
        $score_percent = $record['metrics']['performance']['score_percent'] ?? null;
        if ($score_percent === null && is_numeric($score)) {
            $score_val = floatval($score);
            $score_percent = $score_val <= 1 ? $score_val * 100 : $score_val;
        } elseif ($score_percent !== null) {
            $score_percent = floatval($score_percent);
        } else {
            $score_percent = 0;
        }
        if ($score === null) {
            $score = floatval($score_percent) / 100;
        }
        $record['metrics']['performance']['score'] = round(floatval($score), 4);
        $record['metrics']['performance']['score_percent'] = round(floatval($score_percent), 0);
        if (!isset($record['opportunities']) || !is_array($record['opportunities'])) {
            $record['opportunities'] = array();
        }
        return $record;
    }

    private function aysa_normalize_link_issue_row($row) {
        if (!is_array($row)) {
            return array();
        }

        $pick = function ($keys) use ($row) {
            return $this->aysa_find_first_string_by_keys_recursive($row, $keys);
        };

        $http_code = $pick(array('http_status_code', 'httpStatusCode', 'status_code'));
        $is_valid = $pick(array('is_valid', 'isValid'));
        $timeline_event = $pick(array('event', 'event_type', 'type'));
        $message = $pick(array(
            'issue_message',
            'message',
            'reason',
            'details',
            'error_message',
            'verification_message',
            'status_message'
        ));
        $issue_type = $pick(array('issue_type', 'type', 'category', 'reason_code', 'verification_status'));
        $status = $pick(array('status', 'issue_status', 'verification_status', 'state'));

        $detail_parts = array();
        if ($issue_type !== '') {
            $detail_parts[] = $issue_type;
        }
        if ($message !== '') {
            $detail_parts[] = $message;
        }
        if ($http_code !== '') {
            $detail_parts[] = 'HTTP ' . $http_code;
        }
        if ($is_valid !== '') {
            $detail_parts[] = 'Valid: ' . $is_valid;
        }
        if ($timeline_event !== '') {
            $detail_parts[] = 'Event: ' . $timeline_event;
        }
        $issue_summary = implode(' | ', array_unique(array_filter($detail_parts)));

        return array(
            'order_ref' => $pick(array('order_ref', 'orderRef', 'reference', 'ref')),
            'portal_domain' => $pick(array('portal_domain', 'portalDomain', 'portal', 'domain', 'publisher_domain')),
            'published_keyword' => $pick(array('published_keyword', 'keyword', 'anchor_text', 'anchorText')),
            'published_on_url' => $pick(array('published_on_url', 'source_url', 'sourceUrl', 'published_url', 'page_url', 'url')),
            'linked_to_url' => $pick(array('linked_to_url', 'target_url', 'targetUrl', 'destination_url', 'link_to_url')),
            'status' => $status,
            'issue_type' => $issue_type,
            'issue_message' => $message,
            'issue_summary' => $issue_summary,
            'http_status_code' => $http_code,
            'is_valid' => $is_valid,
            'detected_at' => $pick(array('detected_at', 'detectedAt', 'created_at', 'updated_at', 'checked_at', 'last_checked_at')),
            'raw' => $row,
        );
    }

    private function aysa_extract_link_issues_items($decoded) {
        if (!is_array($decoded)) {
            return array();
        }

        if (isset($decoded['data']) && is_array($decoded['data'])) {
            return $decoded['data'];
        }
        if (isset($decoded['items']) && is_array($decoded['items'])) {
            return $decoded['items'];
        }
        if (isset($decoded['issues']) && is_array($decoded['issues'])) {
            return $decoded['issues'];
        }
        if (isset($decoded['link_issues']) && is_array($decoded['link_issues'])) {
            return $decoded['link_issues'];
        }
        if (array_keys($decoded) === range(0, count($decoded) - 1)) {
            return $decoded;
        }

        return array();
    }

    private function aysa_fetch_adverlink_link_issues($project_id) {
        $creds = $this->aysa_get_adverlink_credentials();
        $api_key = $creds['api_key'];
        $advertiser_org_id = $creds['org_id'];

        if ($project_id <= 0) {
            return new WP_Error('missing_fields', 'Missing project_id.');
        }

        $credentials_check = $this->aysa_validate_adverlink_marketplace_credentials($creds);
        if (is_wp_error($credentials_check)) {
            return $credentials_check;
        }

        $decoded = $this->aysa_broker_request('POST', '/adverlink/v1/public/orders/link-issues', array(
            'project_id' => intval($project_id),
            'api_key' => $api_key,
            'advertiser_org_id' => $advertiser_org_id,
        ), 60);
        if (is_wp_error($decoded)) {
            return $decoded;
        }

        $items = $this->aysa_extract_link_issues_items($decoded);
        $normalized = array();
        foreach ($items as $row) {
            $normalized_row = $this->aysa_normalize_link_issue_row($row);
            if (!empty($normalized_row)) {
                $normalized[] = $normalized_row;
            }
        }

        return array(
            'items' => array_slice($normalized, 0, 500),
            'raw' => $decoded,
        );
    }

    private function aysa_save_adverlink_link_issues($project_id, $items, $source = 'manual') {
        if (!is_array($items)) {
            $items = array();
        }
        update_option($this->aysa_get_adverlink_link_issues_key($project_id), array_values($items), false);
        update_option($this->aysa_get_adverlink_link_issues_sync_key($project_id), array(
            'synced_at' => current_time('mysql'),
            'source' => sanitize_text_field((string) $source),
            'count' => count($items),
        ), false);
    }

    private function aysa_find_first_string_by_keys_recursive($data, $keys, $depth = 0) {
        if ($depth > 8 || !is_array($data)) {
            return '';
        }
        foreach ($keys as $k) {
            if (isset($data[$k]) && is_scalar($data[$k])) {
                $v = trim((string) $data[$k]);
                if ($v !== '') {
                    return $v;
                }
            }
        }
        foreach ($data as $value) {
            if (is_array($value)) {
                $found = $this->aysa_find_first_string_by_keys_recursive($value, $keys, $depth + 1);
                if ($found !== '') {
                    return $found;
                }
            }
        }
        return '';
    }

    private function aysa_find_first_array_by_key_recursive($data, $target_key, $depth = 0) {
        if ($depth > 8 || !is_array($data)) {
            return array();
        }
        if (isset($data[$target_key]) && is_array($data[$target_key])) {
            return $data[$target_key];
        }
        foreach ($data as $value) {
            if (is_array($value)) {
                $found = $this->aysa_find_first_array_by_key_recursive($value, $target_key, $depth + 1);
                if (!empty($found)) {
                    return $found;
                }
            }
        }
        return array();
    }

    private function aysa_extract_external_order_ref($payload) {
        $strict_ref = sanitize_text_field($this->aysa_find_first_string_by_keys_recursive($payload, array(
            'orderRef', 'order_ref', 'reference', 'ref'
        )));
        if ($strict_ref !== '') {
            return $strict_ref;
        }

        // Last-chance fallback: search for ADV-style refs in payload JSON
        if (is_array($payload)) {
            $json = wp_json_encode($payload);
            if (is_string($json) && preg_match('/\bADV\d+\b/i', $json, $m)) {
                return sanitize_text_field((string) $m[0]);
            }
        }

        return '';
    }

    private function aysa_extract_external_order_status($payload) {
        if (is_array($payload)) {
            if (isset($payload['status']) && is_scalar($payload['status'])) {
                $status = sanitize_text_field((string) $payload['status']);
                if ($status !== '') {
                    return $status;
                }
            }
            if (isset($payload['data']) && is_array($payload['data']) && isset($payload['data']['status'])) {
                $status = sanitize_text_field((string) $payload['data']['status']);
                if ($status !== '') {
                    return $status;
                }
            }
            if (isset($payload['order']) && is_array($payload['order']) && isset($payload['order']['status'])) {
                $status = sanitize_text_field((string) $payload['order']['status']);
                if ($status !== '') {
                    return $status;
                }
            }
        }

        return sanitize_text_field($this->aysa_find_first_string_by_keys_recursive($payload, array(
            'status', 'order_status'
        )));
    }

    private function aysa_extract_external_verification($payload) {
        $verification = $this->aysa_find_first_array_by_key_recursive($payload, 'verification');
        if (empty($verification)) {
            return array();
        }
        return array(
            'is_valid' => isset($verification['is_valid']) ? (bool) $verification['is_valid'] : null,
            'verified_at' => sanitize_text_field((string) ($verification['verified_at'] ?? '')),
            'http_status_code' => intval($verification['http_status_code'] ?? 0),
            'error_message' => sanitize_text_field((string) ($verification['error_message'] ?? ''))
        );
    }

    private function aysa_extract_external_subscription($payload) {
        $subscription = $this->aysa_find_first_array_by_key_recursive($payload, 'subscription');
        if (empty($subscription)) {
            return array();
        }

        $id = sanitize_text_field((string) ($subscription['id'] ?? ''));
        $status = sanitize_text_field((string) ($subscription['status'] ?? ''));
        $next_renewal_at = sanitize_text_field((string) ($subscription['next_renewal_at'] ?? ''));
        $grace_until = sanitize_text_field((string) ($subscription['grace_until'] ?? ''));
        $removed_at = sanitize_text_field((string) ($subscription['removed_at'] ?? ''));
        $cycle_days = intval($subscription['cycle_days'] ?? 0);

        if ($id === '' && $status === '' && $next_renewal_at === '') {
            return array();
        }

        return array(
            'id' => $id,
            'status' => $status,
            'cycle_days' => $cycle_days,
            'next_renewal_at' => $next_renewal_at,
            'grace_until' => $grace_until,
            'removed_at' => $removed_at,
        );
    }

    private function aysa_extract_external_order_cost_cents($payload) {
        if (!is_array($payload)) {
            return 0;
        }

        $contexts = array($payload);
        if (isset($payload['data']) && is_array($payload['data'])) {
            $contexts[] = $payload['data'];
        }
        if (isset($payload['order']) && is_array($payload['order'])) {
            $contexts[] = $payload['order'];
        }
        if (isset($payload['result']) && is_array($payload['result'])) {
            $contexts[] = $payload['result'];
        }
        $pricing = $this->aysa_find_first_array_by_key_recursive($payload, 'pricing');
        if (!empty($pricing)) {
            $contexts[] = $pricing;
        }

        $cents_keys = array(
            'total_cost_cents',
            'total_price_cents',
            'amount_cents',
            'price_cents',
            'cost_cents',
            'credits_cents',
            'credits'
        );
        foreach ($contexts as $ctx) {
            foreach ($cents_keys as $key) {
                if (isset($ctx[$key]) && is_scalar($ctx[$key])) {
                    $v = intval($ctx[$key]);
                    if ($v > 0) {
                        return $v;
                    }
                }
            }
        }

        $eur_keys = array(
            'sale_price_eur',
            'purchase_price_eur',
            'total_cost_eur',
            'total_price_eur',
            'amount_eur',
            'price_eur',
            'cost_eur',
            'total',
            'amount',
            'price',
            'cost'
        );
        foreach ($contexts as $ctx) {
            foreach ($eur_keys as $key) {
                if (!isset($ctx[$key]) || !is_scalar($ctx[$key])) {
                    continue;
                }
                $raw = trim((string) $ctx[$key]);
                if ($raw === '') {
                    continue;
                }
                if (preg_match('/([0-9]+(?:[\\.,][0-9]+)?)/', $raw, $m)) {
                    $num = str_replace(',', '.', (string) $m[1]);
                    $eur = floatval($num);
                    if ($eur > 0) {
                        return max(1, intval(round($eur * 100)));
                    }
                }
            }
        }

        return 0;
    }

    private function aysa_is_subscription_order($order) {
        if (!is_array($order)) {
            return false;
        }

        $billing_type = strtolower(trim((string) ($order['billing_type'] ?? '')));
        return $billing_type === 'monthly' || $billing_type === 'subscription';
    }

    private function aysa_is_subscription_renewal_blocked_status($status) {
        $s = strtoupper(trim((string) $status));
        if ($s === '') {
            return false;
        }

        return in_array($s, array(
            'CANCELED',
            'CANCELLED',
            'REJECTED',
            'FAILED',
            'ERROR',
            'AUTO_DISPUTED',
            'EXPIRED'
        ), true);
    }

    private function aysa_is_subscription_renewal_eligible_status($status) {
        $s = strtoupper(trim((string) $status));
        return in_array($s, array(
            'IN_PROGRESS',
            'COMPLETED'
        ), true);
    }

    private function aysa_subscription_next_renewal_timestamp($order) {
        if (!is_array($order) || !isset($order['subscription']) || !is_array($order['subscription'])) {
            return 0;
        }

        $value = trim((string) ($order['subscription']['next_renewal_at'] ?? ''));
        if ($value === '') {
            return 0;
        }

        $ts = strtotime($value);
        return $ts ? intval($ts) : 0;
    }

    private function aysa_save_adverlink_orders($project_id, $orders) {
        if (!is_array($orders)) {
            $orders = array();
        }
        update_option($this->aysa_get_adverlink_orders_key($project_id), array_values($orders), false);
    }

    private function aysa_is_terminal_order_status($status) {
        $s = strtoupper(trim((string) $status));
        if ($s === '') {
            return false;
        }
        return in_array($s, array(
            'COMPLETED',
            'CANCELED',
            'CANCELLED',
            'REJECTED',
            'FAILED',
            'ERROR',
            'AUTO_DISPUTED',
            'EXPIRED'
        ), true);
    }

    private function aysa_reconcile_reserved_credits($project_id, &$orders) {
        if (!is_array($orders)) {
            $orders = array();
        }

        $changed = false;
        $reserved_total = 0;
        foreach ($orders as &$order) {
            $status = (string) ($order['status'] ?? '');
            $estimated = max(0, intval($order['estimated_credits'] ?? 0));
            $released = !empty($order['credits_released']);

            if ($this->aysa_is_terminal_order_status($status)) {
                if (!$released) {
                    $order['credits_released'] = true;
                    $released = true;
                    $changed = true;
                }
            }

            if (!$released && $estimated > 0) {
                $reserved_total += $estimated;
            }
        }
        unset($order);

        $current_reserved = intval(get_option($this->aysa_get_reserved_credits_key($project_id), 0));
        if ($current_reserved !== $reserved_total) {
            update_option($this->aysa_get_reserved_credits_key($project_id), $reserved_total, false);
            $changed = true;
        }

        if ($changed) {
            $this->aysa_save_adverlink_orders($project_id, $orders);
        }

        return $reserved_total;
    }

    public function handle_adverlink_save_article_draft() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = $this->aysa_resolve_project_id_with_local_fallback(intval($_POST['project_id'] ?? 0));
        $portal_id = sanitize_text_field((string) ($_POST['portal_id'] ?? ''));
        $draft_json = (string) ($_POST['draft_json'] ?? '');
        if ($project_id <= 0 || $portal_id === '') {
            wp_send_json_error('Invalid project_id or portal_id');
            return;
        }

        $draft = array();
        if ($draft_json !== '') {
            $decoded = json_decode(wp_unslash($draft_json), true);
            if (is_array($decoded)) {
                $draft = $decoded;
            }
        }

        $allowed = array(
            'mode', 'target_url', 'keyword', 'title', 'lead', 'content_html',
            'remarks', 'rel_type', 'billing_type', 'photo_source',
            'linking_strategy', 'guarantee_36m',
            'li_keyword', 'li_target_search', 'li_target_url', 'li_target_title',
            'li_target_meta', 'li_rel_type', 'li_billing_type', 'li_remarks',
            'ads_title', 'ads_keyword', 'ads_target_search', 'ads_target_url',
            'ads_display_url', 'ads_target_article_url', 'ads_description',
            'ads_duration_months', 'ads_remarks'
        );
        $sanitized = array();
        foreach ($allowed as $k) {
            if (!array_key_exists($k, $draft)) {
                continue;
            }
            if ($k === 'target_url' || $k === 'li_target_url') {
                $sanitized[$k] = esc_url_raw((string) $draft[$k]);
            } elseif ($k === 'content_html') {
                $sanitized[$k] = (string) $draft[$k];
            } elseif ($k === 'guarantee_36m') {
                $sanitized[$k] = !empty($draft[$k]) ? 1 : 0;
            } elseif ($k === 'ads_duration_months') {
                $sanitized[$k] = max(1, min(12, intval($draft[$k])));
            } else {
                $sanitized[$k] = sanitize_textarea_field((string) $draft[$k]);
            }
        }
        $sanitized['updated_at'] = current_time('mysql');

        update_option($this->aysa_get_article_draft_key($project_id, $portal_id), $sanitized, false);
        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_adverlink_save_article_draft',
            'offpage_save_article_draft_' . md5($portal_id),
            array(
                'module' => 'offpage',
                'portal_id' => $portal_id,
            ),
            1,
            'wp_ajax/aysa_adverlink_save_article_draft'
        );
        wp_send_json_success(array('saved' => true, 'updated_at' => $sanitized['updated_at']));
    }

    public function handle_adverlink_get_article_draft() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = $this->aysa_resolve_project_id_with_local_fallback(intval($_POST['project_id'] ?? 0));
        $portal_id = sanitize_text_field((string) ($_POST['portal_id'] ?? ''));
        if ($project_id <= 0 || $portal_id === '') {
            wp_send_json_error('Invalid project_id or portal_id');
            return;
        }

        $draft = get_option($this->aysa_get_article_draft_key($project_id, $portal_id), array());
        if (!is_array($draft)) {
            $draft = array();
        }
        wp_send_json_success(array('draft' => $draft));
    }

    public function handle_adverlink_create_publisher_article_draft() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = $this->aysa_resolve_project_id_with_local_fallback(intval($_POST['project_id'] ?? 0));
        $order_ref = sanitize_text_field((string) ($_POST['order_ref'] ?? ''));
        if ($project_id <= 0 || $order_ref === '') {
            wp_send_json_error('Invalid project_id or order_ref');
            return;
        }

        $orders = $this->aysa_get_adverlink_orders($project_id);
        $matched_order = null;
        foreach ($orders as $order) {
            if (!is_array($order)) {
                continue;
            }
            $current_ref = sanitize_text_field((string) ($order['order_ref'] ?? ''));
            if ($current_ref === $order_ref) {
                $matched_order = $order;
                break;
            }
        }

        if (!is_array($matched_order)) {
            wp_send_json_error('Order not found.');
            return;
        }

        $result = $this->aysa_create_publisher_article_draft_for_order($project_id, $matched_order);
        if (is_wp_error($result)) {
            wp_send_json_error($result->get_error_message());
            return;
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_adverlink_create_publisher_article_draft',
            'offpage_create_publisher_article_draft_' . md5($order_ref),
            array(
                'module' => 'offpage',
                'order_ref' => $order_ref,
            ),
            12,
            'wp_ajax/aysa_adverlink_create_publisher_article_draft'
        );

        wp_send_json_success($result);
    }

    public function handle_adverlink_list_publisher_article_drafts() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = $this->aysa_resolve_project_id_with_local_fallback(intval($_POST['project_id'] ?? 0));
        $status = sanitize_key((string) ($_POST['status'] ?? 'draft'));
        $limit = max(1, min(100, intval($_POST['limit'] ?? 50)));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project_id');
            return;
        }

        $this->aysa_sync_publisher_article_drafts_from_orders($project_id);

        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/publisher-articles/list', array(
            'project_id' => $project_id,
            'status' => $status,
            'limit' => $limit,
        ), 60);

        if (is_wp_error($broker_result)) {
            wp_send_json_error($this->aysa_humanize_broker_error($broker_result, 'Could not load publisher article drafts.'));
            return;
        }

        $items = isset($broker_result['items']) && is_array($broker_result['items']) ? $broker_result['items'] : array();
        wp_send_json_success(array('items' => $items));
    }

    public function handle_adverlink_get_publisher_article_draft() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $draft_id = intval($_POST['draft_id'] ?? 0);
        if ($draft_id <= 0) {
            wp_send_json_error('Invalid draft_id');
            return;
        }

        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/publisher-articles/get', array(
            'draft_id' => $draft_id,
        ), 60);

        if (is_wp_error($broker_result)) {
            wp_send_json_error($this->aysa_humanize_broker_error($broker_result, 'Could not load draft.'));
            return;
        }

        $draft = isset($broker_result['draft']) && is_array($broker_result['draft']) ? $broker_result['draft'] : array();
        wp_send_json_success(array('draft' => $draft));
    }

    public function handle_adverlink_update_publisher_article_draft() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $draft_id = intval($_POST['draft_id'] ?? 0);
        if ($draft_id <= 0) {
            wp_send_json_error('Invalid draft_id');
            return;
        }

        $title = sanitize_text_field((string) ($_POST['title'] ?? ''));
        $excerpt = sanitize_textarea_field((string) ($_POST['excerpt'] ?? ''));
        $body_html = wp_kses_post((string) ($_POST['body_html'] ?? ''));
        $status = sanitize_key((string) ($_POST['status'] ?? 'draft'));

        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/publisher-articles/update', array(
            'draft_id' => $draft_id,
            'title' => $title,
            'excerpt' => $excerpt,
            'body_html' => $body_html,
            'status' => $status !== '' ? $status : 'edited',
        ), 60);

        if (is_wp_error($broker_result)) {
            wp_send_json_error($this->aysa_humanize_broker_error($broker_result, 'Could not save draft.'));
            return;
        }

        $draft = isset($broker_result['draft']) && is_array($broker_result['draft']) ? $broker_result['draft'] : array();
        $project_id = $this->aysa_resolve_project_id_with_local_fallback(intval($_POST['project_id'] ?? 0));
        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_adverlink_update_publisher_article_draft',
            'offpage_update_publisher_article_draft_' . $draft_id,
            array(
                'module' => 'offpage',
                'draft_id' => $draft_id,
                'status' => $status !== '' ? $status : 'edited',
            ),
            8,
            'wp_ajax/aysa_adverlink_update_publisher_article_draft'
        );
        wp_send_json_success(array('draft' => $draft));
    }

    public function handle_adverlink_approve_publisher_article_draft() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $draft_id = intval($_POST['draft_id'] ?? 0);
        if ($draft_id <= 0) {
            wp_send_json_error('Invalid draft_id');
            return;
        }

        $project_id = $this->aysa_resolve_project_id_with_local_fallback(intval($_POST['project_id'] ?? 0));
        $reference = 'offpage_approve_publisher_article_draft_' . $draft_id;
        if ($this->aysa_wallet_usage_reference_exists($project_id, $reference) || $this->aysa_agent8_is_publisher_draft_approved($project_id, $draft_id)) {
            $this->aysa_agent8_mark_publisher_draft_approved($project_id, $draft_id);
            wp_send_json_success(array(
                'draft' => array('id' => $draft_id, 'status' => 'approved'),
                'already_approved' => true,
            ));
            return;
        }

        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/publisher-articles/approve', array(
            'draft_id' => $draft_id,
        ), 60);

        if (is_wp_error($broker_result)) {
            wp_send_json_error($this->aysa_humanize_broker_error($broker_result, 'Could not approve draft.'));
            return;
        }

        $draft = isset($broker_result['draft']) && is_array($broker_result['draft']) ? $broker_result['draft'] : array();
        $this->aysa_agent8_mark_publisher_draft_approved($project_id, $draft_id, $draft);
        if (!$this->aysa_wallet_usage_reference_exists($project_id, $reference)) {
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_adverlink_approve_publisher_article_draft',
                $reference,
                array(
                    'module' => 'offpage',
                    'draft_id' => $draft_id,
                ),
                8,
                'wp_ajax/aysa_adverlink_approve_publisher_article_draft'
            );
        }
        wp_send_json_success(array('draft' => $draft));
    }

    private function aysa_create_publisher_article_draft_for_order($project_id, array $order) {
        $service_type = sanitize_key((string) ($order['service_type'] ?? ''));
        if ($service_type !== 'seo_article') {
            return new WP_Error('publisher_draft_unsupported', 'Draft generation is available only for SEO article orders.');
        }

        $existing_draft_id = intval($order['publisher_article_draft_id'] ?? 0);
        if ($existing_draft_id > 0) {
            $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/publisher-articles/get', array(
                'draft_id' => $existing_draft_id,
            ), 60);
            if (!is_wp_error($broker_result) && isset($broker_result['draft']) && is_array($broker_result['draft'])) {
                return array(
                    'draft' => $broker_result['draft'],
                    'already_exists' => true,
                );
            }
        }

        $project_id = intval($project_id);
        $payload = $this->aysa_build_publisher_article_draft_payload($project_id, $order);
        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/publisher-articles/draft/create', $payload, 120);
        if (is_wp_error($broker_result)) {
            $recovered = $this->aysa_find_publisher_article_draft_by_order_ref(
                $project_id,
                sanitize_text_field((string) ($order['order_ref'] ?? ''))
            );
            if (!is_wp_error($recovered) && isset($recovered['draft']) && is_array($recovered['draft'])) {
                return array(
                    'draft' => $recovered['draft'],
                    'already_exists' => false,
                    'recovered_after_error' => true,
                );
            }
            return $broker_result;
        }

        $draft = isset($broker_result['draft']) && is_array($broker_result['draft']) ? $broker_result['draft'] : array();
        $draft_id = intval($draft['id'] ?? 0);
        if ($draft_id > 0) {
            $orders = $this->aysa_get_adverlink_orders($project_id);
            $current_ref = sanitize_text_field((string) ($order['order_ref'] ?? ''));
            foreach ($orders as &$row) {
                if (!is_array($row)) {
                    continue;
                }
                if (sanitize_text_field((string) ($row['order_ref'] ?? '')) !== $current_ref) {
                    continue;
                }
                $row['publisher_article_draft_id'] = $draft_id;
                $row['publisher_article_draft_status'] = sanitize_key((string) ($draft['status'] ?? 'draft'));
                $row['publisher_article_draft_updated_at'] = sanitize_text_field((string) ($draft['updated_at'] ?? current_time('mysql')));
                break;
            }
            unset($row);
            $this->aysa_save_adverlink_orders($project_id, $orders);
        }

        return array(
            'draft' => $draft,
            'already_exists' => false,
        );
    }

    private function aysa_find_publisher_article_draft_by_order_ref($project_id, $order_ref) {
        $project_id = intval($project_id);
        $order_ref = sanitize_text_field((string) $order_ref);
        if ($project_id <= 0 || $order_ref === '') {
            return new WP_Error('publisher_draft_lookup_invalid', 'Invalid project_id or order_ref.');
        }

        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/publisher-articles/list', array(
            'project_id' => $project_id,
            'status' => 'draft',
            'order_ref' => $order_ref,
            'limit' => 5,
        ), 60);
        if (is_wp_error($broker_result)) {
            return $broker_result;
        }

        $items = isset($broker_result['items']) && is_array($broker_result['items']) ? $broker_result['items'] : array();
        foreach ($items as $item) {
            if (!is_array($item)) {
                continue;
            }
            if (sanitize_text_field((string) ($item['order_ref'] ?? '')) !== $order_ref) {
                continue;
            }
            return array('draft' => $item);
        }

        return new WP_Error('publisher_draft_not_found', 'Publisher article draft was not found after create.');
    }

    private function aysa_build_publisher_article_draft_payload($project_id, array $order) {
        $setup_user_id = function_exists('get_current_user_id') ? intval(get_current_user_id()) : 0;
        $setup = $this->aysa_get_local_setup_state_for_user($setup_user_id);
        $general = isset($setup['general']) && is_array($setup['general']) ? $setup['general'] : array();

        $keyword = sanitize_text_field((string) ($order['keyword'] ?? ''));
        $article_keywords = isset($order['article_keywords']) && is_array($order['article_keywords']) ? $order['article_keywords'] : array();
        $language = sanitize_key((string) ($order['article_language'] ?? 'ro'));
        if (!in_array($language, array('ro', 'en', 'bg'), true)) {
            $language = 'ro';
        }

        $business_name = sanitize_text_field((string) ($general['company_name'] ?? get_bloginfo('name')));
        $business_type = sanitize_text_field((string) ($general['business_type'] ?? $general['industry'] ?? ''));
        $business_description = sanitize_textarea_field((string) ($general['business_description'] ?? $general['company_description'] ?? $general['about_business'] ?? ''));

        $products_services = array();
        foreach (array('products_services', 'main_services', 'services', 'products') as $key) {
            if (!isset($general[$key])) {
                continue;
            }
            if (is_array($general[$key])) {
                foreach ($general[$key] as $item) {
                    $text = sanitize_text_field((string) $item);
                    if ($text !== '') {
                        $products_services[] = $text;
                    }
                }
            } else {
                $chunks = preg_split('/[,;\n]+/', (string) $general[$key]) ?: array();
                foreach ($chunks as $item) {
                    $text = sanitize_text_field((string) $item);
                    if ($text !== '') {
                        $products_services[] = $text;
                    }
                }
            }
        }
        $products_services = array_values(array_unique(array_filter($products_services)));

        $internal_links = array();
        $targets = $this->aysa_collect_wp_primary_keyword_targets($keyword, 6);
        foreach ($targets as $target) {
            if (!is_array($target)) {
                continue;
            }
            $href = esc_url_raw((string) ($target['url'] ?? ''));
            $anchor = sanitize_text_field((string) ($target['keyword'] ?? ''));
            $title = sanitize_text_field((string) ($target['title'] ?? $anchor));
            if ($href === '' || $anchor === '') {
                continue;
            }
            $internal_links[] = array(
                'href' => $href,
                'anchor' => $anchor,
                'title' => $title !== '' ? $title : $anchor,
            );
            if (count($internal_links) >= 2) {
                break;
            }
        }

        $article_topic = sanitize_text_field((string) ($order['article_topic'] ?? ''));
        if ($article_topic === '') {
            $article_topic = $business_name . ' - ' . $keyword;
        }

        $keyword_cluster = array_values(array_unique(array_filter(array_merge(array($keyword), array_map('sanitize_text_field', $article_keywords)))));
        $notes = sanitize_textarea_field((string) ($order['article_brief'] ?? ''));

        return array(
            'project_id' => intval($project_id),
            'order_ref' => sanitize_text_field((string) ($order['order_ref'] ?? '')),
            'order_id' => sanitize_text_field((string) ($order['order_id'] ?? '')),
            'portal_id' => sanitize_text_field((string) ($order['portal_id'] ?? '')),
            'portal_domain' => sanitize_text_field((string) ($order['portal_domain'] ?? '')),
            'service_type' => 'seo_article',
            'language' => $language,
            'primary_keyword' => $keyword,
            'secondary_keywords' => array_values(array_unique(array_filter(array_map('sanitize_text_field', $article_keywords)))),
            'keyword_cluster' => $keyword_cluster,
            'business_name' => $business_name,
            'business_type' => $business_type,
            'business_description' => $business_description,
            'products_services' => $products_services,
            'target_url' => esc_url_raw((string) ($order['target_url'] ?? home_url('/'))),
            'internal_links' => $internal_links,
            'article_goal' => 'Create a publisher-ready article that can support the target page with natural editorial SEO value.',
            'article_angle' => $article_topic,
            'tone' => 'professional editorial',
            'word_count' => max(700, intval($order['article_word_count'] ?? 900)),
            'image_count' => 2,
            'notes_for_writer' => $notes,
            'publisher_context' => sanitize_text_field((string) ($order['portal_domain'] ?? 'specialty publisher')),
        );
    }

    private function aysa_sync_publisher_article_drafts_from_orders($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return;
        }

        $orders = $this->aysa_get_adverlink_orders($project_id);
        if (!is_array($orders) || empty($orders)) {
            return;
        }

        foreach ($orders as $order) {
            if (!is_array($order)) {
                continue;
            }
            if (sanitize_key((string) ($order['service_type'] ?? '')) !== 'seo_article') {
                continue;
            }

            $draft_id = intval($order['publisher_article_draft_id'] ?? 0);
            if ($draft_id <= 0) {
                continue;
            }

            $status = strtoupper(trim((string) ($order['status'] ?? '')));
            $published_url = '';

            $source = null;
            if (isset($order['raw_track']) && is_array($order['raw_track'])) {
                $source = $order['raw_track'];
            } elseif (isset($order['raw_response']) && is_array($order['raw_response'])) {
                $source = $order['raw_response'];
            }
            if (is_array($source)) {
                $published_url = esc_url_raw((string) $this->aysa_find_first_string_by_keys_recursive($source, array(
                    'published_url', 'publishedUrl', 'insertion_url', 'insertionUrl',
                    'article_url', 'articleUrl', 'placement_url', 'placementUrl',
                    'source_url', 'sourceUrl', 'published_on_url', 'publishedOnUrl'
                )));
            }

            if (!in_array($status, array('COMPLETED', 'PUBLISHED', 'LIVE'), true) || $published_url === '') {
                continue;
            }

            $this->aysa_broker_request('POST', '/adverlink/v1/publisher-articles/update', array(
                'draft_id' => $draft_id,
                'status' => 'published',
                'published_url' => $published_url,
            ), 60);
        }
    }

    private function aysa_extract_assoc_value($row, $keys, $default = '') {
        foreach ((array) $keys as $key) {
            if (isset($row[$key]) && $row[$key] !== null && $row[$key] !== '') {
                return $row[$key];
            }
        }
        return $default;
    }

    private function aysa_build_entity_url($entity_type, $entity_id, $fallback_url = '') {
        $entity_id = intval($entity_id);
        if (!empty($fallback_url)) {
            $url = esc_url_raw((string) $fallback_url);
            if (!empty($url)) {
                return $url;
            }
        }
        if ($entity_id <= 0) {
            return '';
        }

        $type = sanitize_key((string) $entity_type);
        if (in_array($type, array('post', 'page', 'product', 'article'), true)) {
            $post = get_post($entity_id);
            if ($post && $post->post_status === 'publish') {
                return (string) get_permalink($entity_id);
            }
        }

        if (in_array($type, array('category', 'product_cat', 'tag', 'product_tag', 'term'), true)) {
            $taxonomy_candidates = array();
            if ($type === 'category') {
                $taxonomy_candidates = array('category', 'product_cat');
            } elseif ($type === 'tag') {
                $taxonomy_candidates = array('post_tag', 'product_tag');
            } elseif ($type === 'term') {
                $taxonomy_candidates = array('category', 'product_cat', 'post_tag', 'product_tag');
            } else {
                $taxonomy_candidates = array($type);
            }

            foreach ($taxonomy_candidates as $taxonomy) {
                $term = get_term($entity_id, $taxonomy);
                if ($term && !is_wp_error($term)) {
                    $term_link = get_term_link($term);
                    if (!is_wp_error($term_link)) {
                        return (string) $term_link;
                    }
                }
            }
        }

        $post = get_post($entity_id);
        if ($post && $post->post_status === 'publish') {
            return (string) get_permalink($entity_id);
        }

        return '';
    }

    private function aysa_build_entity_title($entity_type, $entity_id, $fallback_title = '') {
        $title = sanitize_text_field((string) $fallback_title);
        if ($title !== '') {
            return $title;
        }

        $entity_id = intval($entity_id);
        if ($entity_id <= 0) {
            return '';
        }

        $type = sanitize_key((string) $entity_type);
        if (in_array($type, array('post', 'page', 'product', 'article'), true)) {
            $post = get_post($entity_id);
            if ($post) {
                return sanitize_text_field((string) $post->post_title);
            }
        }

        if (in_array($type, array('category', 'product_cat', 'tag', 'product_tag', 'term'), true)) {
            $taxonomy_candidates = array();
            if ($type === 'category') {
                $taxonomy_candidates = array('category', 'product_cat');
            } elseif ($type === 'tag') {
                $taxonomy_candidates = array('post_tag', 'product_tag');
            } elseif ($type === 'term') {
                $taxonomy_candidates = array('category', 'product_cat', 'post_tag', 'product_tag');
            } else {
                $taxonomy_candidates = array($type);
            }

            foreach ($taxonomy_candidates as $taxonomy) {
                $term = get_term($entity_id, $taxonomy);
                if ($term && !is_wp_error($term)) {
                    return sanitize_text_field((string) $term->name);
                }
            }
        }

        $post = get_post($entity_id);
        if ($post) {
            return sanitize_text_field((string) $post->post_title);
        }

        return '';
    }

    private function aysa_collect_wp_primary_keyword_targets($query, $limit = 25) {
        global $wpdb;
        $results = array();
        $limit = max(1, min(100, intval($limit)));
        $like = '%' . $wpdb->esc_like($query) . '%';

        $post_rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT p.ID AS entity_id, p.post_type AS entity_type, p.post_title AS entity_title, m.meta_value AS keyword_value
                 FROM {$wpdb->postmeta} m
                 INNER JOIN {$wpdb->posts} p ON p.ID = m.post_id
                 WHERE m.meta_key IN ('_aysa_primary_keyword','aysa_primary_keyword')
                   AND p.post_status = 'publish'
                   AND p.post_type IN ('post','page','product')
                   AND m.meta_value LIKE %s
                 ORDER BY p.post_modified_gmt DESC
                 LIMIT %d",
                $like,
                $limit
            ),
            ARRAY_A
        );

        if (is_array($post_rows)) {
            foreach ($post_rows as $row) {
                $entity_id = intval($row['entity_id'] ?? 0);
                if ($entity_id <= 0) {
                    continue;
                }
                $entity_type = sanitize_key((string) ($row['entity_type'] ?? 'post'));
                $url = get_permalink($entity_id);
                if (!$url) {
                    continue;
                }
                $keyword = sanitize_text_field((string) ($row['keyword_value'] ?? ''));
                if ($keyword === '') {
                    continue;
                }
                $results[] = array(
                    'keyword' => $keyword,
                    'entity_id' => $entity_id,
                    'entity_type' => $entity_type,
                    'title' => sanitize_text_field((string) ($row['entity_title'] ?? '')),
                    'url' => esc_url_raw((string) $url),
                    'source' => 'wp_meta'
                );
            }
        }

        $term_rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT t.term_id AS entity_id, tt.taxonomy AS entity_type, t.name AS entity_title, tm.meta_value AS keyword_value
                 FROM {$wpdb->termmeta} tm
                 INNER JOIN {$wpdb->term_taxonomy} tt ON tt.term_id = tm.term_id
                 INNER JOIN {$wpdb->terms} t ON t.term_id = tm.term_id
                 WHERE tm.meta_key IN ('_aysa_primary_keyword','aysa_primary_keyword')
                   AND tm.meta_value LIKE %s
                 ORDER BY t.term_id DESC
                 LIMIT %d",
                $like,
                $limit
            ),
            ARRAY_A
        );

        if (is_array($term_rows)) {
            foreach ($term_rows as $row) {
                $entity_id = intval($row['entity_id'] ?? 0);
                if ($entity_id <= 0) {
                    continue;
                }
                $entity_type = sanitize_key((string) ($row['entity_type'] ?? 'term'));
                $term_link = get_term_link($entity_id, $entity_type);
                if (is_wp_error($term_link) || empty($term_link)) {
                    continue;
                }
                $keyword = sanitize_text_field((string) ($row['keyword_value'] ?? ''));
                if ($keyword === '') {
                    continue;
                }
                $results[] = array(
                    'keyword' => $keyword,
                    'entity_id' => $entity_id,
                    'entity_type' => $entity_type,
                    'title' => sanitize_text_field((string) ($row['entity_title'] ?? '')),
                    'url' => esc_url_raw((string) $term_link),
                    'source' => 'wp_meta'
                );
            }
        }

        return $results;
    }

    public function handle_adverlink_search_link_targets() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = $this->aysa_resolve_project_id_with_local_fallback(intval($_POST['project_id'] ?? 0));
        $query = sanitize_text_field((string) ($_POST['q'] ?? ''));
        $limit = max(1, min(100, intval($_POST['limit'] ?? 25)));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID.');
            return;
        }
        $query_len = function_exists('mb_strlen') ? mb_strlen($query, 'UTF-8') : strlen($query);
        if ($query === '' || $query_len < 2) {
            wp_send_json_success(array('items' => array()));
            return;
        }

        $items = array();

        if (class_exists('Aysa_Auth_Manager')) {
            try {
                $auth_manager = Aysa_Auth_Manager::get_instance();
                if ($auth_manager && $auth_manager->is_authenticated()) {
                    $assoc_resp = $auth_manager->make_authenticated_request(
                        'api/Association?projectId=' . $project_id . '&pageSize=10000',
                        'GET'
                    );
                    $assoc_rows = $this->normalize_aysa_collection_response($assoc_resp);
                    foreach ($assoc_rows as $assoc) {
                        if (!is_array($assoc)) {
                            continue;
                        }

                        $keyword = sanitize_text_field((string) $this->aysa_extract_assoc_value($assoc, array('keyword', 'Keyword', 'primary_keyword'), ''));
                        if ($keyword === '') {
                            continue;
                        }

                        $keyword_lc = function_exists('mb_strtolower') ? mb_strtolower($keyword, 'UTF-8') : strtolower($keyword);
                        $query_lc = function_exists('mb_strtolower') ? mb_strtolower($query, 'UTF-8') : strtolower($query);
                        if (strpos($keyword_lc, $query_lc) === false) {
                            continue;
                        }

                        $is_main_raw = $this->aysa_extract_assoc_value($assoc, array('isMain', 'is_main', 'IsMain'), true);
                        $is_main = is_bool($is_main_raw)
                            ? $is_main_raw
                            : in_array(strtolower((string) $is_main_raw), array('1', 'true', 'yes'), true);
                        if (!$is_main) {
                            continue;
                        }

                        $entity_id = intval($this->aysa_extract_assoc_value($assoc, array('idIntern', 'id_intern', 'entityId', 'entity_id'), 0));
                        $entity_type = sanitize_key((string) $this->aysa_extract_assoc_value($assoc, array('associatedOn', 'entity_type', 'AssociatedOn'), ''));
                        $raw_link = (string) $this->aysa_extract_assoc_value($assoc, array('link', 'url', 'matched_page_url'), '');
                        $raw_title = (string) $this->aysa_extract_assoc_value($assoc, array('value', 'title', 'entityTitle'), '');

                        $resolved_url = $this->aysa_build_entity_url($entity_type, $entity_id, $raw_link);
                        if ($resolved_url === '') {
                            continue;
                        }

                        $items[] = array(
                            'keyword' => $keyword,
                            'entity_id' => $entity_id,
                            'entity_type' => $entity_type,
                            'title' => $this->aysa_build_entity_title($entity_type, $entity_id, $raw_title),
                            'url' => $resolved_url,
                            'source' => 'associations_api'
                        );
                    }
                }
            } catch (Exception $e) {
                aysa_debug_log('[AYSA Adverlink] link target search API error: ' . $e->getMessage());
            }
        }

        $items = array_merge($items, $this->aysa_collect_wp_primary_keyword_targets($query, $limit));

        $dedupe = array();
        $normalized = array();
        $query_lc = function_exists('mb_strtolower') ? mb_strtolower($query, 'UTF-8') : strtolower($query);
        foreach ($items as $item) {
            $keyword = sanitize_text_field((string) ($item['keyword'] ?? ''));
            $url = esc_url_raw((string) ($item['url'] ?? ''));
            if ($keyword === '' || $url === '') {
                continue;
            }
            $entity_type = sanitize_key((string) ($item['entity_type'] ?? ''));
            $entity_id = intval($item['entity_id'] ?? 0);
            $key = md5($keyword . '|' . $url . '|' . $entity_type . '|' . $entity_id);
            if (isset($dedupe[$key])) {
                continue;
            }
            $dedupe[$key] = true;

            $kw_lc = function_exists('mb_strtolower') ? mb_strtolower($keyword, 'UTF-8') : strtolower($keyword);
            $rank = 3;
            if ($kw_lc === $query_lc) {
                $rank = 0;
            } elseif (strpos($kw_lc, $query_lc) === 0) {
                $rank = 1;
            } elseif (strpos($kw_lc, $query_lc) !== false) {
                $rank = 2;
            }

            $normalized[] = array(
                'keyword' => $keyword,
                'entity_id' => $entity_id,
                'entity_type' => $entity_type,
                'title' => sanitize_text_field((string) ($item['title'] ?? '')),
                'url' => $url,
                'source' => sanitize_key((string) ($item['source'] ?? '')),
                '_rank' => $rank
            );
        }

        usort($normalized, function ($a, $b) {
            if (($a['_rank'] ?? 99) !== ($b['_rank'] ?? 99)) {
                return intval($a['_rank'] ?? 99) - intval($b['_rank'] ?? 99);
            }
            $ak = strlen((string) ($a['keyword'] ?? ''));
            $bk = strlen((string) ($b['keyword'] ?? ''));
            if ($ak !== $bk) {
                return $ak - $bk;
            }
            return strcmp((string) ($a['title'] ?? ''), (string) ($b['title'] ?? ''));
        });

        $normalized = array_slice($normalized, 0, $limit);
        foreach ($normalized as &$item) {
            unset($item['_rank']);
        }
        unset($item);

        wp_send_json_success(array(
            'items' => $normalized
        ));
    }

    public function handle_adverlink_credit_balance() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID.');
            return;
        }

        $actual_credits = $this->aysa_get_project_credit_balance($project_id, true);
        $orders = $this->aysa_get_adverlink_orders($project_id);
        $reserved_credits = $this->aysa_reconcile_reserved_credits($project_id, $orders);
        $available_credits = max(0, $actual_credits - $reserved_credits);
        $wallet_snapshot = array();
        $wallet_manager = $this->aysa_get_wallet_manager_instance();
        if ($wallet_manager && method_exists($wallet_manager, 'get_wallet_snapshot')) {
            $wallet_snapshot = $wallet_manager->get_wallet_snapshot($project_id, array(
                'allow_remote' => false,
            ));
        }

        $included_links_total = intval($wallet_snapshot['included_links_total'] ?? 0);
        $included_links_used = intval($wallet_snapshot['included_links_used'] ?? 0);
        $included_links_period_starts_at = sanitize_text_field((string) ($wallet_snapshot['included_links_period_starts_at'] ?? ''));
        $included_links_period_ends_at = sanitize_text_field((string) ($wallet_snapshot['included_links_period_ends_at'] ?? ''));
        foreach ($orders as $order) {
            if (!is_array($order) || empty($order['included_link_applied'])) {
                continue;
            }
            $included_links_used++;
        }

        if ($included_links_total > 0) {
            $included_links_used = min($included_links_used, $included_links_total);
        }
        $included_links_remaining = max(0, $included_links_total - $included_links_used);

        wp_send_json_success(array(
            'project_id' => $project_id,
            'actual_credits' => $actual_credits,
            'reserved_credits' => $reserved_credits,
            'available_credits' => $available_credits,
            'included_links_total' => $included_links_total,
            'included_links_used' => $included_links_used,
            'included_links_remaining' => $included_links_remaining,
            'included_links_period_starts_at' => $included_links_period_starts_at,
            'included_links_period_ends_at' => $included_links_period_ends_at,
            'latest_usage_amount_cents' => intval($wallet_snapshot['latest_usage_amount_cents'] ?? 0),
            'latest_usage_action_key' => sanitize_key((string) ($wallet_snapshot['latest_usage_action_key'] ?? '')),
            'latest_usage_reference' => sanitize_text_field((string) ($wallet_snapshot['latest_usage_reference'] ?? '')),
            'latest_usage_at' => sanitize_text_field((string) ($wallet_snapshot['latest_usage_at'] ?? '')),
        ));
    }

    public function handle_adverlink_marketplace_diagnostics() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error(array(
                'code' => 'invalid_nonce',
                'message' => 'Invalid nonce.',
            ));
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error(array(
                'code' => 'insufficient_permissions',
                'message' => 'Insufficient permissions.',
            ));
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        $creds = $this->aysa_get_adverlink_credentials();
        $credential_type = $this->aysa_detect_adverlink_credential_type($creds['api_key'] ?? '');
        $credentials_check = $this->aysa_validate_adverlink_marketplace_credentials($creds);

        $response = array(
            'project_id' => $project_id,
            'credential_type' => $credential_type,
            'has_api_key' => trim((string) ($creds['api_key'] ?? '')) !== '',
            'has_advertiser_org_id' => trim((string) ($creds['org_id'] ?? '')) !== '',
        );

        if (is_wp_error($credentials_check)) {
            wp_send_json_error(array_merge($response, array(
                'code' => $credentials_check->get_error_code(),
                'message' => $credentials_check->get_error_message(),
            )));
            return;
        }

        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/public/diagnostics', array(
            'project_id' => $project_id,
            'api_key' => $creds['api_key'],
            'advertiser_org_id' => $creds['org_id'],
        ), 25);

        if (is_wp_error($broker_result)) {
            wp_send_json_error(array_merge($response, array(
                'code' => 'broker_request_failed',
                'message' => $this->aysa_humanize_broker_error($broker_result, 'Could not reach marketplace diagnostics.'),
            )));
            return;
        }

        $payload = is_array($broker_result) ? $broker_result : array();
        $ok = !empty($payload['public_marketplace_ready']);
        $response = array_merge($response, array(
            'provider_status' => intval($payload['provider_status'] ?? 0),
            'upstream_base_url' => sanitize_text_field((string) ($payload['upstream_base_url'] ?? '')),
            'public_marketplace_ready' => $ok,
            'probe' => is_array($payload['probe'] ?? null) ? $payload['probe'] : array(),
            'message' => $ok
                ? 'Marketplace diagnostics passed.'
                : sanitize_text_field((string) (($payload['error']['message'] ?? '') ?: 'Marketplace diagnostics failed.')),
        ));

        if (!$ok) {
            wp_send_json_error(array_merge($response, array(
                'code' => sanitize_text_field((string) ($payload['error']['code'] ?? 'marketplace_not_ready')),
            )));
            return;
        }

        wp_send_json_success($response);
    }

    public function handle_adverlink_exchange_list_portals() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID.');
            return;
        }

        $creds = $this->aysa_get_adverlink_credentials();
        $credentials_check = $this->aysa_validate_adverlink_marketplace_credentials($creds);
        if (is_wp_error($credentials_check)) {
            wp_send_json_error($this->aysa_format_adverlink_marketplace_error($credentials_check));
            return;
        }

        $requester = $this->aysa_get_link_exchange_profile($project_id);
        $payload = array(
            'project_id' => $project_id,
            'api_key' => $creds['api_key'],
            'advertiser_org_id' => $creds['org_id'],
            'service_type' => 'link_insertion',
            'billing_type' => 'one_time',
            'per_page' => max(1, min(200, intval($_POST['per_page'] ?? 100))),
            'requester_site_url' => $requester['site_url'],
            'requester_site_domain' => $requester['site_domain'],
            'requester_website_type' => $requester['website_type'],
            'requester_industry' => $requester['industry'],
            'requester_language' => $requester['language'],
            'requester_country' => $requester['country'],
            'requester_topics' => array_values($requester['topics']),
            'requester_authority_value' => intval($requester['authority_value'] ?? 0),
        );

        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/portals', $payload, 45);
        if (is_wp_error($broker_result)) {
            wp_send_json_error(array(
                'code' => 'broker_error',
                'message' => $this->aysa_humanize_broker_error($broker_result, 'Could not load eligible exchange portals.'),
            ));
            return;
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_adverlink_exchange_list_portals',
            'offpage_exchange_list_portals_' . $project_id,
            array(
                'module' => 'offpage',
                'source' => 'marketplace_exchange',
            ),
            4,
            'wp_ajax/aysa_adverlink_exchange_list_portals'
        );

        wp_send_json_success(array(
            'items' => isset($broker_result['items']) && is_array($broker_result['items']) ? $broker_result['items'] : array(),
            'requester_profile' => isset($broker_result['requester_profile']) && is_array($broker_result['requester_profile']) ? $broker_result['requester_profile'] : $requester,
        ));
    }

    public function handle_adverlink_exchange_create_request() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID.');
            return;
        }

        $portal_json = (string) ($_POST['counterparty_portal_json'] ?? '');
        $portal = json_decode(wp_unslash($portal_json), true);
        if (!is_array($portal) || empty($portal)) {
            wp_send_json_error('Counterparty portal is required.');
            return;
        }

        $requester = $this->aysa_get_link_exchange_profile($project_id);
        $payload = array(
            'project_id' => $project_id,
            'requester_site_url' => $requester['site_url'],
            'requester_site_domain' => $requester['site_domain'],
            'requester_website_type' => $requester['website_type'],
            'requester_industry' => $requester['industry'],
            'requester_language' => $requester['language'],
            'requester_country' => $requester['country'],
            'requester_topics' => array_values($requester['topics']),
            'requester_authority_value' => intval($requester['authority_value'] ?? 0),
            'counterparty_portal' => $portal,
            'requester_target_url' => esc_url_raw(trim((string) ($_POST['requester_target_url'] ?? ''))),
            'requester_anchor_text' => sanitize_text_field((string) ($_POST['requester_anchor_text'] ?? '')),
            'counterparty_target_url' => esc_url_raw(trim((string) ($_POST['counterparty_target_url'] ?? ''))),
            'counterparty_anchor_text' => sanitize_text_field((string) ($_POST['counterparty_anchor_text'] ?? '')),
            'requester_notes' => sanitize_textarea_field((string) ($_POST['requester_notes'] ?? '')),
        );

        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/create', $payload, 45);
        if (is_wp_error($broker_result)) {
            wp_send_json_error(array(
                'code' => 'broker_error',
                'message' => $this->aysa_humanize_broker_error($broker_result, 'Could not create exchange request.'),
            ));
            return;
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_adverlink_exchange_create_request',
            'offpage_exchange_create_request_' . md5((string) ($payload['requester_target_url'] ?? '') . '|' . (string) ($payload['counterparty_target_url'] ?? '')),
            array(
                'module' => 'offpage',
                'source' => 'marketplace_exchange',
            ),
            12,
            'wp_ajax/aysa_adverlink_exchange_create_request'
        );

        wp_send_json_success(array(
            'request' => isset($broker_result['request']) && is_array($broker_result['request']) ? $broker_result['request'] : array(),
            'deduplicated' => !empty($broker_result['deduplicated']),
        ));
    }

    public function handle_adverlink_exchange_list_requests() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID.');
            return;
        }

        $requester = $this->aysa_get_link_exchange_profile($project_id);
        $payload = array(
            'project_id' => $project_id,
            'current_site_domain' => $requester['site_domain'],
            'direction' => sanitize_text_field((string) ($_POST['direction'] ?? 'all')),
            'status' => sanitize_text_field((string) ($_POST['status'] ?? '')),
            'limit' => max(1, min(200, intval($_POST['limit'] ?? 50))),
        );

        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/list', $payload, 30);
        if (is_wp_error($broker_result)) {
            wp_send_json_error(array(
                'code' => 'broker_error',
                'message' => $this->aysa_humanize_broker_error($broker_result, 'Could not load exchange requests.'),
            ));
            return;
        }

        wp_send_json_success(array(
            'items' => isset($broker_result['items']) && is_array($broker_result['items']) ? $broker_result['items'] : array(),
        ));
    }

    public function handle_adverlink_exchange_get_request() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID.');
            return;
        }

        $requester = $this->aysa_get_link_exchange_profile($project_id);
        $payload = array(
            'project_id' => $project_id,
            'current_site_domain' => $requester['site_domain'],
            'request_id' => intval($_POST['request_id'] ?? 0),
            'exchange_ref' => sanitize_text_field((string) ($_POST['exchange_ref'] ?? '')),
        );

        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/get', $payload, 30);
        if (is_wp_error($broker_result)) {
            wp_send_json_error(array(
                'code' => 'broker_error',
                'message' => $this->aysa_humanize_broker_error($broker_result, 'Could not load exchange request.'),
            ));
            return;
        }

        wp_send_json_success(array(
            'request' => isset($broker_result['request']) && is_array($broker_result['request']) ? $broker_result['request'] : array(),
        ));
    }

    public function handle_adverlink_exchange_list_activation_candidates() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $limit = max(1, min(100, intval($_POST['limit'] ?? 30)));
        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        $requester = $this->aysa_get_link_exchange_profile($project_id);
        $exchange_ref = sanitize_text_field((string) ($_POST['exchange_ref'] ?? ''));
        $target_url = esc_url_raw(trim((string) ($_POST['target_url'] ?? '')));
        $anchor_text = sanitize_text_field((string) ($_POST['anchor_text'] ?? ''));
        if ($exchange_ref !== '' && $project_id > 0) {
            $request_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/get', array(
                'project_id' => $project_id,
                'current_site_domain' => $requester['site_domain'],
                'exchange_ref' => $exchange_ref,
            ), 30);
            if (!is_wp_error($request_result) && isset($request_result['request']) && is_array($request_result['request'])) {
                $request = $request_result['request'];
                $role = (string) ($request['viewer_role'] ?? '');
                if ($target_url === '') {
                    $target_url = $role === 'requester'
                        ? esc_url_raw((string) ($request['counterparty_target_url'] ?? ''))
                        : esc_url_raw((string) ($request['requester_target_url'] ?? ''));
                }
                if ($anchor_text === '') {
                    $anchor_text = $role === 'requester'
                        ? sanitize_text_field((string) ($request['counterparty_anchor_text'] ?? ''))
                        : sanitize_text_field((string) ($request['requester_anchor_text'] ?? ''));
                }
            }
        }
        $post_types = array('post', 'page');
        if (post_type_exists('product')) {
            $post_types[] = 'product';
        }

        $posts = get_posts(array(
            'post_type' => $post_types,
            'post_status' => 'publish',
            'posts_per_page' => $limit,
            'orderby' => 'modified',
            'order' => 'DESC',
            'suppress_filters' => false,
        ));

        $items = array();
        foreach ($posts as $post) {
            if (!($post instanceof WP_Post)) {
                continue;
            }
            $semantic = $this->aysa_exchange_score_source_candidate($post, $target_url, $anchor_text);
            $items[] = array(
                'id' => intval($post->ID),
                'title' => get_the_title($post),
                'url' => get_permalink($post),
                'post_type' => $post->post_type,
                'excerpt' => wp_trim_words(wp_strip_all_tags((string) ($post->post_excerpt ?: $post->post_content)), 24, ''),
                'semantic_score' => intval($semantic['score'] ?? 0),
                'semantic_fit' => !empty($semantic['eligible']),
                'semantic_reasons' => isset($semantic['reasons']) && is_array($semantic['reasons']) ? array_values($semantic['reasons']) : array(),
            );
        }

        usort($items, static function ($left, $right) {
            $score_left = intval($left['semantic_score'] ?? 0);
            $score_right = intval($right['semantic_score'] ?? 0);
            if ($score_left !== $score_right) {
                return $score_right <=> $score_left;
            }
            return strcmp((string) ($left['title'] ?? ''), (string) ($right['title'] ?? ''));
        });

        wp_send_json_success(array(
            'items' => $items,
        ));
    }

    public function handle_adverlink_exchange_respond_request() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID.');
            return;
        }

        $requester = $this->aysa_get_link_exchange_profile($project_id);
        $payload = array(
            'project_id' => $project_id,
            'current_site_domain' => $requester['site_domain'],
            'request_id' => intval($_POST['request_id'] ?? 0),
            'exchange_ref' => sanitize_text_field((string) ($_POST['exchange_ref'] ?? '')),
            'decision' => sanitize_text_field((string) ($_POST['decision'] ?? 'accept')),
            'counterparty_target_url' => esc_url_raw(trim((string) ($_POST['counterparty_target_url'] ?? ''))),
            'counterparty_anchor_text' => sanitize_text_field((string) ($_POST['counterparty_anchor_text'] ?? '')),
            'counterparty_notes' => sanitize_textarea_field((string) ($_POST['counterparty_notes'] ?? '')),
        );

        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/respond', $payload, 30);
        if (is_wp_error($broker_result)) {
            wp_send_json_error(array(
                'code' => 'broker_error',
                'message' => $this->aysa_humanize_broker_error($broker_result, 'Could not update exchange request.'),
            ));
            return;
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_adverlink_exchange_respond_request',
            'offpage_exchange_respond_request_' . md5((string) ($payload['exchange_ref'] ?? '') . '|' . (string) ($payload['decision'] ?? '')),
            array(
                'module' => 'offpage',
                'decision' => (string) ($payload['decision'] ?? ''),
            ),
            8,
            'wp_ajax/aysa_adverlink_exchange_respond_request'
        );

        wp_send_json_success(array(
            'request' => isset($broker_result['request']) && is_array($broker_result['request']) ? $broker_result['request'] : array(),
        ));
    }

    public function handle_adverlink_exchange_activate_link() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID.');
            return;
        }

        $requester = $this->aysa_get_link_exchange_profile($project_id);
        $request_payload = array(
            'project_id' => $project_id,
            'current_site_domain' => $requester['site_domain'],
            'request_id' => intval($_POST['request_id'] ?? 0),
            'exchange_ref' => sanitize_text_field((string) ($_POST['exchange_ref'] ?? '')),
        );
        $request_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/get', $request_payload, 30);
        if (is_wp_error($request_result)) {
            wp_send_json_error(array(
                'code' => 'broker_error',
                'message' => $this->aysa_humanize_broker_error($request_result, 'Could not load exchange request before activation.'),
            ));
            return;
        }

        $request = isset($request_result['request']) && is_array($request_result['request']) ? $request_result['request'] : array();
        if (empty($request)) {
            wp_send_json_error('Exchange request not found.');
            return;
        }

        $source_post_id = intval($_POST['source_post_id'] ?? 0);
        if ($source_post_id <= 0) {
            wp_send_json_error('Source content is required.');
            return;
        }

        $role = (string) ($request['viewer_role'] ?? '');
        $target_url = $role === 'requester'
            ? esc_url_raw((string) ($request['counterparty_target_url'] ?? ''))
            : esc_url_raw((string) ($request['requester_target_url'] ?? ''));
        $anchor_text = $role === 'requester'
            ? sanitize_text_field((string) ($request['counterparty_anchor_text'] ?? ''))
            : sanitize_text_field((string) ($request['requester_anchor_text'] ?? ''));

        if ($target_url === '' || $anchor_text === '') {
            wp_send_json_error('Target URL and anchor are missing for activation.');
            return;
        }

        $source_post = get_post($source_post_id);
        $semantic = $this->aysa_exchange_score_source_candidate($source_post, $target_url, $anchor_text);

        $activation = $this->aysa_exchange_insert_link_into_post($source_post_id, $target_url, $anchor_text, (string) ($request['exchange_ref'] ?? ''));
        if (is_wp_error($activation)) {
            wp_send_json_error(array(
                'code' => $activation->get_error_code(),
                'message' => $activation->get_error_message(),
            ));
            return;
        }

        $payload = array(
            'project_id' => $project_id,
            'current_site_domain' => $requester['site_domain'],
            'request_id' => intval($request['id'] ?? 0),
            'exchange_ref' => sanitize_text_field((string) ($request['exchange_ref'] ?? '')),
            'source_post_id' => $source_post_id,
            'source_url' => esc_url_raw((string) ($activation['source_url'] ?? '')),
            'published_url' => esc_url_raw((string) ($activation['source_url'] ?? '')),
            'target_url' => $target_url,
            'anchor_text' => $anchor_text,
            'notes' => sanitize_textarea_field((string) ($_POST['notes'] ?? '')),
        );

        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/activate', $payload, 45);
        if (is_wp_error($broker_result)) {
            wp_send_json_error(array(
                'code' => 'broker_error',
                'message' => $this->aysa_humanize_broker_error($broker_result, 'Could not activate reciprocal link.'),
            ));
            return;
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_adverlink_exchange_activate_link',
            'offpage_exchange_activate_link_' . md5((string) ($request['exchange_ref'] ?? '') . '|' . (string) $source_post_id),
            array(
                'module' => 'offpage',
                'exchange_ref' => (string) ($request['exchange_ref'] ?? ''),
                'source_post_id' => $source_post_id,
            ),
            12,
            'wp_ajax/aysa_adverlink_exchange_activate_link'
        );

        wp_send_json_success(array(
            'request' => isset($broker_result['request']) && is_array($broker_result['request']) ? $broker_result['request'] : array(),
            'activation' => $activation,
            'semantic' => $semantic,
            'placement_mode' => !empty($semantic['eligible']) ? 'semantic_match' : 'partner_box_fallback',
        ));
    }

    public function handle_adverlink_exchange_submit_publication() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID.');
            return;
        }

        $requester = $this->aysa_get_link_exchange_profile($project_id);
        $payload = array(
            'project_id' => $project_id,
            'current_site_domain' => $requester['site_domain'],
            'request_id' => intval($_POST['request_id'] ?? 0),
            'exchange_ref' => sanitize_text_field((string) ($_POST['exchange_ref'] ?? '')),
            'published_url' => esc_url_raw(trim((string) ($_POST['published_url'] ?? ''))),
            'target_url' => esc_url_raw(trim((string) ($_POST['target_url'] ?? ''))),
            'anchor_text' => sanitize_text_field((string) ($_POST['anchor_text'] ?? '')),
            'notes' => sanitize_textarea_field((string) ($_POST['notes'] ?? '')),
        );

        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/publication', $payload, 30);
        if (is_wp_error($broker_result)) {
            wp_send_json_error(array(
                'code' => 'broker_error',
                'message' => $this->aysa_humanize_broker_error($broker_result, 'Could not submit publication proof.'),
            ));
            return;
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_adverlink_exchange_submit_publication',
            'offpage_exchange_submit_publication_' . md5((string) ($payload['exchange_ref'] ?? '') . '|' . (string) ($payload['published_url'] ?? '')),
            array(
                'module' => 'offpage',
                'exchange_ref' => (string) ($payload['exchange_ref'] ?? ''),
            ),
            8,
            'wp_ajax/aysa_adverlink_exchange_submit_publication'
        );

        wp_send_json_success(array(
            'request' => isset($broker_result['request']) && is_array($broker_result['request']) ? $broker_result['request'] : array(),
        ));
    }

    public function handle_adverlink_exchange_refresh_verification() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID.');
            return;
        }

        $requester = $this->aysa_get_link_exchange_profile($project_id);
        $payload = array(
            'project_id' => $project_id,
            'current_site_domain' => $requester['site_domain'],
            'request_id' => intval($_POST['request_id'] ?? 0),
            'exchange_ref' => sanitize_text_field((string) ($_POST['exchange_ref'] ?? '')),
        );

        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/verify', $payload, 45);
        if (is_wp_error($broker_result)) {
            wp_send_json_error(array(
                'code' => 'broker_error',
                'message' => $this->aysa_humanize_broker_error($broker_result, 'Could not refresh exchange verification.'),
            ));
            return;
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_adverlink_exchange_refresh_verification',
            'offpage_exchange_refresh_verification_' . md5((string) ($payload['exchange_ref'] ?? '')),
            array(
                'module' => 'offpage',
                'exchange_ref' => (string) ($payload['exchange_ref'] ?? ''),
            ),
            6,
            'wp_ajax/aysa_adverlink_exchange_refresh_verification'
        );

        wp_send_json_success(array(
            'request' => isset($broker_result['request']) && is_array($broker_result['request']) ? $broker_result['request'] : array(),
        ));
    }

    public function handle_adverlink_exchange_change_state() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID.');
            return;
        }

        $requester = $this->aysa_get_link_exchange_profile($project_id);
        $payload = array(
            'project_id' => $project_id,
            'current_site_domain' => $requester['site_domain'],
            'request_id' => intval($_POST['request_id'] ?? 0),
            'exchange_ref' => sanitize_text_field((string) ($_POST['exchange_ref'] ?? '')),
            'action' => sanitize_text_field((string) ($_POST['state_action'] ?? $_POST['action_name'] ?? '')),
            'reason' => sanitize_textarea_field((string) ($_POST['reason'] ?? '')),
        );

        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/state', $payload, 30);
        if (is_wp_error($broker_result)) {
            wp_send_json_error(array(
                'code' => 'broker_error',
                'message' => $this->aysa_humanize_broker_error($broker_result, 'Could not update exchange request state.'),
            ));
            return;
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_adverlink_exchange_change_state',
            'offpage_exchange_change_state_' . md5((string) ($payload['exchange_ref'] ?? '') . '|' . (string) ($payload['action'] ?? '')),
            array(
                'module' => 'offpage',
                'action' => (string) ($payload['action'] ?? ''),
                'exchange_ref' => (string) ($payload['exchange_ref'] ?? ''),
            ),
            6,
            'wp_ajax/aysa_adverlink_exchange_change_state'
        );

        wp_send_json_success(array(
            'request' => isset($broker_result['request']) && is_array($broker_result['request']) ? $broker_result['request'] : array(),
        ));
    }

    public function schedule_adverlink_link_issues_cron() {
        if (!wp_next_scheduled('aysa_adverlink_sync_link_issues_event')) {
            wp_schedule_event(time() + 120, 'hourly', 'aysa_adverlink_sync_link_issues_event');
        }
    }

    public function handle_adverlink_cron_link_issues_sync() {
        $project_id = intval(get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            return;
        }
        $result = $this->aysa_fetch_adverlink_link_issues($project_id);
        if (is_wp_error($result)) {
            aysa_debug_log('[AYSA Adverlink] Link issues cron sync failed: ' . $result->get_error_message());
            return;
        }
        $items = (is_array($result) && isset($result['items']) && is_array($result['items'])) ? $result['items'] : array();
        $this->aysa_save_adverlink_link_issues($project_id, $items, 'cron');
    }

    public function schedule_adverlink_subscription_renewal_cron() {
        if (!wp_next_scheduled('aysa_adverlink_process_subscription_renewals_event')) {
            wp_schedule_event(time() + 300, 'hourly', 'aysa_adverlink_process_subscription_renewals_event');
        }
    }

    public function schedule_adverlink_exchange_verification_cron() {
        if (!wp_next_scheduled('aysa_adverlink_exchange_verify_event')) {
            wp_schedule_event(time() + 180, 'aysa_every_fifteen_minutes', 'aysa_adverlink_exchange_verify_event');
        }
    }

    public function handle_adverlink_cron_subscription_renewals() {
        $project_id = intval(get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            return;
        }

        $creds = $this->aysa_get_adverlink_credentials();
        $api_key = trim((string) ($creds['api_key'] ?? ''));
        $advertiser_org_id = trim((string) ($creds['org_id'] ?? ''));
        if ($api_key === '') {
            return;
        }
        $credentials_check = $this->aysa_validate_adverlink_marketplace_credentials($creds);
        if (is_wp_error($credentials_check)) {
            return;
        }

        $orders = $this->aysa_get_adverlink_orders($project_id);
        if (!is_array($orders) || empty($orders)) {
            return;
        }

        $actual_credits = $this->aysa_get_project_credit_balance($project_id, true);
        $reserved_credits = $this->aysa_reconcile_reserved_credits($project_id, $orders);
        $available_credits = max(0, $actual_credits - $reserved_credits);
        $wallet_manager = $this->aysa_get_wallet_manager_instance();
        $changed = false;
        $now = time();

        foreach ($orders as &$order) {
            if (!$this->aysa_is_subscription_order($order)) {
                continue;
            }

            $status = (string) ($order['status'] ?? '');
            if ($this->aysa_is_subscription_renewal_blocked_status($status)) {
                continue;
            }
            if (!$this->aysa_is_subscription_renewal_eligible_status($status)) {
                continue;
            }

            $subscription = isset($order['subscription']) && is_array($order['subscription']) ? $order['subscription'] : array();
            if (empty($subscription)) {
                continue;
            }
            if (strtolower(trim((string) ($subscription['status'] ?? ''))) !== 'active') {
                continue;
            }

            $next_renewal_ts = $this->aysa_subscription_next_renewal_timestamp($order);
            if ($next_renewal_ts <= 0 || $next_renewal_ts > $now) {
                continue;
            }

            $last_attempt_ts = intval($order['last_subscription_renewal_attempt_ts'] ?? 0);
            if ($last_attempt_ts > 0 && ($now - $last_attempt_ts) < 1800) {
                continue;
            }

            $order_ref = sanitize_text_field((string) ($order['order_ref'] ?? ''));
            if ($order_ref === '') {
                continue;
            }

            $provider_cost_cents = max(0, intval($order['provider_cost_cents'] ?? ($order['charged_credits_cents'] ?? 0)));
            if ($provider_cost_cents <= 0) {
                continue;
            }

            $service_type = sanitize_text_field((string) ($order['service_type'] ?? ''));
            $included_link_applied = false;
            if ($wallet_manager && $this->aysa_is_included_link_eligible_service($service_type) && method_exists($wallet_manager, 'has_included_link_available')) {
                $included_link_applied = (bool) $wallet_manager->has_included_link_available($project_id);
            }

            $renewal_cost_cents = $included_link_applied ? 0 : $provider_cost_cents;
            if ($available_credits < $renewal_cost_cents) {
                $order['last_subscription_renewal_attempt_ts'] = $now;
                $order['last_subscription_renewal_error'] = 'insufficient_credits';
                $order['updated_at'] = current_time('mysql');
                $changed = true;
                continue;
            }

            $idempotency_key = 'aysa-renew-' . $project_id . '-' . preg_replace('/[^A-Za-z0-9]/', '', $order_ref) . '-' . gmdate('YmdHi');
            $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/public/orders/renew', array(
                'project_id' => $project_id,
                'api_key' => $api_key,
                'advertiser_org_id' => $advertiser_org_id,
                'order_ref' => $order_ref,
                'idempotency_key' => $idempotency_key,
            ), 45);
            $order['last_subscription_renewal_attempt_ts'] = $now;

            if (is_wp_error($broker_result)) {
                $order['last_subscription_renewal_error'] = $broker_result->get_error_message();
                $order['updated_at'] = current_time('mysql');
                $changed = true;
                continue;
            }

            $decoded = is_array($broker_result['provider_response'] ?? null)
                ? $broker_result['provider_response']
                : (is_array($broker_result['data'] ?? null) ? $broker_result['data'] : $broker_result);
            $subscription_after = $this->aysa_extract_external_subscription($decoded);
            if (empty($subscription_after)) {
                $order['last_subscription_renewal_error'] = 'missing_subscription_in_response';
                $order['updated_at'] = current_time('mysql');
                $changed = true;
                continue;
            }

            if ($included_link_applied && $wallet_manager && method_exists($wallet_manager, 'consume_included_link')) {
                $consumption = $wallet_manager->consume_included_link(
                    $project_id,
                    'adverlink_subscription_renewal:' . $order_ref,
                    array(
                        'module' => 'offpage',
                        'service_type' => $service_type,
                        'order_ref' => $order_ref,
                        'subscription_id' => sanitize_text_field((string) ($subscription_after['id'] ?? '')),
                        'provider_cost_cents' => $provider_cost_cents,
                    )
                );
                if (is_wp_error($consumption)) {
                    $included_link_applied = false;
                    $renewal_cost_cents = $provider_cost_cents;
                } else {
                    $order['included_link_snapshot'] = $consumption;
                }
            }

            if ($renewal_cost_cents > 0) {
                $this->aysa_wallet_track_server_usage(
                    $project_id,
                    'wp_cron/aysa_adverlink_process_subscription_renewals',
                    $renewal_cost_cents,
                    'offpage_subscription_renew_' . $order_ref . '_' . gmdate('YmdHis'),
                    array(
                        'module' => 'offpage',
                        'action_key' => 'aysa_adverlink_subscription_renew',
                        'service_type' => $service_type,
                        'order_ref' => $order_ref,
                        'subscription_id' => sanitize_text_field((string) ($subscription_after['id'] ?? '')),
                        'provider_cost_cents' => $provider_cost_cents,
                        'included_link_applied' => false,
                    )
                );
                $available_credits -= $renewal_cost_cents;
            }

            $order['subscription'] = $subscription_after;
            $order['updated_at'] = current_time('mysql');
            $order['last_subscription_renewed_at'] = current_time('mysql');
            $order['last_subscription_renewal_error'] = '';
            $order['renewal_count'] = intval($order['renewal_count'] ?? 0) + 1;
            $order['raw_subscription_renew'] = $decoded;
            $order['provider_cost_cents'] = $provider_cost_cents;
            $order['charged_credits_cents'] = $renewal_cost_cents;
            $order['included_link_applied'] = $included_link_applied;
            $changed = true;
        }
        unset($order);

        if ($changed) {
            $this->aysa_save_adverlink_orders($project_id, $orders);
        }
    }

    public function handle_adverlink_cron_exchange_verification() {
        $project_id = intval(get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            return;
        }

        $requester = $this->aysa_get_link_exchange_profile($project_id);
        if (empty($requester['site_domain'])) {
            return;
        }

        $list = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/list', array(
            'project_id' => $project_id,
            'current_site_domain' => $requester['site_domain'],
            'direction' => 'all',
            'limit' => 200,
        ), 45);
        if (is_wp_error($list)) {
            aysa_debug_log('[AYSA Exchange] Verification cron list failed: ' . $list->get_error_message());
            return;
        }

        $items = isset($list['items']) && is_array($list['items']) ? $list['items'] : array();
        foreach ($items as $request) {
            if (!is_array($request)) {
                continue;
            }

            $viewer_role = (string) ($request['viewer_role'] ?? '');
            $viewer_owes_activation = ($viewer_role === 'requester' && (string) ($request['current_obligation'] ?? '') === 'requester_activation_required')
                || ($viewer_role === 'counterparty' && (string) ($request['current_obligation'] ?? '') === 'counterparty_activation_required');
            $local_activated = ($viewer_role === 'requester')
                ? ((string) ($request['requester_activation_state'] ?? '') === 'active')
                : ((string) ($request['counterparty_activation_state'] ?? '') === 'active');
            $remote_activated = ($viewer_role === 'requester')
                ? ((string) ($request['counterparty_activation_state'] ?? '') === 'active')
                : ((string) ($request['requester_activation_state'] ?? '') === 'active');

            $remote_activation_event = $this->aysa_exchange_find_latest_remote_activation_event($request);
            if ($viewer_owes_activation && !$local_activated && is_array($remote_activation_event)) {
                $event_marker = md5((string) ($request['exchange_ref'] ?? '') . '|' . (string) ($remote_activation_event['created_at'] ?? ''));
                $initial_key = 'activation_email:' . $event_marker;
                if (!$this->aysa_exchange_notification_sent($project_id, $initial_key)) {
                    if ($this->aysa_exchange_send_activation_email($project_id, $request, 'activation')) {
                        $this->aysa_mark_exchange_notification_sent($project_id, $initial_key);
                    }
                }

                $event_ts = strtotime((string) ($remote_activation_event['created_at'] ?? ''));
                $reminder_hours = $this->aysa_exchange_activation_reminder_hours();
                if ($event_ts > 0 && (time() - $event_ts) >= ($reminder_hours * HOUR_IN_SECONDS)) {
                    $reminder_key = 'activation_email_reminder:' . $event_marker;
                    if (!$this->aysa_exchange_notification_sent($project_id, $reminder_key)) {
                        if ($this->aysa_exchange_send_activation_email($project_id, $request, 'reminder_24h')) {
                            $this->aysa_mark_exchange_notification_sent($project_id, $reminder_key);
                        }
                    }
                }
            }

            if ($local_activated && !$remote_activated) {
                $local_published_at = $viewer_role === 'requester'
                    ? (string) ($request['requester_published_at'] ?? '')
                    : (string) ($request['counterparty_published_at'] ?? '');
                $local_source_post_id = $viewer_role === 'requester'
                    ? intval($request['requester_source_post_id'] ?? 0)
                    : intval($request['counterparty_source_post_id'] ?? 0);
                $published_ts = $local_published_at !== '' ? strtotime($local_published_at) : 0;
                if ($published_ts > 0 && (time() - $published_ts) >= ($this->aysa_exchange_activation_grace_hours() * HOUR_IN_SECONDS)) {
                    if ($local_source_post_id > 0) {
                        $cleanup = $this->aysa_exchange_remove_link_from_post($local_source_post_id, (string) ($request['exchange_ref'] ?? ''));
                        if (is_wp_error($cleanup)) {
                            aysa_debug_log('[AYSA Exchange] Auto-expire cleanup failed for ' . (string) ($request['exchange_ref'] ?? '-') . ': ' . $cleanup->get_error_message());
                        }
                    }

                    $expire = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/state', array(
                        'project_id' => $project_id,
                        'current_site_domain' => $requester['site_domain'],
                        'request_id' => intval($request['id'] ?? 0),
                        'exchange_ref' => sanitize_text_field((string) ($request['exchange_ref'] ?? '')),
                        'action' => 'expire',
                        'reason' => 'Auto-expired after 48 hours without reciprocal activation.',
                    ), 30);
                    if (is_wp_error($expire)) {
                        aysa_debug_log('[AYSA Exchange] Auto-expire failed for ' . (string) ($request['exchange_ref'] ?? '-') . ': ' . $expire->get_error_message());
                    }
                    continue;
                }
            }

            $status = (string) ($request['status'] ?? '');
            $has_both_urls = !empty($request['requester_published_url']) && !empty($request['counterparty_published_url']);
            if (!$has_both_urls) {
                continue;
            }
            if (!in_array($status, array('verification_pending', 'completed', 'disputed'), true)) {
                continue;
            }

            $verify = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/verify', array(
                'project_id' => $project_id,
                'current_site_domain' => $requester['site_domain'],
                'request_id' => intval($request['id'] ?? 0),
                'exchange_ref' => sanitize_text_field((string) ($request['exchange_ref'] ?? '')),
            ), 45);
            if (is_wp_error($verify)) {
                aysa_debug_log('[AYSA Exchange] Verification cron refresh failed for ' . (string) ($request['exchange_ref'] ?? '-') . ': ' . $verify->get_error_message());
            }
        }
    }

    public function handle_adverlink_get_link_issues() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID.');
            return;
        }

        $force_refresh = isset($_POST['force_refresh']) && (string) $_POST['force_refresh'] === '1';
        if ($force_refresh) {
            $result = $this->aysa_fetch_adverlink_link_issues($project_id);
            if (is_wp_error($result)) {
                wp_send_json_error($result->get_error_message());
                return;
            }
            $items = (is_array($result) && isset($result['items']) && is_array($result['items'])) ? $result['items'] : array();
            $this->aysa_save_adverlink_link_issues($project_id, $items, 'manual');
        }

        $items = get_option($this->aysa_get_adverlink_link_issues_key($project_id), array());
        if (!is_array($items)) {
            $items = array();
        }
        $last_sync = get_option($this->aysa_get_adverlink_link_issues_sync_key($project_id), array());
        if (!is_array($last_sync)) {
            $last_sync = array();
        }

        wp_send_json_success(array(
            'items' => array_values($items),
            'last_sync' => $last_sync,
        ));
    }

    public function handle_adverlink_create_order() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        $creds = $this->aysa_get_adverlink_credentials();
        $api_key = $creds['api_key'];

        $portal_id = sanitize_text_field((string) ($_POST['portal_id'] ?? ''));
        $service_type = sanitize_text_field((string) ($_POST['service_type'] ?? 'link_insertion'));
        $billing_type = sanitize_text_field((string) ($_POST['billing_type'] ?? 'one_time'));
        $keyword = sanitize_text_field((string) ($_POST['keyword'] ?? ''));
        $target_url = esc_url_raw(trim((string) ($_POST['target_url'] ?? '')));
        $title = sanitize_text_field((string) ($_POST['title'] ?? ''));
        $rel_type = sanitize_text_field((string) ($_POST['rel_type'] ?? 'follow'));
        $client_ref = sanitize_text_field((string) ($_POST['client_ref'] ?? ''));
        $client_name = sanitize_text_field((string) ($_POST['client_name'] ?? ''));
        $project_ref = sanitize_text_field((string) ($_POST['project_ref'] ?? ''));
        $article_topic = sanitize_text_field((string) ($_POST['article_topic'] ?? ''));
        $article_brief = sanitize_textarea_field((string) ($_POST['article_brief'] ?? ''));
        $article_word_count = intval($_POST['article_word_count'] ?? 0);
        $article_language = sanitize_text_field((string) ($_POST['article_language'] ?? ''));
        $article_tone = sanitize_text_field((string) ($_POST['article_tone'] ?? ''));
        $article_content_html = wp_kses_post((string) ($_POST['article_content_html'] ?? ''));
        $ad_display_url = esc_url_raw(trim((string) ($_POST['ad_display_url'] ?? '')));
        $ad_description = sanitize_textarea_field((string) ($_POST['ad_description'] ?? ''));
        $target_article_url = esc_url_raw(trim((string) ($_POST['target_article_url'] ?? '')));
        $duration_months = max(1, min(12, intval($_POST['duration_months'] ?? 1)));
        $article_keywords_raw = (string) ($_POST['article_keywords'] ?? '');
        $article_keywords = array();
        if ($article_keywords_raw !== '') {
            $decoded_keywords = json_decode(wp_unslash($article_keywords_raw), true);
            if (is_array($decoded_keywords)) {
                foreach ($decoded_keywords as $kw) {
                    $kw = sanitize_text_field((string) $kw);
                    if ($kw !== '') {
                        $article_keywords[] = $kw;
                    }
                }
            } else {
                $split_keywords = array_map('trim', explode(',', $article_keywords_raw));
                foreach ($split_keywords as $kw) {
                    $kw = sanitize_text_field((string) $kw);
                    if ($kw !== '') {
                        $article_keywords[] = $kw;
                    }
                }
            }
        }
        $estimated_credits = max(0, intval($_POST['estimated_credits'] ?? 0));
        $portal_domain = sanitize_text_field((string) ($_POST['portal_domain'] ?? ''));
        $order_meta_json = (string) ($_POST['order_meta_json'] ?? '');
        $order_meta = array();
        if ($order_meta_json !== '') {
            $decoded_meta = json_decode(wp_unslash($order_meta_json), true);
            if (is_array($decoded_meta)) {
                $order_meta = $decoded_meta;
            }
        }

        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID.');
            return;
        }

        $credentials_check = $this->aysa_validate_adverlink_marketplace_credentials($creds);
        if (is_wp_error($credentials_check)) {
            wp_send_json_error($this->aysa_format_adverlink_marketplace_error($credentials_check));
            return;
        }

        if (empty($portal_id) || empty($keyword) || empty($target_url)) {
            wp_send_json_error('Missing required fields: portal_id, keyword, target_url.');
            return;
        }
        if ($client_ref === '') {
            $client_ref = 'AYSA-' . $project_id . '-' . gmdate('YmdHis') . '-' . wp_generate_password(6, false, false);
        }
        if ($client_name === '') {
            $client_name = sanitize_text_field(get_bloginfo('name'));
        }
        if ($project_ref === '') {
            $project_ref = 'wp-project-' . $project_id;
        }

        if ($estimated_credits <= 0) {
            wp_send_json_error('Unable to determine order cost. Please refresh portals and retry.');
            return;
        }

        $payload = array(
            'portal_id' => $portal_id,
            'portal_domain' => $portal_domain,
            'service_type' => $service_type,
            'billing_type' => $billing_type,
            'keyword' => $keyword,
            'target_url' => $target_url,
            'title' => $title,
            'rel_type' => $rel_type,
            'client_ref' => $client_ref,
            'client_name' => $client_name,
            'project_ref' => $project_ref,
            'estimated_credits' => $estimated_credits,
            'order_meta' => $order_meta,
        );
        if ($service_type === 'seo_article' || $service_type === 'professional_article') {
            if ($article_topic !== '') {
                $payload['article_topic'] = $article_topic;
            }
            if ($article_brief !== '') {
                $payload['article_brief'] = $article_brief;
            }
            if ($article_word_count > 0) {
                $payload['article_word_count'] = $article_word_count;
            }
            if ($article_language !== '') {
                $payload['article_language'] = $article_language;
            }
            if ($article_tone !== '') {
                $payload['article_tone'] = $article_tone;
            }
            if (!empty($article_keywords)) {
                $payload['article_keywords'] = array_values(array_unique($article_keywords));
            }
            if ($article_content_html !== '') {
                $payload['article_content_html'] = $article_content_html;
            }
        } elseif ($service_type === 'seo_ads_text') {
            if ($title === '' || $ad_display_url === '' || $ad_description === '') {
                wp_send_json_error('Missing required fields for seo_ads_text: title, ad_display_url, ad_description.');
                return;
            }
            if (function_exists('mb_strlen') && mb_strlen($ad_description, 'UTF-8') > 90) {
                wp_send_json_error('ad_description max length is 90 characters.');
                return;
            } elseif (!function_exists('mb_strlen') && strlen($ad_description) > 90) {
                wp_send_json_error('ad_description max length is 90 characters.');
                return;
            }
            $payload['ad_display_url'] = $ad_display_url;
            $payload['ad_description'] = $ad_description;
            // SEO text ads should not force sponsored rel on inserted href.
            $payload['rel_type'] = 'follow';
            if ($target_article_url !== '') {
                $payload['target_article_url'] = $target_article_url;
            }
            $payload['duration_months'] = $duration_months;
        }
        $order_result = $this->aysa_adjustments_create_adverlink_order_internal($project_id, $payload);
        if (is_wp_error($order_result)) {
            if ($order_result->get_error_code() === 'insufficient_credits') {
                $error_data = $order_result->get_error_data();
                $payment_response = $this->aysa_build_insufficient_credits_payment_response(
                    $project_id,
                    intval(is_array($error_data) ? ($error_data['required_credits'] ?? 0) : 0),
                    intval(is_array($error_data) ? ($error_data['available_credits'] ?? 0) : 0),
                    'en',
                    match ($service_type) {
                        'professional_article' => 'professional_article_order',
                        'seo_article' => 'seo_article_order',
                        'link_insertion' => 'link_insertion_order',
                        'seo_ads_text' => 'seo_text_ads_order',
                        default => 'order',
                    },
                    'offpage_link_building',
                    array(
                        'portal_id' => $portal_id,
                        'portal_domain' => $portal_domain,
                    )
                );
                wp_send_json_error(array(
                    'message' => 'Insufficient credits.',
                    'assistant_message' => (string) ($payment_response['assistant_message'] ?? ''),
                    'payment_link' => (string) ($payment_response['payment_link'] ?? ''),
                    'required_credits' => intval($payment_response['required_credits'] ?? 0),
                    'available_credits' => intval($payment_response['available_credits'] ?? 0),
                    'missing_credits' => intval($payment_response['missing_credits'] ?? 0),
                ));
                return;
            }
            wp_send_json_error('API request failed: ' . $order_result->get_error_message());
            return;
        }

        $publisher_draft = null;
        $publisher_draft_warning = '';
        if ($service_type === 'seo_article' && is_array($order_result)) {
            $draft_result = $this->aysa_create_publisher_article_draft_for_order($project_id, $order_result);
            if (is_wp_error($draft_result)) {
                $publisher_draft_warning = $draft_result->get_error_message();
            } elseif (isset($draft_result['draft']) && is_array($draft_result['draft'])) {
                $publisher_draft = $draft_result['draft'];
            }
        }

        wp_send_json_success(array(
            'order_ref' => sanitize_text_field((string) ($order_result['order_ref'] ?? '')),
            'status' => sanitize_text_field((string) ($order_result['status'] ?? 'created')),
            'charged_credits_cents' => intval($order_result['charged_credits_cents'] ?? 0),
            'provider_cost_cents' => intval($order_result['provider_cost_cents'] ?? 0),
            'included_link_applied' => !empty($order_result['included_link_applied']),
            'included_link_snapshot' => is_array($order_result['included_link_snapshot'] ?? null) ? $order_result['included_link_snapshot'] : null,
            'actual_credits' => intval($order_result['actual_credits'] ?? 0),
            'reserved_credits' => intval($order_result['reserved_credits'] ?? 0),
            'available_credits' => intval($order_result['available_credits'] ?? 0),
            'raw' => is_array($order_result['raw'] ?? null) ? $order_result['raw'] : array(),
            'publisher_article_draft' => $publisher_draft,
            'publisher_article_draft_warning' => $publisher_draft_warning,
        ));
    }

    public function handle_adverlink_get_orders() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID.');
            return;
        }

        $orders = $this->aysa_get_adverlink_orders($project_id);
        $changed = false;
        foreach ($orders as &$order) {
            $current_ref = sanitize_text_field((string) ($order['order_ref'] ?? ''));
            $portal_id = sanitize_text_field((string) ($order['portal_id'] ?? ''));
            $raw_source = null;
            if (isset($order['raw_track']) && is_array($order['raw_track'])) {
                $raw_source = $order['raw_track'];
            } elseif (isset($order['raw_response']) && is_array($order['raw_response'])) {
                $raw_source = $order['raw_response'];
            }

            if (is_array($raw_source)) {
                $ext_ref = $this->aysa_extract_external_order_ref($raw_source);
                $ref_is_placeholder = (strpos($current_ref, 'local-') === 0)
                    || ($current_ref !== '' && $portal_id !== '' && $current_ref === $portal_id);
                if ($ext_ref !== '' && $ref_is_placeholder) {
                    $order['order_ref'] = $ext_ref;
                    $current_ref = $ext_ref;
                    $changed = true;
                }
                $ext_status = $this->aysa_extract_external_order_status($raw_source);
                if ($ext_status !== '' && empty($order['status'])) {
                    $order['status'] = $ext_status;
                    $changed = true;
                }
                $ext_verification = $this->aysa_extract_external_verification($raw_source);
                if (!empty($ext_verification) && empty($order['verification'])) {
                    $order['verification'] = $ext_verification;
                    $changed = true;
                }
                $ext_subscription = $this->aysa_extract_external_subscription($raw_source);
                if (!empty($ext_subscription) && empty($order['subscription'])) {
                    $order['subscription'] = $ext_subscription;
                    $changed = true;
                }
            }

            $order['is_local_ref'] = (strpos($current_ref, 'local-') === 0);
            $order['is_trackable_ref'] = ($current_ref !== '' && strpos($current_ref, 'local-') !== 0 && !($portal_id !== '' && $current_ref === $portal_id));
        }
        unset($order);
        if ($changed) {
            $this->aysa_save_adverlink_orders($project_id, $orders);
        }

        // Recompute reserved credits from current order states (including completed).
        $this->aysa_reconcile_reserved_credits($project_id, $orders);
        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_adverlink_get_orders',
            'offpage_get_orders_' . $project_id,
            array(
                'module' => 'offpage',
                'items_count' => count($orders),
            ),
            0,
            'wp_ajax/aysa_adverlink_get_orders'
        );
        wp_send_json_success(array('items' => $orders));
    }

    public function handle_adverlink_track_order() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        $creds = $this->aysa_get_adverlink_credentials();
        $api_key = $creds['api_key'];
        $advertiser_org_id = $creds['org_id'];
        $order_ref = sanitize_text_field((string) ($_POST['order_ref'] ?? ''));

        if ($project_id <= 0 || empty($order_ref)) {
            wp_send_json_error('Missing required fields.');
            return;
        }

        $credentials_check = $this->aysa_validate_adverlink_marketplace_credentials($creds);
        if (is_wp_error($credentials_check)) {
            wp_send_json_error($this->aysa_format_adverlink_marketplace_error($credentials_check));
            return;
        }
        $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/public/orders/track', array(
            'project_id' => $project_id,
            'api_key' => $api_key,
            'advertiser_org_id' => $advertiser_org_id,
            'order_ref' => $order_ref,
        ), 45);
        if (is_wp_error($broker_result)) {
            wp_send_json_error('API request failed: ' . $broker_result->get_error_message());
            return;
        }
        $decoded = is_array($broker_result['provider_response'] ?? null)
            ? $broker_result['provider_response']
            : (is_array($broker_result['data'] ?? null) ? $broker_result['data'] : $broker_result);

        $new_status = 'unknown';
        if (is_array($decoded)) {
            $new_status = (string) $this->aysa_extract_external_order_status($decoded);
        }
        if ($new_status === '') {
            $new_status = 'unknown';
        }
        $verification = $this->aysa_extract_external_verification($decoded);
        if (
            is_array($verification) &&
            isset($verification['is_valid']) &&
            $verification['is_valid'] === true &&
            intval($verification['http_status_code'] ?? 0) === 200 &&
            in_array(strtoupper($new_status), array('AWAITING_PUBLISHER', 'PENDING', 'IN_PROGRESS', 'UNKNOWN'), true)
        ) {
            $new_status = 'COMPLETED';
        }

        $orders = $this->aysa_get_adverlink_orders($project_id);
        foreach ($orders as &$order) {
            if ((string)($order['order_ref'] ?? '') === $order_ref) {
                $order['status'] = $new_status;
                $order['updated_at'] = current_time('mysql');
                $order['raw_track'] = $decoded;
                if (!empty($verification)) {
                    $order['verification'] = $verification;
                }
                $subscription = $this->aysa_extract_external_subscription($decoded);
                if (!empty($subscription)) {
                    $order['subscription'] = $subscription;
                }
                break;
            }
        }
        unset($order);

        // Always reconcile after track updates so completed orders release reserved credits.
        $reserved_credits = $this->aysa_reconcile_reserved_credits($project_id, $orders);
        $this->aysa_save_adverlink_orders($project_id, $orders);
        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_adverlink_track_order',
            'offpage_track_order_' . $order_ref,
            array(
                'module' => 'offpage',
                'status' => $new_status,
            ),
            0,
            'wp_ajax/aysa_adverlink_track_order'
        );

        $actual_credits = $this->aysa_get_project_credit_balance($project_id);
        wp_send_json_success(array(
            'order_ref' => $order_ref,
            'status' => $new_status,
            'actual_credits' => $actual_credits,
            'reserved_credits' => $reserved_credits,
            'available_credits' => max(0, $actual_credits - $reserved_credits),
            'raw' => $decoded
        ));
    }

    /**
     * Handle AJAX request to recheck a specific link
     * Uses data from the initial API call for efficiency
     */
    public function handle_recheck_link() {
        // Verify nonce
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }

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

        // Get parameters
        $wtl_id = intval($_POST['wtl_id'] ?? 0);
        
        if (!$wtl_id) {
            wp_send_json_error('Invalid link ID');
            return;
        }

        aysa_debug_log("[AYSA RECHECK] Starting optimized recheck for link ID: {$wtl_id}");
        $project_id = $this->aysa_resolve_project_id_with_local_fallback(intval($_POST['project_id'] ?? 0));
        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_recheck_link',
            'offpage_recheck_link_' . $wtl_id,
            array(
                'module' => 'offpage_legacy',
                'wtl_id' => $wtl_id,
            ),
            2,
            'wp_ajax/aysa_recheck_link'
        );

        // Simply trigger a refresh of the current data by telling frontend to reload that specific line
        // The frontend will use the same API call that was made initially
        wp_send_json_success(array(
            'message' => 'Triggering link data refresh',
            'action' => 'refresh_from_current_data',
            'wtl_id' => $wtl_id,
            'timestamp' => current_time('mysql')
        ));
    }

    /**
     * Handle AJAX request to cancel a WordToLink
     */
    public function handle_cancel_wordtolink() {
        // Verify nonce
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }

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

        // Get parameters
        $wtl_id = intval($_POST['wtl_id'] ?? 0);
        
        if (!$wtl_id) {
            wp_send_json_error('Invalid WordToLink ID');
            return;
        }

        aysa_debug_log("[AYSA CANCEL] Starting cancel process for WordToLink ID: {$wtl_id}");
        $project_id = $this->aysa_resolve_project_id_with_local_fallback(intval($_POST['project_id'] ?? 0));

        // Use the centralized API configuration
        if (!class_exists('Aysa_API_Config')) {
            require_once AYSA_PLUGIN_DIR . 'includes/config/aysa-api-config.php';
        }

        try {
            // Get the full API URL using the centralized configuration
            $api_url = Aysa_API_Config::get_endpoint_url_with_params(
                'cancel_wordtolink',
                array(
                    'wtlId' => $wtl_id
                )
            );

            // For development/testing, if we get 401, simulate the cancellation locally
            aysa_debug_log("[AYSA CANCEL] API URL: {$api_url}");
            aysa_debug_log("[AYSA CANCEL] Note: This endpoint requires authentication. Using user 123123240 as specified.");

            if (!$api_url) {
                wp_send_json_error('Failed to generate API URL');
                return;
            }

            aysa_debug_log("[AYSA CANCEL] Making DELETE API call to: {$api_url}");

            // Make DELETE API call with authentication simulation
            // Since the backend expects an authenticated user (123123240), we'll simulate this
            $response = wp_remote_request($api_url, array(
                'method' => 'DELETE',
                'timeout' => 30,
                'headers' => array(
                    'Content-Type' => 'application/json',
                    'Authorization' => 'Bearer dummy-token-for-user-123123240',
                    'X-User-ID' => '123123240'  // Add user ID as custom header
                )
            ));

            aysa_debug_log("[AYSA CANCEL] API call completed");

            if (is_wp_error($response)) {
                aysa_debug_log("[AYSA CANCEL] API request failed: " . $response->get_error_message());
                wp_send_json_error('API request failed: ' . $response->get_error_message());
                return;
            }

            $response_code = wp_remote_retrieve_response_code($response);
            $response_body = wp_remote_retrieve_body($response);

            aysa_debug_log("[AYSA CANCEL] API response code: {$response_code}");
            aysa_debug_log("[AYSA CANCEL] API response body: " . substr($response_body, 0, 500));

            if ($response_code === 200 || $response_code === 204) {
                // Success - WordToLink cancelled
                aysa_debug_log("[AYSA CANCEL] WordToLink {$wtl_id} cancelled successfully");
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_cancel_wordtolink',
                    'offpage_cancel_wordtolink_' . $wtl_id,
                    array(
                        'module' => 'offpage_legacy',
                        'wtl_id' => $wtl_id,
                        'response_code' => $response_code,
                        'source' => 'remote',
                    ),
                    10,
                    'wp_ajax/aysa_cancel_wordtolink'
                );
                
                wp_send_json_success(array(
                    'message' => 'WordToLink cancelled successfully',
                    'wtl_id' => $wtl_id,
                    'action' => 'cancelled',
                    'timestamp' => current_time('mysql'),
                    'api_response_code' => $response_code
                ));
            } else if ($response_code === 401) {
                // Unauthorized - simulate cancellation for development
                aysa_debug_log("[AYSA CANCEL] API returned 401 (Unauthorized). Simulating cancellation for development.");
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_cancel_wordtolink',
                    'offpage_cancel_wordtolink_' . $wtl_id,
                    array(
                        'module' => 'offpage_legacy',
                        'wtl_id' => $wtl_id,
                        'response_code' => 401,
                        'source' => 'simulated_401',
                    ),
                    10,
                    'wp_ajax/aysa_cancel_wordtolink'
                );
                
                wp_send_json_success(array(
                    'message' => 'WordToLink cancellation simulated (development mode)',
                    'wtl_id' => $wtl_id,
                    'action' => 'cancelled',
                    'timestamp' => current_time('mysql'),
                    'api_response_code' => 401,
                    'note' => 'Cancellation simulated due to authentication requirements'
                ));
            } else if ($response_code === 404) {
                // WordToLink not found or already cancelled
                aysa_debug_log("[AYSA CANCEL] WordToLink {$wtl_id} not found or already cancelled");
                
                wp_send_json_error('WordToLink not found or already cancelled');
            } else {
                // Other error
                aysa_debug_log("[AYSA CANCEL] API returned error code: {$response_code}");
                
                $error_message = 'API returned error: ' . $response_code;
                
                // Try to parse error message from response
                if (!empty($response_body)) {
                    $error_data = json_decode($response_body, true);
                    if (json_last_error() === JSON_ERROR_NONE && isset($error_data['message'])) {
                        $error_message = $error_data['message'];
                    }
                }
                
                wp_send_json_error($error_message);
            }

        } catch (Exception $e) {
            aysa_debug_log("[AYSA CANCEL] Exception during cancel: " . $e->getMessage());
            wp_send_json_error('Error during cancellation: ' . $e->getMessage());
        }
    }
    
    /**
     * AJAX handler: Cleanup SuggestedKeywords
     */
    public function handle_cleanup_suggested_keywords() {
        if (!$this->aysa_guard_destructive_tool_request()) {
            return;
        }

        // Security checks
        if (!$this->aysa_current_user_can_workspace()) {
            wp_send_json_error('Access denied');
            return;
        }
        
        if (!wp_verify_nonce($_POST['nonce'], 'aysa-cleanup')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        if ($project_id <= 0) {
            wp_send_json_error('Invalid Project ID');
            return;
        }
        
        try {
            if (!class_exists('Aysa_Auth_Manager')) {
                require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
            }
            
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $keywords_url = 'api/SuggestedKeywords/ByProject/' . $project_id;
            $keywords_response = $auth_manager->make_authenticated_request($keywords_url, 'GET');
            
            $deleted = 0;
            $errors = [];
            
            if ($keywords_response !== false && is_array($keywords_response)) {
                // Check if response is wrapped
                if (isset($keywords_response['data']) && is_array($keywords_response['data'])) {
                    $keywords_response = $keywords_response['data'];
                }
                
                foreach ($keywords_response as $keyword) {
                    $keyword_id = isset($keyword['id']) ? $keyword['id'] : (isset($keyword['Id']) ? $keyword['Id'] : null);
                    if ($keyword_id) {
                        $delete_url = 'api/SuggestedKeywords/' . $keyword_id;
                        $delete_response = $auth_manager->make_authenticated_request($delete_url, 'DELETE');
                        
                        if ($delete_response !== false) {
                            $deleted++;
                        } else {
                            $errors[] = 'Failed to delete keyword ID: ' . $keyword_id;
                        }
                    }
                }
            }
            
            $this->invalidate_suggested_keywords_cache($project_id);

            wp_send_json_success(array(
                'deleted' => $deleted,
                'errors' => $errors
            ));
            
        } catch (Exception $e) {
            wp_send_json_error('Error: ' . $e->getMessage());
        }
    }

    /**
     * Build cache key for suggested keywords.
     */
    private function get_suggested_keywords_cache_key($project_id) {
        return 'aysa_suggested_keywords_cache_' . intval($project_id);
    }

    /**
     * Remove cached suggested keywords for a project.
     */
    private function invalidate_suggested_keywords_cache($project_id) {
        $cache_key = $this->get_suggested_keywords_cache_key($project_id);
        delete_transient($cache_key);
    }

    /**
     * AJAX handler: get suggested keywords with transient caching.
     */
    public function handle_get_suggested_keywords_cached() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_keyword_cache_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        if ($project_id <= 0) {
            $project_id = intval(get_option('aysa_project_id', 0));
        }
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID');
            return;
        }

        $force_refresh = isset($_POST['force_refresh']) && in_array(strtolower((string) $_POST['force_refresh']), array('1', 'true', 'yes'), true);
        $cache_ttl = intval(apply_filters('aysa_suggested_keywords_cache_ttl', 15 * MINUTE_IN_SECONDS));
        if ($cache_ttl <= 0) {
            $cache_ttl = 15 * MINUTE_IN_SECONDS;
        }

        $cache_key = $this->get_suggested_keywords_cache_key($project_id);
        $cached_payload = get_transient($cache_key);

        if (!$force_refresh && is_array($cached_payload) && isset($cached_payload['keywords']) && is_array($cached_payload['keywords'])) {
            wp_send_json_success(array(
                'keywords' => $cached_payload['keywords'],
                'cached' => true,
                'fetched_at' => isset($cached_payload['fetched_at']) ? intval($cached_payload['fetched_at']) : 0,
                'cache_ttl' => $cache_ttl
            ));
            return;
        }

        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }

        try {
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $decoded = $auth_manager->make_authenticated_request('api/SuggestedKeywords/ByProject/' . rawurlencode((string) $project_id), 'GET');
        } catch (Exception $e) {
            wp_send_json_error('Failed to fetch suggested keywords: ' . $e->getMessage());
            return;
        }

        $keywords = $this->normalize_aysa_collection_response($decoded);
        if (empty($keywords) && is_array($decoded)) {
            if (isset($decoded['keywords']) && is_array($decoded['keywords'])) {
                $keywords = array_values($decoded['keywords']);
            } elseif (isset($decoded['Keywords']) && is_array($decoded['Keywords'])) {
                $keywords = array_values($decoded['Keywords']);
            }
        }

        $payload = array(
            'keywords' => $keywords,
            'fetched_at' => time()
        );
        set_transient($cache_key, $payload, $cache_ttl);

        wp_send_json_success(array(
            'keywords' => $keywords,
            'cached' => false,
            'fetched_at' => $payload['fetched_at'],
            'cache_ttl' => $cache_ttl
        ));
    }

    public function handle_keyword_discovery_get_associations() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        // Local-first mode for Keyword Discovery load:
        // - avoids remote timeouts on passive page loads
        // - avoids credit consumption for read-only local associations
        // Remote can still be requested explicitly via force_remote=1.
        $force_remote = !empty($_POST['force_remote']) && intval($_POST['force_remote']) === 1;
        if (!$force_remote) {
            $local_rows = $this->get_keyword_discovery_local_associations($project_id);
            wp_send_json_success(array(
                'project_id' => $project_id,
                'associations' => array_values($local_rows),
                'count' => count($local_rows),
                'source' => 'local_only',
            ));
            return;
        }

        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }

        try {
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $raw = $auth_manager->make_authenticated_request(
                'api/Association?projectId=' . $project_id . '&pageSize=1000&pageNumber=1&sortOrder=asc',
                'GET'
            );
            $rows = $this->normalize_aysa_collection_response($raw);
            if (!is_array($rows)) {
                $rows = array();
            }

            $rows = $this->merge_keyword_discovery_associations(
                $rows,
                $this->get_keyword_discovery_local_associations($project_id)
            );

            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_keyword_discovery_get_associations',
                'keyword_discovery_get_associations',
                array(
                    'module' => 'keyword_discovery',
                    'source' => 'remote_with_local_merge',
                    'count' => count($rows),
                ),
                5,
                'api/Association'
            );

            wp_send_json_success(array(
                'project_id' => $project_id,
                'associations' => array_values($rows),
                'count' => count($rows),
            ));
        } catch (Exception $e) {
            $local_rows = $this->get_keyword_discovery_local_associations($project_id);
            wp_send_json_success(array(
                'project_id' => $project_id,
                'associations' => array_values($local_rows),
                'count' => count($local_rows),
                'source' => 'local_fallback',
                'warning' => 'Remote association API unavailable: ' . $e->getMessage(),
            ));
        }
    }

    public function handle_keyword_discovery_upsert_association() {
        $nonce = isset($_POST['nonce']) ? (string) $_POST['nonce'] : '';
        $nonce_ok = ($nonce !== '') && (
            wp_verify_nonce($nonce, 'aysa_nonce') ||
            wp_verify_nonce($nonce, 'aysa_metabox_nonce') ||
            wp_verify_nonce($nonce, 'aysa-ajax-nonce') ||
            wp_verify_nonce($nonce, 'aysa_wizard_nonce')
        );
        if (!$nonce_ok) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        $keyword = trim((string) ($_POST['keyword'] ?? ''));
        $associated_on = trim((string) ($_POST['associated_on'] ?? ''));
        $id_intern = intval($_POST['id_intern'] ?? 0);
        $value = trim((string) ($_POST['value'] ?? ''));

        if ($project_id <= 0 || $keyword === '' || $associated_on === '' || $id_intern <= 0 || $value === '') {
            wp_send_json_error('Missing required fields');
            return;
        }

        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }

        try {
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $payload = array(
                'projectId' => $project_id,
                'keyword' => $keyword,
                'associatedOn' => $associated_on,
                'idIntern' => $id_intern,
                'value' => $value,
            );
            $raw = $auth_manager->make_authenticated_request('api/Association/upsert', 'PUT', $payload);
            if ($raw === false) {
                $local_assoc = $this->upsert_keyword_discovery_local_association(
                    $project_id,
                    $keyword,
                    $associated_on,
                    $id_intern,
                    $value
                );
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_keyword_discovery_upsert_association',
                    'keyword_discovery_upsert_association',
                    array(
                        'module' => 'keyword_discovery',
                        'source' => 'local_fallback',
                        'keyword' => $keyword,
                        'associated_on' => $associated_on,
                    ),
                    5,
                    'api/Association/upsert'
                );
                wp_send_json_success(array(
                    'project_id' => $project_id,
                    'association' => $local_assoc,
                    'source' => 'local_fallback',
                ));
                return;
            }
            // Mirror successful remote upsert locally so the on-page table can always read
            // consistent DB-backed associations even when remote fetch fails later.
            $local_assoc = $this->upsert_keyword_discovery_local_association(
                $project_id,
                $keyword,
                $associated_on,
                $id_intern,
                $value
            );
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_keyword_discovery_upsert_association',
                'keyword_discovery_upsert_association',
                array(
                    'module' => 'keyword_discovery',
                    'source' => 'remote_with_local_mirror',
                    'keyword' => $keyword,
                    'associated_on' => $associated_on,
                ),
                5,
                'api/Association/upsert'
            );
            wp_send_json_success(array(
                'project_id' => $project_id,
                'association' => is_array($raw) ? $raw : array(),
                'local_association' => $local_assoc,
                'source' => 'remote_with_local_mirror',
            ));
        } catch (Exception $e) {
            $local_assoc = $this->upsert_keyword_discovery_local_association(
                $project_id,
                $keyword,
                $associated_on,
                $id_intern,
                $value
            );
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_keyword_discovery_upsert_association',
                'keyword_discovery_upsert_association',
                array(
                    'module' => 'keyword_discovery',
                    'source' => 'local_fallback_exception',
                    'keyword' => $keyword,
                    'associated_on' => $associated_on,
                ),
                5,
                'api/Association/upsert'
            );
            wp_send_json_success(array(
                'project_id' => $project_id,
                'association' => $local_assoc,
                'source' => 'local_fallback',
                'warning' => 'Remote association API unavailable: ' . $e->getMessage(),
            ));
        }
    }

    private function get_keyword_discovery_local_associations_option_key($project_id) {
        return 'aysa_keyword_discovery_local_associations_' . intval($project_id);
    }

    private function get_keyword_discovery_local_associations($project_id) {
        $key = $this->get_keyword_discovery_local_associations_option_key($project_id);
        $rows = get_option($key, array());
        if (!is_array($rows)) {
            return array();
        }
        $normalized = array();
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['keyword'] ?? ''));
            $associated_on = trim((string) ($row['associatedOn'] ?? $row['associated_on'] ?? ''));
            $id_intern = intval($row['idIntern'] ?? $row['id_intern'] ?? 0);
            $value = trim((string) ($row['value'] ?? ''));
            if ($keyword === '' || $associated_on === '' || $id_intern <= 0 || $value === '') {
                continue;
            }
            $normalized[] = array(
                'id' => isset($row['id']) && is_numeric($row['id'])
                    ? intval($row['id'])
                    : $this->build_keyword_discovery_stable_local_id($keyword, $associated_on, $id_intern),
                'projectId' => intval($project_id),
                'keyword' => $keyword,
                'associatedOn' => $associated_on,
                'idIntern' => $id_intern,
                'value' => $value,
                'desiredPosition' => isset($row['desiredPosition']) ? intval($row['desiredPosition']) : null,
                'source' => 'local_fallback',
                'isMain' => !empty($row['isMain']) || !empty($row['is_main']) ? 1 : 0,
                'secondKeyword' => trim((string) ($row['secondKeyword'] ?? $row['second_keyword'] ?? '')),
                'secondaryKeywords' => isset($row['secondaryKeywords']) && is_array($row['secondaryKeywords'])
                    ? array_values(array_filter(array_map('strval', $row['secondaryKeywords'])))
                    : array(),
                'entityKey' => trim((string) ($row['entityKey'] ?? $row['entity_key'] ?? '')),
                'mappingSource' => trim((string) ($row['mappingSource'] ?? $row['mapping_source'] ?? '')),
                'confidenceScore' => isset($row['confidenceScore']) && is_numeric($row['confidenceScore']) ? floatval($row['confidenceScore']) : (isset($row['confidence_score']) && is_numeric($row['confidence_score']) ? floatval($row['confidence_score']) : null),
                'intentType' => trim((string) ($row['intentType'] ?? $row['intent_type'] ?? '')),
                'targetUrl' => trim((string) ($row['targetUrl'] ?? $row['target_url'] ?? '')),
                'updatedAt' => $row['updatedAt'] ?? current_time('mysql'),
            );
        }
        return $normalized;
    }

    private function save_keyword_discovery_local_associations($project_id, $rows) {
        $key = $this->get_keyword_discovery_local_associations_option_key($project_id);
        update_option($key, array_values($rows), false);
        $this->sync_keyword_discovery_associations_batch_to_entity_meta($rows);
        $this->mark_keyword_discovery_entity_meta_synced($project_id);
    }

    private function upsert_keyword_discovery_local_association($project_id, $keyword, $associated_on, $id_intern, $value) {
        $rows = $this->get_keyword_discovery_local_associations($project_id);
        $keyword_norm = strtolower(trim((string) $keyword));
        $associated_norm = strtolower(trim((string) $associated_on));
        $found_index = -1;
        foreach ($rows as $idx => $row) {
            $row_keyword = strtolower(trim((string) ($row['keyword'] ?? '')));
            $row_assoc = strtolower(trim((string) ($row['associatedOn'] ?? '')));
            $row_id_intern = intval($row['idIntern'] ?? 0);
            if ($row_keyword === $keyword_norm && $row_assoc === $associated_norm && $row_id_intern === intval($id_intern)) {
                $found_index = $idx;
                break;
            }
        }

        $new_row = array(
            'id' => $this->build_keyword_discovery_stable_local_id($keyword_norm, $associated_norm, intval($id_intern)),
            'projectId' => intval($project_id),
            'keyword' => trim((string) $keyword),
            'associatedOn' => trim((string) $associated_on),
            'idIntern' => intval($id_intern),
            'value' => trim((string) $value),
            'source' => 'local_fallback',
            'updatedAt' => current_time('mysql'),
        );

        if ($found_index >= 0) {
            $rows[$found_index] = array_merge($rows[$found_index], $new_row);
        } else {
            $rows[] = $new_row;
        }

        $this->save_keyword_discovery_local_associations($project_id, $rows);
        $this->sync_keyword_discovery_association_to_entity_meta($new_row);
        return $new_row;
    }

    private function update_keyword_discovery_local_association_desired_position($project_id, $keyword, $associated_on, $id_intern, $desired_position) {
        $rows = $this->get_keyword_discovery_local_associations($project_id);
        $keyword_norm = strtolower(trim((string) $keyword));
        $associated_norm = strtolower(trim((string) $associated_on));
        $desired_position = intval($desired_position);
        foreach ($rows as $idx => $row) {
            $row_keyword = strtolower(trim((string) ($row['keyword'] ?? '')));
            $row_assoc = strtolower(trim((string) ($row['associatedOn'] ?? $row['associated_on'] ?? '')));
            $row_id_intern = intval($row['idIntern'] ?? $row['id_intern'] ?? 0);
            if ($row_keyword !== $keyword_norm || $row_assoc !== $associated_norm || $row_id_intern !== intval($id_intern)) {
                continue;
            }

            $rows[$idx]['desiredPosition'] = $desired_position > 0 ? $desired_position : null;
            $rows[$idx]['updatedAt'] = current_time('mysql');
            $this->save_keyword_discovery_local_associations($project_id, $rows);
            $this->sync_keyword_discovery_association_to_entity_meta($rows[$idx]);
            return $rows[$idx];
        }

        return null;
    }

    private function find_keyword_discovery_local_association($project_id, $keyword, $associated_on = '', $id_intern = 0) {
        $rows = $this->get_keyword_discovery_local_associations($project_id);
        $keyword_norm = strtolower(trim((string) $keyword));
        $associated_norm = strtolower(trim((string) $associated_on));
        $id_intern = intval($id_intern);
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $row_keyword = strtolower(trim((string) ($row['keyword'] ?? '')));
            if ($row_keyword !== $keyword_norm) {
                continue;
            }
            if ($associated_norm !== '' && strtolower(trim((string) ($row['associatedOn'] ?? $row['associated_on'] ?? ''))) !== $associated_norm) {
                continue;
            }
            if ($id_intern > 0 && intval($row['idIntern'] ?? $row['id_intern'] ?? 0) !== $id_intern) {
                continue;
            }
            return $row;
        }
        return null;
    }

    private function sync_keyword_discovery_associations_batch_to_entity_meta($rows) {
        if (!is_array($rows) || empty($rows)) {
            return;
        }
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $this->sync_keyword_discovery_association_to_entity_meta($row);
        }
    }

    private function sync_keyword_discovery_association_to_entity_meta($row) {
        if (!is_array($row)) {
            return;
        }
        $keyword = trim((string) ($row['keyword'] ?? ''));
        if ($keyword === '') {
            return;
        }

        $associated_on = (string) ($row['associatedOn'] ?? $row['associated_on'] ?? '');
        $id_intern = intval($row['idIntern'] ?? $row['id_intern'] ?? 0);
        if ($id_intern <= 0 || $associated_on === '') {
            return;
        }

        $target = $this->resolve_keyword_discovery_entity_target($associated_on, $id_intern);
        if (!is_array($target)) {
            return;
        }

        $kind = (string) ($target['kind'] ?? '');
        $entity_id = intval($target['id'] ?? 0);
        if (($kind !== 'post' && $kind !== 'term') || $entity_id <= 0) {
            return;
        }

        $keyword_data = $this->get_keyword_discovery_entity_keyword_data($kind, $entity_id);
        if (!is_array($keyword_data)) {
            $keyword_data = array();
        }

        $main_keyword = trim((string) ($keyword_data['keyword'] ?? $keyword_data['aysa_keyword'] ?? ''));
        $secondary_raw = (string) ($keyword_data['second_keyword'] ?? $keyword_data['aysa_secondary_keywords'] ?? '');
        $secondary = $this->parse_keyword_discovery_secondary_keywords($secondary_raw);
        $incoming_norm = strtolower($keyword);

        if ($main_keyword === '') {
            $main_keyword = $keyword;
        } else {
            $main_norm = strtolower($main_keyword);
            if ($main_norm !== $incoming_norm) {
                $seen_secondary = array();
                foreach ($secondary as $sec) {
                    $seen_secondary[strtolower($sec)] = true;
                }
                if (!isset($seen_secondary[$incoming_norm])) {
                    $secondary[] = $keyword;
                }
            }
        }

        $secondary = $this->parse_keyword_discovery_secondary_keywords(implode(', ', $secondary));
        $secondary_string = implode(', ', $secondary);

        $keyword_data['keyword'] = $main_keyword;
        $keyword_data['aysa_keyword'] = $main_keyword;
        $keyword_data['second_keyword'] = $secondary_string;
        $keyword_data['aysa_secondary_keywords'] = $secondary_string;

        $this->update_keyword_discovery_entity_keyword_data($kind, $entity_id, $keyword_data);
    }

    private function resolve_keyword_discovery_entity_target($associated_on, $id_intern) {
        $id_intern = intval($id_intern);
        if ($id_intern <= 0) {
            return null;
        }

        $assoc = strtolower(trim((string) $associated_on));
        if ($assoc === '') {
            return null;
        }

        if (strpos($assoc, 'categor') !== false || strpos($assoc, 'term') !== false) {
            $term = get_term($id_intern);
            if ($term && !is_wp_error($term)) {
                return array('kind' => 'term', 'id' => $id_intern);
            }
            return null;
        }

        if (strpos($assoc, 'post') !== false || strpos($assoc, 'page') !== false || strpos($assoc, 'product') !== false) {
            $post = get_post($id_intern);
            if ($post instanceof WP_Post) {
                return array('kind' => 'post', 'id' => $id_intern);
            }
            return null;
        }

        $post = get_post($id_intern);
        if ($post instanceof WP_Post) {
            return array('kind' => 'post', 'id' => $id_intern);
        }
        $term = get_term($id_intern);
        if ($term && !is_wp_error($term)) {
            return array('kind' => 'term', 'id' => $id_intern);
        }

        return null;
    }

    private function get_keyword_discovery_entity_keyword_data($kind, $entity_id) {
        if ($kind === 'term') {
            $data = get_term_meta($entity_id, 'aysa_keyword_data', true);
            return is_array($data) ? $data : array();
        }
        $data = get_post_meta($entity_id, 'aysa_keyword_data', true);
        return is_array($data) ? $data : array();
    }

    private function update_keyword_discovery_entity_keyword_data($kind, $entity_id, $keyword_data) {
        if ($kind === 'term') {
            update_term_meta($entity_id, 'aysa_keyword_data', $keyword_data);
            return;
        }
        update_post_meta($entity_id, 'aysa_keyword_data', $keyword_data);
    }

    private function parse_keyword_discovery_secondary_keywords($raw_secondary) {
        $raw_secondary = trim((string) $raw_secondary);
        if ($raw_secondary === '') {
            return array();
        }

        $parts = array_map('trim', explode(',', $raw_secondary));
        $parts = array_filter($parts, function($value) {
            return $value !== '';
        });

        $deduped = array();
        $seen = array();
        foreach ($parts as $part) {
            $key = strtolower($part);
            if (isset($seen[$key])) {
                continue;
            }
            $seen[$key] = true;
            $deduped[] = $part;
        }
        return $deduped;
    }

    private function normalize_keyword_discovery_entity_type($entity_type) {
        $entity_type = strtolower(trim((string) $entity_type));
        return match ($entity_type) {
            'page', 'post', 'article', 'product', 'produs' => 'post',
            'category', 'categorie', 'term' => 'term',
            default => '',
        };
    }

    private function resolve_keyword_discovery_entity_from_payload($entity_type, $entity_id, $entity_label = '') {
        $kind = $this->normalize_keyword_discovery_entity_type($entity_type);
        $entity_id = intval($entity_id);
        $entity_label = trim((string) $entity_label);

        if ($entity_id > 0 && $kind !== '') {
            if ($kind === 'post') {
                $post = get_post($entity_id);
                if ($post instanceof WP_Post) {
                    return array(
                        'kind' => 'post',
                        'id' => $entity_id,
                        'associated_on' => $post->post_type === 'product' ? 'product' : 'page',
                        'label' => get_the_title($entity_id),
                    );
                }
            } else {
                $term = get_term($entity_id);
                if ($term && !is_wp_error($term)) {
                    return array(
                        'kind' => 'term',
                        'id' => $entity_id,
                        'associated_on' => 'category',
                        'label' => (string) $term->name,
                    );
                }
            }
        }

        if ($entity_label === '' || $kind === '') {
            return null;
        }

        if ($kind === 'post') {
            $posts = get_posts(array(
                'post_type' => array('page', 'post', 'product'),
                'post_status' => array('publish', 'draft', 'pending', 'private'),
                'posts_per_page' => 20,
                'title' => $entity_label,
                'suppress_filters' => false,
            ));
            foreach ($posts as $post) {
                if (!$post instanceof WP_Post) {
                    continue;
                }
                if (strcasecmp(trim((string) $post->post_title), $entity_label) === 0) {
                    return array(
                        'kind' => 'post',
                        'id' => intval($post->ID),
                        'associated_on' => $post->post_type === 'product' ? 'product' : ($post->post_type === 'post' ? 'article' : 'page'),
                        'label' => trim((string) $post->post_title),
                    );
                }
            }
        }

        if ($kind === 'term') {
            $terms = get_terms(array(
                'taxonomy' => array('category', 'product_cat'),
                'hide_empty' => false,
                'name' => $entity_label,
                'number' => 20,
            ));
            if (!is_wp_error($terms)) {
                foreach ($terms as $term) {
                    if (!($term instanceof WP_Term)) {
                        continue;
                    }
                    if (strcasecmp(trim((string) $term->name), $entity_label) === 0) {
                        return array(
                            'kind' => 'term',
                            'id' => intval($term->term_id),
                            'associated_on' => 'category',
                            'label' => trim((string) $term->name),
                        );
                    }
                }
            }
        }

        return null;
    }

    private function aysa_adjustments_execute_keyword_association_upsert($project_id, $payload, $language) {
        $keyword = trim((string) ($payload['keyword'] ?? ''));
        $entity = $this->resolve_keyword_discovery_entity_from_payload(
            (string) ($payload['entity_type'] ?? ''),
            intval($payload['entity_id'] ?? 0),
            (string) ($payload['entity_label'] ?? '')
        );

        if ($keyword === '' || !is_array($entity)) {
            return array(
                'module' => 'keyword_research',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I need both the exact keyword and the target entity before I can save the mapping.',
                    'Am nevoie atat de keywordul exact, cat si de entitatea tinta inainte sa pot salva maparea.',
                    'Трябват ми и точният keyword, и целевата entitate, преди да мога да запазя mapping-а.'
                ),
                'ui_action' => 'open_artifact_matches',
            );
        }

        $associated_on = (string) ($entity['associated_on'] ?? '');
        $id_intern = intval($entity['id'] ?? 0);
        $label = trim((string) ($entity['label'] ?? ''));
        if ($associated_on === '' || $id_intern <= 0 || $label === '') {
            return array(
                'module' => 'keyword_research',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I could not resolve the target entity well enough to save the mapping.',
                    'Nu am reusit sa rezolv suficient de bine entitatea tinta ca sa salvez maparea.',
                    'Не успях да разпозная достатъчно добре целевата entitate, за да запазя mapping-а.'
                ),
                'ui_action' => 'open_artifact_matches',
            );
        }

        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }

        $payload_remote = array(
            'projectId' => intval($project_id),
            'keyword' => $keyword,
            'associatedOn' => $associated_on,
            'idIntern' => $id_intern,
            'value' => $label,
        );

        try {
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $raw = $auth_manager->make_authenticated_request('api/Association/upsert', 'PUT', $payload_remote);
            $local_assoc = $this->upsert_keyword_discovery_local_association($project_id, $keyword, $associated_on, $id_intern, $label);
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_adjustments_keyword_association_upsert',
                'adjustments_keyword_association_upsert',
                array(
                    'module' => 'keyword_discovery',
                    'source' => is_array($raw) ? 'adjustments_remote_with_local_mirror' : 'adjustments_local_fallback',
                    'keyword' => $keyword,
                    'associated_on' => $associated_on,
                ),
                5,
                'api/Association/upsert'
            );

            return array(
                'module' => 'keyword_research',
                'assistant_message' => match ($language) {
                    'ro' => 'Am mapat keywordul "' . $keyword . '" la "' . $label . '". Asocierea este acum salvata si sincronizata local pentru Keyword Discovery.',
                    'bg' => 'Свързах keyword-а "' . $keyword . '" към "' . $label . '". Свързването вече е запазено и синхронизирано локално за Keyword Discovery.',
                    default => 'I mapped the keyword "' . $keyword . '" to "' . $label . '". The association is now saved and mirrored locally for Keyword Discovery.',
                },
                'result' => array(
                    'keyword' => $keyword,
                    'association' => is_array($raw) ? $raw : $local_assoc,
                    'local_association' => $local_assoc,
                ),
                'ui_action' => 'open_artifact_matches',
            );
        } catch (Exception $e) {
            $local_assoc = $this->upsert_keyword_discovery_local_association($project_id, $keyword, $associated_on, $id_intern, $label);
            return array(
                'module' => 'keyword_research',
                'assistant_message' => match ($language) {
                    'ro' => 'Am salvat local maparea pentru "' . $keyword . '" la "' . $label . '". API-ul remote nu a raspuns, dar Keyword Discovery vede acum asocierea locala.',
                    'bg' => 'Запазих локално mapping-а за "' . $keyword . '" към "' . $label . '". Remote API не отговори, но Keyword Discovery вече вижда локалното свързване.',
                    default => 'I saved the mapping locally for "' . $keyword . '" to "' . $label . '". The remote API did not respond, but Keyword Discovery can already see the local association.',
                },
                'result' => array(
                    'keyword' => $keyword,
                    'local_association' => $local_assoc,
                    'warning' => $e->getMessage(),
                ),
                'ui_action' => 'open_artifact_matches',
            );
        }
    }

    private function aysa_adjustments_execute_keyword_target_position_set($project_id, $payload, $language) {
        $keyword = trim((string) ($payload['keyword'] ?? ''));
        $target_position = intval($payload['target_position'] ?? 0);
        $entity = $this->resolve_keyword_discovery_entity_from_payload(
            (string) ($payload['entity_type'] ?? ''),
            intval($payload['entity_id'] ?? 0),
            (string) ($payload['entity_label'] ?? '')
        );

        if ($keyword === '' || $target_position <= 0) {
            return array(
                'module' => 'keyword_research',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I need both the exact keyword and the target position before I can update the desired position.',
                    'Am nevoie atat de keywordul exact, cat si de targetul dorit inainte sa pot actualiza pozitia.',
                    'Трябват ми и точният keyword, и target позицията, преди да мога да обновя desired position.'
                ),
                'ui_action' => 'open_artifact_matches',
            );
        }

        $association = null;
        if (is_array($entity)) {
            $association = $this->find_keyword_discovery_local_association(
                $project_id,
                $keyword,
                (string) ($entity['associated_on'] ?? ''),
                intval($entity['id'] ?? 0)
            );
        }
        if (!is_array($association)) {
            $association = $this->find_keyword_discovery_local_association($project_id, $keyword);
        }
        if (!is_array($association)) {
            return array(
                'module' => 'keyword_research',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I could not find a saved association for that keyword yet. First map the keyword to a page, product, category, or article, then I can set the desired position.',
                    'Nu am gasit inca o asociere salvata pentru acel keyword. Mai intai mapeaza keywordul pe o pagina, produs, categorie sau articol, apoi pot seta pozitia dorita.',
                    'Все още не намерих запазено свързване за този keyword. Първо вържи keyword-а към страница, продукт, категория или статия и после мога да задам desired position.'
                ),
                'ui_action' => 'open_artifact_matches',
            );
        }

        $associated_on = (string) ($association['associatedOn'] ?? $association['associated_on'] ?? '');
        $id_intern = intval($association['idIntern'] ?? $association['id_intern'] ?? 0);
        $association_id = intval($association['id'] ?? 0);
        $label = trim((string) ($association['value'] ?? ''));
        $local_assoc = $this->update_keyword_discovery_local_association_desired_position($project_id, $keyword, $associated_on, $id_intern, $target_position);

        if ($association_id > 0 && !class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }

        $remote_warning = '';
        if ($association_id > 0) {
            try {
                $auth_manager = Aysa_Auth_Manager::get_instance();
                $auth_manager->make_authenticated_request(
                    'api/Association/' . rawurlencode((string) $association_id) . '/desired-position',
                    'PATCH',
                    array('DesiredPosition' => $target_position)
                );
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_adjustments_keyword_target_position_set',
                    'adjustments_keyword_target_position_set',
                    array(
                        'module' => 'keyword_discovery',
                        'source' => 'adjustments_remote_with_local_mirror',
                        'keyword' => $keyword,
                        'target_position' => $target_position,
                    ),
                    2,
                    'api/Association/{id}/desired-position'
                );
            } catch (Exception $e) {
                $remote_warning = $e->getMessage();
            }
        }

        return array(
            'module' => 'keyword_research',
            'assistant_message' => match ($language) {
                'ro' => 'Am setat targetul pentru "' . $keyword . '" la pozitia ' . $target_position . ($label !== '' ? ' pe "' . $label . '"' : '') . '. Folosesc acest target in discutia despre efort, refresh si cost.',
                'bg' => 'Зададох target-а за "' . $keyword . '" на позиция ' . $target_position . ($label !== '' ? ' върху "' . $label . '"' : '') . '. Използвам този target в разговора за усилие, refresh и разход.',
                default => 'I set the target for "' . $keyword . '" to position ' . $target_position . ($label !== '' ? ' on "' . $label . '"' : '') . '. I now use that target in the effort, refresh, and cost discussion.',
            },
            'result' => array(
                'keyword' => $keyword,
                'target_position' => $target_position,
                'association' => $local_assoc ?: $association,
                'warning' => $remote_warning,
            ),
            'ui_action' => 'open_artifact_matches',
        );
    }

    private function aysa_adjustments_execute_keyword_primary_set($project_id, $payload, $language) {
        $keyword = trim((string) ($payload['keyword'] ?? ''));
        $entity = $this->resolve_keyword_discovery_entity_from_payload(
            (string) ($payload['entity_type'] ?? ''),
            intval($payload['entity_id'] ?? 0),
            (string) ($payload['entity_label'] ?? '')
        );
        if ($keyword === '' || !is_array($entity)) {
            return array(
                'module' => 'keyword_research',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I need both the exact keyword and the target entity before I can mark the primary keyword.',
                    'Am nevoie atat de keywordul exact, cat si de entitatea tinta inainte sa pot marca keywordul principal.',
                    'Трябват ми и точният keyword, и целевата entitate, преди да мога да маркирам primary keyword.'
                ),
                'ui_action' => 'open_keyword_discovery',
            );
        }

        $kind = (string) ($entity['kind'] ?? '');
        $entity_id = intval($entity['id'] ?? 0);
        $label = trim((string) ($entity['label'] ?? ''));
        if (($kind !== 'post' && $kind !== 'term') || $entity_id <= 0) {
            return array(
                'module' => 'keyword_research',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I could not resolve the entity well enough to set the primary keyword.',
                    'Nu am reusit sa rezolv suficient de bine entitatea ca sa setez keywordul principal.',
                    'Не успях да разпозная достатъчно добре entitate-та, за да задам primary keyword.'
                ),
                'ui_action' => 'open_keyword_discovery',
            );
        }

        $keyword_data = $this->get_keyword_discovery_entity_keyword_data($kind, $entity_id);
        $current_main = trim((string) ($keyword_data['keyword'] ?? $keyword_data['aysa_keyword'] ?? ''));
        $secondary = $this->parse_keyword_discovery_secondary_keywords((string) ($keyword_data['second_keyword'] ?? $keyword_data['aysa_secondary_keywords'] ?? ''));

        $secondary = array_values(array_filter($secondary, function($item) use ($keyword) {
            return strtolower(trim((string) $item)) !== strtolower($keyword);
        }));

        if ($current_main !== '' && strtolower($current_main) !== strtolower($keyword)) {
            $secondary[] = $current_main;
        }

        $secondary = $this->parse_keyword_discovery_secondary_keywords(implode(', ', $secondary));
        $secondary_string = implode(', ', $secondary);

        $keyword_data['keyword'] = $keyword;
        $keyword_data['aysa_keyword'] = $keyword;
        $keyword_data['second_keyword'] = $secondary_string;
        $keyword_data['aysa_secondary_keywords'] = $secondary_string;
        $this->update_keyword_discovery_entity_keyword_data($kind, $entity_id, $keyword_data);

        return array(
            'module' => 'keyword_research',
            'assistant_message' => match ($language) {
                'ro' => 'Am setat "' . $keyword . '" ca keyword principal pentru "' . $label . '". Restul raman keyworduri de suport pe aceeasi entitate.',
                'bg' => 'Зададох "' . $keyword . '" като primary keyword за "' . $label . '". Останалите остават supporting keywords за същата entitate.',
                default => 'I set "' . $keyword . '" as the primary keyword for "' . $label . '". The rest stay as supporting keywords on the same entity.',
            },
            'result' => array(
                'keyword' => $keyword,
                'entity' => $entity,
                'keyword_data' => $keyword_data,
            ),
            'ui_action' => 'open_keyword_discovery',
        );
    }

    private function aysa_adjustments_execute_keyword_supporting_add($project_id, $payload, $language) {
        $keyword = trim((string) ($payload['keyword'] ?? ''));
        $entity = $this->resolve_keyword_discovery_entity_from_payload(
            (string) ($payload['entity_type'] ?? ''),
            intval($payload['entity_id'] ?? 0),
            (string) ($payload['entity_label'] ?? '')
        );
        if ($keyword === '' || !is_array($entity)) {
            return array(
                'module' => 'keyword_research',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I need both the exact keyword and the target entity before I can add a supporting keyword.',
                    'Am nevoie atat de keywordul exact, cat si de entitatea tinta inainte sa pot adauga un keyword de suport.',
                    'Трябват ми и точният keyword, и целевата entitate, преди да мога да добавя supporting keyword.'
                ),
                'ui_action' => 'open_keyword_discovery',
            );
        }

        $kind = (string) ($entity['kind'] ?? '');
        $entity_id = intval($entity['id'] ?? 0);
        $label = trim((string) ($entity['label'] ?? ''));
        if (($kind !== 'post' && $kind !== 'term') || $entity_id <= 0) {
            return array(
                'module' => 'keyword_research',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I could not resolve the entity well enough to add the supporting keyword.',
                    'Nu am reusit sa rezolv suficient de bine entitatea ca sa adaug keywordul de suport.',
                    'Не успях да разпозная достатъчно добре entitate-та, за да добавя supporting keyword.'
                ),
                'ui_action' => 'open_keyword_discovery',
            );
        }

        $keyword_data = $this->get_keyword_discovery_entity_keyword_data($kind, $entity_id);
        $current_main = trim((string) ($keyword_data['keyword'] ?? $keyword_data['aysa_keyword'] ?? ''));
        $secondary = $this->parse_keyword_discovery_secondary_keywords((string) ($keyword_data['second_keyword'] ?? $keyword_data['aysa_secondary_keywords'] ?? ''));

        if ($current_main === '') {
            $keyword_data['keyword'] = $keyword;
            $keyword_data['aysa_keyword'] = $keyword;
        } elseif (strtolower($current_main) !== strtolower($keyword)) {
            $secondary[] = $keyword;
        }

        $secondary = $this->parse_keyword_discovery_secondary_keywords(implode(', ', $secondary));
        $secondary_string = implode(', ', $secondary);
        $keyword_data['second_keyword'] = $secondary_string;
        $keyword_data['aysa_secondary_keywords'] = $secondary_string;
        $this->update_keyword_discovery_entity_keyword_data($kind, $entity_id, $keyword_data);

        return array(
            'module' => 'keyword_research',
            'assistant_message' => match ($language) {
                'ro' => 'Am adaugat "' . $keyword . '" ca keyword de suport pentru "' . $label . '".',
                'bg' => 'Добавих "' . $keyword . '" като supporting keyword за "' . $label . '".',
                default => 'I added "' . $keyword . '" as a supporting keyword for "' . $label . '".',
            },
            'result' => array(
                'keyword' => $keyword,
                'entity' => $entity,
                'keyword_data' => $keyword_data,
            ),
            'ui_action' => 'open_keyword_discovery',
        );
    }

    private function aysa_adjustments_execute_keyword_cluster_map_to_entity($project_id, $payload, $language) {
        $cluster_keywords = isset($payload['cluster_keywords']) && is_array($payload['cluster_keywords'])
            ? array_values(array_filter(array_map('strval', $payload['cluster_keywords'])))
            : array();
        $cluster_keywords = array_values(array_unique(array_filter(array_map('trim', $cluster_keywords))));
        $entity_type = (string) ($payload['entity_type'] ?? '');
        $entity_id = intval($payload['entity_id'] ?? 0);
        $entity_label = (string) ($payload['entity_label'] ?? '');

        if (count($cluster_keywords) < 2) {
            return array(
                'module' => 'keyword_research',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I need at least two keywords in the cluster before I can map the cluster to one entity.',
                    'Am nevoie de cel putin doua keyworduri in cluster inainte sa pot mapa clusterul pe o singura entitate.',
                    'Трябват ми поне два keyword-а в cluster-а, преди да мога да го свържа с една entitate.'
                ),
                'ui_action' => 'open_keyword_discovery',
            );
        }

        $created = 0;
        $failed = 0;
        foreach ($cluster_keywords as $keyword) {
            $result = $this->aysa_adjustments_execute_keyword_association_upsert($project_id, array(
                'keyword' => $keyword,
                'entity_type' => $entity_type,
                'entity_id' => $entity_id,
                'entity_label' => $entity_label,
            ), $language);
            if (!empty($result['result'])) {
                $created++;
            } else {
                $failed++;
            }
        }

        return array(
            'module' => 'keyword_research',
            'assistant_message' => match ($language) {
                'ro' => 'Am mapat clusterul pe entitatea aleasa. Keyworduri procesate: ' . $created . ', esecuri: ' . $failed . '. Dupa asta putem stabili si keywordul principal plus termenii de suport.',
                'bg' => 'Свързах cluster-а към избраната entitate. Обработени keyword-и: ' . $created . ', неуспехи: ' . $failed . '. След това можем да зададем и primary keyword плюс supporting terms.',
                default => 'I mapped the cluster to the chosen entity. Processed keywords: ' . $created . ', failures: ' . $failed . '. After that we can also set the primary keyword and the supporting terms.',
            },
            'result' => array(
                'cluster_keywords' => $cluster_keywords,
                'processed' => $created,
                'failed' => $failed,
            ),
            'ui_action' => 'open_keyword_discovery',
        );
    }

    private function aysa_adjustments_execute_keyword_tracking_update($project_id, $payload, $language) {
        $keywords = isset($payload['keywords']) && is_array($payload['keywords'])
            ? array_values(array_filter(array_map('strval', $payload['keywords'])))
            : array();
        $keywords = array_values(array_unique(array_filter(array_map('trim', $keywords))));
        $tracking_mode = trim((string) ($payload['tracking_mode'] ?? 'balanced'));
        $target_position = intval($payload['target_position'] ?? 0);

        if ($keywords === []) {
            return array(
                'module' => 'keyword_research',
                'assistant_message' => $this->aysa_adjustments_tr(
                    $language,
                    'I need at least one keyword before I can save the tracking plan.',
                    'Am nevoie de cel putin un keyword inainte sa pot salva planul de tracking.',
                    'Трябва ми поне един keyword, преди да мога да запазя tracking плана.'
                ),
                'ui_action' => 'open_keyword_discovery',
            );
        }

        $mode = in_array($tracking_mode, array('conservative', 'balanced', 'aggressive'), true)
            ? $tracking_mode
            : 'balanced';
        $cost_band = match ($mode) {
            'aggressive' => 'high',
            'conservative' => 'low',
            default => 'medium',
        };
        $refresh_frequency = match ($mode) {
            'aggressive' => 'weekly',
            'conservative' => 'monthly',
            default => 'biweekly',
        };
        $content_intensity = match ($mode) {
            'aggressive' => 'high',
            'conservative' => 'low',
            default => 'medium',
        };

        $plan = array(
            'project_id' => intval($project_id),
            'updated_at' => current_time('mysql'),
            'tracking_mode' => $mode,
            'target_position' => $target_position > 0 ? $target_position : null,
            'refresh_frequency' => $refresh_frequency,
            'content_intensity' => $content_intensity,
            'cost_band' => $cost_band,
            'keywords' => array_map(function($keyword) use ($mode, $target_position, $refresh_frequency, $content_intensity, $cost_band) {
                return array(
                    'keyword' => $keyword,
                    'tracking_mode' => $mode,
                    'target_position' => $target_position > 0 ? $target_position : null,
                    'refresh_frequency' => $refresh_frequency,
                    'content_intensity' => $content_intensity,
                    'cost_band' => $cost_band,
                );
            }, $keywords),
        );
        $this->aysa_save_keyword_tracking_plan($project_id, $plan);

        return array(
            'module' => 'keyword_research',
            'assistant_message' => match ($language) {
                'ro' => 'Am salvat planul de tracking pentru ' . count($keywords) . ' keyworduri in modul `' . $mode . '`. Asta seteaza si discutia despre refresh, continut si cost in jurul acestor keyworduri.',
                'bg' => 'Запазих tracking plan-а за ' . count($keywords) . ' keyword-а в режим `' . $mode . '`. Това задава и разговора за refresh, съдържание и разход около тези keyword-и.',
                default => 'I saved the tracking plan for ' . count($keywords) . ' keywords in `' . $mode . '` mode. That now sets the refresh, content, and cost discussion around those keywords.',
            },
            'result' => $plan,
            'ui_action' => 'open_keyword_discovery',
        );
    }

    private function get_keyword_discovery_entity_meta_sync_option_key($project_id) {
        return 'aysa_keyword_discovery_entity_meta_synced_' . intval($project_id);
    }

    private function is_keyword_discovery_entity_meta_synced($project_id) {
        return !empty(get_option($this->get_keyword_discovery_entity_meta_sync_option_key($project_id), false));
    }

    private function mark_keyword_discovery_entity_meta_synced($project_id) {
        update_option($this->get_keyword_discovery_entity_meta_sync_option_key($project_id), 1, false);
    }

    private function build_keyword_discovery_stable_local_id($keyword, $associated_on, $id_intern) {
        $signature = strtolower(trim((string) $keyword)) . '|' .
            strtolower(trim((string) $associated_on)) . '|' .
            intval($id_intern);
        $hash = sprintf('%u', crc32($signature));
        return intval($hash);
    }

    private function merge_keyword_discovery_associations($primary_rows, $secondary_rows) {
        $merged = array();
        $seen = array();
        $append = function($rows) use (&$merged, &$seen) {
            if (!is_array($rows)) {
                return;
            }
            foreach ($rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $keyword = strtolower(trim((string) ($row['keyword'] ?? '')));
                $associated_on = strtolower(trim((string) ($row['associatedOn'] ?? $row['associated_on'] ?? '')));
                $id_intern = intval($row['idIntern'] ?? $row['id_intern'] ?? 0);
                if ($keyword === '' || $associated_on === '' || $id_intern <= 0) {
                    continue;
                }
                $sig = $keyword . '|' . $associated_on . '|' . $id_intern;
                if (isset($seen[$sig])) {
                    continue;
                }
                $seen[$sig] = true;
                $merged[] = $row;
            }
        };
        $append($primary_rows);
        $append($secondary_rows);
        return $merged;
    }

    public function handle_keyword_discovery_get_keywords_and_competitors() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }

        try {
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $raw = $auth_manager->make_authenticated_request(
                'api/KeywordsAndCompetitors/ByProject/' . rawurlencode((string) $project_id),
                'GET'
            );

            $payload = is_array($raw) ? $raw : array();
            if (isset($payload['data']) && is_array($payload['data'])) {
                $payload = $payload['data'];
            }

            $keywords = array();
            if (isset($payload['keywords']) && is_array($payload['keywords'])) {
                $keywords = array_values($payload['keywords']);
            } elseif (isset($payload['Keywords']) && is_array($payload['Keywords'])) {
                $keywords = array_values($payload['Keywords']);
            }

            // Fallback: when remote target keywords are empty, use locally saved seed keywords
            // from Setup SEO Profile > Competition step so users always see planning anchors.
            if (empty($keywords)) {
                $seed_data = $this->aysa_keyword_strategy_extract_seed_data(get_current_user_id());
                if (is_array($seed_data) && !empty($seed_data['seed_keywords']) && is_array($seed_data['seed_keywords'])) {
                    $keywords = array_values(array_filter(array_map(function($kw) {
                        return trim((string) $kw);
                    }, $seed_data['seed_keywords']), function($kw) {
                        return $kw !== '';
                    }));
                }
            }

            // Secondary fallback: if current user has no saved competition data (common when
            // switching admin accounts), reuse the most recent non-empty competition option.
            if (empty($keywords)) {
                global $wpdb;
                $like = $wpdb->esc_like('aysa_wizard_competition_') . '%';
                $rows = $wpdb->get_results(
                    $wpdb->prepare(
                        "SELECT option_value FROM {$wpdb->options} WHERE option_name LIKE %s ORDER BY option_id DESC LIMIT 25",
                        $like
                    ),
                    ARRAY_A
                );

                if (is_array($rows)) {
                    foreach ($rows as $row) {
                        $raw_option = isset($row['option_value']) ? maybe_unserialize($row['option_value']) : array();
                        if (!is_array($raw_option) || empty($raw_option)) {
                            continue;
                        }

                        $candidate = array();
                        foreach ($raw_option as $k => $v) {
                            if (!preg_match('/^keyword\d+$/', (string) $k)) {
                                continue;
                            }
                            $kw = trim((string) $v);
                            if ($kw !== '') {
                                $candidate[] = $kw;
                            }
                        }

                        $candidate = array_values(array_unique($candidate));
                        if (!empty($candidate)) {
                            $keywords = $candidate;
                            break;
                        }
                    }
                }
            }

            wp_send_json_success(array(
                'project_id' => $project_id,
                'payload' => $payload,
                'keywords' => $keywords,
            ));
        } catch (Exception $e) {
            wp_send_json_error('Failed to fetch keywords and competitors: ' . $e->getMessage());
        }
    }

    private function aysa_research_async_supported_tasks() {
        return array(
            'competitor_gap',
            'competitor_identification',
            'keyword_strategy_generate',
        );
    }

    private function aysa_research_async_status_key($project_id, $task) {
        return 'aysa_research_async_' . sanitize_key((string) $task) . '_' . intval($project_id);
    }

    private function aysa_research_async_get_status($project_id, $task) {
        $raw = get_option($this->aysa_research_async_status_key($project_id, $task), array());
        return is_array($raw) ? $raw : array();
    }

    private function aysa_research_async_save_status($project_id, $task, $status) {
        update_option($this->aysa_research_async_status_key($project_id, $task), is_array($status) ? $status : array(), false);
    }

    private function aysa_research_async_schedule_event($payload, $delay_seconds = 1) {
        if (!is_array($payload)) {
            return;
        }
        $timestamp = time() + max(1, intval($delay_seconds));
        wp_schedule_single_event($timestamp, 'aysa_research_async_event', array($payload));
        if (function_exists('spawn_cron')) {
            spawn_cron();
        }
    }

    private function aysa_research_async_prepare_competitor_gap_request($input) {
        $project_id = intval($input['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            return new WP_Error('invalid_project', 'Invalid project id');
        }

        $force_refresh = !empty($input['force_refresh']) && intval($input['force_refresh']) === 1;
        $target = sanitize_text_field((string) ($input['target'] ?? ''));
        if ($target === '') {
            $target = (string) wp_parse_url(home_url('/'), PHP_URL_HOST);
        }
        $location_name = sanitize_text_field((string) ($input['location_name'] ?? 'Romania'));
        $language_name = sanitize_text_field((string) ($input['language_name'] ?? 'Romanian'));
        $limit = max(1, min(2000, intval($input['limit'] ?? 200)));

        $competitors_raw = $input['competitors'] ?? array();
        if (is_string($competitors_raw)) {
            $decoded = json_decode(wp_unslash($competitors_raw), true);
            $competitors_raw = is_array($decoded) ? $decoded : array();
        } elseif (is_array($competitors_raw)) {
            $competitors_raw = array_map('wp_unslash', $competitors_raw);
        } else {
            $competitors_raw = array();
        }

        $competitors = array();
        foreach ($competitors_raw as $competitor) {
            $value = trim(sanitize_text_field((string) $competitor));
            if ($value !== '') {
                $competitors[] = $value;
            }
        }
        $competitors = array_values(array_unique($competitors));

        $context = $this->aysa_monitoring_resolve_context($target);
        $resolved_target = trim((string) ($context['target'] ?? ''));
        if ($resolved_target === '') {
            $resolved_target = $target;
        }

        $normalized_competitors = array();
        foreach ($competitors as $competitor) {
            $host = $this->aysa_monitoring_extract_host((string) $competitor);
            if ($host === '' || $this->aysa_monitoring_is_local_host($host)) {
                if ($resolved_target !== '') {
                    $host = $resolved_target;
                }
            }
            if ($host !== '') {
                $normalized_competitors[] = $host;
            }
        }
        $normalized_competitors = array_values(array_unique($normalized_competitors));
        if (empty($normalized_competitors) && $resolved_target !== '') {
            $normalized_competitors[] = $resolved_target;
        }

        $gap_payload = array(
            'project_id' => $project_id,
            'target' => $resolved_target,
            'location_code' => intval($context['location_code'] ?? 2840),
            'language_code' => (string) ($context['language_code'] ?? 'ro'),
            'limit' => $limit,
            'competitors' => $normalized_competitors,
        );

        return array(
            'task' => 'competitor_gap',
            'project_id' => $project_id,
            'force_refresh' => $force_refresh,
            'target' => $target,
            'location_name' => $location_name,
            'language_name' => $language_name,
            'limit' => $limit,
            'competitors' => $competitors,
            'gap_payload' => $gap_payload,
            'request_hash' => md5(wp_json_encode($gap_payload)),
        );
    }

    private function aysa_research_async_get_competitor_gap_cached_result($request) {
        if (!is_array($request) || !empty($request['force_refresh'])) {
            return null;
        }
        $project_id = intval($request['project_id'] ?? 0);
        $gap_payload = isset($request['gap_payload']) && is_array($request['gap_payload']) ? $request['gap_payload'] : array();
        $cached = $this->aysa_competitor_gap_get_cached_payload($project_id);
        $cached_request = is_array($cached) && isset($cached['request']) && is_array($cached['request']) ? $cached['request'] : array();
        if (
            is_array($cached) &&
            !empty($cached['data']) &&
            is_array($cached['data']) &&
            $this->aysa_competitor_gap_requests_match($cached_request, $gap_payload)
        ) {
            $cached_data = $cached['data'];
            $cached_data['cached'] = true;
            $cached_data['cached_at'] = intval($cached['cached_at'] ?? 0);
            $cached_data['inKeywordResearch'] = $this->aysa_keyword_discovery_existing_keywords_map(
                $project_id,
                isset($cached_data['rankedKeywords']) && is_array($cached_data['rankedKeywords']) ? $cached_data['rankedKeywords'] : array()
            );
            return $cached_data;
        }

        return null;
    }

    private function aysa_research_async_execute_competitor_gap($request) {
        if (!is_array($request)) {
            return new WP_Error('invalid_request', 'Invalid competitor gap request.');
        }

        $project_id = intval($request['project_id'] ?? 0);
        $target = (string) ($request['target'] ?? '');
        $competitors = isset($request['competitors']) && is_array($request['competitors']) ? array_values($request['competitors']) : array();
        $gap_payload = isset($request['gap_payload']) && is_array($request['gap_payload']) ? $request['gap_payload'] : array();

        $cached = $this->aysa_research_async_get_competitor_gap_cached_result($request);
        if (is_array($cached)) {
            return $cached;
        }

        $broker_paths = array(
            '/v1/dataforseo/keywords/gap',
            '/v1/keywords/gap',
        );

        foreach ($broker_paths as $broker_path) {
            $broker_result = $this->aysa_broker_request('POST', $broker_path, $gap_payload, 180);
            if (is_wp_error($broker_result)) {
                continue;
            }

            if (is_array($broker_result) && !empty($broker_result['ok']) && isset($broker_result['rankedKeywords'])) {
                $this->aysa_competitor_gap_save_cached_payload($project_id, $gap_payload, $broker_result);
                $broker_result['cached'] = false;
                $broker_result['cached_at'] = time();
                $broker_result['inKeywordResearch'] = $this->aysa_keyword_discovery_existing_keywords_map(
                    $project_id,
                    isset($broker_result['rankedKeywords']) && is_array($broker_result['rankedKeywords']) ? $broker_result['rankedKeywords'] : array()
                );
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_competitor_gap_keywords_gap',
                    'competitor_gap_keywords_gap',
                    array(
                        'module' => 'competitor_gap',
                        'source' => 'broker',
                        'broker_path' => $broker_path,
                        'target' => $target,
                        'competitors_count' => count($competitors),
                    ),
                    35,
                    (string) $broker_path
                );
                return $broker_result;
            }

            if (is_array($broker_result) && isset($broker_result['data']) && is_array($broker_result['data']) && isset($broker_result['data']['rankedKeywords'])) {
                $data = $broker_result['data'];
                $this->aysa_competitor_gap_save_cached_payload($project_id, $gap_payload, $data);
                $data['cached'] = false;
                $data['cached_at'] = time();
                $data['inKeywordResearch'] = $this->aysa_keyword_discovery_existing_keywords_map(
                    $project_id,
                    isset($data['rankedKeywords']) && is_array($data['rankedKeywords']) ? $data['rankedKeywords'] : array()
                );
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_competitor_gap_keywords_gap',
                    'competitor_gap_keywords_gap',
                    array(
                        'module' => 'competitor_gap',
                        'source' => 'broker_data',
                        'broker_path' => $broker_path,
                        'target' => $target,
                        'competitors_count' => count($competitors),
                    ),
                    35,
                    (string) $broker_path
                );
                return $data;
            }
        }

        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }

        $query = array(
            'target' => $target,
            'locationName' => (string) ($request['location_name'] ?? 'Romania'),
            'languageName' => (string) ($request['language_name'] ?? 'Romanian'),
            'ProjectId' => $project_id,
            'limit' => intval($request['limit'] ?? 200),
        );
        if (!empty($competitors)) {
            $query['competitors'] = $competitors;
        }

        try {
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $endpoint = 'api/keywordsgap/keywords-gap?' . http_build_query($query);
            $raw = $auth_manager->make_authenticated_request($endpoint, 'GET');
            if ($raw === false) {
                return new WP_Error('empty_response', 'Keywords gap endpoint returned empty response.');
            }

            if (is_string($raw)) {
                $decoded = json_decode($raw, true);
                if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
                    $raw = $decoded;
                }
            }

            if (!is_array($raw)) {
                return new WP_Error('invalid_response', 'Invalid keywords gap response format.');
            }

            if (isset($raw['rankedKeywords']) && is_array($raw['rankedKeywords'])) {
                $this->aysa_competitor_gap_save_cached_payload($project_id, $gap_payload, $raw);
                $raw['cached'] = false;
                $raw['cached_at'] = time();
                $raw['inKeywordResearch'] = $this->aysa_keyword_discovery_existing_keywords_map(
                    $project_id,
                    $raw['rankedKeywords']
                );
            }
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_competitor_gap_keywords_gap',
                'competitor_gap_keywords_gap',
                array(
                    'module' => 'competitor_gap',
                    'source' => 'legacy_remote',
                    'target' => $target,
                    'competitors_count' => count($competitors),
                ),
                35,
                '/v1/keywords/gap'
            );

            return $raw;
        } catch (Exception $e) {
            return new WP_Error('legacy_gap_failed', 'Failed to fetch keywords gap data: ' . $e->getMessage());
        }
    }

    private function aysa_research_async_prepare_competitor_identification_request($input) {
        $project_id = intval($input['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            return new WP_Error('invalid_project', 'Invalid project id');
        }

        $force_refresh = !empty($input['force_refresh']) && intval($input['force_refresh']) === 1;
        $top_n = intval($input['top_n'] ?? 10);
        if ($top_n <= 0) {
            $top_n = 10;
        }
        $top_n = min($top_n, 20);

        $seed_user_id = intval($input['seed_user_id'] ?? get_current_user_id());
        $seed_data = $this->aysa_keyword_strategy_extract_seed_data($seed_user_id);
        $seed_keywords = isset($seed_data['seed_keywords']) && is_array($seed_data['seed_keywords']) ? array_values($seed_data['seed_keywords']) : array();
        if (empty($seed_keywords)) {
            $shared_user_id = intval(get_option('aysa_user_id', 0));
            if ($shared_user_id > 0 && $shared_user_id !== $seed_user_id) {
                $shared_seed_data = $this->aysa_keyword_strategy_extract_seed_data($shared_user_id);
                $shared_seed_keywords = isset($shared_seed_data['seed_keywords']) && is_array($shared_seed_data['seed_keywords'])
                    ? array_values($shared_seed_data['seed_keywords'])
                    : array();
                if (!empty($shared_seed_keywords)) {
                    $seed_keywords = $shared_seed_keywords;
                }
            }
        }
        $seed_keywords = array_values(array_filter(array_map(function($kw) {
            return trim((string) $kw);
        }, $seed_keywords), function($kw) {
            return $kw !== '';
        }));

        $configured_competitors = isset($seed_data['competitors']) && is_array($seed_data['competitors']) ? array_values($seed_data['competitors']) : array();
        if (empty($configured_competitors)) {
            $shared_user_id = intval(get_option('aysa_user_id', 0));
            if ($shared_user_id > 0 && $shared_user_id !== $seed_user_id) {
                $shared_seed_data = $this->aysa_keyword_strategy_extract_seed_data($shared_user_id);
                $shared_competitors = isset($shared_seed_data['competitors']) && is_array($shared_seed_data['competitors'])
                    ? array_values($shared_seed_data['competitors'])
                    : array();
                if (!empty($shared_competitors)) {
                    $configured_competitors = $shared_competitors;
                }
            }
        }
        $configured_competitors = array_values(array_unique(array_filter(array_map(function($domain) {
            return $this->aysa_monitoring_extract_host((string) $domain);
        }, $configured_competitors), function($domain) {
            return $domain !== '';
        })));

        if (empty($seed_keywords)) {
            return new WP_Error('missing_seeds', 'No seed keywords found in Setup SEO Profile.');
        }

        $home_host = $this->aysa_monitoring_extract_host((string) home_url('/'));
        $resolved_context = $this->aysa_monitoring_resolve_context($home_host !== '' ? $home_host : 'florariebragadiru.ro');
        $location_code = intval($resolved_context['location_code'] ?? 2840);
        if ($location_code <= 0) {
            $location_code = 2840;
        }
        $language_code = strtolower(trim((string) ($resolved_context['language_code'] ?? 'ro')));
        if ($language_code === '') {
            $language_code = 'ro';
        }
        $target_host = trim((string) ($resolved_context['target'] ?? ''));
        $own_domains = array();
        if ($home_host !== '') {
            $own_domains[] = strtolower($home_host);
        }
        if ($target_host !== '') {
            $own_domains[] = strtolower($target_host);
        }
        $own_domains = array_values(array_unique(array_filter($own_domains)));

        $request_payload = array(
            'project_id' => $project_id,
            'seed_keywords' => array_values($seed_keywords),
            'active_competitors' => array_values($configured_competitors),
            'location_code' => $location_code,
            'language_code' => $language_code,
            'top_n' => $top_n,
            'own_domains' => $own_domains,
        );

        return array(
            'task' => 'competitor_identification',
            'project_id' => $project_id,
            'force_refresh' => $force_refresh,
            'top_n' => $top_n,
            'seed_keywords' => $seed_keywords,
            'request_payload' => $request_payload,
            'request_hash' => md5(wp_json_encode($request_payload)),
        );
    }

    private function aysa_research_async_get_competitor_identification_cached_result($request) {
        if (!is_array($request) || !empty($request['force_refresh'])) {
            return null;
        }
        $project_id = intval($request['project_id'] ?? 0);
        $request_payload = isset($request['request_payload']) && is_array($request['request_payload']) ? $request['request_payload'] : array();
        $cached = $this->aysa_competitor_identification_get_cached_payload($project_id);
        if (is_array($cached) && !empty($cached['data']) && is_array($cached['data'])) {
            $cached_request = isset($cached['request']) && is_array($cached['request']) ? $cached['request'] : array();
            $same_seeds = isset($cached_request['seed_keywords']) && is_array($cached_request['seed_keywords']) ? array_map('strtolower', array_values($cached_request['seed_keywords'])) : array();
            $current_seeds = isset($request_payload['seed_keywords']) && is_array($request_payload['seed_keywords']) ? array_map('strtolower', array_values($request_payload['seed_keywords'])) : array();
            sort($same_seeds);
            sort($current_seeds);
            if ($same_seeds === $current_seeds) {
                $data = $cached['data'];
                $data['cached'] = true;
                $data['cached_at'] = intval($cached['cached_at'] ?? 0);
                return $data;
            }
        }

        return null;
    }

    private function aysa_research_async_execute_competitor_identification($request) {
        if (!is_array($request)) {
            return new WP_Error('invalid_request', 'Invalid competitor identification request.');
        }

        $project_id = intval($request['project_id'] ?? 0);
        $seed_keywords = isset($request['seed_keywords']) && is_array($request['seed_keywords']) ? array_values($request['seed_keywords']) : array();
        $top_n = intval($request['top_n'] ?? 10);
        $request_payload = isset($request['request_payload']) && is_array($request['request_payload']) ? $request['request_payload'] : array();

        $cached = $this->aysa_research_async_get_competitor_identification_cached_result($request);
        if (is_array($cached)) {
            return $cached;
        }

        $last_error = '';
        $broker_result = $this->aysa_broker_request('POST', '/v1/competitors/identify/from-seeds', $request_payload, 180);
        if (is_wp_error($broker_result)) {
            $last_error = $broker_result->get_error_message();
        } else {
            $data = isset($broker_result['data']) && is_array($broker_result['data']) ? $broker_result['data'] : $broker_result;
            if (is_array($data) && !empty($data['error'])) {
                $last_error = (string) $data['error'];
            }
            if (is_array($data) && !empty($data['ok']) && isset($data['unique_competitors']) && is_array($data['unique_competitors'])) {
                $data = $this->aysa_keyword_v2_finalize_snapshot_contract($project_id, 'competitor_identification_v2', $data, 'unique_competitors');
                $this->aysa_competitor_identification_save_cached_payload($project_id, $request_payload, $data);
                $data['cached'] = false;
                $data['cached_at'] = time();
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_competitor_identification_seed_competitors',
                    'competitor_identification_seed_competitors',
                    array(
                        'module' => 'competitor_identification',
                        'source' => 'broker',
                        'broker_path' => '/v1/competitors/identify/from-seeds',
                        'seed_keywords_count' => count($seed_keywords),
                        'top_n' => $top_n,
                    ),
                    35,
                    '/v1/competitors/identify/from-seeds'
                );
                return $data;
            }
        }

        $cached = $this->aysa_competitor_identification_get_cached_payload($project_id);
        if (is_array($cached) && !empty($cached['data']) && is_array($cached['data'])) {
            $data = $cached['data'];
            $data['cached'] = true;
            $data['cached_at'] = intval($cached['cached_at'] ?? 0);
            return $data;
        }

        $message = 'Could not identify competitors from seed keywords.';
        if ($last_error !== '') {
            $message .= ' ' . $last_error;
        }
        return new WP_Error('competitor_identification_failed', $message);
    }

    private function aysa_research_async_prepare_keyword_strategy_request($input) {
        if (!$this->check_authentication(false)) {
            return new WP_Error('auth_required', 'Authentication required');
        }
        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }
        $auth_manager = Aysa_Auth_Manager::get_instance();
        $auth_status = $auth_manager->is_authenticated();
        $user_id = intval($auth_status['aysa_user_id'] ?? 0);
        if ($user_id <= 0) {
            return new WP_Error('invalid_auth_context', 'Invalid authentication context');
        }

        $project_id = intval($input['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            return new WP_Error('invalid_project', 'Invalid project id');
        }

        $seed_data = $this->aysa_keyword_strategy_extract_seed_data($user_id);
        $seed_keywords = isset($seed_data['seed_keywords']) && is_array($seed_data['seed_keywords']) ? $seed_data['seed_keywords'] : array();
        $competitors = isset($seed_data['competitors']) && is_array($seed_data['competitors']) ? $seed_data['competitors'] : array();
        if (empty($seed_keywords)) {
            return new WP_Error('missing_seeds', 'No seed keywords found in Setup SEO Profile > Competition.');
        }

        $context = $this->aysa_keyword_strategy_get_context($user_id);
        $broker_payload = array(
            'project_id' => $project_id,
            'seed_keywords' => $seed_keywords,
            'competitors' => $competitors,
            'targeted_country' => (string) ($context['targeted_country'] ?? ''),
            'targeted_region' => (string) ($context['targeted_region'] ?? ''),
            'targeted_city' => (string) ($context['targeted_city'] ?? ''),
            'primary_language' => (string) ($context['primary_language'] ?? ''),
            'website_type' => (string) ($context['website_type'] ?? ''),
            'industry' => (string) ($context['industry'] ?? ''),
            'max_keywords' => 250,
        );

        return array(
            'task' => 'keyword_strategy_generate',
            'project_id' => $project_id,
            'user_id' => $user_id,
            'seed_keywords' => array_values($seed_keywords),
            'competitors' => array_values($competitors),
            'context' => $context,
            'broker_payload' => $broker_payload,
            'request_hash' => md5(wp_json_encode($broker_payload)),
        );
    }

    private function aysa_research_async_execute_keyword_strategy($request) {
        if (!is_array($request)) {
            return new WP_Error('invalid_request', 'Invalid keyword strategy request.');
        }

        $project_id = intval($request['project_id'] ?? 0);
        $user_id = intval($request['user_id'] ?? 0);
        $seed_keywords = isset($request['seed_keywords']) && is_array($request['seed_keywords']) ? $request['seed_keywords'] : array();
        $competitors = isset($request['competitors']) && is_array($request['competitors']) ? $request['competitors'] : array();
        $context = isset($request['context']) && is_array($request['context']) ? $request['context'] : array();
        $broker_payload = isset($request['broker_payload']) && is_array($request['broker_payload']) ? $request['broker_payload'] : array();

        $strategy = array();
        $used_remote = false;

        $remote_result = $this->aysa_broker_request('POST', '/v1/keywords/strategy/from-seeds', $broker_payload, 120);
        if (!is_wp_error($remote_result) && is_array($remote_result)) {
            $remote_data = isset($remote_result['data']) && is_array($remote_result['data']) ? $remote_result['data'] : $remote_result;
            $remote_items = isset($remote_data['items']) && is_array($remote_data['items']) ? $remote_data['items'] : array();
            if (!empty($remote_items)) {
                $strategy = array(
                    'project_id' => $project_id,
                    'user_id' => $user_id,
                    'generated_at' => current_time('mysql'),
                    'source' => 'broker',
                    'seed_keywords' => array_values($seed_keywords),
                    'competitors' => array_values($competitors),
                    'context' => $context,
                    'summary' => isset($remote_data['summary']) && is_array($remote_data['summary']) ? $remote_data['summary'] : array(),
                    'items' => array_values($remote_items),
                );
                $used_remote = true;
            }
        }

        if (empty($strategy)) {
            $strategy = $this->aysa_keyword_strategy_build_local_plan($project_id, $user_id, $seed_keywords, $competitors, $context);
        }

        $option_key = $this->aysa_keyword_strategy_option_key($project_id, $user_id);
        update_option($option_key, $strategy, false);
        $this->aysa_adjustments_add_pending_approval(
            $project_id,
            'keyword_strategy_review',
            'Keyword strategy is ready for approval',
            'I finished the keyword strategy. I prepared the initial mapping, the opportunities, and the recommended directions. I need your approval before I continue into tracking, content, and execution.',
            'open_keyword_discovery'
        );

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_keyword_discovery_generate_strategy',
            'keyword_discovery_generate_strategy',
            array(
                'module' => 'keyword_discovery',
                'source' => $used_remote ? 'broker' : 'local_fallback',
                'seed_keywords_count' => count($seed_keywords),
                'competitors_count' => count($competitors),
            ),
            25,
            '/v1/keywords/strategy/generate'
        );

        return array(
            'project_id' => $project_id,
            'user_id' => $user_id,
            'strategy' => $strategy,
            'source' => $used_remote ? 'broker' : 'local_fallback',
        );
    }

    private function aysa_research_async_schedule_request($task, $request) {
        $task = sanitize_key((string) $task);
        if (!in_array($task, $this->aysa_research_async_supported_tasks(), true) || !is_array($request)) {
            return new WP_Error('unsupported_task', 'Unsupported research task.');
        }

        $project_id = intval($request['project_id'] ?? 0);
        $request_hash = (string) ($request['request_hash'] ?? '');
        if ($project_id <= 0 || $request_hash === '') {
            return new WP_Error('invalid_research_payload', 'Invalid research task payload.');
        }

        if ($task === 'competitor_gap') {
            $cached = $this->aysa_research_async_get_competitor_gap_cached_result($request);
            if (is_array($cached)) {
                return array(
                    'status' => 'completed',
                    'worker_ref' => '',
                    'worker_status' => 'completed',
                    'result_summary' => array(
                        'cached' => true,
                        'count' => count((array) ($cached['rankedKeywords'] ?? array())),
                    ),
                    'result' => $cached,
                    'started_at' => time(),
                );
            }
        } elseif ($task === 'competitor_identification') {
            $cached = $this->aysa_research_async_get_competitor_identification_cached_result($request);
            if (is_array($cached)) {
                return array(
                    'status' => 'completed',
                    'worker_ref' => '',
                    'worker_status' => 'completed',
                    'result_summary' => array(
                        'cached' => true,
                        'count' => count((array) ($cached['unique_competitors'] ?? array())),
                    ),
                    'result' => $cached,
                    'started_at' => time(),
                );
            }
        }

        $existing = $this->aysa_research_async_get_status($project_id, $task);
        if (
            is_array($existing) &&
            !empty($existing['run_id']) &&
            in_array((string) ($existing['status'] ?? ''), array('queued', 'running'), true) &&
            (string) ($existing['request_hash'] ?? '') === $request_hash
        ) {
            $run_id = (string) ($existing['run_id'] ?? '');
            return array(
                'status' => 'running',
                'worker_ref' => $this->aysa_project_jobs_make_worker_ref('research_async', $task, $run_id),
                'worker_status' => (string) ($existing['status'] ?? 'queued'),
                'result_summary' => array('deduped_worker' => true),
                'started_at' => intval($existing['started_at'] ?? time()),
            );
        }

        $run_id = uniqid('research_', true);
        $status = array(
            'task' => $task,
            'project_id' => $project_id,
            'run_id' => $run_id,
            'request_hash' => $request_hash,
            'status' => 'queued',
            'result' => null,
            'error' => '',
            'started_at' => time(),
            'updated_at' => time(),
            'finished_at' => 0,
        );
        $this->aysa_research_async_save_status($project_id, $task, $status);
        $this->aysa_research_async_schedule_event(array(
            'task' => $task,
            'project_id' => $project_id,
            'run_id' => $run_id,
            'request_hash' => $request_hash,
            'request' => $request,
        ), 1);

        return array(
            'status' => 'running',
            'worker_ref' => $this->aysa_project_jobs_make_worker_ref('research_async', $task, $run_id),
            'worker_status' => 'queued',
            'result_summary' => null,
            'started_at' => time(),
        );
    }

    private function aysa_monitoring_keywords_async_schedule_request($request) {
        if (!is_array($request)) {
            return new WP_Error('invalid_monitoring_keywords_request', 'Invalid monitoring keywords async request.');
        }

        $project_id = intval($request['project_id'] ?? 0);
        $request_hash = (string) ($request['request_hash'] ?? '');
        if ($project_id <= 0 || $request_hash === '') {
            return new WP_Error('invalid_monitoring_keywords_request', 'Invalid monitoring keywords async request.');
        }

        $existing = $this->aysa_monitoring_keywords_async_get_status($project_id);
        if (
            is_array($existing) &&
            !empty($existing['run_id']) &&
            in_array((string) ($existing['status'] ?? ''), array('queued', 'running'), true) &&
            (string) ($existing['request_hash'] ?? '') === $request_hash
        ) {
            return array(
                'status' => (string) ($existing['status'] ?? 'running'),
                'worker_ref' => $this->aysa_project_jobs_make_worker_ref('monitoring_keywords_async', 'keywords_overview_refresh', (string) ($existing['run_id'] ?? '')),
                'worker_status' => (string) ($existing['status'] ?? ''),
                'result_summary' => array('deduped_worker' => true),
                'started_at' => intval($existing['started_at'] ?? time()),
            );
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_monitoring_keywords_overview',
            'monitoring_keywords_overview',
            array(
                'module' => 'monitoring_analytics',
                'source' => 'manual_refresh_async',
            ),
            60,
            '/v1/monitoring/keywords/overview'
        );

        $run_id = uniqid('mon_kw_', true);
        $status = array(
            'project_id' => $project_id,
            'domain' => (string) ($request['domain'] ?? ''),
            'run_id' => $run_id,
            'request_hash' => $request_hash,
            'status' => 'queued',
            'result' => null,
            'error' => '',
            'started_at' => time(),
            'updated_at' => time(),
            'finished_at' => 0,
            '_request' => $request,
        );
        $this->aysa_monitoring_keywords_async_save_status($project_id, $status);

        wp_schedule_single_event(time() + 1, 'aysa_monitoring_keywords_async_event', array(array(
            'project_id' => $project_id,
            'run_id' => $run_id,
            'request_hash' => $request_hash,
            'request' => $request,
        )));
        if (function_exists('spawn_cron')) {
            spawn_cron();
        }

        return array(
            'status' => 'running',
            'worker_ref' => $this->aysa_project_jobs_make_worker_ref('monitoring_keywords_async', 'keywords_overview_refresh', $run_id),
            'worker_status' => 'queued',
            'result_summary' => null,
            'started_at' => time(),
        );
    }

    public function handle_research_async_start() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $task = sanitize_key((string) ($_POST['task'] ?? ''));
        if (!in_array($task, $this->aysa_research_async_supported_tasks(), true)) {
            wp_send_json_error('Unsupported research task.');
            return;
        }

        switch ($task) {
            case 'competitor_gap':
                $request = $this->aysa_research_async_prepare_competitor_gap_request($_POST);
                break;
            case 'competitor_identification':
                $request = $this->aysa_research_async_prepare_competitor_identification_request($_POST);
                break;
            case 'keyword_strategy_generate':
                $request = $this->aysa_research_async_prepare_keyword_strategy_request($_POST);
                break;
            default:
                $request = new WP_Error('unsupported_task', 'Unsupported research task.');
        }

        if (is_wp_error($request)) {
            wp_send_json_error($request->get_error_message());
            return;
        }

        $project_id = intval($request['project_id'] ?? 0);
        $request_hash = (string) ($request['request_hash'] ?? '');
        if ($project_id <= 0 || $request_hash === '') {
            wp_send_json_error('Invalid research task payload.');
            return;
        }

        $job = $this->aysa_project_jobs_enqueue(
            $project_id,
            'research',
            $task,
            array(
                'task' => $task,
                'request' => $request,
            ),
            array(
                'dedupe_key' => $request_hash,
                'input_summary' => array(
                    'task' => $task,
                    'request_hash' => $request_hash,
                ),
            )
        );
        if (is_wp_error($job)) {
            wp_send_json_error($job->get_error_message());
            return;
        }

        $job_row = isset($job['job']) && is_array($job['job']) ? $job['job'] : array();
        wp_send_json_success(array(
            'task' => $task,
            'project_id' => $project_id,
            'run_id' => (string) ($job_row['job_id'] ?? ''),
            'status' => (string) ($job_row['status'] ?? 'queued'),
            'result' => $job_row['result_payload'] ?? null,
            'error' => (string) ($job_row['error_summary'] ?? ''),
            'worker_ref' => (string) ($job_row['worker_ref'] ?? ''),
            'worker_status' => (string) ($job_row['worker_status'] ?? ''),
            'blocked_by' => array_values((array) ($job_row['blocked_by'] ?? array())),
            'project_job_rack' => isset($job['rack']) && is_array($job['rack']) ? $job['rack'] : array(),
        ));
    }

    public function handle_research_async_status() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $task = sanitize_key((string) ($_POST['task'] ?? ''));
        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0 || !in_array($task, $this->aysa_research_async_supported_tasks(), true)) {
            wp_send_json_error('Invalid research task status request.');
            return;
        }

        $status = $this->aysa_research_async_get_status($project_id, $task);
        if (empty($status)) {
            $job = $this->aysa_project_jobs_find_latest_job($project_id, 'research', $task, array('queued', 'running', 'completed', 'failed'));
            if (is_array($job)) {
                wp_send_json_success(array(
                    'task' => $task,
                    'project_id' => $project_id,
                    'run_id' => (string) ($job['job_id'] ?? ''),
                    'status' => (string) ($job['status'] ?? 'idle'),
                    'result' => $job['result_payload'] ?? null,
                    'error' => (string) ($job['error_summary'] ?? ''),
                    'worker_ref' => (string) ($job['worker_ref'] ?? ''),
                    'project_job_rack' => $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id)),
                ));
                return;
            }
            wp_send_json_success(array(
                'task' => $task,
                'project_id' => $project_id,
                'status' => 'idle',
                'result' => null,
                'error' => '',
            ));
            return;
        }
        $status['project_job_rack'] = $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id));
        wp_send_json_success($status);
    }

    public function run_research_async_event($payload) {
        if (!is_array($payload)) {
            return;
        }

        $task = sanitize_key((string) ($payload['task'] ?? ''));
        $project_id = intval($payload['project_id'] ?? 0);
        $run_id = (string) ($payload['run_id'] ?? '');
        $request_hash = (string) ($payload['request_hash'] ?? '');
        $request = isset($payload['request']) && is_array($payload['request']) ? $payload['request'] : array();
        if ($project_id <= 0 || $run_id === '' || $request_hash === '' || !in_array($task, $this->aysa_research_async_supported_tasks(), true)) {
            return;
        }

        $status = $this->aysa_research_async_get_status($project_id, $task);
        if (
            empty($status) ||
            (string) ($status['run_id'] ?? '') !== $run_id ||
            (string) ($status['request_hash'] ?? '') !== $request_hash
        ) {
            return;
        }

        $status['status'] = 'running';
        $status['updated_at'] = time();
        $status['error'] = '';
        $this->aysa_research_async_save_status($project_id, $task, $status);

        switch ($task) {
            case 'competitor_gap':
                $result = $this->aysa_research_async_execute_competitor_gap($request);
                break;
            case 'competitor_identification':
                $result = $this->aysa_research_async_execute_competitor_identification($request);
                break;
            case 'keyword_strategy_generate':
                $result = $this->aysa_research_async_execute_keyword_strategy($request);
                break;
            default:
                $result = new WP_Error('unsupported_task', 'Unsupported research task.');
        }

        $status['updated_at'] = time();
        $status['finished_at'] = time();
        if (is_wp_error($result)) {
            $status['status'] = 'failed';
            $status['result'] = null;
            $status['error'] = $result->get_error_message();
        } else {
            $status['status'] = 'completed';
            $status['result'] = $result;
            $status['error'] = '';
        }
        $this->aysa_research_async_save_status($project_id, $task, $status);
        $this->aysa_project_jobs_finalize(
            $project_id,
            $this->aysa_project_jobs_make_worker_ref('research_async', $task, $run_id),
            (string) ($status['status'] ?? 'failed'),
            !is_wp_error($result) && is_array($result) ? array(
                'task' => $task,
                'status' => (string) ($status['status'] ?? ''),
            ) : null,
            is_wp_error($result) ? $result->get_error_message() : ''
        );
    }

    private function aysa_offpage_async_supported_tasks() {
        return array(
            'publisher_draft_create',
            'publisher_draft_update',
            'publisher_draft_approve',
            'exchange_request_create',
            'exchange_activate_link',
            'exchange_request_respond',
            'exchange_refresh_verification',
            'exchange_change_state',
            'exchange_submit_publication',
        );
    }

    private function aysa_offpage_async_status_key($project_id, $task) {
        return 'aysa_offpage_async_' . sanitize_key((string) $task) . '_' . intval($project_id);
    }

    private function aysa_offpage_async_get_status($project_id, $task) {
        $raw = get_option($this->aysa_offpage_async_status_key($project_id, $task), array());
        return is_array($raw) ? $raw : array();
    }

    private function aysa_offpage_async_save_status($project_id, $task, $status) {
        update_option($this->aysa_offpage_async_status_key($project_id, $task), is_array($status) ? $status : array(), false);
    }

    private function aysa_offpage_async_schedule_event($payload, $delay_seconds = 1) {
        if (!is_array($payload)) {
            return;
        }
        $timestamp = time() + max(1, intval($delay_seconds));
        wp_schedule_single_event($timestamp, 'aysa_offpage_async_event', array($payload));
        if (function_exists('spawn_cron')) {
            spawn_cron();
        }
    }

    private function aysa_offpage_async_prepare_request($task, $input) {
        $project_id = $this->aysa_resolve_project_id_with_local_fallback(intval($input['project_id'] ?? get_option('aysa_project_id', 0)));

        switch ($task) {
            case 'publisher_draft_create':
                $order_ref = sanitize_text_field((string) ($input['order_ref'] ?? ''));
                if ($project_id <= 0 || $order_ref === '') {
                    return new WP_Error('invalid_request', 'Invalid project_id or order_ref');
                }
                return array(
                    'task' => $task,
                    'project_id' => $project_id,
                    'order_ref' => $order_ref,
                    'request_hash' => md5($project_id . '|' . $order_ref),
                );

            case 'publisher_draft_update':
                $draft_id = intval($input['draft_id'] ?? 0);
                if ($draft_id <= 0) {
                    return new WP_Error('invalid_request', 'Invalid draft_id');
                }
                return array(
                    'task' => $task,
                    'project_id' => $project_id,
                    'draft_id' => $draft_id,
                    'title' => sanitize_text_field((string) ($input['title'] ?? '')),
                    'excerpt' => sanitize_textarea_field((string) ($input['excerpt'] ?? '')),
                    'body_html' => wp_kses_post((string) ($input['body_html'] ?? '')),
                    'status_value' => sanitize_key((string) ($input['status'] ?? 'draft')),
                    'request_hash' => md5($project_id . '|' . $draft_id . '|' . sanitize_text_field((string) ($input['title'] ?? ''))),
                );

            case 'publisher_draft_approve':
                $draft_id = intval($input['draft_id'] ?? 0);
                if ($draft_id <= 0) {
                    return new WP_Error('invalid_request', 'Invalid draft_id');
                }
                return array(
                    'task' => $task,
                    'project_id' => $project_id,
                    'draft_id' => $draft_id,
                    'request_hash' => md5($project_id . '|approve|' . $draft_id),
                );

            case 'exchange_request_create':
                $portal_json = (string) ($input['counterparty_portal_json'] ?? '');
                $portal = json_decode(wp_unslash($portal_json), true);
                if ($project_id <= 0) {
                    return new WP_Error('invalid_request', 'Invalid project ID.');
                }
                if (!is_array($portal) || empty($portal)) {
                    return new WP_Error('invalid_request', 'Counterparty portal is required.');
                }
                return array(
                    'task' => $task,
                    'project_id' => $project_id,
                    'portal' => $portal,
                    'requester_target_url' => esc_url_raw(trim((string) ($input['requester_target_url'] ?? ''))),
                    'requester_anchor_text' => sanitize_text_field((string) ($input['requester_anchor_text'] ?? '')),
                    'counterparty_target_url' => esc_url_raw(trim((string) ($input['counterparty_target_url'] ?? ''))),
                    'counterparty_anchor_text' => sanitize_text_field((string) ($input['counterparty_anchor_text'] ?? '')),
                    'requester_notes' => sanitize_textarea_field((string) ($input['requester_notes'] ?? '')),
                    'request_hash' => md5($project_id . '|' . wp_json_encode($portal) . '|' . (string) ($input['requester_target_url'] ?? '') . '|' . (string) ($input['counterparty_target_url'] ?? '')),
                );

            case 'exchange_activate_link':
                $source_post_id = intval($input['source_post_id'] ?? 0);
                $exchange_ref = sanitize_text_field((string) ($input['exchange_ref'] ?? ''));
                if ($project_id <= 0) {
                    return new WP_Error('invalid_request', 'Invalid project ID.');
                }
                if ($source_post_id <= 0) {
                    return new WP_Error('invalid_request', 'Source content is required.');
                }
                return array(
                    'task' => $task,
                    'project_id' => $project_id,
                    'exchange_ref' => $exchange_ref,
                    'request_id' => intval($input['request_id'] ?? 0),
                    'source_post_id' => $source_post_id,
                    'notes' => sanitize_textarea_field((string) ($input['notes'] ?? '')),
                    'request_hash' => md5($project_id . '|activate|' . $exchange_ref . '|' . $source_post_id),
                );

            case 'exchange_request_respond':
                if ($project_id <= 0) {
                    return new WP_Error('invalid_request', 'Invalid project ID.');
                }
                return array(
                    'task' => $task,
                    'project_id' => $project_id,
                    'request_id' => intval($input['request_id'] ?? 0),
                    'exchange_ref' => sanitize_text_field((string) ($input['exchange_ref'] ?? '')),
                    'decision' => sanitize_text_field((string) ($input['decision'] ?? 'accept')),
                    'counterparty_target_url' => esc_url_raw(trim((string) ($input['counterparty_target_url'] ?? ''))),
                    'counterparty_anchor_text' => sanitize_text_field((string) ($input['counterparty_anchor_text'] ?? '')),
                    'counterparty_notes' => sanitize_textarea_field((string) ($input['counterparty_notes'] ?? '')),
                    'request_hash' => md5($project_id . '|respond|' . (string) ($input['exchange_ref'] ?? '') . '|' . (string) ($input['decision'] ?? 'accept')),
                );

            case 'exchange_refresh_verification':
                if ($project_id <= 0) {
                    return new WP_Error('invalid_request', 'Invalid project ID.');
                }
                return array(
                    'task' => $task,
                    'project_id' => $project_id,
                    'request_id' => intval($input['request_id'] ?? 0),
                    'exchange_ref' => sanitize_text_field((string) ($input['exchange_ref'] ?? '')),
                    'request_hash' => md5($project_id . '|verify|' . (string) ($input['exchange_ref'] ?? '')),
                );

            case 'exchange_change_state':
                if ($project_id <= 0) {
                    return new WP_Error('invalid_request', 'Invalid project ID.');
                }
                return array(
                    'task' => $task,
                    'project_id' => $project_id,
                    'request_id' => intval($input['request_id'] ?? 0),
                    'exchange_ref' => sanitize_text_field((string) ($input['exchange_ref'] ?? '')),
                    'state_action' => sanitize_text_field((string) ($input['state_action'] ?? $input['action_name'] ?? '')),
                    'reason' => sanitize_textarea_field((string) ($input['reason'] ?? '')),
                    'request_hash' => md5($project_id . '|state|' . (string) ($input['exchange_ref'] ?? '') . '|' . (string) ($input['state_action'] ?? $input['action_name'] ?? '')),
                );

            case 'exchange_submit_publication':
                if ($project_id <= 0) {
                    return new WP_Error('invalid_request', 'Invalid project ID.');
                }
                return array(
                    'task' => $task,
                    'project_id' => $project_id,
                    'request_id' => intval($input['request_id'] ?? 0),
                    'exchange_ref' => sanitize_text_field((string) ($input['exchange_ref'] ?? '')),
                    'published_url' => esc_url_raw(trim((string) ($input['published_url'] ?? ''))),
                    'target_url' => esc_url_raw(trim((string) ($input['target_url'] ?? ''))),
                    'anchor_text' => sanitize_text_field((string) ($input['anchor_text'] ?? '')),
                    'notes' => sanitize_textarea_field((string) ($input['notes'] ?? '')),
                    'request_hash' => md5($project_id . '|publication|' . (string) ($input['exchange_ref'] ?? '') . '|' . (string) ($input['published_url'] ?? '')),
                );
        }

        return new WP_Error('unsupported_task', 'Unsupported off-page task.');
    }

    private function aysa_offpage_async_execute($request) {
        if (!is_array($request)) {
            return new WP_Error('invalid_request', 'Invalid off-page request.');
        }

        $task = sanitize_key((string) ($request['task'] ?? ''));
        $project_id = intval($request['project_id'] ?? 0);

        switch ($task) {
            case 'publisher_draft_create':
                $order_ref = sanitize_text_field((string) ($request['order_ref'] ?? ''));
                $orders = $this->aysa_get_adverlink_orders($project_id);
                $matched_order = null;
                foreach ($orders as $order) {
                    if (!is_array($order)) {
                        continue;
                    }
                    if (sanitize_text_field((string) ($order['order_ref'] ?? '')) === $order_ref) {
                        $matched_order = $order;
                        break;
                    }
                }
                if (!is_array($matched_order)) {
                    return new WP_Error('order_not_found', 'Order not found.');
                }
                $result = $this->aysa_create_publisher_article_draft_for_order($project_id, $matched_order);
                if (is_wp_error($result)) {
                    return $result;
                }
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_adverlink_create_publisher_article_draft',
                    'offpage_create_publisher_article_draft_' . md5($order_ref),
                    array('module' => 'offpage', 'order_ref' => $order_ref),
                    12,
                    'wp_ajax/aysa_adverlink_create_publisher_article_draft'
                );
                return $result;

            case 'publisher_draft_update':
                $draft_id = intval($request['draft_id'] ?? 0);
                $status_value = sanitize_key((string) ($request['status_value'] ?? 'draft'));
                $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/publisher-articles/update', array(
                    'draft_id' => $draft_id,
                    'title' => sanitize_text_field((string) ($request['title'] ?? '')),
                    'excerpt' => sanitize_textarea_field((string) ($request['excerpt'] ?? '')),
                    'body_html' => wp_kses_post((string) ($request['body_html'] ?? '')),
                    'status' => $status_value !== '' ? $status_value : 'edited',
                ), 60);
                if (is_wp_error($broker_result)) {
                    return new WP_Error('broker_error', $this->aysa_humanize_broker_error($broker_result, 'Could not save draft.'));
                }
                $draft = isset($broker_result['draft']) && is_array($broker_result['draft']) ? $broker_result['draft'] : array();
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_adverlink_update_publisher_article_draft',
                    'offpage_update_publisher_article_draft_' . $draft_id,
                    array('module' => 'offpage', 'draft_id' => $draft_id, 'status' => $status_value !== '' ? $status_value : 'edited'),
                    8,
                    'wp_ajax/aysa_adverlink_update_publisher_article_draft'
                );
                return array('draft' => $draft);

            case 'publisher_draft_approve':
                $draft_id = intval($request['draft_id'] ?? 0);
                if ($this->aysa_agent8_is_publisher_draft_approved($project_id, $draft_id)) {
                    $this->aysa_agent8_mark_publisher_draft_approved($project_id, $draft_id);
                    return array('draft' => array('id' => $draft_id, 'status' => 'approved'), 'already_approved' => true);
                }
                $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/publisher-articles/approve', array(
                    'draft_id' => $draft_id,
                ), 60);
                if (is_wp_error($broker_result)) {
                    return new WP_Error('broker_error', $this->aysa_humanize_broker_error($broker_result, 'Could not approve draft.'));
                }
                $draft = isset($broker_result['draft']) && is_array($broker_result['draft']) ? $broker_result['draft'] : array();
                $this->aysa_agent8_mark_publisher_draft_approved($project_id, $draft_id, $draft);
                $reference = 'offpage_approve_publisher_article_draft_' . $draft_id;
                if (!$this->aysa_wallet_usage_reference_exists($project_id, $reference)) {
                    $this->aysa_wallet_track_action_usage(
                        $project_id,
                        'aysa_adverlink_approve_publisher_article_draft',
                        $reference,
                        array('module' => 'offpage', 'draft_id' => $draft_id),
                        8,
                        'wp_ajax/aysa_adverlink_approve_publisher_article_draft'
                    );
                }
                return array('draft' => $draft);

            case 'exchange_request_create':
                $requester = $this->aysa_get_link_exchange_profile($project_id);
                $payload = array(
                    'project_id' => $project_id,
                    'requester_site_url' => $requester['site_url'],
                    'requester_site_domain' => $requester['site_domain'],
                    'requester_website_type' => $requester['website_type'],
                    'requester_industry' => $requester['industry'],
                    'requester_language' => $requester['language'],
                    'requester_country' => $requester['country'],
                    'requester_topics' => array_values($requester['topics']),
                    'requester_authority_value' => intval($requester['authority_value'] ?? 0),
                    'counterparty_portal' => isset($request['portal']) && is_array($request['portal']) ? $request['portal'] : array(),
                    'requester_target_url' => esc_url_raw((string) ($request['requester_target_url'] ?? '')),
                    'requester_anchor_text' => sanitize_text_field((string) ($request['requester_anchor_text'] ?? '')),
                    'counterparty_target_url' => esc_url_raw((string) ($request['counterparty_target_url'] ?? '')),
                    'counterparty_anchor_text' => sanitize_text_field((string) ($request['counterparty_anchor_text'] ?? '')),
                    'requester_notes' => sanitize_textarea_field((string) ($request['requester_notes'] ?? '')),
                );
                $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/create', $payload, 45);
                if (is_wp_error($broker_result)) {
                    return new WP_Error('broker_error', $this->aysa_humanize_broker_error($broker_result, 'Could not create exchange request.'));
                }
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_adverlink_exchange_create_request',
                    'offpage_exchange_create_request_' . md5((string) ($payload['requester_target_url'] ?? '') . '|' . (string) ($payload['counterparty_target_url'] ?? '')),
                    array('module' => 'offpage', 'source' => 'marketplace_exchange'),
                    12,
                    'wp_ajax/aysa_adverlink_exchange_create_request'
                );
                return array(
                    'request' => isset($broker_result['request']) && is_array($broker_result['request']) ? $broker_result['request'] : array(),
                    'deduplicated' => !empty($broker_result['deduplicated']),
                );

            case 'exchange_activate_link':
                $requester = $this->aysa_get_link_exchange_profile($project_id);
                $request_payload = array(
                    'project_id' => $project_id,
                    'current_site_domain' => $requester['site_domain'],
                    'request_id' => intval($request['request_id'] ?? 0),
                    'exchange_ref' => sanitize_text_field((string) ($request['exchange_ref'] ?? '')),
                );
                $request_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/get', $request_payload, 30);
                if (is_wp_error($request_result)) {
                    return new WP_Error('broker_error', $this->aysa_humanize_broker_error($request_result, 'Could not load exchange request before activation.'));
                }
                $exchange_request = isset($request_result['request']) && is_array($request_result['request']) ? $request_result['request'] : array();
                if (empty($exchange_request)) {
                    return new WP_Error('request_not_found', 'Exchange request not found.');
                }
                $source_post_id = intval($request['source_post_id'] ?? 0);
                $role = (string) ($exchange_request['viewer_role'] ?? '');
                $target_url = $role === 'requester'
                    ? esc_url_raw((string) ($exchange_request['counterparty_target_url'] ?? ''))
                    : esc_url_raw((string) ($exchange_request['requester_target_url'] ?? ''));
                $anchor_text = $role === 'requester'
                    ? sanitize_text_field((string) ($exchange_request['counterparty_anchor_text'] ?? ''))
                    : sanitize_text_field((string) ($exchange_request['requester_anchor_text'] ?? ''));
                if ($target_url === '' || $anchor_text === '') {
                    return new WP_Error('missing_target', 'Target URL and anchor are missing for activation.');
                }
                $source_post = get_post($source_post_id);
                $semantic = $this->aysa_exchange_score_source_candidate($source_post, $target_url, $anchor_text);
                $activation = $this->aysa_exchange_insert_link_into_post($source_post_id, $target_url, $anchor_text, (string) ($exchange_request['exchange_ref'] ?? ''));
                if (is_wp_error($activation)) {
                    return $activation;
                }
                $payload = array(
                    'project_id' => $project_id,
                    'current_site_domain' => $requester['site_domain'],
                    'request_id' => intval($exchange_request['id'] ?? 0),
                    'exchange_ref' => sanitize_text_field((string) ($exchange_request['exchange_ref'] ?? '')),
                    'source_post_id' => $source_post_id,
                    'source_url' => esc_url_raw((string) ($activation['source_url'] ?? '')),
                    'published_url' => esc_url_raw((string) ($activation['source_url'] ?? '')),
                    'target_url' => $target_url,
                    'anchor_text' => $anchor_text,
                    'notes' => sanitize_textarea_field((string) ($request['notes'] ?? '')),
                );
                $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/activate', $payload, 45);
                if (is_wp_error($broker_result)) {
                    return new WP_Error('broker_error', $this->aysa_humanize_broker_error($broker_result, 'Could not activate reciprocal link.'));
                }
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_adverlink_exchange_activate_link',
                    'offpage_exchange_activate_link_' . md5((string) ($exchange_request['exchange_ref'] ?? '') . '|' . (string) $source_post_id),
                    array(
                        'module' => 'offpage',
                        'exchange_ref' => (string) ($exchange_request['exchange_ref'] ?? ''),
                        'source_post_id' => $source_post_id,
                    ),
                    12,
                    'wp_ajax/aysa_adverlink_exchange_activate_link'
                );
                return array(
                    'request' => isset($broker_result['request']) && is_array($broker_result['request']) ? $broker_result['request'] : array(),
                    'activation' => $activation,
                    'semantic' => $semantic,
                    'placement_mode' => !empty($semantic['eligible']) ? 'semantic_match' : 'partner_box_fallback',
                );

            case 'exchange_request_respond':
                $requester = $this->aysa_get_link_exchange_profile($project_id);
                $payload = array(
                    'project_id' => $project_id,
                    'current_site_domain' => $requester['site_domain'],
                    'request_id' => intval($request['request_id'] ?? 0),
                    'exchange_ref' => sanitize_text_field((string) ($request['exchange_ref'] ?? '')),
                    'decision' => sanitize_text_field((string) ($request['decision'] ?? 'accept')),
                    'counterparty_target_url' => esc_url_raw(trim((string) ($request['counterparty_target_url'] ?? ''))),
                    'counterparty_anchor_text' => sanitize_text_field((string) ($request['counterparty_anchor_text'] ?? '')),
                    'counterparty_notes' => sanitize_textarea_field((string) ($request['counterparty_notes'] ?? '')),
                );
                $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/respond', $payload, 30);
                if (is_wp_error($broker_result)) {
                    return new WP_Error('broker_error', $this->aysa_humanize_broker_error($broker_result, 'Could not update exchange request.'));
                }
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_adverlink_exchange_respond_request',
                    'offpage_exchange_respond_request_' . md5((string) ($payload['exchange_ref'] ?? '') . '|' . (string) ($payload['decision'] ?? '')),
                    array('module' => 'offpage', 'decision' => (string) ($payload['decision'] ?? '')),
                    8,
                    'wp_ajax/aysa_adverlink_exchange_respond_request'
                );
                return array(
                    'request' => isset($broker_result['request']) && is_array($broker_result['request']) ? $broker_result['request'] : array(),
                );

            case 'exchange_refresh_verification':
                $requester = $this->aysa_get_link_exchange_profile($project_id);
                $payload = array(
                    'project_id' => $project_id,
                    'current_site_domain' => $requester['site_domain'],
                    'request_id' => intval($request['request_id'] ?? 0),
                    'exchange_ref' => sanitize_text_field((string) ($request['exchange_ref'] ?? '')),
                );
                $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/verify', $payload, 45);
                if (is_wp_error($broker_result)) {
                    return new WP_Error('broker_error', $this->aysa_humanize_broker_error($broker_result, 'Could not refresh exchange verification.'));
                }
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_adverlink_exchange_refresh_verification',
                    'offpage_exchange_refresh_verification_' . md5((string) ($payload['exchange_ref'] ?? '')),
                    array('module' => 'offpage', 'exchange_ref' => (string) ($payload['exchange_ref'] ?? '')),
                    6,
                    'wp_ajax/aysa_adverlink_exchange_refresh_verification'
                );
                return array(
                    'request' => isset($broker_result['request']) && is_array($broker_result['request']) ? $broker_result['request'] : array(),
                );

            case 'exchange_change_state':
                $requester = $this->aysa_get_link_exchange_profile($project_id);
                $payload = array(
                    'project_id' => $project_id,
                    'current_site_domain' => $requester['site_domain'],
                    'request_id' => intval($request['request_id'] ?? 0),
                    'exchange_ref' => sanitize_text_field((string) ($request['exchange_ref'] ?? '')),
                    'action' => sanitize_text_field((string) ($request['state_action'] ?? '')),
                    'reason' => sanitize_textarea_field((string) ($request['reason'] ?? '')),
                );
                $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/state', $payload, 30);
                if (is_wp_error($broker_result)) {
                    return new WP_Error('broker_error', $this->aysa_humanize_broker_error($broker_result, 'Could not update exchange request state.'));
                }
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_adverlink_exchange_change_state',
                    'offpage_exchange_change_state_' . md5((string) ($payload['exchange_ref'] ?? '') . '|' . (string) ($payload['action'] ?? '')),
                    array(
                        'module' => 'offpage',
                        'action' => (string) ($payload['action'] ?? ''),
                        'exchange_ref' => (string) ($payload['exchange_ref'] ?? ''),
                    ),
                    6,
                    'wp_ajax/aysa_adverlink_exchange_change_state'
                );
                return array(
                    'request' => isset($broker_result['request']) && is_array($broker_result['request']) ? $broker_result['request'] : array(),
                );

            case 'exchange_submit_publication':
                $requester = $this->aysa_get_link_exchange_profile($project_id);
                $payload = array(
                    'project_id' => $project_id,
                    'current_site_domain' => $requester['site_domain'],
                    'request_id' => intval($request['request_id'] ?? 0),
                    'exchange_ref' => sanitize_text_field((string) ($request['exchange_ref'] ?? '')),
                    'published_url' => esc_url_raw(trim((string) ($request['published_url'] ?? ''))),
                    'target_url' => esc_url_raw(trim((string) ($request['target_url'] ?? ''))),
                    'anchor_text' => sanitize_text_field((string) ($request['anchor_text'] ?? '')),
                    'notes' => sanitize_textarea_field((string) ($request['notes'] ?? '')),
                );
                $broker_result = $this->aysa_broker_request('POST', '/adverlink/v1/exchange/requests/publication', $payload, 30);
                if (is_wp_error($broker_result)) {
                    return new WP_Error('broker_error', $this->aysa_humanize_broker_error($broker_result, 'Could not submit publication proof.'));
                }
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_adverlink_exchange_submit_publication',
                    'offpage_exchange_submit_publication_' . md5((string) ($payload['exchange_ref'] ?? '') . '|' . (string) ($payload['published_url'] ?? '')),
                    array('module' => 'offpage', 'exchange_ref' => (string) ($payload['exchange_ref'] ?? '')),
                    8,
                    'wp_ajax/aysa_adverlink_exchange_submit_publication'
                );
                return array(
                    'request' => isset($broker_result['request']) && is_array($broker_result['request']) ? $broker_result['request'] : array(),
                );
        }

        return new WP_Error('unsupported_task', 'Unsupported off-page task.');
    }

    public function handle_offpage_async_start() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $task = sanitize_key((string) ($_POST['task'] ?? ''));
        if (!in_array($task, $this->aysa_offpage_async_supported_tasks(), true)) {
            wp_send_json_error('Unsupported off-page task.');
            return;
        }

        $request = $this->aysa_offpage_async_prepare_request($task, $_POST);
        if (is_wp_error($request)) {
            wp_send_json_error($request->get_error_message());
            return;
        }

        $project_id = intval($request['project_id'] ?? 0);
        $request_hash = (string) ($request['request_hash'] ?? '');
        $existing = $this->aysa_offpage_async_get_status($project_id, $task);
        if (
            is_array($existing) &&
            !empty($existing['run_id']) &&
            in_array((string) ($existing['status'] ?? ''), array('queued', 'running'), true) &&
            (string) ($existing['request_hash'] ?? '') === $request_hash
        ) {
            wp_send_json_success($existing);
            return;
        }

        $run_id = uniqid('offpage_', true);
        $status = array(
            'task' => $task,
            'project_id' => $project_id,
            'run_id' => $run_id,
            'request_hash' => $request_hash,
            'status' => 'queued',
            'result' => null,
            'error' => '',
            'started_at' => time(),
            'updated_at' => time(),
            'finished_at' => 0,
        );
        $this->aysa_offpage_async_save_status($project_id, $task, $status);
        $this->aysa_offpage_async_schedule_event(array(
            'task' => $task,
            'project_id' => $project_id,
            'run_id' => $run_id,
            'request_hash' => $request_hash,
            'request' => $request,
        ), 1);

        wp_send_json_success($status);
    }

    public function handle_offpage_async_status() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $task = sanitize_key((string) ($_POST['task'] ?? ''));
        $project_id = $this->aysa_resolve_project_id_with_local_fallback(intval($_POST['project_id'] ?? get_option('aysa_project_id', 0)));
        if ($project_id <= 0 || !in_array($task, $this->aysa_offpage_async_supported_tasks(), true)) {
            wp_send_json_error('Invalid off-page task status request.');
            return;
        }

        $status = $this->aysa_offpage_async_get_status($project_id, $task);
        if (empty($status)) {
            wp_send_json_success(array(
                'task' => $task,
                'project_id' => $project_id,
                'status' => 'idle',
                'result' => null,
                'error' => '',
            ));
            return;
        }

        wp_send_json_success($status);
    }

    public function run_offpage_async_event($payload) {
        if (!is_array($payload)) {
            return;
        }

        $task = sanitize_key((string) ($payload['task'] ?? ''));
        $project_id = intval($payload['project_id'] ?? 0);
        $run_id = (string) ($payload['run_id'] ?? '');
        $request_hash = (string) ($payload['request_hash'] ?? '');
        $request = isset($payload['request']) && is_array($payload['request']) ? $payload['request'] : array();
        if ($project_id <= 0 || $run_id === '' || $request_hash === '' || !in_array($task, $this->aysa_offpage_async_supported_tasks(), true)) {
            return;
        }

        $status = $this->aysa_offpage_async_get_status($project_id, $task);
        if (
            empty($status) ||
            (string) ($status['run_id'] ?? '') !== $run_id ||
            (string) ($status['request_hash'] ?? '') !== $request_hash
        ) {
            return;
        }

        $status['status'] = 'running';
        $status['updated_at'] = time();
        $status['error'] = '';
        $this->aysa_offpage_async_save_status($project_id, $task, $status);

        $result = $this->aysa_offpage_async_execute($request);
        $status['updated_at'] = time();
        $status['finished_at'] = time();
        if (is_wp_error($result)) {
            $status['status'] = 'failed';
            $status['result'] = null;
            $status['error'] = $result->get_error_message();
        } else {
            $status['status'] = 'completed';
            $status['result'] = $result;
            $status['error'] = '';
        }
        $this->aysa_offpage_async_save_status($project_id, $task, $status);
    }

    public function handle_competitor_gap_keywords_gap() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }
        $force_refresh = !empty($_POST['force_refresh']) && intval($_POST['force_refresh']) === 1;

        $target = sanitize_text_field((string) ($_POST['target'] ?? ''));
        if ($target === '') {
            $target = (string) wp_parse_url(home_url('/'), PHP_URL_HOST);
        }
        $location_name = sanitize_text_field((string) ($_POST['location_name'] ?? 'Romania'));
        $language_name = sanitize_text_field((string) ($_POST['language_name'] ?? 'Romanian'));
        $limit = max(1, min(2000, intval($_POST['limit'] ?? 200)));

        $competitors_raw = $_POST['competitors'] ?? array();
        if (is_string($competitors_raw)) {
            $decoded = json_decode(wp_unslash($competitors_raw), true);
            $competitors_raw = is_array($decoded) ? $decoded : array();
        } elseif (is_array($competitors_raw)) {
            $competitors_raw = array_map('wp_unslash', $competitors_raw);
        } else {
            $competitors_raw = array();
        }

        $competitors = array();
        foreach ($competitors_raw as $competitor) {
            $value = trim(sanitize_text_field((string) $competitor));
            if ($value !== '') {
                $competitors[] = $value;
            }
        }
        $competitors = array_values(array_unique($competitors));

        $context = $this->aysa_monitoring_resolve_context($target);
        $resolved_target = trim((string) ($context['target'] ?? ''));
        if ($resolved_target === '') {
            $resolved_target = $target;
        }

        $normalized_competitors = array();
        foreach ($competitors as $competitor) {
            $host = $this->aysa_monitoring_extract_host((string) $competitor);
            if ($host === '' || $this->aysa_monitoring_is_local_host($host)) {
                if ($resolved_target !== '') {
                    $host = $resolved_target;
                }
            }
            if ($host !== '') {
                $normalized_competitors[] = $host;
            }
        }
        $normalized_competitors = array_values(array_unique($normalized_competitors));
        if (empty($normalized_competitors) && $resolved_target !== '') {
            $normalized_competitors[] = $resolved_target;
        }

        $gap_payload = array(
            'project_id' => $project_id,
            'target' => $resolved_target,
            'location_code' => intval($context['location_code'] ?? 2840),
            'language_code' => (string) ($context['language_code'] ?? 'ro'),
            'limit' => $limit,
            'competitors' => $normalized_competitors,
        );

        if (!$force_refresh) {
            $cached = $this->aysa_competitor_gap_get_cached_payload($project_id);
            $cached_request = is_array($cached) && isset($cached['request']) && is_array($cached['request'])
                ? $cached['request']
                : array();
            if (
                is_array($cached) &&
                !empty($cached['data']) &&
                is_array($cached['data']) &&
                $this->aysa_competitor_gap_requests_match($cached_request, $gap_payload)
            ) {
                $cached_data = $cached['data'];
                $cached_data['cached'] = true;
                $cached_data['cached_at'] = intval($cached['cached_at'] ?? 0);
                $cached_data['inKeywordResearch'] = $this->aysa_keyword_discovery_existing_keywords_map(
                    $project_id,
                    isset($cached_data['rankedKeywords']) && is_array($cached_data['rankedKeywords'])
                        ? $cached_data['rankedKeywords']
                        : array()
                );
                // Local/cache read must not consume credits.
                wp_send_json_success($cached_data);
                return;
            }
        }

        $broker_paths = array(
            '/v1/dataforseo/keywords/gap',
            '/v1/keywords/gap',
        );

        foreach ($broker_paths as $broker_path) {
            $broker_result = $this->aysa_broker_request('POST', $broker_path, $gap_payload, 180);
            if (is_wp_error($broker_result)) {
                continue;
            }

            if (is_array($broker_result) && !empty($broker_result['ok']) && isset($broker_result['rankedKeywords'])) {
                $this->aysa_competitor_gap_save_cached_payload($project_id, $gap_payload, $broker_result);
                $broker_result['cached'] = false;
                $broker_result['cached_at'] = time();
                $broker_result['inKeywordResearch'] = $this->aysa_keyword_discovery_existing_keywords_map(
                    $project_id,
                    isset($broker_result['rankedKeywords']) && is_array($broker_result['rankedKeywords'])
                        ? $broker_result['rankedKeywords']
                        : array()
                );
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_competitor_gap_keywords_gap',
                    'competitor_gap_keywords_gap',
                    array(
                        'module' => 'competitor_gap',
                        'source' => 'broker',
                        'broker_path' => $broker_path,
                        'target' => $target,
                        'competitors_count' => count($competitors),
                    ),
                    35,
                    (string) $broker_path
                );
                wp_send_json_success($broker_result);
                return;
            }

            if (is_array($broker_result) && isset($broker_result['data']) && is_array($broker_result['data']) && isset($broker_result['data']['rankedKeywords'])) {
                $data = $broker_result['data'];
                $this->aysa_competitor_gap_save_cached_payload($project_id, $gap_payload, $data);
                $data['cached'] = false;
                $data['cached_at'] = time();
                $data['inKeywordResearch'] = $this->aysa_keyword_discovery_existing_keywords_map(
                    $project_id,
                    isset($data['rankedKeywords']) && is_array($data['rankedKeywords'])
                        ? $data['rankedKeywords']
                        : array()
                );
                $this->aysa_wallet_track_action_usage(
                    $project_id,
                    'aysa_competitor_gap_keywords_gap',
                    'competitor_gap_keywords_gap',
                    array(
                        'module' => 'competitor_gap',
                        'source' => 'broker_data',
                        'broker_path' => $broker_path,
                        'target' => $target,
                        'competitors_count' => count($competitors),
                    ),
                    35,
                    (string) $broker_path
                );
                wp_send_json_success($data);
                return;
            }
        }

        // Legacy safety fallback while old endpoint still exists.
        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }

        $query = array(
            'target' => $target,
            'locationName' => $location_name,
            'languageName' => $language_name,
            'ProjectId' => $project_id,
            'limit' => $limit,
        );
        if (!empty($competitors)) {
            $query['competitors'] = $competitors;
        }

        try {
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $endpoint = 'api/keywordsgap/keywords-gap?' . http_build_query($query);
            $raw = $auth_manager->make_authenticated_request($endpoint, 'GET');
            if ($raw === false) {
                wp_send_json_error('Keywords gap endpoint returned empty response.');
                return;
            }

            if (is_string($raw)) {
                $decoded = json_decode($raw, true);
                if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
                    $raw = $decoded;
                }
            }

            if (!is_array($raw)) {
                wp_send_json_error('Invalid keywords gap response format.');
                return;
            }

            if (is_array($raw) && isset($raw['rankedKeywords'])) {
                $this->aysa_competitor_gap_save_cached_payload($project_id, $gap_payload, $raw);
                $raw['cached'] = false;
                $raw['cached_at'] = time();
                $raw['inKeywordResearch'] = $this->aysa_keyword_discovery_existing_keywords_map(
                    $project_id,
                    is_array($raw['rankedKeywords']) ? $raw['rankedKeywords'] : array()
                );
            }
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_competitor_gap_keywords_gap',
                'competitor_gap_keywords_gap',
                array(
                    'module' => 'competitor_gap',
                    'source' => 'legacy_remote',
                    'target' => $target,
                    'competitors_count' => count($competitors),
                ),
                35,
                '/v1/keywords/gap'
            );
            wp_send_json_success($raw);
        } catch (Exception $e) {
            wp_send_json_error('Failed to fetch keywords gap data: ' . $e->getMessage());
        }
    }

    public function handle_competitor_identification_seed_competitors() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $force_refresh = !empty($_POST['force_refresh']) && intval($_POST['force_refresh']) === 1;
        $top_n = intval($_POST['top_n'] ?? 10);
        if ($top_n <= 0) {
            $top_n = 10;
        }
        $top_n = min($top_n, 20);

        $seed_user_id = get_current_user_id();
        $seed_data = $this->aysa_keyword_strategy_extract_seed_data($seed_user_id);
        $seed_keywords = isset($seed_data['seed_keywords']) && is_array($seed_data['seed_keywords']) ? array_values($seed_data['seed_keywords']) : array();
        if (empty($seed_keywords)) {
            $shared_user_id = intval(get_option('aysa_user_id', 0));
            if ($shared_user_id > 0 && $shared_user_id !== $seed_user_id) {
                $shared_seed_data = $this->aysa_keyword_strategy_extract_seed_data($shared_user_id);
                $shared_seed_keywords = isset($shared_seed_data['seed_keywords']) && is_array($shared_seed_data['seed_keywords'])
                    ? array_values($shared_seed_data['seed_keywords'])
                    : array();
                if (!empty($shared_seed_keywords)) {
                    $seed_keywords = $shared_seed_keywords;
                }
            }
        }
        $seed_keywords = array_values(array_filter(array_map(function($kw) {
            return trim((string) $kw);
        }, $seed_keywords), function($kw) {
            return $kw !== '';
        }));

        $configured_competitors = isset($seed_data['competitors']) && is_array($seed_data['competitors']) ? array_values($seed_data['competitors']) : array();
        if (empty($configured_competitors)) {
            $shared_user_id = intval(get_option('aysa_user_id', 0));
            if ($shared_user_id > 0 && $shared_user_id !== $seed_user_id) {
                $shared_seed_data = $this->aysa_keyword_strategy_extract_seed_data($shared_user_id);
                $shared_competitors = isset($shared_seed_data['competitors']) && is_array($shared_seed_data['competitors'])
                    ? array_values($shared_seed_data['competitors'])
                    : array();
                if (!empty($shared_competitors)) {
                    $configured_competitors = $shared_competitors;
                }
            }
        }
        $configured_competitors = array_values(array_unique(array_filter(array_map(function($domain) {
            return $this->aysa_monitoring_extract_host((string) $domain);
        }, $configured_competitors), function($domain) {
            return $domain !== '';
        })));

        if (empty($seed_keywords)) {
            wp_send_json_error('No seed keywords found in Setup SEO Profile.');
            return;
        }

        $home_host = $this->aysa_monitoring_extract_host((string) home_url('/'));
        $resolved_context = $this->aysa_monitoring_resolve_context($home_host !== '' ? $home_host : 'florariebragadiru.ro');
        $location_code = intval($resolved_context['location_code'] ?? 2840);
        if ($location_code <= 0) {
            $location_code = 2840;
        }
        $language_code = strtolower(trim((string) ($resolved_context['language_code'] ?? 'ro')));
        if ($language_code === '') {
            $language_code = 'ro';
        }
        $target_host = trim((string) ($resolved_context['target'] ?? ''));
        $own_domains = array();
        if ($home_host !== '') {
            $own_domains[] = strtolower($home_host);
        }
        if ($target_host !== '') {
            $own_domains[] = strtolower($target_host);
        }
        $own_domains = array_values(array_unique(array_filter($own_domains)));

        $request_payload = array(
            'project_id' => $project_id,
            'seed_keywords' => array_values($seed_keywords),
            'active_competitors' => array_values($configured_competitors),
            'location_code' => $location_code,
            'language_code' => $language_code,
            'top_n' => $top_n,
            'own_domains' => $own_domains,
        );

        if (!$force_refresh) {
            $cached = $this->aysa_competitor_identification_get_cached_payload($project_id);
            if (is_array($cached) && !empty($cached['data']) && is_array($cached['data'])) {
                $cached_request = isset($cached['request']) && is_array($cached['request']) ? $cached['request'] : array();
                $same_seeds = isset($cached_request['seed_keywords']) && is_array($cached_request['seed_keywords'])
                    ? array_map('strtolower', array_values($cached_request['seed_keywords']))
                    : array();
                $current_seeds = array_map('strtolower', array_values($request_payload['seed_keywords']));
                sort($same_seeds);
                sort($current_seeds);
                if ($same_seeds === $current_seeds) {
                    $data = $cached['data'];
                    $data['cached'] = true;
                    $data['cached_at'] = intval($cached['cached_at'] ?? 0);
                    // Local/cache read must not consume credits.
                    wp_send_json_success($data);
                    return;
                }
            }
        }

        $broker_paths = array(
            '/v1/competitors/identify/from-seeds',
        );
        $last_error = '';
        foreach ($broker_paths as $broker_path) {
            $broker_result = $this->aysa_broker_request('POST', $broker_path, $request_payload, 180);
            if (is_wp_error($broker_result)) {
                $last_error = $broker_result->get_error_message();
                continue;
            }

            $data = $broker_result;
            if (isset($data['data']) && is_array($data['data'])) {
                $data = $data['data'];
            }
            if (is_array($data) && !empty($data['error'])) {
                $last_error = (string) $data['error'];
            }
            if (!is_array($data) || empty($data['ok'])) {
                if ($last_error === '') {
                    $last_error = 'Broker response missing "ok=true".';
                }
                continue;
            }
            if (!isset($data['unique_competitors']) || !is_array($data['unique_competitors'])) {
                $last_error = 'Broker response missing competitors list.';
                continue;
            }

            $data = $this->aysa_keyword_v2_finalize_snapshot_contract($project_id, 'competitor_identification_v2', $data, 'unique_competitors');

            $this->aysa_competitor_identification_save_cached_payload($project_id, $request_payload, $data);
            $data['cached'] = false;
            $data['cached_at'] = time();
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_competitor_identification_seed_competitors',
                'competitor_identification_seed_competitors',
                array(
                    'module' => 'competitor_identification',
                    'source' => 'broker',
                    'broker_path' => $broker_path,
                    'seed_keywords_count' => count($seed_keywords),
                    'top_n' => $top_n,
                ),
                35,
                (string) $broker_path
            );
            wp_send_json_success($data);
            return;
        }

        $cached = $this->aysa_competitor_identification_get_cached_payload($project_id);
        if (is_array($cached) && !empty($cached['data']) && is_array($cached['data'])) {
            $data = $cached['data'];
            $data['cached'] = true;
            $data['cached_at'] = intval($cached['cached_at'] ?? 0);
            // Cached fallback is still local read; do not charge credits.
            wp_send_json_success($data);
            return;
        }

        $message = 'Could not identify competitors from seed keywords.';
        if ($last_error !== '') {
            $message .= ' ' . $last_error;
        }
        wp_send_json_error($message);
    }

    public function handle_competitor_backlinks_setup_competitors() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $seed_user_id = get_current_user_id();
        $seed_data = $this->aysa_keyword_strategy_extract_seed_data($seed_user_id);
        $competitors = isset($seed_data['competitors']) && is_array($seed_data['competitors']) ? array_values($seed_data['competitors']) : array();

        if (empty($competitors)) {
            $shared_user_id = intval(get_option('aysa_user_id', 0));
            if ($shared_user_id > 0 && $shared_user_id !== $seed_user_id) {
                $shared_seed_data = $this->aysa_keyword_strategy_extract_seed_data($shared_user_id);
                $shared_competitors = isset($shared_seed_data['competitors']) && is_array($shared_seed_data['competitors'])
                    ? array_values($shared_seed_data['competitors'])
                    : array();
                if (!empty($shared_competitors)) {
                    $competitors = $shared_competitors;
                }
            }
        }

        $normalized = array();
        foreach ($competitors as $raw) {
            $host = $this->aysa_monitoring_extract_host((string) $raw);
            if ($host === '') {
                $host = strtolower(trim((string) $raw));
            }
            $host = preg_replace('/^www\./', '', $host);
            if ($host === '' || !preg_match('/^[a-z0-9.-]+\.[a-z]{2,}$/', $host)) {
                continue;
            }
            $normalized[$host] = true;
        }

        $domains = array_keys($normalized);
        wp_send_json_success(array(
            'project_id' => $project_id,
            'competitors' => $domains,
            'count' => count($domains),
            'source' => 'setup_seo_profile',
        ));
    }

    private function aysa_competitor_identification_cache_key($project_id) {
        return 'aysa_competitor_identification_cache_' . intval($project_id);
    }

    private function aysa_competitor_identification_get_cached_payload($project_id) {
        $raw = get_option($this->aysa_competitor_identification_cache_key($project_id), array());
        return is_array($raw) ? $raw : array();
    }

    private function aysa_competitor_identification_save_cached_payload($project_id, $request_payload, $data_payload) {
        $record = array(
            'project_id' => intval($project_id),
            'cached_at' => time(),
            'request' => is_array($request_payload) ? $request_payload : array(),
            'data' => is_array($data_payload) ? $data_payload : array(),
        );
        update_option($this->aysa_competitor_identification_cache_key($project_id), $record, false);
    }

    private function aysa_competitor_gap_cache_key($project_id) {
        return 'aysa_competitor_gap_cache_' . intval($project_id);
    }

    private function aysa_competitor_gap_get_cached_payload($project_id) {
        $raw = get_option($this->aysa_competitor_gap_cache_key($project_id), array());
        return is_array($raw) ? $raw : array();
    }

    private function aysa_competitor_gap_save_cached_payload($project_id, $request_payload, $data_payload) {
        $record = array(
            'project_id' => intval($project_id),
            'cached_at' => time(),
            'request' => is_array($request_payload) ? $request_payload : array(),
            'data' => is_array($data_payload) ? $data_payload : array(),
        );
        update_option($this->aysa_competitor_gap_cache_key($project_id), $record, false);
    }

    private function aysa_competitor_gap_requests_match($left, $right) {
        if (!is_array($left) || !is_array($right)) {
            return false;
        }

        $normalize = function($payload) {
            $competitors = isset($payload['competitors']) && is_array($payload['competitors']) ? $payload['competitors'] : array();
            $normalized_competitors = array();
            foreach ($competitors as $competitor) {
                $host = strtolower(trim((string) $competitor));
                if ($host !== '') {
                    $normalized_competitors[] = $host;
                }
            }
            sort($normalized_competitors);

            return array(
                'target' => strtolower(trim((string) ($payload['target'] ?? ''))),
                'location_code' => intval($payload['location_code'] ?? 0),
                'language_code' => strtolower(trim((string) ($payload['language_code'] ?? ''))),
                'limit' => intval($payload['limit'] ?? 0),
                'competitors' => $normalized_competitors,
            );
        };

        return $normalize($left) === $normalize($right);
    }

    private function aysa_keyword_discovery_existing_keywords_map($project_id, $keywords) {
        $project_id = intval($project_id);
        if ($project_id <= 0 || !is_array($keywords) || empty($keywords)) {
            return array();
        }

        global $wpdb;
        $table = $this->aysa_keyword_discovery_table();
        $rows = $wpdb->get_col($wpdb->prepare(
            "SELECT keyword FROM {$table} WHERE project_id = %d",
            $project_id
        ));
        if (!is_array($rows)) {
            $rows = array();
        }

        $existing = array();
        foreach ($rows as $row_keyword) {
            $norm = strtolower(trim((string) $row_keyword));
            if ($norm !== '') {
                $existing[$norm] = true;
            }
        }

        $out = array();
        foreach ($keywords as $kw) {
            $keyword = trim((string) $kw);
            if ($keyword === '') {
                continue;
            }
            $out[$keyword] = !empty($existing[strtolower($keyword)]);
        }
        return $out;
    }

    private function aysa_agent8_get_setup_snapshot($project_id, $setup_user_id = 0) {
        $project_id = intval($project_id);
        $user_id = intval($setup_user_id);
        if ($user_id <= 0) {
            $user_id = intval($this->aysa_get_effective_setup_user_id());
        }

        $local_setup_state = $this->aysa_get_local_setup_state_for_user($user_id, $project_id);
        foreach (array('general', 'objectives', 'seo-settings', 'competition', 'tone-of-voice') as $step_key) {
            $legacy = array();
            if ($user_id > 0) {
                $legacy = get_option('aysa_wizard_' . $step_key . '_' . $user_id, array());
            }
            if ((!is_array($legacy) || empty($legacy)) && $project_id > 0) {
                $legacy = get_option('aysa_wizard_' . $step_key . '_project_' . $project_id, array());
            }
            if (!is_array($legacy) || empty($legacy)) {
                continue;
            }
            $existing_step = isset($local_setup_state[$step_key]) && is_array($local_setup_state[$step_key])
                ? $local_setup_state[$step_key]
                : array();
            $local_setup_state[$step_key] = array_replace($legacy, $existing_step);
        }
        $google_step = isset($local_setup_state['integrations']) && is_array($local_setup_state['integrations'])
            ? $local_setup_state['integrations']
            : array();

        return array(
            'local_setup_state' => $local_setup_state,
            'activation' => array(
                'project_id' => $project_id,
                'canonical_project_id' => intval(get_option('aysa_project_id', 0)),
                'website_id' => intval(get_option('aysa_website_id', 0)),
                'verified' => $this->aysa_get_effective_access_key('') !== '',
            ),
            'google_context' => array(
                'connected' => !empty($google_step['confirmed_with_google']) || !empty($google_step['gsc_connected']),
                'email' => (string) ($google_step['google_email'] ?? ''),
                'selected_gsc_site' => (string) ($google_step['gsc_site'] ?? ''),
                'selected_ga4_property' => (string) ($google_step['ga4_property'] ?? ''),
                'gsc_sites_count' => intval($google_step['gsc_sites_count'] ?? 0),
                'ga4_properties_count' => intval($google_step['ga4_properties_count'] ?? 0),
                'capabilities' => isset($google_step['capabilities']) && is_array($google_step['capabilities'])
                    ? $google_step['capabilities']
                    : array(),
                'granted_scopes' => isset($google_step['granted_scopes']) && is_array($google_step['granted_scopes'])
                    ? array_values($google_step['granted_scopes'])
                    : array(),
            ),
        );
    }

    public function handle_competitor_gap_add_to_keyword_research() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $raw_keywords = $_POST['keywords'] ?? array();
        if (is_string($raw_keywords)) {
            $decoded = json_decode(wp_unslash($raw_keywords), true);
            $raw_keywords = is_array($decoded) ? $decoded : array();
        } elseif (is_array($raw_keywords)) {
            $raw_keywords = array_map('wp_unslash', $raw_keywords);
        } else {
            $raw_keywords = array();
        }

        $keywords = array();
        foreach ($raw_keywords as $kw) {
            $text = trim(sanitize_text_field((string) $kw));
            if ($text !== '') {
                $keywords[] = $text;
            }
        }
        $keywords = array_values(array_unique($keywords));
        if (empty($keywords)) {
            wp_send_json_error('No keywords provided');
            return;
        }

        $raw_volume_map = $_POST['volumes'] ?? array();
        if (is_string($raw_volume_map)) {
            $decoded = json_decode(wp_unslash($raw_volume_map), true);
            $raw_volume_map = is_array($decoded) ? $decoded : array();
        } elseif (!is_array($raw_volume_map)) {
            $raw_volume_map = array();
        }

        $volume_map = array();
        foreach ($raw_volume_map as $kw => $value) {
            $keyword = trim((string) $kw);
            if ($keyword === '') {
                continue;
            }
            $parsed = $this->aysa_keyword_discovery_parse_numeric($value);
            $volume_map[$keyword] = ($parsed === null ? null : intval(round($parsed)));
        }

        $context = $this->aysa_monitoring_resolve_context('');
        $metrics_map = $this->aysa_keyword_discovery_fetch_keyword_metrics($project_id, $keywords, $context);
        $intent_map = $this->aysa_keyword_discovery_fetch_keyword_intent_bulk($project_id, $keywords, $context);
        if (!is_array($metrics_map)) {
            $metrics_map = array();
        }
        if (!is_array($intent_map)) {
            $intent_map = array();
        }

        $rows = array();
        $normalized_keywords = array();
        foreach ($keywords as $kw) {
            $norm = strtolower(trim((string) $kw));
            if ($norm === '') {
                continue;
            }
            $normalized_keywords[$norm] = $kw;

            $m = isset($metrics_map[$norm]) && is_array($metrics_map[$norm]) ? $metrics_map[$norm] : array();
            $i = isset($intent_map[$norm]) && is_array($intent_map[$norm]) ? $intent_map[$norm] : array();

            $search_volume = null;
            if (isset($volume_map[$kw]) && $volume_map[$kw] !== null) {
                $search_volume = intval($volume_map[$kw]);
            } elseif (isset($m['search_volume']) && $m['search_volume'] !== null) {
                $search_volume = intval($m['search_volume']);
            }

            $rows[] = array(
                'keyword' => $kw,
                'searchVolume' => $search_volume,
                'difficulty' => (isset($m['kd']) && $m['kd'] !== null) ? floatval($m['kd']) : null,
                'intent' => (string) (($i['intent'] ?? $m['intent'] ?? '')),
                'intent_probabilities' => $i['intent_probabilities'] ?? array(),
                'intent_source' => (string) ($i['intent_source'] ?? 'bulk_intent'),
                'intent_updated_at' => (string) ($i['intent_updated_at'] ?? current_time('mysql')),
                'competition' => (string) ($m['competition'] ?? ''),
                'traffic' => (isset($m['traffic']) && $m['traffic'] !== null) ? intval($m['traffic']) : null,
                'trafficPct' => (isset($m['traffic_pct']) && $m['traffic_pct'] !== null) ? floatval($m['traffic_pct']) : null,
                'position' => (isset($m['position']) && $m['position'] !== null) ? floatval($m['position']) : null,
                'cpc' => (isset($m['cpc']) && $m['cpc'] !== null) ? floatval($m['cpc']) : null,
                'url' => (string) ($m['url'] ?? ''),
                'item_types' => (isset($m['item_types']) && is_array($m['item_types'])) ? $m['item_types'] : array(),
                'source' => 'competitor_gap',
            );
        }

        $persist_result = $this->aysa_keyword_discovery_persist_keywords($project_id, $rows);
        $in_map = $this->aysa_keyword_discovery_existing_keywords_map($project_id, $keywords);

        wp_send_json_success(array(
            'project_id' => $project_id,
            'total_keywords' => count($keywords),
            'persisted' => $persist_result,
            'inKeywordResearch' => $in_map,
        ));
    }

    private function aysa_keyword_discovery_table() {
        global $wpdb;
        return $wpdb->prefix . 'aysa_keyword_metrics';
    }

    public function aysa_keyword_discovery_maybe_create_table() {
        $this->aysa_keyword_discovery_create_table();
    }

    private function aysa_keyword_discovery_create_table() {
        global $wpdb;
        $table = $this->aysa_keyword_discovery_table();
        $charset_collate = $wpdb->get_charset_collate();
        $sql = "CREATE TABLE {$table} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            project_id bigint(20) unsigned NOT NULL,
            keyword varchar(255) NOT NULL,
            search_volume int(11) DEFAULT NULL,
            difficulty decimal(6,2) DEFAULT NULL,
            intent varchar(64) DEFAULT NULL,
            intent_probabilities longtext NULL,
            intent_source varchar(32) DEFAULT NULL,
            intent_updated_at datetime DEFAULT NULL,
            competition varchar(64) DEFAULT NULL,
            traffic int(11) DEFAULT NULL,
            traffic_pct decimal(10,4) DEFAULT NULL,
            position decimal(10,4) DEFAULT NULL,
            cpc decimal(10,4) DEFAULT NULL,
            target_url text NULL,
            item_types text NULL,
            source varchar(32) DEFAULT 'suggested',
            remote_keyword_id bigint(20) unsigned DEFAULT NULL,
            updated_at datetime NOT NULL,
            created_at datetime NOT NULL,
            PRIMARY KEY  (id),
            UNIQUE KEY project_keyword (project_id, keyword),
            KEY project_updated (project_id, updated_at),
            KEY project_remote (project_id, remote_keyword_id)
        ) {$charset_collate};";
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta($sql);
    }

    private function aysa_keyword_discovery_status_key($project_id) {
        return 'aysa_keyword_discovery_refresh_status_' . intval($project_id);
    }

    private function aysa_keyword_discovery_lock_key($project_id) {
        return 'aysa_keyword_discovery_refresh_lock_' . intval($project_id);
    }

    private function aysa_keyword_discovery_snapshot_key($project_id) {
        return 'aysa_keyword_discovery_snapshot_' . intval($project_id);
    }

    private function aysa_keyword_discovery_get_status($project_id) {
        $status = get_option($this->aysa_keyword_discovery_status_key($project_id), array());
        if (!is_array($status)) {
            $status = array();
        }
        return array(
            'run_id' => (string) ($status['run_id'] ?? ''),
            'refresh_kind' => (string) ($status['refresh_kind'] ?? 'gsc_auto'),
            'status' => (string) ($status['status'] ?? 'idle'),
            'total' => intval($status['total'] ?? 0),
            'processed' => intval($status['processed'] ?? 0),
            'staged' => intval($status['staged'] ?? 0),
            'inserted' => intval($status['inserted'] ?? 0),
            'updated' => intval($status['updated'] ?? 0),
            'apply_to_wp' => !empty($status['apply_to_wp']),
            'last_error' => (string) ($status['last_error'] ?? ''),
            'metrics_notice' => (string) ($status['metrics_notice'] ?? ''),
            'notification_sent' => (string) ($status['notification_sent'] ?? '0'),
            'started_at' => intval($status['started_at'] ?? 0),
            'updated_at' => intval($status['updated_at'] ?? 0),
            'finished_at' => intval($status['finished_at'] ?? 0),
        );
    }

    private function aysa_keyword_discovery_save_status($project_id, $status) {
        if (!is_array($status)) {
            return;
        }
        update_option($this->aysa_keyword_discovery_status_key($project_id), $status, false);
    }

    private function aysa_keyword_discovery_build_pending_item($project_id, $refresh_kind = 'gsc_auto') {
        $project_id = intval($project_id);
        $refresh_kind = (string) $refresh_kind;
        $title = $refresh_kind === 'keyword_suggestions' ? 'Keyword suggestions refresh' : 'Keyword refresh';
        return array(
            'item_key' => 'keyword_refresh:' . $refresh_kind . ':' . $project_id,
            'item_type' => 'keyword_refresh',
            'post_id' => $project_id,
            'title' => $title,
        );
    }

    private function aysa_keyword_discovery_start_background_refresh($project_id, $apply_to_wp = true, $refresh_kind = 'gsc_auto') {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return new WP_Error('invalid_project_id', 'Invalid project id');
        }
        $refresh_kind = strtolower(trim((string) $refresh_kind));
        if (!in_array($refresh_kind, array('gsc_auto', 'keyword_suggestions'), true)) {
            $refresh_kind = 'gsc_auto';
        }

        $existing_status = $this->aysa_keyword_discovery_get_status($project_id);
        if (($existing_status['status'] ?? '') === 'running') {
            $updated_at = intval($existing_status['updated_at'] ?? 0);
            $started_at = intval($existing_status['started_at'] ?? 0);
            $freshness_anchor = max($updated_at, $started_at);
            if ($freshness_anchor > 0 && (time() - $freshness_anchor) > 180) {
                $existing_status['status'] = 'failed';
                $existing_status['last_error'] = 'Keyword refresh stalled. Please retry. If this persists, reconnect Google Search Console.';
                $existing_status['updated_at'] = time();
                $existing_status['finished_at'] = time();
                $this->aysa_keyword_discovery_save_status($project_id, $existing_status);
            } else {
                return array(
                    'started' => false,
                    'already_running' => true,
                    'status' => $existing_status,
                    'pending_item' => $this->aysa_keyword_discovery_build_pending_item($project_id, (string) ($existing_status['refresh_kind'] ?? $refresh_kind)),
                );
            }
        }

        $run_id = uniqid('kwd_', true);
        $now = time();
        $status = array(
            'run_id' => $run_id,
            'refresh_kind' => $refresh_kind,
            'status' => 'running',
            'total' => 0,
            'processed' => 0,
            'inserted' => 0,
            'updated' => 0,
            'staged' => 0,
            'apply_to_wp' => $apply_to_wp ? 1 : 0,
            'last_error' => '',
            'metrics_notice' => '',
            'notification_sent' => '0',
            'started_at' => $now,
            'updated_at' => $now,
            'finished_at' => 0,
        );
        $this->aysa_keyword_discovery_save_status($project_id, $status);
        $this->aysa_keyword_discovery_schedule_background_refresh($project_id, 1);

        return array(
            'started' => true,
            'already_running' => false,
            'status' => $status,
            'pending_item' => $this->aysa_keyword_discovery_build_pending_item($project_id, $refresh_kind),
        );
    }

    private function aysa_keyword_discovery_schedule_background_refresh($project_id, $delay_seconds = 1) {
        $timestamp = time() + max(1, intval($delay_seconds));
        wp_schedule_single_event($timestamp, 'aysa_keyword_discovery_refresh_background_event', array(intval($project_id)));
        if (function_exists('spawn_cron')) {
            spawn_cron();
        }
    }

    private function aysa_keyword_discovery_parse_numeric($value) {
        if ($value === null || $value === '') {
            return null;
        }
        if (is_numeric($value)) {
            return (float) $value;
        }
        $normalized = str_replace(',', '.', (string) $value);
        return is_numeric($normalized) ? (float) $normalized : null;
    }

    private function aysa_keyword_discovery_parse_item_types($value) {
        if (is_array($value)) {
            $items = array_values(array_filter(array_map('strval', $value), function($v) {
                return trim($v) !== '';
            }));
            return $items;
        }
        if (is_string($value) && trim($value) !== '') {
            $decoded = json_decode($value, true);
            if (is_array($decoded)) {
                return array_values(array_filter(array_map('strval', $decoded), function($v) {
                    return trim($v) !== '';
                }));
            }
            return array_values(array_filter(array_map('trim', explode(',', $value)), function($v) {
                return $v !== '';
            }));
        }
        return array();
    }

    private function aysa_keyword_discovery_collect_legacy_keywords($limit = 1000) {
        global $wpdb;
        $legacy_table = $wpdb->prefix . 'aysa_keywords';
        $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $legacy_table));
        if ($table_exists !== $legacy_table) {
            return array();
        }

        $limit = max(1, min(5000, intval($limit)));
        $rows = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT value AS keyword, MAX(volume) AS searchVolume
                 FROM {$legacy_table}
                 WHERE value IS NOT NULL AND TRIM(value) <> ''
                 GROUP BY value
                 ORDER BY MAX(update_at) DESC
                 LIMIT %d",
                $limit
            ),
            ARRAY_A
        );

        return is_array($rows) ? $rows : array();
    }

    private function aysa_keyword_discovery_persist_keywords($project_id, $rows) {
        global $wpdb;
        $table = $this->aysa_keyword_discovery_table();
        $project_id = intval($project_id);
        if ($project_id <= 0 || !is_array($rows)) {
            return array('inserted' => 0, 'updated' => 0, 'total' => 0);
        }

        $inserted = 0;
        $updated = 0;
        $now = current_time('mysql');

        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['keyword'] ?? $row['Keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }
            $search_volume = $this->aysa_keyword_discovery_parse_numeric($row['searchVolume'] ?? $row['search_volume'] ?? null);
            $difficulty = $this->aysa_keyword_discovery_parse_numeric($row['difficulty'] ?? $row['keyword_difficulty'] ?? null);
            $intent = trim((string) ($row['intent'] ?? $row['Intent'] ?? ''));
            $intent_probabilities = $this->aysa_keyword_discovery_normalize_intent_probabilities(
                $row['intent_probabilities'] ?? $row['intentProbabilities'] ?? (isset($row['search_intent_info']) ? $row['search_intent_info'] : array())
            );
            $intent_probabilities_json = !empty($intent_probabilities) ? wp_json_encode($intent_probabilities) : null;
            $intent_source = trim((string) ($row['intent_source'] ?? $row['intentSource'] ?? ''));
            $intent_updated_at = trim((string) ($row['intent_updated_at'] ?? $row['intentUpdatedAt'] ?? ''));
            if ($intent_updated_at === '' && ($intent !== '' || !empty($intent_probabilities))) {
                $intent_updated_at = $now;
            }
            $competition = trim((string) ($row['competition'] ?? $row['competition_level'] ?? ''));
            $traffic = $this->aysa_keyword_discovery_parse_numeric($row['traffic'] ?? $row['gsc_clicks'] ?? null);
            $traffic_pct = $this->aysa_keyword_discovery_parse_numeric($row['traffic_pct'] ?? $row['trafficPct'] ?? $row['trafficPercent'] ?? null);
            $position = $this->aysa_keyword_discovery_parse_numeric($row['position'] ?? $row['rank_group'] ?? null);
            $cpc = $this->aysa_keyword_discovery_parse_numeric($row['cpc'] ?? $row['cpc_usd'] ?? $row['cpcUsd'] ?? null);
            $target_url = trim((string) ($row['url'] ?? $row['target_url'] ?? $row['link'] ?? ''));
            $item_types_array = $this->aysa_keyword_discovery_parse_item_types($row['item_types'] ?? $row['serp_features'] ?? array());
            $item_types_json = !empty($item_types_array) ? wp_json_encode($item_types_array) : null;
            $remote_id = intval($row['id'] ?? $row['Id'] ?? 0);

            $existing_id = intval($wpdb->get_var($wpdb->prepare(
                "SELECT id FROM {$table} WHERE project_id = %d AND keyword = %s LIMIT 1",
                $project_id,
                $keyword
            )));

            $source = strtolower(trim((string) ($row['source'] ?? 'suggested')));
            if ($source === '') {
                $source = 'suggested';
            }
            if (!in_array($source, array('suggested', 'gsc', 'gsc_query', 'gsc_query_only', 'target', 'keyword_suggestion', 'related_keyword', 'association_seed', 'legacy_local_seed', 'competitor_gap'), true)) {
                $source = 'suggested';
            }

            $data = array(
                'project_id' => $project_id,
                'keyword' => $keyword,
                'search_volume' => ($search_volume === null ? null : intval(round($search_volume))),
                'difficulty' => ($difficulty === null ? null : round($difficulty, 2)),
                'intent' => ($intent !== '' ? $intent : null),
                'intent_probabilities' => $intent_probabilities_json,
                'intent_source' => ($intent_source !== '' ? $intent_source : null),
                'intent_updated_at' => ($intent_updated_at !== '' ? $intent_updated_at : null),
                'competition' => ($competition !== '' ? $competition : null),
                'traffic' => ($traffic === null ? null : intval(round($traffic))),
                'traffic_pct' => ($traffic_pct === null ? null : round($traffic_pct, 4)),
                'position' => ($position === null ? null : round($position, 4)),
                'cpc' => ($cpc === null ? null : round($cpc, 4)),
                'target_url' => ($target_url !== '' ? $target_url : null),
                'item_types' => $item_types_json,
                'source' => $source,
                'remote_keyword_id' => ($remote_id > 0 ? $remote_id : null),
                'updated_at' => $now,
            );

            if ($existing_id > 0) {
                $wpdb->update(
                    $table,
                    $data,
                    array('id' => $existing_id),
                    array('%d', '%s', '%d', '%f', '%s', '%s', '%s', '%s', '%s', '%d', '%f', '%f', '%f', '%s', '%s', '%s', '%d', '%s'),
                    array('%d')
                );
                $updated++;
            } else {
                $data['created_at'] = $now;
                $wpdb->insert(
                    $table,
                    $data,
                    array('%d', '%s', '%d', '%f', '%s', '%s', '%s', '%s', '%s', '%d', '%f', '%f', '%f', '%s', '%s', '%s', '%d', '%s', '%s')
                );
                $inserted++;
            }
        }

        return array(
            'inserted' => $inserted,
            'updated' => $updated,
            'total' => $inserted + $updated,
        );
    }

    private function aysa_keyword_discovery_clear_suggestions_rows($project_id) {
        global $wpdb;
        $table = $this->aysa_keyword_discovery_table();
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return 0;
        }
        return intval($wpdb->query($wpdb->prepare(
            "DELETE FROM {$table} WHERE project_id = %d AND source IN (%s, %s)",
            $project_id,
            'keyword_suggestion',
            'related_keyword'
        )));
    }

    private function aysa_keyword_discovery_clear_gsc_rows($project_id) {
        global $wpdb;
        $table = $this->aysa_keyword_discovery_table();
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return 0;
        }
        return intval($wpdb->query($wpdb->prepare(
            "DELETE FROM {$table} WHERE project_id = %d AND source IN (%s, %s, %s)",
            $project_id,
            'gsc',
            'gsc_query',
            'gsc_query_only'
        )));
    }

    public function handle_keyword_discovery_local_keywords() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_keyword_cache_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : intval(get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID');
            return;
        }

        $source_mode = strtolower(trim((string) ($_POST['source_mode'] ?? 'gsc_auto')));
        $keywords = $this->aysa_keyword_discovery_get_local_keywords_payload($project_id, $source_mode);

        $status = $this->aysa_keyword_discovery_get_status($project_id);
        $snapshot = get_option($this->aysa_keyword_discovery_snapshot_key($project_id), array());
        wp_send_json_success(array(
            'keywords' => $keywords,
            'count' => count($keywords),
            'status' => $status,
            'snapshot' => array(
                'fetched_at' => intval($snapshot['fetched_at'] ?? 0),
                'count' => intval($snapshot['count'] ?? 0),
                'run_id' => (string) ($snapshot['run_id'] ?? ''),
            ),
        ));
    }

    private function aysa_keyword_discovery_get_local_keywords_payload($project_id, $source_mode = 'gsc_auto') {
        global $wpdb;
        $table = $this->aysa_keyword_discovery_table();
        $project_id = intval($project_id);
        $source_mode = strtolower(trim((string) $source_mode));
        if ($project_id <= 0) {
            return array();
        }

        $where_sql = "project_id = %d";
        $prepare_args = array($project_id);
        if ($source_mode === 'keyword_suggestions') {
            $where_sql .= " AND source IN (%s, %s)";
            $prepare_args[] = 'keyword_suggestion';
            $prepare_args[] = 'related_keyword';
        } elseif ($source_mode === 'gsc_auto') {
            $where_sql .= " AND source IN (%s, %s, %s)";
            $prepare_args[] = 'gsc';
            $prepare_args[] = 'gsc_query';
            $prepare_args[] = 'gsc_query_only';
        } else {
            $where_sql .= " AND (source IS NULL OR source NOT IN (%s, %s))";
            $prepare_args[] = 'keyword_suggestion';
            $prepare_args[] = 'related_keyword';
        }

        $query = "SELECT keyword, search_volume, difficulty, intent, intent_probabilities, intent_source, intent_updated_at, competition, traffic, traffic_pct, position, cpc, target_url, item_types, remote_keyword_id, source, updated_at
             FROM {$table}
             WHERE {$where_sql}
             ORDER BY updated_at DESC, id DESC
             LIMIT 5000";
        $rows = $wpdb->get_results($wpdb->prepare($query, $prepare_args), ARRAY_A);
        if (!is_array($rows)) {
            return array();
        }

        $keywords = array();
        foreach ($rows as $row) {
            $item_types = $this->aysa_keyword_discovery_parse_item_types($row['item_types'] ?? array());
            $intent_probabilities = $this->aysa_keyword_discovery_normalize_intent_probabilities($row['intent_probabilities'] ?? array());
            $keywords[] = array(
                'keyword' => (string) ($row['keyword'] ?? ''),
                'searchVolume' => ($row['search_volume'] !== null ? intval($row['search_volume']) : 'N/A'),
                'difficulty' => ($row['difficulty'] !== null ? (float) $row['difficulty'] : 'N/A'),
                'intent' => ((string) ($row['intent'] ?? '') !== '' ? (string) $row['intent'] : 'N/A'),
                'intentProbabilities' => $intent_probabilities,
                'intentSource' => ((string) ($row['intent_source'] ?? '') !== '' ? (string) $row['intent_source'] : ''),
                'intentUpdatedAt' => ((string) ($row['intent_updated_at'] ?? '') !== '' ? (string) $row['intent_updated_at'] : ''),
                'competition' => ((string) ($row['competition'] ?? '') !== '' ? (string) $row['competition'] : 'N/A'),
                'traffic' => ($row['traffic'] !== null ? intval($row['traffic']) : 'N/A'),
                'trafficPct' => ($row['traffic_pct'] !== null ? floatval($row['traffic_pct']) : 'N/A'),
                'position' => ($row['position'] !== null ? floatval($row['position']) : 'N/A'),
                'cpc' => ($row['cpc'] !== null ? floatval($row['cpc']) : 'N/A'),
                'url' => ((string) ($row['target_url'] ?? '') !== '' ? (string) $row['target_url'] : 'N/A'),
                'itemTypes' => $item_types,
                'id' => intval($row['remote_keyword_id'] ?? 0),
                'projectId' => $project_id,
                'source' => (string) ($row['source'] ?? 'suggested'),
                'updatedAt' => (string) ($row['updated_at'] ?? ''),
                'createdDate' => (string) ($row['updated_at'] ?? ''),
            );
        }

        return $keywords;
    }

    public function handle_keyword_discovery_intent_batch() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : intval(get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID');
            return;
        }

        $raw_keywords = $_POST['keywords'] ?? array();
        if (is_string($raw_keywords)) {
            $decoded = json_decode(wp_unslash($raw_keywords), true);
            $raw_keywords = is_array($decoded) ? $decoded : array();
        } elseif (is_array($raw_keywords)) {
            $raw_keywords = array_map('wp_unslash', $raw_keywords);
        } else {
            $raw_keywords = array();
        }

        $keywords = array();
        foreach ($raw_keywords as $kw) {
            $text = trim(sanitize_text_field((string) $kw));
            if ($text !== '') {
                $keywords[] = $text;
            }
        }
        $keywords = array_values(array_unique($keywords));
        if (empty($keywords)) {
            wp_send_json_success(array('intents' => array()));
            return;
        }

        $context = $this->aysa_monitoring_resolve_context('');
        $intent_map = $this->aysa_keyword_discovery_fetch_keyword_intent_bulk($project_id, $keywords, $context);
        if (!is_array($intent_map)) {
            $intent_map = array();
        }

        if (!empty($intent_map)) {
            global $wpdb;
            $table = $this->aysa_keyword_discovery_table();
            $now = current_time('mysql');
            $keyword_by_norm = array();
            foreach ($keywords as $kw) {
                $keyword_by_norm[strtolower(trim((string) $kw))] = (string) $kw;
            }

            foreach ($intent_map as $norm_keyword => $intent_row) {
                if (!is_array($intent_row)) {
                    continue;
                }
                $norm_keyword = strtolower(trim((string) $norm_keyword));
                if ($norm_keyword === '' || !isset($keyword_by_norm[$norm_keyword])) {
                    continue;
                }

                $intent = trim((string) ($intent_row['intent'] ?? ''));
                $intent_probs = $this->aysa_keyword_discovery_normalize_intent_probabilities(
                    $intent_row['intent_probabilities'] ?? array()
                );
                $intent_probs_json = !empty($intent_probs) ? wp_json_encode($intent_probs) : null;
                $intent_source = trim((string) ($intent_row['intent_source'] ?? 'bulk_intent'));
                $intent_updated_at = trim((string) ($intent_row['intent_updated_at'] ?? $now));

                if ($intent === '' && empty($intent_probs)) {
                    continue;
                }

                $wpdb->update(
                    $table,
                    array(
                        'intent' => ($intent !== '' ? $intent : null),
                        'intent_probabilities' => $intent_probs_json,
                        'intent_source' => ($intent_source !== '' ? $intent_source : 'bulk_intent'),
                        'intent_updated_at' => ($intent_updated_at !== '' ? $intent_updated_at : $now),
                        'updated_at' => $now,
                    ),
                    array(
                        'project_id' => $project_id,
                        'keyword' => $keyword_by_norm[$norm_keyword],
                    ),
                    array('%s', '%s', '%s', '%s', '%s'),
                    array('%d', '%s')
                );
            }
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_keyword_discovery_intent_batch',
            'keyword_discovery_intent_batch',
            array(
                'module' => 'keyword_discovery',
                'keywords_count' => count($keywords),
                'intents_count' => count($intent_map),
            ),
            30,
            '/v1/dataforseo/keywords/intent-bulk'
        );

        wp_send_json_success(array('intents' => $intent_map));
    }

    private function aysa_keyword_strategy_option_key($project_id, $user_id) {
        return 'aysa_keyword_strategy_' . intval($project_id) . '_' . intval($user_id);
    }

    private function aysa_keyword_strategy_normalize_keyword($keyword) {
        $value = strtolower(trim((string) $keyword));
        if ($value === '') {
            return '';
        }
        if (function_exists('remove_accents')) {
            $value = remove_accents($value);
        }
        $value = preg_replace('/\s+/', ' ', $value);
        return is_string($value) ? trim($value) : '';
    }

    private function aysa_keyword_strategy_extract_seed_data($user_id) {
        $option_name = 'aysa_wizard_competition_' . intval($user_id);
        $saved = get_option($option_name, array());
        if (!is_array($saved)) {
            $saved = array();
        }

        $seed_keywords = array();
        $competitors = array();

        foreach ($saved as $key => $value) {
            $val = trim((string) $value);
            if ($val === '') {
                continue;
            }
            if (preg_match('/^keyword\d+$/', (string) $key)) {
                $seed_keywords[] = $val;
                continue;
            }
            if (preg_match('/^competitor\d+$/', (string) $key)) {
                $competitors[] = $val;
                continue;
            }
        }

        $seed_keywords = array_values(array_unique(array_filter($seed_keywords, function($kw) {
            return trim((string) $kw) !== '';
        })));
        $competitors = array_values(array_unique(array_filter($competitors, function($url) {
            return trim((string) $url) !== '';
        })));

        return array(
            'seed_keywords' => $seed_keywords,
            'competitors' => $competitors,
            'raw' => $saved,
        );
    }

    private function aysa_keyword_strategy_get_context($user_id) {
        $ctx = array(
            'targeted_country' => 'Romania',
            'targeted_region' => '',
            'targeted_city' => '',
            'primary_language' => 'Romanian',
            'website_type' => '',
            'industry' => '',
        );

        $seo_settings = get_option('aysa_wizard_seo-settings_' . intval($user_id), array());
        if (is_array($seo_settings)) {
            $ctx['targeted_country'] = trim((string) ($seo_settings['targeted_country'] ?? $ctx['targeted_country']));
            $ctx['targeted_region'] = trim((string) ($seo_settings['targeted_region'] ?? ''));
            $ctx['targeted_city'] = trim((string) ($seo_settings['targeted_city'] ?? ''));
            $ctx['primary_language'] = trim((string) ($seo_settings['primary_language'] ?? $ctx['primary_language']));
        }

        $general = get_option('aysa_wizard_general_' . intval($user_id), array());
        if (is_array($general)) {
            $ctx['website_type'] = trim((string) ($general['website_type'] ?? ''));
            $ctx['industry'] = trim((string) ($general['industry'] ?? ''));
        }

        return $ctx;
    }

    private function aysa_keyword_strategy_get_association_map($project_id) {
        $rows = $this->get_keyword_discovery_local_associations($project_id);

        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }

        try {
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $remote_raw = $auth_manager->make_authenticated_request(
                'api/Association?projectId=' . intval($project_id) . '&pageSize=1000&pageNumber=1&sortOrder=asc',
                'GET'
            );
            $remote_rows = $this->normalize_aysa_collection_response($remote_raw);
            if (is_array($remote_rows) && !empty($remote_rows)) {
                $rows = $this->merge_keyword_discovery_associations($remote_rows, $rows);
            }
        } catch (Exception $e) {
            // Keep local fallback only.
        }

        $map = array();
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = $this->aysa_keyword_strategy_normalize_keyword($row['keyword'] ?? '');
            if ($keyword === '') {
                continue;
            }
            if (!isset($map[$keyword])) {
                $map[$keyword] = array();
            }
            $map[$keyword][] = array(
                'associatedOn' => (string) ($row['associatedOn'] ?? $row['associated_on'] ?? ''),
                'idIntern' => intval($row['idIntern'] ?? $row['id_intern'] ?? 0),
                'value' => (string) ($row['value'] ?? ''),
            );
        }

        return $map;
    }

    private function aysa_keyword_strategy_collect_local_keyword_rows($project_id) {
        global $wpdb;
        $table = $this->aysa_keyword_discovery_table();
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT keyword, search_volume, difficulty, intent, competition, traffic, traffic_pct, position, cpc, target_url, updated_at
             FROM {$table}
             WHERE project_id = %d
             ORDER BY updated_at DESC, id DESC
             LIMIT 5000",
            $project_id
        ), ARRAY_A);
        return is_array($rows) ? $rows : array();
    }

    public function handle_content_strategy_generate() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_content_strategy_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : intval(get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $strategy_type = sanitize_key((string) ($_POST['strategy_type'] ?? ''));
        if ($strategy_type === '') {
            wp_send_json_error('Missing strategy type');
            return;
        }

        $raw_payload = $_POST['payload'] ?? array();
        if (is_string($raw_payload)) {
            $decoded = json_decode(wp_unslash($raw_payload), true);
            $raw_payload = is_array($decoded) ? $decoded : array();
        } elseif (is_array($raw_payload)) {
            $raw_payload = array_map('wp_unslash', $raw_payload);
        } else {
            $raw_payload = array();
        }

        $setup_user_id = intval($this->aysa_get_authenticated_external_user_id());
        if ($setup_user_id <= 0) {
            $setup_user_id = get_current_user_id();
        }

        $context_bundle = $this->aysa_collect_content_strategy_context_bundle($project_id, $setup_user_id);
        $result = $this->aysa_broker_request('POST', '/v1/content-strategy/generate', array(
            'project_id' => $project_id,
            'strategy_type' => $strategy_type,
            'raw_context' => $context_bundle,
            'payload_overrides' => $raw_payload,
        ), 120);
        if (is_wp_error($result)) {
            wp_send_json_error($result->get_error_message());
            return;
        }

        if (!is_array($result) || empty($result['ok'])) {
            wp_send_json_error(is_array($result) ? ($result['error'] ?? 'content_strategy_generation_failed') : 'content_strategy_generation_failed');
            return;
        }

        wp_send_json_success(array(
            'strategy_type' => (string) ($result['strategy_type'] ?? $strategy_type),
            'items' => (isset($result['items']) && is_array($result['items'])) ? $result['items'] : array(),
            'model' => (string) ($result['model'] ?? ''),
            'usage' => (isset($result['usage']) && is_array($result['usage'])) ? $result['usage'] : array(),
            'warnings' => (isset($result['warnings']) && is_array($result['warnings'])) ? $result['warnings'] : array(),
        ));
    }

    private function aysa_adjustments_generate_content_strategy_items($project_id, $strategy_type, $payload = array()) {
        $project_id = intval($project_id);
        $strategy_type = sanitize_key((string) $strategy_type);
        $payload = is_array($payload) ? $payload : array();
        if ($project_id <= 0 || $strategy_type === '') {
            return new WP_Error('invalid_content_strategy_request', 'Invalid content strategy request.');
        }

        $setup_user_id = intval($this->aysa_get_authenticated_external_user_id());
        if ($setup_user_id <= 0) {
            $setup_user_id = get_current_user_id();
        }

        $result = $this->aysa_broker_request('POST', '/v1/content-strategy/generate', array(
            'project_id' => $project_id,
            'strategy_type' => $strategy_type,
            'raw_context' => $this->aysa_collect_content_strategy_context_bundle($project_id, $setup_user_id),
            'payload_overrides' => $payload,
        ), 120);
        if (is_wp_error($result)) {
            return $result;
        }

        if (!is_array($result) || empty($result['ok'])) {
            return new WP_Error(
                'content_strategy_generation_failed',
                is_array($result) ? (string) ($result['error'] ?? 'content_strategy_generation_failed') : 'content_strategy_generation_failed'
            );
        }

        return $result;
    }

    private function aysa_adjustments_prepare_content_strategy_payload($project_id, $setup_user_id, $strategy_type) {
        $project_id = intval($project_id);
        $setup_user_id = intval($setup_user_id);
        $strategy_type = sanitize_key((string) $strategy_type);

        return array(
            'project_id' => $project_id,
            'strategy_type' => $strategy_type,
            'raw_context' => $this->aysa_collect_content_strategy_context_bundle($project_id, $setup_user_id),
        );

        $local_setup = $this->aysa_get_local_setup_state_for_user($setup_user_id, $project_id);
        $general = isset($local_setup['general']) && is_array($local_setup['general']) ? $local_setup['general'] : array();
        $seo = isset($local_setup['seo-settings']) && is_array($local_setup['seo-settings']) ? $local_setup['seo-settings'] : array();
        $competition = isset($local_setup['competition']) && is_array($local_setup['competition']) ? $local_setup['competition'] : array();
        $objectives = isset($local_setup['objectives']['objectives']) && is_array($local_setup['objectives']['objectives'])
            ? array_values($local_setup['objectives']['objectives'])
            : array();

        $mappingPayload = $this->aysa_adjustments_extract_keyword_mapping_cluster_payload($project_id);

        $research = $this->aysa_adjustments_get_research_snapshot($project_id, $setup_user_id);
        $modules = isset($research['modules']) && is_array($research['modules']) ? $research['modules'] : array();
        $keyword_rows = array_values(array_filter(array_merge(
            isset($modules['keyword_research']['gsc_rows']) && is_array($modules['keyword_research']['gsc_rows']) ? $modules['keyword_research']['gsc_rows'] : array(),
            isset($modules['keyword_research']['suggestion_rows']) && is_array($modules['keyword_research']['suggestion_rows']) ? $modules['keyword_research']['suggestion_rows'] : array(),
            isset($modules['competitor_gap']['rows']) && is_array($modules['competitor_gap']['rows']) ? $modules['competitor_gap']['rows'] : array()
        ), static function($row) {
            return is_array($row) && trim((string) ($row['keyword'] ?? '')) !== '';
        }));

        $all_keywords = array();
        foreach ($keyword_rows as $row) {
            $keyword = sanitize_text_field((string) ($row['keyword'] ?? ''));
            if ($keyword === '' || in_array($keyword, $all_keywords, true)) {
                continue;
            }
            $all_keywords[] = $keyword;
            if (count($all_keywords) >= 40) {
                break;
            }
        }

        if (is_array($mappingPayload) && !empty($mappingPayload['cluster_groups']) && is_array($mappingPayload['cluster_groups'])) {
            $preferred_keywords = array();
            foreach ($mappingPayload['cluster_groups'] as $cluster_group) {
                if (!is_array($cluster_group)) {
                    continue;
                }
                $selection_state = sanitize_key((string) ($cluster_group['selection_state'] ?? 'review'));
                if ($selection_state === 'excluded') {
                    continue;
                }
                $business_fit_score = (float) ($cluster_group['business_fit_score'] ?? 0.0);
                if ($business_fit_score < 0.55) {
                    continue;
                }

                $primary = sanitize_text_field((string) ($cluster_group['primary_keyword'] ?? ''));
                if ($primary !== '' && !in_array($primary, $preferred_keywords, true)) {
                    $preferred_keywords[] = $primary;
                }

                foreach ((array) ($cluster_group['target_keywords'] ?? array()) as $keyword) {
                    $keyword = sanitize_text_field((string) $keyword);
                    if ($keyword === '' || in_array($keyword, $preferred_keywords, true)) {
                        continue;
                    }
                    $preferred_keywords[] = $keyword;
                }
            }

            if (!empty($preferred_keywords)) {
                $all_keywords = array_slice($preferred_keywords, 0, 40);
            }
        }

        if (empty($all_keywords)) {
            for ($i = 1; $i <= 5; $i++) {
                $keyword = sanitize_text_field((string) ($competition['keyword' . $i] ?? ''));
                if ($keyword === '' || in_array($keyword, $all_keywords, true)) {
                    continue;
                }
                $all_keywords[] = $keyword;
            }
        }

        global $wpdb;
        $strategy_table = $wpdb->prefix . 'aysa_content_strategies';
        $existing_strategy_keywords_raw = $wpdb->get_col($wpdb->prepare(
            "SELECT keywords FROM {$strategy_table} WHERE project_id = %d AND strategy_type = %s ORDER BY id DESC LIMIT 80",
            $project_id,
            $strategy_type
        ));
        $existing_strategy_keywords = array();
        foreach ((array) $existing_strategy_keywords_raw as $raw_keywords) {
            foreach ($this->aysa_adjustments_normalize_content_keywords($raw_keywords) as $existing_keyword) {
                if (!in_array($existing_keyword, $existing_strategy_keywords, true)) {
                    $existing_strategy_keywords[] = $existing_keyword;
                }
            }
        }

        $keywords = array();
        foreach ($all_keywords as $keyword) {
            $normalized_keyword = $this->aysa_adjustments_normalize_content_keywords(array($keyword));
            $normalized_keyword = !empty($normalized_keyword) ? $normalized_keyword[0] : '';
            if ($normalized_keyword !== '' && in_array($normalized_keyword, $existing_strategy_keywords, true)) {
                continue;
            }
            $keywords[] = $keyword;
            if (count($keywords) >= 12) {
                break;
            }
        }

        if (count($keywords) < 6) {
            foreach ($all_keywords as $keyword) {
                if (in_array($keyword, $keywords, true)) {
                    continue;
                }
                $keywords[] = $keyword;
                if (count($keywords) >= 12) {
                    break;
                }
            }
        }

        $competitors = $this->aysa_adjustments_extract_competitor_domains_from_setup_state($competition, $project_id, $setup_user_id);
        if (empty($competitors) && !empty($modules['competitor_identification']['rows']) && is_array($modules['competitor_identification']['rows'])) {
            foreach ($modules['competitor_identification']['rows'] as $row) {
                $domain = sanitize_text_field((string) ($row['domain'] ?? ''));
                if ($domain === '' || in_array($domain, $competitors, true)) {
                    continue;
                }
                $competitors[] = $domain;
                if (count($competitors) >= 5) {
                    break;
                }
            }
        }

        $missing_rows = isset($modules['missing_pages']['rows']) && is_array($modules['missing_pages']['rows']) ? $modules['missing_pages']['rows'] : array();
        $primary_keyword = !empty($keywords) ? $keywords[0] : sanitize_text_field((string) ($general['company_name'] ?? ''));
        if (!empty($missing_rows)) {
            $base_keyword_pool = $this->aysa_adjustments_normalize_content_keywords(array_merge(
                $keywords,
                array_filter(array(
                    sanitize_text_field((string) ($general['company_name'] ?? '')),
                    sanitize_text_field((string) ($general['industry'] ?? '')),
                    sanitize_text_field((string) ($seo['targeted_city'] ?? '')),
                    sanitize_text_field((string) ($seo['targeted_country'] ?? '')),
                ))
            ));
            foreach ($missing_rows as $missing_row) {
                $candidate_keyword = sanitize_text_field((string) ($missing_row['keyword'] ?? ''));
                if ($candidate_keyword === '') {
                    continue;
                }
                if ($this->aysa_adjustments_is_relevant_content_strategy_item($candidate_keyword, array($candidate_keyword), $base_keyword_pool)) {
                    $primary_keyword = $candidate_keyword;
                    break;
                }
            }
        }

        $payload = array(
            'brand_name' => sanitize_text_field((string) ($general['brand_name'] ?? $general['company_name'] ?? get_bloginfo('name'))),
            'business_name' => sanitize_text_field((string) ($general['company_name'] ?? get_bloginfo('name'))),
            'website' => esc_url_raw((string) ($general['website'] ?? home_url('/'))),
            'industry' => sanitize_text_field((string) ($general['industry'] ?? 'florist')),
            'city' => sanitize_text_field((string) ($seo['targeted_city'] ?? 'Bragadiru')),
            'country' => sanitize_text_field((string) ($seo['targeted_country'] ?? 'Romania')),
            'language' => sanitize_text_field((string) ($seo['primary_language'] ?? 'Romanian')),
            'primary_keyword' => $primary_keyword,
            'cluster_keywords' => $keywords,
            'target_keywords' => $keywords,
            'seed_keywords' => $keywords,
            'competitors' => $competitors,
            'objectives' => $objectives,
            'current_month' => gmdate('M Y'),
        );

        if (is_array($mappingPayload) && !empty($mappingPayload['keywords'])) {
            $payload['primary_keyword'] = sanitize_text_field((string) ($mappingPayload['primary_keyword'] ?? $payload['primary_keyword']));
            $payload['cluster_keywords'] = array_values(array_filter(array_map('sanitize_text_field', (array) ($mappingPayload['keywords'] ?? array()))));
            $payload['target_keywords'] = $payload['cluster_keywords'];
            $payload['seed_keywords'] = $payload['cluster_keywords'];
            $payload['cluster_groups'] = array_values($mappingPayload['cluster_groups'] ?? array());
            $payload['cluster_count'] = intval($mappingPayload['cluster_count'] ?? count($payload['cluster_groups']));
            $payload['gap_keywords'] = array_values(array_filter(array_map('sanitize_text_field', (array) ($mappingPayload['gap_keywords'] ?? array()))));
            $payload['mapped_keywords'] = array_values(array_filter(array_map('sanitize_text_field', (array) ($mappingPayload['mapped_keywords'] ?? array()))));
            $payload['not_mapped_keywords'] = array_values(array_filter(array_map('sanitize_text_field', (array) ($mappingPayload['not_mapped_keywords'] ?? array()))));
            $payload['transactional_keywords'] = array_values(array_filter(array_map('sanitize_text_field', (array) ($mappingPayload['transactional_keywords'] ?? array()))));
            $payload['informational_keywords'] = array_values(array_filter(array_map('sanitize_text_field', (array) ($mappingPayload['informational_keywords'] ?? array()))));
            $payload['local_keywords'] = array_values(array_filter(array_map('sanitize_text_field', (array) ($mappingPayload['local_keywords'] ?? array()))));

            if (!empty($payload['cluster_groups'][0]) && is_array($payload['cluster_groups'][0])) {
                $payload['cluster_id'] = sanitize_text_field((string) ($payload['cluster_groups'][0]['cluster_id'] ?? ''));
                $payload['cluster_name'] = sanitize_text_field((string) ($payload['cluster_groups'][0]['cluster_name'] ?? ''));
            }

            switch ($strategy_type) {
                case 'intent_layering':
                case 'evergreen_fresh_mix':
                case 'customer_journey':
                case 'authority_building':
                case 'persona_based':
                    if (!empty($payload['transactional_keywords'])) {
                        $payload['target_keywords'] = array_slice(array_values(array_unique(array_merge(
                            $payload['transactional_keywords'],
                            array_slice($payload['informational_keywords'] ?? array(), 0, 8)
                        ))), 0, 12);
                    }
                    break;
                case 'seasonal_event':
                case 'serp_features':
                    if (!empty($payload['informational_keywords'])) {
                        $payload['target_keywords'] = array_slice(array_values(array_unique(array_merge(
                            $payload['informational_keywords'],
                            array_slice($payload['transactional_keywords'] ?? array(), 0, 6)
                        ))), 0, 12);
                    }
                    break;
                case 'local_seo':
                    if (!empty($payload['local_keywords'])) {
                        $payload['target_keywords'] = array_slice(array_values(array_unique(array_merge(
                            $payload['local_keywords'],
                            array_slice($payload['transactional_keywords'] ?? array(), 0, 6)
                        ))), 0, 12);
                    } elseif (!empty($payload['transactional_keywords'])) {
                        $payload['target_keywords'] = array_slice($payload['transactional_keywords'], 0, 12);
                    }
                    break;
            }
        }

        if ($strategy_type === 'persona_based') {
            $personaClusterGroups = $this->aysa_adjustments_prepare_persona_cluster_groups((array) ($payload['cluster_groups'] ?? array()));
            if (!empty($personaClusterGroups)) {
                $payload['cluster_groups'] = $personaClusterGroups;
                $payload['cluster_count'] = count($personaClusterGroups);
                $payload['cluster_id'] = sanitize_text_field((string) ($personaClusterGroups[0]['cluster_id'] ?? ''));
                $payload['cluster_name'] = sanitize_text_field((string) ($personaClusterGroups[0]['cluster_name'] ?? ''));

                $personaKeywords = array();
                foreach ($personaClusterGroups as $personaGroup) {
                    foreach ((array) ($personaGroup['target_keywords'] ?? array()) as $personaKeyword) {
                        $personaKeyword = sanitize_text_field((string) $personaKeyword);
                        if ($personaKeyword !== '' && !in_array($personaKeyword, $personaKeywords, true)) {
                            $personaKeywords[] = $personaKeyword;
                        }
                    }
                }
                if (!empty($personaKeywords)) {
                    $payload['target_keywords'] = array_slice($personaKeywords, 0, 18);
                    $payload['cluster_keywords'] = $payload['target_keywords'];
                }
            }

            $payload['persona_context'] = array(
                'business_type' => sanitize_text_field((string) ($payload['industry'] ?? '')),
                'primary_objective' => !empty($objectives) ? sanitize_text_field((string) $objectives[0]) : 'sales',
                'website_type' => sanitize_text_field((string) ($general['website_type'] ?? '')),
                'company_info' => sanitize_text_field((string) ($general['company_info'] ?? '')),
                'city' => sanitize_text_field((string) ($payload['city'] ?? '')),
                'country' => sanitize_text_field((string) ($payload['country'] ?? '')),
            );
            $payload['persona_scenarios'] = $this->aysa_adjustments_build_persona_scenarios((array) ($payload['cluster_groups'] ?? array()), $payload);

            $personaLabels = array_values(array_unique(array_filter(array_map(static function ($row) {
                return is_array($row) ? trim((string) ($row['persona'] ?? '')) : '';
            }, (array) $payload['persona_scenarios']))));

            $payload['target_persona'] = !empty($personaLabels)
                ? sanitize_text_field(implode(' | ', array_slice($personaLabels, 0, 3)))
                : (!empty($objectives)
                    ? sanitize_text_field(implode(', ', array_slice($objectives, 0, 3)))
                    : sanitize_text_field((string) ($general['company_info'] ?? '')));
        }

        if ($strategy_type === 'competitor_derived_titles') {
            $competitorClusterGroups = $this->aysa_adjustments_prepare_competitor_cluster_groups((array) ($payload['cluster_groups'] ?? array()));
            if (!empty($competitorClusterGroups)) {
                $payload['cluster_groups'] = $competitorClusterGroups;
                $payload['cluster_count'] = count($competitorClusterGroups);
                $payload['cluster_id'] = sanitize_text_field((string) ($competitorClusterGroups[0]['cluster_id'] ?? ''));
                $payload['cluster_name'] = sanitize_text_field((string) ($competitorClusterGroups[0]['cluster_name'] ?? ''));

                $competitorKeywords = array();
                foreach ($competitorClusterGroups as $competitorGroup) {
                    foreach ((array) ($competitorGroup['target_keywords'] ?? array()) as $competitorKeyword) {
                        $competitorKeyword = sanitize_text_field((string) $competitorKeyword);
                        if ($competitorKeyword !== '' && !in_array($competitorKeyword, $competitorKeywords, true)) {
                            $competitorKeywords[] = $competitorKeyword;
                        }
                    }
                }
                if (!empty($payload['gap_keywords'])) {
                    $competitorKeywords = array_values(array_unique(array_merge(
                        array_slice((array) $payload['gap_keywords'], 0, 18),
                        $competitorKeywords
                    )));
                }
                if (!empty($competitorKeywords)) {
                    $payload['target_keywords'] = array_slice($competitorKeywords, 0, 18);
                    $payload['cluster_keywords'] = $payload['target_keywords'];
                }
            }

            $payload['competitor_domains'] = array_values(array_filter(array_map('sanitize_text_field', (array) $competitors)));
            $freshCompetitorDiscovery = $this->aysa_adjustments_get_competitor_fresh_content($project_id, (array) ($payload['competitor_domains'] ?? array()));
            $payload['competitor_fresh_content'] = $freshCompetitorDiscovery;
            $payload['competitor_title_signals'] = $this->aysa_adjustments_build_competitor_title_signals(
                $project_id,
                (array) ($payload['cluster_groups'] ?? array()),
                $payload,
                (array) ($payload['competitor_domains'] ?? array())
            );

            $signalTitles = array();
            $freshTitles = array();
            foreach ((array) ($freshCompetitorDiscovery['rows'] ?? array()) as $freshRow) {
                if (!is_array($freshRow)) {
                    continue;
                }
                $freshTitle = sanitize_text_field((string) ($freshRow['title'] ?? ''));
                if ($freshTitle !== '' && !in_array($freshTitle, $freshTitles, true)) {
                    $freshTitles[] = $freshTitle;
                }
            }
            foreach ((array) $payload['competitor_title_signals'] as $signalRow) {
                if (!is_array($signalRow)) {
                    continue;
                }
                $signalTitle = sanitize_text_field((string) ($signalRow['competitor_title'] ?? ''));
                if ($signalTitle !== '' && !in_array($signalTitle, $signalTitles, true)) {
                    $signalTitles[] = $signalTitle;
                }
            }
            $payload['competitor_titles'] = array_slice(array_values(array_unique(array_merge($freshTitles, $signalTitles))), 0, 18);
            if (empty($payload['competitor_titles']) && !empty($signalTitles)) {
                $payload['competitor_titles'] = array_slice($signalTitles, 0, 18);
            }

            $payload['competitor_analysis_context'] = $this->aysa_adjustments_build_competitor_analysis_context(
                (array) ($payload['cluster_groups'] ?? array()),
                $payload,
                (array) ($payload['competitor_title_signals'] ?? array()),
                $freshCompetitorDiscovery
            );
        }

        if ($strategy_type === 'local_seo') {
            $localClusterGroups = $this->aysa_adjustments_prepare_local_seo_cluster_groups((array) ($payload['cluster_groups'] ?? array()));
            if (!empty($localClusterGroups)) {
                $payload['cluster_groups'] = $localClusterGroups;
                $payload['cluster_count'] = count($localClusterGroups);
                $payload['cluster_id'] = sanitize_text_field((string) ($localClusterGroups[0]['cluster_id'] ?? ''));
                $payload['cluster_name'] = sanitize_text_field((string) ($localClusterGroups[0]['cluster_name'] ?? ''));

                $localKeywords = array();
                foreach ($localClusterGroups as $localGroup) {
                    foreach ((array) ($localGroup['target_keywords'] ?? array()) as $localKeyword) {
                        $localKeyword = sanitize_text_field((string) $localKeyword);
                        if ($localKeyword !== '' && !in_array($localKeyword, $localKeywords, true)) {
                            $localKeywords[] = $localKeyword;
                        }
                    }
                }
                if (!empty($localKeywords)) {
                    $payload['target_keywords'] = array_slice($localKeywords, 0, 18);
                    $payload['cluster_keywords'] = $payload['target_keywords'];
                }
            }

            $payload['neighborhoods'] = array_values(array_filter(array(
                sanitize_text_field((string) ($seo['targeted_region'] ?? '')),
                sanitize_text_field((string) ($seo['targeted_city'] ?? '')),
            )));
            $payload['local_context'] = $this->aysa_adjustments_build_local_context((array) ($payload['cluster_groups'] ?? array()), array_merge($payload, array(
                'target_region' => sanitize_text_field((string) ($seo['targeted_region'] ?? '')),
            )));
        }

        if ($strategy_type === 'authority_building') {
            $authorityClusterGroups = $this->aysa_adjustments_prepare_authority_cluster_groups((array) ($payload['cluster_groups'] ?? array()));
            if (!empty($authorityClusterGroups)) {
                $payload['cluster_groups'] = $authorityClusterGroups;
                $payload['cluster_count'] = count($authorityClusterGroups);
                $payload['cluster_id'] = sanitize_text_field((string) ($authorityClusterGroups[0]['cluster_id'] ?? ''));
                $payload['cluster_name'] = sanitize_text_field((string) ($authorityClusterGroups[0]['cluster_name'] ?? ''));

                $authorityKeywords = array();
                foreach ($authorityClusterGroups as $authorityGroup) {
                    foreach ((array) ($authorityGroup['target_keywords'] ?? array()) as $authorityKeyword) {
                        $authorityKeyword = sanitize_text_field((string) $authorityKeyword);
                        if ($authorityKeyword !== '' && !in_array($authorityKeyword, $authorityKeywords, true)) {
                            $authorityKeywords[] = $authorityKeyword;
                        }
                    }
                }
                if (!empty($authorityKeywords)) {
                    $payload['target_keywords'] = array_slice($authorityKeywords, 0, 18);
                    $payload['cluster_keywords'] = $payload['target_keywords'];
                }
            }

            $payload['authority_context'] = $this->aysa_adjustments_build_authority_context((array) ($payload['cluster_groups'] ?? array()), $payload);
        }

        if ($strategy_type === 'customer_journey') {
            $payload['journey_context'] = $this->aysa_adjustments_build_customer_journey_context((array) ($payload['cluster_groups'] ?? array()), $payload);
        }

        if ($strategy_type === 'seasonal_event') {
            $payload['current_date'] = gmdate('Y-m-d');
            $payload['industry_context'] = array(
                'current_month' => sanitize_text_field((string) ($payload['current_month'] ?? '')),
                'business_name' => sanitize_text_field((string) ($payload['business_name'] ?? '')),
                'industry' => sanitize_text_field((string) ($payload['industry'] ?? '')),
                'company_info' => sanitize_text_field((string) ($payload['company_info'] ?? '')),
                'city' => sanitize_text_field((string) ($payload['city'] ?? '')),
                'country' => sanitize_text_field((string) ($payload['country'] ?? '')),
                'seasonality_hint' => sanitize_text_field((string) ($payload['city'] ?? '')) . ' ' . sanitize_text_field((string) ($payload['current_month'] ?? '')),
                'cluster_context' => $this->aysa_adjustments_build_evergreen_fresh_context((array) ($payload['cluster_groups'] ?? array()), $payload),
                'known_events' => array_values(array_filter(array(
                    '8 Martie',
                    'Paste',
                    'Florii',
                    '1 Martie',
                    'Black Friday',
                    'Craciun',
                    'Valentine\'s Day',
                ))),
            );
        }

        if ($strategy_type === 'evergreen_fresh_mix') {
            $payload['industry_context'] = array(
                'current_month' => sanitize_text_field((string) ($payload['current_month'] ?? '')),
                'business_name' => sanitize_text_field((string) ($payload['business_name'] ?? '')),
                'industry' => sanitize_text_field((string) ($payload['industry'] ?? '')),
                'company_info' => sanitize_text_field((string) ($payload['company_info'] ?? '')),
                'city' => sanitize_text_field((string) ($payload['city'] ?? '')),
                'seasonality_hint' => sanitize_text_field((string) ($payload['city'] ?? '')) . ' ' . sanitize_text_field((string) ($payload['current_month'] ?? '')),
                'cluster_context' => $this->aysa_adjustments_build_evergreen_fresh_context((array) ($payload['cluster_groups'] ?? array()), $payload),
            );
        }

        $table = $wpdb->prefix . 'aysa_content_strategies';
        $allowed_keyword_pool = $this->aysa_adjustments_build_allowed_content_keyword_pool($payload);
        $existing_rows = $wpdb->get_results($wpdb->prepare(
            "SELECT title, keywords, strategy_type FROM {$table}
             WHERE project_id = %d
               AND COALESCE(automation_status, '') <> 'deleted'
             ORDER BY id DESC LIMIT 80",
            $project_id
        ), ARRAY_A);
        $existing_titles = array();
        $existing_keywords = array();
        foreach ((array) $existing_rows as $row) {
            $title = sanitize_text_field((string) ($row['title'] ?? ''));
            $row_keywords = $this->aysa_adjustments_normalize_content_keywords($row['keywords'] ?? array());
            if (!$this->aysa_adjustments_is_relevant_content_strategy_item($title, $row_keywords, $allowed_keyword_pool)) {
                continue;
            }
            if ($title !== '' && !in_array($title, $existing_titles, true)) {
                $existing_titles[] = $title;
            }
            foreach ($row_keywords as $keyword) {
                if (!in_array($keyword, $existing_keywords, true)) {
                    $existing_keywords[] = $keyword;
                }
            }
        }
        if (!empty($existing_titles)) {
            $payload['recent_existing_titles'] = array_slice($existing_titles, 0, 20);
        }
        if (!empty($existing_keywords)) {
            $payload['existing_target_keywords'] = array_slice($existing_keywords, 0, 40);
        }

        return $payload;
    }

    private function aysa_adjustments_save_content_strategy_items($project_id, $strategy_type, $items, $fallback_payload = array()) {
        global $wpdb;

        $project_id = intval($project_id);
        $strategy_type = sanitize_text_field((string) $strategy_type);
        $items = is_array($items) ? $items : array();
        $fallback_payload = is_array($fallback_payload) ? $fallback_payload : array();

        $table = $wpdb->prefix . 'aysa_content_strategies';
        $table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($table_exists !== $table) {
            if (!class_exists('Aysa_Content_Strategy_Manager')) {
                require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-content-strategy-manager.php';
            }
            if (class_exists('Aysa_Content_Strategy_Manager')) {
                $manager = new Aysa_Content_Strategy_Manager();
                $manager->create_table();
            }
            $table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
            if ($table_exists !== $table) {
                return array('saved' => 0, 'existing' => 0, 'errors' => count($items));
            }
        }

        $saved = 0;
        $existing = 0;
        $errors = 0;
        $saved_items = array();
        $existing_items = array();
        $plan_batch = 'ai-' . gmdate('YmdHis') . '-' . wp_generate_password(6, false, false);
        $default_keywords = isset($fallback_payload['target_keywords']) && is_array($fallback_payload['target_keywords'])
            ? array_values($fallback_payload['target_keywords'])
            : array();
        $allowed_keyword_pool = $this->aysa_adjustments_build_allowed_content_keyword_pool($fallback_payload);
        $existing_titles = $wpdb->get_results($wpdb->prepare(
            "SELECT title, keywords FROM {$table} WHERE project_id = %d AND strategy_type = %s",
            $project_id,
            $strategy_type
        ), ARRAY_A);
        $topic_pool = array();
        $keyword_pool = array();
        if (is_array($existing_titles)) {
            foreach ($existing_titles as $existing_row) {
                $existing_title = sanitize_text_field((string) ($existing_row['title'] ?? ''));
                if ($existing_title !== '') {
                    $topic_pool[] = $existing_title;
                }
                $existing_keywords = $this->aysa_adjustments_normalize_content_keywords($existing_row['keywords'] ?? array());
                if (!empty($existing_keywords)) {
                    $keyword_pool[] = $existing_keywords;
                }
            }
        }

        foreach ($items as $item) {
            if (!is_array($item)) {
                continue;
            }
            $title = sanitize_text_field((string) ($item['title'] ?? ''));
            if ($title === '') {
                continue;
            }
            if ($this->aysa_adjustments_is_duplicate_content_topic($title, $topic_pool)) {
                $existing++;
                $existing_items[] = $item;
                continue;
            }

            $keywords = isset($item['target_keywords']) && is_array($item['target_keywords'])
                ? array_values(array_filter(array_map('sanitize_text_field', $item['target_keywords'])))
                : $default_keywords;
            $normalized_keywords = $this->aysa_adjustments_normalize_content_keywords($keywords);
            $intent = sanitize_text_field((string) ($item['intent'] ?? 'I'));
            $associated_page = sanitize_text_field((string) ($item['associated_page'] ?? $item['associatedPage'] ?? ''));
            $strategy_meta = array();

            if ($strategy_type === 'competitor_derived_titles') {
                $strategy_meta = array(
                    'competitor_domain' => sanitize_text_field((string) ($item['competitor_domain'] ?? '')),
                    'competitor_title' => sanitize_text_field((string) ($item['competitor_title'] ?? '')),
                    'competitor_url' => esc_url_raw((string) ($item['competitor_url'] ?? '')),
                    'strategy' => sanitize_key((string) ($item['strategy'] ?? '')),
                    'improvement_type' => sanitize_key((string) ($item['improvement_type'] ?? '')),
                    'why_better' => sanitize_text_field((string) ($item['why_better'] ?? '')),
                );
            }

            if ($strategy_type === 'competitor_derived_titles') {
                if (!empty($allowed_keyword_pool) && !empty($normalized_keywords)) {
                    $keyword_relevance_score = $this->aysa_adjustments_content_keywords_overlap_score($normalized_keywords, $allowed_keyword_pool);
                    if ($keyword_relevance_score < 0.2) {
                        $errors++;
                        continue;
                    }
                } elseif (!$this->aysa_adjustments_is_relevant_content_strategy_item($title, $normalized_keywords, $allowed_keyword_pool)) {
                    $errors++;
                    continue;
                }
            } elseif (!$this->aysa_adjustments_is_relevant_content_strategy_item($title, $normalized_keywords, $allowed_keyword_pool)) {
                $errors++;
                continue;
            }

            $exists = $wpdb->get_var($wpdb->prepare(
                "SELECT id FROM {$table} WHERE project_id = %d AND title = %s AND strategy_type = %s AND COALESCE(associated_page, '') = %s LIMIT 1",
                $project_id,
                $title,
                $strategy_type,
                $associated_page
            ));
            if (!empty($exists)) {
                $existing++;
                $existing_items[] = $item;
                continue;
            }

            if ($this->aysa_adjustments_is_duplicate_content_keywords($normalized_keywords, $keyword_pool)) {
                $existing++;
                $existing_items[] = $item;
                continue;
            }

            $inserted = $wpdb->insert(
                $table,
                array(
                    'project_id' => $project_id,
                    'title' => $title,
                    'keywords' => !empty($keywords) ? wp_json_encode($keywords) : '',
                    'intent' => $intent,
                    'associated_page' => $associated_page,
                    'strategy_type' => $strategy_type,
                    'strategy_meta' => !empty($strategy_meta) ? wp_json_encode($strategy_meta) : null,
                    'recommended_page_type' => null,
                    'content_type' => 'post',
                    'execution_stage' => 'planned',
                    'status' => 'proposed',
                    'automation_status' => null,
                    'automation_attempts' => 0,
                    'scheduled_for' => null,
                    'generated_at' => null,
                    'plan_batch' => $plan_batch,
                    'last_error' => null,
                    'automation_updated_at' => current_time('mysql'),
                )
            );

            if ($inserted === false) {
                $errors++;
                continue;
            }
            $saved++;
            $saved_items[] = $item;
            $topic_pool[] = $title;
            if (!empty($normalized_keywords)) {
                $keyword_pool[] = $normalized_keywords;
            }
        }

        if ($saved > 0) {
            delete_option('aysa_content_strategy_hard_reset_' . $project_id);
        }

        return array(
            'saved' => $saved,
            'existing' => $existing,
            'errors' => $errors,
            'plan_batch' => $saved > 0 ? $plan_batch : '',
            'saved_items' => $saved_items,
            'existing_items' => $existing_items,
        );
    }

    private function aysa_adjustments_extract_competitor_domains_from_setup_state($competition, $project_id = 0, $setup_user_id = 0) {
        $competition = is_array($competition) ? $competition : array();
        $domains = array();
        $seen = array();

        $push_domain = static function($value) use (&$domains, &$seen) {
            $value = trim((string) $value);
            if ($value === '') {
                return;
            }

            $value = preg_replace('#^https?://#i', '', $value);
            $value = preg_replace('#^www\.#i', '', $value);
            $value = trim((string) $value, "/ \t\n\r\0\x0B");
            $value = strtolower($value);

            if ($value === '' || strpos($value, '.') === false) {
                return;
            }

            if (isset($seen[$value])) {
                return;
            }

            $seen[$value] = true;
            $domains[] = $value;
        };

        foreach ($competition as $key => $value) {
            $key = (string) $key;

            if (preg_match('/^competitor\d+$/', $key) || preg_match('/^competitor\d+_(domain|url)$/', $key)) {
                $push_domain($value);
                continue;
            }

            if ($key === 'competitors' && is_array($value)) {
                foreach ($value as $competitor_row) {
                    if (is_array($competitor_row)) {
                        $push_domain($competitor_row['domain'] ?? '');
                        $push_domain($competitor_row['url'] ?? '');
                        $push_domain($competitor_row['site'] ?? '');
                    } else {
                        $push_domain($competitor_row);
                    }
                }
            }
        }

        if (!empty($domains)) {
            return $domains;
        }

        global $wpdb;
        $project_id = intval($project_id);
        $setup_user_id = intval($setup_user_id);

        $fallback_options = array();
        if ($project_id > 0) {
            $fallback_options[] = get_option('aysa_wizard_competition_project_' . $project_id, array());
        }
        if ($setup_user_id > 0) {
            $fallback_options[] = get_option('aysa_wizard_competition_' . $setup_user_id, array());
        }

        foreach ($fallback_options as $fallback_option) {
            if (!is_array($fallback_option) || empty($fallback_option)) {
                continue;
            }
            foreach ($fallback_option as $key => $value) {
                $key = (string) $key;
                if (preg_match('/^competitor\d+$/', $key) || preg_match('/^competitor\d+_(domain|url)$/', $key)) {
                    $push_domain($value);
                }
            }
            if (!empty($domains)) {
                return $domains;
            }
        }

        if (isset($wpdb) && $wpdb instanceof wpdb) {
            $like = $wpdb->esc_like('aysa_wizard_competition') . '%';
            $rows = $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT option_value FROM {$wpdb->options} WHERE option_name LIKE %s ORDER BY option_id DESC LIMIT 50",
                    $like
                ),
                ARRAY_A
            );
            if (is_array($rows)) {
                foreach ($rows as $row) {
                    $raw_option = isset($row['option_value']) ? maybe_unserialize($row['option_value']) : array();
                    if (!is_array($raw_option) || empty($raw_option)) {
                        continue;
                    }
                    foreach ($raw_option as $key => $value) {
                        $key = (string) $key;
                        if (preg_match('/^competitor\d+$/', $key) || preg_match('/^competitor\d+_(domain|url)$/', $key)) {
                            $push_domain($value);
                        }
                    }
                    if (!empty($domains)) {
                        return $domains;
                    }
                }
            }
        }

        return array();
    }

    private function aysa_adjustments_build_topic_signature($title) {
        $title = strtolower(trim(remove_accents((string) $title)));
        if ($title === '') {
            return '';
        }

        $title = preg_replace('/[^a-z0-9\\s]+/u', ' ', $title);
        $parts = preg_split('/\\s+/', $title);
        if (!is_array($parts)) {
            return '';
        }

        $stopwords = array(
            'si','sau','de','la','in','din','pe','cu','pentru','cele','cel','cea','cei','un','o','a','ale','al',
            'the','and','for','with','from','into','your','how','top','guide','ghid','complet','best','celemai',
            '2024','2025','2026'
        );
        $stop = array_fill_keys($stopwords, true);
        $tokens = array();

        foreach ($parts as $p) {
            $p = trim((string) $p);
            if ($p === '' || isset($stop[$p])) {
                continue;
            }
            if (strlen($p) < 3) {
                continue;
            }
            $tokens[$p] = true;
        }

        if (empty($tokens)) {
            return '';
        }

        $keys = array_keys($tokens);
        sort($keys);
        return implode(' ', $keys);
    }

    private function aysa_adjustments_topic_similarity_score($a, $b) {
        $sa = $this->aysa_adjustments_build_topic_signature($a);
        $sb = $this->aysa_adjustments_build_topic_signature($b);
        if ($sa === '' || $sb === '') {
            return 0.0;
        }
        if ($sa === $sb) {
            return 1.0;
        }

        $ta = array_fill_keys(explode(' ', $sa), true);
        $tb = array_fill_keys(explode(' ', $sb), true);
        $inter = array_intersect_key($ta, $tb);
        $union = $ta + $tb;
        return count($union) > 0 ? (count($inter) / count($union)) : 0.0;
    }

    private function aysa_adjustments_is_duplicate_content_topic($title, $pool) {
        $title_clean = trim((string) $title);
        if ($title_clean === '') {
            return false;
        }

        foreach ((array) $pool as $existing) {
            $existing_clean = trim((string) $existing);
            if ($existing_clean === '') {
                continue;
            }
            if (strcasecmp($title_clean, $existing_clean) === 0) {
                return true;
            }
            $score = $this->aysa_adjustments_topic_similarity_score($title_clean, $existing_clean);
            if ($score >= 0.75) {
                return true;
            }
        }

        return false;
    }

    private function aysa_adjustments_normalize_content_keywords($keywords) {
        if (is_string($keywords)) {
            $decoded = json_decode($keywords, true);
            if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
                $keywords = $decoded;
            } else {
                $keywords = array($keywords);
            }
        }

        $normalized = array();
        foreach ((array) $keywords as $keyword) {
            $keyword = strtolower(trim(remove_accents((string) $keyword)));
            if ($keyword === '') {
                continue;
            }
            $keyword = preg_replace('/[^a-z0-9\\s]+/u', ' ', $keyword);
            $keyword = preg_replace('/\\s+/u', ' ', $keyword);
            $keyword = trim((string) $keyword);
            if ($keyword === '') {
                continue;
            }
            $normalized[$keyword] = true;
        }

        return array_keys($normalized);
    }

    private function aysa_adjustments_content_keywords_overlap_score($keywords, $existing_keywords) {
        $a = array_fill_keys((array) $keywords, true);
        $b = array_fill_keys((array) $existing_keywords, true);
        if (empty($a) || empty($b)) {
            return 0.0;
        }

        $intersection = array_intersect_key($a, $b);
        $base = min(count($a), count($b));
        if ($base <= 0) {
            return 0.0;
        }

        return count($intersection) / $base;
    }

    private function aysa_adjustments_is_duplicate_content_keywords($keywords, $pool) {
        $keywords = array_values(array_filter((array) $keywords));
        if (empty($keywords)) {
            return false;
        }

        foreach ((array) $pool as $existing_keywords) {
            $existing_keywords = array_values(array_filter((array) $existing_keywords));
            if (empty($existing_keywords)) {
                continue;
            }

            $score = $this->aysa_adjustments_content_keywords_overlap_score($keywords, $existing_keywords);
            if ($score >= 0.5) {
                return true;
            }
        }

        return false;
    }

    private function aysa_adjustments_build_allowed_content_keyword_pool($payload) {
        $payload = is_array($payload) ? $payload : array();
        $raw = array();
        $keys = array('target_keywords', 'cluster_keywords', 'seed_keywords');
        foreach ($keys as $key) {
            if (!empty($payload[$key]) && is_array($payload[$key])) {
                $raw = array_merge($raw, $payload[$key]);
            }
        }

        foreach (array('brand_name', 'business_name', 'industry', 'city', 'country', 'primary_keyword') as $key) {
            if (!empty($payload[$key])) {
                $raw[] = (string) $payload[$key];
            }
        }

        return $this->aysa_adjustments_normalize_content_keywords($raw);
    }

    private function aysa_adjustments_is_relevant_content_strategy_item($title, $keywords, $allowed_pool) {
        $allowed_pool = array_values(array_filter((array) $allowed_pool));
        if (empty($allowed_pool)) {
            return true;
        }

        $title_tokens = $this->aysa_adjustments_normalize_content_keywords(array($title));
        $keywords = array_values(array_filter((array) $keywords));
        $combined = array_values(array_unique(array_merge($title_tokens, $keywords)));
        if (empty($combined)) {
            return false;
        }

        $score = $this->aysa_adjustments_content_keywords_overlap_score($combined, $allowed_pool);
        if ($score >= 0.5) {
            return true;
        }

        foreach ($combined as $token) {
            $token = (string) $token;
            if (strlen($token) < 4) {
                continue;
            }
            if (in_array($token, $allowed_pool, true)) {
                return true;
            }
        }

        return false;
    }

    private function aysa_adjustments_delete_content_strategy_batch($project_id, $plan_batch) {
        global $wpdb;

        $project_id = intval($project_id);
        $plan_batch = sanitize_text_field((string) $plan_batch);
        if ($project_id <= 0 || $plan_batch === '') {
            return array('deleted' => 0);
        }

        $table = $wpdb->prefix . 'aysa_content_strategies';
        $deleted = $wpdb->query($wpdb->prepare(
            "DELETE FROM {$table} WHERE project_id = %d AND plan_batch = %s",
            $project_id,
            $plan_batch
        ));

        return array('deleted' => max(0, intval($deleted)));
    }

    public function handle_missing_pages_send_to_plan() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_content_strategy_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : intval(get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $raw_items = $_POST['items'] ?? array();
        if (is_string($raw_items)) {
            $decoded = json_decode(wp_unslash($raw_items), true);
            $raw_items = is_array($decoded) ? $decoded : array();
        }
        if (!is_array($raw_items) || empty($raw_items)) {
            $single_keyword = sanitize_text_field((string) ($_POST['keyword'] ?? ''));
            if ($single_keyword !== '') {
                $raw_items = array(array(
                    'keyword' => $single_keyword,
                    'intent' => sanitize_text_field((string) ($_POST['intent'] ?? 'N/A')),
                    'source' => sanitize_text_field((string) ($_POST['source'] ?? 'missing_pages')),
                    'recommended_page_type' => sanitize_text_field((string) ($_POST['recommended_page_type'] ?? 'Landing Page')),
                    'recommendation_reason' => sanitize_text_field((string) ($_POST['recommendation_reason'] ?? '')),
                ));
            }
        }
        if (empty($raw_items)) {
            wp_send_json_error('Missing keyword payload');
            return;
        }

        global $wpdb;
        $table = $wpdb->prefix . 'aysa_content_strategies';
        $table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($table_exists !== $table) {
            if (!class_exists('Aysa_Content_Strategy_Manager')) {
                require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-content-strategy-manager.php';
            }
            if (class_exists('Aysa_Content_Strategy_Manager')) {
                $manager = new Aysa_Content_Strategy_Manager();
                $manager->create_table();
            }
            $table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
            if ($table_exists !== $table) {
                wp_send_json_error('Content strategy table unavailable');
                return;
            }
        }

        $result = $this->aysa_missing_pages_send_items_to_plan($project_id, $raw_items);

        wp_send_json_success(array(
            'added' => intval($result['added'] ?? 0),
            'existing' => intval($result['existing'] ?? 0),
            'errors' => intval($result['errors'] ?? 0),
            'total' => intval($result['total'] ?? 0),
        ));
    }

    private function aysa_missing_pages_send_items_to_plan($project_id, $raw_items) {
        global $wpdb;

        $project_id = intval($project_id);
        $raw_items = is_array($raw_items) ? $raw_items : array();
        $table = $wpdb->prefix . 'aysa_content_strategies';
        $table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
        if ($table_exists !== $table) {
            if (!class_exists('Aysa_Content_Strategy_Manager')) {
                require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-content-strategy-manager.php';
            }
            if (class_exists('Aysa_Content_Strategy_Manager')) {
                $manager = new Aysa_Content_Strategy_Manager();
                $manager->create_table();
            }
            $table_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table));
            if ($table_exists !== $table) {
                return array('added' => 0, 'existing' => 0, 'errors' => count($raw_items), 'total' => count($raw_items));
            }
        }

        $existing_titles = $wpdb->get_col($wpdb->prepare(
            "SELECT title
             FROM {$table}
             WHERE project_id = %d
               AND strategy_type = 'missing_pages'
               AND COALESCE(automation_status, '') <> 'deleted'",
            $project_id
        ));

        $result = $this->aysa_broker_request('POST', '/v1/content-strategy/missing-pages-send-to-plan', array(
            'project_id' => $project_id,
            'items' => array_values($raw_items),
            'existing_titles' => is_array($existing_titles) ? array_values($existing_titles) : array(),
        ), 60);
        if (is_wp_error($result) || !is_array($result) || empty($result['ok'])) {
            return array(
                'added' => 0,
                'existing' => 0,
                'errors' => count($raw_items),
                'total' => count($raw_items),
            );
        }

        return $this->aysa_missing_pages_insert_plan_rows(
            $project_id,
            isset($result['rows']) && is_array($result['rows']) ? $result['rows'] : array(),
            intval($result['existing'] ?? 0),
            intval($result['errors'] ?? 0)
        );
    }

    private function aysa_missing_pages_insert_plan_rows($project_id, $rows_to_insert, $existing = 0, $errors = 0) {
        global $wpdb;

        $project_id = intval($project_id);
        $existing = intval($existing);
        $errors = intval($errors);
        $rows_to_insert = is_array($rows_to_insert) ? $rows_to_insert : array();
        $table = $wpdb->prefix . 'aysa_content_strategies';
        $added = 0;

        foreach ($rows_to_insert as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keywords_payload = wp_json_encode(array_values(array_filter(array_map('strval', (array) ($row['keywords'] ?? array())))));
            $inserted = $wpdb->insert(
                $table,
                array(
                    'project_id' => $project_id,
                    'title' => sanitize_text_field((string) ($row['title'] ?? '')),
                    'keywords' => $keywords_payload,
                    'intent' => sanitize_text_field((string) ($row['intent'] ?? 'N/A')),
                    'associated_page' => sanitize_text_field((string) ($row['associated_page'] ?? '')),
                    'strategy_type' => sanitize_text_field((string) ($row['strategy_type'] ?? 'missing_pages')),
                    'recommended_page_type' => sanitize_text_field((string) ($row['recommended_page_type'] ?? 'Landing Page')),
                    'content_type' => sanitize_key((string) ($row['content_type'] ?? 'landing_page')),
                    'execution_stage' => sanitize_key((string) ($row['execution_stage'] ?? 'planned')),
                    'status' => sanitize_key((string) ($row['status'] ?? 'proposed')),
                    'automation_status' => sanitize_text_field((string) ($row['automation_status'] ?? '')),
                    'automation_attempts' => intval($row['automation_attempts'] ?? 0),
                    'scheduled_for' => !empty($row['scheduled_for']) ? sanitize_text_field((string) $row['scheduled_for']) : null,
                    'generated_at' => !empty($row['generated_at']) ? sanitize_text_field((string) $row['generated_at']) : null,
                    'plan_batch' => sanitize_text_field((string) ($row['plan_batch'] ?? 'missing_pages')),
                    'last_error' => sanitize_text_field((string) ($row['last_error'] ?? '')),
                    'automation_updated_at' => current_time('mysql'),
                ),
                array('%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%s', '%s', '%s', '%s', '%s')
            );

            if ($inserted === false) {
                $errors++;
                continue;
            }
            $added++;
        }

        return array(
            'added' => $added,
            'existing' => $existing,
            'errors' => $errors,
            'total' => $added + $existing + $errors,
        );
    }

    private function aysa_build_missing_page_meta_prompt($keyword, $page_type, $intent, $context = array()) {
        $brand_name = trim((string) ($context['brand_name'] ?? ''));
        $city = trim((string) ($context['targeted_city'] ?? ''));
        $country = trim((string) ($context['targeted_country'] ?? 'Romania'));
        $language = trim((string) ($context['primary_language'] ?? 'Romanian'));
        $audience = trim((string) ($context['target_audience'] ?? ''));
        $website_type = trim((string) ($context['website_type'] ?? ''));

        $lines = array(
            'You are an SEO metadata expert.',
            'Generate metadata for ONE page based on this input:',
            '- keyword: "' . $keyword . '"',
            '- page_type: "' . $page_type . '" (allowed: category, product, landing_page)',
            '- intent: "' . $intent . '"',
            '- language: "' . $language . '"',
            '- city: "' . $city . '"',
            '- country: "' . $country . '"',
            '- brand_name: "' . $brand_name . '"',
            '- target_audience: "' . $audience . '"',
            '- website_type: "' . $website_type . '"',
            '',
            'Strict rules:',
            '1) title length: 50-60 chars, include keyword naturally, maximize CTR.',
            '2) h1 length: 20-70 chars, include keyword exactly once.',
            '3) meta_description length: 140-160 chars, include keyword naturally, include one CTA.',
            '4) no keyword stuffing, no quotes, no emoji, no ALL CAPS.',
            '5) if city is present, include it naturally in either title or meta description.',
            '6) keep wording human and commercial-ready.',
            '',
            'Return only valid JSON object:',
            '{',
            '  "keyword": "string",',
            '  "page_type": "category|product|landing_page",',
            '  "title": "string",',
            '  "h1": "string",',
            '  "meta_description": "string"',
            '}',
        );

        return implode("\n", $lines);
    }

    private function aysa_missing_page_meta_table() {
        global $wpdb;
        return $wpdb->prefix . 'aysa_missing_page_meta';
    }

    private function aysa_missing_page_meta_maybe_create_table() {
        global $wpdb;
        $table = $this->aysa_missing_page_meta_table();
        $charset_collate = $wpdb->get_charset_collate();
        $sql = "CREATE TABLE {$table} (
            id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
            project_id mediumint(9) NOT NULL,
            strategy_id mediumint(9) DEFAULT NULL,
            keyword varchar(191) NOT NULL,
            page_type varchar(32) NOT NULL,
            intent varchar(8) DEFAULT 'N/A',
            seo_title varchar(255) NOT NULL,
            h1 text,
            meta_description text,
            model varchar(128) DEFAULT '',
            warning varchar(128) DEFAULT '',
            created_at datetime DEFAULT CURRENT_TIMESTAMP,
            updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
            PRIMARY KEY (id),
            UNIQUE KEY uq_strategy (strategy_id),
            KEY idx_project_keyword (project_id, keyword),
            KEY idx_project_page_type (project_id, page_type)
        ) {$charset_collate};";
        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta($sql);
    }

    private function aysa_missing_page_infer_type_from_title($title) {
        $t = function_exists('mb_strtolower') ? mb_strtolower((string) $title, 'UTF-8') : strtolower((string) $title);
        if (strpos($t, 'create product page') !== false) {
            return 'product';
        }
        if (strpos($t, 'create category page') !== false) {
            return 'category';
        }
        return 'landing_page';
    }

    private function aysa_missing_page_normalize_recommended_page_type($value) {
        $text = function_exists('mb_strtolower') ? mb_strtolower(trim((string) $value), 'UTF-8') : strtolower(trim((string) $value));
        if (strpos($text, 'product') !== false) {
            return 'Product Page';
        }
        if (strpos($text, 'category') !== false) {
            return 'Category Page';
        }
        return 'Landing Page';
    }

    private function aysa_missing_page_content_type_from_recommended($recommended_page_type) {
        $normalized = $this->aysa_missing_page_normalize_recommended_page_type($recommended_page_type);
        if ($normalized === 'Product Page') {
            return 'product_page';
        }
        if ($normalized === 'Category Page') {
            return 'category_page';
        }
        return 'landing_page';
    }

    private function aysa_missing_page_extract_keyword($title, $keywords_json = '') {
        $kw = '';
        $decoded = json_decode((string) $keywords_json, true);
        if (is_array($decoded) && isset($decoded[0]) && is_string($decoded[0])) {
            $kw = trim((string) $decoded[0]);
        }
        if ($kw === '' && preg_match('/for\s+(.+)$/iu', (string) $title, $m)) {
            $kw = trim((string) ($m[1] ?? ''));
        }
        return sanitize_text_field($kw);
    }

    private function aysa_generate_missing_page_meta($project_id, $keyword, $page_type, $intent, $context = array()) {
        $prompt = $this->aysa_build_missing_page_meta_prompt($keyword, $page_type, $intent, $context);

        $broker_strategy_payload = array(
            'project_id' => $project_id,
            'strategy_type' => 'missing_page_meta',
            'payload' => array(
                'keyword' => $keyword,
                'page_type' => $page_type,
                'intent' => $intent,
                'context' => $context,
                'prompt' => $prompt,
            ),
        );
        $broker_meta_payload = array(
            'project_id' => $project_id,
            'keyword' => $keyword,
            'page_type' => $page_type,
            'intent' => $intent,
            'context' => $context,
            'prompt' => $prompt,
        );

        $title = '';
        $h1 = '';
        $meta_description = '';
        $model = '';
        $warning = '';
        $broker_error = '';
        $result = $this->aysa_broker_request('POST', '/v1/content/missing-page/meta', $broker_meta_payload, 90);
        if (is_wp_error($result)) {
            $broker_error = $result->get_error_message();
            $result = null;
        }

        if (is_array($result) && !empty($result['ok'])) {
            $broker_error = '';
            $item_payload = array();
            if (!empty($result['item']) && is_array($result['item'])) {
                $item_payload = $result['item'];
            } elseif (!empty($result['items']) && is_array($result['items']) && isset($result['items'][0]) && is_array($result['items'][0])) {
                $item_payload = $result['items'][0];
            }
            if (!empty($item_payload)) {
                $title = sanitize_text_field((string) ($item_payload['title'] ?? ''));
                $h1 = sanitize_text_field((string) ($item_payload['h1'] ?? $item_payload['heading'] ?? ''));
                $meta_description = sanitize_textarea_field((string) ($item_payload['meta_description'] ?? $item_payload['description'] ?? ''));
            }
            $model = sanitize_text_field((string) ($result['model'] ?? $model));
        } else {
            if (is_array($result)) {
                $broker_error = sanitize_text_field((string) ($result['error'] ?? $result['message'] ?? 'empty_or_invalid_broker_response'));
            } elseif ($broker_error === '') {
                $broker_error = 'empty_or_invalid_broker_response';
            }
        }

        if ($title === '' || $h1 === '' || $meta_description === '') {
            $title = $title !== '' ? $title : ucwords($keyword) . ' - Pagina recomandata';
            $h1 = $h1 !== '' ? $h1 : ucwords($keyword);
            $cta = ($page_type === 'product') ? 'Comandă acum' : 'Descoperă oferta';
            $meta_description = $meta_description !== '' ? $meta_description : ($cta . ' pentru ' . $keyword . ' și obține livrare rapidă, preț bun și suport dedicat pentru alegerea potrivită.');
            $warning = 'fallback_template_used';
            if ($broker_error !== '') {
                $warning .= ':broker=' . sanitize_text_field($broker_error);
            }
        }

        return array(
            'keyword' => $keyword,
            'page_type' => $page_type,
            'intent' => $intent,
            'title' => $title,
            'h1' => $h1,
            'meta_description' => $meta_description,
            'model' => $model,
            'warning' => $warning,
            'source' => ($warning === '' ? 'ai' : 'fallback'),
            'openai_error' => '',
            'broker_error' => $broker_error,
            'prompt_preview' => $prompt,
        );
    }

    private function aysa_save_missing_page_meta($project_id, $strategy_id, $meta) {
        global $wpdb;
        $this->aysa_missing_page_meta_maybe_create_table();
        $table = $this->aysa_missing_page_meta_table();
        $wpdb->replace(
            $table,
            array(
                'project_id' => intval($project_id),
                'strategy_id' => intval($strategy_id) > 0 ? intval($strategy_id) : 0,
                'keyword' => sanitize_text_field((string) ($meta['keyword'] ?? '')),
                'page_type' => sanitize_key((string) ($meta['page_type'] ?? 'landing_page')),
                'intent' => sanitize_text_field((string) ($meta['intent'] ?? 'N/A')),
                'seo_title' => sanitize_text_field((string) ($meta['title'] ?? '')),
                'h1' => sanitize_text_field((string) ($meta['h1'] ?? '')),
                'meta_description' => sanitize_textarea_field((string) ($meta['meta_description'] ?? '')),
                'model' => sanitize_text_field((string) ($meta['model'] ?? '')),
                'warning' => sanitize_text_field((string) ($meta['warning'] ?? '')),
                'updated_at' => current_time('mysql'),
            ),
            array('%d', '%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')
        );
    }

    public function handle_missing_page_generate_meta() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_content_strategy_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : intval(get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $keyword = sanitize_text_field((string) ($_POST['keyword'] ?? ''));
        if ($keyword === '') {
            wp_send_json_error('Missing keyword');
            return;
        }

        $page_type = sanitize_key((string) ($_POST['page_type'] ?? 'landing_page'));
        $allowed_types = array('category', 'product', 'landing_page');
        if (!in_array($page_type, $allowed_types, true)) {
            $page_type = 'landing_page';
        }

        $intent = strtoupper(sanitize_text_field((string) ($_POST['intent'] ?? 'N/A')));
        if (!in_array($intent, array('I', 'C', 'T', 'N', 'N/A'), true)) {
            $intent = 'N/A';
        }
        $strategy_id = intval($_POST['strategy_id'] ?? 0);

        $user_id = $this->aysa_get_effective_setup_user_id();
        $context = $this->aysa_keyword_strategy_get_context($user_id);
        $general = get_option('aysa_wizard_general_' . $user_id, array());
        if (is_array($general)) {
            $context['brand_name'] = (string) ($general['brand_name'] ?? $general['brand'] ?? $general['company_name'] ?? '');
            $context['target_audience'] = (string) ($general['company_info'] ?? $general['business_information'] ?? '');
        }
        $meta = $this->aysa_generate_missing_page_meta($project_id, $keyword, $page_type, $intent, $context);

        if ($strategy_id > 0) {
            global $wpdb;
            $table = $wpdb->prefix . 'aysa_content_strategies';
            $wpdb->update(
                $table,
                array(
                    'title' => sanitize_text_field((string) ($meta['title'] ?? '')),
                    'last_error' => sanitize_text_field((string) ($meta['h1'] ?? '')) . ' || ' . sanitize_textarea_field((string) ($meta['meta_description'] ?? '')),
                    'execution_stage' => 'seo_meta_ready',
                    'automation_updated_at' => current_time('mysql'),
                ),
                array(
                    'id' => $strategy_id,
                    'project_id' => $project_id,
                ),
                array('%s', '%s', '%s', '%s'),
                array('%d', '%d')
            );
            $this->aysa_save_missing_page_meta($project_id, $strategy_id, $meta);
        }

        // Track credit usage only when AI generation actually happened.
        if (($meta['source'] ?? '') === 'ai') {
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_missing_page_generate_meta',
                'missing_page_generate_meta_' . md5($keyword . '|' . $page_type . '|' . $intent),
                array(
                    'module' => 'missing_pages',
                    'keyword' => $keyword,
                    'page_type' => $page_type,
                    'intent' => $intent,
                    'strategy_id' => $strategy_id,
                    'source' => 'ai',
                ),
                55,
                'wp_ajax/aysa_missing_page_generate_meta'
            );
        }

        wp_send_json_success($meta);
    }

    public function handle_missing_pages_refactor_titles() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_content_strategy_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('edit_posts')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : intval(get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $raw_items = $_POST['items'] ?? array();
        if (is_string($raw_items)) {
            $decoded = json_decode(wp_unslash($raw_items), true);
            $raw_items = is_array($decoded) ? $decoded : array();
        }
        if (!is_array($raw_items) || empty($raw_items)) {
            wp_send_json_error('No rows to refactor');
            return;
        }

        global $wpdb;
        $strategy_table = $wpdb->prefix . 'aysa_content_strategies';
        $rows_for_api = array();
        foreach ($raw_items as $item) {
            if (!is_array($item)) {
                continue;
            }
            $strategy_id = intval($item['id'] ?? 0);
            if ($strategy_id <= 0) {
                continue;
            }
            $row = $wpdb->get_row($wpdb->prepare(
                "SELECT id, title, keywords, intent
                 FROM {$strategy_table}
                 WHERE id = %d
                   AND project_id = %d
                   AND strategy_type = 'missing_pages'
                 LIMIT 1",
                $strategy_id,
                $project_id
            ), ARRAY_A);
            if (!is_array($row)) {
                continue;
            }
            $rows_for_api[] = array(
                'id' => $strategy_id,
                'title' => (string) ($row['title'] ?? ''),
                'keywords' => (string) ($row['keywords'] ?? ''),
                'intent' => (string) ($item['intent'] ?? ($row['intent'] ?? 'N/A')),
                'keyword' => (string) ($item['keyword'] ?? ''),
                'page_type' => (string) ($item['page_type'] ?? ''),
            );
        }

        if (empty($rows_for_api)) {
            wp_send_json_success(array(
                'updated' => 0,
                'failed' => 0,
                'ai' => 0,
                'fallback' => 0,
                'total' => 0,
            ));
            return;
        }

        $setup_user_id = $this->aysa_get_effective_setup_user_id();
        $result = $this->aysa_broker_request('POST', '/v1/content-strategy/missing-pages-refactor-titles', array(
            'project_id' => $project_id,
            'rows' => $rows_for_api,
            'raw_context' => $this->aysa_collect_content_strategy_context_bundle($project_id, $setup_user_id),
        ), 90);
        if (is_wp_error($result) || !is_array($result) || empty($result['ok'])) {
            wp_send_json_error(is_wp_error($result) ? $result->get_error_message() : 'Refactor failed');
            return;
        }

        $updated = 0;
        $failed = intval($result['failed'] ?? 0);
        $ai_count = intval($result['ai'] ?? 0);
        $fallback_count = intval($result['fallback'] ?? 0);
        $updates = isset($result['updates']) && is_array($result['updates']) ? $result['updates'] : array();

        foreach ($updates as $update_row) {
            if (!is_array($update_row)) {
                continue;
            }
            $strategy_id = intval($update_row['id'] ?? 0);
            if ($strategy_id <= 0) {
                $failed++;
                continue;
            }
            $meta = isset($update_row['meta']) && is_array($update_row['meta']) ? $update_row['meta'] : array();
            $ok = $wpdb->update(
                $strategy_table,
                array(
                    'title' => sanitize_text_field((string) ($update_row['title'] ?? ($meta['title'] ?? ''))),
                    'last_error' => sanitize_text_field((string) ($update_row['last_error'] ?? '')),
                    'automation_updated_at' => current_time('mysql'),
                ),
                array(
                    'id' => $strategy_id,
                    'project_id' => $project_id,
                ),
                array('%s', '%s', '%s'),
                array('%d', '%d')
            );
            if ($ok === false) {
                $failed++;
                continue;
            }
            if (!empty($meta)) {
                $this->aysa_save_missing_page_meta($project_id, $strategy_id, $meta);
            }
            $updated++;
        }

        // Track credit usage only if this batch used AI for at least one row.
        if ($ai_count > 0) {
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_missing_pages_refactor_titles',
                'missing_pages_refactor_titles_batch_' . md5(wp_json_encode(array(
                    'project_id' => $project_id,
                    'total' => count($raw_items),
                    'updated' => $updated,
                    'ai' => $ai_count,
                ))),
                array(
                    'module' => 'missing_pages',
                    'updated' => $updated,
                    'failed' => $failed,
                    'ai' => $ai_count,
                    'fallback' => $fallback_count,
                    'requested_rows' => count($raw_items),
                ),
                55,
                '/v1/content-strategy/missing-pages-refactor-titles'
            );
        }

        wp_send_json_success(array(
            'updated' => $updated,
            'failed' => $failed,
            'ai' => $ai_count,
            'fallback' => $fallback_count,
            'total' => $updated + $failed,
        ));
    }

    private function aysa_keyword_strategy_calculate_seed_relevance($keyword, $seed_keywords) {
        $kw = $this->aysa_keyword_strategy_normalize_keyword($keyword);
        if ($kw === '' || !is_array($seed_keywords) || empty($seed_keywords)) {
            return 0;
        }

        $kw_tokens = array_values(array_filter(explode(' ', $kw)));
        if (empty($kw_tokens)) {
            return 0;
        }

        $best = 0;
        foreach ($seed_keywords as $seed) {
            $seed_norm = $this->aysa_keyword_strategy_normalize_keyword($seed);
            if ($seed_norm === '') {
                continue;
            }
            if ($kw === $seed_norm) {
                return 100;
            }
            if (strpos($kw, $seed_norm) !== false || strpos($seed_norm, $kw) !== false) {
                $best = max($best, 85);
                continue;
            }

            $seed_tokens = array_values(array_filter(explode(' ', $seed_norm)));
            if (empty($seed_tokens)) {
                continue;
            }
            $common = array_intersect($kw_tokens, $seed_tokens);
            if (!empty($common)) {
                $coverage = count($common) / max(1, count($seed_tokens));
                $score = intval(round($coverage * 70));
                $best = max($best, $score);
            }
        }
        return min(100, max(0, $best));
    }

    private function aysa_keyword_strategy_build_local_plan($project_id, $user_id, $seed_keywords, $competitors, $context = array()) {
        $rows = $this->aysa_keyword_strategy_collect_local_keyword_rows($project_id);
        $association_map = $this->aysa_keyword_strategy_get_association_map($project_id);

        $seed_norm_map = array();
        foreach ($seed_keywords as $seed) {
            $norm = $this->aysa_keyword_strategy_normalize_keyword($seed);
            if ($norm !== '') {
                $seed_norm_map[$norm] = (string) $seed;
            }
        }

        $by_norm = array();
        foreach ($rows as $row) {
            $keyword = trim((string) ($row['keyword'] ?? ''));
            $norm = $this->aysa_keyword_strategy_normalize_keyword($keyword);
            if ($norm === '') {
                continue;
            }
            if (!isset($by_norm[$norm])) {
                $by_norm[$norm] = $row;
            }
        }

        foreach ($seed_keywords as $seed) {
            $norm = $this->aysa_keyword_strategy_normalize_keyword($seed);
            if ($norm === '' || isset($by_norm[$norm])) {
                continue;
            }
            $by_norm[$norm] = array(
                'keyword' => $seed,
                'search_volume' => null,
                'difficulty' => null,
                'intent' => '',
                'competition' => '',
                'traffic' => null,
                'traffic_pct' => null,
                'position' => null,
                'cpc' => null,
                'target_url' => '',
                'updated_at' => current_time('mysql'),
            );
        }

        $candidates = array();
        foreach ($by_norm as $norm => $row) {
            $relevance = $this->aysa_keyword_strategy_calculate_seed_relevance((string) ($row['keyword'] ?? ''), $seed_keywords);
            if (!isset($seed_norm_map[$norm]) && $relevance < 35) {
                continue;
            }
            $candidates[$norm] = array(
                'keyword' => (string) ($row['keyword'] ?? ''),
                'search_volume' => is_null($row['search_volume']) ? null : intval($row['search_volume']),
                'kd' => is_null($row['difficulty']) ? null : floatval($row['difficulty']),
                'intent' => (string) ($row['intent'] ?? ''),
                'traffic' => is_null($row['traffic']) ? null : intval($row['traffic']),
                'traffic_pct' => is_null($row['traffic_pct']) ? null : floatval($row['traffic_pct']),
                'position' => is_null($row['position']) ? null : floatval($row['position']),
                'cpc' => is_null($row['cpc']) ? null : floatval($row['cpc']),
                'target_url' => (string) ($row['target_url'] ?? ''),
                'updated_at' => (string) ($row['updated_at'] ?? ''),
                'seed_relevance' => $relevance,
                'source' => isset($seed_norm_map[$norm]) ? 'seed' : 'related_local',
            );
        }

        $items = array();
        foreach ($candidates as $norm => $row) {
            $assoc_list = isset($association_map[$norm]) && is_array($association_map[$norm]) ? $association_map[$norm] : array();
            $has_association = !empty($assoc_list);
            $has_target_url = trim((string) ($row['target_url'] ?? '')) !== '';
            $volume = is_null($row['search_volume']) ? 0 : intval($row['search_volume']);
            $kd = is_null($row['kd']) ? 60.0 : floatval($row['kd']);
            $traffic = is_null($row['traffic']) ? 0 : intval($row['traffic']);

            $recommended_action = 'enhance_existing';
            $reason = 'No direct page association found yet.';

            if ($has_association) {
                $recommended_action = 'associate_existing';
                $reason = 'Keyword already has an associated page/product/category.';
            } elseif ($has_target_url) {
                $recommended_action = 'associate_existing';
                $reason = 'Matched URL exists in keyword metrics and should be associated.';
            } elseif ($volume >= 40 && $kd <= 60.0) {
                $recommended_action = 'create_new_page';
                $reason = 'No current match and opportunity score suggests a dedicated page.';
            } else {
                $recommended_action = 'enhance_existing';
                $reason = 'Best next step is to strengthen existing relevant pages before creating new ones.';
            }

            $priority = 0;
            $priority += min(50, $volume);
            $priority += min(20, max(0, 20 - intval(round($kd / 5))));
            $priority += min(20, $traffic * 2);
            $priority += min(10, intval(round(($row['seed_relevance'] ?? 0) / 10)));
            if ($recommended_action === 'create_new_page') {
                $priority += 8;
            } elseif ($recommended_action === 'associate_existing') {
                $priority += 5;
            }

            $items[] = array(
                'keyword' => $row['keyword'],
                'source' => $row['source'],
                'seed_relevance' => intval($row['seed_relevance']),
                'intent' => ($row['intent'] !== '' ? $row['intent'] : 'N/A'),
                'search_volume' => ($volume > 0 ? $volume : null),
                'kd' => (!is_null($row['kd']) ? round(floatval($row['kd']), 2) : null),
                'traffic' => ($traffic > 0 ? $traffic : null),
                'traffic_pct' => (!is_null($row['traffic_pct']) ? round(floatval($row['traffic_pct']), 4) : null),
                'position' => (!is_null($row['position']) ? round(floatval($row['position']), 2) : null),
                'cpc' => (!is_null($row['cpc']) ? round(floatval($row['cpc']), 2) : null),
                'target_url' => ($has_target_url ? $row['target_url'] : ''),
                'associations' => array_values($assoc_list),
                'recommended_action' => $recommended_action,
                'reason' => $reason,
                'priority_score' => intval($priority),
                'status' => 'pending',
                'updated_at' => $row['updated_at'],
            );
        }

        usort($items, function($a, $b) {
            return intval($b['priority_score'] ?? 0) <=> intval($a['priority_score'] ?? 0);
        });

        $summary = array(
            'total_keywords' => count($items),
            'associate_existing' => 0,
            'enhance_existing' => 0,
            'create_new_page' => 0,
        );
        foreach ($items as $item) {
            $action = (string) ($item['recommended_action'] ?? '');
            if (isset($summary[$action])) {
                $summary[$action] += 1;
            }
        }

        return array(
            'project_id' => intval($project_id),
            'user_id' => intval($user_id),
            'generated_at' => current_time('mysql'),
            'source' => 'local_fallback',
            'seed_keywords' => array_values($seed_keywords),
            'competitors' => array_values($competitors),
            'context' => is_array($context) ? $context : array(),
            'summary' => $summary,
            'items' => array_values($items),
        );
    }

    public function handle_keyword_discovery_get_strategy() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        if (!$this->check_authentication(false)) {
            wp_send_json_error('Authentication required');
            return;
        }

        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }
        $auth_manager = Aysa_Auth_Manager::get_instance();
        $auth_status = $auth_manager->is_authenticated();
        $user_id = intval($auth_status['aysa_user_id'] ?? 0);
        if ($user_id <= 0) {
            wp_send_json_error('Invalid authentication context');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $option_key = $this->aysa_keyword_strategy_option_key($project_id, $user_id);
        $saved = get_option($option_key, array());
        if (!is_array($saved)) {
            $saved = array();
        }

        wp_send_json_success(array(
            'project_id' => $project_id,
            'user_id' => $user_id,
            'strategy' => $saved,
            'has_strategy' => !empty($saved),
        ));
    }

    public function handle_keyword_discovery_generate_strategy() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        if (!$this->check_authentication(false)) {
            wp_send_json_error('Authentication required');
            return;
        }

        if (!class_exists('Aysa_Auth_Manager')) {
            require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
        }
        $auth_manager = Aysa_Auth_Manager::get_instance();
        $auth_status = $auth_manager->is_authenticated();
        $user_id = intval($auth_status['aysa_user_id'] ?? 0);
        if ($user_id <= 0) {
            wp_send_json_error('Invalid authentication context');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $seed_data = $this->aysa_keyword_strategy_extract_seed_data($user_id);
        $seed_keywords = $seed_data['seed_keywords'];
        $competitors = $seed_data['competitors'];
        if (empty($seed_keywords)) {
            wp_send_json_error('No seed keywords found in Setup SEO Profile > Competition.');
            return;
        }

        $request = $this->aysa_research_async_prepare_keyword_strategy_request(array(
            'project_id' => $project_id,
        ));
        if (is_wp_error($request)) {
            wp_send_json_error($request->get_error_message());
            return;
        }

        $job = $this->aysa_project_jobs_enqueue(
            $project_id,
            'research',
            'keyword_strategy_generate',
            array(
                'task' => 'keyword_strategy_generate',
                'request' => $request,
            ),
            array(
                'dedupe_key' => (string) ($request['request_hash'] ?? ''),
                'input_summary' => array(
                    'seed_keywords_count' => count($seed_keywords),
                    'competitors_count' => count($competitors),
                ),
            )
        );
        if (is_wp_error($job)) {
            wp_send_json_error($job->get_error_message());
            return;
        }
        $job_row = isset($job['job']) && is_array($job['job']) ? $job['job'] : array();
        wp_send_json_success(array(
            'project_id' => $project_id,
            'job_id' => (string) ($job_row['job_id'] ?? ''),
            'status' => (string) ($job_row['status'] ?? 'queued'),
            'worker_ref' => (string) ($job_row['worker_ref'] ?? ''),
            'project_job_rack' => isset($job['rack']) && is_array($job['rack']) ? $job['rack'] : array(),
        ));
    }

    public function handle_keyword_discovery_refresh_start_background() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $apply_to_wp = !isset($_POST['apply_to_wp']) || in_array(strtolower((string) $_POST['apply_to_wp']), array('1', 'true', 'yes'), true);
        $refresh_kind = strtolower(trim((string) ($_POST['refresh_kind'] ?? 'gsc_auto')));
        $job = $this->aysa_project_jobs_enqueue(
            $project_id,
            'research',
            'keyword_discovery_refresh',
            array(
                'apply_to_wp' => $apply_to_wp,
                'refresh_kind' => $refresh_kind,
            ),
            array(
                'dedupe_key' => md5(wp_json_encode(array(
                    'job_type' => 'keyword_discovery_refresh',
                    'refresh_kind' => $refresh_kind,
                    'apply_to_wp' => $apply_to_wp ? 1 : 0,
                ))),
                'input_summary' => array(
                    'refresh_kind' => $refresh_kind,
                    'apply_to_wp' => $apply_to_wp ? 1 : 0,
                ),
            )
        );
        if (is_wp_error($job)) {
            wp_send_json_error($job->get_error_message());
            return;
        }
        $job_row = isset($job['job']) && is_array($job['job']) ? $job['job'] : array();
        $status = array();
        if ((string) ($job_row['status'] ?? '') === 'running' && !empty($job_row['worker_ref'])) {
            $parts = explode(':', (string) $job_row['worker_ref']);
            $run_id = (string) end($parts);
            if ($run_id !== '') {
                $status = $this->aysa_keyword_discovery_get_status($project_id);
            }
        }

        wp_send_json_success(array(
            'started' => (string) ($job_row['status'] ?? '') === 'running',
            'already_running' => !empty($job['deduped']),
            'status' => $status,
            'pending_item' => array(
                'item_key' => 'project_job:' . (string) ($job_row['job_id'] ?? ''),
                'item_type' => 'project_job',
                'post_id' => $project_id,
                'title' => 'Keyword refresh',
            ),
            'project_job_rack' => isset($job['rack']) && is_array($job['rack']) ? $job['rack'] : array(),
        ));
    }

    public function handle_keyword_discovery_refresh_status() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }
        $status = $this->aysa_keyword_discovery_get_status($project_id);
        if (($status['status'] ?? 'idle') === 'idle') {
            $job = $this->aysa_project_jobs_find_latest_job($project_id, 'research', 'keyword_discovery_refresh', array('queued', 'running', 'completed', 'failed'));
            if (is_array($job)) {
                $status = array(
                    'run_id' => (string) ($job['job_id'] ?? ''),
                    'refresh_kind' => 'gsc_auto',
                    'status' => (string) ($job['status'] ?? 'idle'),
                    'total' => 0,
                    'processed' => 0,
                    'staged' => 0,
                    'inserted' => 0,
                    'updated' => 0,
                    'apply_to_wp' => 1,
                    'last_error' => (string) ($job['error_summary'] ?? ''),
                    'metrics_notice' => '',
                    'notification_sent' => '0',
                    'started_at' => intval($job['started_at'] ?? 0),
                    'updated_at' => intval($job['updated_at'] ?? 0),
                    'finished_at' => intval($job['finished_at'] ?? 0),
                    'worker_ref' => (string) ($job['worker_ref'] ?? ''),
                );
            }
        }
        $status['project_job_rack'] = $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id));
        wp_send_json_success($status);
    }

    public function handle_keyword_discovery_apply_staged_to_local() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }
        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $snapshot = get_option($this->aysa_keyword_discovery_snapshot_key($project_id), array());
        $keywords = (is_array($snapshot) && isset($snapshot['keywords']) && is_array($snapshot['keywords'])) ? $snapshot['keywords'] : array();
        if (empty($keywords)) {
            wp_send_json_success(array(
                'applied' => false,
                'inserted' => 0,
                'updated' => 0,
                'count' => 0,
                'message' => 'No staged keywords available.',
            ));
            return;
        }

        $persist = $this->aysa_keyword_discovery_persist_keywords($project_id, $keywords);
        wp_send_json_success(array(
            'applied' => true,
            'inserted' => intval($persist['inserted'] ?? 0),
            'updated' => intval($persist['updated'] ?? 0),
            'count' => count($keywords),
            'snapshot_fetched_at' => intval($snapshot['fetched_at'] ?? 0),
        ));
    }

    private function aysa_keyword_discovery_get_seed_keywords_with_fallback($user_id) {
        $seed_data = $this->aysa_keyword_strategy_extract_seed_data($user_id);
        $seed_keywords = (is_array($seed_data) && isset($seed_data['seed_keywords']) && is_array($seed_data['seed_keywords']))
            ? $seed_data['seed_keywords']
            : array();

        $seed_keywords = array_values(array_unique(array_filter(array_map(function($kw) {
            return trim((string) $kw);
        }, $seed_keywords), function($kw) {
            return $kw !== '';
        })));

        // Strict isolation: do not fall back to other users' Setup data.
        // If current user's Setup SEO Profile has no seed keywords, return empty.
        return $seed_keywords;
    }

    private function aysa_keyword_discovery_fetch_keyword_suggestions($project_id, $seed_keywords, $context) {
        $payload = array(
            'project_id' => intval($project_id),
            'seed_keywords' => array_values($seed_keywords),
            'location_code' => intval($context['location_code'] ?? 2840),
            'language_code' => (string) ($context['language_code'] ?? 'ro'),
            'limit_per_seed' => 80,
            'max_keywords' => 2000,
        );

        $paths = array(
            '/v1/keywords/suggestions',
        );

        foreach ($paths as $path) {
            $result = $this->aysa_broker_request('POST', $path, $payload, 120);
            if (is_wp_error($result)) {
                continue;
            }
            $rows = $this->aysa_keyword_discovery_extract_metrics_rows($result);
            if (!empty($rows)) {
                return $rows;
            }
        }

        return array();
    }

    public function handle_keyword_discovery_keyword_suggestions_refresh() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        // Setup SEO Profile is stored by authenticated external user id (aysa_user_id),
        // not by WordPress user id. Use the same identity here to avoid false "no seed keywords".
        $setup_user_id = intval($this->aysa_get_authenticated_external_user_id());
        if ($setup_user_id <= 0) {
            $setup_user_id = get_current_user_id();
        }
        $seed_keywords = $this->aysa_keyword_discovery_get_seed_keywords_with_fallback($setup_user_id);
        if (empty($seed_keywords)) {
            wp_send_json_error('No seed keywords found in Setup SEO Profile > Competition.');
            return;
        }

        $result = $this->aysa_keyword_discovery_refresh_keyword_suggestions($project_id, $setup_user_id, true);
        if (is_wp_error($result)) {
            wp_send_json_error($result->get_error_message());
            return;
        }

        wp_send_json_success(array(
            'project_id' => $project_id,
            'seed_keywords' => $seed_keywords,
            'count' => intval($result['count'] ?? 0),
            'inserted' => intval($result['inserted'] ?? 0),
            'updated' => intval($result['updated'] ?? 0),
        ));
    }

    public function handle_keyword_discovery_v2_gsc_raw() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_keyword_cache_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }
        $force_refresh = !empty($_POST['force_refresh']);
        if (!$force_refresh) {
            $cached = $this->aysa_keyword_v2_cache_get($project_id, 'gsc_v2');
            if (is_array($cached)) {
                wp_send_json_success($cached);
                return;
            }
        }

        $site_host = wp_parse_url(home_url(), PHP_URL_HOST);
        $site_host = is_string($site_host) ? $site_host : '';

        $raw = $this->aysa_broker_request('POST', '/v1/google/keyword-mappings', array(
            'project_id' => $project_id,
            'site_host' => $site_host,
            'days' => 90,
            'limit' => 1000,
        ), 90);

        if (is_wp_error($raw)) {
            wp_send_json_error($raw->get_error_message());
            return;
        }
        if (!is_array($raw) || empty($raw['connected'])) {
            $reason = is_array($raw) && !empty($raw['error']) ? (string) $raw['error'] : 'Could not fetch raw GSC rows.';
            wp_send_json_error($reason);
            return;
        }

        $query_rows = isset($raw['gsc_queries']) && is_array($raw['gsc_queries']) ? $raw['gsc_queries'] : array();
        $mapping_rows = isset($raw['gsc_mappings']) && is_array($raw['gsc_mappings']) ? $raw['gsc_mappings'] : array();

        $rows = array();
        $mapped_queries = array();

        foreach ($mapping_rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['query'] ?? $row['keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }
            $mapped_queries[strtolower($keyword)] = true;
            $rows[] = array(
                'keyword' => $keyword,
                'page' => trim((string) ($row['page'] ?? $row['url'] ?? '')),
                'landing_page' => trim((string) ($row['page'] ?? $row['url'] ?? '')),
                'clicks' => intval($row['clicks'] ?? 0),
                'impressions' => intval($row['impressions'] ?? 0),
                'ctr' => isset($row['ctr']) ? floatval($row['ctr']) : null,
                'position' => isset($row['position']) ? floatval($row['position']) : null,
                'row_type' => 'query_page',
                'source' => 'gsc',
            );
        }

        foreach ($query_rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['query'] ?? $row['keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }
            if (isset($mapped_queries[strtolower($keyword)])) {
                continue;
            }
            $rows[] = array(
                'keyword' => $keyword,
                'page' => '',
                'landing_page' => '',
                'clicks' => intval($row['clicks'] ?? 0),
                'impressions' => intval($row['impressions'] ?? 0),
                'ctr' => isset($row['ctr']) ? floatval($row['ctr']) : null,
                'position' => isset($row['position']) ? floatval($row['position']) : null,
                'row_type' => 'query_only',
                'source' => 'gsc',
            );
        }

        usort($rows, static function($a, $b) {
            $clickDiff = intval($b['clicks'] ?? 0) <=> intval($a['clicks'] ?? 0);
            if ($clickDiff !== 0) {
                return $clickDiff;
            }
            $impressionDiff = intval($b['impressions'] ?? 0) <=> intval($a['impressions'] ?? 0);
            if ($impressionDiff !== 0) {
                return $impressionDiff;
            }
            return strcmp((string) ($a['keyword'] ?? ''), (string) ($b['keyword'] ?? ''));
        });

        $response = array(
            'rows' => $rows,
            'count' => count($rows),
            'diagnostics' => array(
                'connected' => !empty($raw['connected']),
                'selected_gsc_site' => (string) ($raw['selected_gsc_site'] ?? ''),
                'used_gsc_site' => (string) ($raw['used_gsc_site'] ?? ''),
                'gsc_queries_count' => count($query_rows),
                'gsc_mappings_count' => count($mapping_rows),
            ),
            'cache_meta' => array(
                'source' => 'live_refresh',
                'cached_at' => gmdate('c'),
            ),
            'quality_flags' => array_values(array_filter(array(
                empty($mapping_rows) ? 'no_query_page_rows' : '',
                empty($query_rows) ? 'no_query_only_rows' : '',
            ))),
            'explainability' => array(
                'included_pool' => 'Rows include mapped query-page pairs plus standalone GSC queries that are not already mapped.',
                'excluded_pool' => 'Duplicate GSC queries already represented by query-page rows are excluded from the standalone query pool.',
            ),
            'workspace_handoff' => array(
                'consumer' => 'keyword_mapping_v2',
                'source_tag' => 'gsc',
            ),
        );
        $response = $this->aysa_keyword_v2_finalize_snapshot_contract($project_id, 'gsc_v2', $response, 'rows');
        $this->aysa_keyword_v2_cache_set($project_id, 'gsc_v2', $response);
        wp_send_json_success($response);
    }

    public function handle_keyword_discovery_v2_dataforseo_raw() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_keyword_cache_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }
        $force_refresh = !empty($_POST['force_refresh']);
        if (!$force_refresh) {
            $cached = $this->aysa_keyword_v2_cache_get($project_id, 'dataforseo_v2');
            if (is_array($cached)) {
                wp_send_json_success($cached);
                return;
            }
        }

        $setup_user_id = intval($this->aysa_get_authenticated_external_user_id());
        if ($setup_user_id <= 0) {
            $setup_user_id = get_current_user_id();
        }
        $seed_keywords = $this->aysa_keyword_discovery_get_seed_keywords_with_fallback($setup_user_id);
        if (empty($seed_keywords)) {
            wp_send_json_error('No seed keywords found in Setup SEO Profile > Competition.');
            return;
        }

        $context = $this->aysa_monitoring_resolve_context('');
        $payload = array(
            'project_id' => intval($project_id),
            'seed_keywords' => array_values($seed_keywords),
            'location_code' => intval($context['location_code'] ?? 2840),
            'language_code' => (string) ($context['language_code'] ?? 'ro'),
            'limit_per_seed' => 1000,
            'max_keywords' => 10000,
            'raw_mode' => true,
        );

        $paths = array(
            '/v1/keywords/suggestions',
        );

        $rows = array();
        $provider = '';
        foreach ($paths as $path) {
            $result = $this->aysa_broker_request('POST', $path, $payload, 180);
            if (is_wp_error($result) || !is_array($result)) {
                continue;
            }
            $candidate_rows = isset($result['rows']) && is_array($result['rows']) ? $result['rows'] : array();
            if (!empty($candidate_rows)) {
                $rows = $candidate_rows;
                $provider = (string) ($result['provider'] ?? '');
                break;
            }
        }

        if (empty($rows)) {
            wp_send_json_error('DataForSEO returned no keyword rows.');
            return;
        }

        $normalized = array();
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }
            $normalized[] = array(
                'keyword' => $keyword,
                'seed_keyword' => trim((string) ($row['seed_keyword'] ?? '')),
                'source' => trim((string) ($row['source'] ?? '')),
                'intent' => trim((string) ($row['intent'] ?? '')),
                'search_volume' => isset($row['search_volume']) ? intval($row['search_volume']) : null,
                'kd' => isset($row['kd']) ? floatval($row['kd']) : null,
                'cpc' => isset($row['cpc']) ? floatval($row['cpc']) : null,
                'item_types' => isset($row['item_types']) && is_array($row['item_types']) ? array_values($row['item_types']) : array(),
            );
        }

        $response = array(
            'rows' => $normalized,
            'count' => count($normalized),
            'provider' => $provider,
            'seed_keywords' => array_values($seed_keywords),
            'cache_meta' => array(
                'source' => 'live_refresh',
                'cached_at' => gmdate('c'),
            ),
            'quality_flags' => array_values(array_filter(array(
                empty($normalized) ? 'empty_keyword_rows' : '',
                $provider === '' ? 'provider_unknown' : '',
            ))),
            'explainability' => array(
                'included_pool' => 'Rows combine keyword suggestions and related keywords normalized into one discovery pool.',
                'excluded_pool' => 'Rows without a valid keyword string are dropped before caching.',
            ),
            'workspace_handoff' => array(
                'consumer' => 'keyword_mapping_v2',
                'source_tag' => 'dataforseo',
                'seed_keywords' => array_values($seed_keywords),
            ),
        );
        $response = $this->aysa_keyword_v2_finalize_snapshot_contract($project_id, 'dataforseo_v2', $response, 'rows');
        $this->aysa_keyword_v2_cache_set($project_id, 'dataforseo_v2', $response);
        wp_send_json_success($response);
    }

    public function handle_keyword_discovery_v2_competitor_gap_raw() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_keyword_cache_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }
        $force_refresh = !empty($_POST['force_refresh']);
        if (!$force_refresh) {
            $cached = $this->aysa_keyword_v2_cache_get($project_id, 'competitor_gap_v2');
            if (is_array($cached)) {
                wp_send_json_success($cached);
                return;
            }
        }

        $setup_user_id = intval($this->aysa_get_authenticated_external_user_id());
        if ($setup_user_id <= 0) {
            $setup_user_id = get_current_user_id();
        }

        $seed_data = $this->aysa_keyword_strategy_extract_seed_data($setup_user_id);
        $raw_competitors = isset($seed_data['competitors']) && is_array($seed_data['competitors'])
            ? array_values(array_filter(array_map(static function($value) {
                return trim((string) $value);
            }, $seed_data['competitors']), static function($value) {
                return $value !== '';
            }))
            : array();

        if (empty($raw_competitors)) {
            wp_send_json_error('No competitors found in Setup SEO Profile > Competition.');
            return;
        }

        $target = (string) wp_parse_url(home_url('/'), PHP_URL_HOST);
        $target = trim($target);
        if ($target === '') {
            wp_send_json_error('Could not resolve target host.');
            return;
        }

        $context = $this->aysa_monitoring_resolve_context($target);
        $resolved_target = trim((string) ($context['target'] ?? ''));
        if ($resolved_target === '') {
            $resolved_target = $target;
        }

        $competitors = array();
        foreach ($raw_competitors as $competitor) {
            $host = $this->aysa_monitoring_extract_host((string) $competitor);
            if ($host === '' || $this->aysa_monitoring_is_local_host($host)) {
                continue;
            }
            if ($resolved_target !== '' && strtolower($host) === strtolower($resolved_target)) {
                continue;
            }
            $competitors[] = $host;
        }
        $competitors = array_values(array_unique($competitors));

        if (empty($competitors)) {
            wp_send_json_error('No valid competitor domains found in Setup SEO Profile > Competition.');
            return;
        }

        $payload = array(
            'project_id' => $project_id,
            'target' => $resolved_target,
            'location_code' => intval($context['location_code'] ?? 2840),
            'language_code' => (string) ($context['language_code'] ?? 'ro'),
            'limit' => 2000,
            'competitors' => array_values($competitors),
        );

        $paths = array(
            '/v1/keywords/gap/v2',
        );

        $result = null;
        $broker_errors = array();
        foreach ($paths as $path) {
            $candidate = $this->aysa_broker_request('POST', $path, $payload, 180);
            if (is_wp_error($candidate)) {
                $broker_errors[] = $path . ': ' . $candidate->get_error_message();
                continue;
            }
            if (is_array($candidate) && isset($candidate['data']) && is_array($candidate['data'])) {
                $candidate = $candidate['data'];
            }
            if (is_array($candidate) && isset($candidate['rows']) && is_array($candidate['rows'])) {
                $result = $candidate;
                break;
            }
            if (is_array($candidate) && isset($candidate['error'])) {
                $broker_errors[] = $path . ': ' . (string) $candidate['error'];
            } else {
                $broker_errors[] = $path . ': unexpected_response';
            }
        }

        if (!is_array($result)) {
            $message = 'Competitor gap returned no keyword rows.';
            if (!empty($broker_errors)) {
                $message .= ' ' . implode(' | ', array_slice($broker_errors, 0, 4));
            }
            wp_send_json_error($message);
            return;
        }
        $response = array(
            'rows' => array_values(isset($result['rows']) && is_array($result['rows']) ? $result['rows'] : array()),
            'count' => intval($result['count'] ?? 0),
            'competitors' => array_values(isset($result['competitors']) && is_array($result['competitors']) ? $result['competitors'] : $competitors),
            'our_keywords_count' => intval($result['our_keywords_count'] ?? 0),
            'missing_count' => intval($result['missing_count'] ?? 0),
            'common_count' => intval($result['common_count'] ?? 0),
            'provider' => (string) ($result['provider'] ?? ''),
            'cache_meta' => array(
                'source' => 'live_refresh',
                'cached_at' => gmdate('c'),
            ),
            'quality_flags' => array_values(array_filter(array(
                empty($result['rows']) ? 'empty_gap_rows' : '',
                empty($competitors) ? 'no_competitors' : '',
            ))),
            'explainability' => array(
                'included_pool' => 'Rows represent competitor gap keywords returned by the v2 gap endpoint for configured competitors.',
                'excluded_pool' => 'Invalid competitor hosts and empty keyword rows are excluded before caching.',
            ),
            'workspace_handoff' => array(
                'consumer' => 'keyword_mapping_v2',
                'source_tag' => 'gap',
                'competitor_count' => count($competitors),
            ),
        );
        $response = $this->aysa_keyword_v2_finalize_snapshot_contract($project_id, 'competitor_gap_v2', $response, 'rows');
        $this->aysa_keyword_v2_cache_set($project_id, 'competitor_gap_v2', $response);
        wp_send_json_success($response);
    }

    // LEGACY WORKSPACE ENDPOINT: do not use as the canonical Agent8 V3 Keyword Mapping rerun.
    // Agent8 V3 research must go through start_keyword_research_run -> KeywordResearchRunService -> plugin projection sync.
    public function handle_keyword_mapping_v1_workspace() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_keyword_cache_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }
        $force_refresh = !empty($_POST['force_refresh']);
        if (!$force_refresh) {
            $cached = $this->aysa_keyword_v2_cache_get($project_id, 'keyword_mapping_v2');
            if (is_array($cached)) {
                wp_send_json_success($cached);
                return;
            }
        }

        $payload = array(
            'project_id' => $project_id,
            'raw_context' => $this->aysa_collect_research_context_bundle($project_id),
            'force_refresh' => $force_refresh ? 1 : 0,
        );
        $result = $this->aysa_broker_request('POST', '/v1/research/workspaces/keyword-mapping', $payload, 240);
        if (is_wp_error($result)) {
            wp_send_json_error($result->get_error_message());
            return;
        }
        if (is_array($result) && isset($result['data']) && is_array($result['data'])) {
            $result = $result['data'];
        }
        if (!is_array($result) || empty($result['ok'])) {
            $message = is_array($result) && !empty($result['error']) ? (string) $result['error'] : 'Keyword Mapping workspace failed.';
            wp_send_json_error($message);
            return;
        }

        if (!isset($result['cache_meta']) || !is_array($result['cache_meta'])) {
            $result['cache_meta'] = array();
        }
        $result['cache_meta']['source'] = 'live_refresh';
        $result['cache_meta']['cached_at'] = gmdate('c');
        $result = $this->aysa_keyword_v2_finalize_snapshot_contract($project_id, 'keyword_mapping_v2', $result, 'clusters');
        $this->aysa_keyword_v2_cache_set($project_id, 'keyword_mapping_v2', $result);
        // LEGACY PROJECTION SYNC: this writes operational local associations only; it is not the source of truth.
        if (!empty($_POST['sync_local_projection'])) {
            $result['local_projection_sync'] = $this->aysa_keyword_mapping_sync_local_associations_from_workspace($project_id, $result);
        } else {
            $result['local_projection_sync'] = array(
                'ok' => true,
                'source' => 'keyword_mapping_v2',
                'status' => 'skipped',
                'reason' => 'explicit_sync_required',
            );
        }
        wp_send_json_success($result);
    }

    public function handle_keyword_mapping_v1_save_selection() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_keyword_cache_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $payload = array(
            'project_id' => $project_id,
            'cluster_id' => sanitize_text_field((string) ($_POST['cluster_id'] ?? '')),
            'keyword' => sanitize_text_field((string) ($_POST['keyword'] ?? '')),
            'selection_state' => sanitize_key((string) ($_POST['selection_state'] ?? 'review')),
        );

        $result = $this->aysa_broker_request('POST', '/v1/research/workspaces/save-selection', $payload, 60);
        if (is_wp_error($result)) {
            wp_send_json_error($result->get_error_message());
            return;
        }
        if (is_array($result) && isset($result['data']) && is_array($result['data'])) {
            $result = $result['data'];
        }
        if (!is_array($result) || empty($result['ok'])) {
            $message = is_array($result) && !empty($result['error']) ? (string) $result['error'] : 'Keyword Mapping selection save failed.';
            wp_send_json_error($message);
            return;
        }

        $this->aysa_keyword_v2_cache_delete($project_id, 'keyword_mapping_v2');
        $this->aysa_keyword_v2_cache_delete($project_id, 'missing_pages_v2');
        $this->aysa_keyword_v2_cache_delete($project_id, 'content_strategy_v2');

        wp_send_json_success($result);
    }

    public function handle_missing_pages_v1_workspace() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_keyword_cache_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }

        $force_refresh = !empty($_POST['force_refresh']);
        if (!$force_refresh) {
            $cached = $this->aysa_keyword_v2_cache_get($project_id, 'missing_pages_v2');
            if (is_array($cached)) {
                wp_send_json_success($cached);
                return;
            }
        }

        $payload = array(
            'project_id' => $project_id,
            'raw_context' => $this->aysa_collect_research_context_bundle($project_id),
            'force_refresh' => $force_refresh ? 1 : 0,
        );
        $result = $this->aysa_broker_request('POST', '/v1/research/workspaces/missing-pages', $payload, 240);
        if (is_wp_error($result)) {
            wp_send_json_error($result->get_error_message());
            return;
        }
        if (is_array($result) && isset($result['data']) && is_array($result['data'])) {
            $result = $result['data'];
        }
        if (!is_array($result) || empty($result['ok'])) {
            $message = is_array($result) && !empty($result['error']) ? (string) $result['error'] : 'Missing Pages workspace failed.';
            wp_send_json_error($message);
            return;
        }

        $result['cache_meta'] = isset($result['cache_meta']) && is_array($result['cache_meta']) ? $result['cache_meta'] : array();
        $result['cache_meta']['source'] = 'live_refresh';
        $result['cache_meta']['cached_at'] = gmdate('c');
        $result = $this->aysa_keyword_v2_finalize_snapshot_contract($project_id, 'missing_pages_v2', $result, 'groups');
        $this->aysa_keyword_v2_cache_set($project_id, 'missing_pages_v2', $result);
        wp_send_json_success($result);
    }

    public function handle_content_strategy_v1_workspace() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_keyword_cache_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
            return;
        }

        $project_id = intval($_POST['project_id'] ?? get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project id');
            return;
        }
        $site_host = wp_parse_url(home_url('/'), PHP_URL_HOST);
        $site_host = is_string($site_host) ? strtolower(trim((string) preg_replace('/^www\./i', '', $site_host))) : '';
        $canonical_project_id = intval($this->aysa_resolve_agent8_canonical_project_id($project_id, $site_host, array()));
        $workspace_project_id = $canonical_project_id > 0 ? $canonical_project_id : $project_id;

        $force_refresh = !empty($_POST['force_refresh']);
        if (!$force_refresh) {
            $cached = $this->aysa_keyword_v2_cache_get($project_id, 'content_strategy_v2');
            if (is_array($cached)) {
                wp_send_json_success($cached);
                return;
            }
        }

        $payload = array(
            'project_id' => $workspace_project_id,
            'canonical_project_id' => $canonical_project_id,
            'wp_project_id' => $project_id,
            'raw_context' => $this->aysa_collect_research_context_bundle($project_id),
            'force_refresh' => $force_refresh ? 1 : 0,
        );
        $result = $this->aysa_broker_request('POST', '/v1/research/workspaces/content-strategy', $payload, 240);
        if (is_wp_error($result)) {
            wp_send_json_error($result->get_error_message());
            return;
        }
        if (is_array($result) && isset($result['data']) && is_array($result['data'])) {
            $result = $result['data'];
        }
        if (!is_array($result) || empty($result['ok'])) {
            $message = is_array($result) && !empty($result['error']) ? (string) $result['error'] : 'Content Strategy workspace failed.';
            wp_send_json_error($message);
            return;
        }

        $result['cache_meta'] = isset($result['cache_meta']) && is_array($result['cache_meta']) ? $result['cache_meta'] : array();
        $result['cache_meta']['source'] = 'live_refresh';
        $result['cache_meta']['cached_at'] = gmdate('c');
        $this->aysa_keyword_v2_cache_set($project_id, 'content_strategy_v2', $result);
        wp_send_json_success($result);
    }

    private function aysa_keyword_mapping_v1_get_cached_workspace($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return null;
        }

        $cached = $this->aysa_keyword_v2_cache_get($project_id, 'keyword_mapping_v2');
        if (is_array($cached) && !empty($cached['ok']) && !empty($cached['clusters']) && is_array($cached['clusters'])) {
            return $cached;
        }

        return null;
    }

    private function aysa_adjustments_extract_keyword_mapping_cluster_payload($project_id) {
        $workspace = $this->aysa_keyword_mapping_v1_get_cached_workspace($project_id);
        if (!is_array($workspace) || empty($workspace['clusters']) || !is_array($workspace['clusters'])) {
            return null;
        }

        $selected = array();
        $review = array();
        $fallback = array();
        foreach ($workspace['clusters'] as $cluster) {
            if (!is_array($cluster)) {
                continue;
            }
            $state = sanitize_key((string) ($cluster['selection_state'] ?? 'review'));
            if ($state === 'excluded') {
                continue;
            }
            if ($state === 'selected') {
                $selected[] = $cluster;
            } elseif ($state === 'review') {
                $review[] = $cluster;
            } else {
                $fallback[] = $cluster;
            }
        }

        $clusters = !empty($selected) ? $selected : (!empty($review) ? $review : $fallback);
        if (empty($clusters)) {
            return null;
        }

        usort($clusters, static function ($left, $right) {
            return ((float) ($right['priority_score'] ?? 0.0)) <=> ((float) ($left['priority_score'] ?? 0.0));
        });
        $clusters = array_slice($clusters, 0, 8);

        $clusterGroups = array();
        $keywords = array();
        $gapKeywords = array();
        $mappedKeywords = array();
        $notMappedKeywords = array();
        $transactionalKeywords = array();
        $informationalKeywords = array();
        $localKeywords = array();
        $primaryKeyword = '';

        foreach ($clusters as $cluster) {
            $primary = isset($cluster['primary_keyword']) && is_array($cluster['primary_keyword']) ? $cluster['primary_keyword'] : array();
            $secondaryRows = isset($cluster['secondary_keywords']) && is_array($cluster['secondary_keywords']) ? $cluster['secondary_keywords'] : array();

            $clusterKeywords = array();
            $primaryText = sanitize_text_field((string) ($primary['keyword'] ?? ''));
            if ($primaryText !== '') {
                $clusterKeywords[] = $primaryText;
                if ($primaryKeyword === '') {
                    $primaryKeyword = $primaryText;
                }
            }

            foreach ($secondaryRows as $secondaryRow) {
                if (!is_array($secondaryRow)) {
                    continue;
                }
                $secondaryText = sanitize_text_field((string) ($secondaryRow['keyword'] ?? ''));
                if ($secondaryText === '' || in_array($secondaryText, $clusterKeywords, true)) {
                    continue;
                }
                $clusterKeywords[] = $secondaryText;
            }

            if (empty($clusterKeywords)) {
                continue;
            }

            foreach ($clusterKeywords as $keyword) {
                if (!in_array($keyword, $keywords, true)) {
                    $keywords[] = $keyword;
                }
            }

            $clusterSources = isset($cluster['sources']) && is_array($cluster['sources']) ? $cluster['sources'] : array();
            $mappingStatus = sanitize_key((string) ($cluster['mapping_status'] ?? 'not_mapped'));
            $intentSummary = sanitize_key((string) ($cluster['intent_summary'] ?? ($primary['intent'] ?? 'review')));
            foreach ($clusterKeywords as $keyword) {
                if (in_array('GAP', $clusterSources, true) && !in_array($keyword, $gapKeywords, true)) {
                    $gapKeywords[] = $keyword;
                }
                if ($mappingStatus === 'mapped' && !in_array($keyword, $mappedKeywords, true)) {
                    $mappedKeywords[] = $keyword;
                }
                if ($mappingStatus !== 'mapped' && !in_array($keyword, $notMappedKeywords, true)) {
                    $notMappedKeywords[] = $keyword;
                }
                if (in_array($intentSummary, array('transactional', 'commercial'), true) && !in_array($keyword, $transactionalKeywords, true)) {
                    $transactionalKeywords[] = $keyword;
                }
                if ($intentSummary === 'informational' && !in_array($keyword, $informationalKeywords, true)) {
                    $informationalKeywords[] = $keyword;
                }
                if ($intentSummary === 'local' && !in_array($keyword, $localKeywords, true)) {
                    $localKeywords[] = $keyword;
                }
            }

            $clusterGroups[] = array(
                'cluster_id' => sanitize_text_field((string) ($cluster['cluster_id'] ?? '')),
                'cluster_name' => sanitize_text_field((string) ($cluster['label'] ?? $primaryText)),
                'primary_keyword' => $primaryText,
                'target_keywords' => $clusterKeywords,
                'cluster_keywords' => $clusterKeywords,
                'intent' => sanitize_key((string) ($cluster['intent_summary'] ?? ($primary['intent'] ?? 'review'))),
                'mapping_status' => sanitize_key((string) ($cluster['mapping_status'] ?? 'not_mapped')),
                'mapping_confidence' => round((float) ($cluster['mapping_confidence'] ?? 0.0), 3),
                'target_page_type' => sanitize_key((string) ($cluster['target_page_type'] ?? 'page')),
                'business_fit_score' => round((float) ($cluster['business_fit_score'] ?? ($primary['business_fit_score'] ?? 0.0)), 3),
                'matched_page' => $this->aysa_adjustments_sanitize_match_payload($cluster['matched_page'] ?? null),
                'review_reason' => sanitize_text_field((string) ($cluster['review_context']['reason'] ?? ($cluster['explainability']['why_mapped'] ?? ''))),
                'review_context' => $this->aysa_adjustments_sanitize_review_context($cluster['review_context'] ?? array()),
                'page_matches' => $this->aysa_adjustments_sanitize_page_matches($cluster['explainability']['page_matches'] ?? array()),
                'priority_score' => round((float) ($cluster['priority_score'] ?? 0.0), 2),
                'sources' => isset($cluster['sources']) && is_array($cluster['sources']) ? array_values(array_filter(array_map('sanitize_text_field', $cluster['sources']))) : array(),
                'selection_state' => sanitize_key((string) ($cluster['selection_state'] ?? 'review')),
            );
        }

        if (empty($clusterGroups)) {
            return null;
        }

        return array(
            'primary_keyword' => $primaryKeyword !== '' ? $primaryKeyword : (string) ($keywords[0] ?? ''),
            'keywords' => array_slice($keywords, 0, 24),
            'gap_keywords' => array_slice($gapKeywords, 0, 24),
            'mapped_keywords' => array_slice($mappedKeywords, 0, 24),
            'not_mapped_keywords' => array_slice($notMappedKeywords, 0, 24),
            'transactional_keywords' => array_slice($transactionalKeywords, 0, 24),
            'informational_keywords' => array_slice($informationalKeywords, 0, 24),
            'local_keywords' => array_slice($localKeywords, 0, 24),
            'cluster_groups' => $clusterGroups,
            'cluster_count' => count($clusterGroups),
        );
    }

    private function aysa_adjustments_sanitize_match_payload($match) {
        if (!is_array($match)) {
            return null;
        }

        $url = esc_url_raw((string) ($match['url'] ?? ''));
        if ($url === '') {
            return null;
        }

        return array(
            'url' => $url,
            'title' => sanitize_text_field((string) ($match['title'] ?? '')),
            'type' => sanitize_key((string) ($match['type'] ?? '')),
            'taxonomy' => sanitize_key((string) ($match['taxonomy'] ?? '')),
            'confidence' => round((float) ($match['confidence'] ?? 0.0), 3),
            'primary_coverage' => round((float) ($match['primary_coverage'] ?? 0.0), 3),
            'modifier_coverage' => round((float) ($match['modifier_coverage'] ?? 0.0), 3),
        );
    }

    private function aysa_adjustments_sanitize_page_matches($matches) {
        $matches = is_array($matches) ? $matches : array();
        $sanitized = array();
        foreach (array_slice($matches, 0, 3) as $match) {
            $payload = $this->aysa_adjustments_sanitize_match_payload($match);
            if ($payload !== null) {
                $sanitized[] = $payload;
            }
        }

        return $sanitized;
    }

    private function aysa_adjustments_sanitize_review_context($context) {
        $context = is_array($context) ? $context : array();

        return array(
            'reason' => sanitize_text_field((string) ($context['reason'] ?? '')),
            'target_page_type' => sanitize_key((string) ($context['target_page_type'] ?? '')),
            'business_fit_score' => round((float) ($context['business_fit_score'] ?? 0.0), 3),
            'mapping_threshold' => round((float) ($context['mapping_threshold'] ?? 0.0), 3),
            'review_floor' => round((float) ($context['review_floor'] ?? 0.0), 3),
            'failed_checks' => array_values(array_filter(array_map('sanitize_key', is_array($context['failed_checks'] ?? null) ? $context['failed_checks'] : array()))),
            'top_candidates' => $this->aysa_adjustments_sanitize_page_matches($context['top_candidates'] ?? array()),
        );
    }

    private function aysa_adjustments_prepare_persona_cluster_groups($cluster_groups) {
        $cluster_groups = is_array($cluster_groups) ? $cluster_groups : array();
        if (empty($cluster_groups)) {
            return array();
        }

        $preferred = array();
        $secondary = array();
        foreach ($cluster_groups as $group) {
            if (!is_array($group)) {
                continue;
            }
            $keywords = array_values(array_filter(array_map('sanitize_text_field', (array) ($group['cluster_keywords'] ?? $group['target_keywords'] ?? array()))));
            if (empty($keywords)) {
                continue;
            }

            $intent = sanitize_key((string) ($group['intent'] ?? 'review'));
            if (in_array($intent, array('transactional', 'commercial', 'local'), true)) {
                $preferred[] = $group;
            } elseif ($intent === 'informational') {
                $secondary[] = $group;
            }
        }

        $ordered = array_merge($preferred, $secondary);
        usort($ordered, static function ($left, $right) {
            return ((float) ($right['priority_score'] ?? 0.0)) <=> ((float) ($left['priority_score'] ?? 0.0));
        });

        return array_slice($ordered, 0, 6);
    }

    private function aysa_adjustments_build_persona_scenarios($cluster_groups, $payload) {
        $cluster_groups = is_array($cluster_groups) ? $cluster_groups : array();
        $payload = is_array($payload) ? $payload : array();
        if (empty($cluster_groups)) {
            return array();
        }

        $city = sanitize_text_field((string) ($payload['city'] ?? ''));
        $business_name = sanitize_text_field((string) ($payload['business_name'] ?? ''));
        $industry = sanitize_text_field((string) ($payload['industry'] ?? ''));
        $scenarios = array();

        foreach ($cluster_groups as $group) {
            if (!is_array($group)) {
                continue;
            }

            $intent = sanitize_key((string) ($group['intent'] ?? 'informational'));
            $cluster_name = sanitize_text_field((string) ($group['cluster_name'] ?? ''));
            $primary_keyword = sanitize_text_field((string) ($group['primary_keyword'] ?? $cluster_name));
            $mapping_status = sanitize_key((string) ($group['mapping_status'] ?? 'not_mapped'));
            $target_page_type = sanitize_key((string) ($group['target_page_type'] ?? 'page'));
            $keyword_list = array_values(array_filter(array_map('sanitize_text_field', (array) ($group['target_keywords'] ?? $group['cluster_keywords'] ?? array()))));

            $persona = 'buyer researching options before ordering';
            $scenario = 'The user is evaluating options and wants to understand what best fits their need before making a purchase.';
            $pain_point = 'They are unsure which option is relevant and do not want to make the wrong choice.';
            $desired_outcome = 'Choose the right option faster and move confidently toward an order.';

            if ($intent === 'local') {
                $persona = $city !== ''
                    ? 'local buyer looking for a reliable option in ' . $city
                    : 'local buyer looking for a nearby reliable option';
                $scenario = $city !== ''
                    ? 'The user needs ' . $primary_keyword . ' in ' . $city . ' and wants a convenient, trustworthy local choice.'
                    : 'The user wants a relevant nearby option and needs local confidence before ordering.';
                $pain_point = 'They do not know which provider is nearby, reliable, and worth trusting.';
                $desired_outcome = 'Find the right local option quickly and order with confidence.';
            } elseif (in_array($intent, array('transactional', 'commercial'), true)) {
                $persona = 'buyer comparing options before placing an online order';
                $scenario = 'The user is close to purchase and is comparing ' . $primary_keyword . ' to decide what to order now.';
                if ($mapping_status !== 'mapped' || in_array($target_page_type, array('landing_page', 'category', 'service'), true)) {
                    $pain_point = 'They cannot immediately tell which option or page best matches what they want to buy.';
                    $desired_outcome = 'Understand the best-fit option fast and complete the purchase with less hesitation.';
                } else {
                    $pain_point = 'They want reassurance around fit, price, trust, or delivery before ordering.';
                    $desired_outcome = 'Choose confidently and move from comparison to purchase.';
                }
            } elseif ($intent === 'informational') {
                $persona = 'researching customer who wants to make a better buying decision';
                $scenario = 'The user is researching ' . $primary_keyword . ' before deciding what to buy from ' . ($business_name !== '' ? $business_name : ($industry !== '' ? $industry : 'the business')) . '.';
                $pain_point = 'They need practical guidance, not generic explanations, before taking the next step.';
                $desired_outcome = 'Get useful clarity that leads to a better purchase decision.';
            }

            $scenarios[] = array(
                'cluster_id' => sanitize_text_field((string) ($group['cluster_id'] ?? '')),
                'cluster_name' => $cluster_name !== '' ? $cluster_name : $primary_keyword,
                'primary_keyword' => $primary_keyword,
                'intent' => $intent !== '' ? $intent : 'informational',
                'mapping_status' => $mapping_status !== '' ? $mapping_status : 'not_mapped',
                'target_page_type' => $target_page_type !== '' ? $target_page_type : 'page',
                'persona' => $persona,
                'scenario' => $scenario,
                'pain_point' => $pain_point,
                'desired_outcome' => $desired_outcome,
                'target_keywords' => array_slice($keyword_list, 0, 6),
            );
        }

        return $scenarios;
    }

    private function aysa_adjustments_prepare_local_seo_cluster_groups($cluster_groups) {
        $cluster_groups = is_array($cluster_groups) ? $cluster_groups : array();
        if (empty($cluster_groups)) {
            return array();
        }

        $preferred = array();
        $secondary = array();
        foreach ($cluster_groups as $group) {
            if (!is_array($group)) {
                continue;
            }
            $keywords = array_values(array_filter(array_map('sanitize_text_field', (array) ($group['cluster_keywords'] ?? $group['target_keywords'] ?? array()))));
            if (empty($keywords)) {
                continue;
            }

            $intent = sanitize_key((string) ($group['intent'] ?? 'review'));
            if ($intent === 'local') {
                $preferred[] = $group;
            } elseif (in_array($intent, array('transactional', 'commercial'), true)) {
                $secondary[] = $group;
            }
        }

        $ordered = array_merge($preferred, $secondary);
        usort($ordered, static function ($left, $right) {
            return ((float) ($right['priority_score'] ?? 0.0)) <=> ((float) ($left['priority_score'] ?? 0.0));
        });

        return array_slice($ordered, 0, 6);
    }

    private function aysa_adjustments_build_local_context($cluster_groups, $payload) {
        $cluster_groups = is_array($cluster_groups) ? $cluster_groups : array();
        $payload = is_array($payload) ? $payload : array();

        $city = sanitize_text_field((string) ($payload['city'] ?? ''));
        $country = sanitize_text_field((string) ($payload['country'] ?? ''));
        $region = sanitize_text_field((string) ($payload['target_region'] ?? ''));
        $business_name = sanitize_text_field((string) ($payload['business_name'] ?? ''));
        $neighborhoods = array_values(array_filter(array_map('sanitize_text_field', (array) ($payload['neighborhoods'] ?? array()))));

        $cluster_contexts = array();
        foreach ($cluster_groups as $group) {
            if (!is_array($group)) {
                continue;
            }

            $cluster_name = sanitize_text_field((string) ($group['cluster_name'] ?? ''));
            $primary_keyword = sanitize_text_field((string) ($group['primary_keyword'] ?? $cluster_name));
            $intent = sanitize_key((string) ($group['intent'] ?? 'local'));
            $mapping_status = sanitize_key((string) ($group['mapping_status'] ?? 'not_mapped'));

            $search_scenario = $city !== ''
                ? 'The user wants ' . $primary_keyword . ' in ' . $city . ' and needs a trustworthy nearby option.'
                : 'The user is searching for a relevant local option and needs nearby confidence before ordering.';
            $local_friction = 'They are unsure which provider actually serves their area well and can be trusted locally.';
            $desired_outcome = 'Find the best nearby option quickly and order with confidence.';

            if ($mapping_status === 'mapped') {
                $local_friction = 'They need reassurance that this local option is the right fit compared with alternatives nearby.';
                $desired_outcome = 'Choose the existing local option confidently and convert faster.';
            } elseif (in_array($intent, array('transactional', 'commercial'), true)) {
                $local_friction = 'They are close to buying but need clarity on local fit, delivery area, or convenience.';
                $desired_outcome = 'Understand local relevance quickly and move to purchase.';
            }

            $cluster_contexts[] = array(
                'cluster_id' => sanitize_text_field((string) ($group['cluster_id'] ?? '')),
                'cluster_name' => $cluster_name !== '' ? $cluster_name : $primary_keyword,
                'primary_keyword' => $primary_keyword,
                'intent' => $intent !== '' ? $intent : 'local',
                'search_scenario' => $search_scenario,
                'local_friction' => $local_friction,
                'desired_outcome' => $desired_outcome,
                'target_keywords' => array_values(array_filter(array_map('sanitize_text_field', (array) ($group['target_keywords'] ?? array())))),
            );
        }

        return array(
            'city' => $city,
            'region' => $region,
            'country' => $country,
            'business_name' => $business_name,
            'neighborhoods' => $neighborhoods,
            'cluster_contexts' => $cluster_contexts,
        );
    }

    private function aysa_adjustments_prepare_authority_cluster_groups($cluster_groups) {
        $cluster_groups = is_array($cluster_groups) ? $cluster_groups : array();
        if (empty($cluster_groups)) {
            return array();
        }

        $preferred = array();
        $secondary = array();
        foreach ($cluster_groups as $group) {
            if (!is_array($group)) {
                continue;
            }
            $keywords = array_values(array_filter(array_map('sanitize_text_field', (array) ($group['cluster_keywords'] ?? $group['target_keywords'] ?? array()))));
            if (empty($keywords)) {
                continue;
            }

            $intent = sanitize_key((string) ($group['intent'] ?? 'review'));
            $mapping_status = sanitize_key((string) ($group['mapping_status'] ?? 'not_mapped'));
            if (in_array($intent, array('commercial', 'transactional', 'informational'), true)) {
                if ($mapping_status === 'mapped') {
                    $preferred[] = $group;
                } else {
                    $secondary[] = $group;
                }
            }
        }

        $ordered = array_merge($preferred, $secondary);
        usort($ordered, static function ($left, $right) {
            return ((float) ($right['priority_score'] ?? 0.0)) <=> ((float) ($left['priority_score'] ?? 0.0));
        });

        return array_slice($ordered, 0, 6);
    }

    private function aysa_adjustments_build_authority_context($cluster_groups, $payload) {
        $cluster_groups = is_array($cluster_groups) ? $cluster_groups : array();
        $payload = is_array($payload) ? $payload : array();
        if (empty($cluster_groups)) {
            return array();
        }

        $business_name = sanitize_text_field((string) ($payload['business_name'] ?? ''));
        $industry = sanitize_text_field((string) ($payload['industry'] ?? ''));
        $company_info = sanitize_text_field((string) ($payload['company_info'] ?? ''));
        $city = sanitize_text_field((string) ($payload['city'] ?? ''));
        $contexts = array();

        foreach ($cluster_groups as $group) {
            if (!is_array($group)) {
                continue;
            }

            $cluster_name = sanitize_text_field((string) ($group['cluster_name'] ?? ''));
            $primary_keyword = sanitize_text_field((string) ($group['primary_keyword'] ?? $cluster_name));
            $intent = sanitize_key((string) ($group['intent'] ?? 'informational'));
            $mapping_status = sanitize_key((string) ($group['mapping_status'] ?? 'not_mapped'));
            $page_type = sanitize_key((string) ($group['target_page_type'] ?? 'page'));

            $authority_goal = 'Give the reader concrete confidence through useful expertise and real decision support.';
            $trust_signal = 'practical expertise';
            $angle_suggestions = array('expert explanation', 'buying guidance');

            if ($mapping_status === 'mapped') {
                $authority_goal = 'Strengthen trust around an existing commercial page by adding expertise, reassurance, and comparison value.';
                $trust_signal = 'experience-backed guidance';
                $angle_suggestions = array('case study', 'review', 'comparison');
            } elseif (in_array($intent, array('commercial', 'transactional'), true)) {
                $authority_goal = 'Help the reader evaluate options more confidently before buying.';
                $trust_signal = 'decision confidence';
                $angle_suggestions = array('comparison', 'quality framework', 'buying guidance');
            } elseif ($intent === 'informational') {
                $authority_goal = 'Demonstrate real expertise with practical, experience-backed explanations.';
                $trust_signal = 'expert explanation';
                $angle_suggestions = array('expert explanation', 'practical review', 'experience-backed advice');
            }

            $contexts[] = array(
                'cluster_id' => sanitize_text_field((string) ($group['cluster_id'] ?? '')),
                'cluster_name' => $cluster_name !== '' ? $cluster_name : $primary_keyword,
                'primary_keyword' => $primary_keyword,
                'intent' => $intent !== '' ? $intent : 'informational',
                'mapping_status' => $mapping_status !== '' ? $mapping_status : 'not_mapped',
                'target_page_type' => $page_type !== '' ? $page_type : 'page',
                'authority_goal' => $authority_goal,
                'trust_signal' => $trust_signal,
                'angle_suggestions' => $angle_suggestions,
                'business_name' => $business_name,
                'industry' => $industry,
                'company_info' => $company_info,
                'city' => $city,
                'target_keywords' => array_values(array_filter(array_map('sanitize_text_field', (array) ($group['target_keywords'] ?? array())))),
            );
        }

        return array(
            'business_name' => $business_name,
            'industry' => $industry,
            'company_info' => $company_info,
            'city' => $city,
            'cluster_contexts' => $contexts,
        );
    }

    private function aysa_adjustments_build_customer_journey_context($cluster_groups, $payload) {
        $cluster_groups = is_array($cluster_groups) ? $cluster_groups : array();
        $payload = is_array($payload) ? $payload : array();
        if (empty($cluster_groups)) {
            return array();
        }

        $business_name = sanitize_text_field((string) ($payload['business_name'] ?? ''));
        $city = sanitize_text_field((string) ($payload['city'] ?? ''));
        $journey_rows = array();

        foreach ($cluster_groups as $group) {
            if (!is_array($group)) {
                continue;
            }

            $cluster_name = sanitize_text_field((string) ($group['cluster_name'] ?? ''));
            $primary_keyword = sanitize_text_field((string) ($group['primary_keyword'] ?? $cluster_name));
            $intent = sanitize_key((string) ($group['intent'] ?? 'informational'));
            $mapping_status = sanitize_key((string) ($group['mapping_status'] ?? 'not_mapped'));

            $awareness = 'The user is early in the journey, notices a problem or need, and is unsure what the right direction is.';
            $consideration = 'The user is evaluating options, comparisons, and trade-offs to understand what solution fits best.';
            $decision = 'The user is ready to choose a provider and needs trust, clarity on expectations, and confidence to buy.';

            if (in_array($intent, array('commercial', 'transactional'), true)) {
                $awareness = 'The user knows they need something related to ' . $primary_keyword . ' but is still clarifying what is actually appropriate.';
                $consideration = 'The user is comparing options for ' . $primary_keyword . ' and wants to understand differences before buying.';
                $decision = 'The user is close to purchase and wants confidence around provider choice, fit, and ordering expectations.';
            } elseif ($intent === 'local') {
                $awareness = 'The user realizes they need a local option' . ($city !== '' ? ' in ' . $city : '') . ' but is not yet sure what to look for.';
                $consideration = 'The user is comparing local options and wants to understand which one fits the need best.';
                $decision = 'The user wants to choose the right local provider with minimal risk and high confidence.';
            }

            $journey_rows[] = array(
                'cluster_id' => sanitize_text_field((string) ($group['cluster_id'] ?? '')),
                'cluster_name' => $cluster_name !== '' ? $cluster_name : $primary_keyword,
                'primary_keyword' => $primary_keyword,
                'intent' => $intent !== '' ? $intent : 'informational',
                'mapping_status' => $mapping_status !== '' ? $mapping_status : 'not_mapped',
                'business_name' => $business_name,
                'city' => $city,
                'awareness_context' => $awareness,
                'consideration_context' => $consideration,
                'decision_context' => $decision,
                'target_keywords' => array_values(array_filter(array_map('sanitize_text_field', (array) ($group['target_keywords'] ?? array())))),
            );
        }

        return array(
            'business_name' => $business_name,
            'city' => $city,
            'cluster_contexts' => $journey_rows,
        );
    }

    private function aysa_adjustments_build_evergreen_fresh_context($cluster_groups, $payload) {
        $cluster_groups = is_array($cluster_groups) ? $cluster_groups : array();
        $payload = is_array($payload) ? $payload : array();
        if (empty($cluster_groups)) {
            return array();
        }

        $business_name = sanitize_text_field((string) ($payload['business_name'] ?? ''));
        $industry = sanitize_text_field((string) ($payload['industry'] ?? ''));
        $city = sanitize_text_field((string) ($payload['city'] ?? ''));
        $company_info = sanitize_text_field((string) ($payload['company_info'] ?? ''));
        $contexts = array();

        foreach ($cluster_groups as $group) {
            if (!is_array($group)) {
                continue;
            }

            $cluster_name = sanitize_text_field((string) ($group['cluster_name'] ?? ''));
            $primary_keyword = sanitize_text_field((string) ($group['primary_keyword'] ?? $cluster_name));
            $intent = sanitize_key((string) ($group['intent'] ?? 'informational'));
            $mapping_status = sanitize_key((string) ($group['mapping_status'] ?? 'not_mapped'));

            $evergreen_reason = 'This topic solves a stable problem or supports a long-term decision.';
            $fresh_reason = 'This topic can be made timely through seasonality, trends, recent changes, or current urgency.';
            if (in_array($intent, array('commercial', 'transactional'), true)) {
                $evergreen_reason = 'This topic supports recurring buying decisions and remains commercially relevant over time.';
                $fresh_reason = 'This topic can be refreshed through current offers, seasonal demand, or timely comparison needs.';
            }

            $contexts[] = array(
                'cluster_id' => sanitize_text_field((string) ($group['cluster_id'] ?? '')),
                'cluster_name' => $cluster_name !== '' ? $cluster_name : $primary_keyword,
                'primary_keyword' => $primary_keyword,
                'intent' => $intent !== '' ? $intent : 'informational',
                'mapping_status' => $mapping_status !== '' ? $mapping_status : 'not_mapped',
                'evergreen_reason' => $evergreen_reason,
                'fresh_reason' => $fresh_reason,
                'target_keywords' => array_values(array_filter(array_map('sanitize_text_field', (array) ($group['target_keywords'] ?? array())))),
            );
        }

        return array(
            'business_name' => $business_name,
            'industry' => $industry,
            'city' => $city,
            'company_info' => $company_info,
            'cluster_contexts' => $contexts,
        );
    }

    private function aysa_adjustments_prepare_competitor_cluster_groups($cluster_groups) {
        $cluster_groups = is_array($cluster_groups) ? $cluster_groups : array();
        if (empty($cluster_groups)) {
            return array();
        }

        $prepared = array();
        foreach ($cluster_groups as $group) {
            if (!is_array($group)) {
                continue;
            }

            $keywords = array_values(array_filter(array_map('sanitize_text_field', (array) ($group['cluster_keywords'] ?? $group['target_keywords'] ?? array()))));
            if (empty($keywords)) {
                continue;
            }

            $sources = array_values(array_filter(array_map('sanitize_text_field', (array) ($group['sources'] ?? array()))));
            $mapping_status = sanitize_key((string) ($group['mapping_status'] ?? 'not_mapped'));
            $intent = sanitize_key((string) ($group['intent'] ?? 'informational'));
            $score = (float) ($group['priority_score'] ?? 0.0);

            if (in_array('GAP', $sources, true)) {
                $score += 40.0;
            }
            if ($mapping_status !== 'mapped') {
                $score += 20.0;
            }
            if (in_array($intent, array('transactional', 'commercial', 'local'), true)) {
                $score += 10.0;
            } elseif ($intent === 'informational') {
                $score += 3.0;
            }

            $group['_competitor_score'] = $score;
            $prepared[] = $group;
        }

        usort($prepared, static function ($left, $right) {
            return ((float) ($right['_competitor_score'] ?? 0.0)) <=> ((float) ($left['_competitor_score'] ?? 0.0));
        });

        $prepared = array_slice($prepared, 0, 6);
        foreach ($prepared as &$group) {
            unset($group['_competitor_score']);
        }
        unset($group);

        return $prepared;
    }

    private function aysa_adjustments_build_competitor_title_signals($project_id, $cluster_groups, $payload, $competitor_domains) {
        $project_id = intval($project_id);
        $cluster_groups = is_array($cluster_groups) ? $cluster_groups : array();
        $payload = is_array($payload) ? $payload : array();
        $competitor_domains = array_values(array_filter(array_map('sanitize_text_field', (array) $competitor_domains)));

        $cluster_map = array();
        foreach ($cluster_groups as $group) {
            if (!is_array($group)) {
                continue;
            }

            $cluster_id = sanitize_text_field((string) ($group['cluster_id'] ?? ''));
            $cluster_name = sanitize_text_field((string) ($group['cluster_name'] ?? ''));
            $primary_keyword = sanitize_text_field((string) ($group['primary_keyword'] ?? $cluster_name));
            $keywords = array_values(array_filter(array_map('sanitize_text_field', (array) ($group['target_keywords'] ?? $group['cluster_keywords'] ?? array()))));
            if ($primary_keyword === '' && !empty($keywords)) {
                $primary_keyword = (string) $keywords[0];
            }

            foreach ($keywords as $keyword) {
                $norm = $this->aysa_keyword_strategy_normalize_keyword($keyword);
                if ($norm === '' || isset($cluster_map[$norm])) {
                    continue;
                }
                $cluster_map[$norm] = array(
                    'cluster_id' => $cluster_id,
                    'cluster_name' => $cluster_name !== '' ? $cluster_name : $primary_keyword,
                    'primary_keyword' => $primary_keyword !== '' ? $primary_keyword : $keyword,
                    'target_keywords' => array_slice($keywords, 0, 6),
                    'intent' => sanitize_key((string) ($group['intent'] ?? 'informational')),
                    'mapping_status' => sanitize_key((string) ($group['mapping_status'] ?? 'not_mapped')),
                );
            }
        }

        $signals = array();
        $seen = array();
        $gap_snapshot = $this->aysa_keyword_v2_cache_get($project_id, 'competitor_gap_v2');
        $gap_rows = is_array($gap_snapshot) && !empty($gap_snapshot['rows']) && is_array($gap_snapshot['rows'])
            ? $gap_snapshot['rows']
            : array();

        foreach ($gap_rows as $row) {
            if (!is_array($row)) {
                continue;
            }

            $keyword = sanitize_text_field((string) ($row['keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }

            $norm = $this->aysa_keyword_strategy_normalize_keyword($keyword);
            $cluster = $cluster_map[$norm] ?? null;
            if (!is_array($cluster)) {
                continue;
            }

            $row_competitors = array();
            $raw_competitors = $row['competitors'] ?? array();
            if (is_string($raw_competitors)) {
                $decoded = json_decode($raw_competitors, true);
                $raw_competitors = is_array($decoded) ? $decoded : preg_split('/\s*,\s*/', $raw_competitors);
            }
            foreach ((array) $raw_competitors as $value) {
                $value = sanitize_text_field((string) $value);
                if ($value !== '' && !in_array($value, $row_competitors, true)) {
                    $row_competitors[] = $value;
                }
            }
            if (empty($row_competitors)) {
                $row_competitors = $competitor_domains;
            }

            $lead_domain = sanitize_text_field((string) ($row_competitors[0] ?? ''));
            $signal_key = $norm . '|' . strtolower($lead_domain);
            if ($signal_key === '|' || isset($seen[$signal_key])) {
                continue;
            }
            $seen[$signal_key] = true;

            $signals[] = array(
                'competitor_title' => sanitize_text_field((string) ($row['competitor_title'] ?? $row['title'] ?? '')),
                'competitor_domain' => $lead_domain,
                'competitor_domains' => array_slice($row_competitors, 0, 5),
                'source_keyword' => $keyword,
                'gap_type' => sanitize_key((string) ($row['gap_type'] ?? 'missing')),
                'search_volume' => intval($row['search_volume'] ?? 0),
                'competitor_count' => intval($row['competitor_count'] ?? count($row_competitors)),
                'cluster_id' => (string) ($cluster['cluster_id'] ?? ''),
                'cluster_name' => (string) ($cluster['cluster_name'] ?? $keyword),
                'primary_keyword' => (string) ($cluster['primary_keyword'] ?? $keyword),
                'target_keywords' => array_values((array) ($cluster['target_keywords'] ?? array($keyword))),
                'intent' => (string) ($cluster['intent'] ?? 'informational'),
                'mapping_status' => (string) ($cluster['mapping_status'] ?? 'not_mapped'),
            );

            if (count($signals) >= 18) {
                break;
            }
        }

        if (empty($signals)) {
            foreach ($cluster_groups as $group) {
                if (!is_array($group)) {
                    continue;
                }
                $primary_keyword = sanitize_text_field((string) ($group['primary_keyword'] ?? $group['cluster_name'] ?? ''));
                $target_keywords = array_values(array_filter(array_map('sanitize_text_field', (array) ($group['target_keywords'] ?? $group['cluster_keywords'] ?? array()))));
                if ($primary_keyword === '' && !empty($target_keywords)) {
                    $primary_keyword = (string) $target_keywords[0];
                }
                if ($primary_keyword === '') {
                    continue;
                }
                $signals[] = array(
                    'competitor_title' => '',
                    'competitor_domain' => sanitize_text_field((string) ($competitor_domains[0] ?? '')),
                    'competitor_domains' => array_slice($competitor_domains, 0, 5),
                    'source_keyword' => $primary_keyword,
                    'gap_type' => 'theme_gap',
                    'search_volume' => 0,
                    'competitor_count' => count($competitor_domains),
                    'cluster_id' => sanitize_text_field((string) ($group['cluster_id'] ?? '')),
                    'cluster_name' => sanitize_text_field((string) ($group['cluster_name'] ?? $primary_keyword)),
                    'primary_keyword' => $primary_keyword,
                    'target_keywords' => array_slice($target_keywords, 0, 6),
                    'intent' => sanitize_key((string) ($group['intent'] ?? 'informational')),
                    'mapping_status' => sanitize_key((string) ($group['mapping_status'] ?? 'not_mapped')),
                );
                if (count($signals) >= 12) {
                    break;
                }
            }
        }

        return $signals;
    }

    private function aysa_adjustments_enrich_competitor_generated_items($items, $payload) {
        $items = is_array($items) ? $items : array();
        $payload = is_array($payload) ? $payload : array();

        $fresh_rows = isset($payload['competitor_fresh_content']['rows']) && is_array($payload['competitor_fresh_content']['rows'])
            ? $payload['competitor_fresh_content']['rows']
            : array();
        $signal_rows = isset($payload['competitor_title_signals']) && is_array($payload['competitor_title_signals'])
            ? $payload['competitor_title_signals']
            : array();

        $fresh_by_title = array();
        foreach ($fresh_rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $title = sanitize_text_field((string) ($row['title'] ?? ''));
            if ($title === '') {
                continue;
            }
            $fresh_by_title[strtolower($title)] = array(
                'competitor_title' => $title,
                'competitor_domain' => sanitize_text_field((string) ($row['competitor_domain'] ?? '')),
                'competitor_url' => esc_url_raw((string) ($row['url'] ?? '')),
            );
        }

        $signal_by_title = array();
        foreach ($signal_rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $title = sanitize_text_field((string) ($row['competitor_title'] ?? ''));
            if ($title === '') {
                continue;
            }
            $signal_by_title[strtolower($title)] = array(
                'competitor_title' => $title,
                'competitor_domain' => sanitize_text_field((string) ($row['competitor_domain'] ?? '')),
                'competitor_url' => '',
            );
        }

        foreach ($items as &$item) {
            if (!is_array($item)) {
                continue;
            }
            $title_key = strtolower(sanitize_text_field((string) ($item['competitor_title'] ?? '')));
            $match = array();
            if ($title_key !== '' && isset($fresh_by_title[$title_key])) {
                $match = $fresh_by_title[$title_key];
            } elseif ($title_key !== '' && isset($signal_by_title[$title_key])) {
                $match = $signal_by_title[$title_key];
            }

            if (!empty($match)) {
                if (empty($item['competitor_title'])) {
                    $item['competitor_title'] = (string) ($match['competitor_title'] ?? '');
                }
                if (empty($item['competitor_domain'])) {
                    $item['competitor_domain'] = (string) ($match['competitor_domain'] ?? '');
                }
                if (empty($item['competitor_url'])) {
                    $item['competitor_url'] = (string) ($match['competitor_url'] ?? '');
                }
            }
        }
        unset($item);

        return $items;
    }

    private function aysa_adjustments_build_competitor_analysis_context($cluster_groups, $payload, $title_signals, $fresh_discovery = array()) {
        $cluster_groups = is_array($cluster_groups) ? $cluster_groups : array();
        $payload = is_array($payload) ? $payload : array();
        $title_signals = is_array($title_signals) ? $title_signals : array();
        $fresh_discovery = is_array($fresh_discovery) ? $fresh_discovery : array();

        $domains = array();
        foreach ($title_signals as $signal) {
            if (!is_array($signal)) {
                continue;
            }
            $domain = sanitize_text_field((string) ($signal['competitor_domain'] ?? ''));
            if ($domain !== '' && !in_array($domain, $domains, true)) {
                $domains[] = $domain;
            }
        }
        foreach ((array) ($fresh_discovery['rows'] ?? array()) as $row) {
            if (!is_array($row)) {
                continue;
            }
            $domain = sanitize_text_field((string) ($row['competitor_domain'] ?? ''));
            if ($domain !== '' && !in_array($domain, $domains, true)) {
                $domains[] = $domain;
            }
        }

        return array(
            'business_type' => trim((string) ($payload['industry'] ?? '')),
            'business_name' => trim((string) ($payload['business_name'] ?? '')),
            'city' => trim((string) ($payload['city'] ?? '')),
            'target_audience' => trim((string) ($payload['company_info'] ?? '')),
            'competitor_domains' => array_slice($domains, 0, 5),
            'clusters_in_scope' => count($cluster_groups),
            'signal_count' => count($title_signals),
            'fresh_article_count' => intval($fresh_discovery['count'] ?? 0),
            'fresh_provider' => trim((string) ($fresh_discovery['provider'] ?? '')),
            'fresh_date_from' => trim((string) ($fresh_discovery['date_from'] ?? '')),
            'fresh_date_to' => trim((string) ($fresh_discovery['date_to'] ?? '')),
            'focus' => 'Respond to competitor themes with clearer, more useful, more strategic titles that fit the business and selected keyword clusters.',
            'fresh_discovery_status' => !empty($fresh_discovery['ok'])
                ? ((intval($fresh_discovery['count'] ?? 0) > 0) ? 'connected_with_rows' : 'connected_empty')
                : 'connected_error',
        );
    }

    private function aysa_adjustments_get_competitor_fresh_content($project_id, $competitor_domains) {
        $project_id = intval($project_id);
        $competitor_domains = array_values(array_filter(array_map('sanitize_text_field', (array) $competitor_domains)));
        if ($project_id <= 0 || empty($competitor_domains)) {
            return array(
                'ok' => false,
                'rows' => array(),
                'count' => 0,
                'provider' => '',
                'date_from' => '',
                'date_to' => '',
                'errors' => array(array('error' => 'missing_project_or_competitors')),
            );
        }

        $cache_key = 'competitor_fresh_content_v1';
        $memory = $this->aysa_adjustments_get_competitor_fresh_memory($project_id);
        $cached = $this->aysa_keyword_v2_cache_get($project_id, $cache_key);
        if (is_array($cached)) {
            $cached_domains = array_values(array_filter(array_map('sanitize_text_field', (array) ($cached['competitors'] ?? array()))));
            $cached_at = strtotime((string) ($cached['cache_meta']['cached_at'] ?? ''));
            $cached_count = intval($cached['count'] ?? (is_array($cached['rows'] ?? null) ? count((array) $cached['rows']) : 0));
            if ($cached_domains === $competitor_domains
                && $cached_at !== false
                && $cached_at >= (time() - 12 * HOUR_IN_SECONDS)
                && $cached_count > 0) {
                return $cached;
            }
        }

        $date_to = gmdate('Y-m-d');
        $date_from = gmdate('Y-m-d', strtotime('-30 days'));
        $payload = array(
            'competitors' => $competitor_domains,
            'date_from' => $date_from,
            'date_to' => $date_to,
            'limit_per_domain' => 5,
            'language' => 'ro',
        );

        $result = $this->aysa_broker_request('POST', '/v1/competitors/fresh-content/google', $payload, 90);
        if (is_wp_error($result)) {
            return array(
                'ok' => false,
                'rows' => array(),
                'count' => 0,
                'competitors' => $competitor_domains,
                'provider' => '',
                'date_from' => $date_from,
                'date_to' => $date_to,
                'errors' => array(array('error' => $result->get_error_message())),
            );
        }

        if (is_array($result) && isset($result['data']) && is_array($result['data'])) {
            $result = $result['data'];
        }

        if (!is_array($result)) {
            return array(
                'ok' => false,
                'rows' => array(),
                'count' => 0,
                'competitors' => $competitor_domains,
                'provider' => '',
                'date_from' => $date_from,
                'date_to' => $date_to,
                'errors' => array(array('error' => 'invalid_fresh_content_response')),
            );
        }

        $memory_seen_keys = array_fill_keys(array_values(array_filter(array_map('strval', (array) ($memory['seen_keys'] ?? array())))), true);
        foreach ($this->aysa_adjustments_get_existing_competitor_content_keys($project_id) as $existing_seen_key) {
            $memory_seen_keys[$existing_seen_key] = true;
        }

        $rows = array();
        $persist_seen_keys = array_keys($memory_seen_keys);
        foreach ((array) ($result['rows'] ?? array()) as $row) {
            if (!is_array($row)) {
                continue;
            }
            $title = sanitize_text_field((string) ($row['title'] ?? ''));
            $url = esc_url_raw((string) ($row['url'] ?? ''));
            if ($title === '' || $url === '') {
                continue;
            }
            if (preg_match('/^\-\s*[A-Z0-9.-]+$/i', $title)) {
                continue;
            }
            $memory_key = $this->aysa_adjustments_build_competitor_content_key($row);
            if ($memory_key !== '' && isset($memory_seen_keys[$memory_key])) {
                continue;
            }
            $rows[] = array(
                'competitor_domain' => sanitize_text_field((string) ($row['competitor_domain'] ?? '')),
                'title' => $title,
                'url' => $url,
                'snippet' => sanitize_text_field((string) ($row['snippet'] ?? '')),
                'date_hint' => sanitize_text_field((string) ($row['date_hint'] ?? '')),
                'query' => sanitize_text_field((string) ($row['query'] ?? '')),
                'source' => sanitize_key((string) ($row['source'] ?? '')),
            );
            if ($memory_key !== '') {
                $persist_seen_keys[] = $memory_key;
            }
            if (count($rows) >= 20) {
                break;
            }
        }

        $persist_seen_keys = array_values(array_unique(array_filter(array_map('strval', $persist_seen_keys))));
        if (count($persist_seen_keys) > 500) {
            $persist_seen_keys = array_slice($persist_seen_keys, -500);
        }

        $normalized = array(
            'ok' => !empty($result['ok']),
            'rows' => $rows,
            'count' => count($rows),
            'competitors' => $competitor_domains,
            'provider' => sanitize_text_field((string) ($result['provider'] ?? 'google_simple_crawler')),
            'date_from' => sanitize_text_field((string) ($result['date_from'] ?? $date_from)),
            'date_to' => sanitize_text_field((string) ($result['date_to'] ?? $date_to)),
            'errors' => isset($result['errors']) && is_array($result['errors']) ? $result['errors'] : array(),
            'last_checked_at' => current_time('mysql'),
            'last_checked_date_to' => $date_to,
            'memory_seen_count' => count($persist_seen_keys),
            'cache_meta' => array(
                'source' => 'live_refresh',
                'cached_at' => gmdate('c'),
            ),
        );

        $this->aysa_keyword_v2_cache_set($project_id, $cache_key, $normalized);
        $this->aysa_adjustments_set_competitor_fresh_memory($project_id, array(
            'last_checked_at' => current_time('mysql'),
            'last_checked_date_to' => $date_to,
            'seen_keys' => $persist_seen_keys,
        ));

        return $normalized;
    }

    private function aysa_adjustments_get_competitor_fresh_memory($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array(
                'last_checked_at' => '',
                'last_checked_date_to' => '',
                'seen_keys' => array(),
            );
        }

        $memory = get_option('aysa_competitor_fresh_memory_' . $project_id, array());
        if (!is_array($memory)) {
            $memory = array();
        }

        return array(
            'last_checked_at' => sanitize_text_field((string) ($memory['last_checked_at'] ?? '')),
            'last_checked_date_to' => sanitize_text_field((string) ($memory['last_checked_date_to'] ?? '')),
            'seen_keys' => array_values(array_filter(array_map('strval', (array) ($memory['seen_keys'] ?? array())))),
        );
    }

    private function aysa_adjustments_set_competitor_fresh_memory($project_id, $memory) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return;
        }

        $memory = is_array($memory) ? $memory : array();
        update_option('aysa_competitor_fresh_memory_' . $project_id, array(
            'last_checked_at' => sanitize_text_field((string) ($memory['last_checked_at'] ?? '')),
            'last_checked_date_to' => sanitize_text_field((string) ($memory['last_checked_date_to'] ?? '')),
            'seen_keys' => array_values(array_filter(array_map('strval', (array) ($memory['seen_keys'] ?? array())))),
        ), false);
    }

    private function aysa_adjustments_build_competitor_content_key($row) {
        $row = is_array($row) ? $row : array();
        $url = esc_url_raw((string) ($row['url'] ?? ''));
        if ($url !== '') {
            return 'url:' . strtolower($url);
        }

        $domain = sanitize_text_field((string) ($row['competitor_domain'] ?? ''));
        $title = sanitize_text_field((string) ($row['title'] ?? $row['competitor_title'] ?? ''));
        if ($domain === '' || $title === '') {
            return '';
        }

        return 'title:' . strtolower($domain . '|' . $title);
    }

    private function aysa_adjustments_get_existing_competitor_content_keys($project_id) {
        global $wpdb;

        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array();
        }

        $table = $wpdb->prefix . 'aysa_content_strategies';
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT strategy_meta FROM {$table}
             WHERE project_id = %d
               AND strategy_type = %s
               AND COALESCE(automation_status, '') <> 'deleted'",
            $project_id,
            'competitor_derived_titles'
        ), ARRAY_A);

        $keys = array();
        foreach ((array) $rows as $row) {
            $meta = isset($row['strategy_meta']) ? json_decode((string) $row['strategy_meta'], true) : array();
            if (!is_array($meta) || empty($meta)) {
                continue;
            }

            $key = $this->aysa_adjustments_build_competitor_content_key(array(
                'url' => $meta['competitor_url'] ?? '',
                'competitor_domain' => $meta['competitor_domain'] ?? '',
                'title' => $meta['competitor_title'] ?? '',
            ));
            if ($key !== '') {
                $keys[] = $key;
            }
        }

        return array_values(array_unique($keys));
    }

    private function aysa_keyword_mapping_v1_build_payload($project_id) {
        $project_id = intval($project_id);
        $setup_user_id = intval($this->aysa_get_authenticated_external_user_id());
        if ($setup_user_id <= 0) {
            $setup_user_id = get_current_user_id();
        }

        return $this->aysa_collect_research_context_bundle($project_id, $setup_user_id);
    }

    private function aysa_collect_research_context_bundle($project_id, $setup_user_id = 0) {
        $project_id = intval($project_id);
        $setup_user_id = intval($setup_user_id);
        if ($setup_user_id <= 0) {
            $setup_user_id = intval($this->aysa_get_authenticated_external_user_id());
        }
        if ($setup_user_id <= 0) {
            $setup_user_id = get_current_user_id();
        }

        $shared_user_id = intval(get_option('aysa_user_id', 0));
        $profile_user_id = $setup_user_id;

        $general = get_option('aysa_wizard_general_' . intval($profile_user_id), array());
        $objectives = get_option('aysa_wizard_objectives_' . intval($profile_user_id), array());
        $seo_settings = get_option('aysa_wizard_seo-settings_' . intval($profile_user_id), array());
        $seed_data = $this->aysa_keyword_strategy_extract_seed_data($profile_user_id);

        $has_seed_data = !empty($seed_data['seed_keywords']) || !empty($seed_data['competitors']);
        if ((!$has_seed_data || $profile_user_id <= 0) && $shared_user_id > 0 && $shared_user_id !== $profile_user_id) {
            $shared_general = get_option('aysa_wizard_general_' . intval($shared_user_id), array());
            $shared_objectives = get_option('aysa_wizard_objectives_' . intval($shared_user_id), array());
            $shared_seo_settings = get_option('aysa_wizard_seo-settings_' . intval($shared_user_id), array());
            $shared_seed_data = $this->aysa_keyword_strategy_extract_seed_data($shared_user_id);
            $shared_has_seed_data = !empty($shared_seed_data['seed_keywords']) || !empty($shared_seed_data['competitors']);
            if ($shared_has_seed_data) {
                $profile_user_id = $shared_user_id;
                $general = $shared_general;
                $objectives = $shared_objectives;
                $seo_settings = $shared_seo_settings;
                $seed_data = $shared_seed_data;
            }
        }

        $site_host = wp_parse_url(home_url(), PHP_URL_HOST);
        $site_host = is_string($site_host) ? trim($site_host) : '';

        return array(
            'project_id' => $project_id,
            'site_host' => $site_host,
            'profile_user_id' => $profile_user_id,
            'general' => is_array($general) ? $general : array(),
            'objectives' => is_array($objectives) ? $objectives : array(),
            'seo_settings' => is_array($seo_settings) ? $seo_settings : array(),
            'seed_data' => is_array($seed_data) ? $seed_data : array(),
            'research_snapshot' => $this->aysa_adjustments_get_keyword_research_snapshot($project_id, $profile_user_id),
            'monitoring_context' => $this->aysa_monitoring_resolve_context(''),
            'site_context' => array(
                'page_inventory' => $this->aysa_keyword_mapping_collect_site_page_inventory(),
            ),
        );
    }

    private function aysa_collect_content_strategy_context_bundle($project_id, $setup_user_id = 0) {
        $bundle = $this->aysa_collect_research_context_bundle($project_id, $setup_user_id);
        $bundle['mapping_workspace'] = $this->aysa_keyword_v2_cache_get(intval($project_id), 'keyword_mapping_v2');
        $bundle['missing_pages_workspace'] = $this->aysa_keyword_v2_cache_get(intval($project_id), 'missing_pages_v2');
        $bundle['content_strategy_workspace'] = $this->aysa_keyword_v2_cache_get(intval($project_id), 'content_strategy_v2');
        $bundle['research_snapshot'] = $this->aysa_adjustments_get_research_snapshot(intval($project_id), intval($setup_user_id));
        return $bundle;
    }

    private function aysa_keyword_mapping_collect_site_page_inventory() {
        $items = array();
        $seen = array();

        $push = static function(array &$itemsRef, array &$seenRef, $url, $title, $type, $taxonomy = '', array $meta = array()) {
            $url = esc_url_raw((string) $url);
            $title = sanitize_text_field((string) $title);
            $type = sanitize_key((string) $type);
            $taxonomy = sanitize_key((string) $taxonomy);
            if ($url === '' || $title === '' || $type === '') {
                return;
            }
            $key = md5($url . '|' . $type . '|' . $taxonomy);
            if (isset($seenRef[$key])) {
                return;
            }
            $seenRef[$key] = true;
            $itemsRef[] = array(
                'url' => $url,
                'title' => $title,
                'type' => $type,
                'taxonomy' => $taxonomy,
                'slug' => sanitize_title((string) ($meta['slug'] ?? '')),
                'path' => sanitize_text_field((string) ($meta['path'] ?? '')),
                'semantic_terms' => array_values(array_filter(array_map('sanitize_text_field', (array) ($meta['semantic_terms'] ?? array())))),
                'excerpt' => sanitize_text_field((string) ($meta['excerpt'] ?? '')),
            );
        };

        $push($items, $seen, home_url('/'), get_bloginfo('name'), 'home');

        $posts = get_posts(array(
            'numberposts' => 60,
            'post_status' => 'publish',
            'post_type' => 'post',
            'orderby' => 'date',
            'order' => 'DESC',
        ));
        foreach ((array) $posts as $post) {
            $push($items, $seen, get_permalink($post->ID), $post->post_title, 'post', '', array(
                'slug' => $post->post_name,
                'path' => wp_parse_url(get_permalink($post->ID), PHP_URL_PATH),
                'excerpt' => wp_trim_words(wp_strip_all_tags((string) ($post->post_excerpt ?: $post->post_content)), 24, ''),
            ));
        }

        $pages = get_pages(array(
            'number' => 60,
            'post_status' => 'publish',
            'sort_column' => 'post_date',
            'sort_order' => 'DESC',
        ));
        foreach ((array) $pages as $page) {
            $push($items, $seen, get_permalink($page->ID), $page->post_title, 'page', '', array(
                'slug' => $page->post_name,
                'path' => wp_parse_url(get_permalink($page->ID), PHP_URL_PATH),
                'excerpt' => wp_trim_words(wp_strip_all_tags((string) ($page->post_excerpt ?: $page->post_content)), 24, ''),
            ));
        }

        $products = get_posts(array(
            'numberposts' => 120,
            'post_status' => 'publish',
            'post_type' => 'product',
            'orderby' => 'date',
            'order' => 'DESC',
        ));
        foreach ((array) $products as $product) {
            $product_terms = wp_get_post_terms($product->ID, 'product_cat', array('fields' => 'names'));
            $push($items, $seen, get_permalink($product->ID), $product->post_title, 'product', '', array(
                'slug' => $product->post_name,
                'path' => wp_parse_url(get_permalink($product->ID), PHP_URL_PATH),
                'semantic_terms' => is_array($product_terms) ? $product_terms : array(),
                'excerpt' => wp_trim_words(wp_strip_all_tags((string) ($product->post_excerpt ?: $product->post_content)), 24, ''),
            ));
        }

        $taxonomies = array('category', 'product_cat', 'post_tag', 'product_tag');
        foreach ($taxonomies as $taxonomy) {
            $terms = get_terms(array(
                'taxonomy' => $taxonomy,
                'hide_empty' => false,
                'number' => 150,
            ));
            if (is_wp_error($terms) || !is_array($terms)) {
                continue;
            }
            foreach ($terms as $term) {
                if (!$term || is_wp_error($term)) {
                    continue;
                }
                $term_link = get_term_link($term);
                if (is_wp_error($term_link)) {
                    continue;
                }
                $type = in_array($taxonomy, array('category', 'product_cat'), true) ? 'category' : 'tag';
                $semantic_terms = array($term->name);
                if (is_taxonomy_hierarchical($taxonomy)) {
                    $ancestor_ids = array_reverse((array) get_ancestors((int) $term->term_id, $taxonomy, 'taxonomy'));
                    foreach ($ancestor_ids as $ancestor_id) {
                        $ancestor = get_term((int) $ancestor_id, $taxonomy);
                        if ($ancestor && !is_wp_error($ancestor) && !empty($ancestor->name)) {
                            $semantic_terms[] = (string) $ancestor->name;
                        }
                    }
                }
                $push($items, $seen, $term_link, $term->name, $type, $taxonomy, array(
                    'slug' => $term->slug,
                    'path' => wp_parse_url($term_link, PHP_URL_PATH),
                    'semantic_terms' => $semantic_terms,
                    'excerpt' => wp_trim_words(wp_strip_all_tags((string) ($term->description ?? '')), 20, ''),
                ));
            }
        }

        return array_values($items);
    }

    private function aysa_agent8_get_monitoring_rank_tracking_snapshot($project_id) {
        $snapshot = $this->aysa_adjustments_get_monitoring_ranking_snapshot($project_id);
        return is_array($snapshot) ? $snapshot : array();
    }

    private function aysa_agent8_get_monitoring_ai_overview_snapshot($project_id) {
        $context = $this->aysa_monitoring_resolve_context('');
        $target_host = (string) ($context['target'] ?? '');
        if ($target_host === '') {
            return array();
        }

        $overview = $this->aysa_monitoring_ai_get_local_overview($project_id, $target_host);
        return is_array($overview) ? $overview : array();
    }

    private function aysa_agent8_get_monitoring_query_packs_snapshot($project_id) {
        $overview = $this->aysa_agent8_get_monitoring_ai_overview_snapshot($project_id);
        $history = isset($overview['history']) && is_array($overview['history']) ? array_values($overview['history']) : array();
        $snapshot = isset($overview['snapshot']) && is_array($overview['snapshot']) ? $overview['snapshot'] : array();
        $latest_query_pack = isset($snapshot['query_pack']) && is_array($snapshot['query_pack'])
            ? array_values($snapshot['query_pack'])
            : array();
        if ($latest_query_pack === array() && isset($history[0]['query_pack']) && is_array($history[0]['query_pack'])) {
            $latest_query_pack = array_values($history[0]['query_pack']);
        }

        return array(
            'domain' => (string) ($overview['domain'] ?? ''),
            'latest_query_pack' => $latest_query_pack,
            'history' => $history,
            'snapshot_at' => (string) ($snapshot['snapshot_at'] ?? ($history[0]['snapshot_at'] ?? '')),
        );
    }

    private function aysa_agent8_get_research_sitemap_competition_snapshot($project_id) {
        $project_id = intval($project_id);
        $target_host = $this->aysa_monitoring_extract_host((string) home_url('/'));
        if ($project_id <= 0 || $target_host === '') {
            return array();
        }
        $overview = $this->aysa_sitemap_competition_get_local_overview($project_id, $target_host);
        return is_array($overview) ? $overview : array();
    }

    private function aysa_agent8_apply_research_keyword_discovery_refresh($project_id) {
        $job = $this->aysa_project_jobs_enqueue(
            intval($project_id),
            'research',
            'keyword_discovery_refresh',
            array(
                'apply_to_wp' => true,
                'refresh_kind' => 'gsc_auto',
            ),
            array(
                'dedupe_key' => md5(wp_json_encode(array(
                    'job_type' => 'keyword_discovery_refresh',
                    'refresh_kind' => 'gsc_auto',
                    'apply_to_wp' => 1,
                ))),
                'input_summary' => array(
                    'refresh_kind' => 'gsc_auto',
                    'apply_to_wp' => 1,
                ),
            )
        );
        if (is_wp_error($job)) {
            return array('applied' => false, 'reason' => sanitize_key((string) $job->get_error_code()), 'message' => $job->get_error_message());
        }
        $job_row = isset($job['job']) && is_array($job['job']) ? $job['job'] : array();
        return array(
            'applied' => true,
            'operation' => 'refresh_research_keyword_discovery',
            'summary' => array('status' => (string) ($job_row['status'] ?? 'queued')),
            'snapshot' => $this->aysa_adjustments_get_research_snapshot($project_id),
            'async_job' => $this->aysa_project_jobs_to_rack($this->aysa_project_jobs_get_state($project_id)),
        );
    }

    private function aysa_agent8_apply_research_competitor_gap_refresh($project_id) {
        $project_id = intval($project_id);
        $context = $this->aysa_collect_research_context_bundle($project_id);
        $target = (string) wp_parse_url(home_url('/'), PHP_URL_HOST);
        $resolved = $this->aysa_monitoring_resolve_context($target);
        $resolved_target = trim((string) ($resolved['target'] ?? $target));
        $competitors = array_values(array_unique(array_filter(array_map(function($domain) {
            return $this->aysa_monitoring_extract_host((string) $domain);
        }, (array) ($context['seed_data']['competitors'] ?? array())), function($domain) {
            return $domain !== '';
        })));
        if ($project_id <= 0 || $resolved_target === '' || $competitors === array()) {
            return array('applied' => false, 'reason' => 'competitor_gap_context_required');
        }
        $payload = array(
            'project_id' => $project_id,
            'target' => $resolved_target,
            'location_code' => intval($resolved['location_code'] ?? 2840),
            'language_code' => (string) ($resolved['language_code'] ?? 'ro'),
            'limit' => 200,
            'competitors' => $competitors,
        );
        $request = $this->aysa_research_async_prepare_competitor_gap_request(array(
            'project_id' => $project_id,
            'target' => $resolved_target,
            'limit' => 200,
            'competitors' => $competitors,
        ));
        if (is_wp_error($request)) {
            return array('applied' => false, 'reason' => sanitize_key((string) $request->get_error_code()), 'message' => $request->get_error_message());
        }
        $job = $this->aysa_project_jobs_enqueue(
            $project_id,
            'research',
            'competitor_gap',
            array(
                'task' => 'competitor_gap',
                'request' => $request,
            ),
            array(
                'dedupe_key' => (string) ($request['request_hash'] ?? ''),
                'input_summary' => array(
                    'target' => $resolved_target,
                    'competitors_count' => count($competitors),
                ),
            )
        );
        if (is_wp_error($job)) {
            return array('applied' => false, 'reason' => sanitize_key((string) $job->get_error_code()), 'message' => $job->get_error_message());
        }
        $job_row = isset($job['job']) && is_array($job['job']) ? $job['job'] : array();
        return array(
            'applied' => true,
            'operation' => 'refresh_research_competitor_gap',
            'summary' => array('status' => (string) ($job_row['status'] ?? 'queued'), 'competitors' => count($competitors)),
            'snapshot' => $this->aysa_adjustments_get_research_snapshot($project_id),
            'async_job' => isset($job['rack']) && is_array($job['rack']) ? $job['rack'] : array(),
        );
    }

    private function aysa_agent8_apply_research_competitor_identification_refresh($project_id) {
        $project_id = intval($project_id);
        $context = $this->aysa_collect_research_context_bundle($project_id);
        $seed_keywords = array_values(array_filter(array_map('trim', (array) ($context['seed_data']['seed_keywords'] ?? array()))));
        if ($project_id <= 0 || $seed_keywords === array()) {
            return array('applied' => false, 'reason' => 'seed_keywords_required');
        }
        $resolved = $this->aysa_monitoring_resolve_context((string) wp_parse_url(home_url('/'), PHP_URL_HOST));
        $request_payload = array(
            'project_id' => $project_id,
            'seed_keywords' => $seed_keywords,
            'active_competitors' => array_values((array) ($context['seed_data']['competitors'] ?? array())),
            'location_code' => intval($resolved['location_code'] ?? 2840),
            'language_code' => strtolower(trim((string) ($resolved['language_code'] ?? 'ro'))),
            'top_n' => 10,
            'own_domains' => array_values(array_filter(array(strtolower((string) wp_parse_url(home_url('/'), PHP_URL_HOST))))),
        );
        $request = $this->aysa_research_async_prepare_competitor_identification_request(array(
            'project_id' => $project_id,
            'top_n' => 10,
        ));
        if (is_wp_error($request)) {
            return array('applied' => false, 'reason' => sanitize_key((string) $request->get_error_code()), 'message' => $request->get_error_message());
        }
        $job = $this->aysa_project_jobs_enqueue(
            $project_id,
            'research',
            'competitor_identification',
            array(
                'task' => 'competitor_identification',
                'request' => $request,
            ),
            array(
                'dedupe_key' => (string) ($request['request_hash'] ?? ''),
                'input_summary' => array(
                    'seed_keywords_count' => count($seed_keywords),
                ),
            )
        );
        if (is_wp_error($job)) {
            return array('applied' => false, 'reason' => sanitize_key((string) $job->get_error_code()), 'message' => $job->get_error_message());
        }
        $job_row = isset($job['job']) && is_array($job['job']) ? $job['job'] : array();
        return array(
            'applied' => true,
            'operation' => 'refresh_research_competitor_identification',
            'summary' => array('status' => (string) ($job_row['status'] ?? 'queued')),
            'snapshot' => $this->aysa_adjustments_get_research_snapshot($project_id),
            'async_job' => isset($job['rack']) && is_array($job['rack']) ? $job['rack'] : array(),
        );
    }

    private function aysa_agent8_apply_research_competitor_backlinks_refresh($project_id) {
        $project_id = intval($project_id);
        $home_host = $this->aysa_monitoring_extract_host((string) home_url('/'));
        $resolved_context = $this->aysa_monitoring_resolve_context($home_host !== '' ? $home_host : 'florariebragadiru.ro');
        $target = strtolower(trim((string) ($resolved_context['target'] ?? '')));
        if ($target !== '') {
            $target = $this->aysa_monitoring_extract_host($target);
        }
        if ($target === '') {
            $target = $home_host;
        }
        if ($project_id <= 0 || $target === '') {
            return array('applied' => false, 'reason' => 'target_domain_required');
        }

        $context = $this->aysa_collect_research_context_bundle($project_id);
        $competitors = array_values((array) ($context['seed_data']['competitors'] ?? array()));
        $normalized_competitors = array();
        foreach ($competitors as $raw) {
            $host = $this->aysa_monitoring_extract_host((string) $raw);
            if ($host === '') {
                $host = strtolower(trim((string) $raw));
            }
            $host = preg_replace('/^www\./', '', $host);
            if ($host === '' || $host === $target || !preg_match('/^[a-z0-9.-]+\.[a-z]{2,}$/', $host)) {
                continue;
            }
            $normalized_competitors[$host] = true;
        }
        $competitor_domains = array_keys($normalized_competitors);
        if ($competitor_domains === array()) {
            return array('applied' => false, 'reason' => 'no_competitors_configured');
        }

        $domains = array_merge(array($target), $competitor_domains);
        foreach ($domains as $domain) {
            $broker_result = $this->aysa_broker_request('POST', '/v1/dataforseo/backlink-audit', array(
                'target' => $domain,
                'domain' => $domain,
                'limit' => 150,
                'project_id' => $project_id,
            ), 180);
            if (is_wp_error($broker_result)) {
                continue;
            }
            $items = (isset($broker_result['items']) && is_array($broker_result['items'])) ? $broker_result['items'] : array();
            $snapshot = array(
                'domain' => $domain,
                'provider' => (string) ($broker_result['provider'] ?? 'dataforseo'),
                'generated_at' => current_time('mysql'),
                'count' => count($items),
                'items' => $items,
            );
            update_option($this->aysa_get_backlink_audit_domain_key($project_id, $domain), $snapshot, false);
        }
        $this->aysa_wallet_track_action_usage($project_id, 'aysa_competitor_backlinks_gap_insights', 'competitor_backlinks_gap_insights', array('module' => 'competitor_backlinks', 'source' => 'agent8_refresh', 'target' => $target, 'competitors_count' => count($competitor_domains), 'force_refresh' => 1), 45, '/v1/backlinks/gap-insights');
        return array(
            'applied' => true,
            'operation' => 'refresh_research_competitor_backlinks',
            'summary' => array('status' => 'refreshed'),
            'snapshot' => $this->aysa_adjustments_get_research_snapshot($project_id),
        );
    }

    private function aysa_agent8_apply_research_sitemap_competition_refresh($project_id) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return array('applied' => false, 'reason' => 'invalid_project_id');
        }
        $payload = $this->aysa_sitemap_competition_build_payload();
        $job = $this->aysa_project_jobs_enqueue(
            intval($payload['project_id'] ?? 0),
            'research',
            'sitemap_competition_scan',
            array(
                'project_id' => intval($payload['project_id'] ?? 0),
                'target_host' => (string) ($payload['target_host'] ?? ''),
                'competitors' => isset($payload['competitors']) && is_array($payload['competitors']) ? array_values($payload['competitors']) : array(),
            ),
            array(
                'dedupe_key' => md5(wp_json_encode(array(
                    'project_id' => intval($payload['project_id'] ?? 0),
                    'target_host' => (string) ($payload['target_host'] ?? ''),
                    'competitors' => isset($payload['competitors']) && is_array($payload['competitors']) ? array_values($payload['competitors']) : array(),
                ))),
                'input_summary' => array(
                    'target_host' => (string) ($payload['target_host'] ?? ''),
                    'competitors_count' => isset($payload['competitors']) && is_array($payload['competitors']) ? count($payload['competitors']) : 0,
                ),
            )
        );
        if (is_wp_error($job)) {
            return array('applied' => false, 'reason' => sanitize_key((string) $job->get_error_code()), 'message' => $job->get_error_message());
        }
        $job_row = isset($job['job']) && is_array($job['job']) ? $job['job'] : array();
        $local_run_id = 0;
        if (!empty($job_row['worker_ref'])) {
            $parts = explode(':', (string) $job_row['worker_ref']);
            $local_run_id = intval(end($parts));
        }
        $snapshot = $this->aysa_agent8_get_research_sitemap_competition_snapshot($project_id);
        return array(
            'applied' => true,
            'operation' => 'refresh_research_sitemap_competition',
            'summary' => array('status' => (string) ($job_row['status'] ?? 'queued'), 'local_run_id' => $local_run_id),
            'snapshot' => $snapshot,
            'async_job' => isset($job['rack']) && is_array($job['rack']) ? $job['rack'] : array(),
        );
    }

    private function aysa_agent8_get_monitoring_associations($project_id) {
        $associations = $this->get_keyword_discovery_local_associations(intval($project_id));
        $normalized = $this->aysa_monitoring_normalize_associations($associations);
        return is_array($normalized) ? array_values($normalized) : array();
    }

    private function aysa_agent8_apply_monitoring_rank_refresh($project_id) {
        $project_id = intval($project_id);
        $associations = $this->aysa_agent8_get_monitoring_associations($project_id);
        if ($project_id <= 0 || empty($associations)) {
            return array('applied' => false, 'reason' => 'no_tracked_keywords');
        }

        $existing_status = $this->aysa_monitoring_get_refresh_status($project_id);
        if (($existing_status['status'] ?? '') === 'running') {
            return array(
                'applied' => true,
                'operation' => 'refresh_monitoring_rank_tracking',
                'summary' => array(
                    'status' => (string) ($existing_status['status'] ?? 'running'),
                    'tracked_keywords' => count($associations),
                ),
                'snapshot' => $this->aysa_agent8_get_monitoring_rank_tracking_snapshot($project_id),
                'async_job' => $existing_status,
            );
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_monitoring_rank_refresh_start_background',
            'monitoring_rank_refresh_start_background',
            array(
                'module' => 'monitoring_analytics',
                'source' => 'agent8_background_refresh',
            ),
            120,
            '/v1/dataforseo/rank-tracking/refresh-start-background'
        );

        $batch_size = 10;
        $context = $this->aysa_monitoring_resolve_context('');
        $run_id = uniqid('mon_', true);
        $now = time();

        $queue = array(
            'run_id' => $run_id,
            'project_id' => $project_id,
            'context' => $context,
            'associations' => $associations,
            'cursor' => 0,
            'batch_size' => $batch_size,
            'current_retry' => 0,
            'created_at' => $now,
        );
        update_option($this->aysa_monitoring_refresh_queue_key($project_id), $queue, false);

        $status = array(
            'run_id' => $run_id,
            'status' => 'running',
            'total' => count($associations),
            'processed' => 0,
            'refreshed' => 0,
            'failed' => 0,
            'deferred' => 0,
            'last_error' => '',
            'started_at' => $now,
            'updated_at' => $now,
            'finished_at' => 0,
        );
        $this->aysa_monitoring_save_refresh_status($project_id, $status);
        $this->aysa_monitoring_ensure_refresh_cron_tick($project_id);
        $this->aysa_monitoring_schedule_background_refresh($project_id, 1);

        return array(
            'applied' => true,
            'operation' => 'refresh_monitoring_rank_tracking',
            'summary' => array(
                'status' => 'running',
                'tracked_keywords' => count($associations),
            ),
            'snapshot' => $this->aysa_agent8_get_monitoring_rank_tracking_snapshot($project_id),
            'async_job' => $status,
        );
    }

    private function aysa_agent8_apply_monitoring_ai_refresh($project_id) {
        $project_id = intval($project_id);
        $payload = $this->aysa_monitoring_build_ai_payload();
        $domain = (string) ($payload['domain'] ?? '');
        if ($project_id <= 0 || $domain === '') {
            return array('applied' => false, 'reason' => 'monitoring_ai_context_required');
        }

        $status_key = $this->aysa_monitoring_ai_refresh_status_option_key($project_id, $domain);
        $result = $this->aysa_broker_request('POST', '/v1/monitoring/ai/refresh/start', $payload, 60);
        if (is_wp_error($result)) {
            return array(
                'applied' => false,
                'reason' => 'monitoring_ai_refresh_failed',
                'message' => $this->aysa_humanize_broker_error($result, 'AI monitoring refresh failed.'),
            );
        }

        $snapshot = isset($result['snapshot']) && is_array($result['snapshot']) ? $result['snapshot'] : array();
        if (!empty($snapshot)) {
            $this->aysa_monitoring_ai_persist_snapshot_summary($project_id, $domain, $snapshot);
        }

        $status = array(
            'ok' => true,
            'project_id' => $project_id,
            'domain' => $domain,
            'status' => (string) ($result['status'] ?? 'running'),
            'message' => (string) ($result['message'] ?? 'AI refresh started.'),
            'started_at' => gmdate('c'),
            'updated_at' => gmdate('c'),
            'snapshot_id' => intval($result['snapshot_id'] ?? 0),
            'error' => '',
            'processed_questions' => intval($result['processed_questions'] ?? 0),
            'total_questions' => intval($result['total_questions'] ?? 0),
            'processed_sources' => intval($result['processed_sources'] ?? 0),
            'total_sources' => intval($result['total_sources'] ?? 0),
            'partial_errors' => isset($result['partial_errors']) && is_array($result['partial_errors']) ? array_values($result['partial_errors']) : array(),
            'query_pack' => isset($result['query_pack']) && is_array($result['query_pack']) ? array_values($result['query_pack']) : array(),
            'overview_questions' => isset($result['overview_questions']) && is_array($result['overview_questions']) ? array_values($result['overview_questions']) : array(),
            'snapshot' => $snapshot,
            'history' => isset($result['history']) && is_array($result['history']) ? array_values($result['history']) : array(),
            'billed_provider_milli_cents' => 0,
        );
        $status = $this->aysa_monitoring_ai_finalize_status_payload($status, array());
        update_option($status_key, $status, false);
        $this->aysa_monitoring_schedule_ai_refresh_event($payload, 1);

        return array(
            'applied' => true,
            'operation' => 'refresh_monitoring_ai_monitoring',
            'summary' => array(
                'status' => (string) ($status['status'] ?? 'running'),
                'domain' => $domain,
            ),
            'snapshot' => $this->aysa_agent8_get_monitoring_ai_overview_snapshot($project_id),
            'async_job' => $status,
        );
    }

    private function aysa_agent8_apply_monitoring_keyword_overview_refresh($project_id) {
        $project_id = intval($project_id);
        $associations = $this->aysa_agent8_get_monitoring_associations($project_id);
        if ($project_id <= 0 || empty($associations)) {
            return array('applied' => false, 'reason' => 'no_tracked_keywords');
        }

        $request = $this->aysa_monitoring_keywords_prepare_request(array(
            'project_id' => $project_id,
            'associations' => wp_json_encode($associations),
        ));
        if (is_wp_error($request)) {
            return array(
                'applied' => false,
                'reason' => sanitize_key((string) $request->get_error_code()),
                'message' => $request->get_error_message(),
            );
        }

        $request_hash = (string) ($request['request_hash'] ?? '');
        $existing = $this->aysa_monitoring_keywords_async_get_status($project_id);
        if (
            is_array($existing) &&
            !empty($existing['run_id']) &&
            in_array((string) ($existing['status'] ?? ''), array('queued', 'running'), true) &&
            (string) ($existing['request_hash'] ?? '') === $request_hash
        ) {
            return array(
                'applied' => true,
                'operation' => 'refresh_monitoring_keyword_overview',
                'summary' => array(
                    'status' => (string) ($existing['status'] ?? 'running'),
                    'tracked_keywords' => count($associations),
                ),
                'snapshot' => $this->aysa_agent8_get_monitoring_rank_tracking_snapshot($project_id),
                'async_job' => $this->aysa_monitoring_keywords_async_output_status($existing),
            );
        }

        $this->aysa_wallet_track_action_usage(
            $project_id,
            'aysa_monitoring_keywords_overview',
            'monitoring_keywords_overview',
            array(
                'module' => 'monitoring_analytics',
                'source' => 'agent8_manual_refresh_async',
            ),
            60,
            '/v1/monitoring/keywords/overview'
        );

        $run_id = uniqid('mon_kw_', true);
        $status = array(
            'project_id' => $project_id,
            'domain' => (string) ($request['domain'] ?? ''),
            'run_id' => $run_id,
            'request_hash' => $request_hash,
            'status' => 'queued',
            'result' => null,
            'error' => '',
            'started_at' => time(),
            'updated_at' => time(),
            'finished_at' => 0,
            '_request' => $request,
        );
        $this->aysa_monitoring_keywords_async_save_status($project_id, $status);
        wp_schedule_single_event(time() + 1, 'aysa_monitoring_keywords_async_event', array(array(
            'project_id' => $project_id,
            'run_id' => $run_id,
            'request_hash' => $request_hash,
            'request' => $request,
        )));
        if (function_exists('spawn_cron')) {
            spawn_cron();
        }

        return array(
            'applied' => true,
            'operation' => 'refresh_monitoring_keyword_overview',
            'summary' => array(
                'status' => 'queued',
                'tracked_keywords' => count($associations),
            ),
            'snapshot' => $this->aysa_agent8_get_monitoring_rank_tracking_snapshot($project_id),
            'async_job' => $this->aysa_monitoring_keywords_async_output_status($status),
        );
    }

    private function aysa_agent8_apply_monitoring_query_pack_save($project_id, array $monitoring_operation) {
        $project_id = intval($project_id);
        $payload = $this->aysa_monitoring_build_ai_payload();
        $query_pack = isset($monitoring_operation['query_pack']) && is_array($monitoring_operation['query_pack'])
            ? array_values($monitoring_operation['query_pack'])
            : array();
        if ($project_id <= 0 || empty($query_pack)) {
            return array('applied' => false, 'reason' => 'query_pack_required');
        }

        $payload['query_pack'] = $query_pack;
        $result = $this->aysa_broker_request('POST', '/v1/monitoring/ai/query-pack', $payload, 120);
        if (is_wp_error($result)) {
            return array(
                'applied' => false,
                'reason' => 'monitoring_query_pack_save_failed',
                'message' => $this->aysa_humanize_broker_error($result, 'Could not save the monitoring query pack.'),
            );
        }

        $this->aysa_wallet_track_action_usage(
            intval($payload['project_id'] ?? 0),
            'aysa_monitoring_save_ai_query_pack',
            'monitoring_ai_query_pack_save',
            array(
                'module' => 'monitoring_analytics',
                'source' => 'agent8_manual_save',
                'query_count' => count($query_pack),
            ),
            10,
            '/v1/monitoring/ai/query-pack'
        );

        return array(
            'applied' => true,
            'operation' => 'save_monitoring_query_pack',
            'summary' => array(
                'queries' => count($query_pack),
            ),
            'snapshot' => $this->aysa_agent8_get_monitoring_query_packs_snapshot($project_id),
        );
    }

    private function aysa_keyword_v2_finalize_snapshot_contract($project_id, $tab_key, $payload, $items_key = 'rows') {
        $project_id = intval($project_id);
        $tab_key = sanitize_key((string) $tab_key);
        $payload = is_array($payload) ? $payload : array();
        $items_key = is_string($items_key) && $items_key !== '' ? $items_key : 'rows';

        if (!isset($payload['cache_meta']) || !is_array($payload['cache_meta'])) {
            $payload['cache_meta'] = array();
        }

        $payload['project_id'] = $project_id;
        $payload['snapshot_key'] = $tab_key;
        $payload['source'] = $tab_key;
        $payload['refreshed_at'] = $payload['cache_meta']['cached_at'] ?? gmdate('c');
        $payload['cache_meta']['source'] = $payload['cache_meta']['source'] ?? 'live_refresh';
        $payload['cache_meta']['cached_at'] = $payload['cache_meta']['cached_at'] ?? gmdate('c');

        $items = isset($payload[$items_key]) && is_array($payload[$items_key]) ? array_values($payload[$items_key]) : array();
        $payload['items'] = $items;
        if (!isset($payload['count'])) {
            $payload['count'] = count($items);
        }
        if (!isset($payload['quality_flags']) || !is_array($payload['quality_flags'])) {
            $payload['quality_flags'] = array();
        }
        if (!isset($payload['explainability']) || !is_array($payload['explainability'])) {
            $payload['explainability'] = array();
        }
        if (!isset($payload['workspace_handoff']) || !is_array($payload['workspace_handoff'])) {
            $payload['workspace_handoff'] = array();
        }

        return $payload;
    }

    private function aysa_keyword_v2_cache_option_name($project_id, $tab_key) {
        return 'aysa_keyword_v2_cache_' . intval($project_id) . '_' . sanitize_key((string) $tab_key);
    }

    private function aysa_keyword_v2_cache_get($project_id, $tab_key) {
        global $wpdb;

        $project_id = intval($project_id);
        $tab_key = sanitize_key((string) $tab_key);
        if ($project_id <= 0 || $tab_key === '') {
            return null;
        }

        $this->aysa_keyword_v2_ensure_snapshot_table();
        $table = $this->aysa_keyword_v2_snapshot_table_name();
        $row = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT payload_json, updated_at FROM {$table} WHERE project_id = %d AND tab_key = %s LIMIT 1",
                $project_id,
                $tab_key
            ),
            ARRAY_A
        );

        if (!is_array($row) || empty($row['payload_json'])) {
            return null;
        }

        $payload = json_decode((string) $row['payload_json'], true);
        if (!is_array($payload)) {
            return null;
        }

        if (!isset($payload['cache_meta']) || !is_array($payload['cache_meta'])) {
            $payload['cache_meta'] = array();
        }
        $payload['cache_meta']['source'] = 'db_snapshot';
        $payload['cache_meta']['cached_at'] = (string) ($row['updated_at'] ?? ($payload['cache_meta']['cached_at'] ?? ''));
        return $this->aysa_keyword_v2_finalize_snapshot_contract($project_id, $tab_key, $payload, $tab_key === 'keyword_mapping_v2' ? 'clusters' : 'rows');
    }

    private function aysa_keyword_v2_cache_set($project_id, $tab_key, $payload) {
        global $wpdb;

        $project_id = intval($project_id);
        $tab_key = sanitize_key((string) $tab_key);
        if ($project_id <= 0 || $tab_key === '') {
            return;
        }

        $this->aysa_keyword_v2_ensure_snapshot_table();
        $table = $this->aysa_keyword_v2_snapshot_table_name();
        $payload_json = wp_json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        if (!is_string($payload_json) || $payload_json === '') {
            return;
        }

        $wpdb->replace(
            $table,
            array(
                'project_id' => $project_id,
                'tab_key' => $tab_key,
                'payload_json' => $payload_json,
                'updated_at' => current_time('mysql', true),
            ),
            array('%d', '%s', '%s', '%s')
        );
    }

    private function aysa_keyword_v2_cache_delete($project_id, $tab_key) {
        global $wpdb;

        $project_id = intval($project_id);
        $tab_key = sanitize_key((string) $tab_key);
        if ($project_id <= 0 || $tab_key === '') {
            return;
        }

        $this->aysa_keyword_v2_ensure_snapshot_table();
        $table = $this->aysa_keyword_v2_snapshot_table_name();
        $wpdb->query(
            $wpdb->prepare(
                "DELETE FROM {$table} WHERE project_id = %d AND tab_key = %s",
                $project_id,
                $tab_key
            )
        );
    }

    private function aysa_keyword_v2_snapshot_table_name() {
        global $wpdb;
        return $wpdb->prefix . 'aysa_keyword_v2_snapshots';
    }

    private function aysa_keyword_v2_ensure_snapshot_table() {
        global $wpdb;

        $table = $this->aysa_keyword_v2_snapshot_table_name();
        static $ready = false;
        if ($ready) {
            return;
        }

        $charset_collate = $wpdb->get_charset_collate();
        $sql = "CREATE TABLE IF NOT EXISTS {$table} (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
            project_id BIGINT UNSIGNED NOT NULL,
            tab_key VARCHAR(64) NOT NULL,
            payload_json LONGTEXT NOT NULL,
            updated_at DATETIME NOT NULL,
            PRIMARY KEY (id),
            UNIQUE KEY project_tab_unique (project_id, tab_key),
            KEY project_id_idx (project_id),
            KEY updated_at_idx (updated_at)
        ) {$charset_collate};";

        require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        dbDelta($sql);
        $ready = true;
    }

    private function aysa_keyword_discovery_refresh_keyword_suggestions($project_id, $setup_user_id = 0, $track_usage = true) {
        $project_id = intval($project_id);
        $setup_user_id = intval($setup_user_id);
        if ($project_id <= 0) {
            return new WP_Error('invalid_project_id', 'Invalid project id');
        }

        if ($setup_user_id <= 0) {
            $setup_user_id = intval($this->aysa_get_authenticated_external_user_id());
            if ($setup_user_id <= 0) {
                $setup_user_id = get_current_user_id();
            }
        }

        $seed_keywords = $this->aysa_keyword_discovery_get_seed_keywords_with_fallback($setup_user_id);
        if (empty($seed_keywords)) {
            return new WP_Error('missing_seed_keywords', 'No seed keywords found in Setup SEO Profile > Competition.');
        }

        $context = $this->aysa_monitoring_resolve_context('');
        $rows = $this->aysa_keyword_discovery_fetch_keyword_suggestions($project_id, $seed_keywords, $context);
        if (empty($rows)) {
            return new WP_Error('empty_suggestions', 'Keyword suggestions endpoint returned empty data.');
        }

        $normalized = array();
        $seen = array();
        foreach ($rows as $row) {
            if (!is_array($row)) {
                continue;
            }
            $keyword = trim((string) ($row['keyword'] ?? $row['Keyword'] ?? ''));
            if ($keyword === '') {
                continue;
            }
            $norm = strtolower($keyword);
            if (isset($seen[$norm])) {
                continue;
            }
            $seen[$norm] = true;
            $normalized[] = array(
                'keyword' => $keyword,
                'searchVolume' => $row['search_volume'] ?? ($row['searchVolume'] ?? null),
                'difficulty' => $row['kd'] ?? ($row['difficulty'] ?? null),
                'cpc' => $row['cpc'] ?? null,
                'intent' => $row['intent'] ?? null,
                'intent_probabilities' => $row['intent_probabilities'] ?? array(),
                'item_types' => $row['item_types'] ?? ($row['serp_item_types'] ?? array()),
                'source' => in_array((string) ($row['source'] ?? ''), array('related_keyword', 'keyword_suggestion'), true)
                    ? (string) $row['source']
                    : 'keyword_suggestion',
            );
        }

        if (empty($normalized)) {
            return new WP_Error('empty_normalized_suggestions', 'Keyword suggestions normalization returned empty data.');
        }

        $keyword_texts = array_values(array_unique(array_map(function($row) {
            return trim((string) ($row['keyword'] ?? ''));
        }, $normalized)));
        $metrics = $this->aysa_keyword_discovery_fetch_keyword_metrics($project_id, $keyword_texts, $context);
        if (!empty($metrics)) {
            foreach ($normalized as $i => $row) {
                $key = strtolower(trim((string) ($row['keyword'] ?? '')));
                if ($key === '' || !isset($metrics[$key])) {
                    continue;
                }
                $m = $metrics[$key];
                if (empty($normalized[$i]['intent']) && !empty($m['intent'])) {
                    $normalized[$i]['intent'] = (string) $m['intent'];
                }
                if (!isset($normalized[$i]['searchVolume']) || $normalized[$i]['searchVolume'] === null || $normalized[$i]['searchVolume'] === '') {
                    $normalized[$i]['searchVolume'] = $m['search_volume'] ?? null;
                }
                if (!isset($normalized[$i]['difficulty']) || $normalized[$i]['difficulty'] === null || $normalized[$i]['difficulty'] === '') {
                    $normalized[$i]['difficulty'] = $m['kd'] ?? null;
                }
                if (!isset($normalized[$i]['cpc']) || $normalized[$i]['cpc'] === null || $normalized[$i]['cpc'] === '') {
                    $normalized[$i]['cpc'] = $m['cpc'] ?? null;
                }
            }
        }

        $this->aysa_keyword_discovery_clear_suggestions_rows($project_id);
        $persist = $this->aysa_keyword_discovery_persist_keywords($project_id, $normalized);

        if ($track_usage) {
            $this->aysa_wallet_track_action_usage(
                $project_id,
                'aysa_keyword_discovery_keyword_suggestions_refresh',
                'keyword_discovery_keyword_suggestions_refresh',
                array(
                    'module' => 'keyword_suggestions',
                    'seed_keywords_count' => count($seed_keywords),
                    'rows_count' => count($normalized),
                    'inserted' => intval($persist['inserted'] ?? 0),
                    'updated' => intval($persist['updated'] ?? 0),
                ),
                35,
                '/v1/keywords/suggestions'
            );
        }

        return array(
            'seed_keywords' => $seed_keywords,
            'count' => count($normalized),
            'inserted' => intval($persist['inserted'] ?? 0),
            'updated' => intval($persist['updated'] ?? 0),
            'rows' => $normalized,
        );
    }

    public function handle_keyword_discovery_refresh_background_event($project_id = 0) {
        $project_id = intval($project_id);
        if ($project_id <= 0) {
            return;
        }

        $lock_key = $this->aysa_keyword_discovery_lock_key($project_id);
        if (get_transient($lock_key)) {
            $this->aysa_keyword_discovery_schedule_background_refresh($project_id, 15);
            return;
        }
        set_transient($lock_key, 1, 180);

        try {
            $status = $this->aysa_keyword_discovery_get_status($project_id);
            if (($status['status'] ?? '') !== 'running') {
                return;
            }

            $refresh_kind = strtolower(trim((string) ($status['refresh_kind'] ?? 'gsc_auto')));
            if (!in_array($refresh_kind, array('gsc_auto', 'keyword_suggestions'), true)) {
                $refresh_kind = 'gsc_auto';
            }

            if ($refresh_kind === 'keyword_suggestions') {
                if (!class_exists('Aysa_Auth_Manager')) {
                    require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
                }
                $setup_user_id = intval($this->aysa_get_authenticated_external_user_id());
                if ($setup_user_id <= 0) {
                    $setup_user_id = get_current_user_id();
                }
                $result = $this->aysa_keyword_discovery_refresh_keyword_suggestions($project_id, $setup_user_id, true);
                if (is_wp_error($result)) {
                    throw new Exception($result->get_error_message());
                }
                $status['total'] = intval($result['count'] ?? 0);
                $status['processed'] = intval($result['count'] ?? 0);
                $status['staged'] = intval($result['count'] ?? 0);
                $status['inserted'] = intval($result['inserted'] ?? 0);
                $status['updated'] = intval($result['updated'] ?? 0);
                $status['status'] = 'completed';
                $status['updated_at'] = time();
                $status['finished_at'] = time();
                $status['last_error'] = '';
                $status['metrics_notice'] = '';
                $this->aysa_keyword_discovery_save_status($project_id, $status);
                return;
            }

            $site_host = wp_parse_url(home_url(), PHP_URL_HOST);
            $site_host = is_string($site_host) ? $site_host : '';

            $raw = $this->aysa_broker_request('POST', '/v1/google/keyword-mappings', array(
                'project_id' => $project_id,
                'site_host' => $site_host,
                'days' => 90,
                'limit' => 500,
            ), 60);

            if (is_wp_error($raw)) {
                throw new Exception((string) $raw->get_error_message());
            }
            if (!is_array($raw) || empty($raw['connected'])) {
                $reason = is_array($raw) && !empty($raw['error']) ? (string) $raw['error'] : 'Could not fetch raw GSC keyword mappings.';
                if ($reason === 'reauthorize_required') {
                    $reason = 'Google Search Console needs reconnection.';
                }
                throw new Exception($reason);
            }

            $query_rows = isset($raw['gsc_queries']) && is_array($raw['gsc_queries']) ? $raw['gsc_queries'] : array();
            $mapping_rows = isset($raw['gsc_mappings']) && is_array($raw['gsc_mappings']) ? $raw['gsc_mappings'] : array();

            $keywords = array();
            $keyword_index = array();

            foreach ($query_rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $keyword = trim((string) ($row['query'] ?? $row['keyword'] ?? ''));
                if ($keyword === '') {
                    continue;
                }
                $norm = strtolower($keyword);
                $keywords[] = array(
                    'keyword' => $keyword,
                    'searchVolume' => null,
                    'difficulty' => null,
                    'cpc' => null,
                    'intent' => '',
                    'competition' => '',
                    'traffic' => intval($row['clicks'] ?? 0),
                    'trafficPct' => isset($row['ctr']) ? floatval($row['ctr']) : null,
                    'position' => isset($row['position']) ? floatval($row['position']) : null,
                    'source' => 'gsc_query_only',
                    'impressions' => intval($row['impressions'] ?? 0),
                    'url' => '',
                );
                $keyword_index[$norm] = count($keywords) - 1;
            }

            foreach ($mapping_rows as $row) {
                if (!is_array($row)) {
                    continue;
                }
                $keyword = trim((string) ($row['query'] ?? $row['keyword'] ?? ''));
                if ($keyword === '') {
                    continue;
                }
                $norm = strtolower($keyword);
                $target_url = trim((string) ($row['page'] ?? $row['url'] ?? ''));
                if (isset($keyword_index[$norm])) {
                    $idx = $keyword_index[$norm];
                    if ($target_url !== '') {
                        $keywords[$idx]['url'] = $target_url;
                    }
                    $keywords[$idx]['source'] = 'gsc';
                    if (intval($row['clicks'] ?? 0) > intval($keywords[$idx]['traffic'] ?? 0)) {
                        $keywords[$idx]['traffic'] = intval($row['clicks'] ?? 0);
                    }
                    if ((intval($row['impressions'] ?? 0) > 0) && empty($keywords[$idx]['impressions'])) {
                        $keywords[$idx]['impressions'] = intval($row['impressions'] ?? 0);
                    }
                    if (!empty($row['position'])) {
                        $keywords[$idx]['position'] = floatval($row['position']);
                    }
                    continue;
                }

                $keywords[] = array(
                    'keyword' => $keyword,
                    'searchVolume' => null,
                    'difficulty' => null,
                    'cpc' => null,
                    'intent' => '',
                    'competition' => '',
                    'traffic' => intval($row['clicks'] ?? 0),
                    'trafficPct' => isset($row['ctr']) ? floatval($row['ctr']) : null,
                    'position' => isset($row['position']) ? floatval($row['position']) : null,
                    'source' => 'gsc',
                    'impressions' => intval($row['impressions'] ?? 0),
                    'url' => $target_url,
                );
                $keyword_index[$norm] = count($keywords) - 1;
            }

            $context = $this->aysa_monitoring_resolve_context('');
            $keyword_texts = array();
            foreach ($keywords as $krow) {
                if (!is_array($krow)) {
                    continue;
                }
                $kw = trim((string) ($krow['keyword'] ?? $krow['Keyword'] ?? ''));
                if ($kw !== '') {
                    $keyword_texts[] = $kw;
                }
            }
            $keyword_texts = array_values(array_unique($keyword_texts));
            $keyword_intents = $this->aysa_keyword_discovery_fetch_keyword_intent_bulk($project_id, $keyword_texts, $context);
            if (!empty($keyword_intents)) {
                foreach ($keywords as $i => $krow) {
                    if (!is_array($krow)) {
                        continue;
                    }
                    $kw = strtolower(trim((string) ($krow['keyword'] ?? $krow['Keyword'] ?? '')));
                    if ($kw === '' || !isset($keyword_intents[$kw])) {
                        continue;
                    }
                    $intent_row = $keyword_intents[$kw];
                    if (!empty($intent_row['intent'])) {
                        $keywords[$i]['intent'] = (string) $intent_row['intent'];
                    }
                    if (isset($intent_row['intent_probabilities']) && is_array($intent_row['intent_probabilities'])) {
                        $keywords[$i]['intent_probabilities'] = $intent_row['intent_probabilities'];
                    }
                    if (!empty($intent_row['intent_source'])) {
                        $keywords[$i]['intent_source'] = (string) $intent_row['intent_source'];
                    }
                    if (!empty($intent_row['intent_updated_at'])) {
                        $keywords[$i]['intent_updated_at'] = (string) $intent_row['intent_updated_at'];
                    }
                }
            }
            $keyword_metrics = $this->aysa_keyword_discovery_fetch_keyword_metrics($project_id, $keyword_texts, $context);

            if (!empty($keyword_metrics)) {
                foreach ($keywords as $i => $krow) {
                    if (!is_array($krow)) {
                        continue;
                    }
                    $kw = strtolower(trim((string) ($krow['keyword'] ?? $krow['Keyword'] ?? '')));
                    if ($kw === '' || !isset($keyword_metrics[$kw])) {
                        continue;
                    }
                    $m = $keyword_metrics[$kw];
                    if (!is_null($m['intent']) && (string) ($krow['intent'] ?? '') === '') {
                        $keywords[$i]['intent'] = (string) $m['intent'];
                    }
                    if (!is_null($m['search_volume'])) {
                        $keywords[$i]['searchVolume'] = intval($m['search_volume']);
                    }
                    if (!is_null($m['kd'])) {
                        $keywords[$i]['difficulty'] = floatval($m['kd']);
                    }
                    if (!is_null($m['cpc'])) {
                        $keywords[$i]['cpc'] = floatval($m['cpc']);
                    }
                    if (!is_null($m['traffic'])) {
                        $keywords[$i]['traffic'] = intval($m['traffic']);
                    }
                    if (!is_null($m['traffic_pct'])) {
                        $keywords[$i]['trafficPct'] = floatval($m['traffic_pct']);
                    }
                }
            }

            // Guardrail: avoid false "completed" refreshes with all metrics empty.
            $rows_with_real_metrics = 0;
            foreach ($keywords as $krow) {
                if (!is_array($krow)) {
                    continue;
                }
                $has_intent = trim((string) ($krow['intent'] ?? '')) !== '';
                $has_volume = isset($krow['searchVolume']) && $krow['searchVolume'] !== null && $krow['searchVolume'] !== '' && $krow['searchVolume'] !== 'N/A';
                $has_kd = isset($krow['difficulty']) && $krow['difficulty'] !== null && $krow['difficulty'] !== '' && $krow['difficulty'] !== 'N/A';
                $has_cpc = isset($krow['cpc']) && $krow['cpc'] !== null && $krow['cpc'] !== '' && $krow['cpc'] !== 'N/A';
                if ($has_intent || $has_volume || $has_kd || $has_cpc) {
                    $rows_with_real_metrics++;
                }
            }
            $metrics_notice = '';
            if (!empty($keywords) && $rows_with_real_metrics === 0) {
                $metrics_notice = 'Keyword ideas were refreshed, but live SEO metrics are not available yet.';
            }

            update_option($this->aysa_keyword_discovery_snapshot_key($project_id), array(
                'run_id' => (string) ($status['run_id'] ?? ''),
                'project_id' => $project_id,
                'fetched_at' => time(),
                'count' => count($keywords),
                'keywords' => $keywords,
            ), false);

            $persist = array('inserted' => 0, 'updated' => 0);
            $apply_to_wp = !empty($status['apply_to_wp']);
            if ($apply_to_wp) {
                $this->aysa_keyword_discovery_clear_gsc_rows($project_id);
                $persist = $this->aysa_keyword_discovery_persist_keywords($project_id, $keywords);
            }
            $status['total'] = count($keywords);
            $status['processed'] = count($keywords);
            $status['staged'] = count($keywords);
            $status['inserted'] = intval($persist['inserted'] ?? 0);
            $status['updated'] = intval($persist['updated'] ?? 0);
            $status['status'] = 'completed';
            $status['updated_at'] = time();
            $status['finished_at'] = time();
            $status['last_error'] = '';
            $status['metrics_notice'] = $metrics_notice;
            $this->aysa_keyword_discovery_save_status($project_id, $status);
            $this->aysa_project_jobs_finalize(
                $project_id,
                $this->aysa_project_jobs_make_worker_ref('keyword_discovery', 'keyword_discovery_refresh', (string) ($status['run_id'] ?? '')),
                'completed',
                array(
                    'total' => intval($status['total'] ?? 0),
                    'inserted' => intval($status['inserted'] ?? 0),
                    'updated' => intval($status['updated'] ?? 0),
                ),
                ''
            );
        } catch (Exception $e) {
            $status = $this->aysa_keyword_discovery_get_status($project_id);
            $status['status'] = 'failed';
            $status['last_error'] = (string) $e->getMessage();
            $status['updated_at'] = time();
            $status['finished_at'] = time();
            $this->aysa_keyword_discovery_save_status($project_id, $status);
            $this->aysa_project_jobs_finalize(
                $project_id,
                $this->aysa_project_jobs_make_worker_ref('keyword_discovery', 'keyword_discovery_refresh', (string) ($status['run_id'] ?? '')),
                'failed',
                null,
                (string) $e->getMessage()
            );
        } finally {
            delete_transient($lock_key);
        }
    }

    /**
     * AJAX handler: cumulative Research summary for Content Strategy dashboard.
     */
    public function handle_get_research_summary() {
        if (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], 'aysa_content_strategy_nonce')) {
            wp_send_json_error('Invalid nonce');
            return;
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error('Access denied');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : intval(get_option('aysa_project_id', 0));
        if ($project_id <= 0) {
            wp_send_json_error('Invalid project ID');
            return;
        }

        // Keep Sitemap Competition operationally isolated from the shared Research summary.
        // It has its own AJAX/storage/context flow and should not be preloaded or folded into
        // the fast summary endpoint used by other tabs.
        $requested_scope = isset($_POST['scope']) ? sanitize_key((string) $_POST['scope']) : '';
        if ($requested_scope === 'sitemap-competition') {
            wp_send_json_success(array(
                'project_id' => $project_id,
                'generated_at' => current_time('mysql'),
                'isolated' => true,
                'message' => 'Sitemap Competition is isolated from the shared Research summary endpoint.',
            ));
            return;
        }

        $summary = array(
            'project_id' => $project_id,
            'generated_at' => current_time('mysql'),
            'content_strategy' => array(
                'total' => 0,
                'generated' => 0,
                'published' => 0,
                'pending' => 0
            ),
            'keyword_discovery' => array(
                'suggested_keywords' => 0,
                'associated_keywords' => 0,
                'associations_total' => 0
            ),
            'competitor' => array(
                'domains' => 0,
                'avg_authority' => 0,
                'gap_keywords' => 0,
                'backlink_domains' => 0
            )
        );

        global $wpdb;
        $table_name = $wpdb->prefix . 'aysa_content_strategies';
        $table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table_name));
        if ($table_exists === $table_name) {
            $plan_batch = $this->aysa_agent8_current_research_plan_batch($project_id);
            if ($plan_batch !== '') {
                $rows = $wpdb->get_results($wpdb->prepare(
                    "SELECT status, post_id FROM {$table_name} WHERE project_id = %d AND plan_batch = %s",
                    $project_id,
                    $plan_batch
                ), ARRAY_A);
            } else {
                $rows = $wpdb->get_results($wpdb->prepare(
                    "SELECT status, post_id FROM {$table_name} WHERE project_id = %d",
                    $project_id
                ), ARRAY_A);
            }

            if (is_array($rows)) {
                $summary['content_strategy']['total'] = count($rows);
                foreach ($rows as $row) {
                    $status = isset($row['status']) ? strtolower((string) $row['status']) : '';
                    $post_id = isset($row['post_id']) ? intval($row['post_id']) : 0;

                    if ($status === 'generated') {
                        $summary['content_strategy']['generated']++;
                    }
                    if ($post_id > 0 && get_post_status($post_id) === 'publish') {
                        $summary['content_strategy']['published']++;
                    }
                }
                $summary['content_strategy']['pending'] = max(
                    0,
                    $summary['content_strategy']['total'] - $summary['content_strategy']['generated']
                );
            }
        }

        // Local-first summary for UI responsiveness.
        // This endpoint is loaded frequently in Content Strategy and must stay fast.
        try {
            $keyword_table = $this->aysa_keyword_discovery_table();
            $keyword_table_exists = $wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $keyword_table));
            if ($keyword_table_exists === $keyword_table) {
                $summary['keyword_discovery']['suggested_keywords'] = intval($wpdb->get_var($wpdb->prepare(
                    "SELECT COUNT(*) FROM {$keyword_table} WHERE project_id = %d",
                    $project_id
                )));
                $summary['competitor']['gap_keywords'] = intval($wpdb->get_var($wpdb->prepare(
                    "SELECT COUNT(*) FROM {$keyword_table} WHERE project_id = %d AND source = %s",
                    $project_id,
                    'competitor_gap'
                )));
            }

            $local_associations = get_option('aysa_keyword_discovery_local_associations_' . intval($project_id), array());
            if (is_array($local_associations)) {
                $summary['keyword_discovery']['associations_total'] = count($local_associations);
                $unique_keywords = array();
                foreach ($local_associations as $assoc) {
                    if (!is_array($assoc)) {
                        continue;
                    }
                    $kw = trim((string) ($assoc['keyword'] ?? ''));
                    if ($kw !== '') {
                        $unique_keywords[strtolower($kw)] = true;
                    }
                }
                $summary['keyword_discovery']['associated_keywords'] = count($unique_keywords);
            }

            // Setup SEO Profile competitors for current authenticated setup user (fallback WP user).
            $setup_user_id = intval($this->aysa_get_authenticated_external_user_id());
            if ($setup_user_id <= 0) {
                $setup_user_id = get_current_user_id();
            }
            $competition = get_option('aysa_wizard_competition_' . intval($setup_user_id), array());
            if (is_array($competition)) {
                $domains = array();
                foreach ($competition as $key => $value) {
                    if (!preg_match('/^competitor\d+$/', (string) $key)) {
                        continue;
                    }
                    $domain = trim((string) $value);
                    if ($domain !== '') {
                        $domains[strtolower($domain)] = true;
                    }
                }
                $summary['competitor']['domains'] = count($domains);
                if ($summary['competitor']['backlink_domains'] <= 0) {
                    $summary['competitor']['backlink_domains'] = count($domains);
                }
            }

            // Reuse local backlink cache summary if present.
            $backlink_cache = get_option('aysa_backlink_summary_' . intval($project_id), array());
            if (is_array($backlink_cache) && isset($backlink_cache['items']) && is_array($backlink_cache['items'])) {
                $domains = array();
                $authorities = array();
                foreach ($backlink_cache['items'] as $item) {
                    if (!is_array($item)) {
                        continue;
                    }
                    $domain = trim((string) ($item['domain'] ?? ''));
                    if ($domain !== '') {
                        $domains[strtolower($domain)] = true;
                    }
                    if (isset($item['authority']) && is_numeric($item['authority'])) {
                        $authorities[] = floatval($item['authority']);
                    }
                }
                if (!empty($domains)) {
                    $summary['competitor']['backlink_domains'] = count($domains);
                }
                if (!empty($authorities)) {
                    $summary['competitor']['avg_authority'] = round(array_sum($authorities) / count($authorities), 1);
                }
            }
        } catch (Exception $e) {
            aysa_debug_log('[AYSA Research Summary local] ' . $e->getMessage());
        }

        wp_send_json_success($summary);
    }

    /**
     * Normalize API responses that may be direct arrays or wrapped in data/items/results.
     */
    private function normalize_aysa_collection_response($response) {
        if (!is_array($response)) {
            return array();
        }

        // If already a list, return directly.
        $is_list = array_keys($response) === range(0, count($response) - 1);
        if ($is_list) {
            return array_values($response);
        }

        if (isset($response['data']) && is_array($response['data'])) {
            $data = $response['data'];
            $data_is_list = array_keys($data) === range(0, count($data) - 1);
            if ($data_is_list) {
                return array_values($data);
            }
        }
        if (isset($response['items']) && is_array($response['items'])) {
            return array_values($response['items']);
        }
        if (isset($response['results']) && is_array($response['results'])) {
            return array_values($response['results']);
        }
        if (isset($response['keywords']) && is_array($response['keywords'])) {
            return array_values($response['keywords']);
        }
        if (isset($response['associations']) && is_array($response['associations'])) {
            return array_values($response['associations']);
        }
        if (isset($response['rows']) && is_array($response['rows'])) {
            return array_values($response['rows']);
        }
        if (isset($response['payload']) && is_array($response['payload'])) {
            $nested_payload = $this->normalize_aysa_collection_response($response['payload']);
            if (!empty($nested_payload)) {
                return $nested_payload;
            }
        }

        // Some APIs wrap collections deeper inside object envelopes.
        foreach ($response as $value) {
            if (!is_array($value)) {
                continue;
            }
            $nested = $this->normalize_aysa_collection_response($value);
            if (!empty($nested)) {
                return $nested;
            }
        }

        return array();
    }

    /**
     * Extract likely domain names from a mixed API payload.
     */
    private function extract_domains_from_mixed_payload($payload) {
        $domains = array();
        $walk = function($node, $key = '') use (&$walk, &$domains) {
            if (is_array($node)) {
                foreach ($node as $k => $value) {
                    $walk($value, is_string($k) ? strtolower($k) : $key);
                }
                return;
            }

            if (!is_string($node)) {
                return;
            }

            $value = trim($node);
            if ($value === '') {
                return;
            }

            $looks_relevant_key = ($key !== '') && (
                strpos($key, 'domain') !== false ||
                strpos($key, 'competitor') !== false ||
                strpos($key, 'url') !== false
            );
            if (!$looks_relevant_key) {
                return;
            }

            $normalized = $this->normalize_domain_string($value);
            if ($normalized !== '') {
                $domains[$normalized] = true;
            }
        };

        $walk($payload, '');
        return array_keys($domains);
    }

    /**
     * Extract numeric values where key name suggests authority-like metrics.
     */
    private function extract_numeric_values_from_mixed_payload($payload, $key_candidates) {
        $numbers = array();
        $candidate_map = array_map('strtolower', (array) $key_candidates);

        $walk = function($node, $key = '') use (&$walk, &$numbers, $candidate_map) {
            if (is_array($node)) {
                foreach ($node as $k => $value) {
                    $walk($value, is_string($k) ? strtolower($k) : $key);
                }
                return;
            }

            if ($key === '' || !in_array($key, $candidate_map, true)) {
                return;
            }

            if (is_numeric($node)) {
                $num = floatval($node);
                if ($num >= 0 && $num <= 100) {
                    $numbers[] = $num;
                }
            }
        };

        $walk($payload, '');
        return $numbers;
    }

    /**
     * Extract unique keyword-like string values from mixed payload.
     */
    private function extract_keywords_from_mixed_payload($payload) {
        $keywords = array();

        $walk = function($node, $key = '') use (&$walk, &$keywords) {
            if (is_array($node)) {
                foreach ($node as $k => $value) {
                    $walk($value, is_string($k) ? strtolower($k) : $key);
                }
                return;
            }

            if (!is_string($node)) {
                return;
            }

            if ($key === '' || strpos($key, 'keyword') === false) {
                return;
            }

            $kw = trim($node);
            if ($kw !== '' && strlen($kw) <= 120) {
                $keywords[strtolower($kw)] = true;
            }
        };

        $walk($payload, '');
        return array_keys($keywords);
    }

    /**
     * Normalize URLs/domains to host string.
     */
    private function normalize_domain_string($value) {
        $candidate = trim((string) $value);
        if ($candidate === '') {
            return '';
        }

        if (!preg_match('#^https?://#i', $candidate)) {
            $candidate = 'https://' . ltrim($candidate, '/');
        }

        $host = wp_parse_url($candidate, PHP_URL_HOST);
        if (!is_string($host) || $host === '') {
            return '';
        }

        $host = strtolower($host);
        if (strpos($host, '.') === false) {
            return '';
        }

        return preg_replace('/^www\./', '', $host);
    }
    
    /**
     * AJAX handler: Get Associations List (for batch processing)
     */
    public function handle_cleanup_get_associations() {
        if (!$this->aysa_guard_destructive_tool_request()) {
            return;
        }

        // Security checks
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Access denied');
            return;
        }
        
        if (!wp_verify_nonce($_POST['nonce'], 'aysa-cleanup')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        if ($project_id <= 0) {
            wp_send_json_error('Invalid Project ID');
            return;
        }
        
        try {
            if (!class_exists('Aysa_Auth_Manager')) {
                require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
            }
            
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $associations_url = 'api/Association?projectId=' . $project_id . '&pageSize=10000';
            $associations_response = $auth_manager->make_authenticated_request($associations_url, 'GET');
            
            if ($associations_response === false) {
                wp_send_json_error('Failed to fetch associations from API');
                return;
            }
            
            if (!is_array($associations_response)) {
                wp_send_json_error('Invalid response format from API: ' . gettype($associations_response));
                return;
            }
            
            // Check if response is wrapped or paginated
            if (isset($associations_response['data']) && is_array($associations_response['data'])) {
                $associations_response = $associations_response['data'];
            } elseif (isset($associations_response['items']) && is_array($associations_response['items'])) {
                $associations_response = $associations_response['items'];
            }
            
            // Extract only IDs
            $association_ids = array();
            foreach ($associations_response as $association) {
                $association_id = isset($association['id']) ? $association['id'] : (isset($association['Id']) ? $association['Id'] : null);
                if ($association_id) {
                    $association_ids[] = $association_id;
                }
            }
            
            wp_send_json_success(array(
                'ids' => $association_ids,
                'total' => count($association_ids)
            ));
            
        } catch (Exception $e) {
            aysa_debug_log('[AYSA CLEANUP] Get Associations Exception: ' . $e->getMessage());
            wp_send_json_error('Error: ' . $e->getMessage());
        }
    }
    
    /**
     * AJAX handler: Delete Associations Batch
     */
    public function handle_cleanup_associations_batch() {
        if (!$this->aysa_guard_destructive_tool_request()) {
            return;
        }

        // Security checks
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Access denied');
            return;
        }
        
        if (!wp_verify_nonce($_POST['nonce'], 'aysa-cleanup')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        if ($project_id <= 0) {
            wp_send_json_error('Invalid Project ID');
            return;
        }
        
        $association_ids = isset($_POST['association_ids']) ? $_POST['association_ids'] : array();
        if (!is_array($association_ids) || empty($association_ids)) {
            wp_send_json_error('No association IDs provided');
            return;
        }
        
        try {
            if (!class_exists('Aysa_Auth_Manager')) {
                require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
            }
            
            $auth_manager = Aysa_Auth_Manager::get_instance();
            $deleted = 0;
            $errors = [];
            
            // Process each association ID
            foreach ($association_ids as $association_id) {
                $association_id = intval($association_id);
                if ($association_id <= 0) {
                    continue;
                }
                
                $delete_url = 'api/Association/' . $association_id . '?projectId=' . $project_id;
                $delete_response = $auth_manager->make_authenticated_request($delete_url, 'DELETE');
                
                if ($delete_response !== false) {
                    $deleted++;
                } else {
                    $errors[] = 'Failed to delete association ID: ' . $association_id;
                }
            }
            
            wp_send_json_success(array(
                'deleted' => $deleted,
                'errors' => $errors,
                'processed' => count($association_ids)
            ));
            
        } catch (Exception $e) {
            aysa_debug_log('[AYSA CLEANUP] Delete Batch Exception: ' . $e->getMessage());
            wp_send_json_error('Error: ' . $e->getMessage());
        }
    }
    
    /**
     * AJAX handler: Cleanup WordPress Meta Fields
     */
    public function handle_cleanup_wordpress_meta() {
        if (!$this->aysa_guard_destructive_tool_request()) {
            return;
        }

        // Security checks
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Access denied');
            return;
        }
        
        if (!wp_verify_nonce($_POST['nonce'], 'aysa-cleanup')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        if ($project_id <= 0) {
            wp_send_json_error('Invalid Project ID');
            return;
        }
        
        try {
            global $wpdb;
            
            $total_deleted = 0;
            $errors = [];
            
            // Initialize counters for all meta tables
            $postmeta_deleted = 0;
            $termmeta_deleted = 0;
            $commentmeta_deleted = 0;
            $usermeta_deleted = 0;
            
            // List of known AYSA meta keys to explicitly delete (in addition to pattern matching)
            $known_meta_keys = array(
                'aysa_keyword_data',
                'aysa_keyword',
                'aysa_secondary_keywords',
                'aysa_second_keyword',
                'aysa_meta_title',
                'aysa_meta_description',
                'aysa_meta_keywords',
                'aysa_url_recomandat',
                'aysa_article_topic',
                'aysa_local_specifics',
                '_aysa_seo_title',
                '_aysa_seo_description',
                '_aysa_seo_keywords',
                '_aysa_temp_save_data',
                '_aysa_primary_keyword',
                'aysa_primary_keyword'
            );
            
            // Delete from postmeta (Posts, Pages, Products, etc.)
            // First, delete by pattern (covers most cases)
            $postmeta_deleted_pattern = $wpdb->query(
                "DELETE FROM {$wpdb->postmeta} 
                 WHERE meta_key LIKE 'aysa_%' OR meta_key LIKE '_aysa_%'"
            );
            
            // Log pattern deletion result
            if ($postmeta_deleted_pattern === false) {
                aysa_debug_log('[AYSA CLEANUP] Postmeta pattern query failed: ' . $wpdb->last_error);
            } else {
                aysa_debug_log('[AYSA CLEANUP] Postmeta pattern query result: ' . intval($postmeta_deleted_pattern) . ' rows');
            }
            
            // Then, explicitly delete known meta keys (in case pattern didn't catch them)
            $postmeta_deleted_explicit = 0;
            foreach ($known_meta_keys as $meta_key) {
                $deleted = $wpdb->query(
                    $wpdb->prepare(
                        "DELETE FROM {$wpdb->postmeta} WHERE meta_key = %s",
                        $meta_key
                    )
                );
                if ($deleted !== false) {
                    $explicit_count = intval($deleted);
                    $postmeta_deleted_explicit += $explicit_count;
                    if ($explicit_count > 0) {
                        aysa_debug_log('[AYSA CLEANUP] Deleted ' . $explicit_count . ' postmeta records for key: ' . $meta_key);
                    }
                } else {
                    aysa_debug_log('[AYSA CLEANUP] Failed to delete postmeta for key: ' . $meta_key . ' - Error: ' . $wpdb->last_error);
                }
            }
            
            // Calculate total - $wpdb->query() returns number of rows affected (0 if none, false on error)
            $postmeta_deleted = ($postmeta_deleted_pattern !== false ? intval($postmeta_deleted_pattern) : 0) + $postmeta_deleted_explicit;
            
            if ($postmeta_deleted_pattern === false) {
                $errors[] = 'Postmeta pattern error: ' . $wpdb->last_error;
            } else {
                $total_deleted += $postmeta_deleted;
                aysa_debug_log('[AYSA CLEANUP] Total postmeta deleted: ' . $postmeta_deleted . ' (pattern: ' . ($postmeta_deleted_pattern !== false ? intval($postmeta_deleted_pattern) : 0) . ', explicit: ' . $postmeta_deleted_explicit . ')');
            }
            
            // Delete from termmeta (Categories, Tags, Custom Taxonomies)
            if ($wpdb->get_var("SHOW TABLES LIKE '{$wpdb->prefix}termmeta'") == $wpdb->prefix . 'termmeta') {
                $termmeta_deleted_pattern = $wpdb->query(
                    "DELETE FROM {$wpdb->termmeta} 
                     WHERE meta_key LIKE 'aysa_%' OR meta_key LIKE '_aysa_%'"
                );
                
                $termmeta_deleted_explicit = 0;
                foreach ($known_meta_keys as $meta_key) {
                    $deleted = $wpdb->query(
                        $wpdb->prepare(
                            "DELETE FROM {$wpdb->termmeta} WHERE meta_key = %s",
                            $meta_key
                        )
                    );
                    if ($deleted !== false) {
                        $termmeta_deleted_explicit += intval($deleted);
                    }
                }
                
                $termmeta_deleted = ($termmeta_deleted_pattern !== false ? intval($termmeta_deleted_pattern) : 0) + $termmeta_deleted_explicit;
                
                if ($termmeta_deleted_pattern === false) {
                    $errors[] = 'Termmeta pattern error: ' . $wpdb->last_error;
                } else {
                    $total_deleted += $termmeta_deleted;
                    aysa_debug_log('[AYSA CLEANUP] Deleted ' . $termmeta_deleted . ' termmeta records (pattern: ' . ($termmeta_deleted_pattern !== false ? intval($termmeta_deleted_pattern) : 0) . ', explicit: ' . $termmeta_deleted_explicit . ')');
                }
            }
            
            // Delete from commentmeta (Comments)
            if ($wpdb->get_var("SHOW TABLES LIKE '{$wpdb->prefix}commentmeta'") == $wpdb->prefix . 'commentmeta') {
                $commentmeta_deleted_pattern = $wpdb->query(
                    "DELETE FROM {$wpdb->commentmeta} 
                     WHERE meta_key LIKE 'aysa_%' OR meta_key LIKE '_aysa_%'"
                );
                
                $commentmeta_deleted_explicit = 0;
                foreach ($known_meta_keys as $meta_key) {
                    $deleted = $wpdb->query(
                        $wpdb->prepare(
                            "DELETE FROM {$wpdb->commentmeta} WHERE meta_key = %s",
                            $meta_key
                        )
                    );
                    if ($deleted !== false) {
                        $commentmeta_deleted_explicit += intval($deleted);
                    }
                }
                
                $commentmeta_deleted = ($commentmeta_deleted_pattern !== false ? intval($commentmeta_deleted_pattern) : 0) + $commentmeta_deleted_explicit;
                
                if ($commentmeta_deleted_pattern === false) {
                    $errors[] = 'Commentmeta pattern error: ' . $wpdb->last_error;
                } else {
                    $total_deleted += $commentmeta_deleted;
                    aysa_debug_log('[AYSA CLEANUP] Deleted ' . $commentmeta_deleted . ' commentmeta records (pattern: ' . ($commentmeta_deleted_pattern !== false ? intval($commentmeta_deleted_pattern) : 0) . ', explicit: ' . $commentmeta_deleted_explicit . ')');
                }
            }
            
            // Delete from usermeta (User meta - if any)
            $usermeta_deleted_pattern = $wpdb->query(
                "DELETE FROM {$wpdb->usermeta} 
                 WHERE meta_key LIKE 'aysa_%' OR meta_key LIKE '_aysa_%'"
            );
            
            $usermeta_deleted_explicit = 0;
            foreach ($known_meta_keys as $meta_key) {
                $deleted = $wpdb->query(
                    $wpdb->prepare(
                        "DELETE FROM {$wpdb->usermeta} WHERE meta_key = %s",
                        $meta_key
                    )
                );
                if ($deleted !== false) {
                    $usermeta_deleted_explicit += intval($deleted);
                }
            }
            
            $usermeta_deleted = ($usermeta_deleted_pattern !== false ? intval($usermeta_deleted_pattern) : 0) + $usermeta_deleted_explicit;
            
            if ($usermeta_deleted_pattern === false) {
                $errors[] = 'Usermeta pattern error: ' . $wpdb->last_error;
            } else {
                $total_deleted += $usermeta_deleted;
                aysa_debug_log('[AYSA CLEANUP] Deleted ' . $usermeta_deleted . ' usermeta records (pattern: ' . ($usermeta_deleted_pattern !== false ? intval($usermeta_deleted_pattern) : 0) . ', explicit: ' . $usermeta_deleted_explicit . ')');
            }
            
            // Log final results for debugging
            aysa_debug_log('[AYSA CLEANUP] WordPress Meta - Total deleted: ' . $total_deleted);
            aysa_debug_log('[AYSA CLEANUP] WordPress Meta - Breakdown: postmeta=' . intval($postmeta_deleted) . ', termmeta=' . intval($termmeta_deleted) . ', commentmeta=' . intval($commentmeta_deleted) . ', usermeta=' . intval($usermeta_deleted));
            
            wp_send_json_success(array(
                'deleted' => $total_deleted,
                'errors' => $errors,
                'breakdown' => array(
                    'postmeta' => intval($postmeta_deleted),
                    'termmeta' => intval($termmeta_deleted),
                    'commentmeta' => intval($commentmeta_deleted),
                    'usermeta' => intval($usermeta_deleted)
                )
            ));
            
        } catch (Exception $e) {
            aysa_debug_log('[AYSA CLEANUP] WordPress Meta Exception: ' . $e->getMessage());
            wp_send_json_error('Error: ' . $e->getMessage());
        } catch (Error $e) {
            aysa_debug_log('[AYSA CLEANUP] WordPress Meta Fatal Error: ' . $e->getMessage());
            wp_send_json_error('Fatal Error: ' . $e->getMessage());
        }
    }
    
    /**
     * AJAX handler: Cleanup GeneratedSeoTitles
     */
    public function handle_cleanup_generated_titles() {
        if (!$this->aysa_guard_destructive_tool_request()) {
            return;
        }

        // Security checks
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Access denied');
            return;
        }
        
        if (!wp_verify_nonce($_POST['nonce'], 'aysa-cleanup')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        if ($project_id <= 0) {
            wp_send_json_error('Invalid Project ID');
            return;
        }
        
        try {
            if (!class_exists('Aysa_Auth_Manager')) {
                require_once AYSA_PLUGIN_DIR . 'includes/class-aysa-auth-manager.php';
            }
            
            $auth_manager = Aysa_Auth_Manager::get_instance();
            
            // Try to get all generated titles for this project
            $titles_url = 'api/SeoTitleGeneration/ByProject/' . $project_id;
            $titles_response = $auth_manager->make_authenticated_request($titles_url, 'GET');
            
            $deleted = 0;
            $errors = [];
            
            if ($titles_response !== false && is_array($titles_response)) {
                // Check if response is wrapped
                if (isset($titles_response['data']) && is_array($titles_response['data'])) {
                    $titles_response = $titles_response['data'];
                }
                
                foreach ($titles_response as $title) {
                    $title_id = isset($title['id']) ? $title['id'] : (isset($title['Id']) ? $title['Id'] : null);
                    if ($title_id) {
                        $delete_url = 'api/SeoTitleGeneration/' . $title_id;
                        $delete_response = $auth_manager->make_authenticated_request($delete_url, 'DELETE');
                        
                        // 204 No Content, true, or any truthy value means success
                        if ($delete_response !== false) {
                            $deleted++;
                        } else {
                            $errors[] = 'Failed to delete title ID: ' . $title_id;
                        }
                    }
                }
            } else {
                // Endpoint doesn't exist or returned no data - this is OK, not an error
                aysa_debug_log('[AYSA CLEANUP] GeneratedSeoTitles endpoint not found or returned no data - this is normal if endpoint doesn\'t exist');
                // Don't add to errors - this is expected if endpoint doesn't exist
                // Return success with 0 deleted
            }
            
            wp_send_json_success(array(
                'deleted' => $deleted,
                'errors' => $errors
            ));
            
        } catch (Exception $e) {
            wp_send_json_error('Error: ' . $e->getMessage());
        }
    }
    
    /**
     * AJAX handler: Cleanup Content Strategies
     */
    public function handle_cleanup_content_strategies() {
        if (!wp_verify_nonce($_POST['nonce'], 'aysa-cleanup')) {
            wp_send_json_error('Invalid nonce');
            return;
        }
        
        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        if ($project_id <= 0) {
            wp_send_json_error('Invalid Project ID');
            return;
        }
        
        try {
            global $wpdb;
            
            $table_name = $wpdb->prefix . 'aysa_content_strategies';
            $meta_table = $this->aysa_missing_page_meta_table();
            
            // Check if table exists
            $table_exists = $wpdb->get_var("SHOW TABLES LIKE '{$table_name}'") == $table_name;
            $meta_exists = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $meta_table)) === $meta_table;

            $deleted = 0;
            $meta_deleted = 0;
            $snapshot_deleted = 0;
            $errors = array();

            if ($meta_exists) {
                $meta_deleted_result = $wpdb->query(
                    $wpdb->prepare(
                        "DELETE FROM {$meta_table} WHERE project_id = %d",
                        $project_id
                    )
                );
                if ($meta_deleted_result === false) {
                    $errors[] = 'Missing page meta deletion error: ' . $wpdb->last_error;
                    aysa_debug_log('[AYSA CLEANUP] Missing page meta deletion error: ' . $wpdb->last_error);
                } else {
                    $meta_deleted = intval($meta_deleted_result);
                }
            }

            if ($table_exists) {
                $deleted_result = $wpdb->query(
                    $wpdb->prepare(
                        "DELETE FROM {$table_name} WHERE project_id = %d",
                        $project_id
                    )
                );
                if ($deleted_result === false) {
                    $errors[] = 'Content strategies deletion error: ' . $wpdb->last_error;
                    aysa_debug_log('[AYSA CLEANUP] Content strategies deletion error: ' . $wpdb->last_error);
                } else {
                    $deleted = intval($deleted_result);
                }
            }

            $before_content_snapshot = $this->aysa_keyword_v2_cache_get($project_id, 'content_strategy_v2');
            $before_missing_snapshot = $this->aysa_keyword_v2_cache_get($project_id, 'missing_pages_v2');
            $this->aysa_keyword_v2_cache_delete($project_id, 'content_strategy_v2');
            $this->aysa_keyword_v2_cache_delete($project_id, 'missing_pages_v2');
            if (is_array($before_content_snapshot)) {
                $snapshot_deleted++;
            }
            if (is_array($before_missing_snapshot)) {
                $snapshot_deleted++;
            }

            update_option('aysa_content_strategy_hard_reset_' . $project_id, current_time('mysql'));

            if (!$table_exists && !$meta_exists && $snapshot_deleted === 0) {
                wp_send_json_success(array(
                    'deleted' => 0,
                    'meta_deleted' => 0,
                    'snapshots_deleted' => 0,
                    'errors' => array(),
                    'message' => 'Nothing to clear for this project'
                ));
                return;
            }
            
            wp_send_json_success(array(
                'deleted' => $deleted,
                'meta_deleted' => $meta_deleted,
                'snapshots_deleted' => $snapshot_deleted,
                'errors' => $errors,
                'message' => sprintf(
                    'Cleared %d content strategies, %d meta rows and %d snapshots.',
                    $deleted,
                    $meta_deleted,
                    $snapshot_deleted
                )
            ));
            
        } catch (Exception $e) {
            aysa_debug_log('[AYSA CLEANUP] Content Strategies Exception: ' . $e->getMessage());
            wp_send_json_error('Error: ' . $e->getMessage());
        } catch (Error $e) {
            aysa_debug_log('[AYSA CLEANUP] Content Strategies Fatal Error: ' . $e->getMessage());
            wp_send_json_error('Fatal Error: ' . $e->getMessage());
        }
    }

    /**
     * AJAX handler: Cleanup local project data/caches/queues.
     */
    public function handle_cleanup_local_project_data() {
        if (!$this->aysa_guard_destructive_tool_request()) {
            return;
        }

        if (!current_user_can('manage_options')) {
            wp_send_json_error('Access denied');
            return;
        }

        if (!wp_verify_nonce($_POST['nonce'], 'aysa-cleanup')) {
            wp_send_json_error('Invalid nonce');
            return;
        }

        $project_id = isset($_POST['project_id']) ? intval($_POST['project_id']) : 0;
        if ($project_id <= 0) {
            wp_send_json_error('Invalid Project ID');
            return;
        }

        try {
            $deleted = 0;
            $errors = array();
            $breakdown = array(
                'keyword_metrics_deleted' => 0,
                'monitoring_rank_history_deleted' => 0,
                'keyword_v2_snapshots_deleted' => 0,
                'content_strategies_deleted' => 0,
                'missing_page_meta_deleted' => 0,
                'pending_reviews_deleted' => 0,
                'options_deleted' => 0,
                'transients_deleted' => 0,
                'scheduled_events_cleared' => 0,
            );

            $database_breakdown = $this->aysa_reset_local_project_database_state($project_id);
            foreach ($database_breakdown as $key => $rows) {
                if (array_key_exists($key, $breakdown)) {
                    $rows = intval($rows);
                    $breakdown[$key] = $rows;
                    $deleted += $rows;
                }
            }

            $option_keys = array(
                $this->aysa_keyword_discovery_status_key($project_id),
                $this->aysa_keyword_discovery_snapshot_key($project_id),
                $this->get_keyword_discovery_local_associations_option_key($project_id),
                $this->aysa_monitoring_refresh_status_key($project_id),
                $this->aysa_monitoring_refresh_queue_key($project_id),
                'aysa_content_strategy_hard_reset_' . $project_id,
            );
            foreach ($option_keys as $key) {
                if (delete_option($key)) {
                    $breakdown['options_deleted']++;
                }
            }

            $transient_keys = array(
                $this->get_suggested_keywords_cache_key($project_id),
                $this->aysa_keyword_discovery_lock_key($project_id),
                $this->aysa_monitoring_refresh_lock_key($project_id),
            );
            foreach ($transient_keys as $key) {
                if (delete_transient($key)) {
                    $breakdown['transients_deleted']++;
                }
            }

            while (false !== ($ts = wp_next_scheduled('aysa_keyword_discovery_refresh_background_event', array($project_id)))) {
                wp_unschedule_event($ts, 'aysa_keyword_discovery_refresh_background_event', array($project_id));
                $breakdown['scheduled_events_cleared']++;
            }
            while (false !== ($ts = wp_next_scheduled('aysa_monitoring_rank_refresh_background_event', array($project_id)))) {
                wp_unschedule_event($ts, 'aysa_monitoring_rank_refresh_background_event', array($project_id));
                $breakdown['scheduled_events_cleared']++;
            }
            while (false !== ($ts = wp_next_scheduled('aysa_monitoring_rank_refresh_cron_tick', array($project_id)))) {
                wp_unschedule_event($ts, 'aysa_monitoring_rank_refresh_cron_tick', array($project_id));
                $breakdown['scheduled_events_cleared']++;
            }

            delete_option('aysa_setup_completed');
            $this->aysa_mark_setup_reset_forced();

            wp_send_json_success(array(
                'deleted' => $deleted,
                'errors' => $errors,
                'breakdown' => $breakdown,
            ));
        } catch (Exception $e) {
            aysa_debug_log('[AYSA CLEANUP] Local project cleanup exception: ' . $e->getMessage());
            wp_send_json_error('Error: ' . $e->getMessage());
        } catch (Error $e) {
            aysa_debug_log('[AYSA CLEANUP] Local project cleanup fatal: ' . $e->getMessage());
            wp_send_json_error('Fatal Error: ' . $e->getMessage());
        }
    }
}
