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

/**
 * Enhanced VMO Business Wizard with Media Integration and Two-way Voice
 * Comprehensive business consultant with database storage and advanced features
 */

// Include the consultation database with file existence validation
$consultation_db_file = '';
if (function_exists('plugin_dir_path')) {
    $consultation_db_file = plugin_dir_path(__FILE__) . 'class-vmo-consultation-database.php';
} else {
    $consultation_db_file = dirname(__FILE__) . '/class-vmo-consultation-database.php';
}

if (file_exists($consultation_db_file) && is_readable($consultation_db_file)) {
    try {
        require_once $consultation_db_file;
    } catch (\Throwable $e) {
        if (defined('WP_DEBUG') && WP_DEBUG) {
            error_log('VMO: Failed to load consultation database: ' . $e->getMessage());
        }
        // Create a minimal fallback class to prevent fatal errors
        if (!class_exists('VMO_Consultation_Database')) {
            class VMO_Consultation_Database {
                public function __construct() {}
                public function __call($method, $args) { return null; }
                public static function __callStatic($method, $args) { return null; }
            }
        }
    }
} else {
    if (defined('WP_DEBUG') && WP_DEBUG) {
        error_log('VMO: Consultation database file missing: ' . $consultation_db_file);
    }
    // Create a minimal fallback class to prevent fatal errors
    if (!class_exists('VMO_Consultation_Database')) {
        class VMO_Consultation_Database {
            public function __construct() {}
            public function __call($method, $args) { return null; }
            public static function __callStatic($method, $args) { return null; }
        }
    }
}

// Ensure deep research agent is available when the wizard triggers it server-side
$deep_research_file = function_exists('plugin_dir_path')
    ? plugin_dir_path(__FILE__) . 'class-vmo-deep-research.php'
    : dirname(__FILE__) . '/class-vmo-deep-research.php';
if (file_exists($deep_research_file) && is_readable($deep_research_file)) {
    require_once $deep_research_file;
}

foreach (array('class-vmo-business-manifest-generator.php', 'class-vmo-automation-engine.php', 'class-vmo-document-generator.php', 'class-vmo-legal.php', 'class-vmo-consultation-deliverables-gate.php') as $vmo_os_include) {
    $vmo_os_file = function_exists('plugin_dir_path')
        ? plugin_dir_path(__FILE__) . $vmo_os_include
        : dirname(__FILE__) . '/' . $vmo_os_include;
    if (file_exists($vmo_os_file) && is_readable($vmo_os_file)) {
        require_once $vmo_os_file;
    }
}

class VMO_Enhanced_Business_Wizard {
    private static $instance = null;
    private static $ajax_handlers_registered = false;
    private static $seo_hooks_registered = false;

    private $api_key;
    private $twilio_sid;
    private $twilio_token;
    private $agents;
    private $business_questions;
    private $consultation_db;
    private $consultation_blueprint;
    private $deep_research;

    public static function get_instance() {
        if (self::$instance instanceof self) {
            return self::$instance;
        }

        self::$instance = new self();
        return self::$instance;
    }
    
    public function __construct() {
        if (!(self::$instance instanceof self)) {
            self::$instance = $this;
        }

        $this->api_key = get_option('vmo_openai_api_key');
        $this->twilio_sid = get_option('vmo_twilio_account_sid');
        $this->twilio_token = get_option('vmo_twilio_auth_token');
        
        $this->consultation_db = new VMO_Consultation_Database();
        $this->consultation_blueprint = $this->build_consultation_blueprint();
        $this->init_agents();
        $this->init_business_questions();
        $this->deep_research = $this->get_deep_research_service();
        
        if (!self::$ajax_handlers_registered) {
            $this->setup_ajax_handlers();
            self::$ajax_handlers_registered = true;
        }

        if (!self::$seo_hooks_registered) {
            $this->setup_seo_automation_hooks();
            self::$seo_hooks_registered = true;
        }
    }

    /**
     * Lazily initialize the deep research service so constructor does not fatal when optional file is missing
     */
    private function get_deep_research_service() {
        if ($this->deep_research instanceof VMO_Deep_Research) {
            return $this->deep_research;
        }

        if (!class_exists('VMO_Deep_Research')) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log('VMO: VMO_Deep_Research class not found. Ensure class-vmo-deep-research.php is loaded.');
            }
            return null;
        }

        try {
            $this->deep_research = new VMO_Deep_Research();
        } catch (\Throwable $e) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log('VMO: Failed to initialize deep research service: ' . $e->getMessage());
            }
            $this->deep_research = null;
        }

        return $this->deep_research;
    }
    
    /**
     * Setup SEO automation WordPress cron hooks
     */
    private function setup_seo_automation_hooks() {
        add_action('vmo_seo_audit_event', array($this, 'execute_seo_audit'), 10, 2);
        add_action('vmo_generate_seo_blog_post', array($this, 'generate_automated_blog_post'), 10, 2);
        add_action('vmo_weekly_seo_audit', array($this, 'execute_weekly_seo_audit'), 10, 1);
        add_action('vmo_monthly_blog_generation', array($this, 'execute_monthly_blog_generation'), 10, 2);
        add_action('vmo_competitor_monitoring', array($this, 'execute_competitor_monitoring'), 10, 2);
        add_action('vmo_ranking_tracking', array($this, 'execute_ranking_tracking'), 10, 2);
        add_action('vmo_weekly_deep_research', array($this, 'execute_weekly_deep_research'), 10, 2);
        add_filter('cron_schedules', array($this, 'add_seo_cron_schedules'));
    }
    
    /**
     * Add custom cron schedules for SEO automation
     */
    public function add_seo_cron_schedules($schedules) {
        if (!isset($schedules['weekly'])) {
            $schedules['weekly'] = array(
                'interval' => 7 * DAY_IN_SECONDS,
                'display' => 'Weekly'
            );
        }
        if (!isset($schedules['twicemonthly'])) {
            $schedules['twicemonthly'] = array(
                'interval' => 15 * DAY_IN_SECONDS,
                'display' => 'Twice Monthly'
            );
        }
        return $schedules;
    }
    
    /**
     * Setup AJAX handlers
     */
    private function setup_ajax_handlers() {
        // AJAX handlers for logged-in users
        add_action('wp_ajax_vmo_save_consultation_message', array($this, 'ajax_save_consultation_message'));
        add_action('wp_ajax_vmo_upload_consultation_media', array($this, 'ajax_upload_consultation_media'));
        add_action('wp_ajax_vmo_process_voice_message', array($this, 'ajax_process_voice_message'));
        add_action('wp_ajax_vmo_transcribe_audio', array($this, 'ajax_transcribe_audio'));
        add_action('wp_ajax_vmo_process_consultation_message', array($this, 'ajax_process_consultation_message'));
        add_action('wp_ajax_vmo_get_consultation_context', array($this, 'ajax_get_consultation_context'));
        add_action('wp_ajax_vmo_generate_business_website_enhanced', array($this, 'ajax_generate_business_website_enhanced'));
        add_action('wp_ajax_vmo_generate_website_preview', array($this, 'ajax_generate_website_preview'));
        add_action('wp_ajax_vmo_update_consultation_context', array($this, 'ajax_update_consultation_context'));
        add_action('wp_ajax_vmo_trigger_deep_research', array($this, 'ajax_trigger_deep_research'));
        add_action('wp_ajax_vmo_analyze_uploaded_media', array($this, 'ajax_analyze_uploaded_media'));
        add_action('wp_ajax_vmo_analyze_text_document', array($this, 'ajax_analyze_text_document'));
        add_action('wp_ajax_vmo_transcribe_audio_url', array($this, 'ajax_transcribe_audio_url'));
        add_action('wp_ajax_vmo_describe_image', array($this, 'ajax_describe_image'));
        add_action('wp_ajax_vmo_check_credit_status', array($this, 'ajax_check_credit_status'));
        add_action('wp_ajax_nopriv_vmo_check_credit_status', array($this, 'ajax_check_credit_status'));
        
        // Website workflow handlers
        add_action('wp_ajax_vmo_publish_website', array($this, 'ajax_publish_website'));
        add_action('wp_ajax_vmo_unpublish_website', array($this, 'ajax_unpublish_website'));
        add_action('wp_ajax_vmo_save_website_changes', array($this, 'ajax_save_website_changes'));
        add_action('wp_ajax_vmo_undo_website_change', array($this, 'ajax_undo_website_change'));
        add_action('wp_ajax_vmo_delete_website', array($this, 'ajax_delete_website'));
        add_action('wp_ajax_vmo_delete_business', array($this, 'ajax_delete_business'));
        add_action('wp_ajax_vmo_reset_builder', array($this, 'ajax_reset_builder'));
        add_action('wp_ajax_vmo_reset_consultation', array($this, 'ajax_reset_consultation'));
        add_action('wp_ajax_vmo_list_builder_backups', array($this, 'ajax_list_builder_backups'));
        add_action('wp_ajax_vmo_restore_builder_backup', array($this, 'ajax_restore_builder_backup'));
        add_action('wp_ajax_vmo_get_website_state', array($this, 'ajax_get_website_state'));
        add_action('wp_ajax_vmo_update_user_preference', array($this, 'ajax_update_user_preference'));
        add_action('wp_ajax_vmo_check_unsaved_changes', array($this, 'ajax_check_unsaved_changes'));
        add_action('wp_ajax_vmo_generate_business_roadmap', array($this, 'ajax_generate_business_roadmap'));
        add_action('wp_ajax_vmo_generate_daily_activities', array($this, 'ajax_generate_daily_activities'));
        
        // Elite 8-Phase Consultation endpoints
        add_action('wp_ajax_vmo_get_phase_outputs', array($this, 'ajax_get_phase_outputs'));
        add_action('wp_ajax_vmo_get_progress_tracking', array($this, 'ajax_get_progress_tracking'));
        add_action('wp_ajax_vmo_get_todays_activities', array($this, 'ajax_get_todays_activities'));
        add_action('wp_ajax_vmo_complete_activity', array($this, 'ajax_complete_activity'));
        add_action('wp_ajax_vmo_get_business_roadmap', array($this, 'ajax_get_business_roadmap'));
        add_action('wp_ajax_vmo_download_business_roadmap', array($this, 'ajax_download_business_roadmap'));
        add_action('wp_ajax_vmo_download_daily_activities', array($this, 'ajax_download_daily_activities'));
        add_action('wp_ajax_vmo_get_vision_progress', array($this, 'ajax_get_vision_progress'));
        
        // Vision Roadmap Engine AJAX endpoints
        add_action('wp_ajax_vmo_submit_daily_report', array($this, 'ajax_submit_daily_report'));
        add_action('wp_ajax_vmo_get_progress_summary', array($this, 'ajax_get_progress_summary'));
        add_action('wp_ajax_vmo_apply_roadmap_adjustments', array($this, 'ajax_apply_roadmap_adjustments'));
        add_action('wp_ajax_vmo_export_daily_activities', array($this, 'ajax_export_daily_activities'));
        add_action('wp_ajax_vmo_export_daily_reports', array($this, 'ajax_export_daily_reports'));
        add_action('wp_ajax_vmo_export_milestones', array($this, 'ajax_export_milestones'));
        add_action('wp_ajax_vmo_get_day_activities', array($this, 'ajax_get_day_activities'));
        add_action('wp_ajax_vmo_get_daily_report', array($this, 'ajax_get_daily_report'));
        add_action('wp_ajax_vmo_get_pending_adjustments', array($this, 'ajax_get_pending_adjustments'));
        
        // AJAX handlers for non-logged-in users (voice support)
        add_action('wp_ajax_nopriv_vmo_process_consultation_message', array($this, 'ajax_process_consultation_message'));
        add_action('wp_ajax_nopriv_vmo_transcribe_audio', array($this, 'ajax_transcribe_audio'));
        add_action('wp_ajax_nopriv_vmo_process_voice_message', array($this, 'ajax_process_voice_message'));
        add_action('wp_ajax_nopriv_vmo_analyze_uploaded_media', array($this, 'ajax_analyze_uploaded_media'));
        add_action('wp_ajax_nopriv_vmo_analyze_text_document', array($this, 'ajax_analyze_text_document'));
        add_action('wp_ajax_nopriv_vmo_transcribe_audio_url', array($this, 'ajax_transcribe_audio_url'));
        add_action('wp_ajax_nopriv_vmo_describe_image', array($this, 'ajax_describe_image'));
        add_action('wp_ajax_nopriv_vmo_get_consultation_context', array($this, 'ajax_get_consultation_context'));
        add_action('wp_ajax_nopriv_vmo_trigger_deep_research', array($this, 'ajax_trigger_deep_research'));

        // Contact management handlers
        add_action('wp_ajax_vmo_get_contact_details', array($this, 'ajax_get_contact_details'));
        add_action('wp_ajax_vmo_add_manual_contact', array($this, 'ajax_add_manual_contact')); // NEW: Manual Add hook
        add_action('wp_ajax_vmo_update_contact', array($this, 'ajax_update_contact'));
        add_action('wp_ajax_vmo_add_contact_interaction', array($this, 'ajax_add_contact_interaction'));
        add_action('wp_ajax_vmo_get_contact_interactions', array($this, 'ajax_get_contact_interactions'));
        add_action('wp_ajax_vmo_import_contacts_csv', array($this, 'ajax_import_contacts_csv')); // NEW: Import hook
        add_action('wp_ajax_vmo_delete_contact', array($this, 'ajax_delete_contact'));
        add_action('wp_ajax_vmo_bulk_contact_action', array($this, 'ajax_bulk_contact_action'));
        add_action('wp_ajax_vmo_search_contacts', array($this, 'ajax_search_contacts'));
        add_action('wp_ajax_vmo_get_contacts_list', array($this, 'ajax_get_contacts_list'));
        add_action('wp_ajax_vmo_update_contact_interaction', array($this, 'ajax_update_contact_interaction'));
        add_action('wp_ajax_vmo_get_contact_insights', array($this, 'ajax_get_contact_insights'));
        
        // Voice conversation handlers
        add_action('wp_ajax_vmo_start_voice_conversation', array($this, 'ajax_start_voice_conversation'));
        add_action('wp_ajax_vmo_send_voice_message', array($this, 'ajax_send_voice_message'));
        add_action('wp_ajax_vmo_end_voice_conversation', array($this, 'ajax_end_voice_conversation'));
        add_action('wp_ajax_vmo_transcribe_voice_to_text', array($this, 'ajax_transcribe_voice_to_text'));
        
        // Intelligent consultation endpoints
        add_action('wp_ajax_vmo_extract_consultation_info', array($this, 'ajax_extract_consultation_info'));
        add_action('wp_ajax_nopriv_vmo_extract_consultation_info', array($this, 'ajax_extract_consultation_info'));

        // Missing AJAX registrations for existing methods
        add_action('wp_ajax_vmo_check_existing_business', array($this, 'ajax_check_existing_business'));
        add_action('wp_ajax_vmo_generate_business_summary', array($this, 'ajax_generate_business_summary'));
        add_action('wp_ajax_vmo_save_consultation', array($this, 'ajax_update_consultation_context'));
        add_action('wp_ajax_vmo_handle_message', array($this, 'ajax_handle_quick_message'));
        add_action('wp_ajax_vmo_populate_deliverables', array($this, 'ajax_populate_deliverables'));
        add_action('wp_ajax_vmo_generate_launch_tour_audio', array($this, 'ajax_generate_launch_tour_audio'));
    }

    private function prepare_long_running_runtime() {
        if (function_exists('set_time_limit')) {
            @set_time_limit(0);
        }
        if (function_exists('ignore_user_abort')) {
            @ignore_user_abort(true);
        }
        if (function_exists('ini_set')) {
            @ini_set('memory_limit', '512M');
        }
    }

    /**
     * Build the canonical consultation blueprint that every session should start with.
     * This keeps frontend + backend schemas aligned and prevents missing-key notices.
     */
    private function build_consultation_blueprint() {
        $phases = $this->get_consultation_phases();
        return array(
            'session_id' => '',
            'scenario' => 'scenario_a',
            'consultation_flow' => array(
                'phases' => $phases,
                'current_phase_index' => 0,
                'status' => 'in_progress',
                'timestamps' => array(
                    'created_at' => current_time('mysql')
                )
            ),
            'business_profile' => array(
                'identity' => array(
                    'business_name' => null,
                    'tagline' => null,
                    'legal_structure' => null,
                    'hq_location' => null,
                    'languages' => array('en')
                ),
                'story' => array(
                    'origin' => null,
                    'founder_background' => null,
                    'why_now' => null
                ),
                'vision' => array(
                    'mission' => null,
                    'vision' => null,
                    'core_values' => array()
                ),
                'targets' => array(
                    'short_term' => null,
                    'mid_term' => null,
                    'long_term' => null,
                    'north_star_metric' => null
                ),
                'audience' => array(
                    'primary_segments' => array(),
                    'pain_points' => array(),
                    'motivations' => array(),
                    'buying_power' => null,
                    'decision_drivers' => array(),
                    'preferred_channels' => array(),
                    'geographies' => array()
                ),
                'offers' => array(),
                'operations' => array(
                    'fulfillment_model' => null,
                    'team_structure' => array(),
                    'partners' => array(),
                    'inventory_notes' => null,
                    'support_hours' => null
                ),
                'web_requirements' => array(
                    'ecommerce' => false,
                    'booking' => false,
                    'membership' => false,
                    'digital_downloads' => false,
                    'donations' => false,
                    'gated_content' => false,
                    'integrations' => array(),
                    'reference_sites' => array(),
                    'tone_keywords' => array(),
                    'color_preferences' => array(),
                    'accessibility_needs' => array()
                ),
                'assets' => array(
                    'logos' => array(),
                    'brand_kit' => false,
                    'product_images' => array(),
                    'videos' => array(),
                    'documents' => array()
                ),
                'compliance' => array(
                    'industry_regulations' => array(),
                    'privacy_requirements' => array(),
                    'payment_rules' => array(),
                    'nda_required' => false
                )
            ),
            'offers_profile' => array(),
            'payments_profile' => array(
                'accepts_cards' => true,
                'supports_donations' => false,
                'supported_currencies' => array('USD'),
                'payout_preferences' => null
            ),
            'discovery_profile' => array(
                'mode' => null,
                'status' => null,
                'needs_business_selection' => false,
                'entry_reason' => null,
                'interests' => array(),
                'skills' => array(),
                'work_background' => null,
                'personality_work_style' => null,
                'lifestyle_constraints' => array(),
                'location' => null,
                'time_available' => null,
                'startup_budget' => null,
                'income_goal' => null,
                'risk_tolerance' => null,
                'preferred_business_model' => null,
                'selected_business_idea' => null,
                'selected_reason' => null,
                'updated_at' => null
            ),
            'business_opportunity_shortlist' => array(),
            'document_store' => array(
                'blueprint_raw' => '',
                'blueprint_summary' => '',
                'uploads' => array(),
                'document_qa_blocks' => array(),
                'archived_upload_summaries' => array()
            ),
            'email_roles' => array(
                'general' => null,
                'support' => null,
                'marketing' => null,
                'transactions' => null,
                'customercare' => null,
                'bizdev' => null,
                'newsletter' => null
            ),
            'email_roles_meta' => array(
                'locked_domain' => $this->get_enforced_domain(),
                'warnings' => array(),
                'roles_confirmed' => false,
                'roles_confirmed_at' => null,
                'phase6_skip_approved' => false,
                'provisioning_mode' => 'collect',
                'provisioning_status' => 'not_requested',
                'email_accounts' => array(),
                'bulk_email_provider' => null,
                'bulk_email_verified' => false,
                'alias_preferences' => array(),
                'dns_host_type' => null,
                'hosted_zone_id' => null,
                'manual_dns_confirmed_at' => null,
                'manual_dns_notes' => '',
                'existing_mailbox_validations' => array(),
                'dns_nameservers' => array(),
                'fallback_validation_status' => null,
                    'last_dns_detection_at' => null,
                    'automation_readiness' => 'unknown',
                    'automation_reason' => ''
            ),
            'research_profile' => array(),
            'recommendations_profile' => array(),
            'website_plan' => array(),
            'systems_plan' => array(),
            'setup_readiness' => array(),
            'roadmap_json' => array(),
            'daily_tasks_json' => array(),
            'document_manifest' => array(),
            'approvals' => array(
                'phase1_profile_confirmed' => false,
                'recommendations' => array(
                    'accepted' => array(),
                    'rejected' => array()
                ),
                'artifacts_generated' => false
            )
        );
    }

    /**
     * Phases metadata used across frontend + backend. Each entry contains the "CEO" duties.
     */
    private function get_consultation_phases() {
        $phase = function ($key, $name, $objectives, $done) {
            return array(
                'key' => $key,
                'name' => $name,
                'objectives' => $objectives,
                'done_criteria' => $done,
                'status' => $key === 'phase_1_foundations' ? 'in_progress' : 'pending'
            );
        };
        return array(
            $phase('phase_1_foundations', 'Phase 1 · Business Foundations Intake', array(
                'Discover the user’s business dream, venture intent, audience, offer, and success vision in novice-friendly language',
                'Extract answers from uploaded documents and media',
                'Store information inside the structured business_profile object'
            ), array(
                'Business name or naming direction captured',
                'Industry/type captured',
                'Core offer, business idea, audience/problem, and success vision captured'
            )),
            $phase('phase_2_blueprint', 'Phase 2 · Executive Blueprint Confirmation', array(
                'Generate professional blueprint summary (vision, mission, goals, positioning)',
                'Ensure user confirms or amends before proceeding'
            ), array(
                'approvals.phase1_profile_confirmed === true'
            )),
            $phase('phase_3_research', 'Phase 3 · Deep Industry & Market Research', array(
                'Run automated research scan with progress indicator',
                'Populate research_profile with industry, market, competitor, SEO insights'
            ), array(
                'research_profile.industry exists',
                'research_profile.competitors has >=4 entries'
            )),
            $phase('phase_4_recommendations', 'Phase 4 · Strategic Recommendations & Decisions', array(
                'Convert research insights into actionable levers',
                'Provide checkbox list so user can approve/veto each recommendation'
            ), array(
                'recommendations_profile.items exists',
                'approvals.recommendations.accepted contains >=1 items'
            )),
            $phase('phase_5_website', 'Phase 5 · Website & Experience Architecture', array(
                'Design site map, page strategy, copy tone, and UX differentiators',
                'Tie every section back to research & stakeholder goals',
                'If commerce is needed, identify the exact product/service catalog pages that must be fed by confirmed catalog details'
            ), array(
                'website_plan.pages has entries'
            )),
            $phase('phase_6_systems', 'Phase 6 · Systems, Automations & Compliance', array(
                'Collect or confirm product/service catalog details before checkout, payments, or product pages are treated as ready',
                'Define CRM/phone/email/analytics stack',
                'List every legal/compliance document required and where it lives'
            ), array(
                'product_catalog_status is complete, not_required, or explicitly deferred by owner',
                'systems_plan.automation has entries',
                'document_manifest has >=3 documents'
            )),
            $phase('phase_7_roadmap', 'Phase 7 · Roadmap & Daily Execution Model', array(
                'Translate goals into milestone roadmap and daily task plans',
                'Estimate timeframe to achieve the North Star metric'
            ), array(
                'roadmap_json.phases exists',
                'daily_tasks_json.sequence exists'
            )),
            $phase('phase_8_launch', 'Phase 8 · Launch Readiness & Deliverables', array(
                'Summarize everything, confirm approvals, trigger generation of website + docs',
                'Hand off deliverables to dashboard/download center'
            ), array(
                'approvals.artifacts_generated === true'
            ))
        );
    }

    /**
     * Merge stored consultation data with the canonical blueprint so we never miss keys.
     */
    private function hydrate_consultation_state($session_id, $row = null) {
        if (!$row && $session_id) {
            $row = $this->consultation_db->get_consultation_by_session($session_id);
        }
        $base = $this->consultation_blueprint;
        if ($row && !empty($row->consultation_data)) {
            $stored = json_decode($row->consultation_data, true);
            if (is_array($stored)) {
                $base = $this->merge_profile_delta($base, $stored);
            }
        }
        if (!isset($base['document_store']) || !is_array($base['document_store'])) {
            $base['document_store'] = $this->consultation_blueprint['document_store'];
        } else {
            $base['document_store'] = $this->merge_profile_delta(
                $this->consultation_blueprint['document_store'],
                $base['document_store']
            );
        }
        // Back-compat: migrate legacy blueprint keys into the structured document_store bucket
        if (!empty($base['blueprintRaw']) && empty($base['document_store']['blueprint_raw'])) {
            $base['document_store']['blueprint_raw'] = $base['blueprintRaw'];
        }
        if (!empty($base['blueprintSummary']) && empty($base['document_store']['blueprint_summary'])) {
            $base['document_store']['blueprint_summary'] = $base['blueprintSummary'];
        }
        if (!empty($session_id)) {
            $base['session_id'] = $session_id;
        }
        return array($row, $base);
    }

    /**
     * Persist consultation blueprint + any extra columns back to database.
     */
    private function persist_consultation_state($session_id, array $cd, array $overrides = array()) {
        if (empty($session_id)) {
            return false;
        }
        $user_id = get_current_user_id();
        $business_profile = isset($cd['business_profile']) && is_array($cd['business_profile'])
            ? $cd['business_profile']
            : array();
        $identity = isset($business_profile['identity']) && is_array($business_profile['identity'])
            ? $business_profile['identity']
            : array();
        $consultation_flow = isset($cd['consultation_flow']) && is_array($cd['consultation_flow'])
            ? $cd['consultation_flow']
            : array();

        $payload = array_merge(
            array(
                'consultation_data' => $cd,
                'business_name' => isset($identity['business_name']) ? (string)$identity['business_name'] : '',
                'business_type' => isset($identity['legal_structure']) ? (string)$identity['legal_structure'] : '',
                'status' => isset($consultation_flow['status']) ? (string)$consultation_flow['status'] : 'in_progress'
            ),
            $overrides
        );

        $saved = $this->consultation_db->save_consultation($user_id, $session_id, $payload);

        if ($saved && $user_id > 0 && function_exists('vmo_get_agent_orchestrator')) {
            try {
                vmo_get_agent_orchestrator()->sync_consultation_state($user_id, $session_id, $cd);
            } catch (\Throwable $e) {
                error_log('VMO Orchestrator Sync Warning: ' . $e->getMessage());
            }
        }

        return $saved;
    }

    private function detect_discovery_scenario_type($message) {
        $text = strtolower(trim((string) $message));
        if ($text === '') {
            return null;
        }

        if (preg_match('/\b(just\s+(tell|show)\s+me\s+what\s+to\s+do|pick\s+(a\s+)?business\s+for\s+me|choose\s+(a\s+)?business\s+for\s+me|do\s+it\s+all\s+for\s+me|build\s+everything\s+for\s+me|turnkey|done\s+for\s+me)\b/i', $text)) {
            return 'scenario_d_turnkey';
        }
        if (preg_match('/\b(make\s+(the\s+)?most\s+money|most\s+profitable|best\s+money[-\s]?making|highest\s+(earning|income|profit)|replace\s+my\s+salary|get\s+rich|make\s+me\s+rich)\b/i', $text)
            || preg_match('/(\$\s?\d+[\d,]*(\s*(a|per)?\s*(month|year|week))?|make\s+\$?\d+[\d,]*)/i', $text)) {
            return 'scenario_d_income_goal';
        }
        if (preg_match('/\b(don\'?t\s+know\s+what\s+business|no\s+idea\s+what\s+business|what\s+business\s+should\s+i\s+start|what\s+business\s+would\s+be\s+good\s+for\s+me|help\s+me\s+(figure\s+out|choose|pick|brainstorm)|recommend\s+(a\s+)?business|business\s+ideas?|not\s+sure\s+what\s+i\s+should\s+do|start\s+a\s+business\s+but\s+(have\s+)?no\s+idea|right\s+business\s+for\s+me)\b/i', $text)) {
            return 'scenario_d_discovery';
        }
        return null;
    }

    private function apply_discovery_scenario_intent(array $cd, $message) {
        $scenario_type = $this->detect_discovery_scenario_type($message);
        if (!$scenario_type) {
            return $cd;
        }

        $mode = 'discovery';
        if ($scenario_type === 'scenario_d_income_goal') {
            $mode = 'income_goal';
        } elseif ($scenario_type === 'scenario_d_turnkey') {
            $mode = 'turnkey';
        }

        if (!isset($cd['discovery_profile']) || !is_array($cd['discovery_profile'])) {
            $cd['discovery_profile'] = $this->consultation_blueprint['discovery_profile'];
        }
        $cd['scenario_type'] = $scenario_type;
        $cd['business_stage'] = 'business_discovery';
        $cd['discovery_profile']['mode'] = $mode;
        $cd['discovery_profile']['status'] = !empty($cd['discovery_profile']['status']) ? $cd['discovery_profile']['status'] : 'collecting_profile';
        $cd['discovery_profile']['needs_business_selection'] = empty($cd['discovery_profile']['selected_business_idea']);
        if (empty($cd['discovery_profile']['entry_reason'])) {
            $cd['discovery_profile']['entry_reason'] = sanitize_text_field(mb_substr((string) $message, 0, 300));
        }
        $cd['discovery_profile']['updated_at'] = current_time('mysql');
        if (!isset($cd['business_opportunity_shortlist']) || !is_array($cd['business_opportunity_shortlist'])) {
            $cd['business_opportunity_shortlist'] = array();
        }

        return $cd;
    }

    private function is_discovery_scenario(array $cd) {
        $scenario = strtolower((string) ($cd['scenario_type'] ?? ''));
        $stage = strtolower((string) ($cd['business_stage'] ?? ''));
        return strpos($scenario, 'scenario_d_') === 0 || $stage === 'business_discovery';
    }

    private function get_selected_discovery_business(array $cd) {
        $discovery = isset($cd['discovery_profile']) && is_array($cd['discovery_profile']) ? $cd['discovery_profile'] : array();
        $selected = $discovery['selected_business_idea'] ?? null;
        if (is_array($selected) && $this->has_meaningful_consultation_value($selected)) {
            return $selected;
        }
        if (is_string($selected) && $this->is_meaningful_consultation_value($selected)) {
            return array('name' => sanitize_text_field($selected));
        }
        $shortlist = isset($cd['business_opportunity_shortlist']) && is_array($cd['business_opportunity_shortlist']) ? $cd['business_opportunity_shortlist'] : array();
        foreach ($shortlist as $idea) {
            if (is_array($idea) && !empty($idea['selected']) && $this->has_meaningful_consultation_value($idea)) {
                return $idea;
            }
        }
        return array();
    }

    private function merge_business_opportunity_shortlist(array $existing, array $incoming) {
        $merged = array();
        foreach (array_merge($existing, $incoming) as $index => $idea) {
            if (!is_array($idea)) {
                continue;
            }
            $key = strtolower(trim((string) ($idea['id'] ?? $idea['name'] ?? $idea['title'] ?? $idea['business_name'] ?? ('idea_' . $index))));
            if ($key === '') {
                $key = 'idea_' . $index;
            }
            $prior = isset($merged[$key]) && is_array($merged[$key]) ? $merged[$key] : array();
            $merged[$key] = $this->merge_profile_delta($prior, $idea);
        }
        return array_values(array_slice($merged, 0, 8));
    }

    private function filter_empty_discovery_delta($value) {
        if (!is_array($value)) {
            if ($value === null) {
                return null;
            }
            if (is_string($value) && trim($value) === '') {
                return null;
            }
            return $value;
        }

        $filtered = array();
        foreach ($value as $key => $item) {
            $clean = $this->filter_empty_discovery_delta($item);
            if ($clean === null) {
                continue;
            }
            if (is_array($clean) && empty($clean)) {
                continue;
            }
            $filtered[$key] = $clean;
        }
        return $filtered;
    }

    private function normalize_discovery_context_delta(array $incoming) {
        if (isset($incoming['discovery_profile']) && is_array($incoming['discovery_profile'])) {
            $incoming['discovery_profile'] = $this->filter_empty_discovery_delta($incoming['discovery_profile']);
            if (empty($incoming['discovery_profile'])) {
                unset($incoming['discovery_profile']);
            }
        }
        if (isset($incoming['business_opportunity_shortlist']) && is_array($incoming['business_opportunity_shortlist']) && empty($incoming['business_opportunity_shortlist'])) {
            unset($incoming['business_opportunity_shortlist']);
        }
        if (isset($incoming['extracted_information']['discovery_profile']) && is_array($incoming['extracted_information']['discovery_profile'])) {
            $incoming['extracted_information']['discovery_profile'] = $this->filter_empty_discovery_delta($incoming['extracted_information']['discovery_profile']);
            if (empty($incoming['extracted_information']['discovery_profile'])) {
                unset($incoming['extracted_information']['discovery_profile']);
            }
        }
        if (isset($incoming['extracted_information']['business_opportunity_shortlist']) && is_array($incoming['extracted_information']['business_opportunity_shortlist']) && empty($incoming['extracted_information']['business_opportunity_shortlist'])) {
            unset($incoming['extracted_information']['business_opportunity_shortlist']);
        }
        return $incoming;
    }

    /**
     * Resolve a WordPress uploads URL to an on-disk path (only if URL belongs to this site uploads directory)
     */
    private function resolve_upload_path_from_url($url) {
        $url = esc_url_raw((string)$url);
        if ($url === '') {
            return '';
        }
        $uploads = wp_get_upload_dir();
        $baseurl = rtrim($uploads['baseurl'] ?? '', '/');
        $basedir = rtrim($uploads['basedir'] ?? '', '/');
        if (!$baseurl || !$basedir || strpos($url, $baseurl) !== 0) {
            return '';
        }
        $relative = ltrim(substr($url, strlen($baseurl)), '/');
        $path = $basedir . '/' . $relative;
        if (!file_exists($path) || !is_readable($path)) {
            return '';
        }
        return $path;
    }

    /**
     * Build local text chunks for storage/UI (bounded to keep DB reasonable)
     */
    private function build_local_text_chunks($text, $maxChunks = 40, $maxChunk = 5000) {
        $chunks = [];
        $remaining = (string)$text;
        while ($remaining !== '' && count($chunks) < $maxChunks) {
            if (strlen($remaining) <= $maxChunk) {
                $chunks[] = $remaining;
                break;
            }
            $slice = substr($remaining, 0, $maxChunk);
            // ✅ FIX: Avoid max(false, false) which can trigger deprecation notices in PHP 8.1+
            $bp1 = strrpos($slice, "\n\n");
            $bp2 = strrpos($slice, '. ');
            if ($bp1 === false && $bp2 === false) {
                $breakPos = $maxChunk;
            } elseif ($bp1 === false) {
                $breakPos = $bp2;
            } elseif ($bp2 === false) {
                $breakPos = $bp1;
            } else {
                $breakPos = max($bp1, $bp2);
            }
            if ($breakPos <= 0) { $breakPos = $maxChunk; }
            $chunks[] = substr($remaining, 0, $breakPos);
            $remaining = ltrim(substr($remaining, $breakPos));
        }
        return $chunks;
    }

    /**
     * Upsert a document upload record into consultation_data.document_store.uploads
     */
    private function upsert_document_store_upload(array &$cd, array $record) {
        if (!isset($cd['document_store']) || !is_array($cd['document_store'])) {
            $cd['document_store'] = $this->consultation_blueprint['document_store'];
        }
        if (!isset($cd['document_store']['uploads']) || !is_array($cd['document_store']['uploads'])) {
            $cd['document_store']['uploads'] = [];
        }

        $id = (string)($record['id'] ?? '');
        $url = (string)($record['url'] ?? '');
        $title = (string)($record['title'] ?? 'Uploaded Document');

        // Remove prior records with same id or same url+title (avoid duplicates)
        $cd['document_store']['uploads'] = array_values(array_filter(
            $cd['document_store']['uploads'],
            function ($u) use ($id, $url, $title) {
                if (!is_array($u)) return false;
                if ($id !== '' && (string)($u['id'] ?? '') === $id) return false;
                if ($url !== '' && (string)($u['url'] ?? '') === $url && (string)($u['title'] ?? '') === $title) return false;
                return true;
            }
        ));

        $cd['document_store']['uploads'][] = $record;

        // Keep uploads bounded — but ARCHIVE summaries of evicted documents so the AI never fully forgets them.
        // This is critical for long-term memory: even if we can't keep the full text of all 50+ documents,
        // the AI retains awareness of what was uploaded and key extracted information.
        $max_active_uploads = 50;
        if (count($cd['document_store']['uploads']) > $max_active_uploads) {
            if (!isset($cd['document_store']['archived_upload_summaries']) || !is_array($cd['document_store']['archived_upload_summaries'])) {
                $cd['document_store']['archived_upload_summaries'] = [];
            }
            $evicted = array_slice($cd['document_store']['uploads'], 0, count($cd['document_store']['uploads']) - $max_active_uploads);
            foreach ($evicted as $old_upload) {
                if (!is_array($old_upload)) continue;
                $summary_text = '';
                if (!empty($old_upload['chunks']) && is_array($old_upload['chunks'])) {
                    foreach ($old_upload['chunks'] as $ch) {
                        if (is_array($ch) && isset($ch['text'])) {
                            $summary_text .= (string)$ch['text'] . ' ';
                        }
                    }
                }
                $cd['document_store']['archived_upload_summaries'][] = [
                    'title' => (string)($old_upload['title'] ?? 'Archived Document'),
                    'url' => (string)($old_upload['url'] ?? ''),
                    'archived_at' => current_time('mysql'),
                    'original_length' => (int)($old_upload['length'] ?? strlen($summary_text)),
                    'summary_excerpt' => mb_substr(trim($summary_text), 0, 2000)
                ];
            }
            // Keep only the most recent archived summaries (max 100)
            if (count($cd['document_store']['archived_upload_summaries']) > 100) {
                $cd['document_store']['archived_upload_summaries'] = array_slice($cd['document_store']['archived_upload_summaries'], -100);
            }
            $cd['document_store']['uploads'] = array_slice($cd['document_store']['uploads'], -$max_active_uploads);
        }
    }

    /**
     * Ensure blueprint_raw is present by rehydrating from stored uploads or recent media_files.
     * Returns true if the consultation data was modified.
     */
    private function ensure_blueprint_raw_present(array &$cd) {
        if (!isset($cd['document_store']) || !is_array($cd['document_store'])) {
            $cd['document_store'] = $this->consultation_blueprint['document_store'];
        }
        $doc_store = $cd['document_store'];
        $existing = (string)($doc_store['blueprint_raw'] ?? '');
        if ($existing !== '') {
            return false;
        }

        // Back-compat: if legacy key exists, migrate
        if (!empty($cd['blueprintRaw'])) {
            $cd['document_store']['blueprint_raw'] = (string)$cd['blueprintRaw'];
            return true;
        }

        // 1) Rebuild from stored document_store uploads
        $uploads = isset($doc_store['uploads']) && is_array($doc_store['uploads']) ? $doc_store['uploads'] : [];
        if (!empty($uploads)) {
            $last = end($uploads);
            if (is_array($last) && !empty($last['chunks']) && is_array($last['chunks'])) {
                $joined = '';
                foreach ($last['chunks'] as $ch) {
                    if (is_array($ch) && isset($ch['text'])) {
                        $joined .= (string)$ch['text'] . "\n";
                    }
                }
                $joined = trim($joined);
                if ($joined !== '') {
                    $cd['blueprintRaw'] = $joined;
                    $cd['document_store']['blueprint_raw'] = $joined;
                    return true;
                }
            }
        }

        // 2) Best-effort: extract from most recent document-like media_files
        $media_files = isset($cd['media_files']) && is_array($cd['media_files']) ? $cd['media_files'] : [];
        for ($i = count($media_files) - 1; $i >= 0; $i--) {
            $m = $media_files[$i];
            if (!is_array($m)) continue;
            $url = (string)($m['url'] ?? '');
            $title = (string)($m['title'] ?? 'Uploaded file');
            $mime_hint = (string)($m['mime'] ?? '');
            if ($url === '') continue;

            $path = $this->resolve_upload_path_from_url($url);
            if ($path === '') continue;

            $wpft = wp_check_filetype($path);
            $mime = (string)($wpft['type'] ?? $mime_hint);
            $content = '';

            if (preg_match('/\.(docx)$/i', $path) || stripos($mime, 'officedocument.wordprocessingml.document') !== false) {
                $content = $this->extract_text_from_docx($path);
            } elseif (strpos($mime, 'text/') === 0 || preg_match('/\.(txt|md|csv|json)$/i', $path)) {
                $content = (string)@vmo_get_filesystem()->get_contents($path);
            } elseif (stripos($mime, 'application/pdf') !== false || preg_match('/\.(pdf)$/i', $path)) {
                $content = $this->extract_text_from_pdf_naive($path);
                if (trim($content) === '') {
                    $content = $this->extract_text_from_pdf_with_ocr($path);
                }
            } else {
                continue;
            }

            $content = trim((string)$content);
            if ($content === '') {
                continue;
            }

            // ✅ GPT-5.5 UPGRADE: Raised from 18K to 400K to support 100+ page documents
            // GPT-5.5 has a massive context window — no need to discard document content
            $max_chars = 400000;
            if (strlen($content) > $max_chars) {
                $content = substr($content, 0, $max_chars) . "\n\n[Document truncated at 400K chars — remaining content omitted]";
            }

            $doc_id = 'doc_' . wp_generate_password(10, false);
            $local_chunks = $this->build_local_text_chunks($content);

            $cd['blueprintRaw'] = $content;
            $cd['document_store']['blueprint_raw'] = $content;

            $this->upsert_document_store_upload($cd, [
                'id' => $doc_id,
                'title' => $title,
                'url' => $url,
                'mime' => $mime,
                'length' => strlen($content),
                'saved_at' => current_time('mysql'),
                'chunks' => array_map(function ($t, $idx) {
                    return [ 'id' => 'c' . ($idx + 1), 'text' => $t ];
                }, $local_chunks, array_keys($local_chunks))
            ]);

            return true;
        }

        return false;
    }

    /**
     * AJAX: Hydrate the consultation state so the frontend can resume where it left off.
     */
    public function ajax_get_consultation_context() {
        try {
            if (!function_exists('wp_verify_nonce')) {
                require_once ABSPATH . WPINC . '/pluggable.php';
            }

            // Accept builder/admin/ajax nonce variants; always return JSON on failure.
            $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
            $admin_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
            $ajax_nonce = isset($_POST['_ajax_nonce']) ? sanitize_text_field(wp_unslash($_POST['_ajax_nonce'])) : '';

            $builder_ok = $builder_nonce && wp_verify_nonce($builder_nonce, 'vmo_builder_nonce');
            $admin_ok = $admin_nonce && wp_verify_nonce($admin_nonce, 'vmo_admin_nonce');
            $ajax_ok = ($admin_nonce && wp_verify_nonce($admin_nonce, 'vmo_ajax_nonce'))
                || ($ajax_nonce && wp_verify_nonce($ajax_nonce, 'vmo_ajax_nonce'));

            if (!($builder_ok || $admin_ok || $ajax_ok)) {
                wp_send_json_error(array('message' => 'Security check failed', 'status' => 'invalid_nonce'), 403);
            }

            $session_id = isset($_POST['session_id']) ? sanitize_text_field(wp_unslash($_POST['session_id'])) : '';
            $force_new_session = !empty($_POST['force_new_session']);
            $needs_persist = false;

            if (empty($session_id) || $force_new_session) {
                $session_id = $this->generate_session_id();
                $needs_persist = true;
            }

            list($row, $context) = $this->hydrate_consultation_state($session_id);
            if (!$row) {
                // ✅ CRITICAL FIX: If no row found for session_id but user is logged in,
                // attempt to recover their latest consultation by user_id.
                // This prevents data loss when localStorage is cleared, user switches browsers,
                // or uses a different device — the original consultation is still recoverable.
                $user_id = get_current_user_id();
                if ($user_id && $this->consultation_db && is_callable(array($this->consultation_db, 'get_latest_consultation_by_user'))) {
                    $fallback_row = $this->consultation_db->get_latest_consultation_by_user($user_id);
                    if ($fallback_row && !empty($fallback_row->session_id)) {
                        $session_id = $fallback_row->session_id;
                        list($row, $context) = $this->hydrate_consultation_state($session_id);
                        if ($row) {
                            error_log('VMO: Recovered consultation via user_id fallback. session_id=' . $session_id);
                        }
                    }
                }
                if (!$row) {
                    $needs_persist = true;
                }
            }

            // If a document exists but blueprint text isn't persisted yet, attempt to hydrate.
            if ($this->ensure_blueprint_raw_present($context)) {
                $needs_persist = true;
            }

            if ($needs_persist && $this->consultation_db && is_callable(array($this->consultation_db, 'save_consultation'))) {
                $this->persist_consultation_state($session_id, $context);
            }

            // ✅ CRITICAL FIX: Load conversation_log from DB so frontend can restore history on page refresh
            $conversation_log_for_client = array();
            if ($row && !empty($row->conversation_log)) {
                $raw_log = json_decode($row->conversation_log, true);
                if (is_array($raw_log)) {
                    // Convert DB format {type,content} to OpenAI format {role,content}
                    // ✅ MEMORY FIX: Increased from 60→200 messages and 10K→50K per message
                    // Long consultations (8 phases) can have 200+ exchanges. Restoring too few
                    // causes the AI to lose context of earlier phases including the original vision.
                    $log_slice = array_slice($raw_log, -200);
                    foreach ($log_slice as $entry) {
                        $role = ($entry['type'] === 'user') ? 'user' : 'assistant';
                        $content = isset($entry['content']) ? (string)$entry['content'] : '';
                        if ($content !== '') {
                            $conversation_log_for_client[] = array(
                                'role' => $role,
                                'content' => mb_substr($content, 0, 50000) // GPT-5.5 has massive context — don't truncate key content
                            );
                        }
                    }
                }
            }

            wp_send_json_success(array(
                'session_id' => $session_id,
                'data' => $context,
                'phase_flags' => $context['phase_flags'] ?? array(),
                'consultation_flow' => $context['consultation_flow'] ?? array(),
                'conversation_log' => $conversation_log_for_client
            ));
        } catch (\Throwable $e) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log('VMO: Consultation context bootstrap failed: ' . $e->getMessage());
            }
            wp_send_json_error(array(
                'message' => 'Unable to hydrate consultation state: ' . $e->getMessage()
            ), 500);
        }
    }

    /**
     * Deep merge helper that treats associative arrays recursively and replaces lists.
     */
    private function merge_profile_delta(array $base, array $delta) {
        foreach ($delta as $key => $value) {
            if (is_array($value)) {
                $is_assoc = $this->is_associative_array($value);
                if ($is_assoc) {
                    $base_value = isset($base[$key]) && is_array($base[$key]) ? $base[$key] : array();
                    $base[$key] = $this->merge_profile_delta($base_value, $value);
                } else {
                    // Numeric arrays replace to avoid duplicate stacking
                    $base[$key] = $value;
                }
            } else {
                if (is_null($value)) {
                    unset($base[$key]);
                } else {
                    $base[$key] = $value;
                }
            }
        }
        return $base;
    }

    /**
     * Keep extracted_information and business_profile synchronized so phase gates
     * and prompts read the same facts.
     */
    private function reconcile_intake_memory(array $cd, $latest_message = '') {
        $ei = isset($cd['extracted_information']) && is_array($cd['extracted_information']) ? $cd['extracted_information'] : array();
        $bp = isset($cd['business_profile']) && is_array($cd['business_profile']) ? $cd['business_profile'] : array();

        $aliases = array(
            array('businessName', 'business_name'),
            array('businessIdea', 'business_idea'),
            array('targetAudience', 'target_audience'),
            array('offeringType', 'offering_type'),
            array('businessGoals', 'business_goals'),
            array('websitePreferences', 'website_preferences'),
            array('brandingInfo', 'branding_info'),
            array('successDefinition', 'success_definition'),
            array('scaleIntent', 'scale_intent'),
            array('automationPreference', 'automation_preference')
        );

        foreach ($aliases as $pair) {
            list($camel, $snake) = $pair;
            if (!empty($ei[$snake]) && empty($ei[$camel])) {
                $ei[$camel] = $ei[$snake];
            }
            if (!empty($ei[$camel]) && empty($ei[$snake])) {
                $ei[$snake] = $ei[$camel];
            }
        }

        $selected_discovery_business = $this->get_selected_discovery_business($cd);
        if (!empty($selected_discovery_business)) {
            if (!isset($cd['discovery_profile']) || !is_array($cd['discovery_profile'])) {
                $cd['discovery_profile'] = $this->consultation_blueprint['discovery_profile'];
            }
            $cd['discovery_profile']['status'] = 'business_selected';
            $cd['discovery_profile']['needs_business_selection'] = false;
            $cd['discovery_profile']['selected_business_idea'] = $selected_discovery_business;
            $cd['discovery_profile']['updated_at'] = current_time('mysql');
            $cd['scenario_type'] = 'scenario_a_new';
            $cd['business_stage'] = 'new_business';

            $selected_name = sanitize_text_field((string) ($selected_discovery_business['business_name'] ?? $selected_discovery_business['name'] ?? $selected_discovery_business['title'] ?? ''));
            $selected_summary = sanitize_textarea_field((string) ($selected_discovery_business['summary'] ?? $selected_discovery_business['fit_reason'] ?? $selected_discovery_business['description'] ?? $selected_name));
            $selected_industry = sanitize_text_field((string) ($selected_discovery_business['industry'] ?? $selected_discovery_business['category'] ?? ''));
            $selected_model = sanitize_text_field((string) ($selected_discovery_business['business_model'] ?? $selected_discovery_business['model'] ?? $selected_discovery_business['type'] ?? ''));
            $selected_audience = sanitize_textarea_field((string) ($selected_discovery_business['target_audience'] ?? $selected_discovery_business['audience'] ?? ''));
            $selected_offer = sanitize_text_field((string) ($selected_discovery_business['core_offer'] ?? $selected_discovery_business['offer'] ?? $selected_name));
            $selected_goal = sanitize_textarea_field((string) ($selected_discovery_business['income_potential'] ?? ($cd['discovery_profile']['income_goal'] ?? '')));

            if ($selected_name !== '' && empty($ei['businessIdea'])) {
                $ei['businessIdea'] = $selected_summary !== '' ? $selected_summary : $selected_name;
                $ei['business_idea'] = $ei['businessIdea'];
            }
            if ($selected_industry !== '' && empty($ei['industry'])) {
                $ei['industry'] = $selected_industry;
            }
            if ($selected_model !== '' && empty($ei['offeringType'])) {
                $ei['offeringType'] = $selected_model;
                $ei['offering_type'] = $selected_model;
            }
            if ($selected_audience !== '' && empty($ei['targetAudience'])) {
                $ei['targetAudience'] = $selected_audience;
                $ei['target_audience'] = $selected_audience;
            }
            if ($selected_offer !== '' && empty($ei['offerings'])) {
                $ei['offerings'] = array($selected_offer);
            }
            if ($selected_goal !== '' && empty($ei['businessGoals'])) {
                $ei['businessGoals'] = $selected_goal;
                $ei['business_goals'] = $selected_goal;
            }
        }

        $message = strtolower(trim((string) $latest_message));
        if ($message !== '') {
            if (empty($ei['successDefinition']) && preg_match('/\b(active\s+paying|paying\s+users?|users?|arr|mrr|revenue|north\s*star)\b/i', $latest_message)) {
                $ei['successDefinition'] = trim((string) $latest_message);
                $ei['success_definition'] = $ei['successDefinition'];
            }
            if (empty($ei['scaleIntent']) && preg_match('/\b(lean|small\s+team|up\s*to\s*\d+\s*(employees?|people)|scale\s+to\s+\d+)\b/i', $latest_message)) {
                $ei['scaleIntent'] = trim((string) $latest_message);
                $ei['scale_intent'] = $ei['scaleIntent'];
            }
            if (empty($ei['automationPreference']) && preg_match('/\b(automated?|automation|manual|high[-\s]?touch|self[-\s]?serve)\b/i', $latest_message)) {
                $ei['automationPreference'] = trim((string) $latest_message);
                $ei['automation_preference'] = $ei['automationPreference'];
            }
            if (empty($ei['websiteStatus'])) {
                if (preg_match('/\b(from\s+scratch|new\s+site|fresh\s+build|build\s+from\s+scratch|completely\s+new)\b/i', $latest_message)) {
                    $ei['websiteStatus'] = 'new_from_scratch';
                } elseif (preg_match('/\b(redesign|rebuild|revamp|improve\s+existing|existing\s+site)\b/i', $latest_message)) {
                    $ei['websiteStatus'] = 'rebuild_existing';
                }
            }

            if (empty($ei['primaryWebsiteUrl'])) {
                $detected_urls = $this->extract_urls_from_text($latest_message);
                if (!empty($detected_urls)) {
                    $ei['primaryWebsiteUrl'] = (string) $detected_urls[0];
                }
            }
        }

        if (!isset($bp['identity']) || !is_array($bp['identity'])) {
            $bp['identity'] = array();
        }
        if (!empty($selected_discovery_business)) {
            $selected_name = sanitize_text_field((string) ($selected_discovery_business['business_name'] ?? $selected_discovery_business['name'] ?? $selected_discovery_business['title'] ?? ''));
            $selected_industry = sanitize_text_field((string) ($selected_discovery_business['industry'] ?? $selected_discovery_business['category'] ?? ''));
            $selected_model = sanitize_text_field((string) ($selected_discovery_business['business_model'] ?? $selected_discovery_business['model'] ?? $selected_discovery_business['type'] ?? ''));
            $selected_description = sanitize_textarea_field((string) ($selected_discovery_business['summary'] ?? $selected_discovery_business['fit_reason'] ?? $selected_discovery_business['description'] ?? $selected_name));
            if (empty($bp['identity']['naming_status'])) {
                $bp['identity']['naming_status'] = 'needs_name';
            }
            if ($selected_industry !== '' && empty($bp['identity']['industry'])) {
                $bp['identity']['industry'] = $selected_industry;
            }
            if ($selected_model !== '' && empty($bp['identity']['business_type'])) {
                $bp['identity']['business_type'] = $selected_model;
            }
            if ($selected_description !== '' && empty($bp['identity']['business_description'])) {
                $bp['identity']['business_description'] = $selected_description;
            }
        }
        if (!isset($bp['audience']) || !is_array($bp['audience'])) {
            $bp['audience'] = array();
        }
        if (!isset($bp['targets']) || !is_array($bp['targets'])) {
            $bp['targets'] = array();
        }
        if (!isset($bp['operations']) || !is_array($bp['operations'])) {
            $bp['operations'] = array();
        }
        if (!isset($bp['offers']) || !is_array($bp['offers'])) {
            $bp['offers'] = array();
        }
        if (!isset($bp['web_requirements']) || !is_array($bp['web_requirements'])) {
            $bp['web_requirements'] = array();
        }
        if (!isset($bp['web_requirements']['reference_sites']) || !is_array($bp['web_requirements']['reference_sites'])) {
            $bp['web_requirements']['reference_sites'] = array();
        }

        if (empty($bp['identity']['business_name']) && !empty($ei['businessName'])) {
            $bp['identity']['business_name'] = sanitize_text_field((string) $ei['businessName']);
        }
        if (empty($bp['identity']['industry']) && !empty($ei['industry'])) {
            $bp['identity']['industry'] = sanitize_text_field((string) $ei['industry']);
        }
        if (empty($bp['audience']['primary_segments']) && !empty($ei['targetAudience'])) {
            $bp['audience']['primary_segments'] = array(sanitize_text_field((string) $ei['targetAudience']));
        }
        if (empty($bp['targets']['long_term']) && !empty($ei['businessGoals'])) {
            $bp['targets']['long_term'] = sanitize_text_field((string) $ei['businessGoals']);
        }
        if (empty($bp['targets']['north_star_metric']) && !empty($ei['successDefinition'])) {
            $bp['targets']['north_star_metric'] = sanitize_text_field((string) $ei['successDefinition']);
        }
        if (empty($bp['operations']['fulfillment_model']) && !empty($ei['automationPreference'])) {
            $bp['operations']['fulfillment_model'] = sanitize_text_field((string) $ei['automationPreference']);
        }
        if (!isset($bp['operations']['team_structure']) || !is_array($bp['operations']['team_structure'])) {
            $bp['operations']['team_structure'] = array();
        }
        if (empty($bp['operations']['team_structure']['scale_intent']) && !empty($ei['scaleIntent'])) {
            $bp['operations']['team_structure']['scale_intent'] = sanitize_text_field((string) $ei['scaleIntent']);
        }

        $offerings = array();
        if (!empty($ei['offerings']) && is_array($ei['offerings'])) {
            $offerings = array_values(array_filter(array_map('sanitize_text_field', $ei['offerings'])));
        }
        if (!empty($offerings) && empty($bp['offers'])) {
            foreach ($offerings as $offer_name) {
                $bp['offers'][] = array('name' => $offer_name);
            }
        }
        if (!empty($offerings) && empty($cd['offers_profile'])) {
            $offering_type = !empty($ei['offeringType']) ? sanitize_text_field((string) $ei['offeringType']) : 'service';
            $cd['offers_profile'] = array_map(function ($offer_name) use ($offering_type) {
                return array(
                    'name' => $offer_name,
                    'type' => $offering_type
                );
            }, $offerings);
        }

        if (!empty($ei['primaryWebsiteUrl'])) {
            $primary_url = esc_url_raw((string) $ei['primaryWebsiteUrl']);
            if ($primary_url !== '') {
                $existing_refs = isset($bp['web_requirements']['reference_sites']) && is_array($bp['web_requirements']['reference_sites'])
                    ? $bp['web_requirements']['reference_sites'] : array();
                if (!in_array($primary_url, $existing_refs, true)) {
                    $existing_refs[] = $primary_url;
                }
                $bp['web_requirements']['reference_sites'] = array_values(array_unique($existing_refs));
            }
        }

        $cd['extracted_information'] = $ei;
        $cd['business_profile'] = $bp;
        return $cd;
    }

    private function build_product_catalog_status(array $cd) {
        $ei = isset($cd['extracted_information']) && is_array($cd['extracted_information']) ? $cd['extracted_information'] : array();
        $bp = isset($cd['business_profile']) && is_array($cd['business_profile']) ? $cd['business_profile'] : array();
        $bp_web = isset($bp['web_requirements']) && is_array($bp['web_requirements']) ? $bp['web_requirements'] : array();

        $extracted_ecommerce = isset($ei['ecommerce']) && is_array($ei['ecommerce']) ? $ei['ecommerce'] : array();
        $consultation_ecommerce = isset($cd['ecommerce']) && is_array($cd['ecommerce']) ? $cd['ecommerce'] : array();
        $ecommerce = array_replace_recursive($consultation_ecommerce, $extracted_ecommerce);
        $payment_collection = isset($ei['payment_collection']) && is_array($ei['payment_collection']) ? $ei['payment_collection'] : array();

        $collection_mode = '';
        foreach (array($payment_collection['mode'] ?? '', $ecommerce['collection_mode'] ?? '') as $candidate_mode) {
            $candidate_mode = strtolower(sanitize_key((string) $candidate_mode));
            if (in_array($candidate_mode, array('products', 'donations', 'services', 'none'), true)) {
                $collection_mode = $candidate_mode;
                break;
            }
        }

        $products = array_merge(
            $this->normalize_catalog_items_for_status($ei['products_inventory'] ?? array()),
            $this->normalize_catalog_items_for_status($cd['products_inventory'] ?? array()),
            $this->normalize_catalog_items_for_status($ecommerce['products'] ?? array())
        );
        $services = array_merge(
            $this->normalize_catalog_items_for_status($ei['services_inventory'] ?? array()),
            $this->normalize_catalog_items_for_status($cd['services_inventory'] ?? array())
        );
        $products = $this->dedupe_catalog_status_items($products);
        $services = $this->dedupe_catalog_status_items($services);
        $items = array_merge($products, $services);

        $offering_type = strtolower(trim((string) ($ei['offeringType'] ?? ($ei['offering_type'] ?? ''))));
        $requires_catalog = !empty($products)
            || !empty($services)
            || $collection_mode === 'products'
            || $collection_mode === 'services'
            || (!empty($payment_collection['collects_payments']) && $collection_mode !== 'donations')
            || (!empty($ei['needsEcommerce']) && $collection_mode !== 'donations')
            || (!empty($ei['ecommerce_needed']) && $collection_mode !== 'donations')
            || (!empty($bp_web['ecommerce']) && $collection_mode !== 'donations')
            || in_array($offering_type, array('product', 'products', 'both'), true)
            || !empty($ecommerce['intends_to_sell'])
            || !empty($ecommerce['sell_on_website']);

        if ($collection_mode === 'donations') {
            $requires_catalog = false;
        }

        $deferred = !empty($ei['product_catalog_deferred'])
            || !empty($ecommerce['catalog_deferred'])
            || !empty($cd['approvals']['product_catalog_deferred']);

        $missing_required = array();
        $missing_media = array();
        $missing_variants = array();
        foreach ($items as $index => $item) {
            $label = $item['name'] !== '' ? $item['name'] : 'Catalog item ' . ($index + 1);
            if ($item['name'] === '') {
                $missing_required[] = $label . ': name';
            }
            if ($item['description'] === '') {
                $missing_required[] = $label . ': description';
            }
            if (!$item['has_price']) {
                $missing_required[] = $label . ': price/currency';
            }
            if (!$item['has_media'] && empty($item['media_status'])) {
                $missing_media[] = $label . ': images/videos or AI-media preference';
            }
            if (!$item['variants_confirmed']) {
                $missing_variants[] = $label . ': variations/options confirmation';
            }
        }

        if ($requires_catalog && empty($items)) {
            $missing_required[] = 'At least one product/service with name, description, price, media preference, and variations/options status';
        }

        $core_complete = !$requires_catalog || $deferred || (!empty($items) && empty($missing_required) && empty($missing_media) && empty($missing_variants));

        return array(
            'required' => (bool) $requires_catalog,
            'deferred' => (bool) $deferred,
            'core_complete' => (bool) $core_complete,
            'complete' => (bool) (!$requires_catalog || $deferred || $core_complete),
            'collection_mode' => $collection_mode ?: ($requires_catalog ? 'products' : 'none'),
            'products_count' => count($products),
            'services_count' => count($services),
            'items_count' => count($items),
            'missing_required' => array_values(array_unique($missing_required)),
            'missing_media' => array_values(array_unique($missing_media)),
            'missing_variants' => array_values(array_unique($missing_variants)),
            'question_required' => (bool) ($requires_catalog && !$deferred && !$core_complete),
        );
    }

    private function normalize_catalog_items_for_status($items) {
        $normalized = array();
        if (!is_array($items)) {
            return $normalized;
        }

        foreach ($items as $item) {
            if (!is_array($item)) {
                $name = sanitize_text_field((string) $item);
                if ($name !== '') {
                    $normalized[] = array(
                        'name' => $name,
                        'description' => '',
                        'has_price' => false,
                        'has_media' => false,
                        'media_status' => '',
                        'variants_confirmed' => false,
                    );
                }
                continue;
            }

            $name = sanitize_text_field((string) ($item['name'] ?? ($item['title'] ?? '')));
            $description = trim(wp_strip_all_tags((string) ($item['description'] ?? ($item['summary'] ?? ''))));
            $price_value = $item['price'] ?? ($item['price_text'] ?? ($item['pricing'] ?? null));
            $has_price = false;
            if (is_numeric($price_value)) {
                $has_price = true;
            } elseif (is_string($price_value)) {
                $has_price = trim($price_value) !== '' && strtolower(trim($price_value)) !== 'not specified';
            }
            $has_price = $has_price || !empty($item['price_pending']) || !empty($item['quote_based']) || !empty($item['deposit_required']);

            $has_media = false;
            foreach (array('main_image', 'image', 'image_url') as $image_key) {
                if (!empty($item[$image_key])) {
                    $has_media = true;
                    break;
                }
            }
            foreach (array('gallery_images', 'images', 'videos', 'video_urls') as $media_key) {
                if (!empty($item[$media_key]) && is_array($item[$media_key])) {
                    $has_media = true;
                    break;
                }
            }

            $variants = is_array($item['variants'] ?? null) ? $item['variants'] : array();
            $variants_confirmed = !empty($variants)
                || isset($item['has_variants'])
                || !empty($item['variants_confirmed'])
                || !empty($item['no_variants'])
                || !empty($item['options_confirmed']);

            $normalized[] = array(
                'name' => $name,
                'description' => $description,
                'has_price' => $has_price,
                'has_media' => $has_media,
                'media_status' => sanitize_text_field((string) ($item['media_status'] ?? ($item['media_preference'] ?? ''))),
                'variants_confirmed' => $variants_confirmed,
            );
        }

        return $normalized;
    }

    private function dedupe_catalog_status_items(array $items) {
        $deduped = array();
        $seen = array();
        foreach ($items as $item) {
            $key = strtolower((string) ($item['name'] ?? ''));
            if ($key === '') {
                $key = md5(wp_json_encode($item));
            }
            if (isset($seen[$key])) {
                continue;
            }
            $seen[$key] = true;
            $deduped[] = $item;
        }
        return $deduped;
    }

    private function user_message_defers_product_catalog($message) {
        $message = strtolower(trim((string) $message));
        if ($message === '') {
            return false;
        }
        return (bool) preg_match('/\b(add|finish|upload|provide|set\s*up|enter)\b.{0,40}\b(product|products|catalog|prices?|photos?|images?|videos?|variations?|variants?)\b.{0,40}\b(later|after|dashboard|not\s+now)\b|\b(skip|defer)\b.{0,40}\b(product|products|catalog|prices?|photos?|images?|videos?|variations?|variants?)\b|\b(no\s+products?\s+yet|i\s+do\s+not\s+have\s+products?\s+ready|i\s+don\'t\s+have\s+products?\s+ready)\b/i', $message);
    }

    private function apply_product_catalog_deferral_intent(array $cd, $message) {
        if (!$this->user_message_defers_product_catalog($message)) {
            return $cd;
        }
        if (!isset($cd['extracted_information']) || !is_array($cd['extracted_information'])) {
            $cd['extracted_information'] = array();
        }
        if (!isset($cd['extracted_information']['ecommerce']) || !is_array($cd['extracted_information']['ecommerce'])) {
            $cd['extracted_information']['ecommerce'] = array();
        }
        if (!isset($cd['approvals']) || !is_array($cd['approvals'])) {
            $cd['approvals'] = array();
        }
        $cd['extracted_information']['product_catalog_deferred'] = true;
        $cd['extracted_information']['ecommerce']['catalog_deferred'] = true;
        $cd['approvals']['product_catalog_deferred'] = true;
        $cd['approvals']['product_catalog_deferred_at'] = current_time('mysql');
        return $cd;
    }

    /**
     * Extract URLs from user message text
     * 
     * @param string $text User message
     * @return array List of detected URLs
     */
    private function extract_urls_from_text($text) {
        $urls = array();
        
        // Match http/https URLs
        $pattern = '/(https?:\/\/[^\s<>"\']+)/i';
        if (preg_match_all($pattern, $text, $matches)) {
            foreach ($matches[1] as $url) {
                // Clean up trailing punctuation
                $url = rtrim($url, '.,;:!?)\'">');
                if (filter_var($url, FILTER_VALIDATE_URL)) {
                    $urls[] = $url;
                }
            }
        }
        
        // Also match www. URLs without scheme
        $pattern_www = '/(?<![\/\.])(www\.[^\s<>"\']+)/i';
        if (preg_match_all($pattern_www, $text, $matches_www)) {
            foreach ($matches_www[1] as $url) {
                $url = rtrim($url, '.,;:!?)\'">');
                $full_url = 'https://' . $url;
                if (filter_var($full_url, FILTER_VALIDATE_URL) && !in_array($full_url, $urls)) {
                    $urls[] = $full_url;
                }
            }
        }

        // Match spoken/loosely formatted domains such as "get vmo .com" or "getvmo, .com"
        // and normalize them into URLs.
        $pattern_loose_domain = '/\b([a-z0-9-]+(?:\s+[a-z0-9-]+){0,4})\s*[,;:]?\s*\.\s*(com|net|org|io|co|ai|app|dev|biz|info)\b/i';
        if (preg_match_all($pattern_loose_domain, $text, $matches_loose, PREG_SET_ORDER)) {
            foreach ($matches_loose as $match) {
                $host_left = isset($match[1]) ? preg_replace('/\s+/', '', strtolower((string)$match[1])) : '';
                $tld = strtolower((string)($match[2] ?? ''));
                if ($host_left === '' || $tld === '') {
                    continue;
                }
                $domain = $host_left . '.' . $tld;
                $full_url = 'https://' . $domain;
                if (filter_var($full_url, FILTER_VALIDATE_URL) && !in_array($full_url, $urls, true)) {
                    $urls[] = $full_url;
                }
            }
        }
        
        return array_unique($urls);
    }
    
    /**
     * Scan URLs found in user message and add content to consultation context
     * 
     * @param string $message User message
     * @param string $session_id Consultation session ID
     * @param array $consultation_cd Current consultation data
     * @return array Updated consultation data with scanned website info
     */
    private function scan_urls_in_message($message, $session_id, &$consultation_cd) {
        $urls = $this->extract_urls_from_text($message);
        
        if (empty($urls)) {
            return $consultation_cd;
        }
        
        // Ensure scanned_websites array exists
        if (!isset($consultation_cd['scanned_websites'])) {
            $consultation_cd['scanned_websites'] = array();
        }
        
        // Check if website scanner is available
        if (!function_exists('vmo_website_scanner')) {
            error_log('VMO: Website scanner not available for URL scanning');
            // ✅ FIX: Note in consultation data so AI doesn't fabricate scan results
            if (!isset($consultation_cd['scanned_websites'])) {
                $consultation_cd['scanned_websites'] = array();
            }
            foreach ($urls as $url_note) {
                if (!isset($consultation_cd['scanned_websites'][$url_note])) {
                    $consultation_cd['scanned_websites'][$url_note] = array(
                        'scanned_at' => current_time('mysql'),
                        'purpose' => 'reference',
                        'title' => 'Scan unavailable',
                        'analysis' => array('note' => 'Website scanner module was not available. The AI should ask the user to describe this website instead of fabricating details.'),
                        'scan_failed' => true
                    );
                }
            }
            return $consultation_cd;
        }
        
        $scanner = vmo_website_scanner();
        $scanned_count = 0;
        $max_urls_per_message = 3; // Limit to prevent long processing
        
        foreach ($urls as $url) {
            // Skip if already scanned
            if (isset($consultation_cd['scanned_websites'][$url])) {
                continue;
            }
            
            if ($scanned_count >= $max_urls_per_message) {
                break;
            }
            
            error_log("VMO: Auto-scanning URL from message: {$url}");
            
            try {
                // Determine scan purpose based on URL and context
                $purpose = 'reference';
                if (stripos($message, 'clone') !== false || stripos($message, 'copy') !== false) {
                    $purpose = 'clone';
                } elseif (stripos($message, 'inspiration') !== false || stripos($message, 'like this') !== false) {
                    $purpose = 'inspiration';
                } elseif (stripos($message, 'competitor') !== false) {
                    $purpose = 'competitor';
                }
                
                $result = $scanner->scan_website($url, $purpose);
                
                if ($result['success']) {
                    $consultation_cd['scanned_websites'][$url] = array(
                        'scanned_at' => current_time('mysql'),
                        'purpose'    => $purpose,
                        'title'      => $result['extracted']['title'] ?? '',
                        'analysis'   => $result['analysis'] ?? array(),
                        'headings'   => array_slice($result['extracted']['headings'] ?? array(), 0, 10),
                        'navigation' => array_slice($result['extracted']['navigation'] ?? array(), 0, 10),
                    );
                    
                    error_log("VMO: Successfully scanned {$url} - Title: " . ($result['extracted']['title'] ?? 'Unknown'));
                    $scanned_count++;
                } else {
                    error_log("VMO: Failed to scan {$url}: " . ($result['error'] ?? 'Unknown error'));
                }
            } catch (Exception $e) {
                error_log("VMO: Exception scanning {$url}: " . $e->getMessage());
            }
        }
        
        // Persist if we scanned anything
        if ($scanned_count > 0) {
            $this->persist_consultation_state($session_id, $consultation_cd);
        }
        
        return $consultation_cd;
    }
    
    /**
     * Get summary of scanned websites for AI context
     * 
     * @param array $consultation_cd Consultation data
     * @return string Formatted summary for AI system prompt
     */
    private function get_scanned_websites_context($consultation_cd) {
        if (empty($consultation_cd['scanned_websites'])) {
            return '';
        }
        
        $context = "\n\n";
        $context .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
        $context .= "🌐 SCANNED WEBSITE DATA (VMO WEBSITE SCANNER RESULTS)\n";
        $context .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
        $context .= "⚠️ IMPORTANT: These websites have been SCANNED and ANALYZED by VMO.\n";
        $context .= "You MUST reference this data when discussing these sites.\n";
        $context .= "DO NOT say 'I can't scan websites' - you have the data right here!\n";
        $context .= "DO NOT ask the user 'What is your website about?' - the scan results SHOW you.\n";
        $context .= "TREAT THIS DATA AS A REFERENCE alongside any uploaded document. User conversation corrections always override both.\n\n";
        
        foreach ($consultation_cd['scanned_websites'] as $url => $data) {
            $context .= "📎 URL: {$url}\n";
            $context .= "   Title: " . ($data['title'] ?? 'Unknown') . "\n";
            $context .= "   Purpose: " . ($data['purpose'] ?? 'reference') . "\n";
            
            if (!empty($data['analysis'])) {
                $analysis = $data['analysis'];
                if (!empty($analysis['business_type'])) {
                    $context .= "Business Type: {$analysis['business_type']}\n";
                }
                if (!empty($analysis['value_proposition'])) {
                    $context .= "Value Prop: {$analysis['value_proposition']}\n";
                }
                if (!empty($analysis['key_features']) && is_array($analysis['key_features'])) {
                    $context .= "Key Features: " . implode(', ', array_slice($analysis['key_features'], 0, 5)) . "\n";
                }
                if (!empty($analysis['design_analysis'])) {
                    $design = $analysis['design_analysis'];
                    if (!empty($design['color_scheme'])) {
                        $context .= "Color Scheme: {$design['color_scheme']}\n";
                    }
                    if (!empty($design['layout_type'])) {
                        $context .= "Layout: {$design['layout_type']}\n";
                    }
                }
            }
            
            $context .= "\n";
        }
        
        $context .= "Use this information when discussing the user's website preferences and creating their website.\n";
        $context .= "Cross-reference this scan data with any uploaded document — between both sources you may already have everything you need.\n";
        $context .= "=================================\n";
        
        return $context;
    }

    /**
     * Determine whether an array uses associative (non-sequential) keys.
     */
    private function is_associative_array($array) {
        if (!is_array($array) || $array === array()) {
            return false;
        }
        return array_keys($array) !== range(0, count($array) - 1);
    }

    private function apply_safe_openai_temperature(array $payload, $model, $temperature) {
        if (function_exists('vmo_apply_safe_temperature_to_payload')) {
            return vmo_apply_safe_temperature_to_payload($payload, $model, $temperature);
        }

        unset($payload['temperature']);
        if ($temperature === null || $temperature === '') {
            return $payload;
        }

        if (preg_match('/^(gpt-realtime|gpt-5|o1|o3|o4)/i', (string) $model)) {
            return $payload;
        }

        $payload['temperature'] = (float) $temperature;
        return $payload;
    }

    /**
     * Call OpenAI and coerce the response into decoded JSON.
     */
    private function call_openai_json(array $messages, $max_tokens = 800, $temperature = 0.0) {
        if (empty($this->api_key)) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log('VMO: call_openai_json invoked without API key');
            }
            return array();
        }
        
        // ✅ TOKEN DEDUCTION: REMOVED from here — deduction is handled at the handler level
        // (ajax_process_consultation_message covers all sub-calls including delta extraction)

        // ✅ FIX: Use centralized model registry
        $_bw_model = function_exists('vmo_get_model') ? vmo_get_model('fast') : (defined('VMO_OPENAI_MODEL') ? VMO_OPENAI_MODEL : 'gpt-5.5-mini');
        $payload = array_merge(array(
            'model' => $_bw_model,
            'messages' => $messages,
        ), function_exists('vmo_get_token_param') ? vmo_get_token_param($_bw_model, $max_tokens) : array('max_tokens' => $max_tokens));
        $payload = $this->apply_safe_openai_temperature($payload, $_bw_model, $temperature);

        $response = wp_remote_post('https://api.openai.com/v1/chat/completions', array(
            'headers' => array(
                'Authorization' => 'Bearer ' . $this->api_key,
                'Content-Type' => 'application/json',
            ),
            'body' => wp_json_encode($payload),
            'timeout' => 45,
            'method' => 'POST'
        ));

        // FALLBACK LOGIC: If the primary model fails (404, 429, 5xx), try fallback
        if (is_wp_error($response) || wp_remote_retrieve_response_code($response) >= 400) {
            error_log('VMO: Primary model failed in call_openai_json. Switching to fallback.');
            
            $fallback_model = function_exists('vmo_get_model') ? vmo_get_model('fallback') : 'gpt-5.5-nano';
            $payload['model'] = $fallback_model;
            unset($payload['max_tokens'], $payload['max_completion_tokens']);
            $payload += function_exists('vmo_get_token_param') ? vmo_get_token_param($fallback_model, $max_tokens) : array('max_tokens' => $max_tokens);
            $payload = $this->apply_safe_openai_temperature($payload, $fallback_model, $temperature);
            $response = wp_remote_post('https://api.openai.com/v1/chat/completions', array(
                'headers' => array(
                    'Authorization' => 'Bearer ' . $this->api_key,
                    'Content-Type' => 'application/json',
                ),
                'body' => wp_json_encode($payload),
                'timeout' => 45,
                'method' => 'POST'
            ));
        }

        if (is_wp_error($response)) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log('VMO: call_openai_json transport error - ' . $response->get_error_message());
            }
            return array();
        }

        $body = wp_remote_retrieve_body($response);
        $decoded = json_decode($body, true);
        // ✅ FIX: Safe navigation to prevent "Cannot access offset of type string on string"
        // when API returns malformed response (e.g., $decoded['choices'][0]['message'] is a string)
        $content = '';
        if (is_array($decoded)
            && isset($decoded['choices'])
            && is_array($decoded['choices'])
            && isset($decoded['choices'][0])
            && is_array($decoded['choices'][0])
            && isset($decoded['choices'][0]['message'])
            && is_array($decoded['choices'][0]['message'])
            && isset($decoded['choices'][0]['message']['content'])
        ) {
            $content = $decoded['choices'][0]['message']['content'];
        }
        if (!is_string($content) || trim($content) === '') {
            return array();
        }

        $content = trim($content);
        // Strip code fences if model wrapped the JSON
        if (preg_match('/^```(?:json)?\s*(.*)```$/is', $content, $fenced)) {
            $content = trim($fenced[1]);
        }

        $json = json_decode($content, true);
        if (is_array($json)) {
            return $json;
        }

        if (preg_match('/\{(?:[^{}]|(?R))*\}/s', $content, $objectMatch)) {
            $json = json_decode($objectMatch[0], true);
            if (is_array($json)) {
                return $json;
            }
        }

        return array();
    }

    /**
     * Detect when a user is explicitly referencing the uploaded document instead of providing new data.
     */
    private function user_message_references_document($message) {
        $message = strtolower((string)$message);
        if ($message === '') {
            return false;
        }
        $keywords = array('document', 'doc', 'upload', 'blueprint', 'file', 'attachment');
        $reference_flags = array('already', 'as stated', 'as mentioned', 'check', 'see', 'provided', 'included');
        $has_keyword = false;
        foreach ($keywords as $keyword) {
            if (strpos($message, $keyword) !== false) {
                $has_keyword = true;
                break;
            }
        }
        if (!$has_keyword) {
            return false;
        }
        foreach ($reference_flags as $flag) {
            if (strpos($message, $flag) !== false) {
                return true;
            }
        }
        return false;
    }

    /**
     * Return email role keys that are still missing values.
     */
    private function get_missing_email_roles($cd) {
        // ✅ FIX: ALL 7 professional email roles are required for a complete business setup.
        // The AI always recommends all 7 and lets the user accept, remove, or add more.
        $roles = array('general', 'support', 'transactions', 'marketing', 'customercare', 'bizdev', 'newsletter');
        $current = array();
        if (isset($cd['email_roles']) && is_array($cd['email_roles'])) {
            $current = $cd['email_roles'];
        }
        $missing = array();
        foreach ($roles as $role) {
            if (empty($current[$role])) {
                $missing[] = $role;
            }
        }
        return $missing;
    }

    private function are_email_roles_confirmed($cd) {
        if (!is_array($cd)) {
            return false;
        }
        $missing = $this->get_missing_email_roles($cd);
        if (!empty($missing)) {
            return false;
        }
        $meta = isset($cd['email_roles_meta']) && is_array($cd['email_roles_meta']) ? $cd['email_roles_meta'] : array();
        return !empty($meta['roles_confirmed']);
    }

    private function needs_email_roles_confirmation($cd) {
        if (!is_array($cd)) {
            return false;
        }
        $meta = isset($cd['email_roles_meta']) && is_array($cd['email_roles_meta']) ? $cd['email_roles_meta'] : array();
        if (!empty($meta['phase6_skip_approved'])) {
            return false;
        }
        // Missing values always require capture before moving on.
        $missing = $this->get_missing_email_roles($cd);
        if (!empty($missing)) {
            return true;
        }
        // All present, but still require explicit user confirmation.
        return !$this->are_email_roles_confirmed($cd);
    }

    private function user_message_confirms_email_roles($message) {
        $m = strtolower(trim((string) $message));
        if ($m === '') {
            return false;
        }
        // If user is providing/changing addresses, that's not a confirmation.
        if (strpos($m, '@') !== false || strpos($m, 'change') !== false || strpos($m, 'update') !== false || strpos($m, 'wrong') !== false || strpos($m, 'not correct') !== false) {
            return false;
        }
        // Avoid treating meta complaints as confirmation.
        $meta_markers = array("didn't catch", 'didnt catch', "you didn't", 'you didnt', 'repeat', 'stuck', 'processing', 'not listening', 'hear me', 'can you hear');
        foreach ($meta_markers as $marker) {
            if (strpos($m, $marker) !== false) {
                return false;
            }
        }
        $confirm_markers = array('confirm', 'confirmed', "that's correct", 'that is correct', 'looks good', 'looks great', 'all good', 'all set', 'approved', 'go ahead', 'proceed', 'continue');
        foreach ($confirm_markers as $marker) {
            if (strpos($m, $marker) !== false) {
                return true;
            }
        }
        return false;
    }

    /**
     * Extract domain-based email assignments from raw text.
     */
    private function extract_email_roles_from_text($text) {
        $text = (string)$text;
        if ($text === '') {
            return array();
        }

        $role_keywords = array(
            'general' => array('general', 'info', 'hello', 'main', 'primary', 'hq'),
            'support' => array('support', 'help', 'helpdesk', 'service', 'care', 'technical'),
            'marketing' => array('marketing', 'promo', 'ads', 'advert', 'campaign'),
            'transactions' => array('billing', 'payments', 'sales', 'orders', 'finance'),
            'customercare' => array('customer care', 'customer success', 'client care', 'clients'),
            'bizdev' => array('biz dev', 'business development', 'partnership', 'partner', 'alliances'),
            'newsletter' => array('newsletter', 'subscriber', 'updates', 'blog subscription', 'mailing list', 'digest')
        );

        $matches = array();
        preg_match_all('/[A-Z0-9._%+\-]+@[A-Z0-9.\-]+\.[A-Z]{2,}/i', $text, $matches, PREG_OFFSET_CAPTURE);
        if (empty($matches[0])) {
            return array();
        }

        $role_order = array('general', 'support', 'marketing', 'transactions', 'customercare', 'bizdev', 'newsletter');
        $assignments = array();
        foreach ($matches[0] as $match) {
            list($email, $offset) = $match;
            $email = strtolower($email);
            $window_start = max(0, $offset - 80);
            $context = strtolower(substr($text, $window_start, 160));
            $assigned_role = null;
            foreach ($role_keywords as $role => $keywords) {
                foreach ($keywords as $keyword) {
                    if (strpos($context, $keyword) !== false) {
                        $assigned_role = $role;
                        break 2;
                    }
                }
            }
            if (!$assigned_role) {
                foreach ($role_order as $candidate) {
                    if (empty($assignments[$candidate])) {
                        $assigned_role = $candidate;
                        break;
                    }
                }
            }
            if ($assigned_role && empty($assignments[$assigned_role])) {
                $assignments[$assigned_role] = $email;
            }
        }

        return $assignments;
    }

    private function detect_email_provisioning_mode($user_message, $ai_message) {
        $payload = strtoupper($user_message . ' ' . $ai_message);
        if (strpos($payload, '[[AUTO_PROVISION_EMAILS]]') !== false || strpos($payload, 'AUTO-PROVISION EMAILS') !== false || strpos($payload, 'GENERATE THE EMAILS FOR ME') !== false) {
            return 'auto';
        }
        if (strpos($payload, '[[ZOHO_EMAILS]]') !== false || strpos($payload, 'ZOHO MAIL') !== false || strpos($payload, 'USE ZOHO') !== false || strpos($payload, 'BUNDLED ZOHO') !== false) {
            return 'zoho';
        }
        if (strpos($payload, '[[MANUAL_DNS_EMAILS]]') !== false || strpos($payload, 'MANUAL DNS') !== false || strpos($payload, 'I WILL ADD THE DNS RECORDS') !== false) {
            return 'manual_dns';
        }
        if (
            strpos($payload, '[[USER_HAS_EMAILS]]') !== false ||
            strpos($payload, 'WE ALREADY HAVE THE EMAILS') !== false ||
            strpos($payload, 'WE ALREADY USE THESE INBOXES') !== false ||
            strpos($payload, 'USE OUR EXISTING MAILBOXES') !== false ||
            strpos($payload, 'KEEP CURRENT EMAILS') !== false
        ) {
            return 'existing_mailbox';
        }
        return null;
    }

    private function normalize_provisioning_mode($mode) {
        $mode = strtolower((string)$mode);
        if ($mode === 'auto' || $mode === 'automatic') {
            return 'auto';
        }
        if ($mode === 'manual-dns' || $mode === 'manual_dns') {
            return 'manual_dns';
        }
        if ($mode === 'zoho' || $mode === 'zoho_mail' || $mode === 'zoho-manual' || $mode === 'zoho_manual') {
            return 'zoho';
        }
        if ($mode === 'collect' || $mode === 'existing' || $mode === 'existing_mailbox' || $mode === 'manual' || $mode === 'user_provided') {
            return 'existing_mailbox';
        }
        return 'existing_mailbox';
    }

    private function resolve_target_provisioning_mode($raw_mode, array $meta, array $email_roles) {
        $normalized = $this->normalize_provisioning_mode($raw_mode);
        if ($raw_mode && $raw_mode !== '' && $raw_mode !== 'collect') {
            return $normalized;
        }
        if (($meta['bulk_email_provider'] ?? '') === 'zoho') {
            return 'zoho';
        }
        if (!empty($meta['manual_dns_confirmed_at']) || (($meta['dns_records_mode'] ?? '') === 'manual')) {
            return 'manual_dns';
        }
        if ($this->has_existing_mailbox_assignments($email_roles)) {
            return 'existing_mailbox';
        }
        if (($meta['automation_readiness'] ?? '') === 'auto_ready') {
            return 'auto';
        }
        return 'existing_mailbox';
    }

    private function has_existing_mailbox_assignments($email_roles) {
        if (!is_array($email_roles)) {
            return false;
        }
        foreach ($email_roles as $email) {
            if ($email && sanitize_email($email)) {
                return true;
            }
        }
        return false;
    }

    private function refresh_email_automation_state(array &$consultation_cd) {
        if (!isset($consultation_cd['email_roles_meta']) || !is_array($consultation_cd['email_roles_meta'])) {
            $consultation_cd['email_roles_meta'] = $this->consultation_blueprint['email_roles_meta'];
        }
        $meta = &$consultation_cd['email_roles_meta'];
        $domain = $meta['locked_domain'] ?? $this->get_enforced_domain();
        $this->refresh_dns_automation_meta($meta, $domain);
    }

    private function refresh_dns_automation_meta(array &$meta, $domain) {
        $domain = $this->normalize_domain($domain);
        if ($domain === '') {
            return;
        }
        $needs_refresh = empty($meta['dns_host_type']) || empty($meta['last_dns_detection_at']) || (strtotime($meta['last_dns_detection_at']) + 6 * HOUR_IN_SECONDS < time());
        if ($needs_refresh && class_exists('VMO_DNS_Detector')) {
            $detection = VMO_DNS_Detector::detect($domain);
            if (!empty($detection['host_type'])) {
                $meta['dns_host_type'] = $detection['host_type'];
            }
            if (!empty($detection['nameservers'])) {
                $meta['dns_nameservers'] = $detection['nameservers'];
            }
            if (!empty($detection['reason'])) {
                $meta['automation_reason'] = $detection['reason'];
            }
            $meta['last_dns_detection_at'] = current_time('mysql');
        }

        $host_type = $meta['dns_host_type'] ?? 'unknown';
        if ($host_type && $host_type !== 'unknown') {
            $meta['automation_readiness'] = 'manual_only';
            if (empty($meta['automation_reason'])) {
                $meta['automation_reason'] = sprintf('DNS host detected: %s. Follow guided manual DNS validation.', $host_type);
            }
            if (empty($meta['fallback_validation_status'])) {
                $meta['fallback_validation_status'] = 'dns_incompatible';
            }
        } else {
            $meta['automation_readiness'] = 'unknown';
        }
    }

    private function sanitize_email_meta_delta(array $incoming) {
        $sanitized = array();
        if (isset($incoming['locked_domain'])) {
            $sanitized['locked_domain'] = $this->normalize_domain($incoming['locked_domain']);
        }
        if (isset($incoming['roles_confirmed'])) {
            $sanitized['roles_confirmed'] = (bool) $incoming['roles_confirmed'];
            $sanitized['roles_confirmed_at'] = $sanitized['roles_confirmed']
                ? (!empty($incoming['roles_confirmed_at']) ? sanitize_text_field($incoming['roles_confirmed_at']) : current_time('mysql'))
                : null;
        } elseif (isset($incoming['roles_confirmed_at'])) {
            $sanitized['roles_confirmed_at'] = sanitize_text_field($incoming['roles_confirmed_at']);
        }
        if (isset($incoming['phase6_skip_approved'])) {
            $sanitized['phase6_skip_approved'] = (bool) $incoming['phase6_skip_approved'];
        }
        if (isset($incoming['provisioning_mode'])) {
            $sanitized['provisioning_mode'] = $this->normalize_provisioning_mode($incoming['provisioning_mode']);
        }
        if (isset($incoming['provisioning_status'])) {
            $sanitized['provisioning_status'] = sanitize_text_field($incoming['provisioning_status']);
        }
        if (isset($incoming['bulk_email_provider'])) {
            $provider = sanitize_key($incoming['bulk_email_provider']);
            $sanitized['bulk_email_provider'] = $provider !== '' ? $provider : null;
        }
        if (isset($incoming['bulk_email_verified'])) {
            $sanitized['bulk_email_verified'] = (bool) $incoming['bulk_email_verified'];
        }
        if (isset($incoming['automation_readiness'])) {
            $allowed = array('auto_ready', 'manual_only', 'unknown');
            $value = in_array($incoming['automation_readiness'], $allowed, true)
                ? $incoming['automation_readiness']
                : 'unknown';
            $sanitized['automation_readiness'] = $value;
        }
        if (isset($incoming['automation_reason'])) {
            $sanitized['automation_reason'] = sanitize_text_field($incoming['automation_reason']);
        }
        if (isset($incoming['dns_host_type'])) {
            $sanitized['dns_host_type'] = sanitize_text_field($incoming['dns_host_type']);
        }
        if (isset($incoming['dns_records_mode'])) {
            $sanitized['dns_records_mode'] = sanitize_text_field($incoming['dns_records_mode']);
        }
        if (isset($incoming['hosted_zone_id'])) {
            $sanitized['hosted_zone_id'] = sanitize_text_field($incoming['hosted_zone_id']);
        }
        if (isset($incoming['manual_dns_notes'])) {
            $sanitized['manual_dns_notes'] = sanitize_textarea_field($incoming['manual_dns_notes']);
        }
        if (isset($incoming['manual_dns_confirmed_at'])) {
            $sanitized['manual_dns_confirmed_at'] = sanitize_text_field($incoming['manual_dns_confirmed_at']);
        }
        if (isset($incoming['last_dns_detection_at'])) {
            $sanitized['last_dns_detection_at'] = sanitize_text_field($incoming['last_dns_detection_at']);
        }
        if (isset($incoming['fallback_validation_status'])) {
            $sanitized['fallback_validation_status'] = sanitize_text_field($incoming['fallback_validation_status']);
        }
        if (isset($incoming['provisioning_consent'])) {
            $sanitized['provisioning_consent'] = (bool) $incoming['provisioning_consent'];
        }
        if (isset($incoming['ai_auto_reply_enabled'])) {
            $sanitized['ai_auto_reply_enabled'] = (bool) $incoming['ai_auto_reply_enabled'];
        }
        if (isset($incoming['alias_preferences']) && is_array($incoming['alias_preferences'])) {
            $sanitized['alias_preferences'] = array();
            foreach ($incoming['alias_preferences'] as $role => $alias) {
                $clean_role = sanitize_key($role);
                if ($clean_role === '') {
                    continue;
                }
                $alias_value = strtolower(preg_replace('/[^a-z0-9._-]+/i', '', (string) $alias));
                if ($alias_value === '') {
                    continue;
                }
                $sanitized['alias_preferences'][$clean_role] = $alias_value;
            }
        }
        if (isset($incoming['warnings']) && is_array($incoming['warnings'])) {
            $sanitized['warnings'] = array_map('sanitize_text_field', $incoming['warnings']);
        }
        if (isset($incoming['dns_nameservers']) && is_array($incoming['dns_nameservers'])) {
            $sanitized['dns_nameservers'] = array_map('sanitize_text_field', $incoming['dns_nameservers']);
        }
        if (isset($incoming['existing_mailbox_validations']) && is_array($incoming['existing_mailbox_validations'])) {
            $sanitized['existing_mailbox_validations'] = $incoming['existing_mailbox_validations'];
        }
        if (isset($incoming['email_accounts']) && is_array($incoming['email_accounts'])) {
            $sanitized['email_accounts'] = array();
            foreach ($incoming['email_accounts'] as $category => $account) {
                $clean_category = sanitize_key($category);
                if ($clean_category === '' || !is_array($account)) {
                    continue;
                }

                $email = sanitize_email($account['email'] ?? '');
                if ($email === '') {
                    continue;
                }

                $sanitized_account = array(
                    'email' => $email,
                    'display_name' => sanitize_text_field($account['display_name'] ?? ''),
                    'provider_preset' => sanitize_key($account['provider_preset'] ?? 'custom'),
                    'password_stored' => !empty($account['password_stored']),
                    'verified' => !empty($account['verified']),
                    'added_at' => sanitize_text_field($account['added_at'] ?? ''),
                );

                foreach (array('imap', 'smtp') as $transport) {
                    if (!isset($account[$transport]) || !is_array($account[$transport])) {
                        continue;
                    }
                    $sanitized_account[$transport] = array(
                        'host' => sanitize_text_field($account[$transport]['host'] ?? ''),
                        'port' => isset($account[$transport]['port']) ? intval($account[$transport]['port']) : null,
                        'encryption' => sanitize_text_field($account[$transport]['encryption'] ?? ''),
                        'username' => sanitize_text_field($account[$transport]['username'] ?? ''),
                    );
                }

                $sanitized['email_accounts'][$clean_category] = $sanitized_account;
            }
        }
        if (isset($incoming['manual_dns_confirmed_by'])) {
            $sanitized['manual_dns_confirmed_by'] = sanitize_text_field($incoming['manual_dns_confirmed_by']);
        }
        if (isset($incoming['provisioning_snapshot']) && is_array($incoming['provisioning_snapshot'])) {
            $sanitized['provisioning_snapshot'] = $incoming['provisioning_snapshot'];
        }
        if (isset($incoming['provisioning_steps']) && is_array($incoming['provisioning_steps'])) {
            $sanitized['provisioning_steps'] = $incoming['provisioning_steps'];
        }
        if (isset($incoming['mailbox_summary']) && is_array($incoming['mailbox_summary'])) {
            $sanitized['mailbox_summary'] = $incoming['mailbox_summary'];
        }
        if (isset($incoming['provisioning_error'])) {
            $sanitized['provisioning_error'] = sanitize_text_field($incoming['provisioning_error']);
        }
        return $sanitized;
    }

    private function hydrate_provisioning_meta(array &$meta, array $status_payload) {
        $meta['provisioning_status'] = $status_payload['status'] ?? 'unknown';
        $meta['provisioning_snapshot'] = $status_payload;

        if (!empty($status_payload['last_error'])) {
            $meta['provisioning_error'] = $status_payload['last_error'];
        } else {
            unset($meta['provisioning_error']);
        }

        if (!empty($status_payload['dns_host_type'])) {
            $meta['dns_host_type'] = $status_payload['dns_host_type'];
        }
        if (!empty($status_payload['hosted_zone_id'])) {
            $meta['hosted_zone_id'] = $status_payload['hosted_zone_id'];
        }
        if (!empty($status_payload['steps'])) {
            $meta['provisioning_steps'] = $status_payload['steps'];
        }
        if (!empty($status_payload['mailbox_summary'])) {
            $meta['mailbox_summary'] = $status_payload['mailbox_summary'];
        }
        if (!empty($status_payload['metadata']['dns_records']['mode'])) {
            $meta['dns_records_mode'] = $status_payload['metadata']['dns_records']['mode'];
        }
        if (!empty($status_payload['manual_dns_confirmed_at'])) {
            $meta['manual_dns_confirmed_at'] = $status_payload['manual_dns_confirmed_at'];
        } elseif (!empty($status_payload['metadata']['options']['manual_dns_confirmed_at'])) {
            $meta['manual_dns_confirmed_at'] = $status_payload['metadata']['options']['manual_dns_confirmed_at'];
        }
    }

    private function maybe_invoke_auto_provisioning(array &$consultation_cd) {
        // Check if provisioning mode is 'auto' and roles are confirmed
        if (!$this->are_email_roles_confirmed($consultation_cd)) {
            return;
        }

        $meta = isset($consultation_cd['email_roles_meta']) && is_array($consultation_cd['email_roles_meta'])
            ? $consultation_cd['email_roles_meta']
            : [];
        $mode = $meta['provisioning_mode'] ?? '';
        if ($mode !== 'auto') {
            return;
        }

        // Already provisioned?
        $status = $meta['provisioning_status'] ?? '';
        if (in_array($status, ['completed', 'in_progress'], true)) {
            return;
        }

        // Check if provisioner class exists
        if (!class_exists('VMO_Email_Provisioner')) {
            error_log('VMO: Auto-provisioning skipped — VMO_Email_Provisioner not available');
            return;
        }

        $domain = $meta['locked_domain'] ?? $this->get_enforced_domain();
        if (empty($domain) || $domain === 'localhost') {
            error_log('VMO: Auto-provisioning skipped — no valid domain');
            return;
        }

        $email_roles = isset($consultation_cd['email_roles']) && is_array($consultation_cd['email_roles'])
            ? $consultation_cd['email_roles']
            : [];

        // Build mailbox list from confirmed roles
        $mailboxes = [];
        foreach ($email_roles as $role => $email) {
            if (empty($email)) {
                // Generate default alias for unfilled roles
                $email = $this->build_role_alias($role, $domain, $meta);
            }
            $mailboxes[] = [
                'role' => $role,
                'email' => $email,
                'local_part' => explode('@', $email)[0] ?? $role,
            ];
        }

        if (empty($mailboxes)) {
            return;
        }

        try {
            if (!isset($consultation_cd['email_roles_meta']) || !is_array($consultation_cd['email_roles_meta'])) { $consultation_cd['email_roles_meta'] = array(); }
            $consultation_cd['email_roles_meta']['provisioning_status'] = 'in_progress';

            $provisioner = VMO_Email_Provisioner::get_instance();
            $user_id = get_current_user_id();

            $result = $provisioner->provision_emails($user_id, $domain, $mailboxes);

            if (!empty($result['success'])) {
                if (!isset($consultation_cd['email_roles_meta']) || !is_array($consultation_cd['email_roles_meta'])) { $consultation_cd['email_roles_meta'] = array(); }
            $consultation_cd['email_roles_meta']['provisioning_status'] = 'completed';
                $consultation_cd['email_roles_meta']['provisioning_snapshot'] = $result;
                error_log("VMO: Auto-provisioning completed for domain {$domain}");
            } else {
                if (!isset($consultation_cd['email_roles_meta']) || !is_array($consultation_cd['email_roles_meta'])) { $consultation_cd['email_roles_meta'] = array(); }
            $consultation_cd['email_roles_meta']['provisioning_status'] = 'failed';
                $consultation_cd['email_roles_meta']['provisioning_error'] = $result['error'] ?? 'Unknown provisioning error';
                error_log("VMO: Auto-provisioning failed for domain {$domain}: " . ($result['error'] ?? 'unknown'));
            }
        } catch (\Throwable $e) {
            if (!isset($consultation_cd['email_roles_meta']) || !is_array($consultation_cd['email_roles_meta'])) { $consultation_cd['email_roles_meta'] = array(); }
            $consultation_cd['email_roles_meta']['provisioning_status'] = 'failed';
            $consultation_cd['email_roles_meta']['provisioning_error'] = $e->getMessage();
            error_log("VMO: Auto-provisioning exception: " . $e->getMessage());
        }
    }

    private function build_role_alias($role, $domain, $meta) {
        $alias_preferences = isset($meta['alias_preferences']) && is_array($meta['alias_preferences'])
            ? $meta['alias_preferences']
            : array();
        if (!empty($alias_preferences[$role])) {
            $local = sanitize_title(str_replace('@' . $domain, '', $alias_preferences[$role]));
        } else {
            $local = preg_replace('/[^a-z0-9]+/', '', strtolower($role));
        }
        if (!$local) {
            $local = 'team';
        }
        return $local . '@' . $domain;
    }

    private function normalize_domain($domain) {
        $domain = trim((string) $domain);
        if ($domain === '') {
            return '';
        }

        $needs_scheme = stripos($domain, '://') === false;
        $target = $needs_scheme ? 'https://' . $domain : $domain;
        $parsed_host = parse_url($target, PHP_URL_HOST);
        if (!$parsed_host && $needs_scheme) {
            // parse_url may fail if the host already contained a path without scheme.
            $parsed_host = parse_url('https://' . ltrim($domain, '/'), PHP_URL_HOST);
        }

        $host = $parsed_host ? $parsed_host : $domain;
        $host = strtolower(trim($host));

        if (function_exists('idn_to_ascii')) {
            $idn = @idn_to_ascii($host, IDNA_DEFAULT, defined('INTL_IDNA_VARIANT_UTS46') ? INTL_IDNA_VARIANT_UTS46 : 0);
            if ($idn) {
                $host = strtolower($idn);
            }
        }

        $host = trim($host, '.');
        if ($host === '') {
            return '';
        }

        return preg_match('/^[a-z0-9.-]+\.[a-z]{2,}$/i', $host) ? $host : '';
    }

    private function get_enforced_domain() {
        static $cached = null;
        if ($cached !== null) {
            return $cached;
        }
        $host = parse_url(home_url('/'), PHP_URL_HOST);
        if (!$host && !empty($_SERVER['HTTP_HOST'])) {
            $host = $_SERVER['HTTP_HOST'];
        }
        $host = $this->normalize_domain($host);
        if ($host === '') {
            $host = 'localhost';
        }
        $cached = $host;
        return $cached;
    }

    private function get_public_email_domains() {
        return array(
            'gmail.com','yahoo.com','outlook.com','hotmail.com','aol.com','icloud.com','protonmail.com','live.com','ymail.com','me.com','msn.com','gmx.com','pm.me'
        );
    }

    private function extract_domain_from_email($email) {
        $email = sanitize_email((string)$email);
        if (!$email || strpos($email, '@') === false) {
            return '';
        }
        $parts = explode('@', $email);
        $domain = end($parts);
        return $this->normalize_domain($domain);
    }

    private function is_public_email_domain($domain) {
        $domain = $this->normalize_domain($domain);
        if ($domain === '') {
            return true;
        }
        return in_array($domain, $this->get_public_email_domains(), true);
    }

    private function extract_foreign_domains_from_text($text, $allowed_domain) {
        $text = (string)$text;
        if ($text === '') {
            return array();
        }
        $allowed_domain = $this->normalize_domain($allowed_domain);
        preg_match_all('/(?:https?:\/\/)?(?:www\.)?([a-z0-9.-]+\.[a-z]{2,})/i', $text, $matches);
        if (empty($matches[1])) {
            return array();
        }
        $found = array();
        foreach ($matches[1] as $candidate) {
            $normalized = $this->normalize_domain($candidate);
            if ($normalized === '' || $normalized === $allowed_domain) {
                continue;
            }
            if ($this->is_public_email_domain($normalized)) {
                continue;
            }
            $found[] = $normalized;
        }
        return array_values(array_unique($found));
    }

    /**
     * Use OpenAI to figure out what fields should update based on the last turn.
     */
    private function extract_consultation_delta(array $business_profile, $user_message, $assistant_message = '', array $context = array()) {
        $profile_json = wp_json_encode($business_profile, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
        $system  = "You are a strict JSON extraction engine for the VMO Business Consultation.\n";
        $system .= "You ONLY output JSON describing updated fields. Never invent data.\n\n";
        $system .= "IMPORTANT: If the user message is meta feedback about the conversation (e.g., 'you didn't catch that', 'repeat that', 'you're not listening', 'stuck processing', microphone/voice issues), DO NOT extract business facts. In those cases, return empty deltas and include system_notes: ['meta_feedback'].\n\n";
        $system .= "Current business_profile (reference only):\n" . $profile_json . "\n\n";
        $system .= "Return JSON with keys: business_profile_delta (object), offers_profile_delta (array), payments_profile_delta (object), email_roles_meta_delta (object), extracted_information_delta (object), discovery_profile_delta (object), business_opportunity_shortlist_delta (array), corrections (array of objects), system_notes (array).\n\n";
        $system .= "PHASE 1 EXTRACTION MAPPING: When user states success metrics, map to business_profile_delta.targets.north_star_metric. When user states goals, map to business_profile_delta.targets.long_term. When user states target customer, map to business_profile_delta.audience.primary_segments (array). When user states offerings, map to offers_profile_delta as [{name,type}] and/or business_profile_delta.offers. When user states automation or team-size preference, map to business_profile_delta.operations.fulfillment_model and business_profile_delta.operations.team_structure.scale_intent.\n\n";
        $system .= "DISCOVERY MODE EXTRACTION: If the user does not know what business to start, asks VMO/Jerreme to recommend a business, asks for the business that would make the most money, or asks for turnkey direction, populate discovery_profile_delta. Track mode (discovery, income_goal, turnkey), status (collecting_profile, ideas_presented, business_selected), needs_business_selection, interests, skills, work_background, personality_work_style, lifestyle_constraints, location, time_available, startup_budget, income_goal, risk_tolerance, preferred_business_model, selected_business_idea, and selected_reason. If the assistant presents 3-5 business ideas, extract them into business_opportunity_shortlist_delta with name, industry, business_model, target_audience, core_offer, fit_reason, income_potential, startup_cost, timeline, location_fit, first_steps, risks, and selected. If the user chooses or approves one idea, set status to business_selected, needs_business_selection to false, and map the chosen business into business_profile_delta and extracted_information_delta when clear. Do not mark business_selected until a concrete direction is chosen or prescribed.\n\n";
        $system .= "PRODUCT CATALOG EXTRACTION: If the user gives product/service catalog details, put them in extracted_information_delta.products_inventory or extracted_information_delta.services_inventory. For each item capture name, description, price, currency, category, main_image, gallery_images, videos, variants/options, SKU, stock/inventory, shipping/digital/booking fulfillment, and media_status. If they say products/catalog/images/prices/variations will be added later, set extracted_information_delta.product_catalog_deferred = true and extracted_information_delta.ecommerce.catalog_deferred = true.\n\n";
        $system .= "CORRECTIONS DETECTION: If the user is CORRECTING something that was previously stated differently (e.g., 'No, it's X not Y', 'change that to...', 'the name is actually...', 'I said X not Y', 'that's wrong, it should be...'), add an entry to the 'corrections' array with: {\"field\": \"what was corrected (e.g., legal_entity, product_name, email)\", \"wrong_value\": \"what was incorrect\", \"correct_value\": \"what the user said it should be\", \"user_quote\": \"the user's exact words\"}.\n";
        $system .= "This includes cases where the user restates information for emphasis or clarification.\n";
        $system .= "\nIf user mentions 'auto-reply', 'AI reply', or 'automatic response' for emails, set email_roles_meta_delta.ai_auto_reply_enabled = true.";

        $messages = array(
            array('role' => 'system', 'content' => $system),
            array('role' => 'user', 'content' => "USER_MESSAGE:\n" . (string)$user_message)
        );
        if ($assistant_message) {
            $messages[] = array('role' => 'assistant', 'content' => "ASSISTANT_REPLY:\n" . (string)$assistant_message);
        }
        if (!empty($context['phase'])) {
            $messages[] = array('role' => 'user', 'content' => 'CURRENT_PHASE: ' . $context['phase']);
        }
        $delta = $this->call_openai_json($messages, 1200, 0.0);
        if (!is_array($delta) || empty($delta)) {
            return array(
                'business_profile_delta' => array(),
                'offers_profile_delta' => array(),
                'payments_profile_delta' => array(),
                'email_roles_meta_delta' => array(),
                'extracted_information_delta' => array(),
                'discovery_profile_delta' => array(),
                'business_opportunity_shortlist_delta' => array(),
                'corrections' => array(),
                'system_notes' => array('no_update')
            );
        }
        $delta += array(
            'business_profile_delta' => array(),
            'offers_profile_delta' => array(),
            'payments_profile_delta' => array(),
            'email_roles_meta_delta' => array(),
            'extracted_information_delta' => array(),
            'discovery_profile_delta' => array(),
            'business_opportunity_shortlist_delta' => array(),
            'corrections' => array(),
            'system_notes' => array()
        );
        return $delta;
    }

    /**
     * Generate or refresh the deep research profile (Phase 3 output).
     */
    private function generate_research_profile($session_id, array $cd) {
        $business_profile = $cd['business_profile'] ?? array();
        $payload = wp_json_encode($business_profile, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
        $system  = "You are VMO, an elite AI research analyst. Build research_profile JSON covering industry, market, customers, competitors, seo, channels, risk_alignment.\n";
        $system .= "Use realistic estimates, cite qualitative evidence when numeric data unavailable.\n";
        $system .= "BUSINESS PROFILE INPUT:\n" . $payload;
        $messages = array(
            array('role' => 'system', 'content' => $system)
        );
        $json = $this->call_openai_json($messages, 1800, 0.2);
        if (!isset($json['research_profile']) || !is_array($json['research_profile'])) {
            $json = array('research_profile' => is_array($json) ? $json : array());
        }

        $fallback_profile = $this->build_fallback_research_profile($cd);
        $cd['research_profile'] = array_replace_recursive($fallback_profile, $json['research_profile']);
        $this->persist_consultation_state($session_id, $cd);
        return $cd;
    }

    /**
     * Generate website_plan, systems_plan, roadmap_json, daily_tasks_json artifacts (Phase 7 output).
     * roadmap_json and daily_tasks_json now delegate to the Vision Roadmap Engine when available.
     */
    private function generate_phase7_artifacts($session_id, array $cd) {
        $payload = array(
            'business_profile' => $cd['business_profile'] ?? array(),
            'research_profile' => $cd['research_profile'] ?? array(),
            'recommendations_profile' => $cd['recommendations_profile'] ?? array(),
            'offers_profile' => $cd['offers_profile'] ?? array(),
            'payments_profile' => $cd['payments_profile'] ?? array()
        );
        $system  = "You are VMO, the AI CEO. Produce website_plan and systems_plan JSON objects based on provided data.\n";
        $system .= "Each object must be complete and actionable.\n";
        $system .= "INPUT:\n" . wp_json_encode($payload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
        $messages = array(
            array('role' => 'system', 'content' => $system)
        );
        $json = $this->call_openai_json($messages, 2200, 0.25);
        $fallback_artifacts = $this->build_fallback_phase7_artifacts($session_id, $cd);

        $cd['website_plan'] = !empty($json['website_plan']['pages'])
            ? $json['website_plan']
            : $fallback_artifacts['website_plan'];
        $cd['systems_plan'] = !empty($json['systems_plan']['automation'])
            ? $json['systems_plan']
            : $fallback_artifacts['systems_plan'];

        // Roadmap & daily tasks — delegate to Vision Roadmap Engine
        $engine = $this->get_roadmap_engine();
        if ($engine) {
            try {
                $user_id = get_current_user_id();
                $result = $engine->generate_ai_roadmap($session_id, $user_id, $cd);
                if ($result && !empty($result['roadmap_id'])) {
                    $cd['roadmap_json'] = $result;
                    $cd['daily_tasks_json'] = ['engine' => true, 'roadmap_id' => $result['roadmap_id']];
                    $cd['active_roadmap_id'] = $result['roadmap_id'];
                } else {
                    // Engine returned no result — use AI fallback from the call above
                    $cd['roadmap_json'] = !empty($json['roadmap_json']['phases']) || !empty($json['roadmap_json']['milestones'])
                        ? $json['roadmap_json']
                        : $fallback_artifacts['roadmap_json'];
                    $cd['daily_tasks_json'] = !empty($json['daily_tasks_json']['sequence'])
                        ? $json['daily_tasks_json']
                        : $fallback_artifacts['daily_tasks_json'];
                }
            } catch (\Throwable $e) {
                error_log('VMO Phase7 engine roadmap failed, using AI fallback: ' . $e->getMessage());
                $cd['roadmap_json'] = !empty($json['roadmap_json']['phases']) || !empty($json['roadmap_json']['milestones'])
                    ? $json['roadmap_json']
                    : $fallback_artifacts['roadmap_json'];
                $cd['daily_tasks_json'] = !empty($json['daily_tasks_json']['sequence'])
                    ? $json['daily_tasks_json']
                    : $fallback_artifacts['daily_tasks_json'];
            }
        } else {
            $cd['roadmap_json'] = !empty($json['roadmap_json']['phases']) || !empty($json['roadmap_json']['milestones'])
                ? $json['roadmap_json']
                : $fallback_artifacts['roadmap_json'];
            $cd['daily_tasks_json'] = !empty($json['daily_tasks_json']['sequence'])
                ? $json['daily_tasks_json']
                : $fallback_artifacts['daily_tasks_json'];
        }

        if (!isset($cd['approvals']) || !is_array($cd['approvals'])) {
            $cd['approvals'] = array();
        }
        $cd['approvals']['roadmap_generated'] = !empty($cd['roadmap_json']);
        $cd['approvals']['daily_activities_generated'] = !empty($cd['daily_tasks_json']);
        $this->persist_consultation_state($session_id, $cd);
        return $cd;
    }

    private function build_fallback_research_profile(array $cd) {
        $bp = is_array($cd['business_profile'] ?? null) ? $cd['business_profile'] : array();
        $identity = is_array($bp['identity'] ?? null) ? $bp['identity'] : array();
        $vision = is_array($bp['vision'] ?? null) ? $bp['vision'] : array();
        $targets = is_array($bp['targets'] ?? null) ? $bp['targets'] : array();
        $audience = is_array($bp['audience'] ?? null) ? $bp['audience'] : array();
        $web_requirements = is_array($bp['web_requirements'] ?? null) ? $bp['web_requirements'] : array();
        $extracted = is_array($cd['extracted_information'] ?? null) ? $cd['extracted_information'] : array();

        $industry = sanitize_text_field((string) ($extracted['industry'] ?? $identity['industry'] ?? ''));
        $business_name = sanitize_text_field((string) ($identity['business_name'] ?? $extracted['businessName'] ?? ''));
        $geographies = array_values(array_filter(array_map('sanitize_text_field', (array) ($audience['geographies'] ?? ($audience['geographic_focus'] ?? array())))));
        $segments = array_values(array_filter(array_map('sanitize_text_field', (array) ($audience['primary_segments'] ?? array()))));
        $pain_points = array_values(array_filter(array_map('sanitize_text_field', (array) ($audience['pain_points'] ?? array()))));
        $preferred_channels = array_values(array_filter(array_map('sanitize_text_field', (array) ($audience['preferred_channels'] ?? array()))));

        if (empty($preferred_channels)) {
            $preferred_channels = array('website', 'email', 'phone');
            if (!empty($web_requirements['ecommerce'])) {
                $preferred_channels[] = 'search';
            }
        }

        $market_summary_parts = array_values(array_filter(array(
            $industry !== '' ? $industry . ' business research fallback derived from the confirmed consultation state.' : '',
            sanitize_text_field((string) ($targets['short_term'] ?? '')),
            sanitize_text_field((string) ($targets['long_term'] ?? '')),
            sanitize_text_field((string) ($vision['mission'] ?? '')),
        )));

        $keyword_candidates = array();
        foreach ((array) ($bp['offers'] ?? array()) as $offer) {
            if (is_array($offer) && !empty($offer['name'])) {
                $keyword_candidates[] = sanitize_text_field((string) $offer['name']);
            } elseif (is_string($offer) && trim($offer) !== '') {
                $keyword_candidates[] = sanitize_text_field($offer);
            }
        }
        if ($industry !== '') {
            $keyword_candidates[] = $industry;
        }
        if (!empty($segments[0]) && $industry !== '') {
            $keyword_candidates[] = $industry . ' ' . $segments[0];
        }

        $competitors = array();
        if ($this->deep_research instanceof VMO_Deep_Research && method_exists($this->deep_research, 'get_latest_research')) {
            $latest_research = $this->deep_research->get_latest_research(get_current_user_id());
            $candidate_sets = array(
                $latest_research['competitor_landscape']['competitors'] ?? null,
                $latest_research['competitors'] ?? null,
                $latest_research['competitor_analysis']['competitors'] ?? null,
            );
            foreach ($candidate_sets as $candidate_set) {
                if (!is_array($candidate_set)) {
                    continue;
                }

                foreach ($candidate_set as $candidate) {
                    $name = '';
                    if (is_array($candidate)) {
                        $name = sanitize_text_field((string) ($candidate['name'] ?? $candidate['company_name'] ?? ''));
                    } elseif (is_string($candidate)) {
                        $name = sanitize_text_field($candidate);
                    }

                    if ($name !== '') {
                        $competitors[$name] = array('name' => $name);
                    }
                }
            }

            $keyword_research = is_array($latest_research['keyword_research'] ?? null) ? $latest_research['keyword_research'] : array();
            foreach (array('primary_keywords', 'keywords', 'high_intent_keywords') as $keyword_key) {
                if (!empty($keyword_research[$keyword_key]) && is_array($keyword_research[$keyword_key])) {
                    foreach ($keyword_research[$keyword_key] as $keyword) {
                        if (is_string($keyword) && trim($keyword) !== '') {
                            $keyword_candidates[] = sanitize_text_field($keyword);
                        }
                    }
                }
            }
        }

        return array(
            'industry' => array(
                'name' => $industry,
                'summary' => sanitize_textarea_field(implode(' ', array_slice($market_summary_parts, 0, 3))),
            ),
            'market' => array(
                'summary' => sanitize_textarea_field(implode(' ', $market_summary_parts)),
                'geography' => $geographies,
            ),
            'customers' => array(
                'segments' => $segments,
                'pain_points' => $pain_points,
            ),
            'competitors' => array_values(array_slice($competitors, 0, 5)),
            'seo' => array(
                'primary_keywords' => array_values(array_slice(array_unique(array_filter($keyword_candidates)), 0, 10)),
            ),
            'channels' => array(
                'top' => array_values(array_unique($preferred_channels)),
            ),
            'risk_alignment' => array(
                'status' => 'consultation_fallback',
                'notes' => ($business_name !== '' ? $business_name . ': ' : '') . 'Derived from confirmed consultation data because automated research generation did not complete.',
            ),
            'generated_at' => current_time('mysql'),
            'source' => 'confirmed_consultation_fallback',
        );
    }

    private function build_fallback_phase7_artifacts($session_id, array $cd) {
        $bp = is_array($cd['business_profile'] ?? null) ? $cd['business_profile'] : array();
        $identity = is_array($bp['identity'] ?? null) ? $bp['identity'] : array();
        $targets = is_array($bp['targets'] ?? null) ? $bp['targets'] : array();
        $web_requirements = is_array($bp['web_requirements'] ?? null) ? $bp['web_requirements'] : array();
        $offers = (array) ($bp['offers'] ?? array());
        $extracted = is_array($cd['extracted_information'] ?? null) ? $cd['extracted_information'] : array();

        $business_name = sanitize_text_field((string) ($identity['business_name'] ?? $extracted['businessName'] ?? 'Your Business'));
        $primary_offer = 'primary offer';
        foreach ($offers as $offer) {
            if (is_array($offer) && !empty($offer['name'])) {
                $primary_offer = sanitize_text_field((string) $offer['name']);
                break;
            }
            if (is_string($offer) && trim($offer) !== '') {
                $primary_offer = sanitize_text_field($offer);
                break;
            }
        }

        $short_term = sanitize_text_field((string) ($targets['short_term'] ?? 'Launch the business foundation'));
        $mid_term = sanitize_text_field((string) ($targets['mid_term'] ?? 'Begin acquiring qualified leads consistently'));
        $long_term = sanitize_text_field((string) ($targets['long_term'] ?? ($targets['north_star_metric'] ?? 'Measure and improve growth performance')));

        $website_plan = array(
            'pages' => array(
                array('title' => 'Home', 'purpose' => 'Position the business and drive the primary conversion.'),
                array('title' => 'About', 'purpose' => 'Build trust with story, mission, and credibility.'),
                array('title' => !empty($web_requirements['ecommerce']) ? 'Shop' : 'Services', 'purpose' => 'Present the main offer and pricing path.'),
                array('title' => 'Contact', 'purpose' => 'Capture leads and route inquiries into CRM.'),
            ),
            'primary_conversion' => !empty($web_requirements['ecommerce']) ? 'purchase' : 'lead_capture',
            'source' => 'confirmed_consultation_fallback',
        );

        if (!empty($web_requirements['booking'])) {
            $website_plan['pages'][] = array('title' => 'Booking', 'purpose' => 'Convert qualified visitors into scheduled appointments.');
        }
        if (!empty($web_requirements['donations'])) {
            $website_plan['pages'][] = array('title' => 'Donate', 'purpose' => 'Support donation conversion and trust-building.');
        }

        $systems_plan = array(
            'automation' => array(
                'CRM lead capture from website forms',
                'Weekly email follow-up and nurture sequences',
                'Self-hosted analytics and visit tracking',
            ),
            'payments' => array(
                'supports_cards' => !empty($cd['payments_profile']['accepts_cards']),
                'supports_donations' => !empty($cd['payments_profile']['supports_donations']),
                'currencies' => (array) ($cd['payments_profile']['supported_currencies'] ?? array('USD')),
            ),
            'integrations' => array_values(array_filter(array_map('sanitize_text_field', (array) ($web_requirements['integrations'] ?? array())))),
            'source' => 'confirmed_consultation_fallback',
        );

        $milestones = array(
            array(
                'phase' => 'foundation',
                'title' => 'Finalize business foundation for ' . $business_name,
                'target_date' => date('Y-m-d', strtotime('+14 days')),
                'owner' => 'vmo',
                'status' => 'planned',
                'progress' => 0,
                'notes' => $short_term,
                'vision_alignment' => $short_term,
            ),
            array(
                'phase' => 'launch',
                'title' => 'Launch the website and ' . $primary_offer . ' conversion flow',
                'target_date' => date('Y-m-d', strtotime('+30 days')),
                'owner' => 'marketing',
                'status' => 'planned',
                'progress' => 0,
                'notes' => $mid_term,
                'vision_alignment' => $mid_term,
            ),
            array(
                'phase' => 'growth',
                'title' => 'Track performance and optimize toward the north-star goal',
                'target_date' => date('Y-m-d', strtotime('+60 days')),
                'owner' => 'operations',
                'status' => 'planned',
                'progress' => 0,
                'notes' => $long_term,
                'vision_alignment' => $long_term,
            ),
        );

        $phases = array(
            array('slug' => 'foundation', 'title' => 'Foundation', 'objective' => $short_term, 'milestones' => array($milestones[0])),
            array('slug' => 'launch', 'title' => 'Launch', 'objective' => $mid_term, 'milestones' => array($milestones[1])),
            array('slug' => 'growth', 'title' => 'Growth', 'objective' => $long_term, 'milestones' => array($milestones[2])),
        );

        $sequence = array();
        $task_day = 1;
        foreach ($milestones as $milestone) {
            $sequence[] = array(
                'day' => $task_day,
                'phase' => $milestone['phase'],
                'title' => 'Advance milestone: ' . $milestone['title'],
                'status' => 'planned',
                'owner' => $milestone['owner'],
                'due_date' => $milestone['target_date'],
            );
            $sequence[] = array(
                'day' => $task_day + 2,
                'phase' => $milestone['phase'],
                'title' => 'Review blockers and confirm next execution step',
                'status' => 'planned',
                'owner' => 'owner',
                'due_date' => $milestone['target_date'],
            );
            $task_day += 7;
        }

        return array(
            'website_plan' => $website_plan,
            'systems_plan' => $systems_plan,
            'roadmap_json' => array(
                'phases' => $phases,
                'milestones' => $milestones,
                'generation_method' => 'confirmed_consultation_fallback',
                'session_id' => $session_id,
                'generated_at' => current_time('mysql'),
            ),
            'daily_tasks_json' => array(
                'sequence' => $sequence,
                'generation_method' => 'confirmed_consultation_fallback',
                'generated_at' => current_time('mysql'),
            ),
        );
    }

    /**
     * Lightweight AJAX handler for quick AI refinement messages
     * (e.g. recommendation item edits). Uses nano tier for speed.
     */
    public function ajax_handle_quick_message() {
        $nonce_value = '';
        foreach (array('security', '_ajax_nonce', 'nonce') as $nonce_field) {
            if (isset($_POST[$nonce_field])) {
                $nonce_value = sanitize_text_field(wp_unslash($_POST[$nonce_field]));
                break;
            }
        }
        if (!$nonce_value || !wp_verify_nonce($nonce_value, 'vmo_builder_nonce')) {
            wp_send_json_error(array('message' => 'Security validation failed.'), 403);
        }

        $message = sanitize_textarea_field(wp_unslash($_POST['message'] ?? ''));
        if (empty($message)) {
            wp_send_json_error(array('message' => 'No message provided.'));
        }

        $model = function_exists('vmo_get_model') ? vmo_get_model('nano') : 'gpt-5.5-nano';
        $result = function_exists('vmo_openai_chat') ? vmo_openai_chat(array(
            array('role' => 'system', 'content' => 'You are a concise business assistant. Respond with only the requested format. Be brief.'),
            array('role' => 'user', 'content' => $message),
        ), $model, 500) : null;

        if (is_wp_error($result) || empty($result)) {
            wp_send_json_error(array('message' => 'AI processing failed. Please try again.'));
        }

        wp_send_json_success(array(
            'reply'   => $result,
            'message' => $result,
        ));
    }

    /**
     * Check if user has existing business/projects
     */
    public function ajax_check_existing_business() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        $user_id = get_current_user_id();
        global $wpdb;
        
        // Check for existing projects
        $projects_table = $wpdb->prefix . 'vmo_projects';
        $consultations_table = $wpdb->prefix . 'vmo_consultations';
        
        $project_count = 0;
        $business_name = null;
        $last_project = null;
        
        // Check if tables exist
        $projects_exist = $wpdb->get_var("SHOW TABLES LIKE '$projects_table'") === $projects_table;
        $consultations_exist = $wpdb->get_var("SHOW TABLES LIKE '$consultations_table'") === $consultations_table;
        
        if ($projects_exist) {
            $project_count = $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM $projects_table WHERE user_id = %d AND status != 'deleted'",
                $user_id
            ));
            
            // Get most recent project info
            $last_project_row = $wpdb->get_row($wpdb->prepare(
                "SELECT project_name, created_at FROM $projects_table WHERE user_id = %d AND status != 'deleted' ORDER BY created_at DESC LIMIT 1",
                $user_id
            ));
            
            if ($last_project_row) {
                $business_name = $last_project_row->project_name;
                $last_project = $last_project_row->created_at;
            }
        }
        
        // Also check consultations table for business name if not found
        if (!$business_name && $consultations_exist) {
            $consultation_row = $wpdb->get_row($wpdb->prepare(
                "SELECT business_name FROM $consultations_table WHERE user_id = %d AND business_name IS NOT NULL AND business_name != '' ORDER BY updated_at DESC LIMIT 1",
                $user_id
            ));
            
            if ($consultation_row) {
                $business_name = $consultation_row->business_name;
            }
        }
        
        wp_send_json_success(array(
            'has_business' => $project_count > 0,
            'project_count' => intval($project_count),
            'business_name' => $business_name,
            'last_project' => $last_project
        ));
    }

    /**
     * Get consultation context/data from database (including uploaded documents)
     * ✅ CRITICAL FIX: This endpoint was missing, causing document context loss
        if (empty($session_id)) {
            wp_send_json_error(array('message' => 'Missing session_id'));
        }
        
        // Get consultation data from database
        $consultation = $this->consultation_db->get_consultation_by_session($session_id);
        
        if (!$consultation) {
            wp_send_json_error(array('message' => 'Consultation not found'));
        }
        
        // Decode consultation_data JSON
        $consultation_data = json_decode($consultation->consultation_data ?? '{}', true);
        if (!is_array($consultation_data)) {
            $consultation_data = array();
        }
        
        // Ensure all required keys exist with defaults
        $consultation_data = array_merge(array(
            'extracted_information' => array(),
            'documents' => array(),
            'media_files' => array(),
            'consultation_flow' => array(
                'current_phase_index' => 0,
                'phases' => $this->get_consultation_phases()
            ),
            'topicsCovered' => array(),
            'blueprintRaw' => null,
            'blueprintSummary' => null,
            'scenario_type' => 'scenario_a_new',
            'business_stage' => 'new_business',
            'discovery_profile' => array(),
            'business_opportunity_shortlist' => array(),
            'doc_locked' => false,
            'phase_flags' => array(
                'phase2_confirmed' => false,
                'phase3_completed' => false,
                'phase4_presented' => false
            ),
            'research_results' => null,
            'recommendation_checklist' => null
        ), $consultation_data);
        
        wp_send_json_success($consultation_data);
    }

    /**
     * ✅ Build the business context array needed for deep research
     * Extracts relevant business information from consultation state and payload
     */
    private function build_deep_research_context($consultation_state, $payload) {
        $context = array();

        // Prefer canonical extracted_information stored in consultation_state (snake_case)
        $ei_state = isset($consultation_state['extracted_information']) && is_array($consultation_state['extracted_information'])
            ? $consultation_state['extracted_information']
            : array();

        if (!empty($ei_state)) {
            if (!empty($ei_state['business_name'])) $context['business_name'] = $ei_state['business_name'];
            if (!empty($ei_state['business_idea'])) $context['business_idea'] = $ei_state['business_idea'];
            if (!empty($ei_state['industry'])) $context['industry'] = $ei_state['industry'];
            if (!empty($ei_state['location'])) $context['location'] = $ei_state['location'];
            if (!empty($ei_state['target_audience'])) $context['target_market'] = $ei_state['target_audience'];
            if (!empty($ei_state['target_market'])) $context['target_market'] = $ei_state['target_market'];
            if (!empty($ei_state['business_goals'])) $context['business_goal'] = $ei_state['business_goals'];
            if (!empty($ei_state['offerings'])) {
                if (is_array($ei_state['offerings'])) {
                    $context['offerings'] = implode(', ', array_filter(array_map('strval', $ei_state['offerings'])));
                } else {
                    $context['offerings'] = $ei_state['offerings'];
                }
            }

            // Derive business_type if needs are present
            if (!empty($ei_state['needs']) && is_array($ei_state['needs'])) {
                $needs = $ei_state['needs'];
                if (!empty($needs['ecommerce']) || !empty($needs['payments'])) {
                    $context['business_type'] = 'ecommerce';
                }
            }
        }
        
        // Extract from business_profile in consultation_state
        $bp = isset($consultation_state['business_profile']) && is_array($consultation_state['business_profile'])
            ? $consultation_state['business_profile']
            : array();
        $bp_identity = isset($bp['identity']) && is_array($bp['identity']) ? $bp['identity'] : array();
        $bp_vision = isset($bp['vision']) && is_array($bp['vision']) ? $bp['vision'] : array();
        $bp_audience = isset($bp['audience']) && is_array($bp['audience']) ? $bp['audience'] : array();
        $bp_operations = isset($bp['operations']) && is_array($bp['operations']) ? $bp['operations'] : array();
        $bp_web_requirements = isset($bp['web_requirements']) && is_array($bp['web_requirements'])
            ? $bp['web_requirements']
            : array();
        
        // Identity information
        if (!empty($bp_identity)) {
            $context['business_name'] = $bp_identity['business_name'] ?? '';
            $context['tagline'] = $bp_identity['tagline'] ?? '';
            $context['location'] = $bp_identity['hq_location'] ?? '';
            $context['legal_structure'] = $bp_identity['legal_structure'] ?? '';
        }
        
        // Vision and mission
        if (!empty($bp_vision)) {
            $context['mission'] = $bp_vision['mission'] ?? '';
            $context['vision'] = $bp_vision['vision'] ?? '';
            $context['core_values'] = $bp_vision['core_values'] ?? array();
        }
        
        // Target audience
        if (!empty($bp_audience)) {
            $primary_segments = isset($bp_audience['primary_segments']) && is_array($bp_audience['primary_segments'])
                ? $bp_audience['primary_segments']
                : array();
            $pain_points = isset($bp_audience['pain_points']) && is_array($bp_audience['pain_points'])
                ? $bp_audience['pain_points']
                : array();
            $geographies = isset($bp_audience['geographies']) && is_array($bp_audience['geographies'])
                ? $bp_audience['geographies']
                : array();

            $context['target_market'] = implode(', ', $primary_segments);
            $context['pain_points'] = implode(', ', $pain_points);
            $context['geographies'] = implode(', ', $geographies);
        }
        
        // Offers/Services
        if (!empty($bp['offers']) && is_array($bp['offers'])) {
            $offer_names = array_map(function($o) {
                return is_array($o) ? ($o['name'] ?? $o['title'] ?? '') : $o;
            }, $bp['offers']);
            $context['offerings'] = implode(', ', array_filter($offer_names));
        }
        
        // Operations
        if (!empty($bp_operations)) {
            $context['operating_model'] = $bp_operations['fulfillment_model'] ?? '';
        }
        
        // Web requirements (determines business type)
        if (!empty($bp_web_requirements)) {
            if (!empty($bp_web_requirements['ecommerce'])) {
                $context['business_type'] = 'ecommerce';
            } elseif (!empty($bp_web_requirements['booking'])) {
                $context['business_type'] = 'service';
            } else {
                $context['business_type'] = 'service';
            }
        }
        
        // Extract from document_store (blueprint summary often has rich info)
        $ds = isset($consultation_state['document_store']) && is_array($consultation_state['document_store'])
            ? $consultation_state['document_store']
            : array();
        if (!empty($ds['blueprint_summary'])) {
            $context['blueprint_summary'] = $ds['blueprint_summary'];
        }
        if (!empty($ds['blueprint_raw'])) {
            $context['blueprint_raw'] = substr($ds['blueprint_raw'], 0, 5000); // Limit size
        }
        
        // Merge with frontend payload (extracted_information from JS)
        if (!empty($payload['extracted_information']) && is_array($payload['extracted_information'])) {
            $ei = $payload['extracted_information'];
            if (!empty($ei['businessName']) && empty($context['business_name'])) {
                $context['business_name'] = $ei['businessName'];
            }
            if (!empty($ei['businessIdea']) && empty($context['business_idea'])) {
                $context['business_idea'] = $ei['businessIdea'];
            }
            if (!empty($ei['industry'])) {
                $context['industry'] = $ei['industry'];
            }
            if (!empty($ei['location']) && empty($context['location'])) {
                $context['location'] = $ei['location'];
            }
            if (!empty($ei['services'])) {
                $context['services'] = $ei['services'];
            }
            if (!empty($ei['targetAudience']) && empty($context['target_market'])) {
                $context['target_market'] = $ei['targetAudience'];
            }
            if (!empty($ei['goals'])) {
                $context['business_goal'] = $ei['goals'];
            }
        }
        
        // Also check direct payload fields
        if (!empty($payload['business_name']) && empty($context['business_name'])) {
            $context['business_name'] = $payload['business_name'];
        }
        if (!empty($payload['industry']) && empty($context['industry'])) {
            $context['industry'] = $payload['industry'];
        }
        
        // Ensure we have at least some context
        if (empty($context['business_name']) && empty($context['industry']) && empty($context['offerings'])) {
            // Try to get from research_profile if already partially populated
            if (!empty($consultation_state['research_profile'])) {
                $rp = $consultation_state['research_profile'];
                if (!empty($rp['business_name'])) $context['business_name'] = $rp['business_name'];
                if (!empty($rp['industry'])) $context['industry'] = $rp['industry'];
            }
        }
        
        // Set defaults for required fields
        if (empty($context['business_name'])) $context['business_name'] = 'Business';
        if (empty($context['business_type'])) $context['business_type'] = 'service';
        if (!isset($context['industry'])) $context['industry'] = '';

        // If offerings are empty but we have a business idea, use it as offerings context.
        if (empty($context['offerings']) && !empty($context['business_idea'])) {
            $context['offerings'] = $context['business_idea'];
        }
        
        error_log('🔍 VMO build_deep_research_context: Built context with keys: ' . implode(', ', array_keys($context)));
        
        return $context;
    }

    /**
     * ✅ NEW: Trigger comprehensive deep research directly from the consultation wizard.
     * ✅ BULLETPROOF v2.0: Output buffering + comprehensive error handling
     */
    public function ajax_trigger_deep_research() {
        $this->prepare_long_running_runtime();

        // ✅ BULLETPROOF: Start output buffering to capture ANY stray output
        ob_start();
        
        try {
            $has_nonce = check_ajax_referer('vmo_builder_nonce', 'security', false);
            if (!$has_nonce) {
                ob_end_clean();
                wp_send_json_error(array('message' => 'Invalid security token'), 403);
                return;
            }
            
            $user_id = get_current_user_id();

            $session_id = sanitize_text_field($_POST['session_id'] ?? '');
            if (empty($session_id)) {
                ob_end_clean();
                wp_send_json_error(array('message' => 'Missing session identifier'), 400);
                return;
            }
            
            // ✅ CHUNKED RESEARCH: Get chunk number from request (0 = auto-continue)
            $chunk = isset($_POST['chunk']) ? intval($_POST['chunk']) : 0;

            $payload_raw = isset($_POST['consultation_data']) ? wp_unslash($_POST['consultation_data']) : '{}';
            $payload = json_decode($payload_raw, true);
            if (!is_array($payload)) {
                $payload = array();
            }

            list($row, $consultation_state) = $this->hydrate_consultation_state($session_id);
            if (!is_array($consultation_state)) {
                $consultation_state = $this->consultation_blueprint;
            }

            // ✅ Phase gate: require Phase 2 explicit confirmation before any deep research
            $phase_flags = isset($consultation_state['phase_flags']) && is_array($consultation_state['phase_flags'])
                ? $consultation_state['phase_flags']
                : array();
            if (empty($phase_flags['phase2_confirmed'])) {
                ob_end_clean();
                wp_send_json_error(array(
                    'message' => 'Please confirm your business summary (Phase 2) before starting deep research.'
                ), 422);
                return;
            }

            $business_context = $this->build_deep_research_context($consultation_state, $payload);
            if (empty($business_context)) {
                ob_end_clean();
                wp_send_json_error(array('message' => 'Please confirm your business summary before running research.'), 422);
                return;
            }

            // ✅ Required-field gate: try to infer industry from blueprint/document before rejecting
            $industry = isset($business_context['industry']) ? trim((string)$business_context['industry']) : '';
            if ($industry === '' || strtolower($industry) === 'general' || strtolower($industry) === 'your industry') {
                // ✅ FIX: Try to extract industry from the blueprint document or conversation before giving up
                $blueprint_raw = isset($business_context['blueprint_raw']) ? $business_context['blueprint_raw'] : '';
                $blueprint_summary = isset($business_context['blueprint_summary']) ? $business_context['blueprint_summary'] : '';
                $doc_raw = isset($consultation_state['document_store']['blueprint_raw']) ? $consultation_state['document_store']['blueprint_raw'] : '';
                $full_text = $blueprint_raw . ' ' . $blueprint_summary . ' ' . $doc_raw;
                
                // Look for industry patterns in the document
                $inferred_industry = '';
                if (preg_match('/industry[:\s]+([^\n\r.]{3,80})/i', $full_text, $m)) {
                    $inferred_industry = trim($m[1]);
                } elseif (preg_match('/niche[:\s]+([^\n\r.]{3,80})/i', $full_text, $m)) {
                    $inferred_industry = trim($m[1]);
                } elseif (preg_match('/(?:SaaS|e-?commerce|healthcare|fintech|edtech|real estate|restaurant|consulting|marketing|retail|fitness)/i', $full_text, $m)) {
                    $inferred_industry = $m[0];
                }
                
                // Also check conversation history for industry mentions
                if (empty($inferred_industry)) {
                    $history = isset($consultation_state['conversation_history']) ? $consultation_state['conversation_history'] : array();
                    foreach (array_reverse($history) as $msg) {
                        $text = is_array($msg) ? ($msg['content'] ?? $msg['message'] ?? '') : (string)$msg;
                        if (preg_match('/industry[:\s]+([^\n\r.]{3,80})/i', $text, $m)) {
                            $inferred_industry = trim($m[1]);
                            break;
                        }
                    }
                }
                
                if (!empty($inferred_industry) && strtolower($inferred_industry) !== 'general') {
                    $business_context['industry'] = $inferred_industry;
                    error_log('🔍 VMO: Industry inferred from document/conversation: ' . $inferred_industry);
                } else {
                    // ✅ Last resort: if Phase 2 was confirmed, the AI already validated the business info
                    // Let the research proceed with whatever context we have — the AI will determine industry
                    if (!empty($phase_flags['phase2_confirmed'])) {
                        $business_context['industry'] = !empty($business_context['business_name']) 
                            ? $business_context['business_name'] . ' industry' 
                            : 'General Business';
                        error_log('🔍 VMO: Industry not found but Phase 2 confirmed — proceeding with fallback: ' . $business_context['industry']);
                    } else {
                        ob_end_clean();
                        wp_send_json_error(array(
                            'message' => 'Industry/Niche is required. Please specify and confirm your industry in Phase 2 before starting deep research.'
                        ), 422);
                        return;
                    }
                }
            }

            $deep_research = $this->get_deep_research_service();
            if (!$deep_research) {
                ob_end_clean();
                wp_send_json_error(array('message' => 'Deep research service is unavailable'), 500);
                return;
            }

            // ✅ TOKEN DEDUCTION FIX: Charge deep research only once per session.
            // Frontend executes 26 chunk calls; charging per chunk drains tokens unfairly.
            $phase_flags = isset($consultation_state['phase_flags']) && is_array($consultation_state['phase_flags'])
                ? $consultation_state['phase_flags']
                : array();
            $prepaid_research = !empty($phase_flags['phase3_research_tokens_charged']);

            if (!$prepaid_research && function_exists('vmo_deduct_tokens_for_operation')) {
                if (!vmo_deduct_tokens_for_operation($user_id, 'deep_research')) {
                    ob_end_clean();
                    wp_send_json_error(array(
                        'message' => 'Insufficient tokens for deep research. Please top up your balance.',
                        'status' => 'insufficient_tokens',
                        'tokens_required' => function_exists('vmo_get_token_cost') ? vmo_get_token_cost('deep_research') : 13500
                    ));
                    return;
                }

                $phase_flags['phase3_research_tokens_charged'] = true;
                $phase_flags['phase3_research_tokens_charged_at'] = current_time('mysql');
                $consultation_state['phase_flags'] = $phase_flags;
                $this->persist_consultation_state($session_id, $consultation_state);
                $prepaid_research = true;
            }

            if ($prepaid_research) {
                $business_context['_skip_internal_token_deduction'] = true;
            }

            // ✅ CHUNKED: Pass chunk number to research method
            $results = $deep_research->conduct_deep_research($business_context, $chunk);
            if (!is_array($results)) {
                $results = array(
                    'analysis' => is_string($results) ? $results : 'Deep research returned an unexpected response format.',
                    'is_complete' => false,
                    'current_chunk' => max(1, intval($chunk ?: 1)),
                    'total_chunks' => 26
                );
            }
            
            // ✅ Check if all chunks are complete
            $is_complete = !empty($results['is_complete']);
            $current_chunk = isset($results['current_chunk']) ? max(1, intval($results['current_chunk'])) : max(1, intval($chunk ?: 1));
            $total_chunks = isset($results['total_chunks']) ? max(1, intval($results['total_chunks'])) : 26;
            if ($current_chunk > $total_chunks) {
                $current_chunk = $total_chunks;
            }

            // Only update phase flags if fully complete
            if ($is_complete) {
                if (!isset($consultation_state['phase_flags']) || !is_array($consultation_state['phase_flags'])) {
                    $consultation_state['phase_flags'] = array();
                }
                $consultation_state['phase_flags']['phase3_completed'] = true;
                $consultation_state['research_results'] = $results;

                if (!isset($consultation_state['consultation_flow']) || !is_array($consultation_state['consultation_flow'])) {
                    $consultation_state['consultation_flow'] = isset($this->consultation_blueprint['consultation_flow']) && is_array($this->consultation_blueprint['consultation_flow'])
                        ? $this->consultation_blueprint['consultation_flow']
                        : array();
                }
                if (!isset($consultation_state['consultation_flow']['phases']) || !is_array($consultation_state['consultation_flow']['phases'])) {
                    $consultation_state['consultation_flow']['phases'] = isset($this->consultation_blueprint['consultation_flow']['phases']) && is_array($this->consultation_blueprint['consultation_flow']['phases'])
                        ? $this->consultation_blueprint['consultation_flow']['phases']
                        : array();
                }

                $current_index = isset($consultation_state['consultation_flow']['current_index'])
                    ? intval($consultation_state['consultation_flow']['current_index'])
                    : 0;
                if ($current_index < 2) {
                    $consultation_state['consultation_flow']['current_index'] = 2;
                }
                if (isset($consultation_state['consultation_flow']['phases'][2]) && is_array($consultation_state['consultation_flow']['phases'][2])) {
                    $consultation_state['consultation_flow']['phases'][2]['status'] = 'completed';
                    $consultation_state['consultation_flow']['phases'][2]['completed_at'] = current_time('mysql');
                }

                $this->persist_consultation_state($session_id, $consultation_state);
            }

            $progress_percent = round(($current_chunk / max(1, $total_chunks)) * 100);

            // ✅ Clear any stray output before sending JSON
            ob_end_clean();
            
            wp_send_json_success(array(
                'research_results' => $results,
                'phase_flags' => $consultation_state['phase_flags'] ?? array(),
                'is_complete' => $is_complete,
                'current_chunk' => $current_chunk,
                'total_chunks' => $total_chunks,
                'progress_percent' => $progress_percent
            ));
            
        } catch (\Throwable $e) {
            $stray_output = ob_get_clean();
            if (!empty($stray_output)) {
                error_log('VMO Deep Research: Stray output captured: ' . substr($stray_output, 0, 500));
            }
            error_log('VMO Deep Research: Exception: ' . $e->getMessage());
            wp_send_json_error(array(
                'message' => 'Unable to complete deep research right now.',
                'error' => defined('WP_DEBUG') && WP_DEBUG ? $e->getMessage() : 'An error occurred'
            ), 500);
        }
    }
    
    /**
     * ✅ NEW: Populate deliverables after Phase 8 completion
     * This includes: CRM leads, SEO roadmap, Business documents, Payment setup, Shipping config
     */
    public function ajax_populate_deliverables() {
        $this->prepare_long_running_runtime();

        check_ajax_referer('vmo_builder_nonce', 'security');
        
        $session_id = sanitize_text_field($_POST['session_id'] ?? '');
        $consultation_data = json_decode(wp_unslash($_POST['consultation_data'] ?? '{}'), true);
        
        if (empty($session_id)) {
            wp_send_json_error(array('message' => 'Missing session_id'));
        }
        
        $user_id = get_current_user_id();
        if ($user_id <= 0) {
            wp_send_json_error(array('message' => 'You must be logged in to populate consultation deliverables.'), 403);
        }
        
        try {
            $results = $this->populate_phase8_deliverables($user_id, $session_id, is_array($consultation_data) ? $consultation_data : array());
            wp_send_json_success($results);
            
        } catch (\Throwable $e) {
            error_log('VMO Deliverables Population Error: ' . $e->getMessage());
            wp_send_json_error(array('message' => 'Failed to populate deliverables: ' . $e->getMessage()));
        }
    }

    public function ajax_generate_launch_tour_audio() {
        $this->prepare_long_running_runtime();

        check_ajax_referer('vmo_builder_nonce', 'security');

        $session_id = sanitize_text_field($_POST['session_id'] ?? '');
        $consultation_data = json_decode(wp_unslash($_POST['consultation_data'] ?? '{}'), true);
        $user_id = get_current_user_id();

        if ($user_id <= 0) {
            wp_send_json_error(array('message' => 'You must be logged in to generate the launch tour.'), 403);
        }
        if ($session_id === '') {
            wp_send_json_error(array('message' => 'Missing session_id for launch tour generation.'), 400);
        }

        try {
            $payload = $this->build_phase8_launch_tour_deliverable($user_id, $session_id, is_array($consultation_data) ? $consultation_data : array(), true);
            if (empty($payload['audio_ready']) || empty($payload['audio_url'])) {
                wp_send_json_error(array_merge(array('message' => $payload['reason'] ?? 'Launch tour audio could not be generated.'), $payload), 500);
            }
            wp_send_json_success($payload);
        } catch (\Throwable $e) {
            error_log('VMO Launch Tour Audio Error: ' . $e->getMessage());
            wp_send_json_error(array('message' => 'Launch tour audio failed: ' . $e->getMessage()), 500);
        }
    }

    private function populate_phase8_deliverables($user_id, $session_id, array $consultation_data = array()) {
        $user_id = (int) $user_id;
        $session_id = sanitize_text_field((string) $session_id);
        if ($user_id <= 0 || $session_id === '') {
            throw new \RuntimeException('Missing user or session for Phase 8 deliverables.');
        }

        $consultation_data = $this->hydrate_phase8_consultation_data($session_id, $consultation_data);
        $business_context = $this->build_phase8_business_context($consultation_data);
        $research_results = $this->resolve_consultation_research_results($user_id, $session_id, $business_context);
        $results = array(
            'status' => 'processing',
            'documents' => array('generated' => 0),
            'legal_documents' => array('generated' => 0),
            'crm_initialized' => array('status' => 'blocked'),
            'leads' => array('status' => 'blocked', 'count' => 0),
            'today' => array('actions_created' => 0, 'daily_activities_created' => 0),
        );

        if (empty($consultation_data['roadmap_json']) || empty($consultation_data['daily_tasks_json']) || empty($consultation_data['systems_plan'])) {
            try {
                $consultation_data = $this->generate_phase7_artifacts($session_id, $consultation_data);
                $business_context = $this->build_phase8_business_context($consultation_data);
            } catch (\Throwable $e) {
                error_log('VMO Phase 8: Phase 7 artifact generation failed: ' . $e->getMessage());
            }
        }

        $website_result = $this->extract_phase8_website_result($user_id, $session_id, $consultation_data);
        try {
            $manifest = $this->generate_business_operating_manifest($user_id, $session_id, $business_context, array(), $website_result, $research_results);
            if (is_array($manifest) && !empty($manifest)) {
                $results['business_manifest'] = array('status' => 'ready', 'manifest_id' => $manifest['manifest_id'] ?? '');
            }
        } catch (\Throwable $e) {
            $results['business_manifest'] = array('status' => 'needs_attention', 'reason' => $e->getMessage());
            error_log('VMO Phase 8: Manifest generation failed: ' . $e->getMessage());
        }

        if (function_exists('vmo_crm_initialize_from_consultation')) {
            try {
                $results['crm_initialized'] = vmo_crm_initialize_from_consultation($user_id, $session_id, $business_context, $consultation_data, $research_results);
            } catch (\Throwable $e) {
                $results['crm_initialized'] = array('status' => 'needs_attention', 'reason' => $e->getMessage());
                error_log('VMO Phase 8: CRM blueprint initialization failed: ' . $e->getMessage());
            }
        }

        if (class_exists('VMO_Document_Generator')) {
            try {
                $documents = (new VMO_Document_Generator())->generate_all_documents($user_id, $business_context, $research_results);
                $results['documents'] = array(
                    'generated' => is_array($documents) ? count($documents) : 0,
                    'types' => is_array($documents) ? array_values(array_filter(array_map(function($doc) {
                        return is_array($doc) ? sanitize_key($doc['type'] ?? '') : '';
                    }, $documents))) : array(),
                );
            } catch (\Throwable $e) {
                $results['documents'] = array('generated' => 0, 'status' => 'needs_attention', 'reason' => $e->getMessage());
                error_log('VMO Phase 8: Business document generation failed: ' . $e->getMessage());
            }
        }

        if (class_exists('VMO_Legal') && method_exists('VMO_Legal', 'get_instance')) {
            try {
                $legal_types = array('privacy_policy', 'terms_of_service', 'sms_policy', 'email_policy', 'refund_policy');
                if ($this->phase8_requires_shipping_policy($business_context)) {
                    $legal_types[] = 'shipping_policy';
                }
                $results['legal_documents'] = VMO_Legal::get_instance()->generate_required_documents($user_id, $business_context, $legal_types);
            } catch (\Throwable $e) {
                $results['legal_documents'] = array('generated' => 0, 'status' => 'needs_attention', 'reason' => $e->getMessage());
                error_log('VMO Phase 8: Legal document generation failed: ' . $e->getMessage());
            }
        }

        $results['leads'] = $this->maybe_generate_initial_consultation_leads($user_id, $session_id, $business_context);
        $results['today'] = $this->populate_phase8_today_tab($user_id, $session_id, $consultation_data, $business_context);
        $results['email_ready'] = $this->build_phase8_email_readiness($user_id, $session_id, $consultation_data);
        $results['phone_ready'] = $this->build_phase8_phone_readiness($user_id, $session_id);
        $results['payments_ready'] = $this->build_phase8_payment_readiness($user_id, $session_id, $consultation_data, $business_context);
        $results['launch_tour'] = $this->build_phase8_launch_tour_deliverable($user_id, $session_id, $consultation_data, false);

        $gate = $this->evaluate_phase8_deliverables_gate($user_id, $session_id, $consultation_data);
        $results['gate'] = $gate;
        $results['status'] = !empty($gate['launch_ready']) ? 'ready' : 'needs_attention';
        $results['missing_items'] = $gate['missing_items'] ?? array();

        if (!isset($consultation_data['approvals']) || !is_array($consultation_data['approvals'])) {
            $consultation_data['approvals'] = array();
        }
        $consultation_data['phase8_deliverables'] = $results;
        $consultation_data['approvals']['artifacts_generated'] = !empty($gate['launch_ready']);
        $consultation_data['approvals']['phase8_deliverables_orchestrated'] = true;
        $consultation_data['approvals']['phase8_deliverables_orchestrated_at'] = current_time('mysql');
        $this->persist_consultation_state($session_id, $consultation_data);

        return $results;
    }

    private function hydrate_phase8_consultation_data($session_id, array $consultation_data = array()) {
        try {
            list($row, $hydrated) = $this->hydrate_consultation_state($session_id);
            if (is_array($hydrated) && !empty($hydrated)) {
                return !empty($consultation_data) ? array_replace_recursive($hydrated, $consultation_data) : $hydrated;
            }
        } catch (\Throwable $e) {
            error_log('VMO Phase 8: Could not hydrate consultation data: ' . $e->getMessage());
        }
        return $consultation_data;
    }

    private function build_phase8_business_context(array $consultation_data) {
        $ei = is_array($consultation_data['extracted_information'] ?? null) ? $consultation_data['extracted_information'] : array();
        $bp = is_array($consultation_data['business_profile'] ?? null) ? $consultation_data['business_profile'] : array();
        $identity = is_array($bp['identity'] ?? null) ? $bp['identity'] : array();
        $audience = is_array($bp['audience'] ?? null) ? $bp['audience'] : array();

        $context = array_replace_recursive($consultation_data, $ei);
        $context['business_name'] = sanitize_text_field((string) ($ei['business_name'] ?? $ei['businessName'] ?? $identity['business_name'] ?? $consultation_data['business_name'] ?? ''));
        $context['business_type'] = sanitize_text_field((string) ($ei['business_type'] ?? $ei['businessType'] ?? $identity['business_type'] ?? $consultation_data['business_type'] ?? ''));
        $context['industry'] = sanitize_text_field((string) ($ei['industry'] ?? $identity['industry'] ?? $consultation_data['industry'] ?? ''));
        $context['location'] = sanitize_text_field((string) ($ei['location'] ?? $identity['location'] ?? $consultation_data['location'] ?? ''));
        $context['target_audience'] = $ei['target_audience'] ?? $ei['targetAudience'] ?? $audience['primary_segments'] ?? ($consultation_data['target_audience'] ?? '');
        $context['offerings'] = $ei['offerings'] ?? $bp['offers'] ?? $consultation_data['offerings'] ?? array();
        $context['products_inventory'] = is_array($ei['products_inventory'] ?? null) ? $ei['products_inventory'] : array();
        $context['services_inventory'] = is_array($ei['services_inventory'] ?? null) ? $ei['services_inventory'] : array();
        $context['shipping_requirements'] = sanitize_text_field((string) ($ei['shipping_requirements'] ?? $consultation_data['shipping_requirements'] ?? ''));
        $context['payment_requirements'] = sanitize_text_field((string) ($ei['payment_requirements'] ?? $consultation_data['payment_requirements'] ?? ''));
        $context['email_roles'] = is_array($consultation_data['email_roles'] ?? null) ? $consultation_data['email_roles'] : array();

        return $context;
    }

    private function build_phase8_launch_tour_deliverable($user_id, $session_id, array $consultation_data, $force = false) {
        $existing_script = get_user_meta($user_id, 'vmo_launch_tour_script_' . $session_id, true);
        $existing_audio = get_user_meta($user_id, 'vmo_launch_tour_audio_url_' . $session_id, true);
        $existing_state = get_user_meta($user_id, 'vmo_launch_tour_state_' . $session_id, true);

        if (!$force && is_array($existing_state) && !empty($existing_state['script_ready']) && !empty($existing_state['audio_ready']) && !empty($existing_audio)) {
            return $existing_state;
        }

        $consultation_data = $this->hydrate_phase8_consultation_data($session_id, $consultation_data);
        $business_context = $this->build_phase8_business_context($consultation_data);
        $website_result = $this->extract_phase8_website_result($user_id, $session_id, $consultation_data);
        $gate = get_user_meta($user_id, 'vmo_phase8_deliverables_gate_' . $session_id, true);
        $script = $existing_script && !$force ? (string) $existing_script : $this->compose_phase8_launch_tour_script($business_context, $website_result, is_array($gate) ? $gate : array());

        update_user_meta($user_id, 'vmo_launch_tour_script_' . $session_id, $script);
        update_user_meta($user_id, 'vmo_launch_tour_script', $script);

        $audio = $existing_audio && !$force ? array('url' => $existing_audio, 'attachment_id' => intval(get_user_meta($user_id, 'vmo_launch_tour_audio_attachment_id_' . $session_id, true))) : $this->synthesize_phase8_launch_tour_audio($user_id, $session_id, $script);

        $state = array(
            'status' => !empty($audio['url']) ? 'ready' : 'needs_attention',
            'script_ready' => !empty($script),
            'audio_ready' => !empty($audio['url']),
            'script' => $script,
            'audio_url' => esc_url_raw($audio['url'] ?? ''),
            'attachment_id' => intval($audio['attachment_id'] ?? 0),
            'dashboard_walkthrough_steps' => array('Overview', 'Today', 'CRM / Leads', 'Documents', 'KPI Dashboard', 'VMO Email', 'Phone / SMS', 'VMO Pay / Orders'),
            'website_walkthrough_steps' => array('Review each generated page', 'Use replace buttons in the editor', 'Confirm forms and media', 'Publish only after the final gate passes'),
            'message' => !empty($audio['url']) ? 'Your final launch tour is ready. I will walk you through the website, dashboard, and next actions.' : 'The launch tour script is ready, but audio generation still needs attention.',
            'reason' => sanitize_text_field((string) ($audio['error'] ?? '')),
            'updated_at' => current_time('mysql'),
        );

        update_user_meta($user_id, 'vmo_launch_tour_state_' . $session_id, $state);
        update_user_meta($user_id, 'vmo_launch_tour_state', $state);
        if (!empty($state['audio_url'])) {
            update_user_meta($user_id, 'vmo_launch_tour_audio_url_' . $session_id, $state['audio_url']);
            update_user_meta($user_id, 'vmo_launch_tour_audio_url', $state['audio_url']);
            update_user_meta($user_id, 'vmo_launch_tour_audio_attachment_id_' . $session_id, $state['attachment_id']);
        }

        $consultation_data['launch_tour'] = $state;
        $this->persist_consultation_state($session_id, $consultation_data);

        return $state;
    }

    private function compose_phase8_launch_tour_script(array $business_context, array $website_result, array $gate) {
        $business_name = sanitize_text_field((string) ($business_context['business_name'] ?? 'your business'));
        $pages = array();
        if (!empty($website_result['pages_map']) && is_array($website_result['pages_map'])) {
            $pages = array_keys($website_result['pages_map']);
        } elseif (!empty($website_result['pages_created']) && is_array($website_result['pages_created'])) {
            $pages = array_map(function($page) {
                return is_array($page) ? ($page['title'] ?? $page['slug'] ?? 'Page') : (string) $page;
            }, $website_result['pages_created']);
        }
        $page_summary = !empty($pages) ? implode(', ', array_slice(array_map('sanitize_text_field', $pages), 0, 10)) : 'your generated website pages';
        $gate_ready = !empty($gate['launch_ready']);
        $gate_phrase = $gate_ready ? 'The final launch gate is passing.' : 'The final launch gate is still checking a few items, so I will keep the consultation open until every item is verified.';

        return trim("Welcome back. Your VMO launch workspace for {$business_name} is ready for review. I built the website structure and connected it to your business dashboard. The website pages include {$page_summary}. In the editor, you can review every section, replace images, videos, logos, or audio, and publish only after the final readiness gate passes. In your dashboard, start with Today for the next launch actions. Then review CRM and Leads, Documents, KPI targets, VMO Email, Phone and SMS, and VMO Pay or Orders if this business accepts payments. Your documents include strategy, marketing, SEO, policies, and setup guidance where required. Your KPI dashboard tracks the business goals generated from the consultation. {$gate_phrase} I will continue guiding you step by step so nothing is skipped, silently failed, or marked complete before it is truly ready.");
    }

    private function synthesize_phase8_launch_tour_audio($user_id, $session_id, $script) {
        $api_key = get_option('vmo_openai_api_key', '');
        if (!$api_key) {
            return array('error' => 'OpenAI API key is missing, so launch tour audio cannot be generated yet.');
        }

        $response = wp_remote_post('https://api.openai.com/v1/audio/speech', array(
            'timeout' => 120,
            'headers' => array(
                'Authorization' => 'Bearer ' . $api_key,
                'Content-Type' => 'application/json',
            ),
            'body' => wp_json_encode(array(
                'model' => 'tts-1-hd',
                'input' => $script,
                'voice' => 'ash',
                'response_format' => 'mp3',
                'speed' => 1.0,
            )),
        ));

        if (is_wp_error($response)) {
            return array('error' => $response->get_error_message());
        }

        $code = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);
        if ($code < 200 || $code >= 300 || $body === '') {
            return array('error' => 'Launch tour TTS returned an error.');
        }

        if (!function_exists('wp_upload_bits')) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
        }
        $filename = sanitize_file_name('vmo-launch-tour-' . $session_id . '-' . time() . '.mp3');
        $upload = wp_upload_bits($filename, null, $body);
        if (!empty($upload['error']) || empty($upload['file']) || empty($upload['url'])) {
            return array('error' => $upload['error'] ?? 'Could not save launch tour audio.');
        }

        if (!function_exists('wp_insert_attachment') || !function_exists('wp_generate_attachment_metadata')) {
            require_once ABSPATH . 'wp-admin/includes/image.php';
            require_once ABSPATH . 'wp-admin/includes/media.php';
        }

        $attachment_id = wp_insert_attachment(array(
            'post_mime_type' => 'audio/mpeg',
            'post_title' => 'VMO Launch Tour - ' . $session_id,
            'post_content' => '',
            'post_status' => 'inherit',
            'post_author' => intval($user_id),
        ), $upload['file']);

        if (!is_wp_error($attachment_id) && $attachment_id) {
            wp_update_attachment_metadata($attachment_id, wp_generate_attachment_metadata($attachment_id, $upload['file']));
        }

        return array(
            'url' => esc_url_raw($upload['url']),
            'attachment_id' => is_wp_error($attachment_id) ? 0 : intval($attachment_id),
        );
    }

    private function extract_phase8_website_result($user_id, $session_id, array $consultation_data) {
        $website_result = is_array($consultation_data['website'] ?? null) ? $consultation_data['website'] : array();
        if (empty($website_result) && is_array($consultation_data['website_data']['website'] ?? null)) {
            $website_result = $consultation_data['website_data']['website'];
        }
        if (empty($website_result)) {
            $operating = get_user_meta($user_id, 'vmo_business_operating_state_' . $session_id, true);
            if (is_array($operating) && !empty($operating['website'])) {
                $website_result = $operating['website'];
            }
        }
        return is_array($website_result) ? $website_result : array();
    }

    private function evaluate_phase8_deliverables_gate($user_id, $session_id, array $consultation_data) {
        if (!class_exists('VMO_Consultation_Deliverables_Gate')) {
            $gate_file = plugin_dir_path(__FILE__) . 'class-vmo-consultation-deliverables-gate.php';
            if (file_exists($gate_file)) {
                require_once $gate_file;
            }
        }
        if (!class_exists('VMO_Consultation_Deliverables_Gate')) {
            return array(
                'status' => 'needs_attention',
                'launch_ready' => false,
                'missing_items' => array(array('key' => 'deliverables_gate', 'label' => 'Deliverables gate', 'reason' => 'Gate class is unavailable.')),
                'checks' => array(),
                'evaluated_at' => current_time('mysql'),
            );
        }
        return VMO_Consultation_Deliverables_Gate::get_instance()->evaluate($user_id, $session_id, $consultation_data);
    }

    private function append_phase8_recovery_guidance($ai_response, array $gate) {
        $ai_response = trim((string) $ai_response);
        $missing = is_array($gate['missing_items'] ?? null) ? $gate['missing_items'] : array();
        if (empty($missing)) {
            return $ai_response;
        }

        $lines = array();
        $lines[] = 'I ran the final deliverables check. The consultation can continue, but I cannot mark it complete yet because a few launch items still need attention. We will handle them one at a time, then I will re-run the final deliverables check.';

        $max_items = min(5, count($missing));
        for ($index = 0; $index < $max_items; $index++) {
            $item = is_array($missing[$index]) ? $missing[$index] : array();
            $resolution = is_array($item['resolution'] ?? null) ? $item['resolution'] : array();
            $label = sanitize_text_field((string) ($item['label'] ?? 'Launch item'));
            $reason = sanitize_text_field((string) ($item['reason'] ?? 'Needs attention'));
            $action = sanitize_text_field((string) ($resolution['action'] ?? 'I need to collect the missing detail and verify this item again.'));
            $lines[] = ($index + 1) . '. ' . $label . ': ' . $reason . ' Next: ' . $action;
        }

        if (count($missing) > $max_items) {
            $lines[] = 'There are ' . (count($missing) - $max_items) . ' more item(s) after these; I will keep the order simple and continue after the first group is resolved.';
        }

        $next_item = is_array($gate['recovery_plan']['next_item'] ?? null) ? $gate['recovery_plan']['next_item'] : (is_array($missing[0] ?? null) ? $missing[0] : array());
        $next_resolution = is_array($next_item['resolution'] ?? null) ? $next_item['resolution'] : array();
        $question = sanitize_text_field((string) ($next_resolution['question'] ?? 'Can you confirm the missing details for the first launch item?'));
        $lines[] = 'First, let us resolve: ' . sanitize_text_field((string) ($next_item['label'] ?? 'the first missing launch item')) . '.';
        $lines[] = $question;

        $guidance = implode("\n", $lines);
        $lower_response = strtolower($ai_response);
        $overpromised_launch = strpos($lower_response, 'ready to launch') !== false
            || strpos($lower_response, 'fully configured') !== false
            || strpos($lower_response, 'go live') !== false
            || strpos($lower_response, 'business is ready') !== false;

        if ($ai_response === '' || $overpromised_launch) {
            return $guidance;
        }

        if (strpos($ai_response, 'I ran the final deliverables check') !== false) {
            return $ai_response;
        }

        return rtrim($ai_response) . "\n\n" . $guidance;
    }

    private function populate_phase8_today_tab($user_id, $session_id, array $consultation_data, array $business_context) {
        global $wpdb;
        $created_actions = 0;
        $created_activities = 0;
        $business_name = $business_context['business_name'] ?: 'the business';

        $actions_table = $wpdb->prefix . 'vmo_actions';
        if ($this->builder_table_exists($actions_table)) {
            $existing = (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$actions_table} WHERE user_id = %d AND source = %s AND status IN ('pending','in_progress')", $user_id, 'consultation_launch'));
            if ($existing === 0) {
                $tasks = array(
                    array('Review generated website preview for ' . $business_name, 'Confirm content, forms, media, and launch copy are accurate.', 'high', 'website'),
                    array('Verify VMO email routing and auto-replies', 'Confirm all role mailboxes, form routing, and reply automation are working.', 'high', 'operations'),
                    array('Verify phone, SMS, and AI receptionist readiness', 'Confirm number, inbound/outbound routing, extensions, AI agent, and dashboard logging.', 'high', 'operations'),
                    array('Review launch documents and legal policies', 'Open the Documents tab and approve required business, marketing, SEO, lead, and legal docs.', 'normal', 'compliance'),
                    array('Review CRM lead report and outreach priorities', 'Confirm primary, secondary, and tertiary leads before outreach begins.', 'normal', 'sales'),
                );
                foreach ($tasks as $task) {
                    $inserted = $wpdb->insert($actions_table, array(
                        'user_id' => $user_id,
                        'title' => sanitize_text_field($task[0]),
                        'description' => sanitize_textarea_field($task[1]),
                        'source' => 'consultation_launch',
                        'priority' => $task[2],
                        'category' => $task[3],
                        'status' => 'pending',
                        'due_date' => current_time('Y-m-d'),
                        'created_at' => current_time('mysql'),
                        'updated_at' => current_time('mysql'),
                    ), array('%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s'));
                    if ($inserted) {
                        $created_actions++;
                    }
                }
            }
        }

        $activities_table = $wpdb->prefix . 'vmo_daily_activities';
        if ($this->builder_table_exists($activities_table)) {
            $existing = (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$activities_table} WHERE user_id = %d AND roadmap_id = %s", $user_id, $session_id . '_launch'));
            if ($existing === 0) {
                $activities = array('Launch readiness review', 'First outreach preparation', 'KPI baseline review');
                foreach ($activities as $index => $title) {
                    $inserted = $wpdb->insert($activities_table, array(
                        'activity_id' => 'phase8_' . $session_id . '_' . ($index + 1),
                        'roadmap_id' => $session_id . '_launch',
                        'user_id' => $user_id,
                        'day_number' => $index + 1,
                        'activity_date' => date('Y-m-d', strtotime('+' . $index . ' day', current_time('timestamp'))),
                        'activity_title' => sanitize_text_field($title),
                        'activity_description' => 'Consultation-generated launch action for ' . $business_name . '.',
                        'activity_type' => 'user_approval',
                        'estimated_duration_minutes' => 30,
                        'priority' => 2,
                        'status' => 'pending',
                        'ai_generated' => 1,
                        'created_at' => current_time('mysql'),
                        'updated_at' => current_time('mysql'),
                    ), array('%s', '%s', '%d', '%d', '%s', '%s', '%s', '%s', '%d', '%d', '%s', '%d', '%s', '%s'));
                    if ($inserted) {
                        $created_activities++;
                    }
                }
            }
        }

        $state = array(
            'actions_created' => $created_actions,
            'daily_activities_created' => $created_activities,
            'updated_at' => current_time('mysql'),
        );
        update_user_meta($user_id, 'vmo_phase8_today_ready_' . $session_id, $state);
        return $state;
    }

    private function build_phase8_email_readiness($user_id, $session_id, array $consultation_data) {
        global $wpdb;
        $roles = is_array($consultation_data['email_roles'] ?? null) ? array_filter($consultation_data['email_roles']) : array();
        $roles_meta = is_array($consultation_data['email_roles_meta'] ?? null) ? $consultation_data['email_roles_meta'] : array();
        $accounts_count = 0;
        $accounts_table = $wpdb->prefix . 'vmo_email_accounts';
        if ($this->builder_table_exists($accounts_table)) {
            $accounts_count = (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$accounts_table} WHERE user_id = %d", $user_id));
        }
        $zoho_status = get_user_meta($user_id, 'vmo_zoho_setup_status', true);
        $provision_status = strtolower((string) ($roles_meta['provisioning_status'] ?? ''));
        $auto_reply_subsystem_available = class_exists('VMO_Email_Auto_Reply') || class_exists('VMO_Email_Automation');
        if ($auto_reply_subsystem_available) {
            update_user_meta($user_id, 'vmo_ai_auto_reply_enabled', 1);
        }
        $state = array(
            'email_accounts_created' => $accounts_count >= max(1, count($roles)),
            'email_roles_confirmed' => !empty($roles_meta['roles_confirmed']) || !$this->needs_email_roles_confirmation($consultation_data),
            'mailbox_ui_ready' => $accounts_count > 0 || class_exists('VMO_Email_Provisioner') || class_exists('VMO_Zoho_Mail_Integration'),
            'forms_routed_to_mailboxes' => !empty($roles) && (!$this->needs_email_roles_confirmation($consultation_data)),
            'auto_reply_enabled' => !empty(get_user_meta($user_id, 'vmo_ai_auto_reply_enabled', true)),
            'dns_or_zoho_status_confirmed' => $accounts_count > 0 || in_array($provision_status, array('ready', 'connected', 'completed', 'verified'), true) || (is_array($zoho_status) && in_array(($zoho_status['status'] ?? ''), array('ready', 'connected', 'verified'), true)),
            'smtp_verified' => !empty($roles_meta['smtp_verified']) || !empty(get_user_meta($user_id, 'vmo_smtp_verified', true)),
            'imap_verified' => !empty($roles_meta['imap_verified']) || !empty(get_user_meta($user_id, 'vmo_imap_verified', true)),
            'accounts_count' => $accounts_count,
            'roles_count' => count($roles),
            'updated_at' => current_time('mysql'),
        );
        update_user_meta($user_id, 'vmo_final_email_readiness_' . $session_id, $state);
        return $state;
    }

    private function build_phase8_phone_readiness($user_id, $session_id) {
        $service = get_user_meta($user_id, 'vmo_phone_service', true);
        $service = is_array($service) ? $service : array();
        $number = $service['phone_number'] ?? $service['business_number'] ?? get_user_meta($user_id, 'vmo_business_phone_number', true);
        $agent_sid = $service['ai_agent_sid'] ?? get_user_meta($user_id, 'twilio_ai_agent_sid', true);
        $extensions = $service['extensions'] ?? get_user_meta($user_id, 'vmo_phone_extensions', true);
        $state = array(
            'number_provisioned' => !empty($number) && !empty($service['provisioned']),
            'inbound_ready' => !empty($service['inbound_ready']),
            'outbound_ready' => !empty($service['outbound_ready']),
            'extensions_ready' => !empty($extensions),
            'ai_agent_ready' => !empty($agent_sid),
            'website_integrated' => !empty($number),
            'dashboard_integrated' => !empty($service),
            'phone_number' => sanitize_text_field((string) $number),
            'updated_at' => current_time('mysql'),
        );
        update_user_meta($user_id, 'vmo_final_phone_readiness_' . $session_id, $state);
        return $state;
    }

    private function build_phase8_payment_readiness($user_id, $session_id, array $consultation_data, array $business_context) {
        global $wpdb;
        $operating_state = get_user_meta($user_id, 'vmo_business_operating_state_' . $session_id, true);
        if (!is_array($operating_state)) {
            $operating_state = get_user_meta($user_id, 'vmo_business_operating_state', true);
        }
        $payment_state = is_array($operating_state['systems']['payments'] ?? null) ? $operating_state['systems']['payments'] : array();
        $connected = array();
        $accounts_table = $wpdb->prefix . 'vmo_connected_accounts';
        if ($this->builder_table_exists($accounts_table)) {
            $connected = $wpdb->get_row($wpdb->prepare("SELECT stripe_account_id, details_submitted, charges_enabled, payouts_enabled FROM {$accounts_table} WHERE user_id = %d ORDER BY updated_at DESC LIMIT 1", $user_id), ARRAY_A);
            $connected = is_array($connected) ? $connected : array();
        }

        $products_table = $wpdb->prefix . 'vmo_store_products';
        $orders_table = $wpdb->prefix . 'vmo_store_orders';
        $store_products_count = 0;
        if ($this->builder_table_exists($products_table)) {
            $store_products_count = (int) $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM {$products_table} WHERE user_id = %d AND status = 'active' AND price_cents > 0",
                $user_id
            ));
        }

        $products_count = max($this->phase8_catalog_count($business_context, $payment_state), $store_products_count);
        $payment_required = $this->phase8_payments_required($business_context, $payment_state);
        $payments_enabled = defined('VMO_PAYMENTS_ENABLED') && VMO_PAYMENTS_ENABLED;
        $stripe_secret_configured = (bool) get_option('vmo_stripe_secret_key');
        $webhook_secret_configured = (bool) get_option('vmo_stripe_webhook_secret');
        $stripe_sdk_available = defined('VMO_AI_BUILDER_DIR') && file_exists(VMO_AI_BUILDER_DIR . 'stripe-sdk/init.php');
        $checkout_endpoint_ready = class_exists('VMO_Stripe_Connect') || (defined('VMO_AI_BUILDER_DIR') && file_exists(VMO_AI_BUILDER_DIR . 'includes/class-vmo-stripe-connect.php'));
        $checkout_js_ready = defined('VMO_AI_BUILDER_DIR') && file_exists(VMO_AI_BUILDER_DIR . 'assets/vmo-payments.js');
        $webhook_handler_ready = function_exists('vmo_handle_stripe_webhook') || (defined('VMO_AI_BUILDER_DIR') && file_exists(VMO_AI_BUILDER_DIR . 'includes/stripe-webhook.php'));
        $orders_table_ready = $this->builder_table_exists($orders_table);
        $payments_report_ready = class_exists('VMO_Stripe_Connect') || (defined('VMO_AI_BUILDER_DIR') && file_exists(VMO_AI_BUILDER_DIR . 'includes/class-vmo-stripe-connect.php'));
        $cfo_report_ready = class_exists('VMO_Tax_Calculator') || (defined('VMO_AI_BUILDER_DIR') && file_exists(VMO_AI_BUILDER_DIR . 'includes/class-vmo-tax-calculator.php'));
        $mail_ready = function_exists('wp_mail');
        $account_ready = !$payment_required || (!empty($connected['stripe_account_id']) && !empty($connected['details_submitted']) && !empty($connected['charges_enabled']) && !empty($connected['payouts_enabled']));
        $payment_onboarding_status = 'not_required';
        if ($payment_required) {
            if (empty($connected['stripe_account_id']) && empty($payment_state['account_id'])) {
                $payment_onboarding_status = 'required_not_started';
            } elseif (empty($connected['details_submitted']) || empty($connected['charges_enabled']) || empty($connected['payouts_enabled'])) {
                $payment_onboarding_status = 'returned_pending_verification';
            } elseif ($account_ready) {
                $payment_onboarding_status = 'verified_ready';
            } else {
                $payment_onboarding_status = 'failed';
            }
        }
        $checkout_ready = !$payment_required || (!empty($payment_state['checkout_ready']) && $account_ready && $payments_enabled && $stripe_secret_configured && $stripe_sdk_available && $checkout_endpoint_ready && $checkout_js_ready);
        $webhook_ready = !$payment_required || ($webhook_secret_configured && $webhook_handler_ready);
        $receipt_pipeline_ready = !$payment_required || ($webhook_ready && $mail_ready && $orders_table_ready);
        $owner_notification_ready = !$payment_required || ($webhook_ready && $mail_ready && $orders_table_ready);
        $cfo_ledger_ready = !$payment_required || ($orders_table_ready && $cfo_report_ready);
        $order_dashboard_ready = !$payment_required || ($orders_table_ready && $payments_report_ready && $cfo_ledger_ready);
        $full_chain_ready = !$payment_required || ($checkout_ready && $webhook_ready && $receipt_pipeline_ready && $owner_notification_ready && $cfo_ledger_ready && $order_dashboard_ready && $products_count > 0);
        $state = array(
            'required' => $payment_required,
            'products_created' => $products_count,
            'store_products_count' => $store_products_count,
            'payments_enabled' => !$payment_required || $payments_enabled,
            'stripe_secret_configured' => !$payment_required || $stripe_secret_configured,
            'webhook_secret_configured' => !$payment_required || $webhook_secret_configured,
            'stripe_sdk_available' => !$payment_required || $stripe_sdk_available,
            'checkout_endpoint_ready' => !$payment_required || $checkout_endpoint_ready,
            'checkout_js_ready' => !$payment_required || $checkout_js_ready,
            'webhook_handler_ready' => !$payment_required || $webhook_handler_ready,
            'payment_account_ready' => $account_ready,
            'payment_onboarding_status' => $payment_onboarding_status,
            'checkout_ready' => $checkout_ready,
            'buy_buttons_valid' => !$payment_required || ($products_count > 0 && $checkout_ready),
            'orders_table_ready' => !$payment_required || $orders_table_ready,
            'receipt_pipeline_ready' => $receipt_pipeline_ready,
            'owner_notification_ready' => $owner_notification_ready,
            'cfo_ledger_ready' => $cfo_ledger_ready,
            'order_dashboard_ready' => $order_dashboard_ready,
            'full_commerce_chain_ready' => $full_chain_ready,
            'stripe_account_id' => sanitize_text_field((string) ($connected['stripe_account_id'] ?? $payment_state['account_id'] ?? '')),
            'webhook_endpoint' => function_exists('rest_url') ? esc_url_raw(rest_url('vmo-builder/v1/stripe-webhook')) : '',
            'updated_at' => current_time('mysql'),
        );
        update_user_meta($user_id, 'vmo_final_payment_readiness_' . $session_id, $state);
        return $state;
    }

    private function phase8_catalog_count(array $business_context, array $payment_state = array()) {
        $products = is_array($business_context['products_inventory'] ?? null) ? $business_context['products_inventory'] : array();
        $services = is_array($business_context['services_inventory'] ?? null) ? $business_context['services_inventory'] : array();
        return max(count($products) + count($services), (int) ($payment_state['products_count'] ?? 0));
    }

    private function phase8_payments_required(array $business_context, array $payment_state = array()) {
        $payment_requirements = strtolower((string) ($business_context['payment_requirements'] ?? ''));
        $business_type = strtolower((string) ($business_context['business_type'] ?? ''));
        $products = is_array($business_context['products_inventory'] ?? null) ? $business_context['products_inventory'] : array();
        return !empty($payment_state['enabled'])
            || !empty($payment_state['checkout_ready'])
            || !empty($products)
            || strpos($payment_requirements, 'payment') !== false
            || strpos($payment_requirements, 'checkout') !== false
            || strpos($business_type, 'ecommerce') !== false
            || strpos($business_type, 'store') !== false;
    }

    private function phase8_requires_shipping_policy(array $business_context) {
        $shipping = strtolower((string) ($business_context['shipping_requirements'] ?? ''));
        $business_type = strtolower((string) ($business_context['business_type'] ?? ''));
        return !empty($business_context['products_inventory']) || strpos($shipping, 'ship') !== false || strpos($shipping, 'delivery') !== false || strpos($business_type, 'ecommerce') !== false || strpos($business_type, 'store') !== false;
    }
    
    /**
     * Helper: Check if business is product-based (needs shipping)
     */
    private function is_product_business($business_info) {
        $offerings = $business_info['offerings'] ?? array();
        $offering_type = $business_info['offeringType'] ?? '';
        
        $product_keywords = array('product', 'ecommerce', 'store', 'shop', 'merchandise', 'goods');
        
        foreach ($product_keywords as $keyword) {
            if (stripos($offering_type, $keyword) !== false) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Helper: Generate CRM leads based on business info
     * Returns an empty array instead of fake placeholder leads.
     * Real lead generation happens via Deep Research (class-vmo-deep-research.php).
     */
    private function generate_crm_leads($business_info, $location, $industry) {
        // Do not inject placeholder/fake leads into the CRM.
        // The user should use the Lead Generation (Deep Research) system to
        // generate real AI-researched leads for their specific market.
        return array();
    }
    
    /**
     * Helper: Generate SEO roadmap
     * Derives actionable keywords and strategy from the business consultation profile.
     */
    private function generate_seo_roadmap($business_info, $industry) {
        $business_name = $business_info['businessName'] ?? $business_info['business_name'] ?? '';
        $target_audience = $business_info['targetAudience'] ?? $business_info['target_audience'] ?? '';
        $offerings = $business_info['offerings'] ?? [];
        if (is_string($offerings)) $offerings = array_map('trim', explode(',', $offerings));
        $location = $business_info['location'] ?? '';

        // Build relevant keywords from actual business data
        $keywords = [];
        if (!empty($industry) && $industry !== 'General') {
            $keywords[] = strtolower($industry) . ' services';
        }
        if (!empty($business_name)) {
            $keywords[] = strtolower($business_name);
        }
        foreach (array_slice($offerings, 0, 5) as $offering) {
            $keywords[] = strtolower(trim($offering));
        }
        if (!empty($target_audience)) {
            $keywords[] = strtolower($target_audience) . ' solutions';
        }
        if (!empty($location)) {
            $keywords[] = strtolower($industry) . ' in ' . strtolower($location);
        }
        // Deduplicate and clean
        $keywords = array_values(array_unique(array_filter($keywords)));

        // Local SEO actions
        $local_seo = [];
        if (!empty($location)) {
            $local_seo[] = 'Claim and optimize Google Business Profile';
            $local_seo[] = 'Target location-specific keywords for ' . $location;
            $local_seo[] = 'Build citations in local directories';
        }

        // Content strategy based on profile
        $content_strategy = [
            'Publish blog content targeting primary keywords weekly',
            'Create service/product pages optimized for each offering',
        ];
        if (!empty($target_audience)) {
            $content_strategy[] = "Create content addressing {$target_audience} pain points";
        }
        $content_strategy[] = 'Build internal linking structure between related pages';

        return array(
            'keywords' => $keywords,
            'local_seo' => $local_seo,
            'content_strategy' => $content_strategy
        );
    }
    
    /**
     * Helper: Generate business plan document
     */
    private function generate_business_plan_document($consultation_data) {
        $ex = $consultation_data['extracted_information'] ?? [];
        $business_name = $ex['businessName'] ?? 'Your Business';
        $industry = $ex['industry'] ?? 'General';
        $offerings = is_array($ex['offerings'] ?? null) ? implode(', ', $ex['offerings']) : ($ex['offerings'] ?? 'Products/Services');
        
        return "# {$business_name} Business Plan

## Executive Summary
{$business_name} is a {$industry} business offering {$offerings}.

## Business Overview
- **Industry**: {$industry}
- **Business Model**: " . ($ex['offeringType'] ?? 'Mixed') . "
- **Target Market**: " . ($ex['targetAudience'] ?? 'General consumers') . "
- **Location**: " . ($ex['location'] ?? 'To be determined') . "

## Products/Services
{$offerings}

## Goals & Objectives
" . ($ex['businessGoals'] ?? 'Build a successful and sustainable business') . "

## Next Steps
1. Complete website setup and go live
2. Set up payment processing
3. Launch marketing campaigns
4. Monitor KPIs and optimize

---
Generated by VMO AI Business Consultant";
    }
    
    /**
     * Generate quick business roadmap with milestones (for consultation Phase 7)
     * ✅ VISION-CONNECTED: Milestones are derived from the user's actual business vision,
     * targets, and goals — not generic templates. This ensures the roadmap serves as a
     * measurable path toward the user's stated 5-year vision.
     *
     * ✅ REDESIGNED: Now delegates to VMO_Vision_Roadmap_Engine for AI-powered,
     * milestone-based roadmap generation from consultation day to vision achievement.
     * The engine generates milestones, daily activities, and sets up progress tracking.
     */
    public function generate_quick_roadmap($session_id, $consultation_data = null) {
        if (!$consultation_data) {
            $row = $this->consultation_db->get_consultation_by_session($session_id);
            $consultation_data = $row ? json_decode($row->consultation_data ?: '[]', true) : [];
        }

        $user_id = get_current_user_id();

        // Delegate to the Vision Roadmap Engine
        if (class_exists('VMO_Vision_Roadmap_Engine')) {
            try {
                $engine = VMO_Vision_Roadmap_Engine::get_instance();
                $roadmap = $engine->generate_ai_roadmap($session_id, $user_id, $consultation_data);

                if ($roadmap && !empty($roadmap['milestones'])) {
                    // Save roadmap to consultation data for backward compatibility
                    $consultation_data['roadmap_json'] = $roadmap;
                    $consultation_data['roadmap_engine_version'] = 'vision_roadmap_engine_v1';
                    $this->consultation_db->save_consultation($user_id, $session_id, [
                        'consultation_data' => $consultation_data
                    ]);

                    error_log('VMO: AI-powered vision roadmap generated via engine — ' . count($roadmap['milestones']) . ' milestones, ' . ($roadmap['total_days'] ?? 0) . ' total days');
                    return $roadmap['milestones'] ?? [];
                }
            } catch (\Throwable $e) {
                error_log('VMO: Vision Roadmap Engine failed, using legacy fallback — ' . $e->getMessage());
            }
        }

        // Legacy fallback: basic milestones (only if engine unavailable)
        $ex = $consultation_data['extracted_information'] ?? [];
        $bp = $consultation_data['business_profile'] ?? [];
        $business_name = $ex['businessName'] ?? $bp['identity']['name'] ?? 'Your Business';
        $targets = $bp['targets'] ?? [];
        $north_star = $targets['north_star_metric'] ?? '';
        $short_term = $targets['short_term'] ?? '';

        $milestones = [
            ['title' => "Complete {$business_name} Setup", 'target_date' => date('Y-m-d', strtotime('+2 weeks')), 'owner' => 'jarvis', 'status' => 'in_progress', 'progress' => 50, 'notes' => 'Foundation: website, legal, payments', 'vision_alignment' => $short_term ?: 'Launch business'],
            ['title' => 'Go Live & Initial Marketing', 'target_date' => date('Y-m-d', strtotime('+4 weeks')), 'owner' => 'marketing', 'status' => 'planned', 'progress' => 0, 'notes' => 'Launch website, start marketing', 'vision_alignment' => $short_term ?: 'Establish market presence'],
            ['title' => 'First Measurable Results', 'target_date' => date('Y-m-d', strtotime('+8 weeks')), 'owner' => 'operations', 'status' => 'planned', 'progress' => 0, 'notes' => $north_star ?: 'First customers/revenue', 'vision_alignment' => $north_star ?: 'Generate first results'],
        ];

        $consultation_data['roadmap_json'] = ['milestones' => $milestones, 'generation_method' => 'legacy_fallback'];
        $this->consultation_db->save_consultation($user_id, $session_id, [
            'consultation_data' => $consultation_data
        ]);

        return $milestones;
    }
    
    /**
     * Generate daily activities — DELEGATES to Vision Roadmap Engine.
     * 
     * The engine distributes milestone activities across every day from Day 1
     * to vision achievement. Activities are stored in vmo_daily_activities and 
     * retrieved per-day by the frontend via AJAX endpoints.
     *
     * Legacy fallback: If the engine is unavailable (e.g. during upgrade),
     * returns a minimal set of generic daily tasks so the UI is never empty.
     */
    public function generate_daily_activities($session_id, $consultation_data = null) {
        if (!$consultation_data) {
            $row = $this->consultation_db->get_consultation_by_session($session_id);
            $consultation_data = $row ? json_decode($row->consultation_data ?: '[]', true) : [];
        }

        // ── Try the Vision Roadmap Engine first ────────────────────────
        try {
            if (class_exists('VMO_Vision_Roadmap_Engine')) {
                $engine = VMO_Vision_Roadmap_Engine::get_instance();
                $user_id = get_current_user_id();

                // Check if we already have an active roadmap for this session
                $active_roadmap = $engine->get_active_roadmap($session_id, $user_id);

                if ($active_roadmap) {
                    // Roadmap already exists — just fetch today's activities
                    $today_activities = $engine->get_todays_activities(
                        $active_roadmap->id,
                        $user_id
                    );
                    
                    // Also store reference in consultation_data for convenience
                    $consultation_data['daily_activities_engine'] = true;
                    $consultation_data['active_roadmap_id'] = $active_roadmap->id;
                    $this->consultation_db->save_consultation($user_id, $session_id, [
                        'consultation_data' => $consultation_data
                    ]);

                    return [
                        'source'        => 'engine',
                        'roadmap_id'    => $active_roadmap->id,
                        'today'         => $today_activities,
                        'total_days'    => $active_roadmap->total_days ?? 0,
                        'vision_target' => $active_roadmap->vision_target_date ?? '',
                    ];
                }

                // No roadmap yet — the engine's generate_ai_roadmap() already
                // calls generate_and_save_daily_activities() internally.
                // If we reach here, roadmap generation hasn't run yet for this
                // session, so we trigger it now.
                $result = $engine->generate_ai_roadmap($session_id, $user_id, $consultation_data);

                if ($result && !empty($result['roadmap_id'])) {
                    $today_activities = $engine->get_todays_activities(
                        $result['roadmap_id'],
                        $user_id
                    );

                    $consultation_data['daily_activities_engine'] = true;
                    $consultation_data['active_roadmap_id'] = $result['roadmap_id'];
                    $this->consultation_db->save_consultation($user_id, $session_id, [
                        'consultation_data' => $consultation_data
                    ]);

                    return [
                        'source'        => 'engine',
                        'roadmap_id'    => $result['roadmap_id'],
                        'today'         => $today_activities,
                        'total_days'    => $result['total_days'] ?? 0,
                        'vision_target' => $result['vision_target_date'] ?? '',
                    ];
                }
            }
        } catch (\Exception $e) {
            error_log('VMO Vision Roadmap Engine daily activities error: ' . $e->getMessage());
        }

        // ── Legacy fallback — minimal generic tasks ────────────────────
        $ex = $consultation_data['extracted_information'] ?? [];
        $bp = $consultation_data['business_profile'] ?? [];
        $business_name = $ex['businessName'] ?? $bp['identity']['name'] ?? 'Your Business';
        $north_star    = $bp['targets']['north_star_metric'] ?? '';

        $daily_activities = [
            'morning' => [
                ['title' => "Check {$business_name} Dashboard KPIs", 'time' => '9:00 AM', 'priority' => 'high', 'category' => 'analytics',
                 'vision_note' => $north_star ? "Track progress toward: {$north_star}" : 'Monitor key growth metrics'],
                ['title' => 'Review overnight messages & inquiries', 'time' => '9:15 AM', 'priority' => 'high', 'category' => 'customer_service',
                 'vision_note' => 'Every customer interaction is a step toward your vision'],
            ],
            'midday' => [
                ['title' => 'Post to social media', 'time' => '12:00 PM', 'priority' => 'medium', 'category' => 'marketing',
                 'vision_note' => 'Build brand awareness and authority'],
            ],
            'afternoon' => [
                ['title' => 'Follow up with leads', 'time' => '2:00 PM', 'priority' => 'high', 'category' => 'sales',
                 'vision_note' => 'Convert interest into customers'],
            ],
            'evening' => [
                ['title' => 'Review daily performance vs goals', 'time' => '5:00 PM', 'priority' => 'medium', 'category' => 'analytics',
                 'vision_note' => 'Track daily progress toward your vision'],
            ]
        ];

        $consultation_data['daily_activities'] = $daily_activities;
        $consultation_data['daily_activities_engine'] = false;
        $user_id = get_current_user_id();
        $this->consultation_db->save_consultation($user_id, $session_id, [
            'consultation_data' => $consultation_data
        ]);

        return $daily_activities;
    }
    
    /**
     * Helper: Generate marketing strategy document
     */
    private function generate_marketing_strategy_document($consultation_data) {
        return "# Marketing Strategy\\n\\nComprehensive marketing strategy...";
    }
    
    /**
     * Helper: Generate competitive analysis document
     */
    private function generate_competitive_analysis_document($consultation_data) {
        return "# Competitive Analysis\\n\\nDetailed competitive landscape analysis...";
    }

    /**
     * Persist updates to consultation context (e.g., media role assignments)
     */
    public function ajax_update_consultation_context() {
        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['security'])), 'vmo_builder_nonce')) {
            wp_die('Security check failed');
        }
        $session_id = sanitize_text_field($_POST['session_id'] ?? '');
        if (!$session_id) {
            wp_send_json_error(['message' => 'Missing session_id']);
        }
        $action = sanitize_text_field($_POST['context_action'] ?? '');
        $user_id = get_current_user_id();
        $row = $this->consultation_db->get_consultation_by_session($session_id);
        $existing_cd = $row ? json_decode($row->consultation_data ?: '[]', true) : [];
        $existing_log = $row ? json_decode($row->conversation_log ?: '[]', true) : [];
        $existing_bd = $row ? $row->business_name : '';
        $existing_bt = $row ? $row->business_type : '';
        $existing_analysis = $row ? $row->business_analysis : '';
        $existing_reco = $row ? $row->recommendations : '';
        $existing_site = $row ? $row->website_data : '';

        // ✅ CRITICAL FIX: Parse incoming consultation_data from frontend
        $incoming_raw = wp_unslash($_POST['consultation_data'] ?? '{}');
        $incoming = json_decode($incoming_raw, true);
        if (!is_array($incoming)) {
            $incoming = [];
        }

        if ($action === 'assign_media_role') {
            $url = esc_url_raw($_POST['url'] ?? '');
            $title = sanitize_text_field($_POST['title'] ?? '');
            $type = sanitize_text_field($_POST['type'] ?? '');
            $role = sanitize_text_field($_POST['role'] ?? '');
            if (!$url || !$role) {
                wp_send_json_error(['message' => 'Missing url or role']);
            }
            if (!isset($existing_cd['media_roles'])) $existing_cd['media_roles'] = [];
            // Upsert by URL
            $updated = false;
            foreach ($existing_cd['media_roles'] as &$mr) {
                if (isset($mr['url']) && $mr['url'] === $url) {
                    $mr['role'] = $role;
                    $mr['title'] = $title;
                    $mr['type'] = $type;
                    $updated = true;
                    break;
                }
            }
            if (!$updated) {
                $existing_cd['media_roles'][] = [
                    'url' => $url,
                    'title' => $title,
                    'type' => $type,
                    'role' => $role,
                ];
            }
        } elseif ($action === 'save_consultation_data' || empty($action)) {
            // ✅ CRITICAL FIX: Handle save_consultation_data action (phase persistence, etc.)
            
            // Ensure primary containers exist before merging
            $existing_cd = is_array($existing_cd) ? $existing_cd : $this->consultation_blueprint;
            if (!isset($existing_cd['extracted_information']) || !is_array($existing_cd['extracted_information'])) $existing_cd['extracted_information'] = [];
            if (!isset($existing_cd['media_files']) || !is_array($existing_cd['media_files'])) $existing_cd['media_files'] = [];
            if (!isset($existing_cd['documents']) || !is_array($existing_cd['documents'])) $existing_cd['documents'] = [];
            if (!isset($existing_cd['consultation_flow']) || !is_array($existing_cd['consultation_flow'])) $existing_cd['consultation_flow'] = $this->consultation_blueprint['consultation_flow'];
            if (!isset($existing_cd['document_store']) || !is_array($existing_cd['document_store'])) $existing_cd['document_store'] = $this->consultation_blueprint['document_store'];
            $incoming = $this->normalize_discovery_context_delta($incoming);

            // Preserve legacy blueprint mirrors for downstream consumers
            if (isset($incoming['blueprintRaw'])) {
                $existing_cd['blueprintRaw'] = $incoming['blueprintRaw'];
                error_log('VMO: Saving blueprintRaw (' . strlen($incoming['blueprintRaw']) . ' chars) from frontend');
            }
            if (isset($incoming['blueprintSummary'])) {
                $existing_cd['blueprintSummary'] = $incoming['blueprintSummary'];
                error_log('VMO: Saving blueprintSummary from frontend');
            }

            // Merge structured document store (blueprint + uploads)
            if (isset($incoming['document_store']) && is_array($incoming['document_store'])) {
                $incoming_store = $incoming['document_store'];
                $incoming_uploads = [];
                if (isset($incoming_store['uploads']) && is_array($incoming_store['uploads'])) {
                    $incoming_uploads = $incoming_store['uploads'];
                    unset($incoming_store['uploads']);
                }
                $existing_cd['document_store'] = $this->merge_profile_delta(
                    $existing_cd['document_store'],
                    $incoming_store
                );
                if (!isset($existing_cd['document_store']['uploads']) || !is_array($existing_cd['document_store']['uploads'])) {
                    $existing_cd['document_store']['uploads'] = [];
                }
                if (!empty($incoming_uploads)) {
                    $merged_uploads = $existing_cd['document_store']['uploads'];
                    foreach ($incoming_uploads as $upload) {
                        if (!is_array($upload)) {
                            continue;
                        }
                        $exists = false;
                        foreach ($merged_uploads as $idx => $existing_upload) {
                            if (!is_array($existing_upload)) {
                                continue;
                            }
                            $same_id = isset($existing_upload['id'], $upload['id']) && $existing_upload['id'] === $upload['id'];
                            $same_file = (($existing_upload['url'] ?? '') === ($upload['url'] ?? ''))
                                && (($existing_upload['title'] ?? '') === ($upload['title'] ?? ''));
                            if ($same_id || $same_file) {
                                $merged_uploads[$idx] = array_merge($existing_upload, $upload);
                                $exists = true;
                                break;
                            }
                        }
                        if (!$exists) {
                            $merged_uploads[] = $upload;
                        }
                    }
                    $existing_cd['document_store']['uploads'] = $merged_uploads;
                }
            }

            $structured_keys = [
                'business_profile',
                'research_profile',
                'recommendations_profile',
                'discovery_profile',
                'business_opportunity_shortlist',
                'website_plan',
                'systems_plan',
                'roadmap_json',
                'daily_tasks_json'
            ];
            foreach ($structured_keys as $structured_key) {
                if (isset($incoming[$structured_key]) && is_array($incoming[$structured_key])) {
                    if ($structured_key === 'business_opportunity_shortlist') {
                        $existing_cd[$structured_key] = $this->merge_business_opportunity_shortlist(
                            isset($existing_cd[$structured_key]) && is_array($existing_cd[$structured_key]) ? $existing_cd[$structured_key] : array(),
                            $incoming[$structured_key]
                        );
                        continue;
                    }
                    if ($structured_key === 'discovery_profile') {
                        $incoming[$structured_key] = $this->filter_empty_discovery_delta($incoming[$structured_key]);
                    }
                    $existing_cd[$structured_key] = $this->merge_profile_delta(
                        isset($existing_cd[$structured_key]) && is_array($existing_cd[$structured_key]) ? $existing_cd[$structured_key] : [],
                        $incoming[$structured_key]
                    );
                }
            }

            if (isset($incoming['approvals']) && is_array($incoming['approvals'])) {
                $existing_cd['approvals'] = $this->merge_profile_delta(
                    isset($existing_cd['approvals']) && is_array($existing_cd['approvals']) ? $existing_cd['approvals'] : [],
                    $incoming['approvals']
                );
            }

            // Append media_files (avoid duplicates by url+title)
            if (isset($incoming['media_files']) && is_array($incoming['media_files'])) {
                foreach ($incoming['media_files'] as $mf) {
                    if (!is_array($mf)) {
                        continue;
                    }
                    $exists = false;
                    foreach ($existing_cd['media_files'] as $emf) {
                        if (($emf['url'] ?? '') === ($mf['url'] ?? '') && ($emf['title'] ?? '') === ($mf['title'] ?? '')) {
                            $exists = true;
                            break;
                        }
                    }
                    if (!$exists) {
                        $existing_cd['media_files'][] = $mf;
                    }
                }
            }

            // Append documents metadata (avoid duplicates by title+url+length)
            if (isset($incoming['documents']) && is_array($incoming['documents'])) {
                foreach ($incoming['documents'] as $doc) {
                    if (!is_array($doc)) {
                        continue;
                    }
                    $len = intval($doc['length'] ?? 0);
                    $exists = false;
                    foreach ($existing_cd['documents'] as $ed) {
                        if (($ed['title'] ?? '') === ($doc['title'] ?? '') && ($ed['url'] ?? '') === ($doc['url'] ?? '') && intval($ed['length'] ?? 0) === $len) {
                            $exists = true;
                            break;
                        }
                    }
                    if (!$exists) {
                        $existing_cd['documents'][] = $doc;
                    }
                }
            }

            // ✅ CRITICAL: Update consultation_flow pointers when provided (PHASE PERSISTENCE FIX)
            if (isset($incoming['consultation_flow']) && is_array($incoming['consultation_flow'])) {
                $cf = $existing_cd['consultation_flow'];
                $incf = $incoming['consultation_flow'];
                foreach (['phases', 'status', 'timestamps'] as $key) {
                    if (isset($incf[$key])) {
                        $cf[$key] = $incf[$key];
                    }
                }
                // ✅ CRITICAL: Accept current_index from frontend (phase advancement)
                if (isset($incf['current_phase_index'])) {
                    $cf['current_phase_index'] = intval($incf['current_phase_index']);
                    $cf['current_index'] = intval($incf['current_phase_index']);
                } elseif (isset($incf['current_index'])) {
                    $cf['current_index'] = intval($incf['current_index']);
                    $cf['current_phase_index'] = intval($incf['current_index']);
                }
                $existing_cd['consultation_flow'] = $cf;
                error_log('VMO: Saved consultation_flow with current_index=' . ($cf['current_index'] ?? 0));
            }

            // Merge scenario/stage/flags persistence
            $top_level_strings = ['scenario_type', 'business_stage', 'recommendation_checklist'];
            foreach ($top_level_strings as $key) {
                if (array_key_exists($key, $incoming)) {
                    if ($key === 'recommendation_checklist') {
                        $existing_cd[$key] = wp_kses_post($incoming[$key]);
                        continue;
                    }
                    $existing_cd[$key] = is_string($incoming[$key])
                        ? sanitize_text_field($incoming[$key])
                        : $incoming[$key];
                }
            }

            if (isset($incoming['doc_locked'])) {
                $existing_cd['doc_locked'] = (bool) $incoming['doc_locked'];
            }

            if (isset($incoming['research_results'])) {
                $existing_cd['research_results'] = $incoming['research_results'];
            }

            if (isset($incoming['phase_flags']) && is_array($incoming['phase_flags'])) {
                $existing_flags = isset($existing_cd['phase_flags']) && is_array($existing_cd['phase_flags'])
                    ? $existing_cd['phase_flags']
                    : [];
                $existing_cd['phase_flags'] = array_merge($existing_flags, $incoming['phase_flags']);
            }
        } elseif ($action === 'update_email_provisioning') {
            $payload_raw = wp_unslash($_POST['payload'] ?? '');
            $payload = json_decode($payload_raw, true);
            if (!is_array($payload)) {
                wp_send_json_error(['message' => 'Invalid provisioning payload']);
            }

            if (!isset($existing_cd['email_roles']) || !is_array($existing_cd['email_roles'])) {
                $existing_cd['email_roles'] = $this->consultation_blueprint['email_roles'] ?? array();
            }
            if (isset($payload['email_roles']) && is_array($payload['email_roles'])) {
                foreach ($payload['email_roles'] as $role => $email) {
                    $clean_role = sanitize_key($role);
                    if ($clean_role === '') {
                        continue;
                    }
                    $sanitized_email = $email ? sanitize_email($email) : '';
                    $existing_cd['email_roles'][$clean_role] = $sanitized_email ?: null;
                }
            }

            if (!isset($existing_cd['email_roles_meta']) || !is_array($existing_cd['email_roles_meta'])) {
                $existing_cd['email_roles_meta'] = $this->consultation_blueprint['email_roles_meta'];
            }
            if (isset($payload['email_roles_meta']) && is_array($payload['email_roles_meta'])) {
                $meta_delta = $this->sanitize_email_meta_delta($payload['email_roles_meta']);
                if (!empty($meta_delta)) {
                    $existing_cd['email_roles_meta'] = $this->merge_profile_delta($existing_cd['email_roles_meta'], $meta_delta);
                }
            }

            $this->refresh_email_automation_state($existing_cd);
            $this->maybe_invoke_auto_provisioning($existing_cd);
        } elseif ($action === 'set_doc_locked') {
            // Set a simple flag that tells the assistant to avoid re-asking and proceed
            if (!isset($existing_cd) || !is_array($existing_cd)) $existing_cd = [];
            $existing_cd['doc_locked'] = true;
        }

        $this->consultation_db->save_consultation($user_id, $session_id, array(
            'business_name' => $existing_bd,
            'business_type' => $existing_bt,
            'consultation_data' => $existing_cd,
            'conversation_log' => $existing_log,
            'business_analysis' => $existing_analysis,
            'recommendations' => $existing_reco,
            'website_data' => $existing_site,
            'status' => $row ? $row->status : 'in_progress'
        ));

        wp_send_json_success(['updated' => true, 'context' => $existing_cd]);
    }

    /**
     * Initialize comprehensive business consultation questions
     */
    private function init_business_questions() {
        $this->business_questions = [
            'business_basics' => [
                'What type of business are you creating the website for?',
                'What industry does your business operate in?',
                'What is the main purpose/goal of your business?',
                'Where is your business located? (City, State/Province, Country)',
                'What is your business name and do you have a tagline?'
            ],
            'target_market' => [
                'Who is your target audience? (Demographics, age group, interests)',
                'What geographic area do you serve? (Local, regional, national, international)',
                'What problems does your business solve for customers?',
                'How do your customers currently find businesses like yours?',
                'What is your ideal customer profile?'
            ],
            'business_model' => [
                'How does your business make money? (Services, products, subscriptions, etc.)',
                'What are your main products or services?',
                'What are your pricing strategies?',
                'Do you need online ordering/payment systems?',
                'Do you need appointment booking or reservation systems?'
            ],
            'website_design_preferences' => [
                'Do you have any websites you admire or would like yours to look similar to? Please share URLs of websites you like.',
                'Do you already have a logo? If yes, please upload it using the upload button. If not, our AI will create a professional logo for you.',
                'What are your preferred colors for your website and brand identity? (Share specific colors if you have them, otherwise describe your preferences)',
                'Describe your ideal website style (modern, minimalist, bold, elegant, professional, creative, luxurious, etc.)',
                'What specific features or functionality do you need? (e-commerce, booking system, photo gallery, blog, membership area, etc.)',
                'Do you have any existing images, videos, product photos, or team photos you\'d like to use? Please upload them using the upload button.'
            ],
            'goals_objectives' => [
                'What do you want visitors to do on your website? (Contact, buy, book, subscribe)',
                'What are your short-term business goals (next 6-12 months)?',
                'What are your long-term business goals (2-5 years)?',
                'What does success look like for your business?',
                'How many customers/clients do you want to serve monthly?'
            ],
            'brand_vision' => [
                'What is your company mission statement?',
                'What are your core business values?',
                'How do you want customers to perceive your brand?',
                'What makes your business unique compared to competitors?',
                'What is your long-term vision for the company?'
            ],
            'operational_needs' => [
                'What business forms do you need? (Contact, quote request, booking)',
                'Do you need automated business processes?',
                'What type of content marketing would benefit your business?',
                'How do you want to communicate with customers?',
                'What business systems do you currently use or need?'
            ]
        ];
    }
    
    /**
     * Initialize AI agents with enhanced capabilities
     */
    private function init_agents() {
        $this->agents = [
            'media_integration_specialist' => [
                'name' => 'AI Media Integration Specialist',
                'role' => 'Creative media integration and visual content placement',
                'system_prompt' => 'You are an AI Media Integration Specialist who excels at intelligently placing user-provided media (images, videos, documents) into website layouts. You understand optimal placement strategies, responsive design principles, and creative integration techniques. You analyze project context, user intent, and existing design elements to determine the most effective placement and styling for media elements. You create seamless integrations that enhance user experience and business objectives.',
                'tasks' => ['media_placement', 'creative_integration', 'responsive_optimization', 'visual_enhancement']
            ],
            'business_consultant' => [
                'name' => 'Senior Business Consultant',
                'role' => 'Comprehensive business analysis and strategic planning',
                'system_prompt' => 'You are a senior business consultant with 20+ years of experience helping businesses across all industries. UNIVERSAL LANGUAGE SUPPORT: You are fluent in ALL global languages. If the user writes in a language other than English, you must respond in that same language while maintaining your professional persona. detection is automatic. You conduct thorough market analysis, competitive research, and strategic planning. You understand business models, revenue optimization, market positioning, and growth strategies. You create comprehensive business plans, conduct SWOT analysis, develop competitive strategies, and provide actionable business intelligence. Your expertise covers startup guidance, established business optimization, and digital transformation strategies.',
                'tasks' => ['business_analysis', 'market_research', 'competitive_analysis', 'strategic_planning', 'business_modeling', 'swot_analysis']
            ],
            'web_designer' => [
                'name' => 'AI Web Designer & UX Architect',
                'role' => 'Custom website design from scratch (NO TEMPLATES)',
                'system_prompt' => 'You are an elite AI web designer and UX architect who creates stunning, modern, CUSTOM websites from scratch - you NEVER use pre-built templates. You analyze user preferences, competitor websites, industry trends, and psychological design principles to create unique, conversion-optimized designs. You understand color psychology, typography, spatial design, user behavior patterns, and modern web aesthetics. You create complete multi-page websites with custom HTML/CSS/JS that are visually stunning, user-friendly, mobile-responsive, and optimized for the specific business goals. You integrate user-provided media assets (logos, photos, videos) seamlessly into custom layouts. Every website you design is unique, modern, and tailored specifically to the business and its target audience.',
                'tasks' => ['custom_website_design', 'competitor_analysis', 'color_psychology', 'ux_optimization', 'responsive_design', 'media_integration', 'modern_aesthetics', 'conversion_optimization']
            ],
            'competitor_research_analyst' => [
                'name' => 'AI Competitor & Market Research Analyst',
                'role' => 'Deep market and competitor website analysis',
                'system_prompt' => 'You are an AI market research analyst specializing in competitor website analysis and industry trend identification. You analyze competitor websites to identify successful design patterns, features, color schemes, content strategies, and user experience elements. You research industry-specific design trends, identify what the market leaders are doing right, and understand what website visitors expect in specific industries. You analyze user-provided inspiration websites to extract successful elements. You provide detailed insights on: color psychology for the industry, essential website features, design patterns that convert, and competitive differentiation opportunities. Your analysis helps create websites that stand out while meeting industry standards.',
                'tasks' => ['competitor_website_analysis', 'industry_trend_research', 'design_pattern_extraction', 'feature_benchmarking', 'market_positioning', 'inspiration_analysis']
            ],
            'content_seo_specialist' => [
                'name' => 'Content Marketing & SEO Specialist',
                'role' => 'Comprehensive content strategy and copywriting',
                'system_prompt' => 'You are a content marketing and SEO specialist who creates compelling, search-optimized content that drives engagement and conversions. You understand keyword research, content strategy, local SEO, technical SEO, and content marketing best practices. You create blog strategies, website copy, meta descriptions, schema markup, and comprehensive SEO plans. Your content is designed to rank well in search engines while engaging and converting visitors.',
                'tasks' => ['content_creation', 'seo_optimization', 'keyword_research', 'local_seo', 'content_strategy', 'copywriting']
            ],
            'legal_compliance_specialist' => [
                'name' => 'Legal & Compliance Specialist',
                'role' => 'Business legal documentation and compliance',
                'system_prompt' => 'You are a legal and compliance specialist who creates comprehensive legal documentation for businesses. You understand privacy laws, terms of service, disclaimers, industry-specific legal requirements, GDPR compliance, CCPA compliance, and business legal structures. You create legally sound documents that protect businesses while meeting regulatory requirements. Your expertise covers e-commerce law, service business regulations, and digital privacy compliance.',
                'tasks' => ['legal_documentation', 'privacy_policies', 'terms_of_service', 'compliance', 'disclaimers', 'contracts']
            ],
            'seo_digital_marketing_expert' => [
                'name' => 'SEO & Digital Marketing Expert',
                'role' => 'Search optimization and digital marketing strategy',
                'system_prompt' => 'You are an SEO and digital marketing expert who creates comprehensive digital marketing strategies that drive organic traffic and business growth. You understand advanced SEO techniques, local search optimization, competitor analysis, Google My Business optimization, social media integration, and marketing funnel creation. You develop long-term SEO strategies, create marketing campaigns, and optimize for maximum online visibility and lead generation.',
                'tasks' => ['advanced_seo', 'local_search_optimization', 'competitor_seo_analysis', 'digital_marketing_strategy', 'lead_generation', 'marketing_funnels']
            ],
            'operations_systems_specialist' => [
                'name' => 'Operations & Systems Specialist',
                'role' => 'Business process optimization and system integration',
                'system_prompt' => 'You are a business operations specialist who designs efficient business processes and recommends system integrations. You understand e-commerce platforms, payment processing, CRM systems, booking systems, inventory management, automated workflows, and customer communication systems. You create recommendations for business tools, software integrations, and operational processes that streamline business operations and improve customer experience. Your recommendations are practical, cost-effective, and scalable as the business grows.',
                'tasks' => ['payment_systems', 'booking_systems', 'crm_integration', 'automation_workflows', 'inventory_management', 'customer_communication']
            ],

            // ✅ v9.2.0: 5 NEW SPECIALIZED AGENTS — Premium Pipeline Architecture
            'visual_direction_agent' => [
                'name' => 'Visual Direction Agent',
                'role' => 'Design mode selection, palette governance, visual grammar enforcement',
                'system_prompt' => 'You are an elite visual director and brand designer who selects the optimal design mode for every business. You analyse business type, industry, target audience, and brand personality to choose the perfect design system from 8 premium modes: silicon_valley_saas, premium_dtc, luxury_editorial, premium_service_authority, immersive_ecommerce, hospitality_booking, high_trust_medical, education_institution. For each mode you specify the complete visual grammar: palette (bg, surface, accent, text, muted), typography scale, spacing rhythm, shadows, border radii, and image treatment. You ensure every page strictly follows the selected design system with zero inconsistencies. You also verify colour contrast meets WCAG AA and that the palette emotionally resonates with the target demographic.',
                'tasks' => ['design_mode_selection', 'palette_governance', 'visual_grammar', 'brand_alignment', 'wcag_compliance']
            ],
            'hero_specialist_agent' => [
                'name' => 'Hero Section Specialist',
                'role' => 'Above-the-fold hero composition and impact optimization',
                'system_prompt' => 'You are a hero section specialist who crafts the single most important viewport on every website — the above-the-fold hero. You understand layering order (background → media → overlay → headline → CTA), motion choreography (staggered entrance sequences, parallax depth layers), and responsive behaviour across breakpoints. You select from hero variants (dark cinematic overlay, split content+media, centered typographic, video-first) based on the business type and available media assets. You ensure the hero delivers maximum emotional impact in under 3 seconds, with a clear value proposition, compelling CTA, and premium visual treatment. You specify exact GSAP timeline sequences, Lenis scroll interactions, and mobile adaptations.',
                'tasks' => ['hero_composition', 'motion_choreography', 'responsive_hero', 'cta_optimization', 'media_integration']
            ],
            'conversion_copy_agent' => [
                'name' => 'Conversion Copy Specialist',
                'role' => 'High-converting website copy, headlines, CTAs, and microcopy',
                'system_prompt' => 'You are a conversion copywriting specialist who writes website copy that drives action. You understand tone/voice calibration per design mode (e.g. silicon_valley_saas uses confident/minimal copy, luxury_editorial uses aspirational prose). You craft headlines using proven formulas (Benefit → Proof → CTA, Problem → Solution → Promise). You write body copy with strategic paragraph structure: hook sentence, supporting evidence, transitional bridge, embedded CTA. You compose CTA button copy that creates urgency without being sleazy. You write microcopy (form labels, tooltips, success messages) that reduces friction. You enforce anti-patterns: never use "Welcome to our website", never open with "We are a...", never use stock phrases. Every word must earn its place.',
                'tasks' => ['headline_writing', 'cta_copy', 'body_copy', 'microcopy', 'tone_calibration', 'anti_pattern_enforcement']
            ],
            'motion_effects_agent' => [
                'name' => 'Motion & Effects Director',
                'role' => 'GSAP animations, ScrollTrigger choreography, parallax, and transitions',
                'system_prompt' => 'You are a motion design director who choreographs all website animations using GSAP + ScrollTrigger + Lenis smooth scroll. You define entrance sequences (stagger group timelines with 0.08-0.12s offsets), scroll-triggered reveals (threshold 20%, scrub: false for snappy or scrub: true for cinematic), parallax depth layers (background 0.3x, midground 0.6x, foreground 1x), and page transitions. You enforce a strict performance budget: max 12 simultaneous tweens, will-change only during animation, prefers-reduced-motion: reduce fallback for every animation. You select motion grammar per design mode (e.g. silicon_valley_saas uses minimal/functional motion, luxury_editorial uses slow cinematic reveals). You ensure animations enhance storytelling without causing jank or accessibility issues.',
                'tasks' => ['gsap_choreography', 'scroll_trigger', 'parallax_layers', 'page_transitions', 'performance_budget', 'a11y_motion']
            ],
            'information_architecture_agent' => [
                'name' => 'Information Architecture Specialist',
                'role' => 'Page hierarchy, navigation structure, content flow, and user journey',
                'system_prompt' => 'You are an information architecture specialist who designs the optimal page hierarchy, navigation structure, and content flow for every website. You analyse business goals, user intent patterns, and competitive benchmarks to determine: which pages to create, how to order sections within each page, what navigation pattern to use (sticky header, mega menu, hamburger, sidebar), and how to structure the user journey from awareness → consideration → conversion. You create detailed sitemaps with section-by-section content briefs. You ensure every page has a clear purpose, logical flow, and strategic CTAs that guide users toward conversion. You optimise for both SEO crawlability and human navigation patterns.',
                'tasks' => ['sitemap_design', 'navigation_structure', 'content_flow', 'user_journey', 'section_ordering', 'seo_structure']
            ]
        ];
    }
    
    /**
     * Process consultation with enhanced features
     */
    public function process_consultation_responses($responses) {
        $session_id = $this->generate_session_id();
        $user_id = get_current_user_id();
        
        try {
            // Save consultation data to database
            $consultation_data = array(
                'business_name' => $responses['business_name'] ?? '',
                'business_type' => $responses['business_type'] ?? '',
                'consultation_data' => $responses,
                'conversation_log' => $responses['conversation_log'] ?? [],
                'status' => 'processing'
            );
            
            $consultation_id = $this->consultation_db->save_consultation($user_id, $session_id, $consultation_data);
            
            // Generate comprehensive business analysis using all agents
            $business_package = $this->generate_comprehensive_business_package($responses, $consultation_id);
            
            // Update consultation with results
            $consultation_data['business_analysis'] = json_encode($business_package['business_analysis']);
            $consultation_data['recommendations'] = json_encode($business_package['recommendations']);
            $consultation_data['status'] = 'completed';
            
            $this->consultation_db->save_consultation($user_id, $session_id, $consultation_data);
            
            // Send consultation results via email
            $this->send_consultation_results_email($user_id, $business_package);
            
            return array(
                'success' => true,
                'consultation_id' => $consultation_id,
                'session_id' => $session_id,
                'business_package' => $business_package,
                'message' => 'Comprehensive business package generated successfully!'
            );
            
        } catch (\Throwable $e) {
            error_log('VMO Business Wizard Error: ' . $e->getMessage());
            return array(
                'success' => false,
                'message' => 'Failed to process consultation: ' . $e->getMessage()
            );
        }
    }
    
    /**
     * Generate comprehensive business package using all agents
     */
    private function generate_comprehensive_business_package($responses, $consultation_id, $research_data = array()) {
        $package = array();
        
        // Business Analysis by Senior Business Consultant
        $package['business_analysis'] = $this->generate_agent_analysis('business_consultant', $responses, 
            "Conduct a comprehensive business analysis including SWOT analysis, market positioning, competitive landscape, revenue model analysis, and strategic recommendations. USE SPECIFIC DATA from the Deep Research provided to validate the user's business model.", $research_data);
        
        // Website Architecture by Web Designer
        $package['website_architecture'] = $this->generate_agent_analysis('web_designer', $responses,
            "Create a comprehensive website architecture plan including page structure, user flow design, conversion optimization strategy, and responsive design specifications. Ensure the architecture beats the competitors analyzed in Deep Research.", $research_data);
        
        // SEO Strategy by Content & SEO Specialist
        $package['seo_strategy'] = $this->generate_agent_analysis('content_seo_specialist', $responses,
            "Develop a comprehensive SEO strategy including keyword research, content marketing plan, local SEO optimization, and ongoing SEO maintenance strategy. Target gaps identified in the competitor analysis.", $research_data);
        
        // Legal Documentation by Legal Specialist
        $package['legal_requirements'] = $this->generate_agent_analysis('legal_compliance_specialist', $responses,
            "Create comprehensive legal documentation requirements including privacy policy, terms of service, disclaimers, and industry-specific legal compliance needs.", $research_data);
        
        // Digital Marketing Strategy by Marketing Expert
        $package['marketing_strategy'] = $this->generate_agent_analysis('seo_digital_marketing_expert', $responses,
            "Develop a comprehensive digital marketing strategy including lead generation, email marketing campaigns, social media strategy, and conversion tracking. Base this on 'Strategic Advantages' found in Deep Research.", $research_data);
        
        // Operations Plan by Systems Specialist (including phone service recommendation)
        $package['operations_plan'] = $this->generate_agent_analysis('operations_systems_specialist', $responses,
            "Create a comprehensive operations plan including payment systems, booking systems, CRM integration, automated workflows, customer communication systems, and professional phone service with AI receptionist capabilities.", $research_data);
        
        // ✅ v9.2.0: Premium Pipeline Agents — Visual Direction, Hero, Copy, Motion, IA
        $package['visual_direction'] = $this->generate_agent_analysis('visual_direction_agent', $responses,
            "Select the optimal design mode for this business from the 8 premium modes. Specify the complete visual grammar: palette (bg, surface, accent, text, muted hex values), typography (heading + body font families, scale ratio), spacing rhythm, shadow depth, border radii, and image treatment style. Ensure WCAG AA contrast compliance. Output as structured recommendations.", $research_data);

        $package['hero_specification'] = $this->generate_agent_analysis('hero_specialist_agent', $responses,
            "Design the hero section for this business website. Select the best hero variant (dark cinematic overlay, split content+media, centered typographic, or video-first) based on the business type and available assets. Specify: headline text, subheadline, CTA button text and color, background treatment, GSAP entrance timeline (stagger offsets), and mobile adaptation strategy.", $research_data);

        $package['conversion_copy'] = $this->generate_agent_analysis('conversion_copy_agent', $responses,
            "Write all primary website copy including: hero headline and subheadline, services section headlines, about section narrative, testimonial prompts, CTA button copy for each section, and footer tagline. Match tone/voice to the business type. Apply anti-pattern rules. Every headline must follow a proven conversion formula.", $research_data);

        $package['motion_choreography'] = $this->generate_agent_analysis('motion_effects_agent', $responses,
            "Design the complete motion choreography for this website. Specify: hero entrance timeline, section reveal sequences (ScrollTrigger thresholds and stagger timings), parallax depth layers for key sections, hover micro-interactions, and page transition style. Include prefers-reduced-motion fallbacks. Stay within the performance budget of max 12 simultaneous tweens.", $research_data);

        $package['information_architecture'] = $this->generate_agent_analysis('information_architecture_agent', $responses,
            "Design the complete information architecture: sitemap with all pages, section order within each page (with rationale), navigation pattern recommendation, user journey flow from landing → conversion, and content briefs for each section. Optimize for both SEO crawlability and conversion funnel efficiency.", $research_data);

        // Generate phone service recommendations
        $package['phone_service_recommendation'] = $this->generate_phone_service_recommendation($responses);
        
        // Generate SEO service recommendations 
        $package['seo_service_recommendation'] = $this->generate_seo_service_recommendation($responses);
        
        // Generate email marketing service recommendations
        $package['email_marketing_recommendation'] = $this->generate_email_marketing_recommendation($responses);
        
        // Generate consolidated recommendations
        $package['recommendations'] = $this->consolidate_agent_recommendations($package, $responses);
        
        // Check if phone service is recommended and handle auto-provisioning
        $package['phone_service_provisioned'] = $this->handle_phone_service_provisioning($package, $responses);
        
        // Handle automatic SEO service provisioning with competitive analysis
        $package['seo_service_provisioned'] = $this->handle_seo_service_provisioning($package, $responses);
        
        // Handle automatic email marketing service provisioning with competitive analysis
        $package['email_marketing_service_provisioned'] = $this->handle_email_marketing_service_provisioning($package, $responses);
        
        // Initialize comprehensive AI-driven contact management system
        $business_type = $responses['business_type'] ?? 'general_business';
        $business_context = [
            'business_name' => $responses['business_name'] ?? '',
            'business_type' => $business_type,
            'industry' => $responses['industry'] ?? '',
            'location' => $responses['location'] ?? '',
            'services' => $responses['services'] ?? '',
            'target_customers' => $responses['target_customers'] ?? ''
        ];
        
        $package['contact_management_initialized'] = $this->initialize_contact_management_system(
            get_current_user_id(), 
            $business_type, 
            $business_context
        );
        
        // Create implementation roadmap
        $package['implementation_roadmap'] = $this->create_implementation_roadmap($package, $responses);
        
        // Generate business plan executive summary
        $package['executive_summary'] = $this->generate_executive_summary($package, $responses);

        if (function_exists('vmo_get_agent_orchestrator')) {
            try {
                $orchestrated_package = vmo_get_agent_orchestrator()->build_execution_package(
                    get_current_user_id(),
                    (string) $consultation_id,
                    is_array($responses) ? $responses : array(),
                    is_array($research_data) ? $research_data : array(),
                    $package
                );
                if (!empty($orchestrated_package)) {
                    $package = array_merge($package, $orchestrated_package);
                    $package['orchestration_enabled'] = true;
                }
            } catch (\Throwable $e) {
                error_log('VMO Orchestration Package Warning: ' . $e->getMessage());
            }
        }
        
        return $package;
    }
    
    /**
     * Generate analysis from specific agent with smart model selection
     */
    private function generate_agent_analysis($agent_key, $responses, $specific_task, $research_data = array()) {
        if (!isset($this->agents[$agent_key])) {
            return "Agent not available";
        }
        
        $agent = $this->agents[$agent_key];
        $business_context = $this->format_business_context($responses);
        
        $prompt = $agent['system_prompt'] . "

";
        $prompt .= "BUSINESS CONTEXT (PHASE 1 & 2):
" . $business_context . "

";
        // ✅ PHASE 3 INTEGRATION: Inject Deep Research findings into the prompt
        if (!empty($research_data)) {
            $prompt .= "DEEP MARKET RESEARCH FINDINGS (PHASE 3 REALITY CHECK):
";
            if (isset($research_data['competitor_analysis'])) {
                $prompt .= "Competitor Analysis: " . (is_string($research_data['competitor_analysis']) ? $research_data['competitor_analysis'] : json_encode($research_data['competitor_analysis'])) . "\n\n";
            }
            if (isset($research_data['strategic_advantages'])) {
                $prompt .= "Key Strategic Advantages: " . json_encode($research_data['strategic_advantages']) . "\n\n";
            }
            if (isset($research_data['market_gaps'])) {
                $prompt .= "Identified Market Gaps: " . json_encode($research_data['market_gaps']) . "\n\n";
            }
            
            $prompt .= "CRITICAL INSTRUCTION: specific recommendations must VALIDATE the user's expressed desires against these research findings. If the user's desire conflicts with market reality, suggest a strategic pivot or enhancement that achieves their goal more effectively.\n\n";
        }

        $prompt .= "SPECIFIC TASK: " . $specific_task . "

";
        $prompt .= "Provide a detailed, actionable analysis with specific recommendations, implementation steps, and expected outcomes. Format your response with clear headers, bullet points, and actionable items.";
        
        // ✅ TARGETED GPT-5 UPGRADE STRATEGY
        // Use GPT-5 for specialized agents that need:
        // - Deep reasoning (Business Consultant, Business Analyst)
        // - Complex code generation (Website Developer)
        // - Comprehensive research (SEO Expert)
        // Keep fast tier for other agents (cost-effective for simpler tasks)
        
        $use_gpt5 = in_array($agent_key, [
            'business_consultant',           // Business consultations (196K context for long conversations)
            'web_designer',                  // Website generation (better code architecture)
            'competitor_research_analyst',   // Business analysis (deep market research)
            'seo_digital_marketing_expert',  // SEO research (comprehensive keyword & competitor analysis)
            'visual_direction_agent',        // ✅ v9.2.0: Design mode selection (needs creative reasoning)
            'hero_specialist_agent',         // ✅ v9.2.0: Hero composition (complex visual decisions)
            'conversion_copy_agent',         // ✅ v9.2.0: High-converting copy (sophisticated writing)
            'motion_effects_agent',          // ✅ v9.2.0: GSAP choreography (complex technical + creative)
            'information_architecture_agent' // ✅ v9.2.0: IA design (strategic structure decisions)
        ]);
        
        // ✅ USE GPT-5 THINKING MODE FOR STRATEGIC AGENTS
        // Enable advanced reasoning for strategic business decisions
        $use_reasoning = in_array($agent_key, [
            'business_consultant',           // Strategic business planning & SWOT analysis
            'competitor_research_analyst'    // Market analysis & competitive intelligence
        ]);
        
        // Call OpenAI with appropriate model
        return $this->call_openai_api($prompt, 2000, $use_gpt5, $use_reasoning);
    }

    /**
     * Build a condensed "vision anchor" summary from the stored business profile.
     * This is injected as a system message when conversation history is truncated,
     * ensuring the AI NEVER forgets the user's original business vision, mission,
     * targets, north star metric, or key decisions — even in very long consultations.
     *
     * This is the BEDROCK of the plugin's long-term memory promise:
     * The user's original vision set during consultation must persist for years.
     */
    private function build_vision_anchor_summary(array $cd) {
        $bp = $cd['business_profile'] ?? [];
        $ei = $cd['extracted_information'] ?? [];
        $lines = [];

        $lines[] = "══════════════════════════════════════════════════════════════";
        $lines[] = "🎯 VISION ANCHOR — PERMANENT BUSINESS FOUNDATION (NEVER OVERRIDE THESE WITHOUT USER'S EXPLICIT REQUEST)";
        $lines[] = "══════════════════════════════════════════════════════════════";

        // Business identity
        $name = $bp['identity']['name'] ?? $ei['businessName'] ?? '';
        $industry = $bp['identity']['industry'] ?? $ei['industry'] ?? '';
        $model = $bp['identity']['business_model'] ?? '';
        $tagline = $bp['identity']['tagline'] ?? '';
        if ($name) $lines[] = "Business Name: {$name}";
        if ($industry) $lines[] = "Industry: {$industry}";
        if ($model) $lines[] = "Business Model: {$model}";
        if ($tagline) $lines[] = "Tagline: {$tagline}";

        // Vision & Mission
        $vision_block = $bp['vision'] ?? [];
        $mission = $vision_block['mission'] ?? '';
        $vision = $vision_block['vision'] ?? '';
        $values = $vision_block['core_values'] ?? [];
        if ($mission) $lines[] = "Mission: {$mission}";
        if ($vision) $lines[] = "Vision: {$vision}";
        if (!empty($values) && is_array($values)) $lines[] = "Core Values: " . implode(', ', $values);

        // Targets (the user's 5-year ambition and measurable goals)
        $targets = $bp['targets'] ?? [];
        if (!empty($targets['short_term'])) $lines[] = "Short-Term Target: {$targets['short_term']}";
        if (!empty($targets['mid_term'])) $lines[] = "Mid-Term Target: {$targets['mid_term']}";
        if (!empty($targets['long_term'])) $lines[] = "Long-Term Target: {$targets['long_term']}";
        if (!empty($targets['north_star_metric'])) $lines[] = "North Star Metric: {$targets['north_star_metric']}";

        // Story
        $story = $bp['story'] ?? [];
        if (!empty($story['origin'])) $lines[] = "Origin Story: {$story['origin']}";
        if (!empty($story['why_now'])) $lines[] = "Why Now: {$story['why_now']}";

        // Audience
        $audience = $bp['audience'] ?? [];
        if (!empty($audience['primary_segments']) && is_array($audience['primary_segments'])) {
            $lines[] = "Target Audience: " . implode(', ', $audience['primary_segments']);
        }
        if (!empty($audience['pain_points']) && is_array($audience['pain_points'])) {
            $lines[] = "Customer Pain Points: " . implode(', ', $audience['pain_points']);
        }

        // Offers
        if (!empty($bp['offers']) && is_array($bp['offers'])) {
            $offer_names = [];
            foreach ($bp['offers'] as $offer) {
                if (is_array($offer) && !empty($offer['name'])) $offer_names[] = $offer['name'];
            }
            if ($offer_names) $lines[] = "Products/Services: " . implode(', ', $offer_names);
        }

        // Key extracted information fields
        $key_ei_fields = ['domain', 'location', 'targetAudience', 'businessGoals', 'uniqueSellingPoint', 'competitiveAdvantage'];
        foreach ($key_ei_fields as $field) {
            $val = $ei[$field] ?? '';
            if ($val && !is_array($val)) {
                $label = ucfirst(preg_replace('/([A-Z])/', ' $1', $field));
                $lines[] = "{$label}: {$val}";
            }
        }

        // Roadmap summary (if exists)
        if (!empty($cd['roadmap_json']) && is_array($cd['roadmap_json'])) {
            $phase_count = count($cd['roadmap_json']['phases'] ?? $cd['roadmap_json']);
            $lines[] = "Roadmap: {$phase_count} phases defined";
        }

        // Approved decisions
        $approvals = $cd['approvals'] ?? [];
        if (!empty($approvals['phase1_profile_confirmed'])) $lines[] = "Phase 1 Profile: ✅ Confirmed by user";
        if (!empty($approvals['accepted']) && is_array($approvals['accepted'])) {
            $lines[] = "Accepted Recommendations: " . implode(', ', array_slice($approvals['accepted'], 0, 10));
        }

        // Document awareness
        $doc_store = $cd['document_store'] ?? [];
        $upload_count = count($doc_store['uploads'] ?? []);
        $archive_count = count($doc_store['archived_upload_summaries'] ?? []);
        if ($upload_count > 0) $lines[] = "Active Uploaded Documents: {$upload_count}";
        if ($archive_count > 0) $lines[] = "Archived Document Summaries: {$archive_count}";
        if (!empty($doc_store['blueprint_summary'])) {
            $lines[] = "Blueprint Summary: " . mb_substr($doc_store['blueprint_summary'], 0, 1500);
        }

        $lines[] = "══════════════════════════════════════════════════════════════";
        $lines[] = "This vision anchor represents the user's foundational business decisions.";
        $lines[] = "All roadmap milestones, daily activities, and progress tracking must be measured against these goals.";
        $lines[] = "══════════════════════════════════════════════════════════════";

        $output = implode("\n", $lines);
        // Only return if we have meaningful content (at least business name or vision)
        if (strlen($output) < 200) return '';
        return $output;
    }
    
    /**
     * Format business context for AI agents (with awareness of existing website/business state)
     */
    private function format_business_context($responses) {
        $context = "BUSINESS CONSULTATION DATA:

";
        
        // ✅ CRITICAL: Include uploaded documents/blueprints at the TOP
        if (!empty($responses['uploaded_documents'])) {
            $context .= "===== USER-PROVIDED BUSINESS DOCUMENTS (INITIAL REFERENCE — conversation corrections override this) =====\n";
            $context .= "⚠️ The user has provided detailed documentation. Do not ask for information already contained here.\n";
            $context .= "⚠️ IMPORTANT: If the user CORRECTED any information during conversation, their correction is the truth — NOT this document.\n";
            
            foreach ($responses['uploaded_documents'] as $doc) {
                $title = $doc['title'] ?? 'Document';
                $context .= "DOCUMENT: {$title}\n";
                if (!empty($doc['chunks'])) {
                    $context .= "CONTENT:\n";
                    foreach ($doc['chunks'] as $chunk) {
                        $context .= ($chunk['text'] ?? '') . "\n";
                    }
                }
                $context .= "\n";
            }
            $context .= "================================================\n\n";
        }
        
        // ✅ CRITICAL: Include uploaded media in context so AI knows about assets
        if (!empty($responses['uploaded_media'])) {
            $context .= "===== AVAILABLE MEDIA ASSETS =====\n";
            foreach ($responses['uploaded_media'] as $role => $files) {
                 if (is_array($files)) {
                     foreach($files as $f) {
                         $url = is_string($f) ? $f : ($f['url'] ?? 'unknown');
                         $context .= "- [{$role}] {$url}\n";
                     }
                 }
            }
            $context .= "==================================\n\n";
        }
        
        foreach ($responses as $category => $answers) {
            if (is_array($answers) && $category !== 'uploaded_documents' && $category !== 'uploaded_media') {
                $context .= strtoupper(str_replace('_', ' ', $category)) . ":
";
                foreach ($answers as $question => $answer) {
                    if (is_array($answer)) {
                        $serialized_answer = wp_json_encode($answer);
                        if (!empty($serialized_answer)) {
                            $context .= "- " . $serialized_answer . "
";
                        }
                    } elseif ($answer !== null && $answer !== '') {
                        $context .= "- " . $answer . "
";
                    }
                }
                $context .= "
";
            }
        }
        
        // Check for existing website/business state (CRITICAL CONTEXT AWARENESS)
        $user_id = get_current_user_id();
        $existing_state = $this->get_existing_business_state($user_id, $responses);
        
        if (!empty($existing_state)) {
            $context .= "===== CRITICAL: EXISTING BUSINESS STATE =====
";
            $context .= "⚠️ THIS USER HAS AN EXISTING BUSINESS/WEBSITE. Your recommendations must consider:
";
            $context .= "1. Updating/enhancing their existing assets (not creating duplicates)
";
            $context .= "2. Maintaining consistency with existing brand elements
";
            $context .= "3. Offering website redesign options if the existing site needs improvement
";
            $context .= "4. Suggesting service enhancements for their existing business

";
            
            if (isset($existing_state['website'])) {
                $ws = $existing_state['website'];
                $context .= "EXISTING WEBSITE:
";
                $context .= "- Website Name: " . ($ws->website_name ?? 'Untitled') . "
";
                $context .= "- Status: " . ($ws->is_published ? '✅ PUBLISHED & LIVE' : '📝 Draft') . "
";
                if ($ws->is_published) {
                    $context .= "- Live URL: " . $ws->published_url . "
";
                }
                $context .= "- Created: " . $ws->created_at . "
";
                $context .= "- Last Updated: " . $ws->updated_at . "
";
                if ($ws->has_unsaved_live_changes) {
                    $context .= "- ⚠️ Has unsaved changes
";
                }
                $context .= "
";
            }
            
            if (isset($existing_state['phone_service'])) {
                $context .= "EXISTING PHONE SERVICE: ✅ Configured
";
                $ps = $existing_state['phone_service'];
                if (!empty($ps['phone_number'])) {
                    $context .= "- Business Phone Number: " . $ps['phone_number'] . "
";
                }
                $context .= "
";
            }
            
            if (isset($existing_state['seo_service'])) {
                $context .= "EXISTING SEO SERVICE: ✅ Active

";
            }
            
            if (isset($existing_state['email_marketing'])) {
                $context .= "EXISTING EMAIL MARKETING: ✅ Active

";
            }
            
            $context .= "====================================

";
        }
        
        return $context;
    }
    
    /**
     * Get existing business state for context awareness
     */
    private function get_existing_business_state($user_id, $responses) {
        global $wpdb;
        $state = [];
        
        // Check for existing website drafts
        $draft = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}vmo_website_drafts 
             WHERE user_id = %d 
             ORDER BY updated_at DESC LIMIT 1",
            $user_id
        ));
        
        if ($draft) {
            $state['website'] = $draft;
        }
        
        // Check for existing phone service
        $phone_service = get_user_meta($user_id, 'vmo_phone_service', true);
        if (!empty($phone_service)) {
            $state['phone_service'] = $phone_service;
        }
        
        // Check for existing SEO service
        $seo_service = get_user_meta($user_id, 'vmo_seo_service', true);
        if (!empty($seo_service)) {
            $state['seo_service'] = $seo_service;
        }
        
        // Check for existing email marketing
        $email_marketing = get_user_meta($user_id, 'vmo_email_marketing', true);
        if (!empty($email_marketing)) {
            $state['email_marketing'] = $email_marketing;
        }
        
        return $state;
    }
    
    /**
     * Call OpenAI API with smart model selection
     * 
     * @param string $prompt The prompt to send to the API
     * @param int $max_tokens Maximum tokens for response
     * @param bool $use_gpt5 Whether to use GPT-5 (for specialized agents)
     * @param bool $use_reasoning Whether to use GPT-5 Thinking mode (for strategic analysis)
     */
    private function call_openai_api($prompt, $max_tokens = 2000, $use_gpt5 = false, $use_reasoning = false, $system_prompt = null, $assistant_context = null, $as_chat = true, $conversation_history = null, $stream = false, $model_tier = '') {
        if (empty($this->api_key)) {
            return "AI API key not configured";
        }
        
        // ✅ TOKEN DEDUCTION: REMOVED from here — deduction is handled at the handler level
        // (ajax_process_consultation_message uses vmo_deduct_tokens_for_operation which covers
        // all sub-calls including this one, classify_user_intent, and extract_consultation_delta)
        
        // ✅ SMART MODEL SELECTION
        // GPT-5 for specialized agents (Business Consultant, Website Developer, Analyst, SEO)
        // GPT-5.5 Pro only for high-judgment consultation turns.
        // GPT-5.5 fast tier for everything else (cost-effective for simple tasks)
        $model_tier = is_string($model_tier) ? sanitize_key($model_tier) : '';
        if ($model_tier === 'supreme') {
            $model_tier = 'pro';
        }
        if ($model_tier === 'pro') {
            // Use pro tier for executive judgment, final recommendations, and phase handoffs.
            $model = function_exists('vmo_get_model') ? vmo_get_model('pro') : (defined('VMO_MODEL_PRO') ? VMO_MODEL_PRO : 'gpt-5.5-pro');
            $max_tokens = max(4000, intval($max_tokens));
        } elseif ($use_gpt5 || $model_tier === 'strategic') {
            // Use strategic tier for specialized agents
            $model = function_exists('vmo_get_model') ? vmo_get_model('strategic') : (defined('VMO_OPENAI_MODEL') ? VMO_OPENAI_MODEL : 'gpt-5.5');
            $max_tokens = 4000;
        } else {
            // Use fast tier for standard consultation
            $model = function_exists('vmo_get_model') ? vmo_get_model('fast') : (defined('VMO_OPENAI_MODEL') ? VMO_OPENAI_MODEL : 'gpt-5.5-mini');
        }
        
        $messages = array();
        if ($as_chat) {
            if ($system_prompt && is_string($system_prompt) && trim($system_prompt) !== '') {
                $messages[] = array('role' => 'system', 'content' => $system_prompt);
            }
            
            // ✅ CRITICAL FIX: Include conversation history as proper messages
            // This allows AI to see the actual conversation flow instead of a text summary
            if ($conversation_history && is_array($conversation_history)) {
                foreach ($conversation_history as $msg) {
                    if (!isset($msg['role']) || !isset($msg['content'])) continue;
                    $role = $msg['role'];
                    $content = trim($msg['content']);
                    if ($content === '') continue;
                    
                    // Map roles: 'user' stays 'user', 'assistant' stays 'assistant'
                    if ($role === 'user' || $role === 'assistant') {
                        $messages[] = array('role' => $role, 'content' => $content);
                    }
                }
                error_log("VMO: Added " . count($conversation_history) . " history messages to OpenAI call");
            }
            
            // Only add assistant_context if it contains non-history information
            // ✅ FIX: Inject as 'system' role instead of 'assistant' role.
            // Previously this was injected as a fake assistant message, making the AI think
            // it had "said" this stale context data. This caused the AI to repeat old values
            // because it interpreted them as its own recent statements. As a system message,
            // the AI correctly treats it as supplementary context, not its own words.
            if ($assistant_context && is_string($assistant_context) && trim($assistant_context) !== '') {
                $messages[] = array('role' => 'system', 'content' => $assistant_context);
            }
            $messages[] = array('role' => 'user', 'content' => $prompt);
        } else {
            if ($system_prompt && is_string($system_prompt) && trim($system_prompt) !== '') {
                $messages[] = array('role' => 'system', 'content' => $system_prompt);
            }
            $messages[] = array('role' => 'user', 'content' => $prompt);
        }
        
        // ✅ FIX: Use correct token parameter based on model
        // GPT-5.x requires 'max_completion_tokens', older models use 'max_tokens'
        $token_param = function_exists('vmo_get_token_param') 
            ? vmo_get_token_param($model, $max_tokens) 
            : array('max_completion_tokens' => $max_tokens); // Default to new style for safety
        
        $data = array_merge(array(
            'model' => $model,
            'messages' => $messages,
        ), $token_param);
        $data = $this->apply_safe_openai_temperature($data, $model, 0.7);
        
        // ✅ ADD REASONING EFFORT FOR GPT-5 THINKING MODE
        // This enables advanced strategic analysis for business decisions
        if ($use_reasoning && $use_gpt5) {
            $data['reasoning_effort'] = 'high'; // Options: 'low', 'medium', 'high'
        }
        
        // Log model selection for monitoring
        $model_label = $model . ($use_reasoning ? ' (with reasoning)' : '');
        error_log("🤖 VMO: Using AI model '{$model_label}' for analysis");
        
        // ── STREAMING MODE: Stream tokens as SSE events ──
        // SSE headers must already be set by caller before entering this path.
        if ($stream && function_exists('vmo_stream_openai')) {
            $stream_temperature = array_key_exists('temperature', $data) ? $data['temperature'] : 1.0;
            $result = vmo_stream_openai($messages, $this->api_key, $model, $max_tokens, $stream_temperature);
            if (!empty($result['error'])) {
                echo 'data: ' . json_encode(['error' => $result['error']]) . "\n\n";
                flush();
                return 'Error: ' . $result['error'];
            }
            return trim($result['text']);
        }
        
        $args = array(
            'headers' => array(
                'Authorization' => 'Bearer ' . $this->api_key,
                'Content-Type' => 'application/json',
            ),
            'body' => json_encode($data),
            'method' => 'POST',
            'timeout' => 90 // Increased timeout for GPT-5 reasoning mode
        );
        
        $response = wp_remote_post('https://api.openai.com/v1/chat/completions', $args);
        
        // -------------------------
        // FALLBACK LOGIC (v1.1)
        // -------------------------
        $response_code = wp_remote_retrieve_response_code($response);
        $body = wp_remote_retrieve_body($response);
        $result = json_decode($body, true);

        // If specific error codes occur (e.g., 404 Model Not Found, 429 Rate Limit, 500 Server Error)
        // AND the current model is NOT already the fallback
        $_fb = function_exists('vmo_get_model') ? vmo_get_model('fallback') : 'gpt-5.5-nano';
        if (($response_code >= 400 || is_wp_error($response) || isset($result['error'])) && $model !== $_fb) {
            error_log("⚠️ VMO: Model '$model' failed (Code: $response_code). Attempting fallback to '$_fb'.");
            
            // Reconfigure for fallback
            $data['model'] = $_fb;
            unset($data['reasoning_effort']);
            unset($data['max_tokens'], $data['max_completion_tokens']);
            $data += function_exists('vmo_get_token_param') ? vmo_get_token_param($_fb, $max_tokens) : array('max_tokens' => $max_tokens);
            $data = $this->apply_safe_openai_temperature($data, $_fb, 0.7);
            
            $args['body'] = json_encode($data);
            $response = wp_remote_post('https://api.openai.com/v1/chat/completions', $args);
            
            // Re-parse the second response
            $body = wp_remote_retrieve_body($response);
            $result = json_decode($body, true);
        }

        if (is_wp_error($response)) {
            return 'Error: ' . $response->get_error_message();
        }
        
        // ✅ FIX: Safe navigation to prevent "Cannot access offset of type string on string"
        if (is_array($result)
            && isset($result['choices'])
            && is_array($result['choices'])
            && isset($result['choices'][0])
            && is_array($result['choices'][0])
            && isset($result['choices'][0]['message'])
            && is_array($result['choices'][0]['message'])
            && isset($result['choices'][0]['message']['content'])
        ) {
            return trim($result['choices'][0]['message']['content']);
        }

        // Return actual API error if present
        if (isset($result['error']['message'])) {
            error_log("VMO OpenAI API Error: " . $result['error']['message']);
            return 'Error: ' . $result['error']['message'];
        }
        
        return 'Error: Unable to generate response (' . substr($body, 0, 100) . '...)';
    }

    /**
     * Multilingual intent classifier: detect proceed/confirm intent in any language.
     * Also detects a payment mode choice when the user expresses it.
     * Returns array like ['proceed' => bool, 'payment_choice' => 'products|donations|none|null', 'language' => 'xx', 'raw' => '...']
     */
    private function classify_user_intent($message, $context = []) {
        $message = is_string($message) ? $message : '';
        $trimmed = trim($message);

        if (empty($this->api_key) || $trimmed === '') {
            return ['proceed' => false, 'payment_choice' => null, 'language' => null, 'raw' => null];
        }
        
        // ✅ TOKEN DEDUCTION: REMOVED from here — deduction is handled at the handler level
        // (ajax_process_consultation_message covers all sub-calls including intent classification)

        // Lightweight local shortcut: numeric choices in Phase 1 (0-based index).
        // This avoids relying on English keywords and avoids an LLM call for common UI replies.
        try {
            $phase_index = null;
            if (is_array($context)) {
                if (isset($context['server_consultation_data']['consultation_flow']['current_phase_index'])) {
                    $phase_index = intval($context['server_consultation_data']['consultation_flow']['current_phase_index']);
                } elseif (isset($context['consultation_data']['consultation_flow']['current_phase_index'])) {
                    $phase_index = intval($context['consultation_data']['consultation_flow']['current_phase_index']);
                }
            }
            $quick_map = array(
                '1' => 'products',
                '2' => 'donations',
                '3' => 'none'
            );
            if ($phase_index === 0 && isset($quick_map[$trimmed])) {
                return ['proceed' => false, 'payment_choice' => $quick_map[$trimmed], 'language' => null, 'raw' => 'local'];
            }
        } catch (\Throwable $e) {
            // Non-blocking: fall back to LLM classifier
        }

        $system = implode("\n", array(
            "You are an intent classifier.",
            "Analyze the user's last message in ANY language and return ONLY strict JSON.",
            "Return keys:",
            "- proceed: true if the user authorizes to continue / proceed / confirm / approve (in their language)",
            "- payment_choice: one of 'products', 'donations', 'none', or null.",
            "  Use 'products' if they indicate selling products/services online requiring checkout.",
            "  Use 'donations' if they indicate collecting donations/contributions/tithes/offerings.",
            "  Use 'none' if they explicitly indicate they will not collect payments.",
            "- language: IETF code like en, es, fr, de, zh, pt-BR if evident, else null.",
            "No extra text. No markdown. No code fences. JSON only."
        ));
        $user = "User message:\n" . $trimmed;

        // Low token, deterministic
        $messages = array(
            array('role' => 'system', 'content' => $system),
            array('role' => 'user', 'content' => $user)
        );
        // ✅ FIX: Use centralized model registry — nano for classification
        $_ic_model = function_exists('vmo_get_model') ? vmo_get_model('nano') : (defined('VMO_OPENAI_MODEL') ? VMO_OPENAI_MODEL : 'gpt-5.5-nano');
        $data = array_merge(array(
            'model' => $_ic_model,
            'messages' => $messages,
        ), function_exists('vmo_get_token_param') ? vmo_get_token_param($_ic_model, 160) : array('max_tokens' => 160));
        $data = $this->apply_safe_openai_temperature($data, $_ic_model, 0.0);
        $args = array(
            'headers' => array(
                'Authorization' => 'Bearer ' . $this->api_key,
                'Content-Type' => 'application/json',
            ),
            'body' => json_encode($data),
            'method' => 'POST',
            'timeout' => 30
        );
        $resp = wp_remote_post('https://api.openai.com/v1/chat/completions', $args);

        // Fallback Logic: Retry with fallback model if primary fails
        if (is_wp_error($resp) || wp_remote_retrieve_response_code($resp) >= 400) {
            error_log('VMO: Intent classification model failed. Retrying with fallback.');
            $fallback_model = function_exists('vmo_get_model') ? vmo_get_model('fallback') : 'gpt-5.5-nano';
            $data['model'] = $fallback_model;
            unset($data['max_tokens'], $data['max_completion_tokens']);
            $data += function_exists('vmo_get_token_param') ? vmo_get_token_param($fallback_model, 160) : array('max_tokens' => 160);
            $data = $this->apply_safe_openai_temperature($data, $fallback_model, 0.0);
            $args['body'] = json_encode($data);
            $resp = wp_remote_post('https://api.openai.com/v1/chat/completions', $args);
        }

        if (is_wp_error($resp)) {
            return ['proceed' => false, 'payment_choice' => null, 'language' => null, 'raw' => $resp->get_error_message()];
        }
        $body = wp_remote_retrieve_body($resp);
        $json = json_decode($body, true);
        // ✅ FIX: Safe navigation to prevent "Cannot access offset of type string on string"
        $content = '';
        if (is_array($json) && isset($json['choices']) && is_array($json['choices'])
            && isset($json['choices'][0]) && is_array($json['choices'][0])
            && isset($json['choices'][0]['message']) && is_array($json['choices'][0]['message'])
            && isset($json['choices'][0]['message']['content'])) {
            $content = $json['choices'][0]['message']['content'];
        }
        // Strip code fences if any
        $content = trim(preg_replace('/^```(json)?/i', '', $content));
        $content = trim(preg_replace('/```$/', '', $content));

        $out = json_decode($content, true);
        if (!is_array($out)) {
            return ['proceed' => false, 'payment_choice' => null, 'language' => null, 'raw' => $content];
        }

        $payment_choice = null;
        if (isset($out['payment_choice'])) {
            $candidate = is_string($out['payment_choice']) ? strtolower(trim($out['payment_choice'])) : null;
            if (in_array($candidate, array('products', 'donations', 'none'), true)) {
                $payment_choice = $candidate;
            }
        }

        return array(
            'proceed' => isset($out['proceed']) ? (bool) $out['proceed'] : false,
            'payment_choice' => $payment_choice,
            'language' => isset($out['language']) && is_string($out['language']) ? trim($out['language']) : null,
            'raw' => $content
        );
    }

    /**
     * Apply a detected payment choice into consultation state in a schema-friendly way.
     */
    private function apply_payment_choice_intent(&$consultation_cd, $payment_choice) {
        if (!is_array($consultation_cd) || !is_string($payment_choice)) {
            return;
        }
        $choice = strtolower(trim($payment_choice));
        if (!in_array($choice, array('products', 'donations', 'none'), true)) {
            return;
        }
        if (!isset($consultation_cd['business_profile']) || !is_array($consultation_cd['business_profile'])) {
            $consultation_cd['business_profile'] = $this->consultation_blueprint['business_profile'];
        }
        if (!isset($consultation_cd['business_profile']['web_requirements']) || !is_array($consultation_cd['business_profile']['web_requirements'])) {
            $consultation_cd['business_profile']['web_requirements'] = $this->consultation_blueprint['business_profile']['web_requirements'];
        }
        if (!isset($consultation_cd['payments_profile']) || !is_array($consultation_cd['payments_profile'])) {
            $consultation_cd['payments_profile'] = $this->consultation_blueprint['payments_profile'];
        }

        // Persist the explicit choice for downstream prompts/extraction.
        $consultation_cd['payments_profile']['payment_choice'] = $choice;

        // Frontend legacy compat: mirror into extracted_information.payment_collection so the
        // consultation UI/prompt can stop re-asking once the user already confirmed.
        if (!isset($consultation_cd['extracted_information']) || !is_array($consultation_cd['extracted_information'])) {
            $consultation_cd['extracted_information'] = array();
        }
        if (!isset($consultation_cd['extracted_information']['payment_collection']) || !is_array($consultation_cd['extracted_information']['payment_collection'])) {
            $consultation_cd['extracted_information']['payment_collection'] = array(
                'collects_payments' => null,
                'mode' => null,
                'confirmed' => false
            );
        }
        if (!isset($consultation_cd['extracted_information']['ecommerce']) || !is_array($consultation_cd['extracted_information']['ecommerce'])) {
            $consultation_cd['extracted_information']['ecommerce'] = array(
                'collection_mode' => null,
                'donation_funds' => array()
            );
        }

        if ($choice === 'products') {
            $consultation_cd['business_profile']['web_requirements']['ecommerce'] = true;
            $consultation_cd['business_profile']['web_requirements']['donations'] = false;
            $consultation_cd['payments_profile']['accepts_cards'] = true;
            $consultation_cd['payments_profile']['supports_donations'] = false;

            $consultation_cd['extracted_information']['payment_collection']['collects_payments'] = true;
            $consultation_cd['extracted_information']['payment_collection']['mode'] = 'products';
            $consultation_cd['extracted_information']['payment_collection']['confirmed'] = true;
            $consultation_cd['extracted_information']['ecommerce']['collection_mode'] = 'products';
            $consultation_cd['extracted_information']['needsEcommerce'] = true;
            $consultation_cd['extracted_information']['ecommerce_needed'] = true;
        } elseif ($choice === 'donations') {
            $consultation_cd['business_profile']['web_requirements']['ecommerce'] = false;
            $consultation_cd['business_profile']['web_requirements']['donations'] = true;
            $consultation_cd['payments_profile']['accepts_cards'] = true;
            $consultation_cd['payments_profile']['supports_donations'] = true;

            $consultation_cd['extracted_information']['payment_collection']['collects_payments'] = true;
            $consultation_cd['extracted_information']['payment_collection']['mode'] = 'donations';
            $consultation_cd['extracted_information']['payment_collection']['confirmed'] = true;
            $consultation_cd['extracted_information']['ecommerce']['collection_mode'] = 'donations';
            if (!isset($consultation_cd['extracted_information']['ecommerce']['donation_funds']) || !is_array($consultation_cd['extracted_information']['ecommerce']['donation_funds'])) {
                $consultation_cd['extracted_information']['ecommerce']['donation_funds'] = array();
            }
            $consultation_cd['extracted_information']['needsEcommerce'] = true;
            $consultation_cd['extracted_information']['ecommerce_needed'] = true;
        } else {
            $consultation_cd['business_profile']['web_requirements']['ecommerce'] = false;
            $consultation_cd['business_profile']['web_requirements']['donations'] = false;
            $consultation_cd['payments_profile']['accepts_cards'] = false;
            $consultation_cd['payments_profile']['supports_donations'] = false;

            $consultation_cd['extracted_information']['payment_collection']['collects_payments'] = false;
            $consultation_cd['extracted_information']['payment_collection']['mode'] = 'none';
            $consultation_cd['extracted_information']['payment_collection']['confirmed'] = true;
            $consultation_cd['extracted_information']['ecommerce']['collection_mode'] = null;
            $consultation_cd['extracted_information']['needsEcommerce'] = false;
            $consultation_cd['extracted_information']['ecommerce_needed'] = false;
        }
    }

    /**
     * Call OpenAI Vision with an image URL alongside text
     */
    private function call_openai_vision($instruction_text, $image_url, $max_tokens = 600) {
        if (empty($this->api_key) || empty($image_url)) {
            return '';
        }
        
        $model = function_exists('vmo_get_model') ? vmo_get_model('fast') : (defined('VMO_OPENAI_MODEL') ? VMO_OPENAI_MODEL : 'gpt-5.5-mini');
        $messages = array(
            array(
                'role' => 'user',
                'content' => array(
                    array('type' => 'text', 'text' => $instruction_text),
                    array('type' => 'image_url', 'image_url' => array('url' => $image_url))
                )
            )
        );
        // ✅ FIX: Use centralized token params
        $data = array_merge(array(
            'model' => $model,
            'messages' => $messages,
        ), function_exists('vmo_get_token_param') ? vmo_get_token_param($model, $max_tokens) : array('max_tokens' => $max_tokens));
        $data = $this->apply_safe_openai_temperature($data, $model, 0.4);
        $args = array(
            'headers' => array(
                'Authorization' => 'Bearer ' . $this->api_key,
                'Content-Type' => 'application/json',
            ),
            'body' => json_encode($data),
            'method' => 'POST',
            'timeout' => 90
        );
        $response = wp_remote_post('https://api.openai.com/v1/chat/completions', $args);

        // Fallback Logic: Retry with fallback model if primary fails
        if (is_wp_error($response) || wp_remote_retrieve_response_code($response) >= 400) {
            error_log('VMO: Vision model failed. Retrying with fallback.');
            $fallback_model = function_exists('vmo_get_model') ? vmo_get_model('fallback') : 'gpt-5.5-nano';
            $data['model'] = $fallback_model;
            unset($data['max_tokens'], $data['max_completion_tokens']);
            $data += function_exists('vmo_get_token_param') ? vmo_get_token_param($fallback_model, $max_tokens) : array('max_tokens' => $max_tokens);
            $data = $this->apply_safe_openai_temperature($data, $fallback_model, 0.4);
            $args['body'] = json_encode($data);
            $response = wp_remote_post('https://api.openai.com/v1/chat/completions', $args);
        }

        if (is_wp_error($response)) return '';
        $body = wp_remote_retrieve_body($response);
        $result = json_decode($body, true);
        return isset($result['choices'][0]['message']['content']) ? trim($result['choices'][0]['message']['content']) : '';
    }

    
    
    /**
     * Analyze competitor websites and market trends
     * This function performs deep research on competitors in the user's industry
     */
    private function analyze_competitors_and_market($responses) {
        // ✅ TIMEOUT PROTECTION: Check for existing Deep Research results first
        // This utilizes the chunked Deep Research data instead of running a risky synchronous call
        if ($this->deep_research instanceof VMO_Deep_Research && method_exists($this->deep_research, 'get_latest_research')) {
            $cached = $this->deep_research->get_latest_research(get_current_user_id());
            if ($cached && !empty($cached['research_summary'])) {
                error_log('🚀 VMO Wizard: Using existing Deep Research results to prevent timeout');
                
                // Format cached analysis to match expected string format
                $formatted_analysis = "EXECUTIVE SUMMARY FROM DEEP RESEARCH:\n" . $cached['research_summary'] . "\n\n";
                if (!empty($cached['strategic_advantages'])) {
                    $formatted_analysis .= "STRATEGIC ADVANTAGES:\n" . json_encode($cached['strategic_advantages'], JSON_PRETTY_PRINT) . "\n\n";
                }
                
                return [
                    'competitor_analysis' => $formatted_analysis,
                    'inspiration_analysis' => [], // Skip heavy inspiration analysis to save time
                    'research_timestamp' => $cached['research_timestamp'] ?? current_time('mysql'),
                    'source' => 'deep_research_cache'
                ];
            }
        }

        $business_type = $responses['business_type'] ?? 'general business';
        $industry = $responses['industry'] ?? '';
        $location = $responses['location'] ?? '';
        $inspiration_urls = $responses['inspiration_websites'] ?? [];
        
        $research_prompt = "Conduct comprehensive competitor and market research for this business:

BUSINESS DETAILS:
- Type: {$business_type}
- Industry: {$industry}
- Location: {$location}

RESEARCH TASKS:
1. **Competitor Website Analysis**: Identify top 5-10 competitors in this industry and location. Analyze their websites for:
   - Color schemes and why they work
   - Layout and design patterns
   - Key features and functionality
   - Content strategy
   - Conversion elements
   - Mobile responsiveness approach

2. **Industry Design Trends**: Research current design trends in this industry including:
   - Popular color palettes and their psychological impact
   - Common website structures
   - Essential features customers expect
   - Emerging technologies being adopted

3. **Target Audience Preferences**: Analyze what website visitors in this industry expect:
   - User behavior patterns
   - Navigation preferences
   - Content consumption habits
   - Trust signals and credibility markers

4. **Competitive Differentiation**: Identify opportunities to stand out:
   - Gaps in competitor offerings
   - Unique features to implement
   - Design elements that would differentiate
   - Innovative approaches to try

Provide detailed, actionable insights with specific recommendations for color choices, features, layout strategies, and competitive advantages.";

        $competitor_analysis = $this->call_openai_deep_research($research_prompt, 'competitive_analysis');
        
        // If user provided inspiration websites, analyze them too
        $inspiration_analysis = [];
        if (!empty($inspiration_urls) && is_array($inspiration_urls)) {
            foreach ($inspiration_urls as $url) {
                $inspiration_prompt = "Analyze this website that the user admires: {$url}

Extract and explain:
1. Color scheme and psychology behind it
2. Layout structure and why it's effective
3. Typography choices
4. Key features and functionality
5. User experience elements that work well
6. Visual style and aesthetic approach
7. Conversion optimization techniques
8. Mobile responsiveness strategy

Provide insights on what makes this website effective and how similar elements could be adapted for the user's business.";
                
                $inspiration_analysis[] = $this->call_openai_deep_research($inspiration_prompt, 'competitive_analysis');
            }
        }
        
        return [
            'competitor_analysis' => $competitor_analysis,
            'inspiration_analysis' => $inspiration_analysis,
            'research_timestamp' => current_time('mysql')
        ];
    }
    
    /**
     * Save generated custom website to WordPress
     * Makes website persistent and editable
     */
    private function persist_custom_website($website_data, $consultation_id, $user_id) {
        if (!empty($website_data['payment_runtime']) && is_array($website_data['payment_runtime'])) {
            $website_data = $this->apply_payment_runtime_to_website_data($website_data, $website_data['payment_runtime']);
        }

        $pages = $website_data['pages_created'];
        $page_ids = [];
        
        foreach ($pages as $page) {
            // Create WordPress page
            $page_id = wp_insert_post([
                'post_title' => $page['title'],
                'post_content' => $page['html'],
                'post_status' => 'publish',
                'post_type' => 'page',
                'post_author' => $user_id,
                'menu_order' => $page['order'],
                'meta_input' => [
                    'vmo_custom_generated' => true,
                    'vmo_consultation_id' => $consultation_id,
                    'vmo_generation_method' => 'custom_ai_design',
                    'vmo_template_used' => 'NONE',
                    'vmo_editable_via_jarvis' => true,
                    'vmo_generated_at' => current_time('mysql')
                ]
            ]);
            
            if (!is_wp_error($page_id)) {
                $page_ids[] = $page_id;
            }
        }
        
        // Store website configuration for easy access and Jarvis editing
        update_user_meta($user_id, 'vmo_active_custom_website', [
            'consultation_id' => $consultation_id,
            'page_ids' => $page_ids,
            'homepage_id' => $page_ids[0] ?? null,
            'design_data' => $website_data,
            'payment_runtime' => is_array($website_data['payment_runtime'] ?? null) ? $website_data['payment_runtime'] : array(),
            'created_at' => current_time('mysql'),
            'editable_via_jarvis' => true,
            'generation_method' => 'custom_ai_design'
        ]);
        
        return [
            'success' => true,
            'page_ids' => $page_ids,
            'homepage_url' => get_permalink($page_ids[0] ?? null),
            'message' => 'Custom website generated and published successfully!'
        ];
    }
    
    /**
     * Save website as draft (unpublished, for iframe preview)
     */
    private function save_website_draft($user_id, $session_id, $website_data, $business_package) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'vmo_website_drafts';
        
        $draft_id = 'draft_' . uniqid();
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->insert($table_name, [
            'draft_id' => $draft_id,
            'user_id' => $user_id,
            'session_id' => $session_id,
            'website_data' => wp_json_encode($website_data),
            'business_package' => wp_json_encode($business_package),
            'is_published' => 0,
            'published_url' => null,
            'created_at' => current_time('mysql'),
            'updated_at' => current_time('mysql')
        ]);

        $qa_report = array();
        if (function_exists('vmo_get_agent_orchestrator')) {
            try {
                $qa_report = vmo_get_agent_orchestrator()->run_website_qa($user_id, $session_id, $website_data, $business_package, array(
                    'draft_key' => $draft_id,
                ));
            } catch (\Throwable $e) {
                error_log('VMO Draft QA Warning: ' . $e->getMessage());
            }
        } elseif (function_exists('vmo_get_website_qa_service')) {
            $qa_report = vmo_get_website_qa_service()->run_qa($user_id, $session_id, $website_data, $business_package, array(
                'draft_key' => $draft_id,
            ));
        }

        if (function_exists('vmo_get_website_final_qa_gate')) {
            try {
                $final_qa_gate = vmo_get_website_final_qa_gate()->evaluate($user_id, $session_id, $website_data, $business_package, array(
                    'draft_key' => $draft_id,
                ));
                $qa_report = vmo_get_website_final_qa_gate()->merge_with_existing_report($qa_report, $final_qa_gate);
            } catch (\Throwable $e) {
                error_log('VMO Draft Final QA Warning: ' . $e->getMessage());
            }
        }

        if (!empty($qa_report)) {
            $business_package['latest_website_qa'] = $qa_report;
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->update(
                $table_name,
                array(
                    'business_package' => wp_json_encode($business_package),
                    'updated_at' => current_time('mysql'),
                ),
                array('draft_id' => $draft_id),
                array('%s', '%s'),
                array('%s')
            );
        }
        
        return [
            'draft_id' => $draft_id,
            'created_at' => current_time('mysql'),
            'is_published' => false,
            'qa_report' => $qa_report,
            'publish_ready' => !empty($qa_report['publish_ready']),
            'publish_checklist' => $qa_report['checklist'] ?? array(),
        ];
    }
    
    /**
     * Initialize version history for undo functionality
     */
    private function initialize_version_history($user_id, $draft_id) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'vmo_website_versions';
        
        // Create initial version (generation)
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->insert($table_name, [
            'draft_id' => $draft_id,
            'user_id' => $user_id,
            'version_number' => 1,
            'change_description' => 'Initial website generation',
            'website_snapshot' => $this->get_current_draft_data($draft_id),
            'can_undo' => 0, // Cannot undo initial generation
            'created_at' => current_time('mysql')
        ]);
    }
    
    /**
     * Get draft preview URL for iframe
     */
    private function get_draft_preview_url($draft_id) {
        return admin_url('admin-ajax.php?action=vmo_preview_draft&draft_id=' . $draft_id);
    }
    
    /**
     * Get current draft data
     */
    private function get_current_draft_data($draft_id) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'vmo_website_drafts';
        
        $draft = $wpdb->get_row($wpdb->prepare(
            "SELECT website_data FROM $table_name WHERE draft_id = %s",
            $draft_id
        ));
        
        return $draft ? $draft->website_data : null;
    }
    
    /**
     * AJAX: Publish website (makes it live on the internet)
     */
    public function ajax_publish_website() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        try {
            $draft_id = sanitize_text_field(wp_unslash($_POST['draft_id']));
            $website_name = sanitize_text_field(wp_unslash($_POST['website_name']));
            $user_id = get_current_user_id();
            
            global $wpdb;
            $drafts_table = $wpdb->prefix . 'vmo_website_drafts';
            
            // Get draft data
            $draft = $wpdb->get_row($wpdb->prepare(
                "SELECT * FROM $drafts_table WHERE draft_id = %s AND user_id = %d",
                $draft_id,
                $user_id
            ));
            
            if (!$draft) {
                wp_send_json_error(['message' => 'Draft not found']);
                return;
            }
            
            // Check if already published
            if ($draft->is_published) {
                wp_send_json_error(['message' => 'Website is already published']);
                return;
            }
            
            $website_data = json_decode($draft->website_data, true);
            $business_package = json_decode($draft->business_package ?? '{}', true);
            if (!is_array($business_package)) {
                $business_package = array();
            }

            if (function_exists('vmo_get_approval_guard')) {
                $publish_gate = vmo_get_approval_guard()->can_execute('publish_website', $user_id, (string) $draft->session_id, is_array($website_data) ? $website_data : array(), $business_package, array(
                    'draft_key' => $draft_id,
                    'related_object_id' => $draft_id,
                    'title' => $website_name !== '' ? 'Publish website draft: ' . $website_name : 'Publish website draft',
                    'user_requested_publish' => true,
                ));

                if (empty($publish_gate['allowed'])) {
                    wp_send_json_error(array(
                        'message' => $publish_gate['message'] ?? 'Publish blocked by approval or readiness checks.',
                        'approval_required' => !empty($publish_gate['approval_required']),
                        'approval_granted' => !empty($publish_gate['approval_granted']),
                        'queue_id' => $publish_gate['queue_id'] ?? 0,
                        'readiness' => $publish_gate['readiness'] ?? array(),
                    ));
                    return;
                }
            }
            
            // Publish to WordPress (create actual pages)
            $published_result = $this->persist_custom_website($website_data, $draft->session_id, $user_id);
            
            if ($published_result['success']) {
                // Update draft to published status
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                $wpdb->update(
                    $drafts_table,
                    [
                        'is_published' => 1,
                        'website_name' => $website_name,
                        'published_url' => $published_result['homepage_url'],
                        'published_at' => current_time('mysql'),
                        'published_page_ids' => json_encode($published_result['page_ids'])
                    ],
                    ['draft_id' => $draft_id]
                );
                
                // Log publish action in version history
                $this->add_version_snapshot($draft_id, 'Website published live', false);
                
                // ✅ STEP 1 COMPLETE: Send website publication email notification
                $this->send_website_published_email($user_id, [
                    'website_name' => $website_name,
                    'live_url' => $published_result['homepage_url'],
                    'page_count' => count($published_result['page_ids']),
                    'published_at' => current_time('mysql')
                ]);
                
                wp_send_json_success([
                    'message' => 'Website published successfully!',
                    'website_name' => $website_name,
                    'live_url' => $published_result['homepage_url'],
                    'is_published' => true,
                    'page_ids' => $published_result['page_ids']
                ]);
            } else {
                wp_send_json_error(['message' => 'Failed to publish website']);
            }
            
        } catch (\Throwable $e) {
            error_log('VMO Publish Error: ' . $e->getMessage());
            wp_send_json_error(['message' => 'Publish failed: ' . $e->getMessage()]);
        }
    }
    
    /**
     * AJAX: Unpublish website (remove from live internet, keep in plugin)
     */
    public function ajax_unpublish_website() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        try {
            $user_id = get_current_user_id();
            $draft_id = isset($_POST['draft_id']) ? sanitize_text_field(wp_unslash($_POST['draft_id'])) : '';
            $page_id = isset($_POST['page_id']) ? intval($_POST['page_id']) : 0;
            
            // If page_id is provided directly (from publish button), handle it
            if ($page_id > 0 && empty($draft_id)) {
                // Verify the user owns this page
                $page = get_post($page_id);
                if (!$page || $page->post_author != $user_id) {
                    wp_send_json_error(['message' => 'You do not have permission to unpublish this page']);
                    return;
                }
                
                // Set page to draft status
                wp_update_post([
                    'ID' => $page_id,
                    'post_status' => 'draft'
                ]);
                
                wp_send_json_success([
                    'message' => 'Website unpublished successfully',
                    'page_id' => $page_id
                ]);
                return;
            }
            
            // Original draft_id based unpublish logic
            global $wpdb;
            $drafts_table = $wpdb->prefix . 'vmo_website_drafts';
            
            // Get draft data
            $draft = $wpdb->get_row($wpdb->prepare(
                "SELECT * FROM $drafts_table WHERE draft_id = %s AND user_id = %d",
                $draft_id,
                $user_id
            ));
            
            if (!$draft || !$draft->is_published) {
                wp_send_json_error(['message' => 'Website is not published']);
                return;
            }
            
            // Get published page IDs
            $page_ids = json_decode($draft->published_page_ids, true);
            
            // Unpublish pages (set to draft status, don't delete)
            foreach ($page_ids as $page_id) {
                wp_update_post([
                    'ID' => $page_id,
                    'post_status' => 'draft'
                ]);
            }
            
            // Update draft status
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->update(
                $drafts_table,
                [
                    'is_published' => 0,
                    'unpublished_at' => current_time('mysql')
                ],
                ['draft_id' => $draft_id]
            );
            
            // Log unpublish action
            $this->add_version_snapshot($draft_id, 'Website unpublished from live internet', false);
            
            wp_send_json_success([
                'message' => 'Website unpublished successfully',
                'is_published' => false,
                'can_republish' => true
            ]);
            
        } catch (\Throwable $e) {
            error_log('VMO Unpublish Error: ' . $e->getMessage());
            wp_send_json_error(['message' => 'Unpublish failed: ' . $e->getMessage()]);
        }
    }
    
    /**
     * AJAX: Save website changes (update both draft and live if published)
     */
    public function ajax_save_website_changes() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        try {
            $draft_id = sanitize_text_field(wp_unslash($_POST['draft_id']));
            $updated_content = json_decode(wp_unslash($_POST['updated_content']), true);
            $change_description = sanitize_text_field($_POST['change_description'] ?? 'Manual edit');
            $user_id = get_current_user_id();
            
            global $wpdb;
            $drafts_table = $wpdb->prefix . 'vmo_website_drafts';
            
            // Get current draft
            $draft = $wpdb->get_row($wpdb->prepare(
                "SELECT * FROM $drafts_table WHERE draft_id = %s AND user_id = %d",
                $draft_id,
                $user_id
            ));
            
            if (!$draft) {
                wp_send_json_error(['message' => 'Draft not found']);
                return;
            }
            
            // Save version before updating (for undo)
            $this->add_version_snapshot($draft_id, $change_description, true);
            
            // Update draft
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->update(
                $drafts_table,
                [
                    'website_data' => json_encode($updated_content),
                    'updated_at' => current_time('mysql'),
                    'has_unsaved_live_changes' => 0
                ],
                ['draft_id' => $draft_id]
            );
            
            // If published, also update live pages
            if ($draft->is_published) {
                $page_ids = json_decode($draft->published_page_ids, true);
                $this->update_live_pages($page_ids, $updated_content);
            }
            
            wp_send_json_success([
                'message' => 'Changes saved successfully',
                'has_unsaved_changes' => false,
                'is_published' => (bool)$draft->is_published,
                'updated_at' => current_time('mysql')
            ]);
            
        } catch (\Throwable $e) {
            error_log('VMO Save Changes Error: ' . $e->getMessage());
            wp_send_json_error(['message' => 'Save failed: ' . $e->getMessage()]);
        }
    }
    
    /**
     * AJAX: Undo last change (up to 25 versions back)
     */
    public function ajax_undo_website_change() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        try {
            $draft_id = sanitize_text_field(wp_unslash($_POST['draft_id']));
            $user_id = get_current_user_id();
            
            global $wpdb;
            $versions_table = $wpdb->prefix . 'vmo_website_versions';
            
            // Get last undoable version
            $last_version = $wpdb->get_row($wpdb->prepare(
                "SELECT * FROM $versions_table 
                 WHERE draft_id = %s AND can_undo = 1 
                 ORDER BY version_number DESC LIMIT 1",
                $draft_id
            ));
            
            if (!$last_version) {
                wp_send_json_error(['message' => 'No more changes to undo']);
                return;
            }
            
            // Restore from snapshot
            $drafts_table = $wpdb->prefix . 'vmo_website_drafts';
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->update(
                $drafts_table,
                [
                    'website_data' => $last_version->website_snapshot,
                    'updated_at' => current_time('mysql')
                ],
                ['draft_id' => $draft_id]
            );
            
            // Delete this version (it's been undone)
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->delete($versions_table, ['id' => $last_version->id]);
            
            // Count remaining undoable versions
            $remaining_undos = $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM $versions_table 
                 WHERE draft_id = %s AND can_undo = 1",
                $draft_id
            ));
            
            wp_send_json_success([
                'message' => 'Change undone successfully',
                'change_description' => $last_version->change_description,
                'remaining_undos' => (int)$remaining_undos,
                'can_undo' => $remaining_undos > 0
            ]);
            
        } catch (\Throwable $e) {
            error_log('VMO Undo Error: ' . $e->getMessage());
            wp_send_json_error(['message' => 'Undo failed: ' . $e->getMessage()]);
        }
    }
    
    /**
     * AJAX: Delete website (creates backup first)
     */
    public function ajax_delete_website() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        try {
            $draft_id = sanitize_text_field(wp_unslash($_POST['draft_id']));
            $confirmation = sanitize_text_field(wp_unslash($_POST['confirmation']));
            $user_id = get_current_user_id();
            
            if ($confirmation !== 'DELETE_WEBSITE') {
                wp_send_json_error(['message' => 'Confirmation required']);
                return;
            }
            
            global $wpdb;
            $drafts_table = $wpdb->prefix . 'vmo_website_drafts';
            
            // Get draft
            $draft = $wpdb->get_row($wpdb->prepare(
                "SELECT * FROM $drafts_table WHERE draft_id = %s AND user_id = %d",
                $draft_id,
                $user_id
            ));
            
            if (!$draft) {
                wp_send_json_error(['message' => 'Website not found']);
                return;
            }
            
            // Create compressed backup
            $backup_id = $this->create_website_backup($draft);
            
            // If published, delete live pages
            if ($draft->is_published) {
                $page_ids = json_decode($draft->published_page_ids, true);
                foreach ($page_ids as $page_id) {
                    wp_delete_post($page_id, true); // Force delete
                }
            }
            
            // Delete draft and versions
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->delete($drafts_table, ['draft_id' => $draft_id]);
            $wpdb->delete($wpdb->prefix . 'vmo_website_versions', ['draft_id' => $draft_id]);
            
            wp_send_json_success([
                'message' => 'Website deleted and backed up successfully',
                'backup_id' => $backup_id,
                'can_restore' => true
            ]);
            
        } catch (\Throwable $e) {
            error_log('VMO Delete Website Error: ' . $e->getMessage());
            wp_send_json_error(['message' => 'Delete failed: ' . $e->getMessage()]);
        }
    }
    
    /**
     * AJAX: Delete entire business (website + all services)
     */
    public function ajax_delete_business() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        try {
            $session_id = sanitize_text_field(wp_unslash($_POST['session_id']));
            $confirmation = sanitize_text_field(wp_unslash($_POST['confirmation']));
            $user_id = get_current_user_id();
            
            if ($confirmation !== 'DELETE_BUSINESS') {
                wp_send_json_error(['message' => 'Confirmation required']);
                return;
            }
            
            // Create full business backup
            $backup = $this->create_full_business_backup($session_id, $user_id);
            $backup_id = $backup['backup_id'];
            
            // Delete website
            global $wpdb;
            $draft = $wpdb->get_row($wpdb->prepare(
                "SELECT * FROM {$wpdb->prefix}vmo_website_drafts 
                 WHERE session_id = %s AND user_id = %d",
                $session_id,
                $user_id
            ));
            
            if ($draft) {
                // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- intentionally setting POST data for internal dispatch
                $_POST['draft_id'] = $draft->draft_id;
                // phpcs:ignore WordPress.Security.ValidatedSanitizedInput -- intentionally setting POST data for internal dispatch
                $_POST['confirmation'] = 'DELETE_WEBSITE';
                $this->ajax_delete_website();
            }
            
            // Delete business services (phone, SEO, email marketing, etc.)
            $this->delete_all_business_services($session_id, $user_id);
            
            // Delete consultation data
            $this->consultation_db->delete_consultation($session_id);
            
            wp_send_json_success([
                'message' => 'Business deleted and fully backed up',
                'backup_id' => $backup_id,
                'services_removed' => [
                    'website',
                    'phone_system',
                    'seo_automation',
                    'email_marketing',
                    'crm',
                    'analytics'
                ],
                'can_restore' => true
            ]);
            
        } catch (\Throwable $e) {
            error_log('VMO Delete Business Error: ' . $e->getMessage());
            wp_send_json_error(['message' => 'Delete failed: ' . $e->getMessage()]);
        }
    }
    
    /**
     * AJAX: Check credit/token status for consultation resume functionality
     * Returns success if user has sufficient credits, or insufficient_tokens if depleted
     */
    public function ajax_check_credit_status() {
        // Lightweight nonce check - allow both nonce types
        $nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : 
                 (isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '');
        
        $ok_ajax = $nonce && wp_verify_nonce($nonce, 'vmo_ajax_nonce');
        $ok_builder = $nonce && wp_verify_nonce($nonce, 'vmo_builder_nonce');
        
        if (!$ok_ajax && !$ok_builder) {
            wp_send_json_error(['message' => 'Invalid nonce', 'code' => 'invalid_nonce']);
            return;
        }
        
        $user_id = get_current_user_id();
        
        // Check if VMO_Tokens class exists and user has credits
        if (class_exists('VMO_Tokens')) {
            $tokens_instance = VMO_Tokens::get_instance();
            $balance = $tokens_instance->get_user_balance($user_id);
            
            // Minimum tokens needed to continue (roughly 1 API call)
            $min_required = 100;
            
            if ($balance < $min_required) {
                wp_send_json_error([
                    'message' => 'Insufficient subscription credit',
                    'code' => 'insufficient_tokens',
                    'status' => 'insufficient_tokens',
                    'balance' => $balance,
                    'required' => $min_required
                ]);
                return;
            }
            
            wp_send_json_success([
                'message' => 'Credits available',
                'balance' => $balance,
                'status' => 'ok'
            ]);
        } else {
            // If token system not available, assume credits are OK
            wp_send_json_success([
                'message' => 'Credit check bypassed (no token system)',
                'status' => 'ok'
            ]);
        }
    }

    /**
    * AJAX: Reset Builder - archive the active business instance and return to a blank preview state.
     * This intentionally detaches plugin state without deprovisioning third-party resources.
     */
    public function ajax_reset_builder() {
        check_ajax_referer('vmo_builder_nonce', 'security');

        if (!$this->current_user_can_use_builder_safety_tools()) {
            wp_send_json_error(['message' => 'You do not have permission to reset the builder.', 'code' => 'forbidden'], 403);
            return;
        }

        try {
            $user_id = get_current_user_id();
            $session_id = sanitize_text_field(wp_unslash($_POST['session_id'] ?? ''));
            $confirmation = sanitize_text_field(wp_unslash($_POST['confirmation'] ?? ''));

            if ($confirmation !== 'RESET_BUILDER') {
                wp_send_json_error(['message' => 'Reset Builder confirmation required. Consultation restarts use the consultation-only reset flow.', 'code' => 'confirmation_required']);
                return;
            }

            if (empty($session_id)) {
                $session_id = (string) get_user_meta($user_id, 'vmo_active_consultation_session', true);
            }

            $context = $this->normalize_builder_reset_context($session_id, $user_id, [
                'current_project_post_id' => isset($_POST['current_project_post_id']) ? intval($_POST['current_project_post_id']) : 0,
                'current_project_title' => sanitize_text_field(wp_unslash($_POST['current_project_title'] ?? '')),
                'current_live_page_id' => isset($_POST['current_live_page_id']) ? intval($_POST['current_live_page_id']) : 0,
            ]);

            $backup = $this->create_full_business_backup($context['session_id'], $user_id, $context);
            $this->detach_builder_business_state($user_id, $context['session_id'], $context, $backup['backup_id'], $backup['snapshot']);

            $new_session_id = $this->generateSessionId();
            update_user_meta($user_id, 'vmo_builder_reset_to_demo', 1);
            update_user_meta($user_id, 'vmo_builder_last_reset_backup', $backup['backup_id']);
            update_user_meta($user_id, 'vmo_builder_last_reset_at', current_time('mysql'));

            error_log("VMO: User {$user_id} reset builder. Old session: {$context['session_id']}, New session: {$new_session_id}, Backup: {$backup['backup_id']}");

            wp_send_json_success([
                'message' => 'Reset Builder archived the current business instance and returned the builder to the shipped preview state.',
                'old_session_id' => $context['session_id'],
                'new_session_id' => $new_session_id,
                'backup_id' => $backup['backup_id'],
                'reset_complete' => true,
                'backups_retained' => 3,
                'can_restore' => true,
                'project_title' => $context['current_project_title'] ?? '',
            ]);

        } catch (\Throwable $e) {
            error_log('VMO Reset Builder Error: ' . $e->getMessage());
            wp_send_json_error(['message' => 'Reset Builder failed: ' . $e->getMessage()]);
        }
    }

    /**
     * AJAX: Restart only the consultation session. This must never detach the
     * builder website, unpublish a page, or invoke Reset Builder backups.
     */
    public function ajax_reset_consultation() {
        check_ajax_referer('vmo_builder_nonce', 'security');

        if (!is_user_logged_in()) {
            wp_send_json_error(['message' => 'You must be logged in to restart the consultation.', 'code' => 'forbidden'], 403);
            return;
        }

        $user_id = get_current_user_id();
        $old_session_id = sanitize_text_field(wp_unslash($_POST['session_id'] ?? ''));
        if ($old_session_id === '') {
            $old_session_id = (string) get_user_meta($user_id, 'vmo_active_consultation_session', true);
        }

        $new_session_id = $this->generateSessionId();
        update_user_meta($user_id, 'vmo_active_consultation_session', $new_session_id);

        wp_send_json_success([
            'message' => 'Consultation restarted. Builder preview, published website, and Reset Builder backups were not changed.',
            'old_session_id' => $old_session_id,
            'new_session_id' => $new_session_id,
            'reset_complete' => true,
            'builder_untouched' => true,
        ]);
    }

    /**
     * AJAX: Return the three newest Reset Builder backups for the current user.
     */
    public function ajax_list_builder_backups() {
        check_ajax_referer('vmo_builder_nonce', 'security');

        if (!$this->current_user_can_use_builder_safety_tools()) {
            wp_send_json_error(['message' => 'You do not have permission to view Reset Builder backups.', 'code' => 'forbidden'], 403);
            return;
        }

        try {
            $user_id = get_current_user_id();
            $this->ensure_builder_backup_table_exists();

            global $wpdb;
            $table = $this->get_builder_backup_table_name();
            $rows = $wpdb->get_results($wpdb->prepare(
                "SELECT backup_id, session_id, project_title, created_at, restore_count, backup_reason
                 FROM {$table}
                 WHERE user_id = %d
                 ORDER BY created_at DESC, id DESC
                 LIMIT 3",
                $user_id
            ), ARRAY_A);

            wp_send_json_success([
                'backups' => array_map(function ($row) {
                    return [
                        'backup_id' => (string) ($row['backup_id'] ?? ''),
                        'session_id' => (string) ($row['session_id'] ?? ''),
                        'project_title' => (string) ($row['project_title'] ?? ''),
                        'created_at' => (string) ($row['created_at'] ?? ''),
                        'restore_count' => (int) ($row['restore_count'] ?? 0),
                        'backup_reason' => (string) ($row['backup_reason'] ?? 'reset_builder'),
                    ];
                }, is_array($rows) ? $rows : []),
            ]);
        } catch (\Throwable $e) {
            error_log('VMO List Builder Backups Error: ' . $e->getMessage());
            wp_send_json_error(['message' => 'Could not load Reset Builder backups: ' . $e->getMessage()]);
        }
    }

    /**
     * AJAX: Restore a previously archived Reset Builder backup.
     */
    public function ajax_restore_builder_backup() {
        check_ajax_referer('vmo_builder_nonce', 'security');

        if (!$this->current_user_can_use_builder_safety_tools()) {
            wp_send_json_error(['message' => 'You do not have permission to restore builder backups.', 'code' => 'forbidden'], 403);
            return;
        }

        try {
            $user_id = get_current_user_id();
            $backup_id = sanitize_text_field(wp_unslash($_POST['backup_id'] ?? ''));
            $confirmation = sanitize_text_field(wp_unslash($_POST['confirmation'] ?? ''));

            if ($backup_id === '') {
                wp_send_json_error(['message' => 'Backup ID is required.']);
                return;
            }

            if ($confirmation !== 'RESTORE_BUILDER_BACKUP') {
                wp_send_json_error(['message' => 'Restore confirmation required.']);
                return;
            }

            $this->ensure_builder_backup_table_exists();

            global $wpdb;
            $table = $this->get_builder_backup_table_name();
            $row = $wpdb->get_row($wpdb->prepare(
                "SELECT * FROM {$table} WHERE backup_id = %s AND user_id = %d LIMIT 1",
                $backup_id,
                $user_id
            ), ARRAY_A);

            if (!$row) {
                wp_send_json_error(['message' => 'Backup not found.']);
                return;
            }

            $snapshot = $this->decode_builder_backup_snapshot($row['snapshot'] ?? '', $row['snapshot_checksum'] ?? '');
            if (!is_array($snapshot)) {
                throw new \RuntimeException('Backup payload could not be decoded.');
            }

            $snapshot_user_id = intval($snapshot['meta']['user_id'] ?? 0);
            if ($snapshot_user_id > 0 && $snapshot_user_id !== $user_id) {
                throw new \RuntimeException('Backup ownership verification failed.');
            }

            $current_session_id = (string) get_user_meta($user_id, 'vmo_active_consultation_session', true);
            $current_context = $this->normalize_builder_reset_context($current_session_id, $user_id, [
                'current_project_post_id' => isset($_POST['current_project_post_id']) ? intval($_POST['current_project_post_id']) : 0,
                'current_project_title' => sanitize_text_field(wp_unslash($_POST['current_project_title'] ?? '')),
                'current_live_page_id' => isset($_POST['current_live_page_id']) ? intval($_POST['current_live_page_id']) : 0,
            ]);
            $current_snapshot = $this->build_builder_backup_snapshot($current_context['session_id'], $user_id, $current_context);
            $this->detach_builder_business_state($user_id, $current_context['session_id'], $current_context, $backup_id, $current_snapshot);

            $restore_result = $this->restore_builder_backup_snapshot($snapshot, $user_id, $backup_id);

            $wpdb->update(
                $table,
                [
                    'restored_at' => current_time('mysql'),
                    'restore_count' => intval($row['restore_count'] ?? 0) + 1,
                ],
                ['id' => intval($row['id'])],
                ['%s', '%d'],
                ['%d']
            );

            error_log("VMO: User {$user_id} restored builder backup {$backup_id}. Restored session: " . ($restore_result['session_id'] ?? ''));

            wp_send_json_success([
                'message' => 'Backup restored. The website is back in the builder, but it remains unpublished until you publish again.',
                'backup_id' => $backup_id,
                'restored_session_id' => $restore_result['session_id'] ?? '',
                'project_title' => $restore_result['project_title'] ?? '',
                'project_id' => $restore_result['project_id'] ?? 0,
                'project' => $restore_result['project'] ?? null,
                'requires_publish' => true,
            ]);
        } catch (\Throwable $e) {
            error_log('VMO Restore Builder Backup Error: ' . $e->getMessage());
            wp_send_json_error(['message' => 'Restore failed: ' . $e->getMessage()]);
        }
    }

    /**
     * AJAX: Get progress tracking WITH vision comparison.
     * ✅ VISION-CONNECTED: Returns not just milestone progress, but also a comparison
     * of where the business currently stands vs. the original consultation vision.
     * This is the core of the "5-year vision tracking" promise.
     */
    public function ajax_get_vision_progress() {
        check_ajax_referer('vmo_builder_nonce', 'security');

        $session_id = sanitize_text_field($_POST['session_id'] ?? '');
        $user_id = get_current_user_id();

        try {
            // Load consultation data to get the original vision
            $row = null;
            if ($session_id) {
                $row = $this->consultation_db->get_consultation_by_session($session_id);
            }
            if (!$row && $user_id) {
                $row = $this->consultation_db->get_latest_consultation_by_user($user_id);
            }
            if (!$row) {
                wp_send_json_success([
                    'has_vision' => false,
                    'message' => 'No consultation data found. Complete the consultation first to set your vision.'
                ]);
                return;
            }

            $cd = json_decode($row->consultation_data ?: '{}', true);
            if (!is_array($cd)) $cd = [];

            $bp = $cd['business_profile'] ?? [];
            $targets = $bp['targets'] ?? [];
            $vision_data = $bp['vision'] ?? [];
            $ex = $cd['extracted_information'] ?? [];
            $roadmap = $cd['roadmap_json'] ?? [];

            // Build vision summary
            $vision_summary = [
                'business_name' => $bp['identity']['name'] ?? $ex['businessName'] ?? 'Your Business',
                'mission' => $vision_data['mission'] ?? '',
                'vision' => $vision_data['vision'] ?? '',
                'core_values' => $vision_data['core_values'] ?? [],
                'north_star_metric' => $targets['north_star_metric'] ?? '',
                'targets' => [
                    'short_term' => $targets['short_term'] ?? '',
                    'mid_term' => $targets['mid_term'] ?? '',
                    'long_term' => $targets['long_term'] ?? '',
                ],
                'consultation_date' => $row->created_at ?? '',
                'days_since_consultation' => $row->created_at ? max(0, (int)((time() - strtotime($row->created_at)) / 86400)) : 0,
            ];

            // Calculate roadmap milestone completion
            $milestone_progress = [];
            $milestones = $roadmap['milestones'] ?? [];
            $total_milestones = count($milestones);
            $completed_count = 0;
            foreach ($milestones as $m) {
                if (($m['status'] ?? '') === 'completed') $completed_count++;
                $milestone_progress[] = [
                    'title' => $m['title'] ?? 'Milestone',
                    'status' => $m['status'] ?? 'planned',
                    'progress' => (int)($m['progress'] ?? 0),
                    'target_date' => $m['target_date'] ?? '',
                    'vision_alignment' => $m['vision_alignment'] ?? '',
                ];
            }

            // Document store stats (what the AI remembers)
            $doc_store = $cd['document_store'] ?? [];
            $memory_stats = [
                'active_documents' => count($doc_store['uploads'] ?? []),
                'archived_summaries' => count($doc_store['archived_upload_summaries'] ?? []),
                'has_blueprint' => !empty($doc_store['blueprint_raw']),
                'has_blueprint_summary' => !empty($doc_store['blueprint_summary']),
                'total_documents_ever_processed' => count($doc_store['uploads'] ?? []) + count($doc_store['archived_upload_summaries'] ?? []),
            ];

            wp_send_json_success([
                'has_vision' => true,
                'vision' => $vision_summary,
                'roadmap_progress' => [
                    'total_milestones' => $total_milestones,
                    'completed' => $completed_count,
                    'completion_percentage' => $total_milestones > 0 ? round(($completed_count / $total_milestones) * 100) : 0,
                    'milestones' => $milestone_progress,
                ],
                'memory_stats' => $memory_stats,
                'vision_anchor' => $roadmap['vision_anchor'] ?? [],
            ]);
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Failed to get vision progress: ' . $e->getMessage()]);
        }
    }

    // ═══════════════════════════════════════════════════════════════════
    // Vision Roadmap Engine — AJAX Handlers
    // ═══════════════════════════════════════════════════════════════════

    /**
     * Helper: Get the Vision Roadmap Engine singleton (with existence check).
     */
    private function get_roadmap_engine() {
        if (!class_exists('VMO_Vision_Roadmap_Engine')) {
            return null;
        }
        return VMO_Vision_Roadmap_Engine::get_instance();
    }

    /**
     * AJAX: Submit a daily report — records what actually happened vs. what was planned.
     * Adjusts vision target date based on progress.
     */
    public function ajax_submit_daily_report() {
        check_ajax_referer('vmo_builder_nonce', 'security');

        $engine = $this->get_roadmap_engine();
        if (!$engine) {
            wp_send_json_error(['message' => 'Roadmap engine not available']);
            return;
        }

        $roadmap_id            = intval($_POST['roadmap_id'] ?? 0);
        $user_id               = get_current_user_id();
        $day_number            = intval($_POST['day_number'] ?? 0);
        $completed_activity_ids = json_decode(wp_unslash($_POST['completed_activity_ids'] ?? '[]'), true);
        $user_notes            = sanitize_textarea_field($_POST['user_notes'] ?? '');
        $blockers              = sanitize_textarea_field($_POST['blockers'] ?? '');
        $wins                  = sanitize_textarea_field($_POST['wins'] ?? '');

        if (!$roadmap_id || !$day_number) {
            wp_send_json_error(['message' => 'Missing roadmap_id or day_number']);
            return;
        }

        if (!is_array($completed_activity_ids)) {
            $completed_activity_ids = [];
        }

        try {
            $result = $engine->submit_daily_report(
                $roadmap_id,
                $user_id,
                $day_number,
                $completed_activity_ids,
                $user_notes,
                $blockers,
                $wins
            );

            wp_send_json_success($result);
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Failed to submit daily report: ' . $e->getMessage()]);
        }
    }

    /**
     * AJAX: Get a comprehensive progress summary for the active roadmap.
     */
    public function ajax_get_progress_summary() {
        check_ajax_referer('vmo_builder_nonce', 'security');

        $engine = $this->get_roadmap_engine();
        if (!$engine) {
            wp_send_json_error(['message' => 'Roadmap engine not available']);
            return;
        }

        $session_id = sanitize_text_field($_POST['session_id'] ?? '');
        $user_id    = get_current_user_id();

        if (!$session_id) {
            wp_send_json_error(['message' => 'Missing session_id']);
            return;
        }

        try {
            $active = $engine->get_active_roadmap($session_id, $user_id);
            if (!$active) {
                wp_send_json_error(['message' => 'No active roadmap found for this session']);
                return;
            }

            $summary = $engine->get_progress_summary($active->id, $user_id);
            wp_send_json_success($summary);
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Failed to get progress summary: ' . $e->getMessage()]);
        }
    }

    /**
     * AJAX: Apply adjustments from deep research or user requests.
     * Supports: add_milestone, add_activity, modify_timeline, remove_activity, reprioritize.
     */
    public function ajax_apply_roadmap_adjustments() {
        check_ajax_referer('vmo_builder_nonce', 'security');

        $engine = $this->get_roadmap_engine();
        if (!$engine) {
            wp_send_json_error(['message' => 'Roadmap engine not available']);
            return;
        }

        $roadmap_id  = intval($_POST['roadmap_id'] ?? 0);
        $user_id     = get_current_user_id();
        $adjustments = json_decode(wp_unslash($_POST['adjustments'] ?? '[]'), true);

        if (!$roadmap_id) {
            wp_send_json_error(['message' => 'Missing roadmap_id']);
            return;
        }

        if (!is_array($adjustments) || empty($adjustments)) {
            wp_send_json_error(['message' => 'No adjustments provided']);
            return;
        }

        try {
            $result = $engine->apply_adjustments($roadmap_id, $user_id, $adjustments);
            wp_send_json_success($result);
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Failed to apply adjustments: ' . $e->getMessage()]);
        }
    }

    /**
     * AJAX: Export daily activities for a date range (CSV-ready array).
     */
    public function ajax_export_daily_activities() {
        check_ajax_referer('vmo_builder_nonce', 'security');

        $engine = $this->get_roadmap_engine();
        if (!$engine) {
            wp_send_json_error(['message' => 'Roadmap engine not available']);
            return;
        }

        $roadmap_id = intval($_POST['roadmap_id'] ?? 0);
        $start_date = sanitize_text_field($_POST['start_date'] ?? '');
        $end_date   = sanitize_text_field($_POST['end_date'] ?? '');

        if (!$roadmap_id) {
            wp_send_json_error(['message' => 'Missing roadmap_id']);
            return;
        }

        try {
            $data = $engine->export_daily_activities($roadmap_id, $start_date, $end_date);
            wp_send_json_success(['activities' => $data, 'count' => count($data)]);
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Export failed: ' . $e->getMessage()]);
        }
    }

    /**
     * AJAX: Export daily reports for a date range.
     */
    public function ajax_export_daily_reports() {
        check_ajax_referer('vmo_builder_nonce', 'security');

        $engine = $this->get_roadmap_engine();
        if (!$engine) {
            wp_send_json_error(['message' => 'Roadmap engine not available']);
            return;
        }

        $roadmap_id = intval($_POST['roadmap_id'] ?? 0);
        $start_date = sanitize_text_field($_POST['start_date'] ?? '');
        $end_date   = sanitize_text_field($_POST['end_date'] ?? '');

        if (!$roadmap_id) {
            wp_send_json_error(['message' => 'Missing roadmap_id']);
            return;
        }

        try {
            $data = $engine->export_daily_reports($roadmap_id, $start_date, $end_date);
            wp_send_json_success(['reports' => $data, 'count' => count($data)]);
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Export failed: ' . $e->getMessage()]);
        }
    }

    /**
     * AJAX: Export milestones for a date range.
     */
    public function ajax_export_milestones() {
        check_ajax_referer('vmo_builder_nonce', 'security');

        $engine = $this->get_roadmap_engine();
        if (!$engine) {
            wp_send_json_error(['message' => 'Roadmap engine not available']);
            return;
        }

        $roadmap_id = intval($_POST['roadmap_id'] ?? 0);
        $start_date = sanitize_text_field($_POST['start_date'] ?? '');
        $end_date   = sanitize_text_field($_POST['end_date'] ?? '');

        if (!$roadmap_id) {
            wp_send_json_error(['message' => 'Missing roadmap_id']);
            return;
        }

        try {
            $data = $engine->export_milestones($roadmap_id, $start_date, $end_date);
            wp_send_json_success(['milestones' => $data, 'count' => count($data)]);
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Export failed: ' . $e->getMessage()]);
        }
    }

    /**
     * AJAX: Get activities for a specific day number.
     */
    public function ajax_get_day_activities() {
        check_ajax_referer('vmo_builder_nonce', 'security');

        $engine = $this->get_roadmap_engine();
        if (!$engine) {
            wp_send_json_error(['message' => 'Roadmap engine not available']);
            return;
        }

        $roadmap_id = intval($_POST['roadmap_id'] ?? 0);
        $day_number = intval($_POST['day_number'] ?? 0);

        if (!$roadmap_id || !$day_number) {
            wp_send_json_error(['message' => 'Missing roadmap_id or day_number']);
            return;
        }

        try {
            $activities = $engine->get_day_activities($roadmap_id, $day_number);
            wp_send_json_success([
                'day_number'  => $day_number,
                'activities'  => $activities,
                'count'       => count($activities),
            ]);
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Failed to get day activities: ' . $e->getMessage()]);
        }
    }

    /**
     * AJAX: Get the daily report for a specific day.
     */
    public function ajax_get_daily_report() {
        check_ajax_referer('vmo_builder_nonce', 'security');

        $engine = $this->get_roadmap_engine();
        if (!$engine) {
            wp_send_json_error(['message' => 'Roadmap engine not available']);
            return;
        }

        $roadmap_id = intval($_POST['roadmap_id'] ?? 0);
        $day_number = intval($_POST['day_number'] ?? 0);

        if (!$roadmap_id || !$day_number) {
            wp_send_json_error(['message' => 'Missing roadmap_id or day_number']);
            return;
        }

        try {
            $report = $engine->get_daily_report($roadmap_id, $day_number);
            if ($report) {
                wp_send_json_success(['report' => $report]);
            } else {
                wp_send_json_success(['report' => null, 'message' => 'No report submitted for this day yet']);
            }
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Failed to get daily report: ' . $e->getMessage()]);
        }
    }

    /**
     * AJAX: Get pending adjustments from deep research that need user approval.
     */
    public function ajax_get_pending_adjustments() {
        check_ajax_referer('vmo_builder_nonce', 'security');

        $engine = $this->get_roadmap_engine();
        if (!$engine) {
            wp_send_json_error(['message' => 'Roadmap engine not available']);
            return;
        }

        $roadmap_id = intval($_POST['roadmap_id'] ?? 0);

        if (!$roadmap_id) {
            wp_send_json_error(['message' => 'Missing roadmap_id']);
            return;
        }

        try {
            $adjustments = $engine->get_pending_adjustments($roadmap_id);
            wp_send_json_success([
                'adjustments' => $adjustments,
                'count'       => count($adjustments),
            ]);
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Failed to get pending adjustments: ' . $e->getMessage()]);
        }
    }
    
    /**
     * AJAX: Get current website state
     */
    public function ajax_get_website_state() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        $draft_id = sanitize_text_field(wp_unslash($_POST['draft_id']));
        $user_id = get_current_user_id();
        
        global $wpdb;
        $draft = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}vmo_website_drafts 
             WHERE draft_id = %s AND user_id = %d",
            $draft_id,
            $user_id
        ));
        
        if (!$draft) {
            wp_send_json_error(['message' => 'Website not found']);
            return;
        }
        
        // Count available undos
        $undo_count = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$wpdb->prefix}vmo_website_versions 
             WHERE draft_id = %s AND can_undo = 1",
            $draft_id
        ));
        
        wp_send_json_success([
            'draft_id' => $draft->draft_id,
            'is_published' => (bool)$draft->is_published,
            'website_name' => $draft->website_name,
            'published_url' => $draft->published_url,
            'has_unsaved_changes' => (bool)$draft->has_unsaved_live_changes,
            'undo_count' => (int)$undo_count,
            'can_undo' => $undo_count > 0,
            'created_at' => $draft->created_at,
            'updated_at' => $draft->updated_at
        ]);
    }
    
    /**
     * AJAX: Check for unsaved changes before navigation
     */
    public function ajax_check_unsaved_changes() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        $draft_id = sanitize_text_field(wp_unslash($_POST['draft_id']));
        $user_id = get_current_user_id();
        
        global $wpdb;
        $draft = $wpdb->get_row($wpdb->prepare(
            "SELECT has_unsaved_live_changes FROM {$wpdb->prefix}vmo_website_drafts 
             WHERE draft_id = %s AND user_id = %d",
            $draft_id,
            $user_id
        ));
        
        wp_send_json_success([
            'has_unsaved_changes' => $draft ? (bool)$draft->has_unsaved_live_changes : false
        ]);
    }

    /**
     * AJAX: Update generic user preference
     */
    public function ajax_update_user_preference() {
        check_ajax_referer('vmo_builder_nonce', 'nonce');
        
        if (!is_user_logged_in()) {
            wp_send_json_error(['message' => 'Not logged in']);
        }
        
        $preference = sanitize_key($_POST['preference']);
        $value = sanitize_text_field(wp_unslash($_POST['value']));
        
        // Whitelist allowed preferences for security
        $allowed = ['call_recording_enabled', 'auto_reply_enabled', 'theme_mode'];
        if (!in_array($preference, $allowed)) {
            wp_send_json_error(['message' => 'Invalid preference']);
        }
        
        // Save to user meta
        $user_id = get_current_user_id();
        update_user_meta($user_id, $preference, $value);
        
        wp_send_json_success(['message' => 'Preference saved', 'key' => $preference, 'value' => $value]);
    }
    
    /**
     * AJAX: Generate comprehensive business roadmap
     */
    public function ajax_generate_business_roadmap() {
        $this->prepare_long_running_runtime();

        check_ajax_referer('vmo_builder_nonce', 'security');
        
        // ✅ TOKEN DEDUCTION v8.1.0: Charge for roadmap generation
        $user_id = get_current_user_id();
        if (function_exists('vmo_deduct_tokens_for_operation')) {
            if (!vmo_deduct_tokens_for_operation($user_id, 'roadmap')) {
                wp_send_json_error([
                    'message' => 'Insufficient tokens for roadmap generation. Please top up your balance.',
                    'status' => 'insufficient_tokens'
                ]);
                return;
            }
        }
        
        $session_id = sanitize_text_field($_POST['session_id'] ?? '');
        if (!$session_id) {
            wp_send_json_error(['message' => 'Missing session_id']);
        }

        if (!class_exists('VMO_Phase_Output_Manager')) {
            wp_send_json_error(['message' => 'Phase output manager not available']);
        }
        
        try {
            $output_manager = VMO_Phase_Output_Manager::get_instance();
            $user_id = get_current_user_id();

            // Approval-gated: requires an approved execution plan (Phase 4)
            $generated = $output_manager->generate_business_roadmap($session_id, $user_id);
            if (!$generated) {
                wp_send_json_error([
                    'message' => 'Cannot generate roadmap yet. Ensure the Phase 4 execution plan is approved first.'
                ]);
            }

            // Return the newly generated roadmap (single source of truth)
            $roadmap_output = $output_manager->get_phase_output($session_id, 'business_roadmap', 4);
            $roadmap_data = $roadmap_output ? $roadmap_output->output_data : null;

            wp_send_json_success([
                'message' => 'Business roadmap generated successfully',
                'roadmap' => $roadmap_data
            ]);
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Failed to generate roadmap: ' . $e->getMessage()]);
        }
    }
    
    /**
     * AJAX: Generate daily activities schedule
     */
    public function ajax_generate_daily_activities() {
        $this->prepare_long_running_runtime();

        check_ajax_referer('vmo_builder_nonce', 'security');
        
        $session_id = sanitize_text_field($_POST['session_id'] ?? '');
        if (!$session_id) {
            wp_send_json_error(['message' => 'Missing session_id']);
        }

        if (!class_exists('VMO_Phase_Output_Manager')) {
            wp_send_json_error(['message' => 'Phase output manager not available']);
        }
        
        try {
            $output_manager = VMO_Phase_Output_Manager::get_instance();
            $user_id = get_current_user_id();

            // Ensure roadmap exists (roadmap generation also seeds daily activities)
            $roadmap_output = $output_manager->get_phase_output($session_id, 'business_roadmap', 4);
            if (!$roadmap_output) {
                $generated = $output_manager->generate_business_roadmap($session_id, $user_id);
                if (!$generated) {
                    wp_send_json_error([
                        'message' => 'Cannot generate daily activities yet. Ensure the Phase 4 execution plan is approved first.'
                    ]);
                }
            }

            $todays = $output_manager->get_todays_activities($user_id);
            $upcoming = $output_manager->get_upcoming_activities($user_id, 14);

            wp_send_json_success([
                'message' => 'Daily activities are ready',
                'today' => $todays,
                'upcoming' => $upcoming
            ]);
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Failed to generate daily activities: ' . $e->getMessage()]);
        }
    }
    
    /**
     * AJAX: Get structured phase outputs for a session
     */
    public function ajax_get_phase_outputs() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        $session_id = sanitize_text_field($_POST['session_id'] ?? '');
        $phase_number = intval($_POST['phase_number'] ?? 0);
        
        if (!$session_id) {
            wp_send_json_error(['message' => 'Missing session_id']);
        }
        
        if (!class_exists('VMO_Phase_Output_Manager')) {
            wp_send_json_error(['message' => 'Phase output manager not available']);
        }
        
        try {
            $output_manager = VMO_Phase_Output_Manager::get_instance();
            
            if ($phase_number > 0) {
                $outputs = $output_manager->get_all_phase_outputs($session_id, $phase_number);
                $status = $output_manager->get_phase_status($session_id, $phase_number);
            } else {
                // Get all phase outputs
                $outputs = [];
                for ($i = 1; $i <= 8; $i++) {
                    $phase_outputs = $output_manager->get_all_phase_outputs($session_id, $i);
                    if (!empty($phase_outputs)) {
                        $outputs["phase_{$i}"] = $phase_outputs;
                    }
                }
                $status = [];
                for ($i = 1; $i <= 8; $i++) {
                    $status["phase_{$i}"] = $output_manager->get_phase_status($session_id, $i);
                }
            }
            
            wp_send_json_success([
                'outputs' => $outputs,
                'status' => $status
            ]);
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Failed to get phase outputs: ' . $e->getMessage()]);
        }
    }
    
    /**
     * AJAX: Get progress tracking for user's active roadmap
     */
    public function ajax_get_progress_tracking() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        $roadmap_id = sanitize_text_field($_POST['roadmap_id'] ?? '');
        
        if (!class_exists('VMO_Phase_Output_Manager')) {
            wp_send_json_error(['message' => 'Phase output manager not available']);
        }
        
        try {
            $output_manager = VMO_Phase_Output_Manager::get_instance();
            $user_id = get_current_user_id();
            
            // If no roadmap_id provided, get the user's active roadmap
            if (empty($roadmap_id)) {
                global $wpdb;
                $roadmaps_table = $wpdb->prefix . 'vmo_business_roadmaps';
                $roadmap_id = $wpdb->get_var($wpdb->prepare(
                    "SELECT roadmap_id FROM {$roadmaps_table} WHERE user_id = %d AND is_active = 1 ORDER BY created_at DESC LIMIT 1",
                    $user_id
                ));
            }
            
            if (empty($roadmap_id)) {
                wp_send_json_success([
                    'has_roadmap' => false,
                    'message' => 'No active roadmap found'
                ]);
            }
            
            $progress = $output_manager->calculate_progress($roadmap_id, $user_id);
            
            wp_send_json_success([
                'has_roadmap' => true,
                'progress' => $progress
            ]);
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Failed to get progress: ' . $e->getMessage()]);
        }
    }
    
    /**
     * AJAX: Get today's activities for the user
     */
    public function ajax_get_todays_activities() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        if (!class_exists('VMO_Phase_Output_Manager')) {
            wp_send_json_error(['message' => 'Phase output manager not available']);
        }
        
        try {
            $output_manager = VMO_Phase_Output_Manager::get_instance();
            $user_id = get_current_user_id();
            
            $todays = $output_manager->get_todays_activities($user_id);
            $upcoming = $output_manager->get_upcoming_activities($user_id, 7);
            
            wp_send_json_success([
                'today' => $todays,
                'upcoming' => $upcoming,
                'date' => date('Y-m-d')
            ]);
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Failed to get activities: ' . $e->getMessage()]);
        }
    }
    
    /**
     * AJAX: Mark an activity as completed
     */
    public function ajax_complete_activity() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        $activity_id = sanitize_text_field($_POST['activity_id'] ?? '');
        $notes = sanitize_textarea_field($_POST['notes'] ?? '');
        
        if (empty($activity_id)) {
            wp_send_json_error(['message' => 'Missing activity_id']);
        }
        
        if (!class_exists('VMO_Phase_Output_Manager')) {
            wp_send_json_error(['message' => 'Phase output manager not available']);
        }
        
        try {
            $output_manager = VMO_Phase_Output_Manager::get_instance();
            $result = $output_manager->complete_activity($activity_id, $notes);
            
            if ($result) {
                wp_send_json_success(['message' => 'Activity marked as completed']);
            } else {
                wp_send_json_error(['message' => 'Failed to complete activity']);
            }
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Error: ' . $e->getMessage()]);
        }
    }
    
    /**
     * AJAX: Get business roadmap data
     */
    public function ajax_get_business_roadmap() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        $session_id = sanitize_text_field($_POST['session_id'] ?? '');
        
        if (!class_exists('VMO_Phase_Output_Manager')) {
            wp_send_json_error(['message' => 'Phase output manager not available']);
        }
        
        try {
            $output_manager = VMO_Phase_Output_Manager::get_instance();
            $user_id = get_current_user_id();
            
            // Get roadmap from phase outputs
            $roadmap_output = $output_manager->get_phase_output($session_id, 'business_roadmap', 4);
            
            if (!$roadmap_output) {
                // Try to get from dedicated table
                global $wpdb;
                $roadmaps_table = $wpdb->prefix . 'vmo_business_roadmaps';
                
                $query = "SELECT * FROM {$roadmaps_table} WHERE user_id = %d AND is_active = 1 ORDER BY created_at DESC LIMIT 1";
                $params = [$user_id];
                
                if (!empty($session_id)) {
                    $query = "SELECT * FROM {$roadmaps_table} WHERE session_id = %s AND is_active = 1 ORDER BY created_at DESC LIMIT 1";
                    $params = [$session_id];
                }
                
                $roadmap = $wpdb->get_row($wpdb->prepare($query, ...$params));
                
                if ($roadmap) {
                    wp_send_json_success([
                        'has_roadmap' => true,
                        'roadmap' => json_decode($roadmap->roadmap_data, true),
                        'roadmap_id' => $roadmap->roadmap_id,
                        'roadmap_created_at' => $roadmap->created_at,
                        'roadmap_updated_at' => $roadmap->updated_at,
                        'current_day' => $roadmap->current_day_number,
                        'total_days' => $roadmap->total_duration_days
                    ]);
                } else {
                    wp_send_json_success([
                        'has_roadmap' => false,
                        'message' => 'No roadmap found'
                    ]);
                }
            } else {
                // Attach authoritative roadmap metadata if available in the dedicated table
                global $wpdb;
                $roadmaps_table = $wpdb->prefix . 'vmo_business_roadmaps';
                $query = "SELECT * FROM {$roadmaps_table} WHERE user_id = %d AND is_active = 1 ORDER BY created_at DESC LIMIT 1";
                $params = [$user_id];
                if (!empty($session_id)) {
                    $query = "SELECT * FROM {$roadmaps_table} WHERE session_id = %s AND is_active = 1 ORDER BY created_at DESC LIMIT 1";
                    $params = [$session_id];
                }
                $roadmap_row = $wpdb->get_row($wpdb->prepare($query, ...$params));

                wp_send_json_success([
                    'has_roadmap' => true,
                    'roadmap' => $roadmap_output->output_data,
                    'roadmap_id' => $roadmap_row ? $roadmap_row->roadmap_id : null,
                    'roadmap_created_at' => $roadmap_row ? $roadmap_row->created_at : null,
                    'roadmap_updated_at' => $roadmap_row ? $roadmap_row->updated_at : null
                ]);
            }
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Failed to get roadmap: ' . $e->getMessage()]);
        }
    }

    /**
     * AJAX: Download business roadmap export (JSON/CSV)
     * Params (POST): roadmap_id (optional), session_id (optional), format=json|csv
     */
    public function ajax_download_business_roadmap() {
        check_ajax_referer('vmo_builder_nonce', 'security');

        $format = sanitize_text_field($_POST['format'] ?? 'json');
        $roadmap_id = sanitize_text_field($_POST['roadmap_id'] ?? '');
        $session_id = sanitize_text_field($_POST['session_id'] ?? '');
        $user_id = get_current_user_id();

        global $wpdb;
        $roadmaps_table = $wpdb->prefix . 'vmo_business_roadmaps';

        $roadmap = null;
        if (!empty($roadmap_id)) {
            $roadmap = $wpdb->get_row($wpdb->prepare(
                "SELECT * FROM {$roadmaps_table} WHERE roadmap_id = %s AND user_id = %d",
                $roadmap_id,
                $user_id
            ));
        } elseif (!empty($session_id)) {
            $roadmap = $wpdb->get_row($wpdb->prepare(
                "SELECT * FROM {$roadmaps_table} WHERE session_id = %s AND user_id = %d AND is_active = 1 ORDER BY created_at DESC LIMIT 1",
                $session_id,
                $user_id
            ));
        } else {
            $roadmap = $wpdb->get_row($wpdb->prepare(
                "SELECT * FROM {$roadmaps_table} WHERE user_id = %d AND is_active = 1 ORDER BY created_at DESC LIMIT 1",
                $user_id
            ));
        }

        if (!$roadmap) {
            wp_send_json_error(['message' => 'No roadmap found']);
        }

        $roadmap_payload = json_decode($roadmap->roadmap_data, true);
        if (!is_array($roadmap_payload)) {
            $roadmap_payload = [];
        }

        $safe_date = date('Y-m-d');
        $filename_base = 'vmo-roadmap-' . preg_replace('/[^a-zA-Z0-9_-]/', '', (string)$roadmap->roadmap_id) . '-' . $safe_date;

        nocache_headers();
        if ($format === 'csv') {
            header('Content-Type: text/csv; charset=utf-8');
            header('Content-Disposition: attachment; filename="' . $filename_base . '.csv"');

            $out = fopen('php://output', 'w');
            fputcsv($out, ['milestone_id', 'name', 'phase', 'target_day', 'description', 'completion_criteria']);

            $milestones = $roadmap_payload['milestones'] ?? [];
            if (is_array($milestones)) {
                foreach ($milestones as $m) {
                    if (!is_array($m)) {
                        continue;
                    }
                    fputcsv($out, [
                        $m['milestone_id'] ?? '',
                        $m['name'] ?? '',
                        $m['phase'] ?? '',
                        $m['target_day'] ?? '',
                        $m['description'] ?? '',
                        $m['completion_criteria'] ?? ''
                    ]);
                }
            }
            fclose($out);
            wp_die();
        }

        header('Content-Type: application/json; charset=utf-8');
        header('Content-Disposition: attachment; filename="' . $filename_base . '.json"');
        echo wp_json_encode([
            'roadmap_id' => $roadmap->roadmap_id,
            'session_id' => $roadmap->session_id,
            'user_id' => (int)$roadmap->user_id,
            'roadmap_created_at' => $roadmap->created_at,
            'roadmap_updated_at' => $roadmap->updated_at,
            'exported_at' => current_time('mysql'),
            'roadmap' => $roadmap_payload
        ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
        wp_die();
    }

    /**
     * AJAX: Download daily activities export (JSON/CSV)
     * Params (POST): roadmap_id (optional), session_id (optional), format=json|csv
     */
    public function ajax_download_daily_activities() {
        check_ajax_referer('vmo_builder_nonce', 'security');

        $format = sanitize_text_field($_POST['format'] ?? 'csv');
        $roadmap_id = sanitize_text_field($_POST['roadmap_id'] ?? '');
        $session_id = sanitize_text_field($_POST['session_id'] ?? '');
        $user_id = get_current_user_id();

        global $wpdb;
        $roadmaps_table = $wpdb->prefix . 'vmo_business_roadmaps';
        $activities_table = $wpdb->prefix . 'vmo_daily_activities';

        if (empty($roadmap_id)) {
            if (!empty($session_id)) {
                $roadmap_id = $wpdb->get_var($wpdb->prepare(
                    "SELECT roadmap_id FROM {$roadmaps_table} WHERE session_id = %s AND user_id = %d AND is_active = 1 ORDER BY created_at DESC LIMIT 1",
                    $session_id,
                    $user_id
                ));
            } else {
                $roadmap_id = $wpdb->get_var($wpdb->prepare(
                    "SELECT roadmap_id FROM {$roadmaps_table} WHERE user_id = %d AND is_active = 1 ORDER BY created_at DESC LIMIT 1",
                    $user_id
                ));
            }
        }

        if (empty($roadmap_id)) {
            wp_send_json_error(['message' => 'No roadmap found']);
        }

        $roadmap_created_at = $wpdb->get_var($wpdb->prepare(
            "SELECT created_at FROM {$roadmaps_table} WHERE roadmap_id = %s AND user_id = %d LIMIT 1",
            $roadmap_id,
            $user_id
        ));

        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT activity_id, roadmap_id, milestone_id, day_number, activity_date, activity_title, activity_description, activity_type, estimated_duration_minutes, priority, status, expected_complete_by, actual_completed_at, completion_notes, ai_generated, recurring, recurrence_pattern, created_at, updated_at
             FROM {$activities_table}
             WHERE roadmap_id = %s AND user_id = %d
             ORDER BY day_number ASC",
            $roadmap_id,
            $user_id
        ), ARRAY_A);

        $safe_date = date('Y-m-d');
        $filename_base = 'vmo-daily-activities-' . preg_replace('/[^a-zA-Z0-9_-]/', '', (string)$roadmap_id) . '-' . $safe_date;

        nocache_headers();
        if ($format === 'json') {
            header('Content-Type: application/json; charset=utf-8');
            header('Content-Disposition: attachment; filename="' . $filename_base . '.json"');
            echo wp_json_encode([
                'roadmap_id' => $roadmap_id,
                'user_id' => (int)$user_id,
                'roadmap_created_at' => $roadmap_created_at,
                'exported_at' => current_time('mysql'),
                'activities' => is_array($rows) ? $rows : []
            ], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
            wp_die();
        }

        header('Content-Type: text/csv; charset=utf-8');
        header('Content-Disposition: attachment; filename="' . $filename_base . '.csv"');

        $out = fopen('php://output', 'w');
        fputcsv($out, [
            'activity_date',
            'day_number',
            'activity_title',
            'activity_type',
            'priority',
            'status',
            'estimated_duration_minutes',
            'milestone_id',
            'expected_complete_by',
            'actual_completed_at',
            'completion_notes'
        ]);
        if (is_array($rows)) {
            foreach ($rows as $r) {
                fputcsv($out, [
                    $r['activity_date'] ?? '',
                    $r['day_number'] ?? '',
                    $r['activity_title'] ?? '',
                    $r['activity_type'] ?? '',
                    $r['priority'] ?? '',
                    $r['status'] ?? '',
                    $r['estimated_duration_minutes'] ?? '',
                    $r['milestone_id'] ?? '',
                    $r['expected_complete_by'] ?? '',
                    $r['actual_completed_at'] ?? '',
                    $r['completion_notes'] ?? ''
                ]);
            }
        }
        fclose($out);
        wp_die();
    }
    
    /**
     * Add version snapshot for undo functionality
     */
    private function add_version_snapshot($draft_id, $change_description, $can_undo = true) {
        global $wpdb;
        $versions_table = $wpdb->prefix . 'vmo_website_versions';
        
        // Get current version count
        $version_count = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM $versions_table WHERE draft_id = %s",
            $draft_id
        ));
        
        // Limit to 25 undoable versions
        if ($version_count >= 26) {
            // Delete oldest undoable version
            $oldest = $wpdb->get_var($wpdb->prepare(
                "SELECT id FROM $versions_table 
                 WHERE draft_id = %s AND can_undo = 1 
                 ORDER BY version_number ASC LIMIT 1",
                $draft_id
            ));
            if ($oldest) {
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                $wpdb->delete($versions_table, ['id' => $oldest]);
            }
        }
        
        // Add new version
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->insert($versions_table, [
            'draft_id' => $draft_id,
            'user_id' => get_current_user_id(),
            'version_number' => $version_count + 1,
            'change_description' => $change_description,
            'website_snapshot' => $this->get_current_draft_data($draft_id),
            'can_undo' => $can_undo ? 1 : 0,
            'created_at' => current_time('mysql')
        ]);
    }
    
    /**
     * Update live published pages with new content
     */
    private function update_live_pages($page_ids, $updated_content) {
        foreach ($page_ids as $index => $page_id) {
            if (isset($updated_content['pages_created'][$index])) {
                $page_data = $updated_content['pages_created'][$index];
                wp_update_post([
                    'ID' => $page_id,
                    'post_content' => $page_data['html'],
                    'post_title' => $page_data['title']
                ]);
            }
        }
    }
    
    /**
     * Create website backup before deletion
     */
    private function create_website_backup($draft) {
        global $wpdb;
        $backups_table = $wpdb->prefix . 'vmo_website_backups';
        
        $backup_id = 'backup_' . uniqid();
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->insert($backups_table, [
            'backup_id' => $backup_id,
            'user_id' => $draft->user_id,
            'draft_id' => $draft->draft_id,
            'website_name' => $draft->website_name,
            'backup_data' => gzcompress(json_encode([
                'draft_data' => $draft,
                'versions' => $wpdb->get_results($wpdb->prepare(
                    "SELECT * FROM {$wpdb->prefix}vmo_website_versions WHERE draft_id = %s",
                    $draft->draft_id
                ))
            ])),
            'backup_type' => 'website_deletion',
            'created_at' => current_time('mysql')
        ]);
        
        return $backup_id;
    }

    private function get_builder_backup_table_name() {
        global $wpdb;
        return $wpdb->prefix . 'vmo_builder_backups';
    }

    private function current_user_can_use_builder_safety_tools() {
        $user_id = get_current_user_id();
        if ($user_id <= 0 || !current_user_can('read')) {
            return false;
        }

        return (bool) apply_filters('vmo_current_user_can_use_builder_safety_tools', true, $user_id);
    }

    private function ensure_builder_backup_table_exists() {
        global $wpdb;

        static $ensured = false;
        if ($ensured) {
            return;
        }

        $table = $this->get_builder_backup_table_name();
        $charset_collate = $wpdb->get_charset_collate();
        $sql = "CREATE TABLE {$table} (
            id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
            backup_id VARCHAR(96) NOT NULL,
            user_id BIGINT UNSIGNED NOT NULL,
            session_id VARCHAR(191) DEFAULT '',
            project_post_id BIGINT UNSIGNED DEFAULT 0,
            project_title VARCHAR(255) DEFAULT '',
            backup_reason VARCHAR(64) DEFAULT 'reset_builder',
            snapshot LONGTEXT NOT NULL,
            snapshot_checksum VARCHAR(64) DEFAULT '',
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            restored_at DATETIME DEFAULT NULL,
            restore_count INT UNSIGNED DEFAULT 0,
            PRIMARY KEY (id),
            UNIQUE KEY backup_id (backup_id),
            KEY user_id (user_id),
            KEY session_id (session_id),
            KEY created_at (created_at)
        ) {$charset_collate};";

        if (!function_exists('dbDelta')) {
            require_once ABSPATH . 'wp-admin/includes/upgrade.php';
        }

        dbDelta($sql);
        $ensured = true;
    }

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

        if ($table_name === '') {
            return false;
        }

        $found = $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $wpdb->esc_like($table_name)));
        return $found === $table_name;
    }

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

        if (strpos($table_name, $wpdb->prefix) === 0) {
            return substr($table_name, strlen($wpdb->prefix));
        }

        return $table_name;
    }

    private function get_builder_table_priority($table_name) {
        $priorities = array(
            'vmo_consultations' => 10,
            'vmo_projects' => 20,
            'vmo_contacts' => 30,
            'vmo_voice_conversations' => 40,
            'vmo_website_drafts' => 50,
            'vmo_consultation_media' => 60,
            'vmo_project_history' => 70,
            'vmo_voice_conversation_turns' => 80,
            'vmo_website_versions' => 90,
        );

        $normalized = $this->normalize_builder_table_name($table_name);
        return $priorities[$normalized] ?? 100;
    }

    private function sort_builder_tables_for_snapshot(array $tables) {
        usort($tables, function ($left, $right) {
            $left_priority = $this->get_builder_table_priority($left);
            $right_priority = $this->get_builder_table_priority($right);

            if ($left_priority === $right_priority) {
                return strcmp($this->normalize_builder_table_name($left), $this->normalize_builder_table_name($right));
            }

            return $left_priority <=> $right_priority;
        });

        return array_values($tables);
    }

    private function get_builder_backup_excluded_tables() {
        return array(
            'vmo_builder_backups',
            'vmo_website_backups',
            'vmo_token_transactions',
            'vmo_api_usage',
            'vmo_manager_users',
            'vmo_manager_activations',
            'vmo_manager_emails',
            'vmo_licenses',
            'vmo_usage_logs',
            'vmo_affiliates',
            'vmo_commissions',
            'vmo_support_tickets',
        );
    }

    private function get_builder_backup_tables() {
        global $wpdb;

        $like = $wpdb->esc_like($wpdb->prefix . 'vmo_') . '%';
        $tables = $wpdb->get_col($wpdb->prepare('SHOW TABLES LIKE %s', $like));
        $excluded = $this->get_builder_backup_excluded_tables();
        $filtered = array_values(array_filter((array) $tables, function ($table_name) use ($excluded) {
            return !in_array($this->normalize_builder_table_name($table_name), $excluded, true);
        }));

        return $this->sort_builder_tables_for_snapshot($filtered);
    }

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

        static $cache = array();
        if (isset($cache[$table_name])) {
            return $cache[$table_name];
        }

        $rows = $wpdb->get_results('DESCRIBE ' . $table_name, ARRAY_A);
        $cache[$table_name] = is_array($rows) ? $rows : array();
        return $cache[$table_name];
    }

    private function get_builder_primary_key($table_name) {
        foreach ($this->get_builder_table_columns($table_name) as $column) {
            if (($column['Key'] ?? '') === 'PRI') {
                return (string) ($column['Field'] ?? '');
            }
        }

        return '';
    }

    private function build_builder_in_condition($column_name, array $values) {
        $values = array_values(array_filter(array_map('strval', $values), function ($value) {
            return $value !== '';
        }));

        if (empty($values)) {
            return null;
        }

        $placeholders = implode(', ', array_fill(0, count($values), '%s'));
        return array(
            'sql' => $column_name . ' IN (' . $placeholders . ')',
            'params' => $values,
        );
    }

    private function get_latest_visible_builder_project($user_id) {
        $projects = $this->get_visible_builder_project_posts($user_id, 1);

        return !empty($projects) ? $projects[0] : null;
    }

    private function get_visible_builder_project_posts($user_id, $numberposts = -1) {
        return get_posts(array(
            'post_type' => 'vmo_project',
            'author' => $user_id,
            'post_status' => array('publish', 'private', 'draft'),
            'numberposts' => $numberposts,
            'orderby' => 'modified',
            'order' => 'DESC',
            'meta_query' => array(
                'relation' => 'OR',
                array(
                    'key' => 'vmo_builder_archived_by_reset',
                    'compare' => 'NOT EXISTS',
                ),
                array(
                    'key' => 'vmo_builder_archived_by_reset',
                    'value' => '1',
                    'compare' => '!=',
                ),
            ),
        ));
    }

    private function normalize_builder_reset_context($session_id, $user_id, array $context = array()) {
        global $wpdb;

        $normalized = array_merge(array(
            'session_id' => sanitize_text_field((string) $session_id),
            'current_project_post_id' => 0,
            'current_project_title' => '',
            'current_live_page_id' => 0,
            'project_post_ids' => array(),
            'live_page_ids' => array(),
            'db_project_row_ids' => array(),
            'draft_ids' => array(),
            'consultation_ids' => array(),
            'voice_conversation_ids' => array(),
            'contact_ids' => array(),
            'consultation_row' => null,
        ), $context);

        $normalized['current_project_post_id'] = intval($normalized['current_project_post_id']);
        $normalized['current_project_title'] = sanitize_text_field((string) $normalized['current_project_title']);
        $normalized['current_live_page_id'] = intval($normalized['current_live_page_id']);

        if ($normalized['session_id'] === '') {
            $normalized['session_id'] = (string) get_user_meta($user_id, 'vmo_active_consultation_session', true);
        }

        if ($normalized['current_project_post_id'] <= 0 && $normalized['current_project_title'] !== '') {
            $projects = get_posts(array(
                'post_type' => 'vmo_project',
                'author' => $user_id,
                'title' => $normalized['current_project_title'],
                'post_status' => array('publish', 'private', 'draft'),
                'numberposts' => 1,
                'meta_query' => array(
                    'relation' => 'OR',
                    array(
                        'key' => 'vmo_builder_archived_by_reset',
                        'compare' => 'NOT EXISTS',
                    ),
                    array(
                        'key' => 'vmo_builder_archived_by_reset',
                        'value' => '1',
                        'compare' => '!=',
                    ),
                ),
            ));

            if (!empty($projects)) {
                $normalized['current_project_post_id'] = intval($projects[0]->ID);
                $normalized['current_project_title'] = (string) $projects[0]->post_title;
            }
        }

        if ($normalized['current_project_post_id'] <= 0 && $normalized['session_id'] !== '') {
            $mapped_post_id = intval(get_option('vmo_website_post_' . $normalized['session_id'], 0));
            if ($mapped_post_id > 0) {
                $normalized['current_project_post_id'] = $mapped_post_id;
            }
        }

        if ($normalized['current_project_post_id'] <= 0) {
            $latest = $this->get_latest_visible_builder_project($user_id);
            if ($latest) {
                $normalized['current_project_post_id'] = intval($latest->ID);
                $normalized['current_project_title'] = (string) $latest->post_title;
            }
        }

        if ($normalized['current_project_post_id'] > 0) {
            $post = get_post($normalized['current_project_post_id']);
            if ($post) {
                $normalized['project_post_ids'][] = intval($post->ID);
                if ($normalized['current_project_title'] === '') {
                    $normalized['current_project_title'] = (string) $post->post_title;
                }
                if ($normalized['current_live_page_id'] <= 0) {
                    $normalized['current_live_page_id'] = intval(get_post_meta($post->ID, 'vmo_live_page_id', true));
                }
            } else {
                $normalized['current_project_post_id'] = 0;
            }
        }

        if ($normalized['current_live_page_id'] > 0) {
            $normalized['live_page_ids'][] = intval($normalized['current_live_page_id']);
        }

        if ($normalized['session_id'] !== '' && $this->consultation_db && method_exists($this->consultation_db, 'get_consultation_by_session')) {
            $consultation_row = $this->consultation_db->get_consultation_by_session($normalized['session_id']);
            if ($consultation_row) {
                $normalized['consultation_row'] = $consultation_row;
                if (!empty($consultation_row->id)) {
                    $normalized['consultation_ids'][] = intval($consultation_row->id);
                }

                $website_data = json_decode((string) ($consultation_row->website_data ?? ''), true);
                if (is_array($website_data) && !empty($website_data['draft_post_id']) && $normalized['current_project_post_id'] <= 0) {
                    $normalized['current_project_post_id'] = intval($website_data['draft_post_id']);
                    $normalized['project_post_ids'][] = intval($website_data['draft_post_id']);
                }
            }
        }

        if ($normalized['current_project_post_id'] > 0 && $normalized['current_project_title'] === '') {
            $normalized['current_project_title'] = (string) get_the_title($normalized['current_project_post_id']);
        }

        foreach ($this->get_visible_builder_project_posts($user_id) as $visible_project) {
            if (!$visible_project || empty($visible_project->ID)) {
                continue;
            }

            $normalized['project_post_ids'][] = intval($visible_project->ID);

            $visible_live_page_id = intval(get_post_meta($visible_project->ID, 'vmo_live_page_id', true));
            if ($visible_live_page_id > 0) {
                $normalized['live_page_ids'][] = $visible_live_page_id;
            }

            if ($normalized['current_project_title'] === '') {
                $normalized['current_project_title'] = (string) $visible_project->post_title;
            }
        }

        if ($normalized['current_project_title'] !== '' && $this->builder_table_exists($wpdb->prefix . 'vmo_projects')) {
            $project_rows = $wpdb->get_col($wpdb->prepare(
                "SELECT id FROM {$wpdb->prefix}vmo_projects WHERE user_id = %d AND project_title = %s",
                $user_id,
                $normalized['current_project_title']
            ));
            $normalized['db_project_row_ids'] = array_values(array_unique(array_map('intval', (array) $project_rows)));
        }

        if ($this->builder_table_exists($wpdb->prefix . 'vmo_website_drafts')) {
            if ($normalized['session_id'] !== '') {
                $draft_ids = $wpdb->get_col($wpdb->prepare(
                    "SELECT draft_id FROM {$wpdb->prefix}vmo_website_drafts WHERE user_id = %d AND session_id = %s",
                    $user_id,
                    $normalized['session_id']
                ));
                $normalized['draft_ids'] = array_values(array_unique(array_filter(array_map('strval', (array) $draft_ids))));
            }
        }

        $normalized['project_post_ids'] = array_values(array_unique(array_map('intval', array_filter((array) $normalized['project_post_ids']))));
        $normalized['live_page_ids'] = array_values(array_unique(array_map('intval', array_filter((array) $normalized['live_page_ids']))));
        $normalized['consultation_ids'] = array_values(array_unique(array_map('intval', array_filter((array) $normalized['consultation_ids']))));

        return $normalized;
    }

    private function get_builder_table_rows($table_name, $user_id, $session_id, array $context = array()) {
        global $wpdb;

        if (!$this->builder_table_exists($table_name)) {
            return array();
        }

        $table_key = $this->normalize_builder_table_name($table_name);
        $columns = array_map(function ($column) {
            return (string) ($column['Field'] ?? '');
        }, $this->get_builder_table_columns($table_name));

        $conditions = array();
        $params = array();

        switch ($table_key) {
            case 'vmo_projects':
                if (in_array('user_id', $columns, true)) {
                    $conditions[] = 'user_id = %d';
                    $params[] = $user_id;
                }
                break;

            case 'vmo_project_history':
                if (!empty($context['db_project_row_ids']) && in_array('project_id', $columns, true)) {
                    $in = $this->build_builder_in_condition('project_id', $context['db_project_row_ids']);
                    if ($in) {
                        $conditions[] = $in['sql'];
                        $params = array_merge($params, $in['params']);
                    }
                }
                break;

            case 'vmo_consultation_media':
                if (!empty($context['consultation_ids']) && in_array('consultation_id', $columns, true)) {
                    $in = $this->build_builder_in_condition('consultation_id', $context['consultation_ids']);
                    if ($in) {
                        $conditions[] = $in['sql'];
                        $params = array_merge($params, $in['params']);
                    }
                }
                break;

            case 'vmo_website_drafts':
                if ($session_id !== '' && in_array('session_id', $columns, true) && in_array('user_id', $columns, true)) {
                    $conditions[] = 'user_id = %d AND session_id = %s';
                    $params[] = $user_id;
                    $params[] = $session_id;
                }
                break;

            case 'vmo_website_versions':
                if (!empty($context['draft_ids']) && in_array('draft_id', $columns, true)) {
                    $in = $this->build_builder_in_condition('draft_id', $context['draft_ids']);
                    if ($in) {
                        $conditions[] = $in['sql'];
                        $params = array_merge($params, $in['params']);
                    }
                }
                break;

            default:
                if (in_array('user_id', $columns, true)) {
                    $conditions[] = 'user_id = %d';
                    $params[] = $user_id;
                }

                if ($session_id !== '' && in_array('session_id', $columns, true)) {
                    $conditions[] = 'session_id = %s';
                    $params[] = $session_id;
                }

                if (!empty($context['consultation_ids']) && in_array('consultation_id', $columns, true)) {
                    $in = $this->build_builder_in_condition('consultation_id', $context['consultation_ids']);
                    if ($in) {
                        $conditions[] = $in['sql'];
                        $params = array_merge($params, $in['params']);
                    }
                }

                if (!empty($context['db_project_row_ids']) && in_array('project_id', $columns, true)) {
                    $in = $this->build_builder_in_condition('project_id', $context['db_project_row_ids']);
                    if ($in) {
                        $conditions[] = $in['sql'];
                        $params = array_merge($params, $in['params']);
                    }
                }

                if (!empty($context['draft_ids']) && in_array('draft_id', $columns, true)) {
                    $in = $this->build_builder_in_condition('draft_id', $context['draft_ids']);
                    if ($in) {
                        $conditions[] = $in['sql'];
                        $params = array_merge($params, $in['params']);
                    }
                }

                if (!empty($context['voice_conversation_ids']) && in_array('conversation_id', $columns, true)) {
                    $in = $this->build_builder_in_condition('conversation_id', $context['voice_conversation_ids']);
                    if ($in) {
                        $conditions[] = $in['sql'];
                        $params = array_merge($params, $in['params']);
                    }
                }

                if (!empty($context['contact_ids']) && in_array('contact_id', $columns, true)) {
                    $in = $this->build_builder_in_condition('contact_id', $context['contact_ids']);
                    if ($in) {
                        $conditions[] = $in['sql'];
                        $params = array_merge($params, $in['params']);
                    }
                }
                break;
        }

        if (empty($conditions)) {
            return array();
        }

        $sql = "SELECT * FROM {$table_name} WHERE " . implode(' OR ', $conditions);
        $prepared = !empty($params) ? $wpdb->prepare($sql, $params) : $sql;
        $rows = $wpdb->get_results($prepared, ARRAY_A);

        return is_array($rows) ? $rows : array();
    }

    private function should_backup_builder_user_meta_key($meta_key) {
        $meta_key = (string) $meta_key;

        if ($meta_key === 'business_phone') {
            return true;
        }

        if (strpos($meta_key, 'vmo_') !== 0) {
            return false;
        }

        $excluded_exact = array(
            'vmo_token_balance',
            'vmo_token_type',
            'vmo_token_history',
            'vmo_token_usage_log',
            'vmo_subscription_status',
            'vmo_subscription_renewal_date',
            'vmo_billing_cycle',
            'vmo_stripe_customer_id',
            'vmo_phone_pack_expiry',
            'vmo_has_phone_pack',
            'vmo_design_preference',
            'vmo_preferred_voice',
            'vmo_pending_notification',
            'vmo_last_upgrade_notification',
            'vmo_needs_upgrade_notification',
            'vmo_builder_reset_to_demo',
            'vmo_builder_last_reset_backup',
            'vmo_builder_last_reset_at',
            'vmo_builder_last_restored_backup',
            'vmo_builder_last_restored_at',
        );

        if (in_array($meta_key, $excluded_exact, true)) {
            return false;
        }

        $excluded_prefixes = array(
            'vmo_token_',
            'vmo_subscription_',
            'vmo_license_',
            'vmo_installation_',
            'vmo_master_',
            'vmo_api_',
            'vmo_usage_',
            'vmo_affiliate_',
            'vmo_manager_',
            'vmo_builder_reset_',
            'vmo_builder_last_',
            'vmo_upgrade_',
            'vmo_last_upgrade_',
            'vmo_needs_upgrade_',
            'vmo_preferred_',
            'vmo_phone_pack_',
            'vmo_stripe_',
        );

        foreach ($excluded_prefixes as $prefix) {
            if (strpos($meta_key, $prefix) === 0) {
                return false;
            }
        }

        return true;
    }

    private function snapshot_builder_user_meta($user_id) {
        $snapshot = array();
        $all_meta = get_user_meta($user_id);

        foreach (array_keys((array) $all_meta) as $meta_key) {
            if (!$this->should_backup_builder_user_meta_key($meta_key)) {
                continue;
            }

            $values = get_user_meta($user_id, $meta_key, false);
            $snapshot[$meta_key] = array_map('maybe_unserialize', (array) $values);
        }

        $business_phone_values = get_user_meta($user_id, 'business_phone', false);
        if (!empty($business_phone_values)) {
            $snapshot['business_phone'] = array_map('maybe_unserialize', (array) $business_phone_values);
        }

        return $snapshot;
    }

    private function snapshot_builder_options($session_id) {
        $snapshot = array();

        if ($session_id !== '') {
            $option_name = 'vmo_website_post_' . $session_id;
            $value = get_option($option_name, null);
            if ($value !== null && $value !== false) {
                $snapshot[$option_name] = maybe_unserialize($value);
            }
        }

        return $snapshot;
    }

    private function snapshot_builder_project_posts($user_id, array $context = array()) {
        $post_ids = array_values(array_unique(array_map('intval', array_filter((array) ($context['project_post_ids'] ?? array())))));

        if (empty($post_ids) && !empty($context['current_project_post_id'])) {
            $post_ids[] = intval($context['current_project_post_id']);
        }

        if (empty($post_ids)) {
            $latest = $this->get_latest_visible_builder_project($user_id);
            if ($latest) {
                $post_ids[] = intval($latest->ID);
            }
        }

        $snapshots = array();
        foreach ($post_ids as $post_id) {
            $snapshot = $this->snapshot_builder_post_record($post_id);
            if (!empty($snapshot)) {
                $snapshots[] = $snapshot;
            }
        }

        return $snapshots;
    }

    private function snapshot_builder_post_record($post_id) {
        $post = get_post($post_id);
        if (!$post) {
            return array();
        }

        return array(
            'post' => (array) $post,
            'meta' => $this->snapshot_builder_post_meta_records($post_id),
        );
    }

    private function snapshot_builder_post_meta_records($post_id) {
        $snapshot = array();
        $meta = get_post_meta($post_id);

        foreach ((array) $meta as $meta_key => $values) {
            $snapshot[$meta_key] = array_map('maybe_unserialize', (array) $values);
        }

        return $snapshot;
    }

    private function build_builder_backup_snapshot($session_id, $user_id, array $context = array()) {
        global $wpdb;

        $context = $this->normalize_builder_reset_context($session_id, $user_id, $context);
        $table_snapshots = array();

        foreach ($this->get_builder_backup_tables() as $table_name) {
            $table_key = $this->normalize_builder_table_name($table_name);
            $rows = $this->get_builder_table_rows($table_name, $user_id, $context['session_id'], $context);
            if (empty($rows)) {
                continue;
            }

            $table_snapshots[$table_key] = array(
                'table' => $table_name,
                'primary_key' => $this->get_builder_primary_key($table_name),
                'rows' => $rows,
            );

            if ($table_key === 'vmo_projects') {
                $context['db_project_row_ids'] = array_values(array_unique(array_merge(
                    (array) $context['db_project_row_ids'],
                    array_map('intval', array_filter(array_column($rows, 'id')))
                )));
            }

            if ($table_key === 'vmo_consultations') {
                $context['consultation_ids'] = array_values(array_unique(array_merge(
                    (array) $context['consultation_ids'],
                    array_map('intval', array_filter(array_column($rows, 'id')))
                )));
            }

            if ($table_key === 'vmo_website_drafts') {
                $context['draft_ids'] = array_values(array_unique(array_merge(
                    (array) $context['draft_ids'],
                    array_map('strval', array_filter(array_column($rows, 'draft_id')))
                )));
            }

            if ($table_key === 'vmo_voice_conversations') {
                $context['voice_conversation_ids'] = array_values(array_unique(array_merge(
                    (array) $context['voice_conversation_ids'],
                    array_map('intval', array_filter(array_column($rows, 'id')))
                )));
            }

            if ($table_key === 'vmo_contacts') {
                $context['contact_ids'] = array_values(array_unique(array_merge(
                    (array) $context['contact_ids'],
                    array_map('intval', array_filter(array_column($rows, 'id')))
                )));
            }
        }

        return array(
            'meta' => array(
                'version' => 1,
                'created_at' => current_time('mysql'),
                'user_id' => (int) $user_id,
                'session_id' => (string) ($context['session_id'] ?? ''),
                'current_project_post_id' => intval($context['current_project_post_id'] ?? 0),
                'current_project_title' => (string) ($context['current_project_title'] ?? ''),
                'current_live_page_id' => intval($context['current_live_page_id'] ?? 0),
                'project_post_ids' => array_values((array) ($context['project_post_ids'] ?? array())),
                'live_page_ids' => array_values((array) ($context['live_page_ids'] ?? array())),
            ),
            'user_meta' => $this->snapshot_builder_user_meta($user_id),
            'options' => $this->snapshot_builder_options((string) ($context['session_id'] ?? '')),
            'project_posts' => $this->snapshot_builder_project_posts($user_id, $context),
            'tables' => $table_snapshots,
        );
    }
    
    /**
     * Create full business backup
     */
    private function create_full_business_backup($session_id, $user_id, array $context = array()) {
        global $wpdb;
        $this->ensure_builder_backup_table_exists();

        $snapshot = $this->build_builder_backup_snapshot($session_id, $user_id, $context);
        $snapshot_json = wp_json_encode($snapshot);
        if (!is_string($snapshot_json) || $snapshot_json === '') {
            throw new \RuntimeException('Reset Builder backup could not be encoded.');
        }

        $backup_id = 'builder_backup_' . time() . '_' . wp_generate_password(10, false, false);
        $compressed = gzcompress($snapshot_json, 6);
        if ($compressed === false) {
            throw new \RuntimeException('Reset Builder backup could not be compressed.');
        }

        $table = $this->get_builder_backup_table_name();
        $inserted = $wpdb->insert(
            $table,
            array(
                'backup_id' => $backup_id,
                'user_id' => $user_id,
                'session_id' => (string) ($snapshot['meta']['session_id'] ?? ''),
                'project_post_id' => intval($snapshot['meta']['current_project_post_id'] ?? 0),
                'project_title' => sanitize_text_field((string) ($snapshot['meta']['current_project_title'] ?? '')),
                'backup_reason' => 'reset_builder',
                'snapshot' => base64_encode($compressed),
                'snapshot_checksum' => hash('sha256', $snapshot_json),
                'created_at' => current_time('mysql'),
            ),
            array('%s', '%d', '%s', '%d', '%s', '%s', '%s', '%s', '%s')
        );

        if (!$inserted) {
            throw new \RuntimeException('Reset Builder backup could not be written to the database.');
        }

        $this->prune_builder_backups($user_id, 3);

        return array(
            'backup_id' => $backup_id,
            'snapshot' => $snapshot,
        );
    }

    private function prune_builder_backups($user_id, $keep = 3) {
        global $wpdb;

        $this->ensure_builder_backup_table_exists();
        $table = $this->get_builder_backup_table_name();
        $rows = $wpdb->get_results($wpdb->prepare(
            "SELECT id FROM {$table} WHERE user_id = %d ORDER BY created_at DESC, id DESC",
            $user_id
        ), ARRAY_A);

        if (!is_array($rows) || count($rows) <= $keep) {
            return;
        }

        $delete_ids = array_map('intval', array_filter(array_column(array_slice($rows, $keep), 'id')));
        if (empty($delete_ids)) {
            return;
        }

        $in = $this->build_builder_in_condition('id', $delete_ids);
        if (!$in) {
            return;
        }

        $sql = $wpdb->prepare("DELETE FROM {$table} WHERE {$in['sql']}", $in['params']);
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->query($sql);
    }

    private function decode_builder_backup_snapshot($payload, $expected_checksum = '') {
        if (!is_string($payload) || $payload === '') {
            return array();
        }

        $decoded = base64_decode($payload, true);
        if ($decoded === false) {
            $decoded = $payload;
        }

        $json = @gzuncompress($decoded);
        if ($json === false) {
            $json = $decoded;
        }

        $json = (string) $json;
        $expected_checksum = strtolower(trim((string) $expected_checksum));
        if ($expected_checksum !== '' && !hash_equals($expected_checksum, hash('sha256', $json))) {
            throw new \RuntimeException('Backup checksum verification failed.');
        }

        $snapshot = json_decode($json, true);
        return is_array($snapshot) ? $snapshot : array();
    }

    private function detach_builder_business_state($user_id, $session_id, array $context, $backup_id, array $snapshot = array()) {
        $this->unpublish_builder_live_pages((array) ($context['live_page_ids'] ?? array()));
        $this->archive_builder_project_posts((array) ($context['project_post_ids'] ?? array()), $backup_id);
        $this->clear_builder_table_snapshots((array) ($snapshot['tables'] ?? array()));
        $this->clear_builder_user_meta($user_id, (array) ($snapshot['user_meta'] ?? array()));
        $this->clear_builder_options((array) ($snapshot['options'] ?? array()));
        $this->clear_builder_session_links((string) $session_id);

        delete_user_meta($user_id, 'vmo_active_consultation_session');
        delete_user_meta($user_id, 'vmo_current_consultation');
        delete_user_meta($user_id, 'vmo_consultation_ready');
        delete_user_meta($user_id, 'vmo_consultation_state');
    }

    private function clear_builder_user_meta($user_id, array $meta_snapshot) {
        foreach (array_keys($meta_snapshot) as $meta_key) {
            delete_user_meta($user_id, $meta_key);
        }
    }

    private function clear_builder_options(array $options_snapshot) {
        foreach (array_keys($options_snapshot) as $option_name) {
            delete_option($option_name);
        }
    }

    private function clear_builder_session_links($session_id) {
        if ($session_id === '') {
            return;
        }

        delete_option('vmo_website_post_' . $session_id);
        delete_transient('vmo_draft_website_' . $session_id);
    }

    private function archive_builder_project_posts(array $project_post_ids, $backup_id) {
        $project_post_ids = array_values(array_unique(array_map('intval', array_filter($project_post_ids))));
        foreach ($project_post_ids as $post_id) {
            if (!get_post($post_id)) {
                continue;
            }

            wp_update_post(array(
                'ID' => $post_id,
                'post_status' => 'draft',
            ));

            update_post_meta($post_id, 'vmo_builder_archived_by_reset', '1');
            update_post_meta($post_id, 'vmo_builder_archived_at', current_time('mysql'));
            update_post_meta($post_id, 'vmo_builder_backup_id', $backup_id);
            delete_post_meta($post_id, 'vmo_live_page_id');
        }
    }

    private function unpublish_builder_live_pages(array $page_ids) {
        $page_ids = array_values(array_unique(array_map('intval', array_filter($page_ids))));
        foreach ($page_ids as $page_id) {
            $page = get_post($page_id);
            if (!$page) {
                continue;
            }

            wp_update_post(array(
                'ID' => $page_id,
                'post_status' => 'draft',
            ));
        }
    }

    private function clear_builder_table_snapshots(array $table_snapshots) {
        $table_names = $this->sort_builder_tables_for_snapshot(array_keys($table_snapshots));
        $table_names = array_reverse($table_names);

        foreach ($table_names as $table_key) {
            $snapshot = $table_snapshots[$table_key] ?? array();
            $this->clear_builder_table_rows($snapshot);
        }
    }

    private function clear_builder_table_rows(array $table_snapshot) {
        global $wpdb;

        $table_name = (string) ($table_snapshot['table'] ?? '');
        $rows = is_array($table_snapshot['rows'] ?? null) ? $table_snapshot['rows'] : array();
        if ($table_name === '' || empty($rows) || !$this->builder_table_exists($table_name)) {
            return;
        }

        $key_column = (string) ($table_snapshot['primary_key'] ?? '');
        if ($key_column === '' || !array_key_exists($key_column, $rows[0])) {
            foreach (array('draft_id', 'project_id', 'consultation_id', 'conversation_id', 'contact_id', 'session_id', 'id') as $candidate) {
                if (array_key_exists($candidate, $rows[0])) {
                    $key_column = $candidate;
                    break;
                }
            }
        }

        if ($key_column === '') {
            return;
        }

        $values = array_values(array_unique(array_filter(array_map('strval', array_column($rows, $key_column)), function ($value) {
            return $value !== '';
        })));
        if (empty($values)) {
            return;
        }

        $in = $this->build_builder_in_condition($key_column, $values);
        if (!$in) {
            return;
        }

        $sql = $wpdb->prepare("DELETE FROM {$table_name} WHERE {$in['sql']}", $in['params']);
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->query($sql);
    }

    private function restore_builder_backup_snapshot(array $snapshot, $user_id, $backup_id) {
        $project_id_map = $this->restore_builder_project_posts((array) ($snapshot['project_posts'] ?? array()), $user_id);
        $this->restore_builder_tables((array) ($snapshot['tables'] ?? array()));
        $this->restore_builder_user_meta($user_id, (array) ($snapshot['user_meta'] ?? array()));
        $this->restore_builder_options((array) ($snapshot['options'] ?? array()), $project_id_map, (array) ($snapshot['meta'] ?? array()));

        $project_payload = $this->build_restored_builder_project_payload($snapshot, $project_id_map);

        delete_user_meta($user_id, 'vmo_builder_reset_to_demo');
        update_user_meta($user_id, 'vmo_builder_last_restored_backup', $backup_id);
        update_user_meta($user_id, 'vmo_builder_last_restored_at', current_time('mysql'));

        return array(
            'session_id' => (string) ($snapshot['meta']['session_id'] ?? ''),
            'project_title' => (string) ($snapshot['meta']['current_project_title'] ?? ''),
            'project_id' => intval($project_payload['project_id'] ?? 0),
            'project' => $project_payload,
        );
    }

    private function get_restored_builder_project_id(array $snapshot, array $project_id_map) {
        $old_project_id = intval($snapshot['meta']['current_project_post_id'] ?? 0);
        if ($old_project_id > 0 && !empty($project_id_map[$old_project_id])) {
            return intval($project_id_map[$old_project_id]);
        }

        if (!empty($project_id_map)) {
            return intval(reset($project_id_map));
        }

        foreach ((array) ($snapshot['project_posts'] ?? array()) as $project_snapshot) {
            $post_data = is_array($project_snapshot['post'] ?? null) ? $project_snapshot['post'] : array();
            $candidate_id = intval($post_data['ID'] ?? 0);
            if ($candidate_id > 0 && get_post($candidate_id)) {
                return $candidate_id;
            }
        }

        return 0;
    }

    private function build_restored_builder_project_payload(array $snapshot, array $project_id_map) {
        $project_id = $this->get_restored_builder_project_id($snapshot, $project_id_map);
        if ($project_id <= 0) {
            return array();
        }

        $post = get_post($project_id);
        if (!$post || $post->post_type !== 'vmo_project') {
            return array();
        }

        $state = get_post_meta($project_id, 'vmo_state', true);
        $pages_map = get_post_meta($project_id, 'vmo_pages_map', true);
        $images = get_post_meta($project_id, 'vmo_images', true);
        $chat = get_post_meta($project_id, 'vmo_chat', true);
        $raw_html = get_post_meta($project_id, 'vmo_project_raw_html', true);
        $html = is_string($raw_html) && trim($raw_html) !== '' ? $raw_html : (string) $post->post_content;

        if (function_exists('vmo_builder_resolve_project_html')) {
            $html = vmo_builder_resolve_project_html($html, $pages_map, $state);
        }

        if (function_exists('vmo_builder_build_state_from_project_payload')) {
            $state_payload = vmo_builder_build_state_from_project_payload($project_id, $html, $state, $pages_map);
        } else {
            $state_payload = is_array($state) ? $state : json_decode((string) $state, true);
            if (!is_array($state_payload)) {
                $state_payload = array();
            }
            if (empty($state_payload['blocks']) && is_string($html) && trim($html) !== '') {
                $state_payload['blocks'] = array(array(
                    'id' => 'restored_project_' . $project_id . '_home',
                    'html' => $html,
                    'type' => 'restored_project',
                ));
            }
        }

        return array(
            'project_id' => $project_id,
            'id' => $project_id,
            'title' => (string) $post->post_title,
            'project_title' => (string) $post->post_title,
            'html' => is_string($html) ? $html : '',
            'state' => $state_payload,
            'pages_map' => function_exists('vmo_builder_decode_project_json') ? vmo_builder_decode_project_json($pages_map) : json_decode((string) $pages_map, true),
            'chat' => is_array($chat) ? $chat : json_decode((string) $chat, true),
            'images' => is_array($images) ? $images : json_decode((string) $images, true),
            'live_page_id' => null,
            'restored_as_draft' => true,
        );
    }

    private function restore_builder_project_posts(array $project_snapshots, $user_id) {
        $project_id_map = array();

        foreach ($project_snapshots as $project_snapshot) {
            $post_data = is_array($project_snapshot['post'] ?? null) ? $project_snapshot['post'] : array();
            if (empty($post_data)) {
                continue;
            }

            $old_post_id = intval($post_data['ID'] ?? 0);
            $restore_data = array(
                'post_title' => (string) ($post_data['post_title'] ?? 'Restored Website'),
                'post_content' => (string) ($post_data['post_content'] ?? ''),
                'post_excerpt' => (string) ($post_data['post_excerpt'] ?? ''),
                'post_status' => 'draft',
                'post_type' => 'vmo_project',
                'post_author' => $user_id,
                'menu_order' => intval($post_data['menu_order'] ?? 0),
            );

            if (!empty($post_data['post_name'])) {
                $restore_data['post_name'] = (string) $post_data['post_name'];
            }

            if ($old_post_id > 0 && get_post($old_post_id)) {
                $restore_data['ID'] = $old_post_id;
                $new_post_id = wp_update_post(wp_slash($restore_data), true);
            } else {
                if ($old_post_id > 0) {
                    $restore_data['import_id'] = $old_post_id;
                }
                $new_post_id = wp_insert_post(wp_slash($restore_data), true);
            }

            if (is_wp_error($new_post_id) || !$new_post_id) {
                continue;
            }

            $project_id_map[$old_post_id] = intval($new_post_id);
            delete_post_meta($new_post_id, 'vmo_builder_archived_by_reset');
            delete_post_meta($new_post_id, 'vmo_builder_archived_at');
            delete_post_meta($new_post_id, 'vmo_builder_backup_id');
            delete_post_meta($new_post_id, 'vmo_live_page_id');
            $this->restore_builder_post_meta($new_post_id, (array) ($project_snapshot['meta'] ?? array()));
            update_post_meta($new_post_id, 'vmo_last_updated', current_time('mysql'));
        }

        return $project_id_map;
    }

    private function restore_builder_post_meta($post_id, array $meta_snapshot) {
        $existing_meta = get_post_meta($post_id);
        foreach (array_keys((array) $existing_meta) as $meta_key) {
            delete_post_meta($post_id, $meta_key);
        }

        foreach ($meta_snapshot as $meta_key => $values) {
            if (in_array($meta_key, array('vmo_builder_archived_by_reset', 'vmo_builder_archived_at', 'vmo_builder_backup_id', 'vmo_live_page_id'), true)) {
                continue;
            }

            foreach ((array) $values as $value) {
                add_post_meta($post_id, $meta_key, maybe_unserialize($value));
            }
        }

        delete_post_meta($post_id, 'vmo_live_page_id');
    }

    private function restore_builder_user_meta($user_id, array $meta_snapshot) {
        foreach ($meta_snapshot as $meta_key => $values) {
            delete_user_meta($user_id, $meta_key);
            foreach ((array) $values as $value) {
                add_user_meta($user_id, $meta_key, maybe_unserialize($value));
            }
        }
    }

    private function restore_builder_options(array $options_snapshot, array $project_id_map, array $meta_snapshot = array()) {
        foreach ($options_snapshot as $option_name => $option_value) {
            $restored_value = $option_value;
            if (strpos($option_name, 'vmo_website_post_') === 0) {
                $old_post_id = intval($option_value);
                if ($old_post_id > 0 && !empty($project_id_map[$old_post_id])) {
                    $restored_value = intval($project_id_map[$old_post_id]);
                }
                if ($restored_value) {
                    $session_id = substr($option_name, strlen('vmo_website_post_'));
                    set_transient('vmo_draft_website_' . $session_id, intval($restored_value), DAY_IN_SECONDS);
                }
            }

            update_option($option_name, $restored_value, false);
        }
    }

    private function prepare_builder_row_for_restore($table_key, array $row) {
        if ($table_key === 'vmo_website_drafts') {
            if (array_key_exists('is_published', $row)) {
                $row['is_published'] = 0;
            }
            if (array_key_exists('published_page_ids', $row)) {
                $row['published_page_ids'] = wp_json_encode(array());
            }
            if (array_key_exists('unpublished_at', $row)) {
                $row['unpublished_at'] = current_time('mysql');
            }
        }

        if ($table_key === 'vmo_business_kpis') {
            $metric_name = '';
            if (isset($row['metric'])) {
                $metric_name = (string) $row['metric'];
            } elseif (isset($row['metric_name'])) {
                $metric_name = (string) $row['metric_name'];
            }

            if ($metric_name === 'website_status') {
                if (array_key_exists('value', $row)) {
                    $row['value'] = 'draft';
                }
                if (array_key_exists('metric_value', $row)) {
                    $row['metric_value'] = 'draft';
                }
                if (array_key_exists('current_value', $row)) {
                    $row['current_value'] = 'draft';
                }
            }
        }

        return $row;
    }

    private function restore_builder_tables(array $table_snapshots) {
        global $wpdb;

        foreach ($this->sort_builder_tables_for_snapshot(array_keys($table_snapshots)) as $table_key) {
            $table_snapshot = $table_snapshots[$table_key] ?? array();
            $table_name = (string) ($table_snapshot['table'] ?? '');
            $rows = is_array($table_snapshot['rows'] ?? null) ? $table_snapshot['rows'] : array();
            if ($table_name === '' || empty($rows) || !$this->builder_table_exists($table_name)) {
                continue;
            }

            $this->clear_builder_table_rows($table_snapshot);

            foreach ($rows as $row) {
                if (!is_array($row)) {
                    continue;
                }

                $row = $this->prepare_builder_row_for_restore($table_key, $row);

                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                $wpdb->replace($table_name, $row);
            }
        }
    }
    
    /**
     * Delete all business services
     */
    private function delete_all_business_services($session_id, $user_id) {
        // Delete phone service
        delete_user_meta($user_id, 'vmo_phone_service_' . $session_id);
        
        // Cancel SEO automation cron jobs
        $seo_hooks = wp_get_scheduled_events();
        foreach ($seo_hooks as $hook => $events) {
            if (strpos($hook, 'vmo_seo') !== false) {
                wp_clear_scheduled_hook($hook);
            }
        }
        
        // Delete email marketing data
        delete_user_meta($user_id, 'vmo_email_marketing_' . $session_id);
        
        // Additional service cleanup can be added here
    }
    
    /**
     * AJAX: Save consultation message
     */
    public function ajax_save_consultation_message() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        $session_id = sanitize_text_field(wp_unslash($_POST['session_id']));
        $message_data = json_decode(wp_unslash($_POST['message_data']), true);
        $user_id = get_current_user_id();
        
        // Get existing consultation
        $consultation = $this->consultation_db->get_consultation_by_session($session_id);
        
        if ($consultation) {
            $conversation_log = json_decode($consultation->conversation_log, true) ?: [];
            $conversation_log[] = array(
                'timestamp' => current_time('mysql'),
                'type' => $message_data['type'], // 'user' or 'ai' or 'system'
                'content' => $message_data['content'],
                'media_attachments' => $message_data['media_attachments'] ?? []
            );
            
            $this->consultation_db->save_consultation($user_id, $session_id, array(
                'conversation_log' => $conversation_log
            ));
            
            wp_send_json_success(array('message' => 'Message saved successfully'));
        } else {
            // Create new consultation session
            $consultation_data = array(
                'consultation_data' => [],
                'conversation_log' => [array(
                    'timestamp' => current_time('mysql'),
                    'type' => $message_data['type'],
                    'content' => $message_data['content'],
                    'media_attachments' => $message_data['media_attachments'] ?? []
                )],
                'status' => 'in_progress'
            );
            
            $consultation_id = $this->consultation_db->save_consultation($user_id, $session_id, $consultation_data);
            wp_send_json_success(array('message' => 'New consultation started', 'consultation_id' => $consultation_id));
        }
    }

    private function sanitize_media_context_value($value) {
        if (is_array($value)) {
            $clean = array();
            foreach ($value as $key => $item) {
                $clean[sanitize_key((string) $key)] = $this->sanitize_media_context_value($item);
            }
            return $clean;
        }

        if (is_bool($value)) {
            return $value;
        }

        if (is_numeric($value)) {
            return $value;
        }

        return sanitize_text_field((string) $value);
    }

    private function parse_consultation_media_context($raw_context) {
        if (is_array($raw_context)) {
            return $this->sanitize_media_context_value($raw_context);
        }

        $raw_context = trim((string) $raw_context);
        if ($raw_context === '') {
            return array('user_context' => 'consultation_media');
        }

        $decoded = json_decode($raw_context, true);
        if (is_array($decoded)) {
            return $this->sanitize_media_context_value($decoded);
        }

        return array('user_context' => sanitize_text_field($raw_context));
    }

    private function normalize_uploaded_media_type($mime_type, $file_path = '') {
        $mime_type = strtolower((string) $mime_type);
        $extension = strtolower(pathinfo((string) $file_path, PATHINFO_EXTENSION));

        if (strpos($mime_type, 'image/') === 0 || in_array($extension, array('jpg', 'jpeg', 'png', 'gif', 'webp', 'svg'), true)) {
            return 'image';
        }
        if (strpos($mime_type, 'video/') === 0 || in_array($extension, array('mp4', 'mov', 'webm', 'avi', 'wmv', 'm4v'), true)) {
            return 'video';
        }
        if (strpos($mime_type, 'application/pdf') === 0 || in_array($extension, array('pdf', 'doc', 'docx', 'txt', 'rtf'), true)) {
            return 'document';
        }

        return 'other';
    }

    private function normalize_media_roles($roles) {
        if (!is_array($roles)) {
            $roles = preg_split('/[,|;\n]+/', (string) $roles);
        }

        $normalized = array();
        foreach ((array) $roles as $role) {
            if (is_array($role)) {
                $role = $role['role'] ?? $role['name'] ?? '';
            }
            $role = sanitize_key((string) $role);
            if ($role !== '') {
                $normalized[] = $role;
            }
        }

        return array_values(array_unique($normalized));
    }

    private function infer_consultation_media_roles(array $context, $media_type, $title) {
        $roles = $this->normalize_media_roles($context['roles'] ?? array());
        foreach (array('business_role', 'placement_role', 'intent', 'user_context') as $key) {
            if (!empty($context[$key]) && is_scalar($context[$key])) {
                $roles[] = sanitize_key((string) $context[$key]);
            }
        }

        $title_lower = strtolower((string) $title);
        $keyword_roles = array(
            'logo' => 'logo',
            'brand' => 'brand_asset',
            'hero' => $media_type === 'video' ? 'hero_video' : 'hero_image',
            'banner' => 'hero_image',
            'product' => 'product_media',
            'gallery' => 'gallery_image',
            'team' => 'team_photo',
            'staff' => 'team_photo',
            'testimonial' => 'testimonial_media',
            'review' => 'testimonial_media',
            'location' => 'location_media',
            'storefront' => 'location_media',
            'demo' => $media_type === 'video' ? 'product_video' : 'product_media',
            'lifestyle' => 'product_lifestyle_image',
            'poster' => 'video_poster',
        );

        foreach ($keyword_roles as $keyword => $role) {
            if (strpos($title_lower, $keyword) !== false) {
                $roles[] = $role;
            }
        }

        if (!$roles) {
            $roles[] = $media_type === 'document' ? 'document' : 'unassigned_media';
        }

        return array_values(array_unique(array_filter($roles)));
    }

    private function build_consultation_media_asset_context(array $context, array $uploaded_file, $attachment_id, array $attachment_data, $session_id, $user_id, $title) {
        $mime_type = sanitize_mime_type((string) ($uploaded_file['type'] ?? ''));
        $media_type = $this->normalize_uploaded_media_type($mime_type, $uploaded_file['file'] ?? '');
        $roles = $this->infer_consultation_media_roles($context, $media_type, $title);
        $placement_role = sanitize_key((string) ($context['placement_role'] ?? $context['placement'] ?? $context['intent'] ?? ($roles[0] ?? '')));
        $business_role = sanitize_key((string) ($context['business_role'] ?? ($roles[0] ?? 'uploaded_media')));
        $product_id = sanitize_key((string) ($context['product_id'] ?? $context['product_slug'] ?? $context['product_key'] ?? ''));
        $product_name = sanitize_text_field((string) ($context['product_name'] ?? $context['product_title'] ?? ''));
        $width = intval($attachment_data['width'] ?? 0);
        $height = intval($attachment_data['height'] ?? 0);
        $file_size = !empty($uploaded_file['file']) && file_exists($uploaded_file['file']) ? filesize($uploaded_file['file']) : 0;
        $has_explicit_target = $placement_role !== '' || $product_id !== '' || $product_name !== '' || !in_array('unassigned_media', $roles, true);

        return array_merge($context, array(
            'schema' => 'vmo_media_asset_v1',
            'source' => sanitize_key((string) ($context['source'] ?? 'uploaded_by_user')),
            'attachment_id' => intval($attachment_id),
            'session_id' => sanitize_text_field((string) $session_id),
            'user_id' => intval($user_id),
            'media_type' => $media_type,
            'mime_type' => $mime_type,
            'business_role' => $business_role,
            'roles' => $roles,
            'placement_role' => $placement_role,
            'page_target' => sanitize_key((string) ($context['page_target'] ?? '')),
            'section_target' => sanitize_key((string) ($context['section_target'] ?? '')),
            'product_id' => $product_id,
            'product_name' => $product_name,
            'user_label' => sanitize_text_field((string) ($context['user_label'] ?? $title)),
            'alt_text' => sanitize_text_field((string) ($context['alt_text'] ?? $context['prompt'] ?? $title)),
            'quality_status' => sanitize_key((string) ($context['quality_status'] ?? 'pending_review')),
            'ai_classification_status' => $has_explicit_target ? 'context_provided' : 'pending_review',
            'needs_generation' => false,
            'mobile_variant_required' => !empty($context['mobile_variant_required']) || in_array($placement_role, array('hero_image', 'product_hero_image', 'mobile_fallback_image'), true),
            'desktop_variant_required' => array_key_exists('desktop_variant_required', $context) ? (bool) $context['desktop_variant_required'] : true,
            'poster_required' => !empty($context['poster_required']) || $media_type === 'video',
            'quality' => array(
                'width' => $width,
                'height' => $height,
                'aspect_ratio' => ($width > 0 && $height > 0) ? round($width / $height, 4) : 0,
                'file_size' => intval($file_size),
                'resolution_status' => ($media_type !== 'image' || $width >= 1200 || $height >= 1200) ? 'review_ready' : 'needs_review',
            ),
        ));
    }
    
    /**
     * AJAX: Upload media for consultation
     */
    public function ajax_upload_consultation_media() {
        // Accept builder/admin/ajax nonce variants; always return JSON on failure.
        $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
        $admin_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
        $ajax_nonce = isset($_POST['_ajax_nonce']) ? sanitize_text_field(wp_unslash($_POST['_ajax_nonce'])) : '';
        $builder_ok = $builder_nonce && wp_verify_nonce($builder_nonce, 'vmo_builder_nonce');
        $admin_ok = $admin_nonce && wp_verify_nonce($admin_nonce, 'vmo_admin_nonce');
        $ajax_ok = ($admin_nonce && wp_verify_nonce($admin_nonce, 'vmo_ajax_nonce'))
            || ($ajax_nonce && wp_verify_nonce($ajax_nonce, 'vmo_ajax_nonce'));
        if (!($builder_ok || $admin_ok || $ajax_ok)) {
            wp_send_json_error(['message' => 'Security check failed', 'status' => 'invalid_nonce'], 403);
        }

        if (!current_user_can('upload_files')) {
            wp_send_json_error(['message' => 'Permission denied.', 'status' => 'forbidden'], 403);
        }
        
        if (!function_exists('wp_handle_upload')) {
            require_once(ABSPATH . 'wp-admin/includes/file.php');
        }
        
        $session_id = sanitize_text_field(wp_unslash($_POST['session_id'] ?? ''));
        $raw_upload_context = $_POST['media_context'] ?? ($_POST['context_json'] ?? ($_POST['context'] ?? 'consultation_media'));
        $upload_context = $this->parse_consultation_media_context(wp_unslash($raw_upload_context));

        if (empty($session_id)) {
            wp_send_json_error(['message' => 'Missing session_id'], 400);
        }
        
        if (empty($_FILES['media'])) {
            wp_send_json_error(['message' => 'No file uploaded.'], 400);
        }

        $uploaded_file = wp_handle_upload($_FILES['media'], array('test_form' => false));
        
        if (isset($uploaded_file['error'])) {
            wp_send_json_error(array('message' => $uploaded_file['error']));
        }
        
        // Save to media library
        $attachment = array(
            'guid' => $uploaded_file['url'],
            'post_mime_type' => $uploaded_file['type'],
            'post_title' => basename($uploaded_file['file']),
            'post_content' => '',
            'post_status' => 'inherit'
        );
        
        $attachment_id = wp_insert_attachment($attachment, $uploaded_file['file']);
        
        if (!is_wp_error($attachment_id)) {
            require_once(ABSPATH . 'wp-admin/includes/image.php');
            $attachment_data = wp_generate_attachment_metadata($attachment_id, $uploaded_file['file']);
            wp_update_attachment_metadata($attachment_id, $attachment_data);
            $media_asset_context = $this->build_consultation_media_asset_context(
                $upload_context,
                $uploaded_file,
                $attachment_id,
                is_array($attachment_data) ? $attachment_data : array(),
                $session_id,
                get_current_user_id(),
                basename($uploaded_file['file'])
            );
            update_post_meta($attachment_id, '_vmo_media_asset_context', wp_json_encode($media_asset_context));
            if (!empty($media_asset_context['alt_text'])) {
                update_post_meta($attachment_id, '_wp_attachment_image_alt', sanitize_text_field((string) $media_asset_context['alt_text']));
            }
            
            // Save to consultation media
            $consultation = $this->consultation_db->get_consultation_by_session($session_id);
            if ($consultation) {
                $this->consultation_db->save_consultation_media($consultation->id, array(
                    'url' => $uploaded_file['url'],
                    'type' => $media_asset_context['media_type'] ?? $uploaded_file['type'],
                    'title' => basename($uploaded_file['file']),
                    'context' => $media_asset_context
                ));
            }
            
            wp_send_json_success(array(
                'media_url' => $uploaded_file['url'],
                'media_type' => $media_asset_context['media_type'] ?? $uploaded_file['type'],
                'attachment_id' => $attachment_id,
                'media_asset' => $media_asset_context,
                'message' => 'Media uploaded successfully'
            ));
        } else {
            wp_send_json_error(array('message' => 'Failed to save media'));
        }
    }
    
    /**
     * AJAX: Process voice message with two-way conversation
     */
    public function ajax_process_voice_message() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        // ✅ TOKEN DEDUCTION v8.1.0: Charge for transcription (75) + AI response (300) = 375 tokens
        $user_id = get_current_user_id();
        if (function_exists('vmo_deduct_tokens')) {
            $token_cost = function_exists('vmo_get_token_cost') ? (int) vmo_get_token_cost('voice_message') : 375;
            if (!vmo_deduct_tokens($user_id, $token_cost, 'voice_message', 'Voice message processing (transcription + AI response)')) {
                wp_send_json_error(array(
                    'message' => 'Insufficient tokens for voice processing. Please top up your balance.',
                    'status' => 'insufficient_tokens'
                ));
                return;
            }
        }
        
        $audio_data = sanitize_text_field(wp_unslash($_POST['audio_data'] ?? ''));
        $session_id = sanitize_text_field(wp_unslash($_POST['session_id']));
        $conversation_context = json_decode(wp_unslash($_POST['context']), true);
        
        if (empty($audio_data)) {
            wp_send_json_error(array('message' => 'No audio data provided'));
        }
        
        // Convert audio to text using OpenAI Whisper
        $transcript = $this->transcribe_audio($audio_data);
        
        if (!$transcript) {
            wp_send_json_error(array('message' => 'Failed to transcribe audio'));
        }
        
        // Generate AI response based on conversation context
    $ai_response = $this->generate_consultation_response($transcript, $conversation_context, $session_id);
        
        // VOICE SYSTEM REMOVED: Text-to-speech synthesis removed for No Voice version
        
        // Save conversation to database
        $this->save_voice_conversation($session_id, $transcript, $ai_response);
        
        wp_send_json_success(array(
            'transcript' => $transcript,
            'ai_response' => $ai_response,
            'ai_audio_url' => $ai_audio_url
        ));
    }
    
    /**
     * AJAX handler for audio transcription only (no AI response)
     */
    public function ajax_transcribe_audio() {
        $this->prepare_long_running_runtime();

        check_ajax_referer('vmo_builder_nonce', 'security');
        
        // ✅ TOKEN DEDUCTION v8.1.0: Charge for Whisper transcription
        $user_id = get_current_user_id();
        if (function_exists('vmo_deduct_tokens_for_operation')) {
            if (!vmo_deduct_tokens_for_operation($user_id, 'transcription')) {
                wp_send_json_error(array(
                    'message' => 'Insufficient tokens for audio transcription. Please top up your balance.',
                    'status' => 'insufficient_tokens'
                ));
                return;
            }
        }
        
        $audio_data = sanitize_text_field(wp_unslash($_POST['audio_data'] ?? ''));
        
        if (empty($audio_data)) {
            wp_send_json_error(array('message' => 'No audio data provided'));
        }
        
        // Convert audio to text using OpenAI Whisper
        $transcript = $this->transcribe_audio($audio_data);
        
        if (!$transcript) {
            wp_send_json_error(array('message' => 'Failed to transcribe audio'));
        }
        
        // Return just the transcript for editing
        wp_send_json_success(array(
            'transcript' => trim($transcript)
        ));
    }
    
    /**
     * AJAX handler for transcribing audio from URL (for unified media handler)
     * Accepts audio_url parameter instead of base64 audio_data
     */
    public function ajax_transcribe_audio_url() {
        $this->prepare_long_running_runtime();

        check_ajax_referer('vmo_builder_nonce', 'security');
        
        // ✅ TOKEN DEDUCTION v8.1.0: Charge for Whisper transcription
        $user_id = get_current_user_id();
        if (function_exists('vmo_deduct_tokens_for_operation')) {
            if (!vmo_deduct_tokens_for_operation($user_id, 'transcription')) {
                wp_send_json_error(array(
                    'message' => 'Insufficient tokens for audio transcription. Please top up your balance.',
                    'status' => 'insufficient_tokens'
                ));
                return;
            }
        }
        
        $audio_url = sanitize_url($_POST['audio_url'] ?? '');
        
        if (empty($audio_url)) {
            wp_send_json_error(array('message' => 'No audio URL provided'));
        }
        
        try {
            // Download audio file from URL
            $audio_response = wp_remote_get($audio_url, array(
                'timeout' => 30,
                'sslverify' => false
            ));
            
            if (is_wp_error($audio_response)) {
                throw new Exception('Failed to download audio: ' . $audio_response->get_error_message());
            }
            
            $audio_data = wp_remote_retrieve_body($audio_response);
            if (empty($audio_data)) {
                throw new Exception('Empty audio file');
            }
            
            // Convert to base64 for OpenAI API
            $audio_base64 = base64_encode($audio_data); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- data encoding
            
            // Use existing transcribe_audio method
            $transcript = $this->transcribe_audio($audio_base64);
            
            if (!$transcript) {
                throw new Exception('Transcription failed');
            }
            
            wp_send_json_success(array(
                'transcription' => trim($transcript)
            ));
            
        } catch (\Throwable $e) {
            error_log('VMO Audio Transcription Error: ' . $e->getMessage());
            wp_send_json_error(array(
                'message' => 'Failed to transcribe audio: ' . $e->getMessage()
            ));
        }
    }
    
    /**
     * AJAX handler for describing images using OpenAI Vision API
     * Accepts image_url parameter and returns description + auto-role suggestion
     */
    public function ajax_describe_image() {
        $this->prepare_long_running_runtime();

        check_ajax_referer('vmo_builder_nonce', 'security');
        
        // ✅ TOKEN DEDUCTION v8.1.0: Charge for vision/image analysis
        $user_id = get_current_user_id();
        if (function_exists('vmo_deduct_tokens_for_operation')) {
            if (!vmo_deduct_tokens_for_operation($user_id, 'vision')) {
                wp_send_json_error(array(
                    'message' => 'Insufficient tokens for image analysis. Please top up your balance.',
                    'status' => 'insufficient_tokens'
                ));
                return;
            }
        }
        
        $image_url = sanitize_url($_POST['image_url'] ?? '');
        
        if (empty($image_url)) {
            wp_send_json_error(array('message' => 'No image URL provided'));
        }
        
        try {
            // Get OpenAI API key
            $api_key = get_option('vmo_openai_api_key');
            if (empty($api_key)) {
                throw new Exception('AI API key not configured');
            }
            
            // Call OpenAI Vision API
            $vision_model = function_exists('vmo_get_model') ? vmo_get_model('fast') : (defined('VMO_OPENAI_MODEL') ? VMO_OPENAI_MODEL : 'gpt-5.5-mini');
            $vision_payload = array_merge(array(
                'model' => $vision_model,
                'messages' => array(
                    array(
                        'role' => 'user',
                        'content' => array(
                            array(
                                'type' => 'text',
                                'text' => 'Analyze this image for a business website. Describe what you see and suggest the best use: logo, hero image, header slider, gallery, product image, background, or testimonial. Be specific about the image content and quality. Format your response as: DESCRIPTION: [your description] | SUGGESTED_ROLE: [role] | CONFIDENCE: [0.0-1.0]'
                            ),
                            array(
                                'type' => 'image_url',
                                'image_url' => array(
                                    'url' => $image_url
                                )
                            )
                        )
                    )
                ),
            ), function_exists('vmo_get_token_param') ? vmo_get_token_param($vision_model, 300) : array('max_tokens' => 300));

            $response = wp_remote_post('https://api.openai.com/v1/chat/completions', array(
                'timeout' => 30,
                'headers' => array(
                    'Authorization' => 'Bearer ' . $api_key,
                    'Content-Type' => 'application/json'
                ),
                'body' => json_encode($vision_payload)
            ));

            // Fallback Logic: Retry with fallback model if primary fails
            if (is_wp_error($response) || wp_remote_retrieve_response_code($response) >= 400) {
                 error_log('VMO: Auto-vision analysis failed. Retrying with fallback.');
                 $vision_payload['model'] = function_exists('vmo_get_model') ? vmo_get_model('fallback') : 'gpt-5.5-nano';
                 $response = wp_remote_post('https://api.openai.com/v1/chat/completions', array(
                    'timeout' => 30,
                    'headers' => array(
                        'Authorization' => 'Bearer ' . $api_key,
                        'Content-Type' => 'application/json'
                    ),
                    'body' => json_encode($vision_payload)
                ));
            }

            
            if (is_wp_error($response)) {
                throw new Exception('AI API request failed: ' . $response->get_error_message());
            }
            
            $body = json_decode(wp_remote_retrieve_body($response), true);
            
            if (isset($body['error'])) {
                throw new Exception('AI API error: ' . $body['error']['message']);
            }
            
            // ✅ FIX: Safe navigation to prevent "Cannot access offset of type string on string"
            $content = '';
            if (is_array($body) && isset($body['choices']) && is_array($body['choices'])
                && isset($body['choices'][0]) && is_array($body['choices'][0])
                && isset($body['choices'][0]['message']) && is_array($body['choices'][0]['message'])
                && isset($body['choices'][0]['message']['content'])) {
                $content = $body['choices'][0]['message']['content'];
            }
            
            if (empty($content)) {
                throw new Exception('Empty response from AI');
            }
            
            // Parse the structured response
            $description = '';
            $suggested_role = 'gallery'; // Default fallback
            $confidence = 0.5;
            
            // Extract DESCRIPTION
            if (preg_match('/DESCRIPTION:\s*(.+?)(?:\s*\||\s*$)/s', $content, $desc_match)) {
                $description = trim($desc_match[1]);
            } else {
                // Fallback: use entire content as description
                $description = trim($content);
            }
            
            // Extract SUGGESTED_ROLE
            if (preg_match('/SUGGESTED_ROLE:\s*([a-z_]+)/i', $content, $role_match)) {
                $suggested_role = strtolower(trim($role_match[1]));
            }
            
            // Extract CONFIDENCE
            if (preg_match('/CONFIDENCE:\s*(0?\.\d+|1\.0)/i', $content, $conf_match)) {
                $confidence = floatval($conf_match[1]);
            }
            
            // Map role names to canonical format
            $role_map = array(
                'logo' => 'logo',
                'hero' => 'hero_image',
                'hero image' => 'hero_image',
                'hero_image' => 'hero_image',
                'header' => 'header_slider',
                'header slider' => 'header_slider',
                'header_slider' => 'header_slider',
                'slider' => 'header_slider',
                'gallery' => 'gallery',
                'product' => 'product_image',
                'product image' => 'product_image',
                'product_image' => 'product_image',
                'background' => 'background',
                'bg' => 'background',
                'testimonial' => 'testimonial',
                'team' => 'team_member',
                'team member' => 'team_member'
            );
            
            $canonical_role = $role_map[$suggested_role] ?? 'gallery';
            
            wp_send_json_success(array(
                'description' => $description,
                'suggested_role' => $canonical_role,
                'confidence' => $confidence,
                'raw_response' => $content
            ));
            
        } catch (\Throwable $e) {
            error_log('VMO Image Description Error: ' . $e->getMessage());
            wp_send_json_error(array(
                'message' => 'Failed to describe image: ' . $e->getMessage()
            ));
        }
    }
    
    /**
     * AJAX handler for processing text consultation messages
     */
    public function ajax_process_consultation_message() {
        // ✅ CRITICAL: Start output buffering to catch any database errors
        ob_start();
        
        // ✅ TIMEOUT GUARD: Extend execution time for this heavy AJAX handler
        $original_time_limit = ini_get('max_execution_time');
        $this->prepare_long_running_runtime(); // ✅ BOTTLENECK FIX: consultation can trigger multiple AI calls and phase orchestration steps
        
        // ✅ Ensure user has activation record (creates table and record if needed)
        if (function_exists('vmo_get_or_create_user_activation')) {
            vmo_get_or_create_user_activation(get_current_user_id());
        }
        
        $nonce_value = '';
        foreach (array('security', '_ajax_nonce', 'nonce') as $nonce_field) {
            if (isset($_POST[$nonce_field])) {
                $nonce_value = sanitize_text_field(wp_unslash($_POST[$nonce_field]));
                break;
            }
        }
        if (!$nonce_value || !wp_verify_nonce($nonce_value, 'vmo_builder_nonce')) {
            ob_end_clean(); // Clean buffer before sending JSON
            wp_send_json_error(array('message' => 'Security validation failed. Please refresh and try again.'), 403);
        }
        
        // ✅ TOKEN DEDUCTION v8.1.0: Charge for consultation AI messages
        $user_id = get_current_user_id();
        $consultation_token_cost = function_exists('vmo_get_token_cost') ? vmo_get_token_cost('consultation') : 300;
        if (function_exists('vmo_deduct_tokens_for_operation')) {
            if (!vmo_deduct_tokens_for_operation($user_id, 'consultation')) {
                ob_end_clean();
                wp_send_json_error(array(
                    'message' => 'Insufficient tokens for AI consultation. Please top up your balance to continue.',
                    'status' => 'insufficient_tokens',
                    'tokens_required' => $consultation_token_cost
                ));
                return;
            }
        }
        
        try {
            $message = sanitize_textarea_field($_POST['message'] ?? '');
            $system_prompt = isset($_POST['system_prompt']) ? (string) wp_unslash($_POST['system_prompt']) : '';
            $raw_context = wp_unslash($_POST['context'] ?? '{}');
            if (!is_string($raw_context)) {
                $raw_context = '{}';
            }
            $context = json_decode($raw_context, true);
            $session_id = sanitize_text_field(wp_unslash($_POST['session_id']));
            
            if (empty($message)) {
                wp_send_json_error(array('message' => 'No message provided'));
                return;
            }

            list($row, $consultation_cd) = $this->hydrate_consultation_state($session_id);

            // If user uploaded a document but only the media reference was saved (e.g., Media Library),
            // attempt to extract text and persist blueprint_raw so the AI can "remember" it.
            if ($this->ensure_blueprint_raw_present($consultation_cd)) {
                $this->persist_consultation_state($session_id, $consultation_cd);
            }
            if (isset($context['consultation_data']) && is_array($context['consultation_data'])) {
                $context_delta = $this->normalize_discovery_context_delta($context['consultation_data']);
                if (isset($context_delta['business_opportunity_shortlist']) && is_array($context_delta['business_opportunity_shortlist'])) {
                    $consultation_cd['business_opportunity_shortlist'] = $this->merge_business_opportunity_shortlist(
                        isset($consultation_cd['business_opportunity_shortlist']) && is_array($consultation_cd['business_opportunity_shortlist']) ? $consultation_cd['business_opportunity_shortlist'] : array(),
                        $context_delta['business_opportunity_shortlist']
                    );
                    unset($context_delta['business_opportunity_shortlist']);
                }
                $consultation_cd = $this->merge_profile_delta(
                    $consultation_cd,
                    $context_delta
                );
            }
            $consultation_cd = $this->apply_discovery_scenario_intent($consultation_cd, $message);
            $consultation_cd = $this->reconcile_intake_memory($consultation_cd, $message);
            $consultation_cd = $this->apply_product_catalog_deferral_intent($consultation_cd, $message);
            $context = is_array($context) ? $context : array();
            $context['server_consultation_data'] = $consultation_cd;
            
            // ✅ CRITICAL FIX: Handle very long messages that exceed normal chat lengths
            // Instead of discarding the content, SAVE it as blueprint data so the AI can reference it
            $max_message_length = 50000; // Characters — GPT-5.5 can handle large documents
            if (strlen($message) > $max_message_length) {
                error_log("VMO: Message too long (" . strlen($message) . " chars) — saving as blueprint document");
                
                // ✅ ACTUALLY SAVE the full text as blueprint data (was previously lost!)
                $full_text = $message;
                $consultation_cd['blueprintRaw'] = mb_substr($full_text, 0, 400000);
                if (!isset($consultation_cd['document_store']) || !is_array($consultation_cd['document_store'])) {
                    $consultation_cd['document_store'] = array('blueprint_raw' => '', 'blueprint_summary' => '');
                }
                $consultation_cd['document_store']['blueprint_raw'] = mb_substr($full_text, 0, 400000);
                
                // Run structured extraction on the pasted content
                $extraction_result = $this->run_document_blueprint_analysis('Pasted Business Plan', $full_text);
                if (!empty($extraction_result['extractedInfo'])) {
                    $consultation_cd['extracted_information'] = array_merge(
                        $consultation_cd['extracted_information'] ?? array(),
                        $extraction_result['extractedInfo']
                    );
                }
                if (!empty($extraction_result['summary'])) {
                    $consultation_cd['document_store']['blueprint_summary'] = $extraction_result['summary'];
                }
                
                // Persist the saved data
                $this->persist_consultation_state($session_id, $consultation_cd);
                
                // Save a reference in message history
                $this->save_consultation_message($session_id, 'user', mb_substr($message, 0, 8000) . "\n\n[Full document: " . strlen($message) . " chars — saved as blueprint]");
                
                // Generate a proper AI response that references the document
                $ai_response = "Thank you for sharing your detailed business plan! I've analyzed the entire document (" . strlen($message) . " characters) and saved it as your business blueprint.\n\n";
                
                if (!empty($extraction_result['extractedInfo'])) {
                    $ei = $extraction_result['extractedInfo'];
                    $ai_response .= "Here's what I found:\n\n";
                    if (!empty($ei['businessName'])) $ai_response .= "**Business Name:** " . $ei['businessName'] . "\n";
                    if (!empty($ei['industry'])) $ai_response .= "**Industry:** " . $ei['industry'] . "\n";
                    if (!empty($ei['offerings'])) {
                        $offerings = is_array($ei['offerings']) ? implode(', ', $ei['offerings']) : $ei['offerings'];
                        $ai_response .= "**Offerings:** " . $offerings . "\n";
                    }
                    if (!empty($ei['targetAudience'])) $ai_response .= "**Target Audience:** " . $ei['targetAudience'] . "\n";
                    if (!empty($ei['businessGoals'])) $ai_response .= "**Goals:** " . $ei['businessGoals'] . "\n";
                    $ai_response .= "\nDoes this accurately represent your business? Let me know if anything needs to be adjusted, then we'll move forward.";
                } else {
                    $ai_response .= "I've saved your full document and will reference it throughout our consultation. Does this cover all the key aspects of your business, or is there anything you'd like to add?";
                }
                
                $this->save_consultation_message($session_id, 'ai', $ai_response);
                
                wp_send_json_success(array(
                    'response' => $ai_response,
                    'isComplete' => false,
                    'session_id' => $session_id,
                    'consultation_data' => $consultation_cd
                ));
                return;
            }
            
            // Ensure phases scaffold exists for this session (non-zombie guidance)

            // ✅ AUTO-SCAN URLS: Detect and scan any URLs mentioned in the user's message
            // This enables the AI to "see" reference websites, inspiration sites, or existing sites
            $consultation_cd = $this->scan_urls_in_message($message, $session_id, $consultation_cd);
            $context['server_consultation_data'] = $consultation_cd;

            // Save user message
            $this->save_consultation_message($session_id, 'user', $message);
            
            // ✅ CRITICAL FIX: Save blueprintRaw and blueprintSummary from context to database
            if (!empty($session_id) && is_array($context)) {
                $row = $this->consultation_db->get_consultation_by_session($session_id);
                $cd = $row ? json_decode($row->consultation_data ?: '[]', true) : [];
                if (!is_array($cd)) {
                    $cd = [];
                }
                if (!isset($cd['document_store']) || !is_array($cd['document_store'])) {
                    $cd['document_store'] = $this->consultation_blueprint['document_store'];
                }

                $blueprint_updated = false;
                if (!empty($context['blueprintRaw'])) {
                    $cd['blueprintRaw'] = $context['blueprintRaw'];
                    $cd['document_store']['blueprint_raw'] = $context['blueprintRaw'];
                    $blueprint_updated = true;
                }
                if (!empty($context['blueprintSummary'])) {
                    $cd['blueprintSummary'] = $context['blueprintSummary'];
                    $cd['document_store']['blueprint_summary'] = $context['blueprintSummary'];
                    $blueprint_updated = true;
                }

                if ($blueprint_updated) {
                    $user_id = get_current_user_id();
                    $this->consultation_db->save_consultation($user_id, $session_id, ['consultation_data' => $cd]);
                    $consultation_cd = $this->merge_profile_delta($consultation_cd, $cd);
                    $context['server_consultation_data'] = $consultation_cd;
                }
            }
            
            // Multilingual intent detection (classifier) - reduces reliance on phrase regex
            $intent = $this->classify_user_intent($message, $context);
            if (!is_array($intent)) {
                $intent = array();
            }
            if (!is_array($context)) $context = [];
            $pending_blueprint_confirmation = false;
            $current_phase_index_for_intent = intval($consultation_cd['consultation_flow']['current_phase_index'] ?? 0);
            if (!empty($intent['proceed']) && in_array($current_phase_index_for_intent, array(0, 1), true)) {
                $foundation_status_for_intent = $this->build_phase1_foundation_status($consultation_cd);
                if (empty($foundation_status_for_intent['ready'])) {
                    $intent['proceed'] = false;
                    $consultation_cd['phase1_foundation_gate'] = array(
                        'status' => 'blocked',
                        'missing' => $foundation_status_for_intent['missing'],
                        'updated_at' => current_time('mysql'),
                    );
                    $context['phase1_foundation_gate'] = $consultation_cd['phase1_foundation_gate'];
                    error_log('VMO: Ignored proceed intent because Phase 1 business foundation is incomplete: ' . implode(', ', $foundation_status_for_intent['missing']));
                }
            }

            if (!empty($intent['proceed'])) {
                // Lock docs and mark proceed for generator to jump into IA
                $this->set_doc_locked($session_id, true);
                $context['proceed_confirmed'] = true;
                $current_phase_index = intval($consultation_cd['consultation_flow']['current_phase_index'] ?? 0);
                if ($current_phase_index === 1) {
                    $pending_blueprint_confirmation = true;
                }
                // ✅ FIX: Set phase-completion approval flags for ALL phases when user confirms.
                // Without these flags, is_phase_ready_for_completion() blocks advancement.
                if (!isset($consultation_cd['approvals']) || !is_array($consultation_cd['approvals'])) {
                    $consultation_cd['approvals'] = array();
                }
                switch ($current_phase_index) {
                    case 2: // Phase 3 (Research) — if user confirms, allow advancement even if research_profile is empty
                        // Try to generate research profile on-demand if it's missing
                        if (empty($consultation_cd['research_profile'])) {
                            try {
                                $consultation_cd = $this->generate_research_profile($session_id, $consultation_cd);
                            } catch (\Throwable $e) {
                                error_log('VMO: On-demand research_profile generation failed: ' . $e->getMessage());
                                $consultation_cd['research_profile'] = $this->build_fallback_research_profile($consultation_cd);
                                $this->persist_consultation_state($session_id, $consultation_cd);
                            }
                        }
                        break;
                    case 3: // Phase 4 (Recommendations) — mark recommendations as accepted
                        if (!isset($consultation_cd['approvals']) || !is_array($consultation_cd['approvals'])) { $consultation_cd['approvals'] = array(); }
                            if (!isset($consultation_cd['approvals']['recommendations']) || !is_array($consultation_cd['approvals']['recommendations'])) {
                            $consultation_cd['approvals']['recommendations'] = array();
                        }
                        if (empty($consultation_cd['approvals']['recommendations']['accepted'])) {
                            $consultation_cd['approvals']['recommendations']['accepted'] = array('user_confirmed_all');
                        }
                        break;
                    case 4: // Phase 5 (Website Architecture) — mark as discussed
                        if (!isset($consultation_cd['approvals']) || !is_array($consultation_cd['approvals'])) { $consultation_cd['approvals'] = array(); }
                        $consultation_cd['approvals']['phase5_discussed'] = true;
                        break;
                    case 5: // Phase 6 (Operations & Systems) — mark as discussed
                        if (!isset($consultation_cd['approvals']) || !is_array($consultation_cd['approvals'])) { $consultation_cd['approvals'] = array(); }
                        $consultation_cd['approvals']['phase6_discussed'] = true;
                        break;
                    case 6: // Phase 7 (Roadmap) — try to generate artifacts if missing
                        $needs_artifacts = empty($consultation_cd['roadmap_json']) || empty($consultation_cd['daily_tasks_json']);
                        if ($needs_artifacts) {
                            try {
                                $consultation_cd = $this->generate_phase7_artifacts($session_id, $consultation_cd);
                            } catch (\Throwable $e) {
                                error_log('VMO: On-demand phase7 artifacts generation failed: ' . $e->getMessage());
                                $fallback_phase7 = $this->build_fallback_phase7_artifacts($session_id, $consultation_cd);
                                if (empty($consultation_cd['website_plan']['pages'])) {
                                    $consultation_cd['website_plan'] = $fallback_phase7['website_plan'];
                                }
                                if (empty($consultation_cd['systems_plan']['automation'])) {
                                    $consultation_cd['systems_plan'] = $fallback_phase7['systems_plan'];
                                }
                                if (empty($consultation_cd['roadmap_json'])) {
                                    $consultation_cd['roadmap_json'] = $fallback_phase7['roadmap_json'];
                                }
                                if (empty($consultation_cd['daily_tasks_json'])) {
                                    $consultation_cd['daily_tasks_json'] = $fallback_phase7['daily_tasks_json'];
                                }
                                if (!isset($consultation_cd['approvals']) || !is_array($consultation_cd['approvals'])) { $consultation_cd['approvals'] = array(); }
                                $consultation_cd['approvals']['roadmap_generated'] = !empty($consultation_cd['roadmap_json']);
                                $consultation_cd['approvals']['daily_activities_generated'] = !empty($consultation_cd['daily_tasks_json']);
                                $this->persist_consultation_state($session_id, $consultation_cd);
                            }
                        }
                        break;
                    case 7: // Phase 8 (Launch) — user confirmed launch readiness; generation gate decides completion
                        if (!isset($consultation_cd['approvals']) || !is_array($consultation_cd['approvals'])) { $consultation_cd['approvals'] = array(); }
                        $consultation_cd['approvals']['launch_readiness_requested'] = true;
                        $consultation_cd['approvals']['launch_readiness_requested_at'] = current_time('mysql');
                        try {
                            $phase8_results = $this->populate_phase8_deliverables(get_current_user_id(), $session_id, $consultation_cd);
                            $consultation_cd = $this->hydrate_phase8_consultation_data($session_id, array());
                            if (!isset($consultation_cd['approvals']) || !is_array($consultation_cd['approvals'])) { $consultation_cd['approvals'] = array(); }
                            $gate_ready = !empty($phase8_results['gate']['launch_ready']);
                            $consultation_cd['approvals']['launch_readiness_confirmed'] = $gate_ready;
                            $consultation_cd['approvals']['launch_readiness_confirmed_at'] = $gate_ready ? current_time('mysql') : '';
                            $consultation_cd['approvals']['launch_missing_items'] = $phase8_results['missing_items'] ?? array();
                        } catch (\Throwable $e) {
                            $consultation_cd['approvals']['launch_readiness_confirmed'] = false;
                            $consultation_cd['approvals']['launch_missing_items'] = array(array(
                                'key' => 'phase8_orchestration',
                                'label' => 'Phase 8 deliverables',
                                'reason' => $e->getMessage(),
                            ));
                            error_log('VMO: Phase 8 deliverables orchestration failed: ' . $e->getMessage());
                        }
                        break;
                }
                $this->persist_consultation_state($session_id, $consultation_cd);
            }

            // Apply any detected payment mode selection into structured consultation data
            if (!empty($intent['payment_choice'])) {
                $this->apply_payment_choice_intent($consultation_cd, $intent['payment_choice']);
                $context['server_consultation_data'] = $consultation_cd;
            }

            $this->refresh_email_automation_state($consultation_cd);

            // Track conversational follow-up turns for document-driven Phase 1 so the AI
            // continues naturally instead of re-introducing the same preface each turn.
            $phase_idx_for_doc_followup = intval($consultation_cd['consultation_flow']['current_phase_index'] ?? 0);
            $has_document_source = !empty($consultation_cd['document_store']['blueprint_raw']) || !empty($consultation_cd['blueprintRaw']);
            if ($phase_idx_for_doc_followup === 0 && $has_document_source) {
                if (!isset($consultation_cd['phase1_doc_followup_turns'])) {
                    $consultation_cd['phase1_doc_followup_turns'] = 0;
                }
                $msg_trim = trim((string) $message);
                if ($msg_trim !== '' && mb_strlen($msg_trim) >= 2) {
                    $consultation_cd['phase1_doc_followup_turns'] = intval($consultation_cd['phase1_doc_followup_turns']) + 1;
                }
            }
            $context['server_consultation_data'] = $consultation_cd;
            
            // ── Streaming mode: set SSE headers right before AI call ──
            $is_streaming = !empty($_POST['stream']);
            if ($is_streaming) {
                ob_end_clean(); // Clean the ob_start() from top of handler
                while (ob_get_level()) ob_end_clean();
                @ini_set('output_buffering', 'off');
                @ini_set('zlib.output_compression', 0);
                header('Content-Type: text/event-stream');
                header('Cache-Control: no-cache');
                header('Connection: keep-alive');
                header('X-Accel-Buffering: no');
            }

            // Generate AI response based on consultation context
            $ai_response = $this->generate_consultation_response($message, $context, $session_id, $system_prompt, $is_streaming);

            // ✅ Phase gating hard guard: Phases 1–3 must never return recommendations.
            // current_phase_index is 0-based; Phase 4 starts at index 3.
            $current_phase_index = intval($consultation_cd['consultation_flow']['current_phase_index'] ?? 0);
            $ai_response = $this->enforce_consultation_continuity($ai_response, $consultation_cd, $current_phase_index);
            if ($current_phase_index === 0 || $current_phase_index === 1) {
                $ai_response = $this->enforce_business_foundation_gate($ai_response, $consultation_cd, $current_phase_index);
            }
            if ($current_phase_index < 3) {
                $ai_response = $this->enforce_no_recommendations_until_phase4($ai_response);
            }
            if ($current_phase_index === 5 && $this->needs_email_roles_confirmation($consultation_cd)) {
                $ai_response = str_replace('[[PHASE_COMPLETE]]', '', (string) $ai_response);
            }
            $catalog_status_for_gate = $this->build_product_catalog_status($consultation_cd);
            if ($current_phase_index === 5 && !empty($catalog_status_for_gate['question_required'])) {
                $ai_response = str_replace('[[PHASE_COMPLETE]]', '', (string) $ai_response);
            }
            if ($current_phase_index === 7) {
                $phase8_gate = get_user_meta(get_current_user_id(), 'vmo_phase8_deliverables_gate_' . $session_id, true);
                if (!is_array($phase8_gate) || empty($phase8_gate['launch_ready'])) {
                    $ai_response = str_replace('[[PHASE_COMPLETE]]', '', (string) $ai_response);
                    $ai_response = $this->append_phase8_recovery_guidance($ai_response, is_array($phase8_gate) ? $phase8_gate : array());
                    $consultation_cd['phase8_resolution_loop'] = array(
                        'status' => 'active',
                        'gate_status' => is_array($phase8_gate) ? ($phase8_gate['status'] ?? 'needs_attention') : 'needs_attention',
                        'missing_items' => is_array($phase8_gate) ? ($phase8_gate['missing_items'] ?? array()) : array(),
                        'recovery_plan' => is_array($phase8_gate) ? ($phase8_gate['recovery_plan'] ?? array()) : array(),
                        'updated_at' => current_time('mysql'),
                    );
                }
            }

            // ✅ FIX: Apply blueprint confirmation and email role confirmation BEFORE advance attempt
            // Previously these were set AFTER advance_consultation_phase(), causing a race condition
            // where is_phase_ready_for_completion(1) would fail due to missing phase1_profile_confirmed.
            if ($pending_blueprint_confirmation) {
                if (!isset($consultation_cd['approvals']) || !is_array($consultation_cd['approvals'])) {
                    $consultation_cd['approvals'] = array();
                }
                $consultation_cd['approvals']['phase1_profile_confirmed'] = true;

                if (!isset($consultation_cd['phase_flags']) || !is_array($consultation_cd['phase_flags'])) {
                    $consultation_cd['phase_flags'] = array();
                }
                $consultation_cd['phase_flags']['phase2_confirmed'] = true;

                $this->persist_consultation_state($session_id, $consultation_cd);
            }

            // ✅ FIX: Pre-confirm email roles if user is confirming and all roles are present
            $missing_roles_pre = $this->get_missing_email_roles($consultation_cd);
            if ($current_phase_index >= 5 && empty($missing_roles_pre) && !empty($intent['proceed'])) {
                if (!isset($consultation_cd['email_roles_meta']) || !is_array($consultation_cd['email_roles_meta'])) {
                    $consultation_cd['email_roles_meta'] = $this->consultation_blueprint['email_roles_meta'];
                }
                $consultation_cd['email_roles_meta']['roles_confirmed'] = true;
                $consultation_cd['email_roles_meta']['roles_confirmed_at'] = current_time('mysql');
                $this->persist_consultation_state($session_id, $consultation_cd);
            }

            // Detect phase-complete signal and advance if present
            $phase_advanced = false;
            $previous_phase_index = intval($consultation_cd['consultation_flow']['current_phase_index'] ?? 0);
            if (strpos($ai_response, '[[PHASE_COMPLETE]]') !== false) {
                $ai_response = str_replace('[[PHASE_COMPLETE]]', '', $ai_response);
                $phase_advanced = $this->advance_consultation_phase($session_id);
                if ($phase_advanced) {
                    // Refresh hydrated data after phase advancement so status reflects new phase
                    list($row, $consultation_cd) = $this->hydrate_consultation_state($session_id);
                } else {
                    error_log('VMO: advance_consultation_phase returned false for session ' . $session_id . ' at phase index ' . $previous_phase_index);
                }
            }
            
            if (!$ai_response) {
                // ✅ FIX: Provide a guaranteed non-empty fallback instead of erroring out
                $ai_response = "I'm processing your request. Could you please rephrase or provide more details so I can assist you better?";
                error_log("VMO: AI response was empty after stripping markers — using fallback");
            }
            
            // Save AI response
            $this->save_consultation_message($session_id, 'ai', $ai_response);

            // Update structured profiles using extraction delta
            $phase_index = $consultation_cd['consultation_flow']['current_phase_index'] ?? 0;
            $phase_name = $consultation_cd['consultation_flow']['phases'][$phase_index]['name'] ?? '';
            $delta = $this->extract_consultation_delta(
                $consultation_cd['business_profile'] ?? array(),
                $message,
                $ai_response,
                array('phase' => $phase_name)
            );
            $consultation_cd['business_profile'] = $this->merge_profile_delta(
                $consultation_cd['business_profile'] ?? array(),
                $delta['business_profile_delta'] ?? array()
            );
            if (!empty($delta['offers_profile_delta'])) {
                $existing_offers = $consultation_cd['offers_profile'] ?? array();
                $consultation_cd['offers_profile'] = array_values(array_merge($existing_offers, $delta['offers_profile_delta']));
            }
            if (!empty($delta['payments_profile_delta'])) {
                $consultation_cd['payments_profile'] = $this->merge_profile_delta(
                    $consultation_cd['payments_profile'] ?? array(),
                    $delta['payments_profile_delta']
                );
            }
            if (!empty($delta['email_roles_meta_delta'])) {
                $consultation_cd['email_roles_meta'] = $this->merge_profile_delta(
                    $consultation_cd['email_roles_meta'] ?? array(),
                    $delta['email_roles_meta_delta']
                );
            }
            if (!empty($delta['discovery_profile_delta']) && is_array($delta['discovery_profile_delta'])) {
                $discovery_delta = $this->filter_empty_discovery_delta($delta['discovery_profile_delta']);
                $consultation_cd['discovery_profile'] = $this->merge_profile_delta(
                    isset($consultation_cd['discovery_profile']) && is_array($consultation_cd['discovery_profile']) ? $consultation_cd['discovery_profile'] : array(),
                    is_array($discovery_delta) ? $discovery_delta : array()
                );
                $consultation_cd['discovery_profile']['updated_at'] = current_time('mysql');
            }
            if (!empty($delta['business_opportunity_shortlist_delta']) && is_array($delta['business_opportunity_shortlist_delta'])) {
                $consultation_cd['business_opportunity_shortlist'] = $this->merge_business_opportunity_shortlist(
                    isset($consultation_cd['business_opportunity_shortlist']) && is_array($consultation_cd['business_opportunity_shortlist']) ? $consultation_cd['business_opportunity_shortlist'] : array(),
                    $delta['business_opportunity_shortlist_delta']
                );
            }
            if (!empty($delta['extracted_information_delta']) && is_array($delta['extracted_information_delta'])) {
                $consultation_cd['extracted_information'] = $this->merge_profile_delta(
                    isset($consultation_cd['extracted_information']) && is_array($consultation_cd['extracted_information']) ? $consultation_cd['extracted_information'] : array(),
                    $delta['extracted_information_delta']
                );
            }
            $consultation_cd = $this->reconcile_intake_memory($consultation_cd, $message);

            // ✅ CORRECTIONS TRACKING: Accumulate user corrections so they persist across turns
            if (!empty($delta['corrections']) && is_array($delta['corrections'])) {
                if (!isset($consultation_cd['user_corrections']) || !is_array($consultation_cd['user_corrections'])) {
                    $consultation_cd['user_corrections'] = array();
                }
                foreach ($delta['corrections'] as $correction) {
                    if (is_array($correction) && !empty($correction['field'])) {
                        // Check for duplicate corrections on the same field — keep latest
                        $field = $correction['field'];
                        $found = false;
                        foreach ($consultation_cd['user_corrections'] as $idx => $existing) {
                            if (($existing['field'] ?? '') === $field) {
                                $consultation_cd['user_corrections'][$idx] = $correction;
                                $found = true;
                                break;
                            }
                        }
                        if (!$found) {
                            $consultation_cd['user_corrections'][] = $correction;
                        }
                        error_log("VMO: Correction tracked — field: {$field}, correct_value: " . ($correction['correct_value'] ?? ''));
                    }
                }
            }

            $corrections_detected_this_turn = !empty($delta['corrections']) && is_array($delta['corrections']);

            // Parse any domain-based email assignments the user just provided
            // ✅ FIX: Also extract from AI response — when AI presents "info@domain.com, support@domain.com..."
            // and user says "use all 7" or "confirmed", the emails are in the AI text, not the user's.
            $email_assignments = $this->extract_email_roles_from_text($message);
            $ai_email_assignments = $this->extract_email_roles_from_text($ai_response);
            // ✅ FIX: Only accept AI-suggested emails that are on the correct domain
            // This prevents blueprint's wrong-domain emails (e.g., support@virtualmediaoffice.com)
            // from being extracted, rejected, and then resetting roles_confirmed.
            $required_domain_pre = $this->get_enforced_domain();
            foreach ($ai_email_assignments as $role => $email) {
                $d = $this->extract_domain_from_email($email);
                if ($d !== $required_domain_pre) {
                    unset($ai_email_assignments[$role]);
                }
            }
            // Merge: user-provided emails take priority over AI-suggested ones
            foreach ($ai_email_assignments as $role => $email) {
                if (empty($email_assignments[$role])) {
                    $email_assignments[$role] = $email;
                }
            }
            if (!isset($consultation_cd['email_roles']) || !is_array($consultation_cd['email_roles'])) {
                $consultation_cd['email_roles'] = $this->consultation_blueprint['email_roles'];
            }
            if (!isset($consultation_cd['email_roles_meta']) || !is_array($consultation_cd['email_roles_meta'])) {
                $consultation_cd['email_roles_meta'] = $this->consultation_blueprint['email_roles_meta'];
            }

            // Ensure confirmation keys exist
            if (!array_key_exists('roles_confirmed', $consultation_cd['email_roles_meta'])) {
                $consultation_cd['email_roles_meta']['roles_confirmed'] = false;
            }
            if (!array_key_exists('roles_confirmed_at', $consultation_cd['email_roles_meta'])) {
                $consultation_cd['email_roles_meta']['roles_confirmed_at'] = null;
            }

            $email_roles_before = wp_json_encode($consultation_cd['email_roles']);
            $provisioning_hint = $this->detect_email_provisioning_mode($message, $ai_response);
            if ($provisioning_hint) {
                $consultation_cd['email_roles_meta']['provisioning_mode'] = $provisioning_hint;
                if (!isset($consultation_cd['email_roles_meta']) || !is_array($consultation_cd['email_roles_meta'])) { $consultation_cd['email_roles_meta'] = array(); }
            $consultation_cd['email_roles_meta']['provisioning_status'] = 'queued';
            }
            $required_domain = $this->get_enforced_domain();
            $domain_warnings = array();

            foreach ($consultation_cd['email_roles'] as $role => $stored_email) {
                if (!empty($stored_email)) {
                    $stored_domain = $this->extract_domain_from_email($stored_email);
                    if ($stored_domain !== $required_domain) {
                        $domain_warnings[] = strtoupper($role) . ': ' . $stored_email . ' was removed because all addresses must use @' . $required_domain . '.';
                        $consultation_cd['email_roles'][$role] = null;
                    }
                }
            }

            if (!empty($email_assignments)) {
                foreach ($email_assignments as $role => $email_address) {
                    $sanitized_email = sanitize_email($email_address);
                    if (!$sanitized_email) {
                        continue;
                    }
                    $email_domain = $this->extract_domain_from_email($sanitized_email);
                    if (!$email_domain) {
                        $domain_warnings[] = strtoupper($role) . ': Invalid email format provided.';
                        continue;
                    }
                    if ($this->is_public_email_domain($email_domain)) {
                        $domain_warnings[] = strtoupper($role) . ': ' . $sanitized_email . ' uses a public mailbox. Provide a domain-based address (e.g., team@' . $required_domain . ').';
                        continue;
                    }
                    if ($email_domain !== $required_domain) {
                        $domain_warnings[] = strtoupper($role) . ': ' . $sanitized_email . ' must use @' . $required_domain . ' to stay compliant.';
                        continue;
                    }
                    $existing_email = $consultation_cd['email_roles'][$role] ?? '';
                    if (empty($existing_email) || $existing_email !== $sanitized_email) {
                        $consultation_cd['email_roles'][$role] = $sanitized_email;
                    }
                }
            }

            if (!empty($domain_warnings)) {
                $domain_warnings = array_values(array_unique($domain_warnings));
            }
            $consultation_cd['email_roles_meta']['locked_domain'] = $required_domain;
            $consultation_cd['email_roles_meta']['warnings'] = $domain_warnings;

            // If role values changed (user provided updates), reset confirmation.
            // ✅ FIX: Only reset if the USER explicitly provided new emails (not AI-extracted ones).
            // Previously, emails extracted from the AI's own response text would trigger this reset,
            // causing roles_confirmed to flip back to false even after the user already confirmed.
            $email_roles_after_user = wp_json_encode($consultation_cd['email_roles']);
            $was_already_confirmed = !empty($consultation_cd['email_roles_meta']['roles_confirmed']);
            $user_provided_emails = !empty($email_assignments) && !empty(array_diff_key($email_assignments, $ai_email_assignments));
            if ($email_roles_after_user !== $email_roles_before) {
                // Only reset confirmation if the USER provided new/different emails
                // (not when AI-extracted emails filled in matching values)
                if (!$was_already_confirmed || $user_provided_emails) {
                    $consultation_cd['email_roles_meta']['roles_confirmed'] = false;
                    $consultation_cd['email_roles_meta']['roles_confirmed_at'] = null;
                }
            }

            $this->maybe_invoke_auto_provisioning($consultation_cd);

            // If auto-provisioning filled/changed roles, also reset confirmation — but only if not already confirmed.
            $email_roles_after_auto = wp_json_encode($consultation_cd['email_roles']);
            if ($email_roles_after_auto !== $email_roles_after_user) {
                if (!$was_already_confirmed) {
                    $consultation_cd['email_roles_meta']['roles_confirmed'] = false;
                    $consultation_cd['email_roles_meta']['roles_confirmed_at'] = null;
                }
            }

            // Mark roles as confirmed only when all exist and user explicitly confirms.
            // Use multilingual intent classifier as the primary signal to avoid English-only matching.
            $missing_roles_now = $this->get_missing_email_roles($consultation_cd);

            // ✅ FIX: Auto-populate default email roles when user confirms but roles are still missing.
            // This handles "use all 7 recommendations", "confirmed", "proceed" etc. where the user
            // accepted the AI's suggested emails but the emails weren't extracted from text.
            if (!empty($missing_roles_now) && !empty($intent['proceed']) && $current_phase_index === 5) {
                $domain = $this->get_enforced_domain();
                if ($domain) {
                    $defaults = array(
                        'general' => 'info@' . $domain,
                        'support' => 'support@' . $domain,
                        'transactions' => 'billing@' . $domain,
                        'marketing' => 'marketing@' . $domain,
                        'customercare' => 'customercare@' . $domain,
                        'bizdev' => 'partnerships@' . $domain,
                        'newsletter' => 'newsletter@' . $domain
                    );
                    foreach ($missing_roles_now as $missing_role) {
                        if (isset($defaults[$missing_role]) && empty($consultation_cd['email_roles'][$missing_role])) {
                            $consultation_cd['email_roles'][$missing_role] = $defaults[$missing_role];
                            error_log("VMO: Auto-populated missing email role '{$missing_role}' with default: " . $defaults[$missing_role]);
                        }
                    }
                    // Re-check after auto-population
                    $missing_roles_now = $this->get_missing_email_roles($consultation_cd);
                }
            }

            if (empty($missing_roles_now)) {
                $confirmed_via_intent = !empty($intent['proceed']);
                $confirmed_via_legacy = $this->user_message_confirms_email_roles($message);
                if ($confirmed_via_intent || $confirmed_via_legacy) {
                    $consultation_cd['email_roles_meta']['roles_confirmed'] = true;
                    $consultation_cd['email_roles_meta']['roles_confirmed_at'] = current_time('mysql');
                    $this->maybe_invoke_auto_provisioning($consultation_cd);
                }
            }

            // ✅ NOTE: $pending_blueprint_confirmation and email role confirmation are now handled
            // BEFORE the first advance_consultation_phase() call above (race condition fix).

            // If the user confirmed they want to proceed (any language) and prerequisites are satisfied,
            // advance phases immediately to prevent repetitive confirmation loops.
            if (!$phase_advanced && !empty($intent['proceed'])) {
                if ($corrections_detected_this_turn) {
                    error_log('VMO: Proceed intent ignored this turn because corrections were detected; awaiting post-correction confirmation.');
                } else {
                $phase_index_now = intval($consultation_cd['consultation_flow']['current_phase_index'] ?? 0);
                if ($phase_index_now === 0 && $this->is_phase_ready_for_completion($consultation_cd, 0)) {
                    $phase_advanced = $this->advance_consultation_phase($session_id);
                    if ($phase_advanced) {
                        list($row, $consultation_cd) = $this->hydrate_consultation_state($session_id);
                    }
                } elseif ($phase_index_now === 1 && $this->is_phase_ready_for_completion($consultation_cd, 1)) {
                    // ✅ FIX: Only advance Phase 2→3 if Phase 1 readiness gate passes
                    // (requires phase1_profile_confirmed). Previously this had no gating check,
                    // which allowed auto-generated AI responses to trigger advancement
                    // before the user explicitly confirmed their blueprint.
                    $phase_advanced = $this->advance_consultation_phase($session_id);
                    if ($phase_advanced) {
                        list($row, $consultation_cd) = $this->hydrate_consultation_state($session_id);
                    }
                }
                }
            }

            $this->persist_consultation_state($session_id, $consultation_cd);
            
            // Check if consultation is complete based on collected information
            $isComplete = $this->is_consultation_complete($session_id, $context);
            $phase8_gate_for_response = get_user_meta(get_current_user_id(), 'vmo_phase8_deliverables_gate_' . $session_id, true);
            if (!is_array($phase8_gate_for_response)) {
                $phase8_gate_for_response = array();
            }
            
            // ── Streaming vs. JSON response ──
            if ($is_streaming) {
                // ✅ FIX: ALWAYS send the done event even if post-processing had issues
                // The done event is critical — without it the JS client hangs indefinitely
                echo 'data: ' . json_encode([
                    'done'           => true,
                    'session_id'     => $session_id,
                    'isComplete'     => $isComplete,
                    'phase_advanced' => $phase_advanced,
                    'phase8_gate'    => $phase8_gate_for_response,
                ]) . "\n\n";
                flush();
                exit;
            }

            // ✅ Clean output buffer before sending JSON (catches any database errors)
            ob_end_clean();
            
            wp_send_json_success(array(
                'response' => trim($ai_response),
                'isComplete' => $isComplete,
                'session_id' => $session_id,
                'phase_advanced' => $phase_advanced,
                'phase8_gate' => $phase8_gate_for_response,
                'consultation_data' => $consultation_cd,
                'structured_delta' => $delta
            ));
            
        } catch (\Throwable $e) {
            error_log('VMO Consultation Message Error: ' . $e->getMessage());

            // Refund consultation tokens on AI failure
            if (isset($consultation_token_cost) && $consultation_token_cost > 0 && $user_id > 0) {
                if (function_exists('vmo_add_tokens')) {
                    vmo_add_tokens($user_id, $consultation_token_cost, 'consultation_refund', 'Refund: Consultation AI failed - ' . $e->getMessage());
                } elseif (function_exists('vmo_refund_tokens')) {
                    vmo_refund_tokens($user_id, $consultation_token_cost, 'consultation_refund');
                }
            }

            if (!empty($is_streaming)) {
                echo 'data: ' . json_encode(['error' => $e->getMessage()]) . "\n\n";
                flush();
                exit;
            }
            ob_end_clean(); // Clean buffer before sending error
            wp_send_json_error(array('message' => 'Processing failed: ' . $e->getMessage()));
        }
    }
    
    /**
     * Helper function to save consultation messages
     */
    private function save_consultation_message($session_id, $type, $content, $media_attachments = array()) {
        $user_id = get_current_user_id();
        
        // Get existing consultation
        $consultation = $this->consultation_db->get_consultation_by_session($session_id);
        
        if ($consultation) {
            $conversation_log = json_decode($consultation->conversation_log, true) ?: [];
            $conversation_log[] = array(
                'timestamp' => current_time('mysql'),
                'type' => $type,
                'content' => $content,
                'media_attachments' => $media_attachments
            );
            
            $this->consultation_db->save_consultation($user_id, $session_id, array(
                'conversation_log' => $conversation_log
            ));
        } else {
            // Create new consultation session
            $consultation_data = array(
                'consultation_data' => [],
                'conversation_log' => [array(
                    'timestamp' => current_time('mysql'),
                    'type' => $type,
                    'content' => $content,
                    'media_attachments' => $media_attachments
                )],
                'status' => 'in_progress'
            );
            
            $this->consultation_db->save_consultation($user_id, $session_id, $consultation_data);
        }
        
        return true;
    }
    
    /**
     * Check if consultation has enough information to be considered complete
     */
    private function is_consultation_release_ready(array $consultation_cd, $session_id = '') {
        $readiness = $this->build_consultation_setup_readiness($consultation_cd, $session_id);
        $user_id = get_current_user_id();
        if ($user_id <= 0 && !empty($readiness['user_id'])) {
            $user_id = (int) $readiness['user_id'];
        }
        $gate = ($user_id > 0 && $session_id !== '')
            ? $this->evaluate_phase8_deliverables_gate($user_id, $session_id, $consultation_cd)
            : array('launch_ready' => false);
        return !empty($readiness['release_ready']) && !empty($gate['launch_ready']);
    }

    private function build_consultation_setup_readiness(array $consultation_cd, $session_id = '') {
        global $wpdb;

        $website_data = array();
        $user_id = get_current_user_id();

        if (!empty($session_id) && $this->consultation_db && is_callable(array($this->consultation_db, 'get_consultation_by_session'))) {
            $row = $this->consultation_db->get_consultation_by_session($session_id);
            if ($row && !empty($row->user_id)) {
                $user_id = (int) $row->user_id;
            }
            if ($row && !empty($row->website_data)) {
                $decoded = json_decode($row->website_data, true);
                if (is_array($decoded)) {
                    $website_data = $decoded;
                }
            }
        }

        if (!empty($consultation_cd['website_data']) && is_array($consultation_cd['website_data'])) {
            $website_data = array_replace_recursive($website_data, $consultation_cd['website_data']);
        }

        $preview_ready = !empty($website_data['preview_ready']) || !empty($consultation_cd['preview_ready']);
        $media_complete = !empty($website_data['media_complete']) || !empty($consultation_cd['media_complete']);
        $draft_ready = !empty($website_data['draft_ready']) || !empty($website_data['draft_assembled']) || !empty($website_data['generation_completed']);
        $roadmap_ready = !empty($consultation_cd['roadmap_json']);
        $daily_ready = !empty($consultation_cd['daily_tasks_json']);
        $legal_docs_ready = !empty($website_data['legal_docs_ready']) || !empty($website_data['legal_docs']);
        $business_package_ready = !empty($website_data['business_package']) || !empty($consultation_cd['document_manifest']) || !empty($consultation_cd['generated_documents']);
        $operating_state = is_array($website_data['operating_state'] ?? null) ? $website_data['operating_state'] : array();
        $systems = is_array($operating_state['systems'] ?? null) ? $operating_state['systems'] : array();

        $email_meta = is_array($consultation_cd['email_roles_meta'] ?? null) ? $consultation_cd['email_roles_meta'] : array();
        $email_mode = $this->normalize_provisioning_mode($email_meta['provisioning_mode'] ?? 'collect');
        $email_status = (string) ($email_meta['provisioning_status'] ?? 'not_requested');
        $email_confirmed = !empty($email_meta['roles_confirmed']);
        $email_skip = !empty($email_meta['phase6_skip_approved']);
        $email_provider = sanitize_key($email_meta['bulk_email_provider'] ?? '');
        $email_accounts = is_array($email_meta['email_accounts'] ?? null) ? $email_meta['email_accounts'] : array();
        $email_account_count = 0;
        foreach ($email_accounts as $account) {
            if (is_array($account) && !empty($account['email']) && sanitize_email($account['email'])) {
                $email_account_count++;
            }
        }
        $email_role_count = 0;
        $email_roles = is_array($consultation_cd['email_roles'] ?? null) ? $consultation_cd['email_roles'] : array();
        foreach ($email_roles as $email) {
            if (!empty($email) && sanitize_email($email)) {
                $email_role_count++;
            }
        }
        if ($email_skip) {
            $email_item = $this->build_readiness_item('blocked', 'Email setup was skipped; choose VMO auto-provisioning, existing inboxes, manual DNS confirmation, or Zoho before release.');
        } elseif (!$email_confirmed) {
            $email_item = $this->build_readiness_item('blocked', 'Role-based email addresses have not been confirmed.');
        } elseif ($email_mode === 'auto' && $email_status !== 'completed') {
            $email_item = $this->build_readiness_item('blocked', 'Automatic email provisioning has not completed.', array('status' => $email_status));
        } elseif ($email_mode === 'zoho' && ($email_provider !== 'zoho' || empty($email_meta['bulk_email_verified']) || $email_account_count < 1)) {
            $email_item = $this->build_readiness_item('blocked', 'Zoho Mail setup must be verified and at least one Zoho mailbox must be created.', array('provider' => $email_provider, 'verified' => !empty($email_meta['bulk_email_verified']), 'accounts' => $email_account_count));
        } elseif ($email_mode === 'manual_dns' && empty($email_meta['manual_dns_confirmed_at']) && $email_status !== 'completed') {
            $email_item = $this->build_readiness_item('blocked', 'Manual DNS email setup has not been confirmed.', array('status' => $email_status, 'dns_records_mode' => $email_meta['dns_records_mode'] ?? null));
        } elseif ($email_mode === 'existing_mailbox' && max($email_account_count, $email_role_count) < 1) {
            $email_item = $this->build_readiness_item('blocked', 'Existing mailbox route needs at least one provided business inbox.', array('accounts' => $email_account_count, 'role_addresses' => $email_role_count));
        } else {
            $email_item = $this->build_readiness_item('ready', 'Email routing is confirmed for this consultation.', array('mode' => $email_mode, 'status' => $email_status, 'accounts' => $email_account_count, 'role_addresses' => $email_role_count));
        }

        $phone_state = is_array($systems['phone'] ?? null) ? $systems['phone'] : array();
        $phone_meta = $user_id > 0 ? get_user_meta($user_id, 'vmo_phone_service', true) : array();
        $phone_ready = !empty($phone_state['ready']) || (is_array($phone_meta) && !empty($phone_meta['provisioned']));
        $phone_item = $phone_ready
            ? $this->build_readiness_item('ready', 'Business phone service is provisioned.', is_array($phone_meta) ? $phone_meta : array())
            : $this->build_readiness_item('blocked', 'Business phone provisioning is not complete.', is_array($phone_state['details'] ?? null) ? $phone_state['details'] : array());

        $product_catalog_status = $this->build_product_catalog_status($consultation_cd);
        if (empty($product_catalog_status['required'])) {
            $product_catalog_item = $this->build_readiness_item('not_required', 'Product/service catalog was not required by this consultation.', $product_catalog_status);
        } elseif (!empty($product_catalog_status['deferred'])) {
            $product_catalog_item = $this->build_readiness_item('human_action_pending', 'Owner chose to add or finish product catalog details later; catalog-dependent output stays draft until confirmed.', $product_catalog_status);
        } elseif (!empty($product_catalog_status['core_complete'])) {
            $product_catalog_item = $this->build_readiness_item('ready', 'Product/service catalog has enough confirmed detail for checkout and product pages.', $product_catalog_status);
        } else {
            $product_catalog_item = $this->build_readiness_item('blocked', 'Product/service catalog details are missing. Jerreme must collect names, descriptions, prices, media, variations, and fulfillment details.', $product_catalog_status);
        }

        $payments_profile = is_array($consultation_cd['payments_profile'] ?? null) ? $consultation_cd['payments_profile'] : array();
        $payments_state = is_array($systems['payments'] ?? null) ? $systems['payments'] : array();
        $payments_required = !empty($payments_profile['accepts_cards']) || !empty($payments_profile['supports_donations']) || !empty($payments_state['enabled']);
        if (!$payments_required) {
            $payments_item = $this->build_readiness_item('not_required', 'Payments were not required by this consultation.');
        } elseif (!empty($payments_state['checkout_ready'])) {
            $payments_item = $this->build_readiness_item('ready', 'VMO Pay checkout runtime is ready.', $payments_state);
        } else {
            $payments_table = $wpdb->prefix . 'vmo_connected_accounts';
            $account = ($user_id > 0 && $this->builder_table_exists($payments_table))
                ? $wpdb->get_row($wpdb->prepare("SELECT stripe_account_id, charges_enabled, payouts_enabled, details_submitted FROM {$payments_table} WHERE user_id = %d ORDER BY created_at DESC LIMIT 1", $user_id), ARRAY_A)
                : null;
            if (is_array($account) && !empty($account['stripe_account_id'])) {
                $payments_item = $this->build_readiness_item('human_action_pending', 'Stripe Express account exists, but onboarding or verification is not fully complete.', $account);
            } else {
                $payments_item = $this->build_readiness_item('blocked', 'VMO Pay / Stripe Express setup has not started or no connected account exists.', $payments_state);
            }
        }

        $lead_state = is_array($systems['lead_generation'] ?? null) ? $systems['lead_generation'] : array();
        $lead_count = $user_id > 0 ? $this->count_ai_discovery_leads($user_id) : 0;
        $leads_item = ($lead_count > 0 || (($lead_state['status'] ?? '') === 'ready'))
            ? $this->build_readiness_item('ready', 'Initial AI lead discovery has populated CRM leads.', array_merge($lead_state, array('count' => max($lead_count, (int) ($lead_state['count'] ?? 0)))))
            : $this->build_readiness_item('blocked', 'Initial lead discovery has not populated CRM leads.', $lead_state);

        $kpi_table = $wpdb->prefix . 'vmo_business_kpis';
        $kpi_count = ($user_id > 0 && $this->builder_table_exists($kpi_table))
            ? (int) $wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM {$kpi_table} WHERE user_id = %d", $user_id))
            : 0;
        $dashboard_item = (!empty($operating_state) && $kpi_count > 0)
            ? $this->build_readiness_item('ready', 'Dashboard operating state and KPI baselines are connected.', array('kpi_count' => $kpi_count))
            : $this->build_readiness_item('blocked', 'Dashboard operating state or KPI baselines are not connected.', array('kpi_count' => $kpi_count));

        $items = array(
            'website' => $this->build_readiness_item($preview_ready && $media_complete && $draft_ready ? 'ready' : 'blocked', 'Website draft, media, and preview readiness.', array('preview_ready' => $preview_ready, 'media_complete' => $media_complete, 'draft_ready' => $draft_ready)),
            'documents' => $this->build_readiness_item($roadmap_ready && $daily_ready && $legal_docs_ready && $business_package_ready ? 'ready' : 'blocked', 'Business package, roadmap, daily plan, and legal docs readiness.', array('roadmap_ready' => $roadmap_ready, 'daily_ready' => $daily_ready, 'legal_docs_ready' => $legal_docs_ready, 'business_package_ready' => $business_package_ready)),
            'email' => $email_item,
            'phone' => $phone_item,
            'product_catalog' => $product_catalog_item,
            'payments' => $payments_item,
            'leads' => $leads_item,
            'dashboard_kpis' => $dashboard_item,
        );

        $blocking_statuses = array('blocked');
        $blockers = array();
        foreach ($items as $key => $item) {
            if (in_array($item['status'] ?? 'blocked', $blocking_statuses, true)) {
                $blockers[$key] = $item;
            }
        }

        return array(
            'release_ready' => empty($blockers),
            'items' => $items,
            'blockers' => $blockers,
            'checked_at' => current_time('mysql'),
        );
    }

    private function build_readiness_item($status, $message, $details = array()) {
        return array(
            'status' => sanitize_key((string) $status),
            'message' => sanitize_text_field((string) $message),
            'details' => is_array($details) ? $details : array(),
        );
    }

    private function is_consultation_complete($session_id, $context) {
        // Check phase-based completion: consultation is complete when we reach
        // Phase 8 (Launch Readiness) or the consultation_flow marks it done.
        $consultation_cd = null;

        // Try server_consultation_data first (authoritative), then context fallback
        if (isset($context['server_consultation_data'])) {
            $consultation_cd = $context['server_consultation_data'];
        } elseif (isset($context['consultation_data'])) {
            $consultation_cd = $context['consultation_data'];
        }

        // If not in context, hydrate from DB
        if (!$consultation_cd && $session_id) {
            list(, $consultation_cd) = $this->hydrate_consultation_state($session_id);
        }

        if (!is_array($consultation_cd)) {
            return false;
        }

        // Check if consultation_flow explicitly marks completion, but never before
        // website/media/deliverable readiness is confirmed.
        $flow = $consultation_cd['consultation_flow'] ?? array();
        if (!empty($flow['completed'])) {
            return $this->is_consultation_release_ready($consultation_cd, $session_id);
        }

        // Check if we've reached Phase 8 (index 7, zero-based)
        $phases = $this->get_consultation_phases();
        $current_phase_index = intval($flow['current_phase_index'] ?? 0);
        $total_phases = count($phases);

        // Phase 8 is the final phase — if we're on it or past it, check artifacts
        if ($current_phase_index >= ($total_phases - 1)) {
            // Check Phase 8 done criterion: approvals.artifacts_generated
            $approvals = $consultation_cd['approvals'] ?? array();
            if (!empty($approvals['artifacts_generated']) && $this->is_consultation_release_ready($consultation_cd, $session_id)) {
                return true;
            }
            // On Phase 8 but artifacts not yet generated — still in progress
            return false;
        }

        return false;
    }
    
    /**
     * Transcribe audio using OpenAI Whisper
     */
    private function transcribe_audio($audio_data) {
        if (empty($this->api_key)) {
            return false;
        }
        
        // Convert base64 audio data to temporary file
        $audio_content = base64_decode($audio_data); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode -- data decoding
        $temp_file = wp_tempnam('vmo_audio_');
        vmo_get_filesystem()->put_contents($temp_file, $audio_content, FS_CHMOD_FILE);
        
        // Call Whisper API via wp_remote_post
        $boundary = wp_generate_password(24, false);
        $file_content = vmo_get_filesystem()->get_contents($temp_file);
        $body  = "--{$boundary}\r\n";
        $body .= "Content-Disposition: form-data; name=\"file\"; filename=\"audio.wav\"\r\n";
        $body .= "Content-Type: audio/wav\r\n\r\n";
        $body .= $file_content . "\r\n";
        $body .= "--{$boundary}\r\n";
        $body .= "Content-Disposition: form-data; name=\"model\"\r\n\r\n";
        $body .= "whisper-1\r\n";
        $body .= "--{$boundary}--\r\n";

        $wp_response = wp_remote_post('https://api.openai.com/v1/audio/transcriptions', array(
            'timeout' => 30,
            'headers' => array(
                'Authorization' => 'Bearer ' . $this->api_key,
                'Content-Type'  => 'multipart/form-data; boundary=' . $boundary,
            ),
            'body' => $body,
        ));

        if (is_wp_error($wp_response)) {
            $response = '';
        } else {
            $response = wp_remote_retrieve_body($wp_response);
        }

        // Clean up temp file
        unlink($temp_file);
        
        $result = json_decode($response, true);
        return $result['text'] ?? false;
    }
    
    // VOICE SYSTEM REMOVED: Text-to-speech method removed for text-only version
    
    /**
     * Generate consultation response with professional formatting and novice-friendly approach
     */
    private function generate_consultation_response($user_message, $context, $session_id = '', $system_prompt_override = null, $stream = false) {
        // ✅ LOAD PERSISTENT BUSINESS PROFILE (Cross-session memory!)
        $user_id = get_current_user_id();
        $business_profile_context = '';
        if ($user_id) {
            try {
                $business_profile_context = $this->build_profile_aware_system_prompt($user_id);
                
                // Auto-update consultation metadata
                $this->update_consultation_metadata($user_id, $user_message);
            } catch (\Throwable $e) {
                // Silently fail if business profile table doesn't exist yet
                // (User needs to deactivate/reactivate plugin to create table)
                error_log('VMO: Business profile error: ' . $e->getMessage());
            }
        }
        
        // Build the canonical backend rules. The frontend may send UI context, but it must
        // never replace the source-of-truth Jerreme consultation rules.
        $system_rules = $this->build_unified_system_rules();
        
        // ✅ INJECT BUSINESS PROFILE INTO SYSTEM RULES (if exists)
        if (!empty($business_profile_context)) {
            $system_rules = $business_profile_context . "

" . $system_rules;
        }
        
        // We'll construct an assistant context snapshot and a compact user instruction
        $assistant_ctx = '';
        $prompt = '';
        
        // Proceed confirmation is determined upstream via multilingual intent classification.
        // Do not rely on English keyword lists here.
        $proceed_confirmed = !empty($context['proceed_confirmed']);

        // Pull consultation context for this session: known answers, media, analysis, and current phase
        $phase_info = '';
        $known_block = '';
        $cd = is_array($context['server_consultation_data'] ?? null) ? $context['server_consultation_data'] : array();
        $row = null;

        if (!empty($session_id)) {
            $row = $this->consultation_db->get_consultation_by_session($session_id);
        }

        if ($row) {
            $analysis = is_string($row->business_analysis ?? '') ? trim($row->business_analysis) : '';
            if ($analysis !== '') {
                $known_block .= "Analysis Summary:
" . wp_strip_all_tags(mb_substr($analysis, 0, 4000)) . "

";
            }
            if (empty($cd)) {
                $decoded = json_decode($row->consultation_data ?: '[]', true);
                if (is_array($decoded)) {
                    $cd = $decoded;
                }
            }
        }

        if (!is_array($cd)) {
            $cd = array();
        }

        $enforced_domain = $this->get_enforced_domain();
        $meta_defaults = $this->consultation_blueprint['email_roles_meta'];
        if (!isset($cd['email_roles_meta']) || !is_array($cd['email_roles_meta'])) {
            $cd['email_roles_meta'] = $meta_defaults;
        } else {
            if (empty($cd['email_roles_meta']['locked_domain'])) {
                $cd['email_roles_meta']['locked_domain'] = $enforced_domain;
            } else {
                $cd['email_roles_meta']['locked_domain'] = $this->normalize_domain($cd['email_roles_meta']['locked_domain']);
                if ($cd['email_roles_meta']['locked_domain'] === '') {
                    $cd['email_roles_meta']['locked_domain'] = $enforced_domain;
                }
            }
            if (!isset($cd['email_roles_meta']['warnings']) || !is_array($cd['email_roles_meta']['warnings'])) {
                $cd['email_roles_meta']['warnings'] = array();
            }
            foreach ($meta_defaults as $meta_key => $meta_value) {
                if (!array_key_exists($meta_key, $cd['email_roles_meta'])) {
                    $cd['email_roles_meta'][$meta_key] = $meta_value;
                }
            }
            if (!is_array($cd['email_roles_meta']['existing_mailbox_validations'])) {
                $cd['email_roles_meta']['existing_mailbox_validations'] = array();
            }
            if (!is_array($cd['email_roles_meta']['dns_nameservers'])) {
                $cd['email_roles_meta']['dns_nameservers'] = array();
            }
        }

        if (!empty($cd['media_roles'])) {
            $known_block .= "Media Roles Assigned: " . count($cd['media_roles']) . " item(s)
";
            $examples = array_slice($cd['media_roles'], 0, 5);
            foreach ($examples as $mr) {
                $role = $mr['role'] ?? 'media';
                $title = $mr['title'] ?? basename((string)($mr['url'] ?? ''));
                $known_block .= "- {$role}: " . ($title ?: 'untitled') . "
";
            }
            $known_block .= "
";
        }

        if (!empty($cd['media_files'])) {
            $known_block .= "Media Files Uploaded: " . count($cd['media_files']) . " item(s)

";
        }

        if (isset($cd['consultation_flow'])) {
            $cf = $cd['consultation_flow'];
            $idx = isset($cf['current_phase_index']) ? intval($cf['current_phase_index']) : 0;
            $phases = isset($cf['phases']) && is_array($cf['phases']) ? $cf['phases'] : [];
            $current = $phases[$idx] ?? null;
            if ($current) {
                $phase_info = "CURRENT PHASE: " . ($current['name'] ?? ('Phase ' . ($idx+1))) . "
";
                if (!empty($current['objectives'])) {
                    $phase_info .= "Objectives:
- " . implode("
- ", array_slice((array)$current['objectives'], 0, 6)) . "
";
                }
                if (!empty($current['done_criteria'])) {
                    $phase_info .= "Completion Criteria:
- " . implode("
- ", array_slice((array)$current['done_criteria'], 0, 6)) . "
";
                }
            }
        }

        $doc_locked = !empty($cd['doc_locked']);
        $user_referenced_document = $this->user_message_references_document($user_message);
        $missing_email_roles = $this->get_missing_email_roles($cd);
        $email_roles_confirmed = $this->are_email_roles_confirmed($cd);
        $needs_email_roles_confirmation = $this->needs_email_roles_confirmation($cd);
        $email_meta = &$cd['email_roles_meta'];
        $email_domain_lock = $this->normalize_domain($email_meta['locked_domain'] ?? '') ?: $enforced_domain;
        $email_domain_warnings = array_filter((array)($email_meta['warnings'] ?? array()));
        $foreign_domains = $this->extract_foreign_domains_from_text($user_message, $email_domain_lock);
        if (!empty($foreign_domains)) {
            foreach ($foreign_domains as $foreign_domain) {
                $email_domain_warnings[] = 'User referenced ' . $foreign_domain . ' but this installation is locked to ' . $email_domain_lock . '.';
            }
        }
        if (!empty($email_domain_warnings)) {
            $email_domain_warnings = array_values(array_unique($email_domain_warnings));
        }
        $this->refresh_dns_automation_meta($email_meta, $email_domain_lock);

        // (Rules are in $system_rules; keep assistant context concise)
        $doc_store = isset($cd['document_store']) && is_array($cd['document_store']) ? $cd['document_store'] : array();
        $blueprintRaw = isset($doc_store['blueprint_raw']) ? (string)$doc_store['blueprint_raw'] : (string)($cd['blueprintRaw'] ?? '');
        $blueprintSummary = isset($doc_store['blueprint_summary']) ? (string)$doc_store['blueprint_summary'] : (string)($cd['blueprintSummary'] ?? '');

        // ✅ FIX: Fallback to frontend context if DB somehow lost blueprintRaw
        if ($blueprintRaw === '' && !empty($context['blueprintRaw'])) {
            $blueprintRaw = (string)$context['blueprintRaw'];
            error_log('VMO: blueprintRaw recovered from frontend context (DB copy was empty)');
        }

        $blueprint_context = '';

        if ($blueprintRaw !== '') {
            $blueprint_context .= "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
";
            $blueprint_context .= "📄 FULL BUSINESS PLAN DOCUMENT (UPLOADED BY USER)
";
            $blueprint_context .= "USE THIS AS THE INITIAL REFERENCE — but ANY corrections the user made during conversation OVERRIDE this document.
";
            $blueprint_context .= "If the user said something different from what this document says, THE USER'S LATEST STATEMENT IS THE TRUTH.
";
            $blueprint_context .= "🚨 DOMAIN LOCK: If this document mentions ANY email addresses on domains OTHER THAN @" . $email_domain_lock . ", IGNORE those emails entirely. ALL emails MUST use @" . $email_domain_lock . " — this is a fraud-prevention security requirement. Do NOT present, suggest, or ask about emails on any other domain. Silently replace them with the equivalent @" . $email_domain_lock . " address (e.g., if document says support@otherdomain.com, use support@" . $email_domain_lock . " instead).
";
            $blueprint_context .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

";
            // ✅ GPT-5.5 UPGRADE: Raised from 15K to 120K chars (~30K tokens)
            // GPT-5.5's massive context window can handle the full document
            $blueprint_context .= mb_substr($blueprintRaw, 0, 120000) . "

";
            $blueprint_context .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

";
        }

        if ($blueprintSummary === '' && !empty($context['blueprintSummary'])) {
            $blueprintSummary = (string)$context['blueprintSummary'];
        }

        if ($blueprintSummary !== '') {
            $known_block .= "Blueprint/Plan Summary (from user-provided document):
" . wp_strip_all_tags(mb_substr($blueprintSummary, 0, 6000)) . "

";
        }

        if (!empty($cd['document_qa_blocks']) && is_array($cd['document_qa_blocks'])) {
            $known_block .= "EXTRACTED Q&A FROM UPLOADED DOCUMENTS (DO NOT RE-ASK THESE):
";
            foreach ($cd['document_qa_blocks'] as $qa_block) {
                $known_block .= "--- From: " . ($qa_block['doc_title'] ?? 'Document') . " ---
";
                $known_block .= wp_strip_all_tags(mb_substr((string)($qa_block['qa_text'] ?? ''), 0, 8000)) . "

";
            }
        }

        if (!empty($cd['research_profile'])) {
            $rp = $cd['research_profile'];
            $known_block .= "RESEARCH HIGHLIGHTS:\n";
            if (!empty($rp['industry']['name'])) {
                $known_block .= '- Industry: ' . wp_strip_all_tags((string)$rp['industry']['name']) . "\n";
            }
            if (!empty($rp['market']['summary'])) {
                $known_block .= '- Market: ' . wp_strip_all_tags(mb_substr((string)$rp['market']['summary'], 0, 2000)) . "\n";
            }
            if (!empty($rp['competitors']) && is_array($rp['competitors'])) {
                $competitor_names = array_slice(array_map(function ($competitor) {
                    return $competitor['name'] ?? 'Competitor';
                }, $rp['competitors']), 0, 5);
                if (!empty($competitor_names)) {
                    $known_block .= '- Key Competitors: ' . implode(', ', $competitor_names) . "\n";
                }
            }
            if (!empty($rp['channels']['top'])) {
                $known_block .= '- Recommended Channels: ' . implode(', ', array_slice((array)$rp['channels']['top'], 0, 4)) . "\n";
            }
            $known_block .= "\n";
        }
        $all_docs_summary = '';

        // ✅ FIX: blueprintRaw is already injected via $blueprint_context in the user message (15,000 chars).
        // Removed duplicate injection here (was 8,000 chars in assistant context) to save ~2,000+ tokens per message.
        // Only inject supplementary document chunks that are NOT the main blueprint.

        // ✅ GPT-5.5 UPGRADE: Raised supplementary doc budget from 3K to 15K
        if (!empty($cd['documents']) && is_array($cd['documents'])) {
            $budget_remaining = 15000;
            foreach ($cd['documents'] as $doc) {
                if ($budget_remaining <= 0) break;
                $title = (string)($doc['title'] ?? 'Document');
                $chunks = (array)($doc['chunks'] ?? []);
                $first_chunks = array_slice($chunks, 0, 2);
                foreach ($first_chunks as $chunk) {
                    $text = is_array($chunk) ? (string)($chunk['text'] ?? $chunk) : (string)$chunk;
                    if ($text === '') continue;
                    $entry = "--- From: {$title} ---\n" . trim($text) . "\n\n";
                    $len = strlen($entry);
                    if ($budget_remaining - $len < 0) break 2;
                    $all_docs_summary .= $entry;
                    $budget_remaining -= $len;
                }
            }
        }

        if ($all_docs_summary !== '') {
            $known_block .= "PERSISTED DOCUMENT EXCERPTS (from database):\n" . $all_docs_summary . "\n";
        }

        // ✅ LONG-TERM MEMORY: Inject archived document summaries so the AI retains awareness
        // of ALL documents ever uploaded, even if the full text was evicted from active storage.
        if (!empty($cd['document_store']['archived_upload_summaries']) && is_array($cd['document_store']['archived_upload_summaries'])) {
            $archive_block = "ARCHIVED DOCUMENTS (full text evicted, summaries retained for memory):\n";
            foreach ($cd['document_store']['archived_upload_summaries'] as $archived) {
                if (!is_array($archived)) continue;
                $archive_block .= "- \"" . ($archived['title'] ?? 'Document') . "\" (uploaded, " . ($archived['original_length'] ?? '?') . " chars): " . ($archived['summary_excerpt'] ?? 'No excerpt') . "\n";
            }
            $known_block .= $archive_block . "\n";
        }

        // ✅ FIX: Add context block with known answers and phase info
        if ($phase_info || $known_block) {
            $assistant_ctx .= "CONTEXT FOR THIS TURN:
" . $phase_info . $known_block;
        }
        
        // ✅ REMOVED: $doc_locked section - it was causing AI to show placeholder templates
        // Blueprint is already injected via $blueprint_context in the user message below
        // Phase-specific instructions handle Phase 1 and Phase 2 correctly

        if (!empty($context) && !empty($context['history'])) {
            // ✅ CRITICAL FIX: DON'T put history in assistant_ctx
            // Instead, we'll pass it directly as conversation_history parameter to call_openai_api
            // This allows AI to see the actual message flow instead of a text summary
            $raw_history = $context['history'];
            error_log("VMO: Found " . count($raw_history) . " messages in conversation history");
            
            // ✅ MEMORY-SAFE HISTORY MANAGEMENT:
            // GPT-5.5 has a massive context window. Instead of brutally dropping middle phases,
            // we keep more history and inject a persistent "vision anchor" so the AI always
            // has the user's original business vision regardless of history length.
            $max_history = 160;
            if (count($raw_history) > $max_history) {
                // Keep first 60 messages (document uploads, initial vision, business profile, phase 1 corrections)
                // + last 80 messages (recent phases + current conversation)
                // + inject a VISION ANCHOR system message summarizing the bedrock information
                $first_batch = array_slice($raw_history, 0, 60);
                $last_batch = array_slice($raw_history, -80);
                $skipped_count = count($raw_history) - 140;

                // Build a vision anchor from the stored business profile
                $vision_anchor = $this->build_vision_anchor_summary($cd);

                $bridge_messages = array();
                if ($vision_anchor !== '') {
                    $bridge_messages[] = array('role' => 'system', 'content' => $vision_anchor);
                }
                $bridge_messages[] = array('role' => 'system', 'content' => '[' . $skipped_count . ' earlier messages omitted — core vision, targets, and all extracted information are preserved above in the vision anchor and context blocks]');

                $conversation_history = array_merge(
                    $first_batch,
                    $bridge_messages,
                    $last_batch
                );
                error_log("VMO: Truncated history from " . count($raw_history) . " to " . count($conversation_history) . " messages (with vision anchor)");
            } else {
                $conversation_history = $raw_history;
            }
        } else {
            $conversation_history = array();
        }

        // (Document context + archived docs are included above in CONTEXT FOR THIS TURN)
        
        // Check if we have collected information from context (snapshot for assistant_ctx)
        $collected = isset($context['collected']) ? $context['collected'] : array();
        if (!empty($collected)) {
            $assistant_ctx .= "INFORMATION COLLECTED SO FAR:
";
            foreach ($collected as $key => $value) {
                if ($value && !is_array($value)) {
                    $assistant_ctx .= "- " . ucfirst(str_replace('_', ' ', $key)) . ": {$value}
";
                } elseif (is_array($value) && count($value) > 0) {
                    $assistant_ctx .= "- " . ucfirst(str_replace('_', ' ', $key)) . ": " . implode(', ', $value) . "
";
                }
            }
            $assistant_ctx .= "
";
        }

        $discovery_profile = isset($cd['discovery_profile']) && is_array($cd['discovery_profile']) ? $cd['discovery_profile'] : array();
        $opportunity_shortlist = isset($cd['business_opportunity_shortlist']) && is_array($cd['business_opportunity_shortlist']) ? $cd['business_opportunity_shortlist'] : array();
        if ($this->is_discovery_scenario($cd) || $this->has_meaningful_consultation_value($discovery_profile) || !empty($opportunity_shortlist)) {
            $assistant_ctx .= "SCENARIO D DISCOVERY STATE:\n";
            if (!empty($cd['scenario_type'])) {
                $assistant_ctx .= "- Scenario: " . sanitize_text_field((string) $cd['scenario_type']) . "\n";
            }
            if (!empty($discovery_profile['mode'])) {
                $assistant_ctx .= "- Mode: " . sanitize_text_field((string) $discovery_profile['mode']) . "\n";
            }
            if (!empty($discovery_profile['status'])) {
                $assistant_ctx .= "- Status: " . sanitize_text_field((string) $discovery_profile['status']) . "\n";
            }
            if (!empty($discovery_profile['income_goal'])) {
                $assistant_ctx .= "- Income goal: " . wp_strip_all_tags((string) $discovery_profile['income_goal']) . "\n";
            }
            if (!empty($discovery_profile['location'])) {
                $assistant_ctx .= "- Location: " . wp_strip_all_tags((string) $discovery_profile['location']) . "\n";
            }
            if (!empty($discovery_profile['interests'])) {
                $assistant_ctx .= "- Interests: " . wp_strip_all_tags($this->consultation_text_from_value($discovery_profile['interests'])) . "\n";
            }
            if (!empty($discovery_profile['skills'])) {
                $assistant_ctx .= "- Skills: " . wp_strip_all_tags($this->consultation_text_from_value($discovery_profile['skills'])) . "\n";
            }
            if (!empty($discovery_profile['selected_business_idea'])) {
                $assistant_ctx .= "- Selected business idea: " . wp_strip_all_tags($this->consultation_text_from_value($discovery_profile['selected_business_idea'])) . "\n";
            }
            if (!empty($opportunity_shortlist)) {
                $assistant_ctx .= "- Opportunity shortlist:\n";
                foreach (array_slice($opportunity_shortlist, 0, 5) as $idea) {
                    if (!is_array($idea)) {
                        continue;
                    }
                    $name = wp_strip_all_tags((string) ($idea['name'] ?? $idea['title'] ?? $idea['business_name'] ?? 'Business idea'));
                    $fit = wp_strip_all_tags((string) ($idea['fit_reason'] ?? $idea['income_potential'] ?? ''));
                    $assistant_ctx .= "  - " . $name . ($fit !== '' ? ': ' . mb_substr($fit, 0, 180) : '') . "\n";
                }
            }
            $assistant_ctx .= "\n";
        }

        if (!empty($cd['email_roles']) && is_array($cd['email_roles'])) {
            $assistant_ctx .= "EMAIL ROLES CAPTURED:
";
            foreach ($cd['email_roles'] as $role => $email) {
                if (!empty($email)) {
                    $assistant_ctx .= "- " . strtoupper($role) . ": " . $email . "
";
                }
            }
            $assistant_ctx .= "
";
        }

        if (empty($missing_email_roles) && $needs_email_roles_confirmation && !$email_roles_confirmed) {
            $assistant_ctx .= "EMAIL ROLES CONFIRMATION REQUIRED: Do not move on until the user explicitly confirms all seven role-based inboxes are correct (or provides corrections). The user can accept all 7, remove ones they don't need, or add more. Ask them to reply 'confirm' once correct.\n\n";
        }

        if (!empty($missing_email_roles)) {
            $role_labels = array(
                'general' => 'General inquiries',
                'support' => 'Support',
                'marketing' => 'Marketing campaigns',
                'transactions' => 'Transactions/billing',
                'customercare' => 'Customer care',
                'bizdev' => 'Business development & partnerships',
                'newsletter' => 'Newsletter & subscriber updates'
            );
            $assistant_ctx .= "EMAIL ROLES STILL NEEDED:
";
            foreach ($missing_email_roles as $role_key) {
                $label = strtoupper($role_labels[$role_key] ?? $role_key);
                $assistant_ctx .= "- " . $label . " (domain email required)
";
            }
            $assistant_ctx .= "\n";
        }

        if (!empty($email_domain_lock)) {
            $assistant_ctx .= "🔒 DOMAIN LOCK: @" . $email_domain_lock . " — ALL emails and websites MUST use this domain only. If the user's document references a different domain, silently convert all emails to @" . $email_domain_lock . " equivalents. NEVER ask if they want emails on another domain. NEVER offer a domain choice.\n\n";
        }

        if (!empty($email_domain_warnings)) {
            $assistant_ctx .= "EMAIL DOMAIN WARNINGS:
";
            foreach ($email_domain_warnings as $warning) {

                $assistant_ctx .= "- " . $warning . "
";
            }
            $assistant_ctx .= "\n";
        }
        $automation_state = $email_meta['automation_readiness'] ?? 'unknown';
        $automation_reason = trim(wp_strip_all_tags((string)($email_meta['automation_reason'] ?? '')));
        if ($automation_state === 'auto_ready') {
            $assistant_ctx .= "EMAIL SETUP STATUS: Automated provisioning is disabled. " . ($automation_reason ?: 'Continue with manual mailbox setup.') . "\n";
            $assistant_ctx .= "Collect the existing role-based inboxes they already operate on this domain and confirm spelling before advancing.\n\n";
        } elseif ($automation_state === 'manual_only') {
            $assistant_ctx .= "EMAIL SETUP STATUS: Automated provisioning is disabled. " . ($automation_reason ?: 'Continue with manual mailbox setup.') . "\n";
            $assistant_ctx .= "Collect the existing role-based inboxes they already operate on this domain and confirm spelling before advancing.\n\n";
        }
        if ($user_referenced_document) {
            $assistant_ctx .= "USER REQUEST: They want you to reuse the uploaded document as a reference for this answer. But if they corrected any details in conversation, use those corrections instead.\n\n";
        }

        // ✅ INJECT SCANNED WEBSITE CONTEXT: Include any URLs the user has shared for reference
        $scanned_websites_context = $this->get_scanned_websites_context($cd);
        if (!empty($scanned_websites_context)) {
            $assistant_ctx .= $scanned_websites_context;
        }

        // Compose compact user prompt (latest message + job focus). Rules/context live in system/assistant.
        // 🔥 CRITICAL FIX: Include blueprint BEFORE the user message so AI sees it as context
        $prompt = '';
        if (!empty($blueprint_context)) {
            $prompt .= $blueprint_context;
        }

        // ✅ CORRECTIONS OVERRIDE: Inject user corrections AFTER the document so they clearly supersede it
        $user_corrections = isset($cd['user_corrections']) && is_array($cd['user_corrections']) ? $cd['user_corrections'] : array();
        if (!empty($user_corrections)) {
            $prompt .= "\n🚨🚨🚨 USER CORRECTIONS (THESE OVERRIDE THE DOCUMENT ABOVE — DO NOT REVERT THESE) 🚨🚨🚨\n";
            $prompt .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n";
            $prompt .= "The user has EXPLICITLY CORRECTED the following items during this conversation.\n";
            $prompt .= "These corrections are FINAL. NEVER revert to the original document values for these fields.\n";
            $prompt .= "If you use the wrong value after being corrected, the user will lose trust in this system.\n\n";
            foreach ($user_corrections as $correction) {
                if (!is_array($correction)) continue;
                $field = $correction['field'] ?? 'unknown';
                $wrong = $correction['wrong_value'] ?? '(original)';
                $correct = $correction['correct_value'] ?? '(unspecified)';
                $quote = $correction['user_quote'] ?? '';
                $prompt .= "  ❌ WRONG: {$field} = \"{$wrong}\"\n";
                $prompt .= "  ✅ CORRECT: {$field} = \"{$correct}\"\n";
                if ($quote) {
                    $prompt .= "  📝 User said: \"{$quote}\"\n";
                }
                $prompt .= "\n";
            }
            $prompt .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
        }

        $prompt .= "THEIR LATEST MESSAGE: \"{$user_message}\"\n\n
    ";

        if ($user_referenced_document && $blueprint_context !== '') {
            $prompt .= "REFERENCE REQUEST: The user says this information already exists in the uploaded document above. Re-inspect that document and answer with the actual data instead of repeating their reminder.\n\n";
        }
        $phase_for_email_gate = intval($cd['consultation_flow']['current_phase_index'] ?? 0);

        if ($phase_for_email_gate >= 5 && !empty($missing_email_roles)) {
            $prompt .= "EMAIL CAPTURE OUTSTANDING: Collect and confirm domain-based addresses for these roles before advancing: " . strtoupper(implode(', ', $missing_email_roles)) . ". Ask now and verify spelling.\n\n";
            $prompt .= "We need to capture and confirm the specific email addresses they'd like to use for various aspects of their business operations.\n";
            $prompt .= "🚨 ALL emails MUST use @" . $email_domain_lock . " (this is the domain where this system is installed — fraud prevention requires it).\n";
            $prompt .= "Here are the key roles that require domain-specific emails:\n";
            $prompt .= "- General inquiries (e.g., info@" . $email_domain_lock . ")\n";
            $prompt .= "- Support (e.g., support@" . $email_domain_lock . ")\n";
            $prompt .= "- Billing/transactions (e.g., billing@" . $email_domain_lock . ")\n";
            $prompt .= "- Customer care (e.g., customercare@" . $email_domain_lock . ")\n";
            $prompt .= "- Business development/partnerships (e.g., partnerships@" . $email_domain_lock . ")\n";
            $prompt .= "- Marketing campaigns (e.g., marketing@" . $email_domain_lock . ")\n";
            $prompt .= "- Newsletter/subscriber updates (e.g., newsletter@" . $email_domain_lock . ")\n";
            $prompt .= "ALWAYS recommend all 7 email addresses. Let the user accept all, remove ones they don't want, or add more.\n";
            $prompt .= "If the user's document mentions emails on a DIFFERENT domain, do NOT offer those. Silently use @" . $email_domain_lock . " instead and explain why.\n\n";
        }

        if ($phase_for_email_gate >= 5 && empty($missing_email_roles) && $needs_email_roles_confirmation && !$email_roles_confirmed) {
            $prompt .= "EMAIL ROLES PENDING CONFIRMATION: All seven role-based inboxes are present but NOT yet confirmed. Repeat the current list of all 7 emails back to the user and ask them to confirm, remove any they don't want, add more, or correct them. Do not ask unrelated questions and do not advance phases until they confirm.\n\n";
        }

        if (!empty($email_domain_lock)) {
            $prompt .= "🔒 WEBSITE & EMAIL DOMAIN RULE (FRAUD PREVENTION — NON-NEGOTIABLE):\n";
            $prompt .= "This installation is LOCKED to @" . $email_domain_lock . ". This is a security requirement.\n";
            $prompt .= "- ALL emails MUST use @" . $email_domain_lock . " — no exceptions.\n";
            $prompt .= "- ALL websites are built for " . $email_domain_lock . " — no other domain.\n";
            $prompt .= "- Reject Gmail/Yahoo/Outlook and any public mailbox provider.\n";
            $prompt .= "- If the user's business document mentions emails on a DIFFERENT domain, do NOT use them.\n";
            $prompt .= "  Instead, silently convert to @" . $email_domain_lock . " equivalents and tell the user:\n";
            $prompt .= "  'I noticed your document references [other domain], but for security and fraud prevention,\n";
            $prompt .= "   all emails and websites are created on your installed domain @" . $email_domain_lock . ".\n";
            $prompt .= "   I've set up the equivalent addresses on @" . $email_domain_lock . " for you.'\n";
            $prompt .= "- NEVER ask 'Would you like to use [other domain] instead?' — the answer is always NO.\n";
            $prompt .= "- NEVER offer the user a choice between domains — there is only ONE domain: @" . $email_domain_lock . ".\n\n";
        }
        if ($phase_for_email_gate >= 5 && $automation_state === 'auto_ready') {
            $prompt .= "EMAIL SETUP PATH: Automated provisioning is disabled. Collect and confirm the real role-based inboxes they already operate for every role (info/support/billing/marketing/etc). Do not mention DNS or third-party provisioning.\n\n";
        } elseif ($phase_for_email_gate >= 5 && $automation_state === 'manual_only') {
            $prompt .= "EMAIL SETUP PATH: Automated provisioning is disabled. Collect and confirm the real role-based inboxes they already operate for every role (info/support/billing/marketing/etc).\n\n";
        }

        if ($phase_for_email_gate >= 5 && !empty($email_domain_warnings)) {
            $prompt .= "EMAIL DOMAIN WARNINGS TO FIX:\n";
            foreach ($email_domain_warnings as $warning) {
                $prompt .= "- " . $warning . "\n";
            }
            $prompt .= "\n";
        }

        // 🚨 CRITICAL: Phase-aware instructions
        $current_phase_index = 0;
        if (!empty($cd['consultation_flow']['current_phase_index'])) {
            $current_phase_index = intval($cd['consultation_flow']['current_phase_index']);
        }
        
        // Check if user is confirming/proceeding (triggers phase transition)
        // Use multilingual intent classifier result passed through context.
        $user_confirming = $proceed_confirmed ? true : false;

        // Global reminders before phase-specific logic
        $phase_titles = [
            0 => 'PHASE 1 - BUSINESS FOUNDATIONS INTAKE',
            1 => 'PHASE 2 - EXECUTIVE BLUEPRINT CONFIRMATION',
            2 => 'PHASE 3 - DEEP INDUSTRY & MARKET RESEARCH',
            3 => 'PHASE 4 - STRATEGIC RECOMMENDATIONS & DECISIONS',
            4 => 'PHASE 5 - WEBSITE & EXPERIENCE ARCHITECTURE',
            5 => 'PHASE 6 - SYSTEMS, AUTOMATIONS & COMPLIANCE',
            6 => 'PHASE 7 - ROADMAP & DAILY EXECUTION MODEL',
            7 => 'PHASE 8 - LAUNCH READINESS & DELIVERABLES'
        ];
        $current_phase_title = $phase_titles[$current_phase_index] ?? ('PHASE ' . ($current_phase_index + 1));
        $phase1_doc_followup_turns = intval($cd['phase1_doc_followup_turns'] ?? 0);
        $product_catalog_status = $this->build_product_catalog_status($cd);
        $prompt .= "⚙️ CURRENT PHASE: {$current_phase_title}\n";
        $prompt .= "⚠️ Stay in this phase until the user explicitly confirms and every requirement is satisfied.\n";
        $prompt .= "🧠 CONVERSATIONAL INTELLIGENCE RULES:\n";
        $prompt .= "- Treat every reply as a continuation of the SAME conversation, never as a restart.\n";
        $prompt .= "- Your first sentence must acknowledge the user's latest point/correction before asking anything else.\n";
        $prompt .= "- Never reuse the same opening phrase in back-to-back turns.\n";
        $prompt .= "- Prefer one crisp bridge sentence + one consolidated question block for remaining gaps.\n";
        $prompt .= "- Assume the user is a business and IT novice unless they explicitly ask for technical depth.\n";
        $prompt .= "- Ask outcome-focused questions in plain language (what they want), not implementation questions (how to build it).\n";
        $prompt .= "- Do NOT ask users to choose technical stack/framework/API/DNS/repository mechanics; you decide those internally.\n";
        $prompt .= "- If you need precision, confirm the user's stated goal in one simple sentence instead of re-asking the same question in different words.\n";
        $prompt .= "- Do NOT ask budget-range, launch-deadline-constraint, or hands-on-vs-automation preference questions in Phases 1-2 unless the user explicitly asks for budget planning.\n";
        $prompt .= "- Infer operating model and execution style from business type, uploaded documents, and market research; present your recommendation later in recommendation phases.\n";
        if ($current_phase_index === 0 && $phase1_doc_followup_turns > 1) {
            $prompt .= "- You are in a follow-up after document intake. Do NOT say 'I have everything from your document' again. Continue naturally from their latest answer.\n";
        }
        if ($current_phase_index < 3) {
            $prompt .= "🚫 PHASE GATING RULE: Phases 1–3 are intake/confirmation/research ONLY. Do NOT provide recommendations, action plans, next steps, roadmaps, checklists, or implementation tactics.\n";
            $prompt .= "✅ Allowed: extracted facts, structured summaries, clarifying questions, and evidence-based research findings.\n";
        } else {
            $prompt .= "📂 Base every recommendation on uploaded documents, confirmed goals, and collected data.\n";
        }
        $prompt .= "📝 If information is missing, ask targeted follow-up questions instead of guessing.\n";

        if (!empty($product_catalog_status['required'])) {
            $prompt .= "\n🛒 PRODUCT/SERVICE CATALOG STATUS:\n";
            $prompt .= "- Catalog required: YES\n";
            $prompt .= "- Items captured: " . intval($product_catalog_status['items_count']) . " (products: " . intval($product_catalog_status['products_count']) . ", services: " . intval($product_catalog_status['services_count']) . ")\n";
            $prompt .= "- Core catalog complete: " . (!empty($product_catalog_status['core_complete']) ? 'YES' : 'NO') . "\n";
            if (!empty($product_catalog_status['deferred'])) {
                $prompt .= "- Owner deferred catalog completion: YES. Mark product/shop/payment output as draft until owner adds details.\n";
            }
            if (!empty($product_catalog_status['missing_required'])) {
                $prompt .= "- Missing required catalog details: " . implode('; ', array_slice($product_catalog_status['missing_required'], 0, 8)) . "\n";
            }
            if (!empty($product_catalog_status['missing_media'])) {
                $prompt .= "- Still ask about media in plain language: " . implode('; ', array_slice($product_catalog_status['missing_media'], 0, 5)) . "\n";
            }
            if (!empty($product_catalog_status['missing_variants'])) {
                $prompt .= "- Still ask about options/variations in plain language: " . implode('; ', array_slice($product_catalog_status['missing_variants'], 0, 5)) . "\n";
            }
            if (!empty($product_catalog_status['question_required'])) {
                $prompt .= "CATALOG INTAKE RULE: The consultant must ask for catalog details now in a novice-friendly way. Do not wait for the user to volunteer them. Ask for product/service names, descriptions, prices/currency, images, videos, variations/options, SKU or inventory if relevant, and fulfillment/shipping/digital/booking details. Offer a simple template they can answer in one message. If they are not ready, let them say they will add products later and mark the catalog as draft/deferred.\n";
            }
            $prompt .= "\n";
        }
        
        // ✅ CRITICAL: Build comprehensive "ALREADY ANSWERED" block from ALL sources
        // This prevents the AI from re-asking questions the user already answered
        $answered_items = array();
        $ei = isset($cd['extracted_information']) && is_array($cd['extracted_information']) ? $cd['extracted_information'] : array();
        if (!empty($ei['businessName']) && $ei['businessName'] !== 'Not specified') $answered_items[] = "Business Name: " . $ei['businessName'];
        if (!empty($ei['industry']) && $ei['industry'] !== 'Not specified') $answered_items[] = "Industry: " . $ei['industry'];
        if (!empty($ei['businessIdea']) && $ei['businessIdea'] !== 'Not specified') $answered_items[] = "Business Concept: " . mb_substr((string)$ei['businessIdea'], 0, 200);
        if (!empty($ei['targetAudience']) && $ei['targetAudience'] !== 'Not specified') $answered_items[] = "Target Audience: " . mb_substr((string)$ei['targetAudience'], 0, 200);
        if (!empty($ei['offerings'])) {
            $offerings_str = is_array($ei['offerings']) ? implode(', ', $ei['offerings']) : (string)$ei['offerings'];
            if ($offerings_str !== 'Not specified') $answered_items[] = "Offerings: " . $offerings_str;
        }
        if (!empty($ei['businessGoals']) && $ei['businessGoals'] !== 'Not specified') $answered_items[] = "Business Goals: " . mb_substr((string)$ei['businessGoals'], 0, 200);
        if (!empty($ei['businessVision']) && $ei['businessVision'] !== 'Not specified') $answered_items[] = "Vision: " . mb_substr((string)$ei['businessVision'], 0, 200);
        if (!empty($ei['visionTimeframe']) && $ei['visionTimeframe'] !== 'Not specified') $answered_items[] = "Vision Timeframe: " . $ei['visionTimeframe'];
        if (!empty($ei['location']) && $ei['location'] !== 'Not specified') $answered_items[] = "Location: " . $ei['location'];
        if (!empty($ei['offeringType'])) $answered_items[] = "Offering Type: " . $ei['offeringType'];
        if (!empty($ei['websitePreferences']) && $ei['websitePreferences'] !== 'Not specified') $answered_items[] = "Website Preferences: " . mb_substr((string)$ei['websitePreferences'], 0, 200);
        if (!empty($ei['pricing']) && $ei['pricing'] !== 'Not specified') $answered_items[] = "Pricing: " . mb_substr((string)$ei['pricing'], 0, 200);
        if (!empty($ei['brandingInfo']) && $ei['brandingInfo'] !== 'Not specified') $answered_items[] = "Branding: " . $ei['brandingInfo'];
        if (isset($ei['needsEcommerce'])) $answered_items[] = "E-commerce Needed: " . ($ei['needsEcommerce'] ? 'Yes' : 'No');
        if (!empty($product_catalog_status['items_count'])) $answered_items[] = "Catalog Items Captured: " . intval($product_catalog_status['items_count']);
        if (!empty($product_catalog_status['deferred'])) $answered_items[] = "Product Catalog: Deferred by owner for later completion";
        if (!empty($ei['payment_collection']['confirmed'])) $answered_items[] = "Payment Collection: " . ($ei['payment_collection']['mode'] ?? 'confirmed');
        if (!empty($ei['businessStage'])) $answered_items[] = "Business Stage: " . $ei['businessStage'];
        if (isset($ei['wants_branded_email'])) $answered_items[] = "Branded Email: " . ($ei['wants_branded_email'] ? 'Yes' : 'No');
        if (!empty($ei['business_email'])) $answered_items[] = "Business Email: " . $ei['business_email'];
        if (!empty($ei['email_frequency'])) $answered_items[] = "Email Frequency: " . $ei['email_frequency'];
        if (!empty($ei['successDefinition'])) $answered_items[] = "Success Definition: " . mb_substr((string)$ei['successDefinition'], 0, 150);
        if (!empty($ei['success_definition'])) $answered_items[] = "Success Definition: " . mb_substr((string)$ei['success_definition'], 0, 150);
        if (!empty($ei['websiteStatus'])) $answered_items[] = "Website Status: " . $ei['websiteStatus'];
        if (!empty($ei['primaryWebsiteUrl'])) $answered_items[] = "Primary Website URL: " . $ei['primaryWebsiteUrl'];
        if (!empty($ei['customPages']) && is_array($ei['customPages'])) $answered_items[] = "Custom Pages: " . implode(', ', $ei['customPages']);
        // Check media uploads
        if (!empty($cd['media_files']) && is_array($cd['media_files'])) {
            $media_count = count($cd['media_files']);
            $logo_files = array_filter($cd['media_files'], function($m) {
                $roles = isset($m['roles']) && is_array($m['roles']) ? $m['roles'] : [];
                return in_array('logo', $roles) || (isset($m['role']) && strpos($m['role'], 'logo') !== false);
            });
            if (count($logo_files) > 0) $answered_items[] = "Logo: UPLOADED (" . count($logo_files) . " file(s))";
            if ($media_count > 0) $answered_items[] = "Media Files: " . $media_count . " uploaded";
        }
        // Check documents
        if (!empty($cd['documents']) && is_array($cd['documents'])) {
            $doc_names = array_map(function($d) { return $d['title'] ?? 'untitled'; }, $cd['documents']);
            $answered_items[] = "Documents: " . count($cd['documents']) . " uploaded (" . implode(', ', $doc_names) . ")";
        }
        
        if (!empty($answered_items)) {
            $prompt .= "\n🚫🚫🚫 ALREADY ANSWERED — DO NOT RE-ASK THESE (" . count($answered_items) . " items) 🚫🚫🚫\n";
            foreach ($answered_items as $item) {
                $prompt .= "  ✅ " . $item . "\n";
            }
            $prompt .= "\nIf ANY of these topics come up, REFERENCE the known answer. NEVER ask for information listed above.\n";
            $prompt .= "Re-asking an already-answered question is a CRITICAL FAILURE. Check this list before every question.\n\n";
        }
        
        // Also scan conversation history for answers the extraction might have missed
        if (!empty($conversation_history) && is_array($conversation_history)) {
            $user_corrections = array();
            foreach ($conversation_history as $msg) {
                if (($msg['role'] ?? '') !== 'user') continue;
                $content = strtolower(trim($msg['content'] ?? ''));
                // Detect location answers
                if (empty($ei['location']) || $ei['location'] === 'Not specified') {
                    if (preg_match('/\b(atlanta|new york|los angeles|chicago|london|toronto|sydney|dubai)\b/i', $content, $m)) {
                        $user_corrections[] = "Location was mentioned in conversation: '" . mb_substr($msg['content'], 0, 100) . "'";
                    }
                }
            }
            if (!empty($user_corrections)) {
                $prompt .= "⚠️ USER CORRECTIONS FROM CONVERSATION (not yet in extracted_information):\n";
                foreach ($user_corrections as $correction) {
                    $prompt .= "  - " . $correction . "\n";
                }
                $prompt .= "\n";
            }
        }
        
        // ✅ CRITICAL: Anti-stalling and frustration detection
        $prompt .= "\n🚨 ANTI-STALLING RULES (HIGHEST PRIORITY — OVERRIDES ALL OTHER QUESTION INSTRUCTIONS):\n";
        $prompt .= "- If the user says 'stop asking questions', 'get going', 'proceed', 'keep going', or expresses ANY frustration → STOP asking questions immediately and PROCEED with what you have.\n";
        $prompt .= "- If a document has been uploaded, you have ENOUGH information to proceed. Present a summary and move forward.\n";
        $prompt .= "- NEVER ask more than 2-3 questions per response. Group related questions together.\n";
        $prompt .= "- NEVER say 'one more question', 'before we continue let me ask', or 'just one more thing'. These phrases FRUSTRATE users.\n";
        $prompt .= "- When user provides a URL → The system has ALREADY SCANNED it automatically. Reference those results directly.\n";
        $prompt .= "- When user says 'use the document' → IMMEDIATELY reference document contents. Do NOT ask further clarifying questions about it.\n";
        $prompt .= "- MAXIMUM 3 total rounds of questions per phase. After 3 rounds, PROCEED with what you have.\n";
        $prompt .= "- After receiving answers, DO THE WORK (summarize, confirm, advance) — do NOT ask yet another round.\n";
        $prompt .= "- COMBINED SOURCES RULE: If a document is uploaded AND a website is scanned, you have TWO sources. Only ask about things missing from BOTH sources.\n\n";
        $prompt .= "- NOVICE-FIRST RULE: never ask technical preference questions (frameworks, APIs, DNS, hosting internals, repo mechanics, churn math definitions) unless the user asks for technical detail first.\n\n";
        $prompt .= "- FRUSTRATION PREVENTION RULE: never ask budget/constraints or hands-on-vs-automation in this phase unless the user requests that planning depth.\n\n";
        
        // ✅ CRITICAL: Prevent re-asking for information that's already in the document
        $prompt .= "🚨 DOCUMENT-REFERENCE RULE - NEVER RE-ASK FOR INFORMATION ALREADY PROVIDED:\n";
        $prompt .= "- If the user's document contains short-term, mid-term, or long-term goals - DO NOT ASK FOR THEM AGAIN\n";
        $prompt .= "- If the document mentions target market, pricing, or offerings - DO NOT ASK FOR THEM AGAIN\n";
        // ✅ ADDED: Explicitly handle website features/style to prevent redundancy
        $prompt .= "- If the document mentions website structure, features, style preferences, or custom pages - DO NOT ASK FOR THEM AGAIN\n";
        $prompt .= "- Instead, QUOTE what you found in the document and ask the user to CONFIRM or CORRECT it\n";
        $prompt .= "- Example: 'I see your short-term goal is [X]. Your mid-term goal is [Y]. Is this still accurate?'\n";
        $prompt .= "- ONLY ask for information that is genuinely MISSING from the document\n\n";
        
        // ✅ UNIVERSAL DOCUMENT ANCHOR: Every phase must reference the uploaded document
        if (!empty($blueprintRaw)) {
            $prompt .= "\n📌 DOCUMENT REFERENCE DIRECTIVE (APPLIES TO ALL PHASES):\n";
            $prompt .= "The user uploaded a business document that is included above as 'FULL BUSINESS PLAN DOCUMENT'.\n";
            $prompt .= "- Reference this document for INITIAL information when no conversation correction exists.\n";
            $prompt .= "- When making recommendations, CITE specific details from the document (quote them).\n";
            $prompt .= "- When the user asks about something covered in their document, go back and EXTRACT the answer from it.\n";
            $prompt .= "- NEVER say 'I don't have that information' if it's in the document above.\n";
            $prompt .= "- ⚠️ CORRECTION PRIORITY: If information conflicts between conversation and document, THE USER'S CONVERSATION STATEMENT IS ALWAYS CORRECT. The document is the STARTING POINT, but the user's spoken/typed corrections SUPERSEDE it — ALWAYS, WITHOUT EXCEPTION.\n";
            $prompt .= "- The document is your INITIAL DATA SOURCE — but user corrections in conversation are the FINAL WORD.\n\n";
        }

        // Phase-specific instructions
        if ($current_phase_index === 0) {
            $discovery_selected_business = $this->get_selected_discovery_business($cd);
            if ($this->is_discovery_scenario($cd) && empty($discovery_selected_business)) {
                $prompt .= "SCENARIO D ACTIVE - BUSINESS DISCOVERY MODE:\n";
                $prompt .= "The user does not yet have a confirmed business idea, or is asking VMO to recommend the most profitable/best-fit business.\n";
                $prompt .= "Do NOT ask normal business-intake questions like business name, target market, or offers until a direction is chosen.\n";
                $prompt .= "First understand the person: interests, strengths, work background, personality/work style, location, time available, startup budget, income goal, risk tolerance, and lifestyle constraints.\n";
                $prompt .= "Ask only 2-3 friendly questions at a time. If enough profile detail exists, present 3-5 realistic business ideas with honest income potential, fit reasoning, startup cost, timeline, first steps, and risks.\n";
                $prompt .= "If they asked VMO to pick for them, recommend the strongest option and ask for approval. Once they approve a specific idea, treat that idea as the selected business and transition into the normal new-business setup path.\n";
                $prompt .= "Never append [[PHASE_COMPLETE]] while discovery_profile.needs_business_selection is true or no selected_business_idea exists.\n\n";
            }
            // ✅ FIX: Check what information already exists from the document to avoid redundant questions
            $has_document = !empty($blueprintRaw);
            $ei = isset($cd['extracted_information']) && is_array($cd['extracted_information']) ? $cd['extracted_information'] : array();
            $business_name_value = $ei['businessName'] ?? ($ei['business_name'] ?? null);
            $industry_value = $ei['industry'] ?? null;
            $offerings_value = $ei['offerings'] ?? array();
            $target_value = $ei['targetAudience'] ?? ($ei['target_audience'] ?? null);
            $pricing_value = $ei['pricing'] ?? null;
            $goals_value = $ei['businessGoals'] ?? ($ei['business_goals'] ?? null);
            $success_definition_value = $ei['successDefinition'] ?? ($ei['success_definition'] ?? null);
            $website_pref_value = $ei['websitePreferences'] ?? ($ei['website_preferences'] ?? null);

            $has_business_name = !empty($business_name_value) && $business_name_value !== 'Not specified';
            $has_industry = !empty($ei['industry']) && $ei['industry'] !== 'Not specified';
            $has_offerings = !empty($offerings_value) && is_array($offerings_value) && count($offerings_value) > 0;
            $has_target = !empty($target_value) && $target_value !== 'Not specified';
            $has_pricing = !empty($pricing_value) && $pricing_value !== 'Not specified';
            $has_goals = (!empty($goals_value) && $goals_value !== 'Not specified') || (!empty($success_definition_value) && $success_definition_value !== 'Not specified');
            $has_website_prefs = !empty($website_pref_value) && $website_pref_value !== 'Not specified';
            $has_pages = !empty($ei['customPages']) || !empty($ei['pageDescriptions']);
            $has_website_status = !empty($ei['websiteStatus']) || !empty($ei['primaryWebsiteUrl']);
            
            $prompt .= "YOUR JOB NOW (PHASE 1 - DOCUMENT ANALYSIS & UNDERSTANDING):\n";
            
            // ✅ SCANNED WEBSITES: Check for scanned website data as a SECOND source of truth
            $has_scanned_websites = !empty($cd['scanned_websites']) && is_array($cd['scanned_websites']);
            if ($has_scanned_websites) {
                $has_website_status = true;
            }
            $scanned_site_info = array();
            if ($has_scanned_websites) {
                foreach ($cd['scanned_websites'] as $scan_url => $scan_data) {
                    $site = array('url' => $scan_url);
                    if (!empty($scan_data['title'])) $site['title'] = $scan_data['title'];
                    if (!empty($scan_data['analysis']['business_type'])) $site['business_type'] = $scan_data['analysis']['business_type'];
                    if (!empty($scan_data['analysis']['value_proposition'])) $site['value_proposition'] = $scan_data['analysis']['value_proposition'];
                    if (!empty($scan_data['analysis']['key_features'])) $site['key_features'] = $scan_data['analysis']['key_features'];
                    if (!empty($scan_data['analysis']['design_analysis']['color_scheme'])) $site['color_scheme'] = $scan_data['analysis']['design_analysis']['color_scheme'];
                    if (!empty($scan_data['analysis']['design_analysis']['layout_type'])) $site['layout_type'] = $scan_data['analysis']['design_analysis']['layout_type'];
                    if (!empty($scan_data['analysis']['design_style'])) $site['design_style'] = $scan_data['analysis']['design_style'];
                    if (!empty($scan_data['headings'])) $site['headings'] = $scan_data['headings'];
                    if (!empty($scan_data['navigation'])) $site['navigation'] = $scan_data['navigation'];
                    $scanned_site_info[] = $site;
                }
            }

            // Determine what we know from ANY source (document + scanned website)
            $has_any_source = $has_document || $has_scanned_websites;
            
            if ($has_any_source) {
                $phase1_doc_followup_turns = intval($cd['phase1_doc_followup_turns'] ?? 0);
                $prompt .= "🚨 DOCUMENT/SCAN DETECTED: ABSORB all information silently. DO NOT comment on document quality or completeness.\n";
                $prompt .= "Treat the document as if the user SPOKE all of this to you. It REPLACES Phase 1 Q&A.\n";
                $prompt .= "Only ask about items GENUINELY MISSING from ALL sources.\n\n";
                
                // === SOURCE 1: UPLOADED DOCUMENT ===
                if ($has_document) {
                    $prompt .= "📄 SOURCE 1 — UPLOADED BUSINESS DOCUMENT:\n";
                    $prompt .= "EXTRACTED DATA (absorbed — DO NOT re-ask or comment on these):\n";
                    if ($has_business_name) $prompt .= "  ✅ Business Name: " . $business_name_value . "\n";
                    if ($has_industry) $prompt .= "  ✅ Industry: " . $ei['industry'] . "\n";
                    if ($has_offerings) $prompt .= "  ✅ Offerings: " . (is_array($offerings_value) ? implode(', ', $offerings_value) : $offerings_value) . "\n";
                    if ($has_target) $prompt .= "  ✅ Target Audience: " . $target_value . "\n";
                    if ($has_pricing) $prompt .= "  ✅ Pricing: " . $pricing_value . "\n";
                    if (!empty($goals_value)) $prompt .= "  ✅ Goals: " . $goals_value . "\n";
                    if (empty($goals_value) && !empty($success_definition_value)) $prompt .= "  ✅ Success Definition: " . $success_definition_value . "\n";
                    if ($has_website_prefs) $prompt .= "  ✅ Website Preferences: " . $website_pref_value . "\n";
                    if ($has_pages) $prompt .= "  ✅ Pages/Structure: Already specified in document\n";
                    $prompt .= "\n";
                }
                
                // === SOURCE 2: SCANNED WEBSITE(S) ===
                if ($has_scanned_websites) {
                    $prompt .= "🌐 SOURCE 2 — SCANNED WEBSITE(S) (auto-scanned by VMO):\n";
                    $prompt .= "The user's existing website(s) have been LIVE-SCANNED. This data is AUTHORITATIVE — use it.\n";
                    foreach ($scanned_site_info as $site) {
                        $prompt .= "  🔗 URL: " . $site['url'] . "\n";
                        if (!empty($site['title'])) $prompt .= "    ✅ Site Title: " . $site['title'] . "\n";
                        if (!empty($site['business_type'])) $prompt .= "    ✅ Business Type (from site): " . $site['business_type'] . "\n";
                        if (!empty($site['value_proposition'])) $prompt .= "    ✅ Value Proposition: " . $site['value_proposition'] . "\n";
                        if (!empty($site['key_features']) && is_array($site['key_features'])) {
                            $prompt .= "    ✅ Key Features: " . implode(', ', array_slice($site['key_features'], 0, 8)) . "\n";
                        }
                        if (!empty($site['color_scheme'])) $prompt .= "    ✅ Current Color Scheme: " . $site['color_scheme'] . "\n";
                        if (!empty($site['layout_type'])) $prompt .= "    ✅ Current Layout: " . $site['layout_type'] . "\n";
                        if (!empty($site['design_style'])) $prompt .= "    ✅ Design Style: " . $site['design_style'] . "\n";
                        if (!empty($site['headings']) && is_array($site['headings'])) {
                            $prompt .= "    ✅ Main Headings: " . implode(', ', array_slice($site['headings'], 0, 6)) . "\n";
                        }
                        if (!empty($site['navigation']) && is_array($site['navigation'])) {
                            $prompt .= "    ✅ Navigation/Pages: " . implode(', ', array_slice($site['navigation'], 0, 8)) . "\n";
                        }
                    }
                    $prompt .= "\n";
                    $prompt .= "  🚫 DO NOT ask 'What is your website about?' — you can SEE it above.\n";
                    $prompt .= "  🚫 DO NOT ask 'What pages do you want?' — the scanned navigation shows existing pages.\n";
                    $prompt .= "  🚫 DO NOT ask about business type when it's visible from the scan.\n";
                    $prompt .= "\n";
                }
                
                // === CROSS-REFERENCE RULE ===
                if ($has_document && $has_scanned_websites) {
                    $prompt .= "📋 CROSS-REFERENCE RULE:\n";
                    $prompt .= "Between the uploaded document AND the scanned website(s), you likely have MOST of the information needed.\n";
                    $prompt .= "Before asking ANY question, check BOTH sources. Only ask about things MISSING from BOTH.\n";
                    $prompt .= "If the document says one thing and the website shows another, note the discrepancy and ask which is current. If the user already clarified in conversation, use THEIR statement.\n\n";
                } elseif ($has_scanned_websites && !$has_document) {
                    $prompt .= "📋 WEBSITE-REFERENCE RULE:\n";
                    $prompt .= "The user's website has been scanned. Use this as your initial reference about their current business.\n";
                    $prompt .= "Summarize what you found on their site and confirm it. Only ask about things NOT visible on the website.\n";
                    $prompt .= "If the user corrects anything about their website data in conversation, their correction is the truth.\n\n";
                }
                
                $prompt .= "YOUR APPROACH:\n";
                $prompt .= "1. ABSORB all document/scan data silently — do NOT comment on quality or completeness\n";
                $prompt .= "2. Check: is ANYTHING genuinely missing that you NEED to understand their business?\n";
                $prompt .= "3. If missing items exist → ask them ALL in ONE consolidated question\n";
                if ($phase1_doc_followup_turns <= 1) {
                    $prompt .= "   You may introduce clarifications once with: 'I have everything from your document. Just a few things I want to clarify:'\n";
                } else {
                    $prompt .= "   CONTINUATION RULE: This is a follow-up turn after document intake. Do NOT repeat opening lines like 'I have everything from your document...'\n";
                    $prompt .= "   Start naturally from the user's latest answer (e.g., 'Perfect, noted on that. Next, ...') and only ask remaining missing items.\n";
                }
                $prompt .= "4. If NOTHING business-critical is missing → move straight to Phase 2 (present the full blueprint)\n";
                if ($has_document && !$has_website_prefs && !$has_pages && !$has_scanned_websites) {
                    $prompt .= "5. Website style preferences are missing — include in your clarification question\n";
                } elseif ($has_scanned_websites) {
                    $prompt .= "5. Website style/structure exists from scan — do NOT ask again\n";
                } else {
                    $prompt .= "5. Website preferences already captured — do NOT ask about them\n";
                }
                $prompt .= "6. Ask about media/assets ONLY if missing from document + uploads + conversation\n";
                if (!empty($product_catalog_status['required']) && !empty($product_catalog_status['question_required'])) {
                    $prompt .= "6A. Because this business will sell online, ask for the catalog now using a simple fill-in template: product/service name, short description, price/currency, images/videos available or needed, variations/options, and fulfillment/shipping/digital/booking notes.\n";
                }
                $prompt .= "7. Once clarifications are answered, append [[PHASE_COMPLETE]] to advance to Phase 2 ONLY if the business name/naming direction, industry/type, offer or venture idea, audience/problem, and success vision are known. A website rebuild request alone is not enough.\n";
                $prompt .= "\n🚫 DO NOT:\n";
                $prompt .= "- Comment on document quality ('great plan', 'this is thorough', 'a bit brief')\n";
                $prompt .= "- Re-summarize what the document says back to the user\n";
                $prompt .= "- Ask technical implementation questions (stack, APIs, DNS, repo path, framework selection)\n";
                $prompt .= "- Ask budget-range, hard launch constraints, or hands-on-vs-automation preference questions\n";
                if ($has_business_name || $has_scanned_websites) $prompt .= "- Ask 'What is your business?' when business info is already known\n";
                if ($has_target) $prompt .= "- Ask about target market when already extracted\n";
                if ($has_pricing) $prompt .= "- Ask about pricing when already in the document\n";
                if ($has_website_status) $prompt .= "- Ask about current website status/URL when it is already known\n";
                if ($has_scanned_websites) $prompt .= "- Ask about website purpose or pages when scan data exists\n";
                $prompt .= "- Ask questions one at a time — batch ALL missing items together\n";
                $prompt .= "- Stall with multiple confirmation rounds — ONE clarification pass then MOVE ON\n";
            } else {
                if ($this->is_discovery_scenario($cd) && empty($discovery_selected_business)) {
                    $prompt .= "THIS IS A DISCOVERY CONSULTATION, NOT NORMAL BUSINESS INTAKE YET:\n";
                    $prompt .= "Round 1: Ask about who they are: interests, skills, experience, preferred work style, location, income goal, available time, and startup budget.\n";
                    $prompt .= "Round 2: Summarize what you learned and present 3-5 best-fit business opportunities, ordered by strongest fit and realistic money potential.\n";
                    $prompt .= "Round 3: Help them choose or approve one business direction. Only after selection should you capture business name/naming direction, audience, offer, and success vision for setup.\n";
                    $prompt .= "Minimum needed to leave discovery: a selected business idea with industry/category, core offer, likely audience/problem, and realistic success/income direction.\n";
                } else {
                $prompt .= "THIS IS A REAL BUSINESS CONSULTATION — conduct a NATURAL CONVERSATION:\n";
                $prompt .= "Round 1: Ask ONE big opening question to let the user talk freely about their business vision.\n";
                $prompt .= "Round 2: Based on what they said, fill in gaps with targeted follow-up (batch 2-3 questions max): what the business does, who it serves, what it offers, what problem it solves, and what success should look like. If they sell products/services online, YOU must ask for catalog details in plain language; do not wait for them to bring it up.\n";
                $prompt .= "Round 3: Quick consolidated sweep for any remaining details (media, assets, website style).\n";
                $prompt .= "Max 3 rounds of conversation, but do not advance if the business itself is still undefined.\n";
                $prompt .= "Minimum needed to advance: business name or naming direction + industry/type + offerings/idea + target audience or problem solved + business vision/success goal.\n";
                }
            }
            $prompt .= "⚠️ DO NOT ask about email addresses yet — that comes in Phase 6 systems setup\n";
            $prompt .= "⚠️ DO NOT ask questions one at a time — batch them naturally\n";
            if ($user_confirming) {
                $prompt .= "User is confirming — append [[PHASE_COMPLETE]] to advance to Phase 2 ONLY if the minimum business foundation is complete. If the business offer, industry/type, audience/problem, or success vision is still blank, ask for those instead.\n";
            }
        } elseif ($current_phase_index === 1) {
            $prompt .= "YOUR JOB NOW (PHASE 2 - COMPLETE BUSINESS BLUEPRINT CONFIRMATION):\n";
            $prompt .= "🚨 CRITICAL OUTPUT RULE: NEVER include instructions, step labels (STEP 1:, STEP 2:), warnings (⚠️), ";
            $prompt .= "rules, AI actions, or meta-commentary in your response to the user. Only show ACTUAL business content.\n";
            $prompt .= "📄 DOCUMENT + CORRECTIONS CHECK: Re-read ALL sources (document, scanned websites, conversation).
";
            $prompt .= "⚠️ CORRECTION PRIORITY: If a 'USER CORRECTIONS' section exists in the context, those values are FINAL.\n";
            $prompt .= "   Use the CORRECTED values in the blueprint — NOT the original document values for those fields.\n";
            $prompt .= "   For any field NOT corrected, use the document/conversation data as-is.\n";
            $prompt .= "   NEVER revert a corrected value back to the document's original value.\n";
            $prompt .= "STEP 1: Present a COMPREHENSIVE, beautifully formatted business blueprint covering:\n";
            $prompt .= "   - Business Identity (name, type, industry, model, geography)\n";
            $prompt .= "   - Vision & Dreams (goals, success definition, income intent, involvement)\n";
            $prompt .= "   - 🌟 AI-FORMULATED BUSINESS VISION (with user/customer targets + revenue targets)\n";
            $prompt .= "   - Products & Services (everything mentioned with descriptions/prices)\n";
            $prompt .= "   - Target Audience (who they want to serve)\n";
            $prompt .= "   - Website Vision (style, features, reference sites, pages)\n";
            $prompt .= "   - Media & Assets (uploaded materials or 'to be AI-generated')\n";
            $prompt .= "   - Any constraints or considerations\n";
            $prompt .= "⚠️ BUSINESS VISION: Do NOT ask 'What is your vision?' — FORMULATE it from everything gathered.\n";
            $prompt .= "   Include measurable targets: user/customer count + revenue target (e.g., '2M users, \$50M revenue').\n";
            $prompt .= "   Present it to user for confirmation/adjustment.\n";
            $prompt .= "STEP 2: Ask user to review and confirm — 'Is this complete and accurate?'\n";
            $prompt .= "STEP 2.5: In this phase, do NOT ask technical implementation questions. Only ask business-intent corrections needed to finalize the blueprint.\n";
            $prompt .= "STEP 2.6: Do NOT ask budget/constraint or hands-on-vs-automation questions here. Finalize summary from known intent and move toward deep research.\n";
            $prompt .= "STEP 3: Explain the deep research transition and append [[PHASE_COMPLETE]] only after the user confirms a blueprint that contains the business name/naming direction, industry/type, offer or venture idea, audience/problem, and business vision/success goal. If those are blank, ask for them instead of offering research.\n";
            $prompt .= "EMAIL NOTE: Do NOT collect or confirm role-based mailbox addresses in Phase 2. Email setup belongs to Phase 6 after research, recommendations, and website architecture.\n";
            $prompt .= "⚠️ Present the FULL blueprint in ONE response — do NOT drip-feed sections.\n";
            $prompt .= "⚠️ Maximum 3 exchanges: (1) Present blueprint, (2) Handle corrections, (3) research transition once the business foundation is complete.\n";
            if ($user_confirming) {
                // Confirmation advances only after the business foundation is complete.
                // Email setup remains Phase 6 and is not part of the blueprint gate.
                $prompt .= "🚨 USER HAS CONFIRMED — advance only if the blueprint contains the core business foundation: business name/naming direction, industry/type, offer or venture idea, audience/problem, and success vision. Do NOT advance from a blueprint full of 'not confirmed yet' blanks.\n";
                $prompt .= "If the foundation is complete, tell user: 'I'm now going to perform a comprehensive deep-dive research of your market. This takes about 30-60 seconds.'\n";
                $prompt .= "Append [[PHASE_COMPLETE]] at the end of your response only when the foundation is complete.\n";
            }
        } elseif ($current_phase_index === 2) {
            $prompt .= "🚨 YOUR JOB NOW (PHASE 3 - DEEP MARKET RESEARCH) 🚨\n";
            $prompt .= "THIS IS THE MOST CRITICAL PHASE - DO NOT SKIP.\n";
            $prompt .= "📄 DOCUMENT CHECK: The uploaded document contains industry, niche, and competitive context. Extract ALL of it before researching.\n";
            $prompt .= "1. Identify the specific industry/niche from the consultation data AND the uploaded document\n";
            $prompt .= "2. Research and list 5-10 top competitors\n";
            $prompt .= "3. For each competitor analyze website structure, standout features, positioning, and pricing\n";
            $prompt .= "4. Identify market gaps and opportunities for differentiation\n";
            $prompt .= "5. Summarize possible differentiators observed in the market (DESCRIPTIVE ONLY — do not tell the user what to do yet)\n";
            $prompt .= "6. Deliver output with competitor comparison + gap analysis + positioning summary (NO recommendations, NO next steps)\n";
            $prompt .= "⚠️ Tie every insight to concrete evidence from research.\n";
            if ($user_confirming) {
                $prompt .= "7. User is confirming research - append [[PHASE_COMPLETE]] to advance to Phase 4 (CUSTOM ARCHITECTURE)\n";
            }
        } elseif ($current_phase_index === 3) {
            $prompt .= "YOUR JOB NOW (PHASE 4 - STRATEGIC RECOMMENDATIONS & DECISIONS):\n";
            $prompt .= "📄 DOCUMENT CHECK: Cross-reference the uploaded document for the client's stated goals, budget constraints, and preferences before making recommendations.\n";
            $prompt .= "1. Convert research insights into actionable, tailored recommendations\n";
            $prompt .= "2. Present recommendations as a structured list the user can approve/veto\n";
            $prompt .= "3. Cover: website strategy, SEO tactics, monetization, automation, compliance\n";
            $prompt .= "4. Tie EVERY recommendation back to research evidence from Phase 3\n";
            $prompt .= "5. NO generic advice – every item must reference THIS client's data\n";
            if ($user_confirming) {
                $prompt .= "6. User is confirming - append [[PHASE_COMPLETE]] to advance to Phase 5 (WEBSITE ARCHITECTURE)\n";
            }
        } elseif ($current_phase_index === 4) {
            $prompt .= "YOUR JOB NOW (PHASE 5 - STRATEGIC WEBSITE & EXPERIENCE ARCHITECTURE):\n";
            $prompt .= "📄 DOCUMENT CHECK: Re-read the document for website preferences, page structure, style preferences, and any reference sites the client mentioned.\n";
            $prompt .= "\n⚡ STRATEGIC REQUIREMENTS (MANDATORY):\n";
            $prompt .= "This is NOT a generic website plan. Every page and section must be strategically tied to the research findings, competitor analysis, and approved recommendations from previous phases.\n";
            $prompt .= "1. Design the full site map based on APPROVED recommendations from Phase 4 — each recommended page must appear\n";
            $prompt .= "2. For EACH page: define its strategic purpose, what competitor weakness it addresses, and how it converts the specific target audience identified in research\n";
            $prompt .= "3. Reference specific competitors by name: explain how each key page outperforms what competitors offer (e.g., 'Your Services page includes outcome-based descriptions — unlike [Competitor] who only lists features')\n";
            $prompt .= "4. Specify copy tone, style direction, and design differentiators that position the business uniquely in its market\n";
            $prompt .= "5. Incorporate reference website insights if user shared any\n";
            $prompt .= "6. For each page's CTA strategy: specify EXACTLY what action the visitor takes and how it feeds the business pipeline\n";
            $prompt .= "7. Tie every section back to research data — market position, target audience pain points, and competitor gaps\n";
            $prompt .= "8. Include conversion optimization strategy per page (what makes a visitor become a customer on THIS specific page)\n";
            if (!empty($product_catalog_status['required']) && !empty($product_catalog_status['question_required'])) {
                $prompt .= "9. E-commerce/catalog intake is not complete. Before approving shop/product pages, ask the user for product/service names, descriptions, prices/currency, images/videos, variations/options, and fulfillment details in one friendly template. Do NOT append [[PHASE_COMPLETE]] until they answer or explicitly say to add the catalog later as draft.\n";
            }
            $prompt .= "\n🏆 PREMIUM TECH STACK BRIEFING (Inform the user):\n";
            $prompt .= "Tell the user their website will be built with these premium technologies:\n";
            $prompt .= "- React 18 component architecture (exportable as Next.js project)\n";
            $prompt .= "- GSAP + ScrollTrigger for cinematic scroll animations on every section\n";
            $prompt .= "- Lenis buttery-smooth scrolling (luxury feel)\n";
            $prompt .= "- 3-layer depth design: background videos/images, content layer, glassmorphic foreground accents\n";
            $prompt .= "- Scroll-triggered video playback, animated counters, chart animations\n";
            $prompt .= "- Barba.js cinematic page transitions\n";
            $prompt .= "- AI-generated images and videos where users don't provide their own\n";
            $prompt .= "- Animated overlays (particles, sparkles, etc.) where appropriate for their brand\n";
            $prompt .= "⚠️ Reassure the user: 'Every element of your website is refineable after launch — you can adjust any page, section, animation, or image through conversation with me.'\n";
            if ($user_confirming) {
                $prompt .= "9. User is confirming website plan - append [[PHASE_COMPLETE]] to advance to Phase 6 (SYSTEMS & COMPLIANCE)\n";
            }
        } elseif ($current_phase_index === 5) {
            $prompt .= "YOUR JOB NOW (PHASE 6 - SYSTEMS, AUTOMATIONS & COMPLIANCE):\n";
            $prompt .= "📄 DOCUMENT CHECK: Check the document for any mentioned tools, integrations, compliance requirements, or automation preferences.\n";
            $prompt .= "1. If online selling is part of the business, finalize the product/service catalog BEFORE treating checkout or product pages as ready. Ask in plain language for names, descriptions, prices/currency, images, videos, variations/options, SKU/inventory if relevant, and fulfillment/shipping/digital/booking details.\n";
            $prompt .= "2. Define the full tech stack: CRM, phone system, email automation, analytics\n";
            $prompt .= "3. Map out automation workflows (lead capture, follow-ups, notifications)\n";
            $prompt .= "4. List every legal/compliance document required (privacy policy, terms, etc.)\n";
            $prompt .= "5. Specify where each system integrates with the website\n";
            $prompt .= "6. Email setup belongs here: present all 7 recommended role-based inboxes on @" . $email_domain_lock . " and explain that each maps to the correct website form, support route, billing route, customer care route, partnership route, marketing route, and newsletter route.\n";
            $prompt .= "7. If this is the managed VMO QuickLaunch path on Vultr.com with Cloudflare + MXroute, explain that VMO can auto-provision the confirmed accounts after the user approves the role names. Do not ask the user for DNS/MX steps on that managed path. If this is an external WordPress/domain path, explain the three realistic options: connect existing inboxes, follow guided manual DNS/MX setup with their provider, or use the bundled Zoho Mail path. Keep this novice-friendly.\n";
            $prompt .= "8. Present the systems plan for user approval\n";
            if (!empty($product_catalog_status['question_required'])) {
                $prompt .= "PRODUCT CATALOG REQUIRED: Do NOT append [[PHASE_COMPLETE]] yet. Ask the catalog question now. Use this simple novice-friendly format: 'For each product/service, send me: name, one-sentence description, price, any images/videos you have, variations like size/color/package, and whether it is shipped, digital, booked, or delivered as a service.'\n";
            }
            if (!$email_roles_confirmed) {
                $prompt .= "EMAIL CONFIRMATION REQUIRED: Do NOT append [[PHASE_COMPLETE]] yet. Ask the user to accept all 7 recommended addresses, remove any they do not want, add more, or correct the aliases.\n";
            } elseif ($user_confirming) {
                $prompt .= "9. User is confirming systems plan - append [[PHASE_COMPLETE]] to advance to Phase 7 (ROADMAP)\n";
            }
        } elseif ($current_phase_index === 6) {
            $prompt .= "YOUR JOB NOW (PHASE 7 - ROADMAP & DAILY EXECUTION MODEL):\n";
            $prompt .= "📄 DOCUMENT CHECK: Reference the document for any timeline expectations, launch dates, or phased rollout plans the client described.\n";
            $prompt .= "⚡ CRITICAL: The Vision Roadmap Engine will generate the FULL milestone-based roadmap automatically.\n";
            $prompt .= "   Your role is to PRESENT and CONFIRM it with the user, NOT to manually list milestones.\n\n";
            $prompt .= "1. Summarize the key milestones the engine has generated (or will generate) based on ALL approved plans\n";
            $prompt .= "2. Explain the MILESTONE-BASED approach: the roadmap runs from Day 1 (consultation date) to the Vision Achievement Date\n";
            $prompt .= "3. Clarify that the timeline is NOT arbitrary — it is CALCULATED by the AI based on business complexity, goals, and scope\n";
            $prompt .= "4. Explain the Daily Activities system: every single day has specific tasks derived from the milestones\n";
            $prompt .= "5. Explain the Daily Reports mechanism: each day the user reports what was completed vs planned\n";
            $prompt .= "   - Completed tasks = PROGRESS → vision achievement date stays or moves closer\n";
            $prompt .= "   - Missed tasks = DELAY → extra days added to vision achievement date (carried forward automatically)\n";
            $prompt .= "6. Explain that deep market research runs in the background and may recommend roadmap adjustments\n";
            $prompt .= "   - The user will be notified of recommended changes and must approve them before they take effect\n";
            $prompt .= "7. Define the North Star metric and how it anchors ALL progress tracking\n";
            $prompt .= "8. Present the execution roadmap overview for final confirmation\n";
            if ($user_confirming) {
                $prompt .= "9. User is confirming roadmap - append [[PHASE_COMPLETE]] to advance to Phase 8 (LAUNCH)\n";
            }
        } elseif ($current_phase_index === 7) {
            $prompt .= "YOUR JOB NOW (PHASE 8 - LAUNCH READINESS & DELIVERABLES):\n";
            $prompt .= "📄 DOCUMENT CHECK: Final cross-reference — ensure all deliverables align with what the client originally described in their uploaded document.\n";
            $prompt .= "1. Summarize the entire consultation: key decisions, approved plans, and deliverables\n";
            $prompt .= "2. Confirm all approvals are in place\n";
            $prompt .= "3. Trigger generation of website, documents, and other artifacts\n";
            $prompt .= "4. Provide a launch checklist and hand-off instructions\n";
            $prompt .= "5. Direct user to the dashboard/download center for their deliverables\n";
            $prompt .= "⚠️ IMPORTANT: Remind the user that after their website is generated, they have FULL access to a refinement system.\n";
            $prompt .= "   They can adjust any page, section, text, image, animation, or layout — all through conversation.\n";
            $prompt .= "   The website is a premium starting point, and they can perfect every detail post-launch.\n";
            if ($user_confirming) {
                $prompt .= "6. User is confirming launch readiness - append [[PHASE_COMPLETE]] to complete the consultation\n";
            }
        } else {
            $prompt .= "YOUR JOB NOW (PHASE " . ($current_phase_index + 1) . "):\n";
            $prompt .= "1. Continue the consultation based on the current context\n";
            $prompt .= "2. Tie every recommendation back to insights gathered earlier\n";
            if ($user_confirming) {
                $prompt .= "3. User is confirming - append [[PHASE_COMPLETE]] to move to the next phase\n";
            }
        }
        
        // Legacy doc-locked logic — proactive consultant drive
        if ($doc_locked && !$user_confirming) {
            $prompt .= "
📄 Note: The user's business blueprint has been analyzed and locked. You have full access to the extracted data above.
CRITICAL: Do NOT wait for the user to say 'keep going' or 'continue'. Proactively drive the consultation forward.
Review the key details you extracted, confirm your understanding with the user, and tell them what comes next.
";
        }

        $consultation_model_tier = $this->resolve_consultation_response_model_tier($current_phase_index, $user_confirming);

        // ✅ CRITICAL FIX: Pass conversation history as separate parameter
        // ✅ GPT-5.5 UPGRADE: use_gpt5=true enables 4000-token responses for comprehensive, intelligent output
        return $this->call_openai_api($prompt, 4000, true, false, $system_rules, $assistant_ctx, true, $conversation_history, $stream, $consultation_model_tier);
    }

    private function resolve_consultation_response_model_tier($current_phase_index, $user_confirming) {
        $current_phase_index = intval($current_phase_index);

        if (!empty($user_confirming)) {
            return 'pro';
        }

        // Phase 2 blueprint, and Phases 4-8, are where Jerreme turns intake/research into
        // executive judgment: strategy, website architecture, systems, roadmap, and launch.
        if ($current_phase_index === 1 || $current_phase_index >= 3) {
            return 'pro';
        }

        return 'strategic';
    }

    private function enforce_consultation_continuity($response, array $cd, $current_phase_index) {
        if (!is_string($response) || $response === '') {
            return $response;
        }

        $out = $response;
        $doc_turns = intval($cd['phase1_doc_followup_turns'] ?? 0);

        if (intval($current_phase_index) === 0 && $doc_turns > 1) {
            $out = preg_replace('/\bI\s+have\s+everything\s+from\s+your\s+document\b[^\n\.]*[\.:]?\s*/i', 'Perfect — thanks for that clarification. ', $out, 1);
            $out = preg_replace('/\bjust\s+a\s+few\s+things\s+i\s+want\s+to\s+clarif(?:y|ication)\b[^\n\.]*[\.:]?\s*/i', '', $out);
        }

        if (intval($current_phase_index) === 0) {
            $ei = isset($cd['extracted_information']) && is_array($cd['extracted_information']) ? $cd['extracted_information'] : array();
            $has_scanned = !empty($cd['scanned_websites']) && is_array($cd['scanned_websites']);
            $has_website_status = !empty($ei['websiteStatus']) || !empty($ei['primaryWebsiteUrl']) || $has_scanned;
            $has_success_definition = !empty($ei['successDefinition']) || !empty($ei['success_definition']);

            if ($has_website_status) {
                $out = preg_replace('/^.*\b(existing\s+website\s+status|website\s+status|current\s+website\s+url|is\s+it\s+https?:\/\/|fresh\s+build\s+from\s+scratch|rebuild\s+or\s+redesign)\b.*\?\s*$/im', '', $out);
            }
            if ($has_success_definition) {
                $out = preg_replace('/^.*\b(success\s+definition|when\s+is\s+.*successful|active\s+paying\s+subscribers|cumulative\s+sign\s*ups|churn)\b.*\?\s*$/im', '', $out);
            }
        }

        if (intval($current_phase_index) <= 1) {
            $out = preg_replace('/^.*\b(api|dns|mx\s*records?|framework|react|next\.?js|hosting\s+provider|repository|repo|wordpress\.org\s+listing|zip\s+download|churn\s+definition|cumulative\s+sign\s*ups)\b.*\?\s*$/im', '', $out);
            $out = preg_replace('/^.*\b(budget|budget\s+range|constraints?|must\s+be\s+live\s+by|launch\s+date|deadline|hands[-\s]?on|self[-\s]?serve|minimal\s+human\s+involvement|automation\s+preference)\b.*\?\s*$/im', '', $out);
        }

        $lines = preg_split('/\R/', trim($out));
        if (is_array($lines) && count($lines) >= 2) {
            $first = trim($lines[0]);
            $second = trim($lines[1]);
            if ($first !== '' && strtolower($first) === strtolower($second)) {
                array_splice($lines, 1, 1);
                $out = implode("\n", $lines);
            }
        }

        $out = preg_replace("/\n{3,}/", "\n\n", $out);
        return trim((string) $out);
    }

    /**
     * Phase gating hard guard.
     *
     * Phases 1–3 must not output recommendations, next steps, roadmaps, or action plans.
     * This strips common "recommendations" sections in case the model leaks them.
     */
    private function enforce_no_recommendations_until_phase4($response) {
        if (!is_string($response) || $response === '') {
            return $response;
        }

        $out = $response;

        // Strip common Markdown header sections.
        $section_headings = [
            'recommendations',
            'strategic recommendations',
            'action plan',
            'implementation plan',
            'next steps',
            'roadmap',
            'tactics'
        ];
        foreach ($section_headings as $heading) {
            $pattern = '/(^|\n)#{1,6}\s*' . preg_quote($heading, '/') . '\b[\s\S]*?(?=\n#{1,6}\s|\z)/i';
            $out = preg_replace($pattern, "\n", $out);
        }

        // Strip bold-heading blocks like **Next Steps** ...
        foreach ($section_headings as $heading) {
            $pattern = '/(^|\n)\*\*\s*' . preg_quote($heading, '/') . '\s*\*\*[\s\S]*?(?=\n\*\*[^*]{2,}\*\*|\z)/i';
            $out = preg_replace($pattern, "\n", $out);
        }

        // If it still contains explicit "Next Steps:" lines, remove that line and following bullets.
        $out = preg_replace('/(^|\n)\s*(next steps|recommendations|action plan)\s*:\s*\n(?:\s*[-•].*\n)+/i', "\n", $out);

        // Keep response readable.
        $out = preg_replace("/\n{3,}/", "\n\n", $out);
        $out = trim($out);

        // Never return empty: provide a phase-safe fallback.
        if ($out === '') {
            return "I’m still in the intake/research phases. I’ll focus on extracting what you’ve provided, confirming any missing details, and compiling evidence-based research findings. Once we reach Phase 4, I’ll present recommendations.";
        }

        return $out;
    }

    // Persist doc_locked flag
    private function set_doc_locked($session_id, $val) {
        if (empty($session_id)) return;
        $row = $this->consultation_db->get_consultation_by_session($session_id);
        $user_id = get_current_user_id();
        $cd = $row ? json_decode($row->consultation_data ?: '[]', true) : [];
        if (!is_array($cd)) $cd = [];
        $cd['doc_locked'] = (bool)$val;
        $this->consultation_db->save_consultation($user_id, $session_id, [ 'consultation_data' => $cd ]);
    }

    /**
     * Unified system behavior rules for consultation (document-first & media-aware)
     * 
     * UPGRADED: Now uses VMO_Elite_Consultation_Prompts for the elite 8-phase flow
     * MASTER PRINCIPLE: VMO is a Business Execution System, NOT a chatbot
     * NO RECOMMENDATIONS until Phase 4
     */
    private function build_unified_system_rules() {
        // Load the elite consultation prompts class
        if (!class_exists('VMO_Elite_Consultation_Prompts')) {
            require_once plugin_dir_path(dirname(__FILE__)) . 'includes/class-vmo-elite-consultation-prompts.php';
        }
        
        // Get elite prompts - these implement the full specification with structured outputs
        $elite_prompts = VMO_Elite_Consultation_Prompts::get_instance();
        $s = $elite_prompts->build_elite_system_rules();
        
        // Add backward compatibility section for existing features
        $s .= "
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📋 ADDITIONAL SYSTEM FEATURES (BACKWARD COMPATIBILITY)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

";
        // Note: The elite prompts already contain all the phase definitions
        // Adding supplemental features below for backward compatibility
        
        $s .= "MEDIA HANDLING:
";
        $s .= "• When user uploads media (logos, images, videos), acknowledge and store for use in Phase 7
";
        $s .= "• If user has no media, offer AI generation options in Phase 7
";
        $s .= "• Reference websites shared by user inform the design direction

";
        $s .= "DOCUMENT HANDLING:
";
        $s .= "• When user uploads business plan, extract all information in Phase 1
";
        $s .= "• Only ask for truly missing CRITICAL fields
";
        $s .= "• Use extracted data verbatim - never show placeholder text

";
        $s .= "CREDIT SYSTEM AWARENESS:
";
        $s .= "• Live AI Chat uses credits per conversation
";
        $s .= "• AI Phone Calls use credits per minute
";
        $s .= "• Inform users about credit-consuming features in Phase 4

";

        $s .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
";
        $s .= "🚀 PROACTIVE CONSULTANT DIRECTIVE (CRITICAL)
";
        $s .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

";
        $s .= "You are a PROACTIVE business consultant. You DRIVE the consultation forward at all times.
";
        $s .= "NEVER go silent. NEVER wait for the user to say 'keep going' or 'continue' or 'next'.
";
        $s .= "After document analysis, IMMEDIATELY review the key findings, confirm details, and guide them forward.
";
        $s .= "If the business sells products, services, subscriptions, bookings, or anything payable online, YOU must ask for the product/service catalog details in novice-friendly language: names, images, videos, descriptions, prices, variations/options, inventory/SKU where relevant, and fulfillment/shipping/digital/booking details. Do not wait for the user to volunteer these.
    ";
        $s .= "If the user is not ready with catalog details, mark the catalog as draft/deferred and explain they can add it later from the dashboard or by continuing the conversation.
    ";
        $s .= "You are being PAID to consult — act like it. Be thorough, insightful, and always moving forward.

";
        // ✅ DEDUP FIX: Question discipline rules removed here — they already exist in
        // get_master_principle() (elite prompts) AND in generate_consultation_response() (wizard).
        // Having them 3x wasted ~400 tokens per request and confused the AI with competing rule sets.

        return $s;
    }

    private function consultation_text_from_value($value) {
        if (is_array($value)) {
            $parts = array();
            foreach ($value as $item) {
                $part = $this->consultation_text_from_value($item);
                if ($part !== '') {
                    $parts[] = $part;
                }
            }
            return trim(implode(' ', $parts));
        }
        if (is_object($value)) {
            return $this->consultation_text_from_value((array) $value);
        }
        return trim(wp_strip_all_tags((string) $value));
    }

    private function is_meaningful_consultation_value($value) {
        $text = $this->consultation_text_from_value($value);
        if ($text === '' || mb_strlen($text) < 3) {
            return false;
        }

        $normalized = strtolower(preg_replace('/\s+/', ' ', $text));
        $empty_markers = array(
            'not confirmed', 'not confirmed yet', 'not specified', 'unspecified', 'unknown',
            'still needed', 'still open', 'open blank', 'blank', 'tbd', 'to be determined',
            'none', 'no', 'n/a', 'na', 'not applicable', 'general', 'your industry',
            'your business', 'the business', 'website', 'site', 'domain'
        );
        if (in_array($normalized, $empty_markers, true)) {
            return false;
        }
        foreach (array('not confirmed', 'still needed', 'open blank') as $marker) {
            if (strpos($normalized, $marker) !== false) {
                return false;
            }
        }
        if (preg_match('/^(yes|yeah|yep|ok|okay|sure|confirmed|correct|proceed|continue)$/i', $normalized)) {
            return false;
        }

        $website_only = preg_match('/\b(build|rebuild|replace|redesign|preview|publish|launch|new)\b.*\b(website|site|domain|getvmo\.com)\b/i', $text)
            || preg_match('/\b(website|site|domain|getvmo\.com)\b.*\b(build|rebuild|replace|redesign|preview|publish|launch|new)\b/i', $text);
        $business_substance = preg_match('/\b(customer|client|audience|serve|sell|offer|product|service|software|platform|tool|consult|consultation|agency|market|revenue|profit|users|members|patients|students|donors|subscribers|community|problem|solution)\b/i', $text);
        if ($website_only && !$business_substance) {
            return false;
        }

        return true;
    }

    private function has_meaningful_consultation_value($value) {
        if (is_array($value)) {
            foreach ($value as $item) {
                if ($this->has_meaningful_consultation_value($item)) {
                    return true;
                }
            }
            return false;
        }
        return $this->is_meaningful_consultation_value($value);
    }

    private function build_phase1_foundation_status(array $cd) {
        $bp = isset($cd['business_profile']) && is_array($cd['business_profile']) ? $cd['business_profile'] : array();
        $ei = isset($cd['extracted_information']) && is_array($cd['extracted_information']) ? $cd['extracted_information'] : array();
        $identity = isset($bp['identity']) && is_array($bp['identity']) ? $bp['identity'] : array();
        $audience = isset($bp['audience']) && is_array($bp['audience']) ? $bp['audience'] : array();
        $targets = isset($bp['targets']) && is_array($bp['targets']) ? $bp['targets'] : array();
        $vision = isset($bp['vision']) && is_array($bp['vision']) ? $bp['vision'] : array();
        $selected_discovery_business = $this->get_selected_discovery_business($cd);

        if ($this->is_discovery_scenario($cd) && empty($selected_discovery_business)) {
            $discovery = isset($cd['discovery_profile']) && is_array($cd['discovery_profile']) ? $cd['discovery_profile'] : array();
            $shortlist = isset($cd['business_opportunity_shortlist']) && is_array($cd['business_opportunity_shortlist']) ? $cd['business_opportunity_shortlist'] : array();
            $has_person_profile = $this->has_meaningful_consultation_value(array(
                $discovery['interests'] ?? array(),
                $discovery['skills'] ?? array(),
                $discovery['work_background'] ?? '',
                $discovery['personality_work_style'] ?? '',
            ));
            $has_constraints = $this->has_meaningful_consultation_value(array(
                $discovery['location'] ?? '',
                $discovery['time_available'] ?? '',
                $discovery['startup_budget'] ?? '',
                $discovery['income_goal'] ?? '',
                $discovery['lifestyle_constraints'] ?? array(),
            ));
            $has_shortlist = !empty($shortlist) && $this->has_meaningful_consultation_value($shortlist);
            $checks = array(
                'personal discovery profile: interests, skills, work background, or work style' => $has_person_profile,
                'practical constraints: location, income goal, time, budget, or lifestyle needs' => $has_constraints,
                'AI-generated business opportunity shortlist' => $has_shortlist,
                'selected business opportunity to build' => false,
            );
            $missing = array();
            foreach ($checks as $label => $passed) {
                if (!$passed) {
                    $missing[] = $label;
                }
            }
            return array(
                'ready' => false,
                'missing' => $missing,
                'checks' => $checks,
                'discovery_mode' => true,
            );
        }

        $naming_status = strtolower(trim((string) ($identity['naming_status'] ?? $ei['naming_status'] ?? '')));
        $has_business_name = $this->has_meaningful_consultation_value($identity['business_name'] ?? ($ei['businessName'] ?? ($ei['business_name'] ?? '')))
            || in_array($naming_status, array('needs_name', 'name_needed', 'undecided'), true);

        $has_industry_or_type = $this->has_meaningful_consultation_value(array(
            $identity['industry'] ?? '',
            $identity['business_type'] ?? '',
            $ei['industry'] ?? '',
            $ei['businessType'] ?? '',
            $ei['business_type'] ?? '',
            $ei['offeringType'] ?? '',
            $ei['offering_type'] ?? '',
        ));

        $has_offer_or_business_idea = $this->has_meaningful_consultation_value(array(
            $cd['offers_profile'] ?? array(),
            $bp['offers'] ?? array(),
            $ei['offerings'] ?? array(),
            $ei['businessIdea'] ?? '',
            $ei['business_idea'] ?? '',
            $identity['business_description'] ?? '',
            $identity['value_proposition'] ?? '',
            $bp['story']['origin'] ?? '',
        ));

        $has_audience_or_problem = $this->has_meaningful_consultation_value(array(
            $audience['primary_segments'] ?? array(),
            $audience['pain_points'] ?? array(),
            $audience['summary'] ?? '',
            $ei['targetAudience'] ?? '',
            $ei['target_audience'] ?? '',
            $ei['problemSolved'] ?? '',
            $ei['problem_solved'] ?? '',
            $ei['customerProblem'] ?? '',
            $ei['customer_problem'] ?? '',
        ));

        $has_success_vision = $this->has_meaningful_consultation_value(array(
            $targets['long_term'] ?? '',
            $targets['north_star_metric'] ?? '',
            $targets['impact_target'] ?? '',
            $targets['financial_target'] ?? '',
            $vision['mission'] ?? '',
            $vision['impact'] ?? '',
            $vision['financial'] ?? '',
            $ei['businessGoals'] ?? '',
            $ei['business_goals'] ?? '',
            $ei['businessVision'] ?? '',
            $ei['business_vision'] ?? '',
            $ei['successDefinition'] ?? '',
            $ei['success_definition'] ?? '',
        ));

        $checks = array(
            'business name or naming direction' => $has_business_name,
            'industry or business category' => $has_industry_or_type,
            'what the business offers or intends to become' => $has_offer_or_business_idea,
            'who the business serves or what problem it solves' => $has_audience_or_problem,
            'business vision or success goal beyond the website build' => $has_success_vision,
        );
        $missing = array();
        foreach ($checks as $label => $passed) {
            if (!$passed) {
                $missing[] = $label;
            }
        }

        return array(
            'ready' => empty($missing),
            'missing' => $missing,
            'checks' => $checks,
        );
    }

    private function enforce_business_foundation_gate($ai_response, array $cd, $phase_index) {
        $status = $this->build_phase1_foundation_status($cd);
        if (!empty($status['ready'])) {
            return $ai_response;
        }

        $response = (string) $ai_response;
        $had_marker = strpos($response, '[[PHASE_COMPLETE]]') !== false;
        $looks_like_research_jump = preg_match('/\b(begin|start|run|move to|proceed to)\b.{0,80}\b(deep|market|industry)?\s*research\b/i', $response);
        $looks_like_blank_blueprint_confirmation = preg_match('/\b(does this look correct|if so,? i.?ll begin|begin the deep|start the deep|move into research)\b/i', $response);

        if (!$had_marker && $phase_index === 0) {
            return $response;
        }
        if (!$had_marker && $phase_index === 1 && !$looks_like_research_jump && !$looks_like_blank_blueprint_confirmation) {
            return $response;
        }

        $response = trim(str_replace('[[PHASE_COMPLETE]]', '', $response));
        $missing = array_values(array_unique($status['missing']));
        $missing_text = '- ' . implode("\n- ", $missing);
        if (!empty($status['discovery_mode'])) {
            $question = "I am still in business discovery mode, so I cannot move into the normal setup path until we choose a real business direction to build.\n\nDiscovery still needs:\n" . $missing_text . "\n\nLet me keep this focused: I will either ask a few discovery questions now, or if I already have enough about your goals and situation, I will present realistic business ideas and help you choose one before we build the setup.";
            if ($response !== '') {
                $response .= "\n\n";
            }
            return $response . $question;
        }
        $lead = $phase_index === 1
            ? 'I cannot begin deep market research yet because the business blueprint still has open business-definition blanks.'
            : 'Before I prepare the executive blueprint, I still need the core business foundation.';

        $question = "Please answer these in your own words, or upload a business document that contains them:\n" . $missing_text . "\n\nA simple way to answer is: what does this business do, who is it for, what problem does it solve, and what would success look like if this works?";
        if ($response !== '') {
            $response .= "\n\n";
        }
        return $response . $lead . "\n\n" . $question;
    }

    /**
     * Ensure consultation phases are initialized in consultation_data for a session
     */


    /**
     * Advance to next consultation phase and persist
     */
    private function advance_consultation_phase($session_id, $force = false) {
        if (!$session_id) return false;
        $row = $this->consultation_db->get_consultation_by_session($session_id);
        if (!$row) return false;
        $cd = json_decode($row->consultation_data ?: '[]', true);
        if (!isset($cd['consultation_flow'])) return false;
        $cf = $cd['consultation_flow'];
        $idx = isset($cf['current_phase_index']) ? intval($cf['current_phase_index']) : 0;
        $phases = isset($cf['phases']) && is_array($cf['phases']) ? $cf['phases'] : [];
        if (!isset($phases[$idx])) return false;

        // Email routing belongs to Phase 6 systems/compliance. Do not block blueprint,
        // research, recommendations, or website architecture on mailbox setup.
        if (!$force && $idx >= 5 && $this->needs_email_roles_confirmation($cd)) {
            // ✅ Try to auto-populate defaults before blocking
            $missing = $this->get_missing_email_roles($cd);
            $domain = $this->get_enforced_domain();
            if (!empty($missing) && $domain) {
                $defaults = array(
                    'general' => 'info@' . $domain,
                    'support' => 'support@' . $domain,
                    'transactions' => 'billing@' . $domain,
                    'marketing' => 'marketing@' . $domain,
                    'customercare' => 'customercare@' . $domain,
                    'bizdev' => 'partnerships@' . $domain,
                    'newsletter' => 'newsletter@' . $domain
                );
                if (!isset($cd['email_roles']) || !is_array($cd['email_roles'])) {
                    $cd['email_roles'] = array();
                }
                foreach ($missing as $role) {
                    if (isset($defaults[$role])) {
                        $cd['email_roles'][$role] = $defaults[$role];
                    }
                }
                error_log("VMO: Auto-populated email defaults during advance attempt for session " . $session_id);
            }
            $this->persist_consultation_state($session_id, $cd);
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log('VMO: Blocking phase advance - email roles not explicitly confirmed for session ' . $session_id);
            }
            return false;
        }
        if (!$force && $idx >= 7 && !$this->is_consultation_release_ready($cd, $session_id)) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log('VMO: Blocking final phase completion - preview/media/deliverables are not release-ready for session ' . $session_id);
            }
            return false;
        }
        if (!$force && !$this->is_phase_ready_for_completion($cd, $idx)) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log('VMO: Gating prevented phase advance for session ' . $session_id . ' at index ' . $idx);
            }
            return false;
        }
        $previous_idx = $idx;
        $phases[$idx]['status'] = 'completed';
        $phases[$idx]['completed_at'] = current_time('mysql');
        $next_idx = $idx;
        if (isset($phases[$idx + 1])) {
            $next_idx = $idx + 1;
            $phases[$next_idx]['status'] = 'in_progress';
            if (empty($phases[$next_idx]['started_at'])) {
                $phases[$next_idx]['started_at'] = current_time('mysql');
            }
            $cf['current_phase_index'] = $next_idx;
        } else {
            $cf['status'] = 'completed';
        }
        $cf['phases'] = $phases;
        $cd['consultation_flow'] = $cf;
        $this->persist_consultation_state($session_id, $cd);
        $this->handle_phase_transition_effects($session_id, $cd, $previous_idx, $cf['current_phase_index'] ?? $previous_idx);
        return true;
    }

    private function is_phase_ready_for_completion(array $cd, $phase_index) {
        $bp = $cd['business_profile'] ?? array();
        // ✅ FIX: Check if user has confirmed this phase — if so, allow advancement.
        // This prevents permanent blocking if auto-generated data (research, artifacts) failed to populate.
        $user_approved_current = false;
        switch ($phase_index) {
            case 3:
                $user_approved_current = !empty($cd['approvals']['recommendations']['accepted']);
                break;
            case 4:
                $user_approved_current = !empty($cd['approvals']['phase5_discussed']);
                break;
            case 5:
                $user_approved_current = !empty($cd['approvals']['phase6_discussed']);
                break;
        }
        switch ($phase_index) {
            case 0: // Foundations Intake
                $foundation_status = $this->build_phase1_foundation_status($cd);
                return !empty($foundation_status['ready']);
            case 1: // Blueprint confirmation
                $foundation_status = $this->build_phase1_foundation_status($cd);
                return !empty($foundation_status['ready']) && !empty($cd['approvals']['phase1_profile_confirmed']);
            case 2: // Research scan — allow if research populated OR user has confirmed the research
                return !empty($cd['research_profile']);
            case 3: // Recommendations — user must accept
                return $user_approved_current;
            case 4: // Website plan — plan exists or user discussed
                $pages = $cd['website_plan']['pages'] ?? array();
                return !empty($pages) || $user_approved_current;
            case 5: // Systems plan — plan exists or user discussed
                $catalog_status = $this->build_product_catalog_status($cd);
                if (!empty($catalog_status['question_required'])) {
                    return false;
                }
                return !empty($cd['systems_plan']) || $user_approved_current;
            case 6: // Roadmap — roadmap and daily execution outputs must exist
                return !empty($cd['roadmap_json']) || !empty($cd['daily_tasks_json']);
            case 7: // Launch
                return !empty($cd['approvals']['artifacts_generated']);
            default:
                return true;
        }
    }

    private function handle_phase_transition_effects($session_id, array $cd, $previous_idx, $current_idx) {
        $phases = $cd['consultation_flow']['phases'] ?? array();
        if (!isset($phases[$current_idx])) {
            return;
        }
        
        // Save structured phase outputs when completing each phase
        try {
            $this->save_phase_structured_outputs($session_id, $cd, $previous_idx);
        } catch (\Throwable $e) {
            error_log('VMO: save_phase_structured_outputs failed (non-fatal): ' . $e->getMessage());
        }
        
        $current_key = $phases[$current_idx]['key'] ?? '';

        // ✅ FIX: Use both key-based AND index-based matching for backward compatibility
        // Existing sessions may not have 'key' fields in their phases.
        $is_entering_phase3 = ($current_key === 'phase_3_research' || $current_idx === 2);
        $is_entering_phase7 = ($current_key === 'phase_7_roadmap' || $current_idx === 6);
        $is_entering_phase8 = ($current_key === 'phase_8_launch' || $current_idx === 7);

        // ✅ FIX: Wrap research profile generation in try/catch to prevent fatal crashes
        // during SSE streaming post-processing. If it fails, Phase 3 will still start
        // and the research can be triggered later by the frontend.
        if ($is_entering_phase3 && empty($cd['research_profile'])) {
            try {
                $this->generate_research_profile($session_id, $cd);
            } catch (\Throwable $e) {
                error_log('VMO: generate_research_profile failed (non-fatal): ' . $e->getMessage());
            }
        }
        if ($is_entering_phase7) {
            $needs_artifacts = empty($cd['roadmap_json']) || empty($cd['daily_tasks_json']) || empty($cd['systems_plan']);
            if ($needs_artifacts) {
                try {
                    $this->generate_phase7_artifacts($session_id, $cd);
                } catch (\Throwable $e) {
                    error_log('VMO: generate_phase7_artifacts failed (non-fatal): ' . $e->getMessage());
                }
            }
        }

        if ($is_entering_phase8) {
            try {
                $this->populate_phase8_deliverables(get_current_user_id(), $session_id, $cd);
            } catch (\Throwable $e) {
                error_log('VMO: populate_phase8_deliverables failed on phase entry (non-fatal): ' . $e->getMessage());
            }
        }
        
        // After Phase 4 approval, generate the business roadmap and daily activities
        if ($previous_idx === 3 && $current_idx === 4) {
            try {
                $this->generate_business_roadmap_from_plan($session_id, $cd);
            } catch (\Throwable $e) {
                error_log('VMO: generate_business_roadmap_from_plan failed (non-fatal): ' . $e->getMessage());
            }
        }
    }
    
    /**
     * Save structured phase outputs to dedicated tables
     * This captures vision_profile, market_inputs, approved_execution_plan, etc.
     */
    private function save_phase_structured_outputs($session_id, array $cd, $phase_index) {
        if (!class_exists('VMO_Phase_Output_Manager')) {
            return;
        }
        
        $output_manager = VMO_Phase_Output_Manager::get_instance();
        $user_id = get_current_user_id();
        
        switch ($phase_index) {
            case 0: // Phase 1 complete - save vision_profile and business_structure
                $bp = $cd['business_profile'] ?? [];
                $identity = $bp['identity'] ?? [];
                $audience = $bp['audience'] ?? [];
                
                // vision_profile
                $vision_profile = [
                    'business_goal' => $identity['business_description'] ?? '',
                    'success_definition' => $cd['goals']['long_term'] ?? '',
                    'income_intent' => 'scale_long_term',
                    'involvement_level' => 'active',
                    'timeline' => '12_months',
                    'automation_tolerance' => 'high',
                    'risk_tolerance' => 'moderate'
                ];
                $output_manager->save_phase_output($user_id, $session_id, 1, 'vision_profile', $vision_profile);
                
                // business_structure
                $business_structure = [
                    'business_type' => $identity['business_type'] ?? 'service',
                    'operating_model' => 'online',
                    'geography' => $audience['geographic_focus'] ?? [],
                    'naming_status' => !empty($identity['business_name']) ? 'has_name' : 'needs_name',
                    'business_name' => $identity['business_name'] ?? '',
                    'constraints' => []
                ];
                $output_manager->save_phase_output($user_id, $session_id, 1, 'business_structure', $business_structure);
                break;
                
            case 1: // Phase 2 complete - save confirmed_business_blueprint
                if (!empty($cd['approvals']['phase1_profile_confirmed'])) {
                    $output_manager->create_confirmed_blueprint($session_id, $user_id);
                }
                break;
                
            case 2: // Phase 3 complete - save market_inputs and market_research
                $research = $cd['research_profile'] ?? [];
                
                // market_inputs (from user)
                $market_inputs = [
                    'customer_description' => $cd['business_profile']['audience']['primary_segments'] ?? [],
                    'perceived_competitors' => [],
                    'perceived_value' => $cd['business_profile']['identity']['value_proposition'] ?? '',
                    'geography_focus' => $cd['business_profile']['audience']['geographic_focus'] ?? []
                ];
                $output_manager->save_phase_output($user_id, $session_id, 3, 'market_inputs', $market_inputs);
                
                // market_research (from AI)
                if (!empty($research)) {
                    $market_research = [
                        'competitors' => $research['competitors'] ?? [],
                        'gaps' => $research['market_opportunities'] ?? [],
                        'pricing_norms' => $research['pricing_strategy'] ?? [],
                        'seo_opportunities' => $research['seo_keywords'] ?? [],
                        'legal_flags' => [],
                        'market_size' => 'unknown',
                        'saturation_level' => 'moderate'
                    ];
                    $output_manager->save_phase_output($user_id, $session_id, 3, 'market_research', $market_research);
                }

                // competitor_analysis (benchmarking output used to justify Phase 4 plan)
                if (!empty($research)) {
                    $competitors_raw = $research['competitors'] ?? [];
                    if (!is_array($competitors_raw)) {
                        $competitors_raw = [];
                    }
                    $competitors = array_map(function ($comp) {
                        if (is_string($comp)) {
                            return [
                                'name' => $comp,
                                'type' => 'direct'
                            ];
                        }
                        if (!is_array($comp)) {
                            return [
                                'name' => '',
                                'type' => 'direct'
                            ];
                        }
                        return [
                            'name' => $comp['name'] ?? ($comp['competitor'] ?? ''),
                            'website' => $comp['website'] ?? ($comp['url'] ?? ''),
                            'type' => $comp['type'] ?? 'direct',
                            'strengths' => $comp['strengths'] ?? [],
                            'weaknesses' => $comp['weaknesses'] ?? [],
                            'pricing_model' => $comp['pricing_model'] ?? ($comp['pricing'] ?? ''),
                            'positioning' => $comp['positioning'] ?? '',
                            'seo_summary' => $comp['seo_summary'] ?? ($comp['seo'] ?? ''),
                            'ux_score' => isset($comp['ux_score']) ? (int) $comp['ux_score'] : 0,
                            'trust_signals' => $comp['trust_signals'] ?? []
                        ];
                    }, $competitors_raw);

                    $analysis_data = [
                        'competitors' => $competitors,
                        'market_gaps' => $research['market_gaps'] ?? ($research['market_opportunities'] ?? []),
                        'opportunities' => $research['opportunities'] ?? ($research['market_opportunities'] ?? []),
                        'threats' => $research['threats'] ?? ($research['risks'] ?? []),
                        'key_differences' => $research['key_differences'] ?? ($research['differentiators'] ?? []),
                        'chosen_advantages' => $research['chosen_advantages'] ?? ($research['advantages'] ?? []),
                        'risks_avoided' => $research['risks_avoided'] ?? []
                    ];

                    if (method_exists($output_manager, 'save_competitor_analysis')) {
                        $output_manager->save_competitor_analysis($session_id, $user_id, $analysis_data);
                    } else {
                        $output_manager->save_phase_output($user_id, $session_id, 3, 'competitor_analysis', $analysis_data);
                    }
                }
                break;
                
            case 3: // Phase 4 complete - save approved_execution_plan
                if (!empty($cd['approvals']['recommendations']['accepted'])) {
                    $recs = $cd['recommendations'] ?? [];
                    $execution_plan = [
                        'approved' => true,
                        'version' => '1.0',
                        'website_structure' => $cd['website_plan']['pages'] ?? [],
                        'seo_strategy' => $recs['seo'] ?? [],
                        'monetization' => $recs['payments'] ?? [],
                        'automation_scope' => $recs['automation'] ?? [],
                        'compliance_plan' => $recs['legal'] ?? [],
                        'phone_system' => ['enabled' => !empty($recs['phone'])],
                        'email_system' => ['enabled' => true],
                        'ecommerce' => ['enabled' => !empty($recs['ecommerce'])],
                        'crm' => ['enabled' => true],
                        'analytics' => ['enabled' => true]
                    ];
                    $output_manager->save_phase_output($user_id, $session_id, 4, 'approved_execution_plan', $execution_plan, true);
                }
                break;
        }
    }
    
    /**
     * Generate business roadmap and daily activities after Phase 4 approval
     */
    private function generate_business_roadmap_from_plan($session_id, array $cd) {
        if (!class_exists('VMO_Phase_Output_Manager')) {
            return;
        }
        
        $output_manager = VMO_Phase_Output_Manager::get_instance();
        $user_id = get_current_user_id();
        
        // Generate the full roadmap
        $output_manager->generate_business_roadmap($session_id, $user_id);
        
        error_log("VMO: Generated business roadmap and daily activities for session {$session_id}");
    }

    /**
     * AJAX: Analyze uploaded media or documents hosted on this WordPress site.
     * Attempts to extract text for text/pdf/docx and summarizes content for consultation.
     */
    public function ajax_analyze_uploaded_media() {
        $this->prepare_long_running_runtime();

        // ✅ CRITICAL: Set up custom error handler to catch PHP warnings/errors as exceptions
        set_error_handler(function($severity, $message, $file, $line) {
            throw new \ErrorException($message, 0, $severity, $file, $line);
        });
        $buffer_level = ob_get_level();
        ob_start();

        $drain_output = function($label = 'document_analysis') use ($buffer_level) {
            $stray_output = '';
            while (ob_get_level() > $buffer_level) {
                $chunk = ob_get_clean();
                if (is_string($chunk) && trim($chunk) !== '') {
                    $stray_output .= $chunk;
                }
            }
            if ($stray_output !== '') {
                error_log('VMO Document Analysis stray output [' . $label . ']: ' . substr(trim($stray_output), 0, 1200));
            }
        };
        $send_success = function(array $payload, string $label = 'success') use ($drain_output) {
            $drain_output($label);
            restore_error_handler();
            wp_send_json_success($payload);
        };
        $send_error = function(array $payload, int $status = 400, string $label = 'error') use ($drain_output) {
            $drain_output($label);
            restore_error_handler();
            wp_send_json_error($payload, $status);
        };
        
        // ✅ CRITICAL: Wrap entire handler in try/catch for PHP 8 type safety
        try {
            // Nonce: accept builder nonce (security) or admin/ajax nonce (nonce/_ajax_nonce)
            $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
            $admin_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
            $ajax_nonce = isset($_POST['_ajax_nonce']) ? sanitize_text_field(wp_unslash($_POST['_ajax_nonce'])) : '';

            $builder_ok = $builder_nonce && wp_verify_nonce($builder_nonce, 'vmo_builder_nonce');
            $admin_ok = $admin_nonce && wp_verify_nonce($admin_nonce, 'vmo_admin_nonce');
            $ajax_ok = ($admin_nonce && wp_verify_nonce($admin_nonce, 'vmo_ajax_nonce'))
                || ($ajax_nonce && wp_verify_nonce($ajax_nonce, 'vmo_ajax_nonce'));

            if (!($builder_ok || $admin_ok || $ajax_ok)) {
                // IMPORTANT: never wp_die() here (HTML breaks JSON parsing in the SPA)
                $send_error([
                    'message' => 'Security check failed',
                    'status' => 'invalid_nonce'
                ], 403, 'invalid_nonce');
            }

            $user_id = get_current_user_id();

            // If logged in, require upload capability for analysis operations.
            if (is_user_logged_in() && !current_user_can('upload_files')) {
                $send_error([
                    'message' => 'Permission denied.',
                    'status' => 'forbidden'
                ], 403, 'permission_denied');
            }
            $url = esc_url_raw($_POST['url'] ?? '');
            $title = sanitize_text_field($_POST['title'] ?? 'Uploaded file');
            $type = sanitize_text_field($_POST['type'] ?? '');
            $session_id = sanitize_text_field($_POST['session_id'] ?? '');
            if (empty($url)) {
                $send_error(['message' => 'No URL provided'], 400, 'missing_url');
            }
            // Validate URL belongs to this site uploads directory
            $uploads = wp_get_upload_dir();
            $baseurl = rtrim($uploads['baseurl'] ?? '', '/');
            $basedir = rtrim($uploads['basedir'] ?? '', '/');
            if (!$baseurl || !$basedir || strpos($url, $baseurl) !== 0) {
                $send_error(['message' => 'Invalid media URL'], 400, 'invalid_media_url');
            }
            // Map URL to local path
            $relative = ltrim(substr($url, strlen($baseurl)), '/');
            $path = $basedir . '/' . $relative;
            if (!file_exists($path) || !is_readable($path)) {
                $send_error(['message' => 'File not readable'], 400, 'file_not_readable');
            }
            // Determine mime/type
            $wpft = wp_check_filetype($path);
            $mime = $wpft['type'] ?? mime_content_type($path);

            // ✅ TOKEN DEDUCTION v8.1.0: Charge based on actual file type
            // - Images: vision
            // - Audio/video: transcription
            // - Documents (txt/pdf/doc/docx): document
            // - Other: document (text-based summarization/ack)
            if ($user_id && function_exists('vmo_deduct_tokens_for_operation')) {
                $operation = 'document';
                $insufficient_message = 'Insufficient tokens for document analysis. Please top up your balance.';

                if (strpos($mime, 'image/') === 0) {
                    $operation = 'vision';
                    $insufficient_message = 'Insufficient tokens for image analysis. Please top up your balance.';
                } elseif (strpos($mime, 'audio/') === 0 || strpos($mime, 'video/') === 0) {
                    $operation = 'transcription';
                    $insufficient_message = 'Insufficient tokens for media analysis. Please top up your balance.';
                } elseif (
                    strpos($mime, 'text/') === 0
                    || stripos($mime, 'application/pdf') !== false
                    || stripos($mime, 'application/msword') !== false
                    || stripos($mime, 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') !== false
                    || preg_match('/\.(txt|md|markdown|csv|json|pdf|doc|docx)$/i', $path)
                ) {
                    $operation = 'document';
                    $insufficient_message = 'Insufficient tokens for document analysis. Please top up your balance.';
                }

                if (!vmo_deduct_tokens_for_operation($user_id, $operation)) {
                    $send_error([
                        'message' => $insufficient_message,
                        'status' => 'insufficient_tokens',
                        'operation' => $operation
                    ], 400, 'insufficient_tokens');
                    return;
                }
            }
            $content = '';
            if (strpos($mime, 'image/') === 0) {
                // Deep image analysis via OpenAI Vision
                $vision_instruction = "You are analyzing a user-provided image for their business website.
Describe the image concisely, infer relevant business details, and extract any answers to typical consultation questions visible or implied by the image (e.g., product type, style, branding hints). Then suggest best website placements (hero, gallery, product, background) and list only missing clarifications if truly needed.";
                $ai = $this->call_openai_vision($vision_instruction, $url, 500);
                if (!$ai) { $ai = 'Received your image and will integrate it into the website (e.g., hero, gallery, or product visuals).'; }
                // Persist media reference
                if ($session_id) { $this->persist_media_file($session_id, [ 'url' => $url, 'title' => $title, 'type' => $type, 'mime' => $mime ]); }
                // Auto-assign a sensible default role based on AI description
                if ($session_id) { $this->auto_assign_media_role($session_id, [ 'url' => $url, 'title' => $title, 'type' => $type, 'mime' => $mime ], $ai); }
                // Attempt to extract and save structured info from the AI description
                if ($session_id && $ai) { $this->extract_and_save_from_text($session_id, $title, $ai); }
                if ($session_id) { $this->save_consultation_message($session_id, 'ai', $ai); }
                $send_success(['response' => $ai], 'image_success');
            } elseif (strpos($mime, 'audio/') === 0) {
                // Transcribe audio using OpenAI Whisper (existing method expects raw bytes)
                $bytes = @vmo_get_filesystem()->get_contents($path);
                $text = $bytes ? $this->transcribe_audio_with_openai($bytes) : '';
                $summary = $text ? $this->call_openai_api("Summarize this audio (from a user upload) and extract any business/website details relevant to consultation. List only missing items as a short checklist.

---
{$text}", 400) : 'Audio received and will be used where appropriate (e.g., background audio, media section).';
                // Persist media reference
                if ($session_id) { $this->persist_media_file($session_id, [ 'url' => $url, 'title' => $title, 'type' => $type, 'mime' => $mime ]); }
                if ($session_id) { $this->auto_assign_media_role($session_id, [ 'url' => $url, 'title' => $title, 'type' => $type, 'mime' => $mime ], $summary ?: $text); }
                // Save extracted answers from transcript (prefer raw transcript over summary)
                if ($session_id && ($text || $summary)) { $this->extract_and_save_from_text($session_id, $title, $text ?: $summary); }
                // Store transcript as a document for future retrieval
                if ($session_id && $text) { $this->save_document_with_chunks($session_id, [ 'title' => $title, 'mime' => $mime, 'url' => $url, 'source' => 'audio_transcript', 'content' => $text ]); }
                if ($session_id) { $this->save_consultation_message($session_id, 'ai', $summary); }
                $send_success(['response' => $summary], 'audio_success');
            } elseif (
                stripos($mime, 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') !== false
                || preg_match('/\.docx$/i', $path)
            ) {
                // ✅ DOCX files - extract text from ZIP structure (Office Open XML)
                error_log("VMO Document: Attempting DOCX extraction for {$path} (mime: {$mime})");
                $content = $this->extract_text_from_docx($path);
                error_log("VMO Document: DOCX extraction returned " . strlen($content) . " chars");
                
                // If ZipArchive failed, try alternative extraction
                if (empty(trim($content))) {
                    error_log("VMO Document: DOCX extraction failed, trying fallback methods");
                    $content = $this->extract_text_from_docx_fallback($path);
                    error_log("VMO Document: DOCX fallback extraction returned " . strlen($content) . " chars");
                }
            } elseif (strpos($mime, 'video/') === 0) {
                // Video files - accept and suggest uses (no ffmpeg in WP.org environments)
                $video_msg = 'Received your video and will integrate it (e.g., background video, header slider, or media section). For detailed content extraction, please upload a transcript.';
                if ($session_id) { $this->persist_media_file($session_id, [ 'url' => $url, 'title' => $title, 'type' => $type, 'mime' => $mime ]); }
                if ($session_id) { $this->auto_assign_media_role($session_id, [ 'url' => $url, 'title' => $title, 'type' => $type, 'mime' => $mime ], $video_msg); }
                if ($session_id) { $this->save_consultation_message($session_id, 'ai', $video_msg); }
                $send_success(['response' => $video_msg], 'video_success');
            } elseif (preg_match('/\.(doc)$/i', $path) || stripos($mime, 'application/msword') !== false) {
                // Old .doc format - try basic extraction
                error_log("VMO Document: Attempting legacy DOC extraction for {$path}");
                $content = $this->extract_text_from_doc_legacy($path);
            } elseif (stripos($mime, 'application/pdf') !== false || preg_match('/\.(pdf)$/i', $path)) {
                // PDF files - try multiple extraction methods
                error_log("VMO Document: Attempting PDF extraction for {$path}");
                $content = $this->extract_text_from_pdf_naive($path);
                if (trim($content) === '') {
                    // Try OCR fallback if available
                    $content = $this->extract_text_from_pdf_with_ocr($path);
                }
                if (trim($content) === '') {
                    // Try pdftotext command if available
                    $content = $this->extract_text_from_pdf_pdftotext($path);
                }
                error_log("VMO Document: PDF extraction returned " . strlen($content) . " chars");
            } else {
                // Other types: provide a structured acknowledgement
                $desc = 'Received ' . ($type ?: $mime ?: 'media') . ' titled "' . $title . '".';
                $ai = $this->call_openai_api("{$desc}

Treat this as a provided asset for the business website. Do NOT say you cannot open links; it is already stored in the system.
Explain how you'll use it effectively on the website (hero, gallery, product, background), and list only essential clarifications if any.", 300);
                // Persist media reference
                if ($session_id) { $this->persist_media_file($session_id, [ 'url' => $url, 'title' => $title, 'type' => $type, 'mime' => $mime ]); }
                if ($session_id) { $this->auto_assign_media_role($session_id, [ 'url' => $url, 'title' => $title, 'type' => $type, 'mime' => $mime ], $ai); }
                // Best-effort extraction from acknowledgment text
                if ($session_id && $ai) { $this->extract_and_save_from_text($session_id, $title, $ai); }
                if ($session_id) { $this->save_consultation_message($session_id, 'ai', $ai); }
                $send_success(['response' => $ai], 'generic_media_success');
            }
        $content = trim((string)$content);
        if (empty($content)) {
            error_log("VMO Document: Content extraction failed completely for {$path} (mime: {$mime})");
            $fallback = 'I received your document but had trouble reading its contents. This can happen with certain file formats. Would you mind copying and pasting the text content directly into the chat, or try uploading a .txt version?';
            if ($session_id) { $this->save_consultation_message($session_id, 'ai', $fallback); }
            $send_success(['response' => $fallback], 'empty_content_fallback');
        }
        // ✅ GPT-5.5 UPGRADE: Raised from 18K to 200K to support 100+ page documents
        $max_chars = 200000;
        if (strlen($content) > $max_chars) {
            $content = substr($content, 0, $max_chars) . "

[Document truncated at 200K chars]";
        }

        $analysis = $this->run_document_blueprint_analysis($title, $content);
        
        // ✅ CRITICAL: Ensure $analysis is an array with expected keys
        if (!is_array($analysis)) {
            error_log('VMO Document: run_document_blueprint_analysis returned non-array: ' . gettype($analysis));
            $analysis = ['extractedInfo' => [], 'rawResponse' => ''];
        }
        
        // ✅ ENHANCED LOGGING: Log exact type and structure of $analysis
        error_log('VMO Document Analysis Type Check: analysis is ' . gettype($analysis) . ', keys: ' . implode(', ', array_keys($analysis)));
        
        $extracted_info = $analysis['extractedInfo'] ?? [];
        
        // ✅ CRITICAL: Ensure $extracted_info is an array
        if (!is_array($extracted_info)) {
            error_log('VMO Document: extractedInfo is not an array: ' . gettype($extracted_info) . ' Value: ' . var_export($extracted_info, true));
            $extracted_info = [];
        }
        
        // ✅ ENHANCED LOGGING: Log $extracted_info structure
        error_log('VMO Document Extracted Info: is_array=' . (is_array($extracted_info) ? 'yes' : 'no') . ', count=' . count($extracted_info));
        
        $friendly_message = $this->format_document_summary_message($extracted_info);

        // Pre-build lightweight chunk info for UI sync
        $doc_id = 'doc_' . wp_generate_password(10, false);
        $local_chunks = [];
        $maxChunk = 1200; $remaining = $content;
        while ($remaining !== '' && count($local_chunks) < 10) {
            if (strlen($remaining) <= $maxChunk) { $local_chunks[] = $remaining; break; }
            $slice = substr($remaining, 0, $maxChunk);
            // ✅ FIX: Avoid max(false, false) which can trigger deprecation notices in PHP 8.1+
            $bp1 = strrpos($slice, "\n\n");
            $bp2 = strrpos($slice, '. ');
            if ($bp1 === false && $bp2 === false) {
                $breakPos = $maxChunk;
            } elseif ($bp1 === false) {
                $breakPos = $bp2;
            } elseif ($bp2 === false) {
                $breakPos = $bp1;
            } else {
                $breakPos = max($bp1, $bp2);
            }
            if ($breakPos <= 0) { $breakPos = $maxChunk; }
            $local_chunks[] = substr($remaining, 0, $breakPos);
            $remaining = ltrim(substr($remaining, $breakPos));
        }

        if ($session_id) {
            $this->persist_media_file($session_id, [ 'url' => $url, 'title' => $title, 'type' => $type, 'mime' => $mime ]);
            $this->auto_assign_media_role($session_id, [ 'url' => $url, 'title' => $title, 'type' => $type, 'mime' => $mime ], $friendly_message);
            $this->save_extracted_information($session_id, $extracted_info);
            $this->save_document_with_chunks($session_id, [ 'id' => $doc_id, 'title' => $title, 'mime' => $mime, 'url' => $url, 'source' => 'document', 'content' => $content ]);

            list($existing_row, $cd) = $this->hydrate_consultation_state($session_id);
            
            // ✅ CRITICAL: Ensure $cd is an array and document_store is properly initialized
            if (!is_array($cd)) {
                error_log('VMO Document: hydrate_consultation_state returned non-array cd: ' . gettype($cd));
                $cd = $this->consultation_blueprint;
            }
            if (!isset($cd['document_store']) || !is_array($cd['document_store'])) {
                $cd['document_store'] = $this->consultation_blueprint['document_store'] ?? ['blueprint_raw' => '', 'blueprint_summary' => '', 'uploads' => []];
            }
            
            $cd['blueprintRaw'] = $content;
            $cd['document_store']['blueprint_raw'] = $content;
            if (!empty($extracted_info['conciseSummary'])) {
                $cd['blueprintSummary'] = $extracted_info['conciseSummary'];
                $cd['document_store']['blueprint_summary'] = $extracted_info['conciseSummary'];
            }
            // ✅ CRITICAL: Ensure business_profile is always an array before delta extraction
            $bp_for_delta = $cd['business_profile'] ?? array();
            if (!is_array($bp_for_delta)) {
                error_log('VMO Document: business_profile is not an array: ' . gettype($bp_for_delta));
                $bp_for_delta = array();
            }
            
            // ✅ FIX: Wrap delta extraction in its own try/catch for error isolation
            try {
                $delta = $this->extract_consultation_delta(
                    $bp_for_delta,
                    $content,
                    '',
                    array('phase' => 'Document Blueprint Upload')
                );
            } catch (\Throwable $delta_err) {
                error_log('VMO Document: extract_consultation_delta failed: ' . $delta_err->getMessage() . ' at ' . $delta_err->getFile() . ':' . $delta_err->getLine());
                $delta = array(
                    'business_profile_delta' => array(),
                    'offers_profile_delta' => array(),
                    'payments_profile_delta' => array(),
                    'email_roles_meta_delta' => array(),
                    'extracted_information_delta' => array(),
                    'system_notes' => array('delta_extraction_failed')
                );
            }
            
            // ✅ ENHANCED LOGGING: Log delta structure
            error_log('VMO Document Delta Check: delta is_array=' . (is_array($delta) ? 'yes' : 'no') . ', type=' . gettype($delta));
            if (is_array($delta)) {
                foreach (['business_profile_delta', 'offers_profile_delta', 'payments_profile_delta'] as $_dk) {
                    if (isset($delta[$_dk])) {
                        $_dt = gettype($delta[$_dk]);
                        $_da = is_array($delta[$_dk]) ? 'yes' : 'no';
                        error_log("VMO Document Delta Key '{$_dk}': type={$_dt}, is_array={$_da}");
                    }
                }
            }
            
            // ✅ CRITICAL: Ensure delta keys are arrays before merging
            if (!empty($delta['business_profile_delta'])) {
                if (!is_array($delta['business_profile_delta'])) {
                    error_log('VMO Document: business_profile_delta is not an array: ' . gettype($delta['business_profile_delta']));
                    $delta['business_profile_delta'] = [];
                }
                if (!empty($delta['business_profile_delta'])) {
                    $cd['business_profile'] = $this->merge_profile_delta($cd['business_profile'] ?? array(), $delta['business_profile_delta']);
                }
            }
            if (!empty($delta['offers_profile_delta'])) {
                if (!is_array($delta['offers_profile_delta'])) {
                    error_log('VMO Document: offers_profile_delta is not an array: ' . gettype($delta['offers_profile_delta']));
                    $delta['offers_profile_delta'] = [];
                }
                if (!empty($delta['offers_profile_delta'])) {
                    $cd['offers_profile'] = array_values(array_merge($cd['offers_profile'] ?? array(), $delta['offers_profile_delta']));
                }
            }
            if (!empty($delta['payments_profile_delta'])) {
                if (!is_array($delta['payments_profile_delta'])) {
                    error_log('VMO Document: payments_profile_delta is not an array: ' . gettype($delta['payments_profile_delta']));
                    $delta['payments_profile_delta'] = [];
                }
                if (!empty($delta['payments_profile_delta'])) {
                    $cd['payments_profile'] = $this->merge_profile_delta($cd['payments_profile'] ?? array(), $delta['payments_profile_delta']);
                }
            }
            if (!empty($delta['discovery_profile_delta']) && is_array($delta['discovery_profile_delta'])) {
                $discovery_delta = $this->filter_empty_discovery_delta($delta['discovery_profile_delta']);
                $cd['discovery_profile'] = $this->merge_profile_delta($cd['discovery_profile'] ?? array(), is_array($discovery_delta) ? $discovery_delta : array());
                $cd['discovery_profile']['updated_at'] = current_time('mysql');
            }
            if (!empty($delta['business_opportunity_shortlist_delta']) && is_array($delta['business_opportunity_shortlist_delta'])) {
                $cd['business_opportunity_shortlist'] = $this->merge_business_opportunity_shortlist($cd['business_opportunity_shortlist'] ?? array(), $delta['business_opportunity_shortlist_delta']);
            }
            if (!empty($delta['extracted_information_delta']) && is_array($delta['extracted_information_delta'])) {
                $cd['extracted_information'] = $this->merge_profile_delta($cd['extracted_information'] ?? array(), $delta['extracted_information_delta']);
            }

            // Persist a structured record of the uploaded document + chunks for future retrieval
            $this->upsert_document_store_upload($cd, [
                'id' => $doc_id,
                'title' => $title,
                'url' => $url,
                'mime' => $mime,
                'length' => strlen($content),
                'saved_at' => current_time('mysql'),
                'chunks' => array_map(function($t,$i){ return [ 'id' => 'c'.($i+1), 'text' => $t ]; }, $local_chunks, array_keys($local_chunks))
            ]);

            $cd = $this->reconcile_intake_memory($cd, '');
            $this->persist_consultation_state($session_id, $cd);
            
            $this->save_consultation_message($session_id, 'ai', $friendly_message);
        }

        $payload = [
            'response' => $friendly_message,
            'summary' => $extracted_info['conciseSummary'] ?? '',
            'extractedInfo' => $extracted_info,
            'documentRecord' => [
                'id' => $doc_id,
                'title' => $title,
                'url' => $url,
                'mime' => $mime,
                'length' => strlen($content),
                'chunks' => array_map(function($t,$i){ return [ 'id' => 'c'.($i+1), 'text' => $t ]; }, $local_chunks, array_keys($local_chunks))
            ],
            'blueprintRaw' => $content,
            'blueprintSummary' => $extracted_info['conciseSummary'] ?? '',
            'missingItems' => $extracted_info['missingItems'] ?? [],
            'isComplete' => $extracted_info['isComplete'] ?? false,
            'structuredSummaryReady' => true
        ];

        $send_success($payload, 'document_success');
        } catch (Throwable $e) {
            if (isset($drain_output) && is_callable($drain_output)) {
                $drain_output('fatal_exception');
            }
            restore_error_handler();
            // ✅ CRITICAL: Detailed error logging for PHP 8 type errors
            error_log('VMO Document Analysis Failure: ' . $e->getMessage());
            error_log('VMO Document Analysis Stack: ' . $e->getTraceAsString());
            error_log('VMO Document Analysis File: ' . $e->getFile() . ':' . $e->getLine());
            wp_send_json_error([
                'message' => 'Document analysis failed: ' . $e->getMessage(),
                'file' => isset($path) ? basename($path) : null,
                'debug_line' => $e->getLine(),
                'debug_file' => basename($e->getFile())
            ], 500);
        }
    }

    /**
     * Run LLM extraction on uploaded blueprint text and return structured data + friendly summary.
     */
    private function run_document_blueprint_analysis($title, $text) {
        $analysis_prompt = "You are analyzing a business plan/blueprint document uploaded by a user. Your task is to:\n\n1. **Extract ALL business information** and return it in JSON format (see structure below)\n2. **Create a friendly, conversational summary** to acknowledge receipt of the document\n\nEXTRACTION JSON STRUCTURE (this is for backend processing):\n{\n  \"businessName\": \"[Extract business name or 'Not specified']\",\n  \"industry\": \"[Extract industry/niche or 'Not specified']\",\n  \"businessStage\": \"[New Business or Existing Business]\",\n  \"location\": \"[City, State, Country or 'Not specified']\",\n  \"offerings\": [\"Product/Service 1\", \"Product/Service 2\"],\n  \"targetAudience\": \"[Describe target customers in plain language]\",\n  \"businessGoals\": \"[Short/mid/long-term goals extracted]\",\n  \"businessVision\": \"[Long-term vision statement or 'Not specified']\",\n  \"visionTimeframe\": \"[e.g. '3 years' or 'Not specified']\",\n  \"pricing\": \"[Pricing details or 'Not specified']\",\n  \"websitePreferences\": \"[Any UI/UX preferences mentioned or 'Not specified']\",\n  \"ecommerce_needed\": true/false,\n  \"customPages\": [\"page-name-1\", \"page-name-2\"],\n  \"pageDescriptions\": {\n    \"home\": \"Description of home page content and features\",\n    \"about\": \"Description of about page content\",\n    \"services\": \"Description of services page\",\n    \"contact\": \"Description of contact page\"\n  },\n  \"email_preferences\": {\n    \"business_email\": \"email@domain.com or 'Not specified'\",\n    \"wants_branded_email\": true/false,\n    \"email_campaign_types\": [\"newsletter\", \"promotions\", \"transactional\"],\n    \"email_frequency\": \"weekly/monthly/special-occasions\"\n  },\n  \"conciseSummary\": \"[2-3 sentence summary that acknowledges the document, highlights the most important business details found, and states what the consultant will do next]\",\n  \"missingItems\": [\"Vision statement\", \"Timeframe to vision\"],\n  \"isComplete\": true/false\n}\n\nCRITICAL EXTRACTION RULES:\n✅ Extract ACTUAL data from document - never use placeholders like '[business name]'\n✅ If business vision exists in document, extract it; if not, add 'Vision statement' to missingItems\n✅ If timeframe to vision is stated, extract it; if not, add 'Timeframe to vision' to missingItems\n✅ Numeric goals like '2 million users in 30 days' go in businessGoals, NOT businessVision\n✅ CRITICAL: Extract ALL page descriptions mentioned in the document - if user specifies what pages they want (Home, About, Features, Pricing, FAQ, etc.) with descriptions, capture EVERY page in pageDescriptions\n✅ CRITICAL: customPages should list ALL specific/custom pages mentioned (beyond standard home/about/contact)\n✅ If email preferences are mentioned, extract them to email_preferences\n✅ Set isComplete=true ONLY if businessName, industry, offerings, targetAudience, businessVision, and visionTimeframe are ALL present\n✅ conciseSummary should be FRIENDLY and CONVERSATIONAL, like: 'Thank you for sharing your comprehensive [industry] blueprint targeting [audience]...'\n✅ Return valid JSON only, no markdown code blocks or extra text\n\nHere is the document to analyze:\n\n{$text}";

        // ✅ GPT-5.5 UPGRADE: Raised from 2000 to 4000 tokens for thorough document extraction
        $ai_response = $this->call_openai_api($analysis_prompt, 4000);
        $extracted_info = json_decode($ai_response, true);

        if ((!is_array($extracted_info) || !isset($extracted_info['businessName']))) {
            if (is_string($ai_response)) {
                if (preg_match('/\{(?:[^{}]|(?R))*\}/s', $ai_response, $json_match)) {
                    $decoded_fragment = json_decode($json_match[0], true);
                    if (is_array($decoded_fragment)) {
                        $extracted_info = $decoded_fragment;
                    }
                } elseif (preg_match('/\{[\s\S]*\}/', $ai_response, $json_match)) {
                    $decoded_fragment = json_decode($json_match[0], true);
                    if (is_array($decoded_fragment)) {
                        $extracted_info = $decoded_fragment;
                    }
                }
            }
        }

        // If the model returned valid JSON but not an object/array (e.g., a JSON string), normalize to fallback.
        if (!is_array($extracted_info) || !isset($extracted_info['businessName'])) {
            $extracted_info = [
                'businessName' => 'Not specified',
                'industry' => 'Not specified',
                'targetAudience' => 'Not specified',
                'offerings' => [],
                'missingItems' => ['Complete business information'],
                'isComplete' => false,
                'conciseSummary' => is_string($ai_response) ? $ai_response : 'Your comprehensive business blueprint has been analyzed.'
            ];
        }

        if (!isset($extracted_info['offerings']) || !is_array($extracted_info['offerings'])) {
            // ✅ FIX: Use array_key_exists + null-safe access to avoid "Undefined array key" warning
            $raw_offerings = array_key_exists('offerings', $extracted_info) ? $extracted_info['offerings'] : null;
            $extracted_info['offerings'] = $raw_offerings ? array_filter((array)$raw_offerings) : [];
        }

        if (!isset($extracted_info['missingItems']) || !is_array($extracted_info['missingItems'])) {
            $extracted_info['missingItems'] = [];
        }

        if (!array_key_exists('isComplete', $extracted_info)) {
            $extracted_info['isComplete'] = empty($extracted_info['missingItems']);
        }

        return [
            'extractedInfo' => $extracted_info,
            'rawResponse' => $ai_response
        ];
    }

    /**
     * Format a friendly document intake summary for chat display.
     */
    private function format_document_summary_message($extracted_info) {
        // ✅ CRITICAL: Ensure $extracted_info is always an array to prevent "Cannot access offset of type string on string"
        if (!is_array($extracted_info)) {
            $extracted_info = [
                'conciseSummary' => is_string($extracted_info) ? $extracted_info : 'Your business blueprint has been analyzed.',
                'missingItems' => [],
                'isComplete' => false
            ];
        }
        
        $friendly_message = "✅ **Thank you for uploading your business blueprint!**\n\n";
        $friendly_message .= "I've carefully analyzed the document. Here's a brief summary of what's contained in your blueprint:\n\n";
        $friendly_message .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
        $friendly_message .= "📋 **BUSINESS OVERVIEW**\n\n";
        $friendly_message .= ($extracted_info['conciseSummary'] ?? 'Your comprehensive business blueprint has been analyzed.') . "\n\n";
        $friendly_message .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
        $friendly_message .= "📊 **KEY DETAILS EXTRACTED**\n\n";
        if (!empty($extracted_info['businessName'])) {
            $friendly_message .= "**Business Name:** " . $extracted_info['businessName'] . "\n\n";
        }
        if (!empty($extracted_info['industry'])) {
            $friendly_message .= "**Industry:** " . $extracted_info['industry'] . "\n\n";
        }
        if (!empty($extracted_info['targetAudience'])) {
            $friendly_message .= "**Target Audience:** " . $extracted_info['targetAudience'] . "\n\n";
        }
        if (!empty($extracted_info['offerings']) && is_array($extracted_info['offerings'])) {
            $friendly_message .= "**Core Offerings:** " . implode(', ', $extracted_info['offerings']) . "\n\n";
        }
        if (!empty($extracted_info['businessGoals'])) {
            $friendly_message .= "**Key Goals:** " . $extracted_info['businessGoals'] . "\n\n";
        }
        if (!empty($extracted_info['pricing'])) {
            $friendly_message .= "**Pricing Structure:** " . $extracted_info['pricing'] . "\n\n";
        }
        
        // ✅ NEW: Display page descriptions from document
        if (!empty($extracted_info['pageDescriptions']) && is_array($extracted_info['pageDescriptions'])) {
            $friendly_message .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
            $friendly_message .= "🌐 **WEBSITE PAGES SPECIFIED**\n\n";
            foreach ($extracted_info['pageDescriptions'] as $page_name => $description) {
                if (!empty($description)) {
                    $display_name = ucfirst(str_replace(['_', '-'], ' ', $page_name));
                    // Truncate long descriptions
                    $short_desc = strlen($description) > 200 ? substr($description, 0, 200) . '...' : $description;
                    $friendly_message .= "**{$display_name}:** {$short_desc}\n\n";
                }
            }
        }
        
        // ✅ NEW: Display custom pages list
        if (!empty($extracted_info['customPages']) && is_array($extracted_info['customPages'])) {
            if (empty($extracted_info['pageDescriptions'])) {
                $friendly_message .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
                $friendly_message .= "🌐 **CUSTOM PAGES REQUESTED**\n\n";
            }
            $friendly_message .= "**Additional Pages:** " . implode(', ', $extracted_info['customPages']) . "\n\n";
        }
        
        // ✅ NEW: Display email preferences if present
        if (!empty($extracted_info['email_preferences']) && is_array($extracted_info['email_preferences'])) {
            $email_prefs = $extracted_info['email_preferences'];
            $has_email_info = !empty($email_prefs['business_email']) || !empty($email_prefs['email_campaign_types']);
            if ($has_email_info) {
                $friendly_message .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
                $friendly_message .= "📧 **EMAIL PREFERENCES**\n\n";
                if (!empty($email_prefs['business_email']) && $email_prefs['business_email'] !== 'Not specified') {
                    $friendly_message .= "**Business Email:** " . $email_prefs['business_email'] . "\n\n";
                }
                if (!empty($email_prefs['email_campaign_types']) && is_array($email_prefs['email_campaign_types'])) {
                    $friendly_message .= "**Email Types:** " . implode(', ', $email_prefs['email_campaign_types']) . "\n\n";
                }
                if (!empty($email_prefs['email_frequency'])) {
                    $friendly_message .= "**Email Frequency:** " . $email_prefs['email_frequency'] . "\n\n";
                }
            }
        }
        
        $friendly_message .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
        if (!empty($extracted_info['missingItems'])) {
            $friendly_message .= "⚠️ **INFORMATION NEEDED**\n\n";
            $friendly_message .= "To complete the analysis, I need the following details:\n\n";
            $friendly_message .= implode("\n", array_map(function($item) { return "• " . $item; }, $extracted_info['missingItems']));
            $friendly_message .= "\n\nPlease provide these details so we can proceed.\n\n";
        } else {
            $friendly_message .= "✅ **ALL ESSENTIAL INFORMATION CAPTURED**\n\n";
            $friendly_message .= "I have everything I need from your blueprint!\n\n";
            $friendly_message .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n";
            $friendly_message .= "📌 **I'll now continue with your consultation** — reviewing your business details and preparing a comprehensive professional overview.";
        }
        return $friendly_message;
    }

    /**
     * AJAX: Analyze raw text document posted from client (e.g., FileReader of txt/csv/json)
     */
    public function ajax_analyze_text_document() {
        set_error_handler(function($severity, $message, $file, $line) {
            throw new \ErrorException($message, 0, $severity, $file, $line);
        });
        $buffer_level = ob_get_level();
        ob_start();
        $drain_output = function($label = 'text_document_analysis') use ($buffer_level) {
            $stray_output = '';
            while (ob_get_level() > $buffer_level) {
                $chunk = ob_get_clean();
                if (is_string($chunk) && trim($chunk) !== '') {
                    $stray_output .= $chunk;
                }
            }
            if ($stray_output !== '') {
                error_log('VMO Text Document Analysis stray output [' . $label . ']: ' . substr(trim($stray_output), 0, 1200));
            }
        };
        $send_success = function(array $payload, string $label = 'success') use ($drain_output) {
            $drain_output($label);
            restore_error_handler();
            wp_send_json_success($payload);
        };
        $send_error = function(array $payload, int $status = 400, string $label = 'error') use ($drain_output) {
            $drain_output($label);
            restore_error_handler();
            wp_send_json_error($payload, $status);
        };
        // ✅ CRITICAL: Wrap entire handler in try/catch for PHP 8 type safety
        try {
            // Accept builder/admin/ajax nonce variants; always return JSON on failure.
            $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
            $admin_nonce = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
            $ajax_nonce = isset($_POST['_ajax_nonce']) ? sanitize_text_field(wp_unslash($_POST['_ajax_nonce'])) : '';
            $builder_ok = $builder_nonce && wp_verify_nonce($builder_nonce, 'vmo_builder_nonce');
            $admin_ok = $admin_nonce && wp_verify_nonce($admin_nonce, 'vmo_admin_nonce');
            $ajax_ok = ($admin_nonce && wp_verify_nonce($admin_nonce, 'vmo_ajax_nonce'))
                || ($ajax_nonce && wp_verify_nonce($ajax_nonce, 'vmo_ajax_nonce'));
        if (!($builder_ok || $admin_ok || $ajax_ok)) {
            $send_error(['message' => 'Security check failed', 'status' => 'invalid_nonce'], 403, 'invalid_nonce');
        }
        
        // ✅ TOKEN DEDUCTION v8.1.0: Charge for document analysis
        $user_id = get_current_user_id();
        if ($user_id && function_exists('vmo_deduct_tokens_for_operation')) {
            if (!vmo_deduct_tokens_for_operation($user_id, 'document')) {
                $send_error([
                    'message' => 'Insufficient tokens for document analysis. Please top up your balance.',
                    'status' => 'insufficient_tokens'
                ], 400, 'insufficient_tokens');
                return;
            }
        }
        
        $title = sanitize_text_field($_POST['title'] ?? 'Document');
        $text = (string) wp_unslash($_POST['text'] ?? '');
        $session_id = sanitize_text_field($_POST['session_id'] ?? '');
        $text = wp_kses_post($text); // keep plain text
        if (empty($text)) {
            $send_error(['message' => 'No text provided'], 400, 'missing_text');
        }
        // ✅ GPT-5.5 UPGRADE: Raised from 18K to 200K to support 100+ page documents
        $max_chars = 200000;
        if (strlen($text) > $max_chars) {
            $text = substr($text, 0, $max_chars) . "\n\n[Document truncated at 200K chars]";
        }
    
        $analysis = $this->run_document_blueprint_analysis($title, $text);
        
        // ✅ CRITICAL: Ensure $analysis is an array with expected keys
        if (!is_array($analysis)) {
            $analysis = ['extractedInfo' => [], 'rawResponse' => ''];
        }
        $extracted_info = $analysis['extractedInfo'] ?? [];
        if (!is_array($extracted_info)) {
            $extracted_info = [];
        }
        
        $friendly_message = $this->format_document_summary_message($extracted_info);
        
        // Persist a pseudo media file record for text documents
        if ($session_id) {
            $this->persist_media_file($session_id, [ 'url' => '', 'title' => $title, 'type' => 'text', 'mime' => 'text/plain' ]);
            $doc_id = 'doc_' . wp_generate_password(10, false);
            // ✅ GPT-5.5 UPGRADE: Use the centralized build_local_text_chunks() method
            $local_chunks = $this->build_local_text_chunks($text);
            $this->auto_assign_media_role($session_id, [ 'url' => '', 'title' => $title, 'type' => 'text', 'mime' => 'text/plain' ], $friendly_message);
            $this->extract_and_save_from_text($session_id, $title, $text);
            $this->save_document_with_chunks($session_id, [ 'id' => $doc_id, 'title' => $title, 'mime' => 'text/plain', 'url' => '', 'source' => 'text_document', 'content' => $text ]);

            list($existing_row, $cd) = $this->hydrate_consultation_state($session_id);
            
            // ✅ CRITICAL: Ensure $cd is an array and document_store is properly initialized
            if (!is_array($cd)) {
                $cd = $this->consultation_blueprint;
            }
            if (!isset($cd['document_store']) || !is_array($cd['document_store'])) {
                $cd['document_store'] = $this->consultation_blueprint['document_store'] ?? ['blueprint_raw' => '', 'blueprint_summary' => '', 'uploads' => []];
            }
            
            $cd['blueprintRaw'] = $text;
            $cd['document_store']['blueprint_raw'] = $text;
            if (!empty($extracted_info['conciseSummary'])) {
                $cd['blueprintSummary'] = $extracted_info['conciseSummary'];
                $cd['document_store']['blueprint_summary'] = $extracted_info['conciseSummary'];
            }
            $delta = $this->extract_consultation_delta(
                $cd['business_profile'] ?? array(),
                $text,
                '',
                array('phase' => 'Document Blueprint Upload')
            );
            if (!empty($delta['business_profile_delta'])) {
                $cd['business_profile'] = $this->merge_profile_delta($cd['business_profile'] ?? array(), $delta['business_profile_delta']);
            }
            if (!empty($delta['offers_profile_delta'])) {
                $cd['offers_profile'] = array_values(array_merge($cd['offers_profile'] ?? array(), $delta['offers_profile_delta']));
            }
            if (!empty($delta['payments_profile_delta'])) {
                $cd['payments_profile'] = $this->merge_profile_delta($cd['payments_profile'] ?? array(), $delta['payments_profile_delta']);
            }
            if (!empty($delta['discovery_profile_delta']) && is_array($delta['discovery_profile_delta'])) {
                $discovery_delta = $this->filter_empty_discovery_delta($delta['discovery_profile_delta']);
                $cd['discovery_profile'] = $this->merge_profile_delta($cd['discovery_profile'] ?? array(), is_array($discovery_delta) ? $discovery_delta : array());
                $cd['discovery_profile']['updated_at'] = current_time('mysql');
            }
            if (!empty($delta['business_opportunity_shortlist_delta']) && is_array($delta['business_opportunity_shortlist_delta'])) {
                $cd['business_opportunity_shortlist'] = $this->merge_business_opportunity_shortlist($cd['business_opportunity_shortlist'] ?? array(), $delta['business_opportunity_shortlist_delta']);
            }
            if (!empty($delta['extracted_information_delta']) && is_array($delta['extracted_information_delta'])) {
                $cd['extracted_information'] = $this->merge_profile_delta($cd['extracted_information'] ?? array(), $delta['extracted_information_delta']);
            }

            // Persist structured document record so it remains available after reload
            $this->upsert_document_store_upload($cd, [
                'id' => $doc_id,
                'title' => $title,
                'url' => '',
                'mime' => 'text/plain',
                'length' => strlen($text),
                'saved_at' => current_time('mysql'),
                'chunks' => array_map(function($t,$i){ return [ 'id' => 'c'.($i+1), 'text' => $t ]; }, $local_chunks, array_keys($local_chunks))
            ]);

            $cd = $this->reconcile_intake_memory($cd, '');
            $this->persist_consultation_state($session_id, $cd);

            $send_success([
                'message' => 'Document analyzed successfully',
                'status' => 'analyzed',
                'response' => $friendly_message,
                'summary' => $extracted_info['conciseSummary'] ?? ($analysis['rawResponse'] ?? ''),
                'extractedInfo' => $extracted_info,
                'documentRecord' => [
                    'id' => $doc_id,
                    'title' => $title,
                    'url' => '',
                    'mime' => 'text/plain',
                    'length' => strlen($text),
                    'chunks' => array_map(function($t,$i){ return [ 'id' => 'c'.($i+1), 'text' => $t ]; }, $local_chunks, array_keys($local_chunks))
                ],
                'blueprintRaw' => $text,
                'blueprintSummary' => $extracted_info['conciseSummary'] ?? '',
                'missingItems' => $extracted_info['missingItems'] ?? [],
                'isComplete' => $extracted_info['isComplete'] ?? false,
                'structuredSummaryReady' => true
            ], 'text_document_success');
        } else {
            // No session - just acknowledge receipt with analysis
            $send_success([
                'message' => 'Document analyzed',
                'status' => 'analyzed',
                'response' => $analysis['rawResponse'] ?? $friendly_message,
                'summary' => $analysis['rawResponse'] ?? $friendly_message
            ], 'text_document_success_no_session');
        }
        } catch (Throwable $e) {
            if (isset($drain_output) && is_callable($drain_output)) {
                $drain_output('fatal_exception');
            }
            restore_error_handler();
            // ✅ CRITICAL: Detailed error logging for PHP 8 type errors
            error_log('VMO Text Document Analysis Failure: ' . $e->getMessage());
            error_log('VMO Text Document Analysis Stack: ' . $e->getTraceAsString());
            error_log('VMO Text Document Analysis File: ' . $e->getFile() . ':' . $e->getLine());
            wp_send_json_error([
                'message' => 'Text document analysis failed: ' . $e->getMessage(),
                'debug_line' => $e->getLine(),
                'debug_file' => basename($e->getFile())
            ], 500);
        }
    }

    /**
     * Persist a media file reference into consultation_data.media_files
     */
    private function persist_media_file($session_id, $file) {
        try {
            $row = $this->consultation_db->get_consultation_by_session($session_id);
            $user_id = get_current_user_id();
            $cd = $row ? json_decode($row->consultation_data ?: '[]', true) : [];
            if (!isset($cd['media_files']) || !is_array($cd['media_files'])) $cd['media_files'] = [];
            // Avoid dup by URL+title
            $exists = false;
            foreach ($cd['media_files'] as $m) {
                // ✅ CRITICAL TYPE CHECK: Ensure item is array before access
                if (!is_array($m)) continue;
                if (($m['url'] ?? '') === ($file['url'] ?? '') && ($m['title'] ?? '') === ($file['title'] ?? '')) { $exists = true; break; }
            }
            if (!$exists) {
                $cd['media_files'][] = [
                    'url' => $file['url'] ?? '',
                    'title' => $file['title'] ?? '',
                    'type' => $file['type'] ?? '',
                    'mime' => $file['mime'] ?? '',
                    'saved_at' => current_time('mysql')
                ];
            }
            $this->consultation_db->save_consultation($user_id, $session_id, [ 'consultation_data' => $cd ]);
        } catch (\Throwable $e) {
            if (defined('WP_DEBUG') && WP_DEBUG) error_log('VMO persist_media_file error: ' . $e->getMessage());
        }
    }

    /**
     * Auto-assign a sensible default media role if one is not already recorded
     */
    private function auto_assign_media_role($session_id, $file, $ai_desc = '') {
        try {
            $row = $this->consultation_db->get_consultation_by_session($session_id);
            $user_id = get_current_user_id();
            $cd = $row ? json_decode($row->consultation_data ?: '[]', true) : [];
            if (!isset($cd['media_roles']) || !is_array($cd['media_roles'])) $cd['media_roles'] = [];

            $url = (string)($file['url'] ?? '');
            $mime = (string)($file['mime'] ?? '');
            $type = (string)($file['type'] ?? '');
            $title = (string)($file['title'] ?? '');
            if ($url === '') return;

            foreach ($cd['media_roles'] as $mr) {
                // ✅ CRITICAL TYPE CHECK: Ensure item is array before access
                if (!is_array($mr)) continue;
                if (($mr['url'] ?? '') === $url) return; // already assigned
            }

            $role = 'generic_media';
            $hint = strtolower($title . ' ' . $type . ' ' . $mime . ' ' . (string)$ai_desc);

            if (strpos($mime, 'image/') === 0) {
                if (preg_match('/hero|banner|header|wide|landscape|cover/', $hint)) {
                    $role = 'hero_candidate';
                } elseif (preg_match('/product|catalog|menu|food|real estate|property|listing|room|interior|exterior/', $hint)) {
                    $role = 'gallery_item';
                } elseif (preg_match('/logo|brand|branding|icon/', $hint)) {
                    $role = 'brand_asset';
                } else {
                    $role = 'gallery_item';
                }
            } elseif (strpos($mime, 'video/') === 0) {
                $role = 'hero_video_candidate';
            } elseif (strpos($mime, 'audio/') === 0) {
                $role = 'background_audio_candidate';
            } elseif (strpos($mime, 'application/pdf') === 0 || strpos($mime, 'text/') === 0) {
                $role = 'reference_document';
            }

            $cd['media_roles'][] = [
                'url' => $url,
                'title' => $title,
                'type' => $type,
                'mime' => $mime,
                'role' => $role,
                'assigned_at' => current_time('mysql')
            ];

            $this->consultation_db->save_consultation($user_id, $session_id, [ 'consultation_data' => $cd ]);
        } catch (\Throwable $e) {
            if (defined('WP_DEBUG') && WP_DEBUG) error_log('auto_assign_media_role error: ' . $e->getMessage());
        }
    }

    /** Extract text from DOCX with layered fallbacks and detailed logging */
    private function extract_text_from_docx($path) {
        $xml = '';
        error_log("VMO DOCX: Starting extraction for {$path}");

        // Method 1: ZipArchive (preferred)
        if (class_exists('ZipArchive')) {
            error_log("VMO DOCX: Trying ZipArchive method");
            $zip = new ZipArchive();
            $result = $zip->open($path);
            if ($result === true) {
                $xml = $zip->getFromName('word/document.xml') ?: '';
                $zip->close();
                error_log("VMO DOCX: ZipArchive extracted " . strlen($xml) . " bytes of XML");
            } else {
                error_log("VMO DOCX: ZipArchive failed to open, error code: {$result}");
            }
        } else {
            error_log("VMO DOCX: ZipArchive class not available");
        }

        // Method 2: zip_open function (deprecated but still works in many environments)
        if (!$xml && function_exists('zip_open')) {
            error_log("VMO DOCX: Trying zip_open method");
            $zip = @zip_open($path);
            if (is_resource($zip)) {
                while ($entry = zip_read($zip)) {
                    if (zip_entry_name($entry) === 'word/document.xml') {
                        if (zip_entry_open($zip, $entry, 'r')) {
                            $xml = zip_entry_read($entry, zip_entry_filesize($entry));
                            zip_entry_close($entry);
                            error_log("VMO DOCX: zip_open extracted " . strlen($xml) . " bytes of XML");
                        }
                        break;
                    }
                }
                zip_close($zip);
            } else {
                error_log("VMO DOCX: zip_open failed");
            }
        }

        // Method 3: zip:// stream wrapper
        if (!$xml && in_array('zip', stream_get_wrappers(), true)) {
            error_log("VMO DOCX: Trying zip:// stream wrapper");
            $realPath = realpath($path);
            if ($realPath) {
                // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- zip:// stream wrapper has no WP_Filesystem equivalent
                $xml = @file_get_contents('zip://' . $realPath . '#word/document.xml') ?: '';
                error_log("VMO DOCX: Stream wrapper extracted " . strlen($xml) . " bytes");
            }
        }

        // Note: shell_exec-based unzip fallback removed for WordPress.org compliance.
        // Methods 1-3 (ZipArchive, zip_open, zip:// stream) provide PHP-native DOCX extraction.

        $result = $this->convert_docx_xml_to_text($xml);
        error_log("VMO DOCX: Final text length: " . strlen($result) . " chars");
        return $result;
    }

    private function convert_docx_xml_to_text($xml) {
        if (!$xml) {
            return '';
        }
        $xml = preg_replace('/<w:p[^>]*>/', "\n", $xml);
        $xml = preg_replace('/<w:tab[^>]*\/>/', "\t", $xml);
        $xml = strip_tags($xml);
        $xml = html_entity_decode($xml, ENT_QUOTES | ENT_XML1, 'UTF-8');
        $xml = preg_replace("/\n{2,}/", "\n", $xml);
        return trim($xml);
    }

    /**
     * ✅ Fallback DOCX extraction using alternative methods
     */
    private function extract_text_from_docx_fallback($path) {
        $text = '';
        
        // Note: shell_exec-based soffice/docx2txt/libreoffice fallbacks removed for WordPress.org compliance.
        // Use PHP-native zip:// stream wrapper for DOCX text extraction.
        
        // Direct XML extraction with error handling
        if (empty(trim($text))) {
            try {
                // Try to open as a stream
                $zipPath = 'zip://' . realpath($path) . '#word/document.xml';
                // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- zip:// stream wrapper has no WP_Filesystem equivalent
                $xml = @file_get_contents($zipPath);
                if ($xml) {
                    $text = $this->convert_docx_xml_to_text($xml);
                }
            } catch (Exception $e) {
                error_log("VMO DOCX fallback stream failed: " . $e->getMessage());
            }
        }
        
        return $text;
    }

    /**
     * ✅ Extract text from legacy .doc format
     */
    private function extract_text_from_doc_legacy($path) {
        $text = '';
        
        // Note: shell_exec-based antiword/catdoc/libreoffice fallbacks removed for WordPress.org compliance.
        // Legacy .doc format extraction uses naive binary extraction as a PHP-native fallback.
        
        // Naive binary extraction (PHP-native fallback)
        if (empty(trim($text))) {
            $bin = @vmo_get_filesystem()->get_contents($path);
            if ($bin !== false) {
                // Extract readable strings from binary
                if (preg_match_all('/[\x20-\x7E]{4,}/', $bin, $m)) {
                    $text = implode(' ', $m[0]);
                    // Clean up common garbage
                    $text = preg_replace('/\s{2,}/', ' ', $text);
                    $text = preg_replace('/[^\x20-\x7E\n]/', '', $text);
                }
            }
        }
        
        return trim($text);
    }

    /**
     * Extract text from PDF.
     * Note: shell_exec-based pdftotext/mutool removed for WordPress.org compliance.
     * Automated PDF text extraction requires a PHP library (e.g., smalot/pdfparser).
     * Falls back to naive extraction of printable strings from raw PDF bytes.
     */
    private function extract_text_from_pdf_pdftotext($path) {
        // Attempt naive text extraction from raw PDF bytes
        $bin = @vmo_get_filesystem()->get_contents($path);
        if ($bin === false) return '';
        // Extract readable text strings (4+ printable characters)
        if (preg_match_all('/[\x20-\x7E]{4,}/', $bin, $m)) {
            $text = preg_replace('/\s{2,}/', ' ', implode(' ', $m[0]));
            return trim($text);
        }
        return '';
    }

    // Note: is_shell_exec_enabled() and find_executable() removed for WordPress.org compliance.
    // shell_exec/exec are strictly prohibited on WordPress.org hosted plugins.

    /** Naive PDF text extraction: extract printable ASCII strings */
    private function extract_text_from_pdf_naive($path) {
        $bin = @vmo_get_filesystem()->get_contents($path);
        if ($bin === false) return '';
        // Extract sequences of readable characters (3+), join with spaces
        if (preg_match_all('/[\x20-\x7E]{3,}/', $bin, $m)) {
            // Collapse excessive spaces
            $text = preg_replace('/\s{2,}/', ' ', implode(' ', $m[0]));
            return trim($text);
        }
        return '';
    }

    /** OCR PDF fallback using Imagick+Tesseract or ImageMagick convert+tesseract if available */
    private function extract_text_from_pdf_with_ocr($path) {
        $text = '';
        // Prefer Imagick if installed
        if (class_exists('Imagick')) {
            try {
                $imagick = new Imagick();
                $imagick->setResolution(300, 300);
                $imagick->readImage($path);
                $num = $imagick->getNumberImages();
                $maxPages = min($num, 100); // ✅ GPT-5.5 UPGRADE: Raised from 5 to 100 pages for OCR PDFs
                $i = 0;
                foreach ($imagick as $page) {
                    if ($i >= $maxPages) break;
                    $page->setImageFormat('png');
                    $tmp = wp_tempnam('vmo_pdf_page_');
                    $imgPath = $tmp . '.png';
                    @rename($tmp, $imgPath);
                    $page->writeImage($imgPath);
                    $ocr = $this->tesseract_ocr_image($imgPath);
                    @unlink($imgPath);
                    if ($ocr) { $text .= "

" . $ocr; }
                    $i++;
                }
                $imagick->clear();
                $imagick->destroy();
                return trim($text);
            } catch (\Throwable $e) {
                // Fall through to CLI tools
            }
        }
        // Note: ImageMagick convert + tesseract CLI calls removed for WordPress.org compliance.
        // OCR-based PDF text extraction requires a hosted OCR service or PHP library.
        if (empty($text)) {
            $text = basename($path) . ' (PDF file – text extraction requires OCR service)';
        }
        return trim($text);
    }

    /**
     * OCR helper for a single image.
     * Note: tesseract CLI calls removed for WordPress.org compliance.
     * Returns the image filename as text content; full OCR requires a hosted OCR service.
     */
    private function tesseract_ocr_image($imgPath) {
        if (!file_exists($imgPath)) return '';
        return basename($imgPath) . ' (Image file – text extraction requires OCR service)';
    }

    /**
     * Extract and persist structured "Known Answers" from arbitrary text
     * Best-effort: never throws; silently returns on failure
     */
    private function extract_and_save_from_text($session_id, $source_title, $text) {
        try {
            $session_id = trim((string)$session_id);
            $text = trim((string)$text);
            if ($session_id === '' || $text === '') { return; }
            // Keep prompt compact; request strict JSON only
            $schema = '{
  "business_name": string|null,
  "industry": string|null,
  "services": string[]|null,
  "products": string[]|null,
  "location": string|null,
  "contact_methods": string[]|null,
  "target_audience": string[]|null,
  "style_tone": string[]|null,
  "goals": string[]|null,
  "languages": string[]|null,
  "colors": string[]|null,
  "brand_values": string[]|null,
  "pricing": string[]|null,
  "operating_hours": string[]|null,
  "social_links": string[]|null,
  "questions_answered": { [question: string]: string }
}';
            $prompt = "From the following content (source: '{$source_title}'), extract any concrete business/website facts as a compact JSON.
Return ONLY JSON that matches this schema (omit fields if absent):
{$schema}

--- BEGIN CONTENT ---
{$text}
--- END CONTENT ---";
            $raw = $this->call_openai_api($prompt, 700);
            if (!$raw || stripos($raw, '{') === false) { return; }
            // Try full decode, then fallback to first JSON object in text
            $data = json_decode($raw, true);
            if (!is_array($data)) {
                if (preg_match('/\{[\s\S]*\}/', $raw, $m)) {
                    $data = json_decode($m[0], true);
                }
            }
            if (!is_array($data)) { return; }
            // Flatten questions_answered into top-level key-value pairs if present
            if (isset($data['questions_answered']) && is_array($data['questions_answered'])) {
                foreach ($data['questions_answered'] as $q => $a) {
                    if (is_string($q) && $q !== '') {
                        $data[$q] = $a;
                    }
                }
                unset($data['questions_answered']);
            }
            // Persist
            $this->save_extracted_information($session_id, $data);
            return $data;
        } catch (\Throwable $e) {
            if (defined('WP_DEBUG') && WP_DEBUG) error_log('VMO extract_and_save_from_text error: ' . $e->getMessage());
        }
    }

    /**
     * Persist full document text into consultation_data.documents with chunking for retrieval
     */
    private function save_document_with_chunks($session_id, $doc) {
        try {
            $row = $this->consultation_db->get_consultation_by_session($session_id);
            $user_id = get_current_user_id();
            $cd = $row ? json_decode($row->consultation_data ?: '[]', true) : [];
            if (!isset($cd['documents']) || !is_array($cd['documents'])) $cd['documents'] = [];

            $title = (string)($doc['title'] ?? 'Document');
            $url = (string)($doc['url'] ?? '');
            $mime = (string)($doc['mime'] ?? 'text/plain');
            $source = (string)($doc['source'] ?? 'document');
            $content = (string)($doc['content'] ?? '');
            if ($content === '') return;

            // Avoid duplicates: same title+url+length
            $len = strlen($content);
            foreach ($cd['documents'] as $d) {
                // ✅ CRITICAL TYPE CHECK: Ensure item is array before access
                if (!is_array($d)) continue;
                if (($d['title'] ?? '') === $title && ($d['url'] ?? '') === $url && intval($d['length'] ?? 0) === $len) {
                    return; // already saved
                }
            }

            // Chunk text safely at ~1800 chars on paragraph boundaries
            $maxChunk = 1800;
            $chunks = [];
            $remaining = $content;
            while ($remaining !== '') {
                if (strlen($remaining) <= $maxChunk) {
                    $chunks[] = $remaining;
                    break;
                }
                $slice = substr($remaining, 0, $maxChunk);
                // ✅ FIX: Avoid max(false, false) which can trigger deprecation notices in PHP 8.1+
                $bp1 = strrpos($slice, "\n\n");
                $bp2 = strrpos($slice, '. ');
                if ($bp1 === false && $bp2 === false) {
                    $breakPos = $maxChunk;
                } elseif ($bp1 === false) {
                    $breakPos = $bp2;
                } elseif ($bp2 === false) {
                    $breakPos = $bp1;
                } else {
                    $breakPos = max($bp1, $bp2);
                }
                if ($breakPos <= 0) { $breakPos = $maxChunk; }
                $chunks[] = substr($remaining, 0, $breakPos);
                $remaining = ltrim(substr($remaining, $breakPos));
            }

            $cd['documents'][] = [
                'id' => (string)($doc['id'] ?? ('doc_' . wp_generate_password(10, false))),
                'title' => $title,
                'url' => $url,
                'mime' => $mime,
                'source' => $source,
                'length' => $len,
                'saved_at' => current_time('mysql'),
                'chunks' => $chunks
            ];

            $this->consultation_db->save_consultation($user_id, $session_id, [ 'consultation_data' => $cd ]);
        } catch (\Throwable $e) {
            if (defined('WP_DEBUG') && WP_DEBUG) error_log('VMO save_document_with_chunks error: ' . $e->getMessage());
        }
    }

    /**
     * Save the Q&A block from document analysis so it can be injected verbatim into assistant context
     */
    private function save_document_qa_block($session_id, $doc_title, $analysis_response) {
        try {
            $row = $this->consultation_db->get_consultation_by_session($session_id);
            $user_id = get_current_user_id();
            $cd = $row ? json_decode($row->consultation_data ?: '[]', true) : [];
            if (!isset($cd['document_qa_blocks']) || !is_array($cd['document_qa_blocks'])) {
                $cd['document_qa_blocks'] = [];
            }
            // Parse out just the "2) Extracted Answers" section
            $qa_block = '';
            if (preg_match('/2\)\s+Extracted Answers\s+(.*?)(?=3\)|$)/s', $analysis_response, $m)) {
                $qa_block = trim($m[1]);
            }
            if ($qa_block !== '') {
                $cd['document_qa_blocks'][] = [
                    'doc_title' => $doc_title,
                    'qa_text' => $qa_block,
                    'saved_at' => current_time('mysql')
                ];
            }
            $this->consultation_db->save_consultation($user_id, $session_id, [ 'consultation_data' => $cd ]);
        } catch (\Throwable $e) {
            if (defined('WP_DEBUG') && WP_DEBUG) error_log('VMO save_document_qa_block error: ' . $e->getMessage());
        }
    }

    /**
     * Retrieve relevant document excerpts by simple keyword scoring against stored chunks
     */
    private function search_document_chunks($session_id, $query, $budget_chars = 2200) {
        try {
            if (!$session_id || !$query) return '';
            $row = $this->consultation_db->get_consultation_by_session($session_id);
            if (!$row) return '';
            $cd = json_decode($row->consultation_data ?: '[]', true);
            if (!is_array($cd) || empty($cd['documents'])) return '';
            $terms = preg_split('/[^\p{L}\p{N}]+/u', mb_strtolower($query));
            $terms = array_values(array_filter(array_unique(array_map(function($t){ return (mb_strlen($t) >= 3) ? $t : ''; }, $terms))));
            if (empty($terms)) return '';

            $scored = [];
            foreach ($cd['documents'] as $doc) {
                $title = (string)($doc['title'] ?? 'Document');
                foreach ((array)($doc['chunks'] ?? []) as $i => $chunk) {
                    $text = mb_strtolower((string)$chunk);
                    $score = 0;
                    foreach ($terms as $t) { if ($t && mb_strpos($text, $t) !== false) $score++; }
                    if ($score > 0) {
                        $scored[] = [ 'score' => $score, 'title' => $title, 'snippet' => trim((string)$chunk) ];
                    }
                }
            }
            if (empty($scored)) return '';
            usort($scored, function($a,$b){ return $b['score'] <=> $a['score']; });
            $collected = '';
            $used = 0;
            foreach ($scored as $hit) {
                $snippet = $hit['snippet'];
                $entry = "— " . $hit['title'] . " —
" . $snippet . "

";
                $len = strlen($entry);
                if ($used + $len > $budget_chars) break;
                $collected .= $entry;
                $used += $len;
            }
            return trim($collected);
        } catch (\Throwable $e) {
            if (defined('WP_DEBUG') && WP_DEBUG) error_log('VMO search_document_chunks error: ' . $e->getMessage());
            return '';
        }
    }

    /**
     * Fallback: Return recent document chunks (top 1-2 chunks from last 1-2 docs) within a budget
     */
    private function recent_document_chunks($session_id, $budget = 1800) {
        try {
            $row = $this->consultation_db->get_consultation_by_session($session_id);
            if (!$row) return '';
            $cd = json_decode($row->consultation_data ?: '[]', true);
            if (!is_array($cd) || empty($cd['documents'])) return '';
            $docs = (array)$cd['documents'];
            $collected = '';
            $used = 0;
            $takeDocs = array_slice(array_reverse($docs), 0, 2);
            foreach ($takeDocs as $doc) {
                $title = (string)($doc['title'] ?? 'Document');
                $chunks = array_slice((array)($doc['chunks'] ?? []), 0, 2);
                foreach ($chunks as $c) {
                    $text = is_array($c) ? (string)($c['text'] ?? '') : (string)$c;
                    if ($text === '') continue;
                    $entry = "— " . $title . " —
" . $text . "

";
                    $len = strlen($entry);
                    if ($used + $len > $budget) break 2;
                    $collected .= $entry;
                    $used += $len;
                }
            }
            return trim($collected);
        } catch (\Throwable $e) {
            if (defined('WP_DEBUG') && WP_DEBUG) error_log('recent_document_chunks error: ' . $e->getMessage());
            return '';
        }
    }
    
    /**
     * Helper methods
     */
    private function generate_session_id() {
        return 'vmo_session_' . time() . '_' . wp_generate_password(8, false);
    }
    
    private function consolidate_agent_recommendations($package, $responses) {
        // Generate structured professional recommendations instead of just text
        return $this->generate_professional_recommendations($package, $responses);
    }
    
    /**
     * Generate professional structured recommendations with checkboxes and descriptions
     */
    private function generate_professional_recommendations($package, $responses) {
        $business_context = $this->format_business_context($responses);
        
        // Define the core essential services with structured data
        $core_services = [
            'business_phone' => [
                'title' => 'Professional Business Phone System',
                'priority' => 'ESSENTIAL',
                'checked' => true,
                'icon' => '📞',
                'description' => 'Automated AI receptionist with 24/7 availability, call recording, and professional voicemail.',
                'benefits' => [
                    'Never miss a customer call again',
                    'Professional business image',
                    'AI handles initial customer inquiries',
                    'Call recording for quality assurance',
                    'Integration with your business dashboard'
                ],
                'monthly_cost' => '$29/month',
                'implementation_time' => 'Instant setup'
            ],
            'seo_optimization' => [
                'title' => 'Search Engine Optimization (SEO)',
                'priority' => 'ESSENTIAL',
                'checked' => true,
                'icon' => '🔍',
                'description' => 'Complete SEO strategy with keyword optimization, competitor analysis, and ongoing monitoring.',
                'benefits' => [
                    'Higher Google search rankings',
                    'More organic website traffic',
                    'Competitive advantage in search',
                    'Weekly SEO performance reports',
                    'Automatic content optimization'
                ],
                'monthly_cost' => '$49/month',
                'implementation_time' => '1-2 weeks for full optimization'
            ],
            'email_marketing' => [
                'title' => 'Professional Email Marketing',
                'priority' => 'ESSENTIAL',
                'checked' => true,
                'icon' => '📧',
                'description' => 'Automated email campaigns, customer newsletters, and lead nurturing sequences.',
                'benefits' => [
                    'Automated customer follow-up',
                    'Professional email templates',
                    'Advanced customer segmentation',
                    'Campaign performance analytics',
                    'Increased customer retention'
                ],
                'monthly_cost' => '$19/month',
                'implementation_time' => '3-5 days setup'
            ]
        ];
        
        // Generate additional recommendations based on business analysis
        $additional_services = $this->generate_additional_service_recommendations($package, $responses);
        
        // Combine core and additional services
        $all_recommendations = array_merge($core_services, $additional_services);
        
        return [
            'type' => 'structured_recommendations',
            'total_services' => count($all_recommendations),
            'essential_services' => 3,
            'recommendations' => $all_recommendations,
            'total_monthly_cost' => $this->calculate_total_monthly_cost($all_recommendations),
            'business_context' => $business_context
        ];
    }
    
    /**
     * Generate additional service recommendations based on business type and analysis
     */
    private function generate_additional_service_recommendations($package, $responses) {
        $additional_services = [];
        
        // Check if e-commerce is needed
        if ($this->needs_ecommerce($responses)) {
            $additional_services['ecommerce_integration'] = [
                'title' => 'E-commerce Integration',
                'priority' => 'RECOMMENDED',
                'checked' => false,
                'icon' => '🛒',
                'description' => 'Complete online store with payment processing, inventory management, and order tracking.',
                'benefits' => [
                    'Accept online payments securely',
                    'Automated order processing',
                    'Inventory management system',
                    'Customer order tracking',
                    'Sales analytics and reporting'
                ],
                'monthly_cost' => '$39/month',
                'implementation_time' => '1 week setup'
            ];
        }
        
        // Check if appointment booking is needed
        if ($this->needs_appointment_booking($responses)) {
            $additional_services['appointment_booking'] = [
                'title' => 'Appointment Booking System',
                'priority' => 'RECOMMENDED',
                'checked' => false,
                'icon' => '📅',
                'description' => 'Online appointment scheduling with calendar integration and automated reminders.',
                'benefits' => [
                    '24/7 online booking availability',
                    'Automated appointment reminders',
                    'Calendar synchronization',
                    'Reduced no-shows',
                    'Staff schedule management'
                ],
                'monthly_cost' => '$25/month',
                'implementation_time' => '2-3 days setup'
            ];
        }
        
        // Check if social media management is needed
        if ($this->needs_social_media($responses)) {
            $additional_services['social_media'] = [
                'title' => 'Social Media Management',
                'priority' => 'OPTIONAL',
                'checked' => false,
                'icon' => '📱',
                'description' => 'Automated social media posting, content creation, and engagement tracking.',
                'benefits' => [
                    'Consistent social media presence',
                    'Automated content posting',
                    'Engagement analytics',
                    'Brand awareness growth',
                    'Customer interaction management'
                ],
                'monthly_cost' => '$35/month',
                'implementation_time' => '1 week setup'
            ];
        }
        
        return $additional_services;
    }
    
    /**
     * Helper functions to determine service needs
     */
    private function needs_ecommerce($responses) {
        $business_context = strtolower(json_encode($responses));
        return (strpos($business_context, 'sell') !== false || 
                strpos($business_context, 'product') !== false ||
                strpos($business_context, 'store') !== false ||
                strpos($business_context, 'shop') !== false ||
                strpos($business_context, 'ecommerce') !== false);
    }
    
    private function needs_appointment_booking($responses) {
        $business_context = strtolower(json_encode($responses));
        return (strpos($business_context, 'appointment') !== false ||
                strpos($business_context, 'booking') !== false ||
                strpos($business_context, 'schedule') !== false ||
                strpos($business_context, 'consultation') !== false ||
                strpos($business_context, 'service') !== false);
    }
    
    private function needs_social_media($responses) {
        $business_context = strtolower(json_encode($responses));
        return (strpos($business_context, 'social') !== false ||
                strpos($business_context, 'facebook') !== false ||
                strpos($business_context, 'instagram') !== false ||
                strpos($business_context, 'marketing') !== false ||
                strpos($business_context, 'brand') !== false);
    }
    
    /**
     * Calculate total monthly cost for selected services
     */
    private function calculate_total_monthly_cost($recommendations) {
        $total = 0;
        foreach ($recommendations as $service) {
            if ($service['checked']) {
                $cost = str_replace(['$', '/month'], '', $service['monthly_cost']);
                $total += intval($cost);
            }
        }
        return $total;
    }
    
    /**
     * Create comprehensive implementation roadmap with daily activities, milestones, and KPIs
     * This is the master function that generates the entire roadmap system
     */
    private function create_implementation_roadmap($package, $responses) {
        $user_id = get_current_user_id();
        $session_id = $package['session_id'] ?? uniqid('session_');
        
        // Extract business vision from consultation responses
        $business_vision = $this->extract_business_vision($responses);
        
        // Generate comprehensive roadmap using OpenAI Deep Research
        $roadmap_data = $this->generate_comprehensive_roadmap($business_vision, $package, $responses);
        
        // Save roadmap to database
        $roadmap_id = $this->save_roadmap_to_database($user_id, $session_id, $business_vision, $roadmap_data);
        
        // Extract and save milestones
        $this->extract_and_save_milestones($roadmap_id, $user_id, $roadmap_data);
        
        // Generate initial daily activities (first 30 days)
        $this->generate_initial_daily_activities($roadmap_id, $user_id, $roadmap_data);
        
        // Initialize KPI tracking system
        $this->initialize_kpi_tracking($roadmap_id, $user_id, $roadmap_data, $responses);
        
        // Schedule first weekly deep research scan (7 days from now)
        $this->schedule_weekly_deep_research($roadmap_id, $user_id);
        
        return $roadmap_data['roadmap_summary'] ?? 'Comprehensive roadmap generated with milestones, daily activities, and KPI tracking.';
    }
    
    /**
     * Extract business vision from consultation responses
     */
    private function extract_business_vision($responses) {
        $vision_indicators = [
            'What are your main business goals?',
            'Where do you see your business in 5 years?',
            'What success looks like?',
            'business objectives',
            'target revenue',
            'growth goals'
        ];
        
        $vision = '';
        foreach ($responses as $category => $answers) {
            if (is_array($answers)) {
                foreach ($answers as $question => $answer) {
                    foreach ($vision_indicators as $indicator) {
                        if (stripos($question, $indicator) !== false || stripos($answer, $indicator) !== false) {
                            $vision .= $answer . ' ';
                        }
                    }
                }
            }
        }
        
        return trim($vision) ?: 'Build a successful, sustainable business with strong online presence and steady growth.';
    }
    
    /**
     * Generate comprehensive roadmap using OpenAI Deep Research
     */
    private function generate_comprehensive_roadmap($business_vision, $package, $responses) {
        $current_date = date('Y-m-d');
        $context = $this->format_business_context($responses);
        
        $prompt = "You are a senior business strategist creating a COMPREHENSIVE, ACTIONABLE roadmap from TODAY ({$current_date}) to achievement of the user's business vision.

**BUSINESS VISION:**
{$business_vision}

**BUSINESS CONTEXT:**
{$context}

**SERVICES BEING PROVIDED:**
- Custom AI-generated website (launching today)
- Phone system with AI receptionist
- SEO automation with weekly audits
- Email marketing automation
- Business analytics and tracking

**YOUR TASK:**
Create a detailed, month-by-month roadmap that includes:

1. **PHASES** (typically 3-6 phases spanning 6-18 months):
   - Phase name and duration
   - Primary focus and objectives
   - Key deliverables

2. **MILESTONES** (15-25 major milestones):
   - Specific, measurable milestones
   - Target dates (realistic timelines)
   - Success criteria (how to measure completion)
   - Dependencies (which milestones depend on others)
   - KPI targets at each milestone

3. **DAILY ACTIVITIES** (for first 60 days):
   - Specific daily tasks the user should do
   - Include variety: content creation, marketing, networking, analysis, optimization
   - Realistic time estimates (30-120 minutes each)
   - Clear priorities (Critical, High, Medium, Low)

4. **KPI TRACKING SYSTEM**:
   - 10-15 critical KPIs to track (website traffic, revenue, leads, conversion rates, SEO rankings, email open rates, phone calls, customer satisfaction, etc.)
   - Baseline values (starting point)
   - Target values at 30, 60, 90, 180, 365 days
   - Measurement frequency for each KPI

5. **WEEKLY DEEP RESEARCH FOCUS**:
   - What should be researched each week (competitor analysis, market trends, new opportunities, customer feedback analysis, etc.)
   - How research findings should influence the roadmap

**OUTPUT FORMAT (JSON):**
```json
{
  \"roadmap_summary\": \"Executive summary of the roadmap\",
  \"vision_target_date\": \"YYYY-MM-DD\",
  \"total_duration_months\": 12,
  \"phases\": [
    {
      \"phase_number\": 1,
      \"phase_name\": \"Foundation & Launch\",
      \"start_date\": \"YYYY-MM-DD\",
      \"end_date\": \"YYYY-MM-DD\",
      \"duration_days\": 30,
      \"objectives\": [\"Launch website\", \"Establish baseline metrics\", \"Initial marketing push\"],
      \"key_focus\": \"Building foundation and establishing online presence\"
    }
  ],
  \"milestones\": [
    {
      \"milestone_id\": \"m1\",
      \"phase_number\": 1,
      \"phase_name\": \"Foundation & Launch\",
      \"title\": \"Website launched and live\",
      \"description\": \"Detailed description\",
      \"target_date\": \"YYYY-MM-DD\",
      \"success_criteria\": [\"Website live\", \"All pages functional\", \"Contact forms working\"],
      \"kpi_targets\": {\"website_traffic\": 100, \"leads_generated\": 5},
      \"depends_on\": null,
      \"priority\": 1
    }
  ],
  \"daily_activities_template\": [
    {
      \"day_number\": 1,
      \"date_offset_from_start\": 0,
      \"activities\": [
        {
          \"title\": \"Review website analytics and traffic sources\",
          \"description\": \"Detailed steps\",
          \"type\": \"analytics\",
          \"duration_minutes\": 30,
          \"priority\": 1,
          \"milestone_id\": \"m1\"
        }
      ]
    }
  ],
  \"kpis\": [
    {
      \"kpi_id\": \"kpi_website_traffic\",
      \"category\": \"website\",
      \"name\": \"Monthly Website Traffic\",
      \"description\": \"Unique visitors per month\",
      \"unit\": \"number\",
      \"baseline_value\": 0,
      \"targets\": {
        \"30_days\": 500,
        \"60_days\": 1500,
        \"90_days\": 3000,
        \"180_days\": 8000,
        \"365_days\": 20000
      },
      \"measurement_frequency\": \"daily\",
      \"is_critical\": true
    }
  ],
  \"weekly_research_topics\": [
    {\"week\": 1, \"topic\": \"Competitor website analysis and positioning\"},
    {\"week\": 2, \"topic\": \"Customer search behavior and keyword opportunities\"},
    {\"week\": 3, \"topic\": \"Content strategy and trending topics in industry\"}
  ]
}
```

Generate this comprehensive roadmap now. Be SPECIFIC and ACTIONABLE.";

        $raw_roadmap = $this->call_openai_deep_research($prompt, 'roadmap_generation');
        if (is_array($raw_roadmap)) {
            return $raw_roadmap;
        }
        if (!is_string($raw_roadmap) || trim($raw_roadmap) === '') {
            return [];
        }

        $decoded_roadmap = json_decode($raw_roadmap, true);
        return is_array($decoded_roadmap) ? $decoded_roadmap : [];
    }
    
    /**
     * Save roadmap to database
     */
    private function save_roadmap_to_database($user_id, $session_id, $business_vision, $roadmap_data) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'vmo_business_roadmaps';
        
        $roadmap_id = 'roadmap_' . uniqid();
        $start_date = date('Y-m-d'); // Today (website generation date)
        $target_date = $roadmap_data['vision_target_date'] ?? date('Y-m-d', strtotime('+12 months'));
        
        // Calculate next deep research date (7 days from now)
        $next_research_date = date('Y-m-d H:i:s', strtotime('+7 days'));
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->insert($table_name, [
            'roadmap_id' => $roadmap_id,
            'user_id' => $user_id,
            'session_id' => $session_id,
            'business_vision' => $business_vision,
            'vision_target_date' => $target_date,
            'roadmap_start_date' => $start_date,
            'roadmap_data' => json_encode($roadmap_data),
            'current_phase' => $roadmap_data['phases'][0]['phase_name'] ?? 'Phase 1: Foundation',
            'overall_progress' => 0.00,
            'next_deep_research_date' => $next_research_date,
            'created_at' => current_time('mysql'),
            'updated_at' => current_time('mysql')
        ]);
        
        return $roadmap_id;
    }
    
    /**
     * Extract and save milestones from roadmap
     */
    private function extract_and_save_milestones($roadmap_id, $user_id, $roadmap_data) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'vmo_roadmap_milestones';
        
        if (empty($roadmap_data['milestones'])) {
            return;
        }
        
        foreach ($roadmap_data['milestones'] as $milestone) {
            $milestone_id = 'milestone_' . uniqid();
            
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->insert($table_name, [
                'milestone_id' => $milestone_id,
                'roadmap_id' => $roadmap_id,
                'user_id' => $user_id,
                'phase_number' => $milestone['phase_number'] ?? 1,
                'phase_name' => $milestone['phase_name'] ?? 'Phase 1',
                'milestone_title' => $milestone['title'],
                'milestone_description' => $milestone['description'] ?? '',
                'target_date' => $milestone['target_date'],
                'status' => 'pending',
                'success_criteria' => json_encode($milestone['success_criteria'] ?? []),
                'kpi_targets' => json_encode($milestone['kpi_targets'] ?? []),
                'dependent_on_milestone_id' => $milestone['depends_on'] ?? null,
                'priority' => $milestone['priority'] ?? 3,
                'progress_percentage' => 0.00,
                'created_at' => current_time('mysql')
            ]);
        }
    }
    
    /**
     * Generate initial daily activities (first 60 days)
     */
    private function generate_initial_daily_activities($roadmap_id, $user_id, $roadmap_data) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'vmo_daily_activities';
        
        if (empty($roadmap_data['daily_activities_template'])) {
            return;
        }
        
        $start_date = date('Y-m-d');
        
        foreach ($roadmap_data['daily_activities_template'] as $day_template) {
            $activity_date = date('Y-m-d', strtotime($start_date . ' + ' . $day_template['date_offset_from_start'] . ' days'));
            
            foreach ($day_template['activities'] as $activity) {
                $activity_id = 'activity_' . uniqid();
                
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                $wpdb->insert($table_name, [
                    'activity_id' => $activity_id,
                    'roadmap_id' => $roadmap_id,
                    'milestone_id' => $activity['milestone_id'] ?? null,
                    'user_id' => $user_id,
                    'activity_date' => $activity_date,
                    'activity_title' => $activity['title'],
                    'activity_description' => $activity['description'] ?? '',
                    'activity_type' => $activity['type'] ?? 'task',
                    'estimated_duration_minutes' => $activity['duration_minutes'] ?? 60,
                    'priority' => $activity['priority'] ?? 3,
                    'status' => 'pending',
                    'ai_generated' => 1,
                    'recurring' => $activity['recurring'] ?? 0,
                    'recurrence_pattern' => $activity['recurrence_pattern'] ?? null,
                    'created_at' => current_time('mysql')
                ]);
            }
        }
    }
    
    /**
     * Initialize KPI tracking system
     */
    private function initialize_kpi_tracking($roadmap_id, $user_id, $roadmap_data, $responses) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'vmo_business_kpis';
        
        if (empty($roadmap_data['kpis'])) {
            return;
        }
        
        foreach ($roadmap_data['kpis'] as $kpi) {
            $kpi_id = 'kpi_' . uniqid();
            
            // Calculate target date (typically 12 months out)
            $target_date = date('Y-m-d', strtotime('+' . ($roadmap_data['total_duration_months'] ?? 12) . ' months'));
            
            // Get target value from targets (use 365_days target)
            $target_value = $kpi['targets']['365_days'] ?? null;
            
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->insert($table_name, [
                'kpi_id' => $kpi_id,
                'roadmap_id' => $roadmap_id,
                'user_id' => $user_id,
                'kpi_category' => $kpi['category'],
                'kpi_name' => $kpi['name'],
                'kpi_description' => $kpi['description'] ?? '',
                'kpi_unit' => $kpi['unit'] ?? 'number',
                'current_value' => $kpi['baseline_value'],
                'baseline_value' => $kpi['baseline_value'],
                'target_value' => $target_value,
                'target_date' => $target_date,
                'measurement_frequency' => $kpi['measurement_frequency'] ?? 'daily',
                'last_measured_at' => current_time('mysql'),
                'trend' => 'stable',
                'trend_percentage' => 0.00,
                'status' => 'on_track',
                'is_critical' => $kpi['is_critical'] ?? 0,
                'display_on_dashboard' => 1,
                'created_at' => current_time('mysql')
            ]);
            
            // Create initial KPI history entry (baseline)
            $this->record_kpi_measurement($kpi_id, $user_id, $kpi['baseline_value'], 'system', 'Baseline measurement at roadmap creation');
        }
    }
    
    /**
     * Record KPI measurement in history
     */
    private function record_kpi_measurement($kpi_id, $user_id, $value, $source = 'system', $notes = '') {
        global $wpdb;
        $table_name = $wpdb->prefix . 'vmo_kpi_history';
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->insert($table_name, [
            'kpi_id' => $kpi_id,
            'user_id' => $user_id,
            'measured_value' => $value,
            'measurement_date' => date('Y-m-d'),
            'measurement_time' => current_time('mysql'),
            'data_source' => $source,
            'notes' => $notes,
            'created_at' => current_time('mysql')
        ]);
    }
    
    /**
     * Schedule weekly deep research using WordPress cron
     * v8.2.1: Now respects plan-based frequency (Tester: none, Starter: biweekly, Pro/Elite: weekly)
     */
    private function schedule_weekly_deep_research($roadmap_id, $user_id) {
        // Check if user's plan supports background ops
        if (!function_exists('vmo_get_background_ops_config')) {
            return false;
        }
        
        $config = vmo_get_background_ops_config($user_id);
        
        if (!$config['enabled']) {
            // Tester/Free plan - send upgrade prompt instead of scheduling
            if (function_exists('vmo_send_background_ops_upgrade_prompt')) {
                vmo_send_background_ops_upgrade_prompt($user_id, 'weekly_deep_research');
            }
            error_log("VMO Background Ops: Deep research not scheduled for user {$user_id} - plan does not support background ops");
            return false;
        }
        
        // Clear any existing schedule to ensure correct frequency
        wp_clear_scheduled_hook('vmo_weekly_deep_research', [$roadmap_id, $user_id]);
        
        // Schedule based on plan frequency
        $interval = $config['cron_interval']; // 'weekly' or 'twicemonthly'
        $next_run = strtotime('+1 day 9:00 AM'); // Start tomorrow at 9 AM
        
        wp_schedule_event($next_run, $interval, 'vmo_weekly_deep_research', [$roadmap_id, $user_id]);
        
        error_log("VMO Background Ops: Deep research scheduled for user {$user_id} at {$config['frequency']} frequency");
        return true;
    }
    
    /**
     * Execute weekly deep research scan (called by WordPress cron)
     * v8.2.1: Now validates plan-based access before running
     */
    public function execute_weekly_deep_research($roadmap_id, $user_id) {
        global $wpdb;
        
        // ✅ PLAN CHECK v8.2.1: Verify user's plan supports background ops
        if (function_exists('vmo_can_run_background_ops') && !vmo_can_run_background_ops($user_id)) {
            error_log("VMO Weekly Research: User {$user_id} plan does not support background ops - skipping");
            if (function_exists('vmo_send_background_ops_upgrade_prompt')) {
                vmo_send_background_ops_upgrade_prompt($user_id, 'weekly_deep_research');
            }
            // Clear this schedule since user shouldn't have background ops
            wp_clear_scheduled_hook('vmo_weekly_deep_research', [$roadmap_id, $user_id]);
            return;
        }
        
        // ✅ TOKEN DEDUCTION v8.1.0: Charge for weekly deep research (4,000 tokens)
        // This is a recurring operation that provides ongoing business intelligence
        if (function_exists('vmo_deduct_tokens')) {
            $cost = defined('VMO_TOKENS_WEEKLY_DEEP_RESEARCH') ? VMO_TOKENS_WEEKLY_DEEP_RESEARCH : 4000;
            if (!vmo_deduct_tokens($user_id, $cost, 'weekly_deep_research', 'Weekly deep business research scan')) {
                error_log("VMO Weekly Research: Insufficient tokens for user {$user_id}");
                // Skip this week's research if user has insufficient tokens
                return;
            }
        }
        
        // Get roadmap data
        $roadmap = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}vmo_business_roadmaps WHERE roadmap_id = %s AND user_id = %d",
            $roadmap_id,
            $user_id
        ));
        
        if (!$roadmap) {
            return;
        }
        
        // Get current KPIs
        $kpis = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}vmo_business_kpis WHERE roadmap_id = %s",
            $roadmap_id
        ));
        
        // Get recent activities
        $activities = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM {$wpdb->prefix}vmo_daily_activities 
             WHERE roadmap_id = %s AND activity_date >= DATE_SUB(NOW(), INTERVAL 7 DAY)
             ORDER BY activity_date DESC",
            $roadmap_id
        ));
        
        // Perform deep research
        $research_results = $this->perform_weekly_deep_research_scan($roadmap, $kpis, $activities);
        
        // Save research results
        $this->save_deep_research_results($roadmap_id, $user_id, $research_results);
        
        // Apply findings to roadmap and KPIs
        $this->apply_research_findings_to_roadmap($roadmap_id, $user_id, $research_results);
        
        // Update next research date
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->update(
            $wpdb->prefix . 'vmo_business_roadmaps',
            [
                'last_deep_research_date' => current_time('mysql'),
                'next_deep_research_date' => date('Y-m-d H:i:s', strtotime('+7 days')),
                'research_findings_count' => $roadmap->research_findings_count + 1
            ],
            ['roadmap_id' => $roadmap_id]
        );
    }
    
    /**
     * Perform weekly deep research scan using OpenAI
     */
    private function perform_weekly_deep_research_scan($roadmap, $kpis, $activities) {
        $roadmap_data = json_decode($roadmap->roadmap_data, true);
        $current_week = ceil((strtotime('now') - strtotime($roadmap->roadmap_start_date)) / (7 * 24 * 60 * 60));
        
        // Get research topic for this week
        $research_topic = $roadmap_data['weekly_research_topics'][$current_week - 1]['topic'] ?? 'General business performance and market opportunities';
        
        // Build KPI summary
        $kpi_summary = '';
        foreach ($kpis as $kpi) {
            $kpi_summary .= "- {$kpi->kpi_name}: Current = {$kpi->current_value}, Target = {$kpi->target_value}, Trend = {$kpi->trend}
";
        }
        
        // Build activities summary
        $activities_summary = count($activities) . " activities in the past 7 days";
        $completed = 0;
        foreach ($activities as $activity) {
            if ($activity->status === 'completed') $completed++;
        }
        $activities_summary .= ", {$completed} completed";
        
        $prompt = "You are conducting a WEEKLY DEEP RESEARCH SCAN for a business using VMO AI Business Suite.

**CURRENT DATE:** " . date('Y-m-d') . "
**WEEK NUMBER:** {$current_week}
**RESEARCH FOCUS:** {$research_topic}

**BUSINESS VISION:**
{$roadmap->business_vision}

**CURRENT PROGRESS:**
- Overall Progress: {$roadmap->overall_progress}%
- Current Phase: {$roadmap->current_phase}

**CURRENT KPIs:**
{$kpi_summary}

**RECENT ACTIVITY:**
{$activities_summary}

**YOUR TASK:**
Conduct comprehensive research and provide:

1. **MARKET ANALYSIS:**
   - Current trends in the user's industry
   - Competitor movements and strategies
   - New opportunities identified

2. **PERFORMANCE ASSESSMENT:**
   - KPI progress evaluation (on track, at risk, or off track)
   - What's working well vs. what needs attention
   - Bottlenecks or obstacles identified

3. **STRATEGIC RECOMMENDATIONS:**
   - Specific actions to take this week
   - Roadmap adjustments needed (if any)
   - KPI target adjustments based on performance
   - New daily activities to add

4. **RISK & OPPORTUNITY ALERTS:**
   - Risks that need immediate attention
   - Opportunities to capitalize on
   - Competitive threats

5. **KPI PREDICTIONS:**
   - Projected KPI values for next 30 days based on current trajectory
   - Likelihood of hitting milestones

**OUTPUT FORMAT (JSON):**
```json
{
  \"research_summary\": \"Executive summary\",
  \"market_insights\": [
    {\"insight\": \"...\", \"impact\": \"high/medium/low\", \"action_required\": \"...\"}
  ],
  \"kpi_assessment\": [
    {\"kpi_name\": \"...\", \"status\": \"on_track/at_risk/off_track\", \"recommendation\": \"...\"}
  ],
  \"recommended_actions\": [
    {\"action\": \"...\", \"priority\": 1, \"estimated_impact\": \"...\"}
  ],
  \"roadmap_adjustments\": [
    {\"adjustment_type\": \"add_milestone/modify_date/add_activity\", \"details\": \"...\"}
  ],
  \"risks\": [{\"risk\": \"...\", \"severity\": \"high/medium/low\", \"mitigation\": \"...\"}],
  \"opportunities\": [{\"opportunity\": \"...\", \"potential_value\": \"...\", \"action_steps\": \"...\"}],
  \"kpi_predictions\": {
    \"30_day_forecast\": {\"kpi_name\": projected_value}
  }
}
```

Conduct this research NOW.";

        $results = $this->call_openai_deep_research($prompt, 'weekly_business_scan');
        if (is_array($results)) {
            return $results;
        }
        if (!is_string($results) || trim($results) === '') {
            return [];
        }

        $decoded_results = json_decode($results, true);
        return is_array($decoded_results) ? $decoded_results : [];
    }
    
    /**
     * Save deep research results to database
     */
    private function save_deep_research_results($roadmap_id, $user_id, $research_results) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'vmo_deep_research_results';
        
        $research_id = 'research_' . uniqid();
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->insert($table_name, [
            'research_id' => $research_id,
            'roadmap_id' => $roadmap_id,
            'user_id' => $user_id,
            'research_date' => current_time('mysql'),
            'research_type' => 'weekly_scan',
            'research_prompt' => 'Weekly deep research scan',
            'research_results' => json_encode($research_results),
            'key_findings' => $research_results['research_summary'] ?? '',
            'action_items' => json_encode($research_results['recommended_actions'] ?? []),
            'kpi_impacts' => json_encode($research_results['kpi_assessment'] ?? []),
            'roadmap_updates_suggested' => json_encode($research_results['roadmap_adjustments'] ?? []),
            'applied_to_roadmap' => 0,
            'created_at' => current_time('mysql')
        ]);
        
        return $research_id;
    }
    
    /**
     * Apply research findings to roadmap and KPIs
     */
    private function apply_research_findings_to_roadmap($roadmap_id, $user_id, $research_results) {
        // Update KPI statuses based on assessment
        if (!empty($research_results['kpi_assessment'])) {
            $this->update_kpi_statuses($roadmap_id, $research_results['kpi_assessment']);
        }
        
        // Add new activities based on recommendations
        if (!empty($research_results['recommended_actions'])) {
            $this->add_recommended_activities($roadmap_id, $user_id, $research_results['recommended_actions']);
        }
        
        // Apply roadmap adjustments if any
        if (!empty($research_results['roadmap_adjustments'])) {
            $this->apply_roadmap_adjustments($roadmap_id, $user_id, $research_results['roadmap_adjustments']);
        }
    }
    
    /**
     * Update KPI statuses based on deep research assessment
     */
    private function update_kpi_statuses($roadmap_id, $kpi_assessments) {
        global $wpdb;
        $kpis_table = $wpdb->prefix . 'vmo_business_kpis';
        
        foreach ($kpi_assessments as $assessment) {
            $wpdb->query($wpdb->prepare(
                "UPDATE {$kpis_table} SET status = %s WHERE roadmap_id = %s AND kpi_name LIKE %s",
                $assessment['status'],
                $roadmap_id,
                '%' . $assessment['kpi_name'] . '%'
            ));
        }
    }
    
    /**
     * Add recommended daily activities from research findings
     */
    private function add_recommended_activities($roadmap_id, $user_id, $recommended_actions) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'vmo_daily_activities';
        
        foreach ($recommended_actions as $action) {
            $activity_id = 'activity_' . uniqid();
            
            // Schedule for tomorrow or based on priority
            $days_offset = ($action['priority'] == 1) ? 1 : 3;
            $activity_date = date('Y-m-d', strtotime('+' . $days_offset . ' days'));
            
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->insert($table_name, [
                'activity_id' => $activity_id,
                'roadmap_id' => $roadmap_id,
                'user_id' => $user_id,
                'activity_date' => $activity_date,
                'activity_title' => $action['action'],
                'activity_description' => $action['estimated_impact'] ?? '',
                'activity_type' => 'strategic',
                'estimated_duration_minutes' => 90,
                'priority' => $action['priority'],
                'status' => 'pending',
                'ai_generated' => 1,
                'created_at' => current_time('mysql')
            ]);
        }
    }
    
    /**
     * Apply roadmap adjustments from research findings
     */
    private function apply_roadmap_adjustments($roadmap_id, $user_id, $adjustments) {
        foreach ($adjustments as $adjustment) {
            switch ($adjustment['adjustment_type']) {
                case 'add_milestone':
                    $this->add_milestone_from_research($roadmap_id, $user_id, $adjustment['details']);
                    break;
                case 'modify_date':
                    $this->modify_milestone_date($roadmap_id, $adjustment['details']);
                    break;
                case 'add_activity':
                    $this->add_activity_from_research($roadmap_id, $user_id, $adjustment['details']);
                    break;
            }
        }
    }
    
    /**
     * Add milestone from research findings
     */
    private function add_milestone_from_research($roadmap_id, $user_id, $details) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'vmo_roadmap_milestones';
        
        $milestone_id = 'milestone_' . uniqid();
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->insert($table_name, [
            'milestone_id' => $milestone_id,
            'roadmap_id' => $roadmap_id,
            'user_id' => $user_id,
            'phase_number' => $details['phase_number'] ?? 2,
            'phase_name' => $details['phase_name'] ?? 'Growth Phase',
            'milestone_title' => $details['title'] ?? 'Research-identified milestone',
            'milestone_description' => $details['description'] ?? '',
            'target_date' => $details['target_date'] ?? date('Y-m-d', strtotime('+60 days')),
            'status' => 'pending',
            'priority' => $details['priority'] ?? 2,
            'created_at' => current_time('mysql')
        ]);
    }
    
    /**
     * Modify milestone date based on research findings
     */
    private function modify_milestone_date($roadmap_id, $details) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'vmo_roadmap_milestones';
        
        $wpdb->query($wpdb->prepare(
            "UPDATE {$table_name} SET target_date = %s WHERE roadmap_id = %s AND milestone_title LIKE %s",
            $details['new_date'],
            $roadmap_id,
            '%' . $details['milestone_title'] . '%'
        ));
    }
    
    /**
     * Add activity from research findings
     */
    private function add_activity_from_research($roadmap_id, $user_id, $details) {
        global $wpdb;
        $table_name = $wpdb->prefix . 'vmo_daily_activities';
        
        $activity_id = 'activity_' . uniqid();
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->insert($table_name, [
            'activity_id' => $activity_id,
            'roadmap_id' => $roadmap_id,
            'user_id' => $user_id,
            'activity_date' => $details['date'] ?? date('Y-m-d', strtotime('+2 days')),
            'activity_title' => $details['title'] ?? 'Research-recommended activity',
            'activity_description' => $details['description'] ?? '',
            'activity_type' => 'research_recommended',
            'priority' => $details['priority'] ?? 2,
            'status' => 'pending',
            'ai_generated' => 1,
            'created_at' => current_time('mysql')
        ]);
    }
    
    private function generate_executive_summary($package, $responses) {
        $prompt = "Create a comprehensive executive summary of the entire business consultation, including key findings, recommendations, and next steps.

";
        $prompt .= "This should be suitable for business stakeholders and decision makers.";
        
        return $this->call_openai_api($prompt, 800);
    }
    
    private function save_voice_conversation($session_id, $transcript, $ai_response) {
        $user_id = get_current_user_id();
        $consultation = $this->consultation_db->get_consultation_by_session($session_id);
        
        if ($consultation) {
            $conversation_log = json_decode($consultation->conversation_log, true) ?: [];
            $conversation_log[] = array(
                'timestamp' => current_time('mysql'),
                'type' => 'user_voice',
                'content' => $transcript
            );
            $conversation_log[] = array(
                'timestamp' => current_time('mysql'),
                'type' => 'ai_voice',
                'content' => $ai_response
            );
            
            $this->consultation_db->save_consultation($user_id, $session_id, array(
                'conversation_log' => $conversation_log
            ));
        }
    }
    
    private function send_consultation_results_email($user_id, $business_package) {
        $user = get_userdata($user_id);
        if (!$user) return false;
        
        // Load glassy email templates
        require_once dirname(__FILE__) . '/class-vmo-glassy-email-templates.php';
        $glassy_templates = new VMO_Glassy_Email_Templates();
        
        // Prepare data for glassy template
        $data = [
            'business_name' => isset($business_package['business_overview']['business_name']) ? 
                $business_package['business_overview']['business_name'] : 'Your Business',
            'duration_minutes' => isset($business_package['consultation_duration']) ? 
                round($business_package['consultation_duration']) : 30,
            'recommendations' => isset($business_package['recommendations']) ? 
                count($business_package['recommendations']) : 10,
            'industry' => isset($business_package['business_overview']['industry']) ? 
                $business_package['business_overview']['industry'] : 'General',
            'consultation_date' => current_time('F j, Y'),
            'schedule_url' => admin_url('admin.php?page=vmo-ai-builder&tab=phone')
        ];
        
        $subject = "🎯 Business Strategy Session Summary - " . $data['business_name'];
        $message = $glassy_templates->get_consultation_complete_template($data);

        // Legacy cloud sender removed: route through local/SMTP mailer.
        require_once dirname(__FILE__) . '/class-vmo-email-router.php';
        $router = VMO_Email_Router::get_instance();
        $res = $router->send_email(
            $user->user_email,
            $subject,
            $message,
            'transactional',
            [ 'headers' => [ 'Content-Type: text/html; charset=UTF-8' ] ]
        );

        return is_array($res) && !empty($res['success']);
    }

    /**
     * Send congratulatory handoff email (Jerreme → Jarvis)
     * Notifies the user that consultation is complete and Jarvis
     * will now manage daily operations from the Strategic Dashboard.
     */
    private function send_handoff_email($user_id, $business_package) {
        $user = get_userdata($user_id);
        if (!$user || empty($user->user_email)) return false;

        $business_name = $business_package['business_overview']['business_name'] ?? 'Your Business';
        $first_name = $user->first_name ?: $user->display_name;
        $dashboard_url = admin_url('admin.php?page=vmo-ai-builder-main');

        $subject = "🎉 Congratulations, {$first_name}! Your business setup is complete";

        ob_start();
        ?>
<!DOCTYPE html>
<html><head><meta charset="UTF-8"><title>Setup Complete</title></head>
<body style="font-family:Arial,sans-serif;line-height:1.6;color:#333;background:#f4f4f4;margin:0;padding:20px;">
<div style="max-width:600px;margin:0 auto;background:#fff;border-radius:12px;overflow:hidden;box-shadow:0 2px 12px rgba(0,0,0,.08);">
    <div style="background:linear-gradient(135deg,#1a1a2e,#16213e);color:#00ffff;padding:30px;text-align:center;">
        <h1 style="margin:0;font-size:24px;">🎉 Congratulations!</h1>
        <p style="margin:10px 0 0;color:#ccffff;">Your business setup with Jerreme is complete</p>
    </div>
    <div style="padding:30px;">
        <p>Hi <?php echo esc_html($first_name); ?>,</p>
        <p>Great news! Your comprehensive business consultation for <strong><?php echo esc_html($business_name); ?></strong> is now complete. Here's what was set up for you:</p>
        <ul style="line-height:2;">
            <li>✅ Full business analysis & strategy</li>
            <li>✅ AI-generated website with all pages</li>
            <li>✅ Business roadmap with daily activities</li>
            <li>✅ SEO strategy & implementation plan</li>
            <li>✅ Marketing & growth recommendations</li>
            <li>✅ Business documents & legal framework</li>
        </ul>
        <div style="background:#f0f8ff;padding:20px;border-radius:8px;margin:20px 0;border-left:4px solid #00ffff;">
            <h3 style="margin:0 0 8px;color:#1a1a2e;">🤖 Meet Jarvis — Your Daily Operations Partner</h3>
            <p style="margin:0;">I'm handing you over to <strong>Jarvis</strong>, your AI operations assistant. From here, Jarvis will:</p>
            <ul style="margin:10px 0 0;padding-left:20px;">
                <li>Give you a daily operational briefing</li>
                <li>Track your business progress and KPIs</li>
                <li>Manage your tasks, calls, emails, and operations</li>
                <li>Provide strategic recommendations as your business grows</li>
            </ul>
        </div>
        <div style="text-align:center;margin:30px 0;">
            <a href="<?php echo esc_url($dashboard_url); ?>" style="background:#00ffff;color:#1a1a2e;padding:14px 30px;text-decoration:none;border-radius:8px;font-weight:bold;display:inline-block;font-size:16px;">
                🚀 Open Your Strategic Dashboard
            </a>
        </div>
        <p style="color:#666;font-size:13px;text-align:center;">— Jerreme, VMO Business Consultation AI</p>
    </div>
</div>
</body></html>
        <?php
        $message = ob_get_clean();

        require_once dirname(__FILE__) . '/class-vmo-email-router.php';
        $router = VMO_Email_Router::get_instance();
        $res = $router->send_email(
            $user->user_email,
            $subject,
            $message,
            'transactional',
            ['headers' => ['Content-Type: text/html; charset=UTF-8']]
        );

        return is_array($res) && !empty($res['success']);
    }
    
    /**
     * AJAX handler for enhanced business website generation
     */
    public function ajax_generate_business_website_enhanced() {
        $this->prepare_long_running_runtime();

        if (!wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['security'])), 'vmo_builder_nonce')) {
            wp_die('Security check failed');
        }
        
        try {
            $session_id = sanitize_text_field(wp_unslash($_POST['session_id']));
            $consultation_data = json_decode(wp_unslash($_POST['consultation_data']), true);
            
            // Get user ID
            $user_id = get_current_user_id();
            
            // ✅ TOKEN DEDUCTION v8.1.0: Charge for website generation (8,000 tokens)
            if (function_exists('vmo_deduct_tokens_for_operation')) {
                if (!vmo_deduct_tokens_for_operation($user_id, 'generate_website')) {
                    wp_send_json_error(array(
                        'message' => 'Insufficient tokens for website generation. Please top up your balance.',
                        'status' => 'insufficient_tokens',
                        'tokens_required' => function_exists('vmo_get_token_cost') ? vmo_get_token_cost('generate_website') : 8000
                    ));
                    return;
                }
            }
            
            if (!$consultation_data) {
                wp_send_json_error(array('message' => 'Invalid consultation data'));
                return;
            }

            if (class_exists('VMO_Canonical_Website_Pipeline')) {
                $pipeline_result = VMO_Canonical_Website_Pipeline::get_instance()->run_from_consultation_payload($session_id, $consultation_data);
                if (is_wp_error($pipeline_result)) {
                    wp_send_json_error(array('message' => $pipeline_result->get_error_message()));
                    return;
                }

                $website_draft = is_array($pipeline_result['website_draft'] ?? null) ? $pipeline_result['website_draft'] : array();
                $media_complete = !empty($pipeline_result['media_complete']);
                $preview_ready = !empty($pipeline_result['preview_ready']) && $media_complete;
                wp_send_json_success(array(
                    'message' => $pipeline_result['message'] ?? 'Website generation completed through the canonical pipeline.',
                    'business_package' => $pipeline_result['business_package'] ?? array(),
                    'website_draft' => $website_draft,
                    'draft_id' => $website_draft['post_id'] ?? ($pipeline_result['post_id'] ?? 0),
                    'preview_url' => $preview_ready ? ($website_draft['preview_url'] ?? ($pipeline_result['preview_url'] ?? '')) : '',
                    'preview_ready' => $preview_ready,
                    'media_complete' => $media_complete,
                    'final_qa_gate' => $pipeline_result['final_qa_gate'] ?? ($website_draft['final_qa_gate'] ?? array()),
                    'visual_preset' => $pipeline_result['visual_preset'] ?? ($website_draft['visual_preset'] ?? ''),
                    'consultation_complete' => $preview_ready,
                    'status' => $pipeline_result['status'] ?? ($preview_ready ? 'completed' : 'generating_media'),
                    'pages' => $pipeline_result['pages'] ?? array(),
                    'is_published' => false,
                    'has_unsaved_changes' => false,
                    'canonical_pipeline' => true,
                ));
                return;
            }

            wp_send_json_error(array('message' => 'Canonical website pipeline is required for website generation.'));
            return;
            
        } catch (\Throwable $e) {
            error_log('VMO Enhanced Business Generation Error: ' . $e->getMessage());
            wp_send_json_error(array('message' => 'Generation failed: ' . $e->getMessage()));
        }
    }

    /**
     * AJAX handler for generating a modern website preview (React/Tailwind/GSAP)
     */
    public function ajax_generate_website_preview() {
        $this->prepare_long_running_runtime();

        check_ajax_referer('vmo_builder_nonce', 'security');
        
        // Charge for website preview generation through the canonical pipeline.
        $user_id = get_current_user_id();
        if (function_exists('vmo_deduct_tokens')) {
            $token_cost = function_exists('vmo_get_token_cost') ? (int) vmo_get_token_cost('website_preview') : 5000;
            if (!vmo_deduct_tokens($user_id, $token_cost, 'website_preview', 'AI website preview generation')) {
                wp_send_json_error(array(
                    'message' => 'Insufficient tokens for website preview. Please top up your balance.',
                    'status' => 'insufficient_tokens'
                ));
                return;
            }
        }
        
        try {
            $session_id = sanitize_text_field(wp_unslash($_POST['session_id']));
            $consultation_data = json_decode(wp_unslash($_POST['consultation_data']), true);
            
            if (!$consultation_data) {
                wp_send_json_error(array('message' => 'Invalid consultation data'));
                return;
            }

            if (!class_exists('VMO_Canonical_Website_Pipeline')) {
                wp_send_json_error(array('message' => 'Canonical website pipeline is required for website preview generation.'), 500);
                return;
            }

            $consultation_data['source'] = 'enhanced_preview';
            $pipeline_result = VMO_Canonical_Website_Pipeline::get_instance()->run_from_consultation_payload($session_id, $consultation_data);
            if (is_wp_error($pipeline_result)) {
                wp_send_json_error(array('message' => $pipeline_result->get_error_message()));
                return;
            }

            $website_draft = is_array($pipeline_result['website_draft'] ?? null) ? $pipeline_result['website_draft'] : array();
            $html = (string) ($pipeline_result['content'] ?? $website_draft['homepage_html'] ?? $pipeline_result['homepage_html'] ?? '');
            $media_complete = !empty($pipeline_result['media_complete']);
            $preview_ready = !empty($pipeline_result['preview_ready']) && $media_complete;

            wp_send_json_success(array(
                'html' => $html,
                'website_draft' => $website_draft,
                'draft_id' => $website_draft['post_id'] ?? ($pipeline_result['post_id'] ?? 0),
                'preview_url' => $preview_ready ? ($website_draft['preview_url'] ?? ($pipeline_result['preview_url'] ?? '')) : '',
                'preview_ready' => $preview_ready,
                'media_complete' => $media_complete,
                'final_qa_gate' => $pipeline_result['final_qa_gate'] ?? ($website_draft['final_qa_gate'] ?? array()),
                'visual_preset' => $pipeline_result['visual_preset'] ?? ($website_draft['visual_preset'] ?? ''),
                'canonical_pipeline' => true,
                'generation_mode' => 'canonical_preview_request',
                'status' => $pipeline_result['status'] ?? ($preview_ready ? 'completed' : 'generating_media'),
            ));
            
        } catch (\Throwable $e) {
            wp_send_json_error(array('message' => 'Preview generation failed: ' . $e->getMessage()));
        }
    }
    
    /**
     * Classify consultation media by role for targeted website integration
     */
    private function classify_consultation_media($media_files) {
        $classified = array(
            'logo' => array(),
            'hero_images' => array(),
            'hero_videos' => array(),
            'product_images' => array(),
            'product_videos' => array(),
            'gallery_images' => array(),
            'team_photos' => array(),
            'brand_assets' => array(),
            'testimonial_media' => array(),
            'documents' => array(),
            'other' => array()
        );
        
        foreach ($media_files as $media) {
            $context = is_array($media['context']) ? $media['context'] : array();
            $roles = isset($context['roles']) ? $context['roles'] : array();
            $type = $media['type'] ?? '';
            $url = $media['url'] ?? '';
            $title = $media['title'] ?? '';
            
            if (empty($url)) continue;
            
            $media_info = array(
                'url' => $url,
                'title' => $title,
                'type' => $type,
                'alt' => $title
            );
            
            // Classify by user-assigned roles (from consultation)
            if (is_array($roles)) {
                if (in_array('logo', $roles) || in_array('brand', $roles)) {
                    $classified['logo'][] = $media_info;
                }
                if (in_array('hero', $roles) || in_array('banner', $roles)) {
                    if (strpos($type, 'video') !== false) {
                        $classified['hero_videos'][] = $media_info;
                    } else {
                        $classified['hero_images'][] = $media_info;
                    }
                }
                if (in_array('product', $roles) || in_array('service', $roles)) {
                    if (strpos($type, 'video') !== false) {
                        $classified['product_videos'][] = $media_info;
                    } else {
                        $classified['product_images'][] = $media_info;
                    }
                }
                if (in_array('gallery', $roles)) {
                    $classified['gallery_images'][] = $media_info;
                }
                if (in_array('team', $roles) || in_array('staff', $roles)) {
                    $classified['team_photos'][] = $media_info;
                }
                if (in_array('testimonial', $roles)) {
                    $classified['testimonial_media'][] = $media_info;
                }
            }
            
            // Auto-classify by filename/type if no roles assigned
            if (empty($roles)) {
                $lower_title = strtolower($title);
                
                if (strpos($lower_title, 'logo') !== false) {
                    $classified['logo'][] = $media_info;
                } elseif (strpos($lower_title, 'hero') !== false || strpos($lower_title, 'banner') !== false) {
                    $classified['hero_images'][] = $media_info;
                } elseif (strpos($lower_title, 'product') !== false || strpos($lower_title, 'service') !== false) {
                    $classified['product_images'][] = $media_info;
                } elseif (strpos($lower_title, 'team') !== false || strpos($lower_title, 'staff') !== false) {
                    $classified['team_photos'][] = $media_info;
                } elseif (strpos($type, 'document') !== false || strpos($type, 'pdf') !== false) {
                    $classified['documents'][] = $media_info;
                } else {
                    $classified['other'][] = $media_info;
                }
            }
        }
        
        return $classified;
    }

    /**
     * Build the rich business package from Phase 7 orchestration business data.
     */
    public function generate_comprehensive_business_package_from_business_data($business_data, $consultation_id = '', $research_data = array()) {
        $responses = $this->normalize_business_data_for_package_generation($business_data);
        $resolved_consultation_id = $consultation_id !== ''
            ? (string) $consultation_id
            : (string) ($business_data['session_id'] ?? '');

        return $this->generate_comprehensive_business_package(
            $responses,
            $resolved_consultation_id,
            is_array($research_data) ? $research_data : array()
        );
    }

    /**
     * Backward-compatible bridge for existing consultation integration callers.
     */
    public function generate_comprehensive_business_package_simple($business_data, $consultation_id = '', $research_data = array()) {
        return $this->generate_comprehensive_business_package_from_business_data($business_data, $consultation_id, $research_data);
    }

    /**
     * Normalize orchestration business data into the richer wizard response schema.
     */
    private function normalize_business_data_for_package_generation($business_data) {
        $business_data = is_array($business_data) ? $business_data : array();
        $consultation_snapshot = is_array($business_data['consultation_snapshot'] ?? null)
            ? $business_data['consultation_snapshot']
            : array();

        $responses = !empty($consultation_snapshot)
            ? $this->format_consultation_data_for_generation($consultation_snapshot)
            : array(
                'business_profile' => array(),
                'strategy' => array(),
                'conversation_highlights' => array(),
            );

        $normalize_list = function($value) {
            $items = array();

            if (is_array($value)) {
                array_walk_recursive($value, function($item) use (&$items) {
                    if (is_scalar($item)) {
                        $items[] = sanitize_text_field((string) $item);
                    }
                });
            } elseif (is_string($value)) {
                $items = preg_split('/[\r\n,]+/', $value);
            } elseif (is_scalar($value)) {
                $items = array((string) $value);
            }

            $normalized = array();
            $seen = array();
            foreach ($items as $item) {
                $text = trim((string) $item);
                if ($text === '') {
                    continue;
                }

                $key = strtolower($text);
                if (isset($seen[$key])) {
                    continue;
                }

                $seen[$key] = true;
                $normalized[] = $text;
            }

            return $normalized;
        };

        $merge_urls = function($existing, $incoming) {
            $normalized = array();
            $seen = array();

            foreach (array($existing, $incoming) as $candidate_set) {
                if (!is_array($candidate_set)) {
                    $candidate_set = array($candidate_set);
                }

                foreach ($candidate_set as $candidate) {
                    $url = esc_url_raw((string) $candidate);
                    if ($url === '') {
                        continue;
                    }

                    if (isset($seen[$url])) {
                        continue;
                    }

                    $seen[$url] = true;
                    $normalized[] = $url;
                }
            }

            return $normalized;
        };

        $services = $normalize_list($business_data['services'] ?? ($business_data['offerings'] ?? array()));
        $goals = $normalize_list($business_data['goals'] ?? ($business_data['business_goals'] ?? ''));
        $brand_values = $normalize_list($business_data['brand_values'] ?? ($business_data['style_preferences'] ?? ''));
        $custom_pages = $normalize_list($business_data['custom_pages'] ?? array());
        $special_features = $normalize_list($business_data['special_features'] ?? array());
        $target_audience_list = $normalize_list($business_data['target_audience'] ?? '');

        $business_name = sanitize_text_field((string) (
            $business_data['business_name']
            ?? $responses['business_name']
            ?? 'Your Business'
        ));
        $business_type = sanitize_text_field((string) (
            $business_data['business_type']
            ?? $responses['business_type']
            ?? ''
        ));
        $industry = sanitize_text_field((string) (
            $business_data['industry']
            ?? $responses['industry']
            ?? ''
        ));
        $location = sanitize_text_field((string) (
            $business_data['location']
            ?? $responses['location']
            ?? ''
        ));
        $target_audience = !empty($target_audience_list)
            ? implode(', ', $target_audience_list)
            : sanitize_text_field((string) (
                $business_data['target_audience']
                ?? $responses['target_audience']
                ?? ''
            ));
        $business_goals = !empty($goals)
            ? implode(', ', $goals)
            : sanitize_text_field((string) (
                $responses['business_goals']
                ?? ''
            ));

        $responses['business_name'] = $business_name !== '' ? $business_name : 'Your Business';
        $responses['business_type'] = $business_type !== '' ? $business_type : ($industry !== '' ? $industry : ($responses['business_type'] ?? 'General Business'));
        $responses['industry'] = $industry;
        $responses['location'] = $location;

        if ($target_audience !== '') {
            $responses['target_audience'] = $target_audience;
            $responses['target_customers'] = $target_audience;
        }

        if (!empty($services)) {
            $responses['offerings'] = $services;
            $responses['services'] = implode(', ', $services);
        }

        if ($business_goals !== '') {
            $responses['business_goals'] = $business_goals;
        }

        if (!empty($brand_values)) {
            $responses['style_preferences'] = implode(', ', $brand_values);
        }

        $brand = is_array($business_data['brand'] ?? null) ? $business_data['brand'] : array();
        $brand_palette = $normalize_list($brand['palette'] ?? array());
        if (!empty($brand_palette)) {
            $responses['color_preferences'] = implode(', ', $brand_palette);
        } elseif (!empty($brand['primary_color']) || !empty($brand['accent_color'])) {
            $responses['color_preferences'] = implode(', ', array_filter(array(
                sanitize_text_field((string) ($brand['primary_color'] ?? '')),
                sanitize_text_field((string) ($brand['accent_color'] ?? '')),
            )));
        }

        $features_needed = array_values(array_unique(array_merge($custom_pages, $special_features)));
        if (!empty($features_needed)) {
            $responses['features_needed'] = implode(', ', $features_needed);
        }

        $responses['inspiration_websites'] = $merge_urls(
            $responses['inspiration_websites'] ?? array(),
            $brand['reference_sites'] ?? array()
        );

        $uploaded_documents = is_array($business_data['uploaded_documents'] ?? null)
            ? $business_data['uploaded_documents']
            : (is_array($consultation_snapshot['documents'] ?? null) ? $consultation_snapshot['documents'] : array());
        if (!empty($uploaded_documents)) {
            $responses['uploaded_documents'] = $uploaded_documents;
        }

        if (!empty($business_data['uploaded_media']) && is_array($business_data['uploaded_media'])) {
            $responses['uploaded_media'] = $business_data['uploaded_media'];
        } elseif (!empty($consultation_snapshot['media_files']) && is_array($consultation_snapshot['media_files'])) {
            $responses['uploaded_media'] = $this->classify_consultation_media($consultation_snapshot['media_files']);
        }

        $responses['products_inventory'] = is_array($responses['products_inventory'] ?? null)
            ? $responses['products_inventory']
            : array();
        $responses['services_inventory'] = is_array($responses['services_inventory'] ?? null)
            ? $responses['services_inventory']
            : array();

        if (empty($responses['products_inventory']) && !empty($business_data['ecommerce']['products']) && is_array($business_data['ecommerce']['products'])) {
            $responses['products_inventory'] = $business_data['ecommerce']['products'];
        }
        if (empty($responses['services_inventory']) && !empty($business_data['services_inventory']) && is_array($business_data['services_inventory'])) {
            $responses['services_inventory'] = $business_data['services_inventory'];
        }

        $payment_collection = is_array($responses['payment_collection'] ?? null)
            ? $responses['payment_collection']
            : array(
                'collects_payments' => null,
                'mode' => null,
                'confirmed' => false,
            );
        $ecommerce = is_array($responses['ecommerce'] ?? null)
            ? $responses['ecommerce']
            : array();
        if (!empty($business_data['ecommerce']) && is_array($business_data['ecommerce'])) {
            $ecommerce = array_merge($ecommerce, $business_data['ecommerce']);
        }

        $collection_mode = strtolower(sanitize_text_field((string) (
            $ecommerce['collection_mode']
            ?? $payment_collection['mode']
            ?? ''
        )));
        $collects_payments = !empty($ecommerce['collect_payments'])
            || !empty($ecommerce['intends_to_sell'])
            || !empty($ecommerce['sell_on_website'])
            || !empty($payment_collection['collects_payments'])
            || in_array($collection_mode, array('products', 'donations'), true)
            || !empty($responses['products_inventory'])
            || !empty($responses['services_inventory']);

        if ($collection_mode === '') {
            $collection_mode = $collects_payments ? 'products' : 'none';
        }

        $payment_collection['collects_payments'] = $collects_payments;
        $payment_collection['mode'] = $collection_mode;
        $payment_collection['confirmed'] = !empty($payment_collection['confirmed']) || $collects_payments;

        $ecommerce['collection_mode'] = $collection_mode;
        $responses['payment_collection'] = $payment_collection;
        $responses['ecommerce'] = $ecommerce;
        $responses['needs'] = is_array($responses['needs'] ?? null) ? $responses['needs'] : array();
        $responses['needs']['ecommerce'] = !empty($responses['needs']['ecommerce']) || $collects_payments;

        $responses['business_profile'] = array_filter(array_merge(
            is_array($responses['business_profile'] ?? null) ? $responses['business_profile'] : array(),
            array(
                'Business Name' => $responses['business_name'],
                'Business Type' => $responses['business_type'],
                'Industry' => $industry,
                'Location' => $location,
                'Target Audience' => $responses['target_audience'] ?? '',
            )
        ));

        $responses['strategy'] = array_filter(array_merge(
            is_array($responses['strategy'] ?? null) ? $responses['strategy'] : array(),
            array(
                'Core Offerings' => $responses['services'] ?? '',
                'Primary Goals' => $responses['business_goals'] ?? '',
                'Website Style' => $responses['style_preferences'] ?? '',
                'Feature Priorities' => $responses['features_needed'] ?? '',
            )
        ));

        $responses['conversation_highlights'] = is_array($responses['conversation_highlights'] ?? null)
            ? $responses['conversation_highlights']
            : array();
        if (empty($responses['conversation_highlights']['User Priorities'])) {
            $responses['conversation_highlights']['User Priorities'] = $responses['business_goals']
                ?? ($responses['services'] ?? 'Create a premium website tailored to this business context.');
        }

        return $responses;
    }
    
    /**
     * Format consultation data for business package generation
     */
    private function format_consultation_data_for_generation($consultation_data) {
        $responses = array(
            'business_profile' => array(),
            'strategy' => array(),
            'conversation_highlights' => array()
        );

        if (!is_array($consultation_data)) {
            return $responses;
        }

        $extracted = is_array($consultation_data['extracted_information'] ?? null)
            ? $consultation_data['extracted_information']
            : array();

        $business_name = sanitize_text_field((string) (
            $extracted['businessName']
            ?? $extracted['business_name']
            ?? $consultation_data['business_name']
            ?? ''
        ));

        $business_type = sanitize_text_field((string) (
            $extracted['businessType']
            ?? $extracted['business_type']
            ?? $consultation_data['business_type']
            ?? ''
        ));

        $industry = sanitize_text_field((string) (
            $extracted['industry']
            ?? $consultation_data['industry']
            ?? ''
        ));

        $target_audience = sanitize_text_field((string) (
            $extracted['targetAudience']
            ?? $extracted['target_audience']
            ?? $consultation_data['target_audience']
            ?? ''
        ));

        $business_goals = sanitize_text_field((string) (
            $extracted['businessGoals']
            ?? $extracted['business_goals']
            ?? ''
        ));

        $location = sanitize_text_field((string) (
            $extracted['location']
            ?? $consultation_data['location']
            ?? ''
        ));

        $style_preferences = sanitize_text_field((string) (
            $extracted['websitePreferences']
            ?? $extracted['website_preferences']
            ?? ''
        ));

        $pricing = sanitize_text_field((string) (
            $extracted['pricing']
            ?? $consultation_data['pricing']
            ?? ''
        ));

        $color_preferences = sanitize_text_field((string) (
            $extracted['colorPreferences']
            ?? $extracted['color_preferences']
            ?? ''
        ));

        $offerings = array();
        if (is_array($extracted['offerings'] ?? null)) {
            $offerings = array_values(array_filter(array_map('sanitize_text_field', $extracted['offerings'])));
        }

        $custom_pages = array();
        if (is_array($extracted['customPages'] ?? null)) {
            $custom_pages = array_values(array_filter(array_map('sanitize_text_field', $extracted['customPages'])));
        }

        $payment_collection = is_array($extracted['payment_collection'] ?? null)
            ? $extracted['payment_collection']
            : array(
                'collects_payments' => null,
                'mode' => null,
                'confirmed' => false,
            );

        $ecommerce = is_array($extracted['ecommerce'] ?? null)
            ? $extracted['ecommerce']
            : array(
                'collection_mode' => null,
                'donation_funds' => array(),
            );

        $products_inventory = is_array($extracted['products_inventory'] ?? null)
            ? $extracted['products_inventory']
            : array();

        $services_inventory = is_array($extracted['services_inventory'] ?? null)
            ? $extracted['services_inventory']
            : array();

        $needs_ecommerce = !empty($extracted['needsEcommerce'])
            || !empty($extracted['ecommerce_needed'])
            || !empty($payment_collection['collects_payments'])
            || in_array(strtolower((string) ($payment_collection['mode'] ?? '')), array('products', 'donations'), true)
            || in_array(strtolower((string) ($ecommerce['collection_mode'] ?? '')), array('products', 'donations'), true);

        $inspiration_websites = array();
        if (is_array($extracted['inspirationWebsites'] ?? null)) {
            foreach ($extracted['inspirationWebsites'] as $url) {
                $url = esc_url_raw((string) $url);
                if (!empty($url)) {
                    $inspiration_websites[] = $url;
                }
            }
        }

        $responses['business_name'] = $business_name !== '' ? $business_name : 'Your Business';
        $responses['business_type'] = $business_type !== '' ? $business_type : ($industry !== '' ? $industry : 'General Business');
        $responses['industry'] = $industry;
        $responses['location'] = $location;
        $responses['target_audience'] = $target_audience;
        $responses['target_customers'] = $target_audience;
        $responses['business_goals'] = $business_goals;
        $responses['style_preferences'] = $style_preferences;
        $responses['pricing'] = $pricing;
        $responses['color_preferences'] = $color_preferences;
        $responses['features_needed'] = !empty($custom_pages) ? implode(', ', $custom_pages) : '';
        $responses['inspiration_websites'] = $inspiration_websites;
        $responses['offerings'] = $offerings;
        $responses['services'] = !empty($offerings) ? implode(', ', $offerings) : '';
        $responses['payment_collection'] = $payment_collection;
        $responses['ecommerce'] = $ecommerce;
        $responses['products_inventory'] = $products_inventory;
        $responses['services_inventory'] = $services_inventory;
        $responses['needs'] = array(
            'ecommerce' => (bool) $needs_ecommerce,
        );

        $responses['business_profile'] = array_filter(array(
            'Business Name' => $responses['business_name'],
            'Business Type' => $responses['business_type'],
            'Industry' => $industry,
            'Location' => $location,
            'Target Audience' => $target_audience
        ));

        $responses['strategy'] = array_filter(array(
            'Core Offerings' => !empty($offerings) ? implode(', ', $offerings) : '',
            'Primary Goals' => $business_goals,
            'Pricing' => $pricing,
            'Website Style' => $style_preferences,
            'Feature Priorities' => !empty($custom_pages) ? implode(', ', $custom_pages) : ''
        ));

        $user_messages = array();
        foreach ($consultation_data as $item) {
            if (is_array($item) && isset($item['type']) && $item['type'] === 'user' && !empty($item['content'])) {
                $user_messages[] = sanitize_text_field((string) $item['content']);
            }
        }

        if (!empty($user_messages)) {
            $responses['conversation_highlights']['User Priorities'] = implode(' | ', array_slice($user_messages, 0, 8));
        } elseif (!empty($business_goals)) {
            $responses['conversation_highlights']['User Priorities'] = $business_goals;
        } else {
            $responses['conversation_highlights']['User Priorities'] = 'Create a premium website tailored to this business context.';
        }
        
        return $responses;
    }
    
    /**
     * Generate complete website from business package
     */
    private function generate_complete_website_from_package($business_package, $responses) {
        if (!class_exists('VMO_Canonical_Website_Pipeline')) {
            return new WP_Error('vmo_canonical_pipeline_missing', 'Canonical website pipeline is required for website generation.');
        }

        $payload = array(
            'source' => 'enhanced_business_wizard_package',
            'business_package' => is_array($business_package) ? $business_package : array(),
            'responses' => is_array($responses) ? $responses : array(),
            'prompt' => 'Generate the complete website from the enhanced business package through the v2.0 canonical website pipeline.',
        );

        $session_id = sanitize_text_field((string) ($responses['session_id'] ?? $business_package['session_id'] ?? ''));
        if ($session_id === '') {
            $session_id = 'vmo_enhanced_package_' . wp_generate_uuid4();
        }

        return VMO_Canonical_Website_Pipeline::get_instance()->run_from_consultation_payload($session_id, $payload);
    }
    
    /**
     * Build comprehensive website generation prompt
     */
    private function build_website_generation_prompt($business_package, $responses) {
        $prompt = "Generate a complete, professional business website based on comprehensive AI business intelligence analysis.

";
        
        // Add business analysis context
        $prompt .= "BUSINESS ANALYSIS:
" . ($business_package['business_analysis'] ?? '') . "

";
        
        // Add website architecture guidance
        $prompt .= "WEBSITE ARCHITECTURE PLAN:
" . ($business_package['website_architecture'] ?? '') . "

";
        
        // Add SEO strategy integration
        $prompt .= "SEO STRATEGY:
" . ($business_package['seo_strategy'] ?? '') . "

";
        
        // Add marketing strategy
        $prompt .= "MARKETING STRATEGY:
" . ($business_package['marketing_strategy'] ?? '') . "

";
        
        // Add phone service integration if provisioned
        $phone_info = $this->get_phone_integration_details($business_package);
        if (!empty($phone_info)) {
            $prompt .= "PHONE SERVICE INTEGRATION:
" . $phone_info . "

";
        }
        
        $prompt .= "Generate a complete, professional HTML website that implements ALL the strategic recommendations above. The website should be:
";
        $prompt .= "- Fully responsive and modern
";
        $prompt .= "- SEO-optimized based on the strategy
";
        $prompt .= "- Conversion-focused based on marketing recommendations
";
        $prompt .= "- Professional and industry-appropriate
";
        $prompt .= "- Complete with all necessary pages and sections
";
        
        // Add phone integration requirements if phone service is active
        if (!empty($phone_info)) {
            $prompt .= "- Phone number prominently displayed in header, footer, and contact sections
";
            $prompt .= "- Click-to-call functionality on all phone numbers (use tel: links)
";
            $prompt .= "- 'Call Now' buttons prominently placed for conversions
";
            $prompt .= "- Phone number mentioned in compelling call-to-action text
";
            $prompt .= "- Mobile-optimized phone calling experience
";
        }
        
        $prompt .= "
Return complete, production-ready HTML/CSS that can be immediately deployed.";
        
        return $prompt;
    }
    
    /**
     * Get phone integration details for website generation
     */
    private function get_phone_integration_details($business_package) {
        $phone_provisioned = $business_package['phone_service_provisioned'] ?? array();
        
        if (empty($phone_provisioned['provisioned']) || !$phone_provisioned['provisioned']) {
            return '';
        }
        
        $phone_number = $phone_provisioned['phone_number'] ?? '';
        if (empty($phone_number)) {
            return '';
        }
        
        // Get all phone numbers for this user
        $user_id = get_current_user_id();
        $twilio_client = new VMO_Twilio_Enhanced_Client();
        $all_phone_numbers = $twilio_client->get_all_active_phone_numbers($user_id);
        
        // Use primary number for main integration, but show all numbers are available
        $primary_phone = $phone_number;
        $formatted_phone = $this->format_phone_for_display($primary_phone);
        
        $integration_details = "PRIMARY BUSINESS PHONE: {$primary_phone}
";
        $integration_details .= "FORMATTED DISPLAY: {$formatted_phone}
";
        $integration_details .= "CLICK-TO-CALL LINK: tel:{$primary_phone}
";
        
        if (count($all_phone_numbers) > 1) {
            $integration_details .= "ADDITIONAL NUMBERS AVAILABLE: " . implode(', ', array_slice($all_phone_numbers, 1)) . "
";
        }
        
        $integration_details .= "
PHONE INTEGRATION REQUIREMENTS:
";
        $integration_details .= "- Display the primary phone number prominently in the website header
";
        $integration_details .= "- Add the phone number to the footer contact information
";
        $integration_details .= "- Include a 'Call Now: {$formatted_phone}' button in the hero section
";
        $integration_details .= "- Make ALL phone numbers clickable with tel: links for mobile users
";
        $integration_details .= "- Add phone number to contact page with click-to-call functionality
";
        $integration_details .= "- Include compelling call-to-action text mentioning 24/7 availability
";
        $integration_details .= "- Style phone numbers to stand out visually (larger font, contrasting color)
";
        $integration_details .= "- Add phone icon next to phone numbers for better UX
";
        $integration_details .= "- Ensure phone buttons are touch-friendly on mobile devices
";
        $integration_details .= "- Include 'Call for immediate assistance' or similar compelling text

";
        
        $integration_details .= "PHONE NUMBER PLACEMENT:
";
        $integration_details .= "- Header: Top-right corner with phone icon
";
        $integration_details .= "- Hero Section: Large 'Call Now' button
";
        $integration_details .= "- Services Section: 'Call to learn more' links
";
        $integration_details .= "- Contact Section: Primary contact method
";
        $integration_details .= "- Footer: Always visible contact information
";
        $integration_details .= "- About Page: Contact information
";
        $integration_details .= "- Services Page: Call-to-action for inquiries

";
        
        $integration_details .= "CSS STYLING REQUIREMENTS:
";
        $integration_details .= "- Phone numbers should be styled with larger font-size (18px or larger)
";
        $integration_details .= "- Use contrasting colors (bright blue, green, or business brand color)
";
        $integration_details .= "- Add phone icon (📞 or FontAwesome phone icon) next to numbers
";
        $integration_details .= "- Call buttons should have hover effects and be touch-friendly (min 44px height)
";
        $integration_details .= "- Use CSS like: .phone-link { color: #00b4d8; font-weight: bold; text-decoration: none; }
";
        $integration_details .= "- Call Now buttons should stand out with gradient backgrounds or solid colors
";
        $integration_details .= "- Mobile responsive design for touch devices

";
        
        $integration_details .= "HTML STRUCTURE REQUIREMENTS:
";
        $integration_details .= "- Use <a href=\"tel:{$primary_phone}\" class=\"phone-link\">{$formatted_phone}</a> for all phone numbers
";
        $integration_details .= "- Add phone icon: <span class=\"phone-icon\">📞</span> before phone numbers
";
        $integration_details .= "- Create prominent call buttons: <a href=\"tel:{$primary_phone}\" class=\"call-btn\">Call Now: {$formatted_phone}</a>
";
        $integration_details .= "- Include compelling text like 'Call for immediate assistance' or '24/7 customer support'

";
        
        return $integration_details;
    }
    
    /**
     * Format phone number for attractive display
     */
    private function format_phone_for_display($phone_number) {
        // Remove any non-numeric characters
        $cleaned = preg_replace('/[^0-9]/', '', $phone_number);
        
        // Handle US phone numbers
        if (strlen($cleaned) === 11 && substr($cleaned, 0, 1) === '1') {
            $cleaned = substr($cleaned, 1);
        }
        
        if (strlen($cleaned) === 10) {
            return sprintf('(%s) %s-%s', 
                substr($cleaned, 0, 3),
                substr($cleaned, 3, 3),
                substr($cleaned, 6, 4)
            );
        }
        
        return $phone_number;
    }
    
    /**
     * Generate additional business pages
     */
    private function generate_additional_business_pages($business_package, $responses) {
        $pages = array();
        
        // Get phone integration details for all pages
        $phone_info = $this->get_phone_integration_details($business_package);
        $phone_context = !empty($phone_info) ? "

PHONE INTEGRATION:
" . $phone_info : '';
        
        // Generate About Us page
        $about_prompt = "Generate a professional About Us page based on: " . ($business_package['business_analysis'] ?? '');
        $about_prompt .= $phone_context;
        if (!empty($phone_context)) {
            $about_prompt .= "

Include the business phone number in the contact information section with click-to-call functionality.";
        }
        $pages['about'] = $this->call_openai_api($about_prompt, 800);
        
        // Generate Services/Products page
        $services_prompt = "Generate a Services/Products page based on: " . ($business_package['marketing_strategy'] ?? '');
        $services_prompt .= $phone_context;
        if (!empty($phone_context)) {
            $services_prompt .= "

Include 'Call for more information' buttons with the phone number for each service/product section.";
        }
        $pages['services'] = $this->call_openai_api($services_prompt, 800);
        
        // Generate Contact page
        $contact_prompt = "Generate a professional Contact page with lead capture forms based on: " . ($business_package['operations_plan'] ?? '');
        $contact_prompt .= $phone_context;
        if (!empty($phone_context)) {
            $contact_prompt .= "

Make the phone number the PRIMARY contact method with prominent 'Call Now' button and mention 24/7 availability.";
        }
        $pages['contact'] = $this->call_openai_api($contact_prompt, 600);
        
        return $pages;
    }
    
    /**
     * Post-process website content to ensure proper phone integration
     */
    private function post_process_website_phone_integration($html_content, $business_package) {
        $phone_provisioned = $business_package['phone_service_provisioned'] ?? array();
        
        if (empty($phone_provisioned['provisioned']) || !$phone_provisioned['provisioned']) {
            return $html_content;
        }
        
        $phone_number = $phone_provisioned['phone_number'] ?? '';
        if (empty($phone_number)) {
            return $html_content;
        }
        
        $formatted_phone = $this->format_phone_for_display($phone_number);
        
        // Ensure all phone numbers are wrapped in tel: links
        $processed_content = $html_content;
        
        // Pattern to find phone numbers that aren't already in tel: links
        $phone_patterns = array(
            $formatted_phone,
            $phone_number,
            preg_replace('/[^0-9]/', '', $phone_number)
        );
        
        foreach ($phone_patterns as $pattern) {
            if (empty($pattern)) continue;
            
            // Don't replace if already in a tel: link
            $processed_content = preg_replace(
                '/(?<!href="tel:)(?<!href="tel:\+1)' . preg_quote($pattern, '/') . '(?![^<]*<\/a>)/',
                '<a href="tel:' . $phone_number . '" class="phone-link">' . $formatted_phone . '</a>',
                $processed_content
            );
        }
        
        // Add click-to-call CSS if not present
        if (strpos($processed_content, '.phone-link') === false && strpos($processed_content, '<style>') !== false) {
            $phone_css = "
.phone-link {
    color: #00b4d8;
    font-weight: bold;
    text-decoration: none;
    font-size: 1.1em;
}

.phone-link:hover {
    color: #0077b6;
    text-decoration: underline;
}

.call-btn {
    background: linear-gradient(135deg, #00b4d8 0%, #0077b6 100%);
    color: white;
    padding: 12px 24px;
    text-decoration: none;
    border-radius: 6px;
    font-weight: bold;
    display: inline-block;
    margin: 10px 0;
    min-height: 44px;
    line-height: 20px;
}

.call-btn:hover {
    background: linear-gradient(135deg, #0077b6 0%, #005577 100%);
    color: white;
    text-decoration: none;
}

@media (max-width: 768px) {
    .phone-link {
        font-size: 1.2em;
    }
    
    .call-btn {
        padding: 15px 25px;
        font-size: 1.1em;
    }
}";
            
            // Insert CSS before closing </style> tag
            $processed_content = preg_replace('/(<\/style>)/', $phone_css . "
$1", $processed_content, 1);
        }
        
        return $processed_content;
    }
    
    /**
     * Post-process website content to apply SEO optimizations from competitive analysis
     */
    private function post_process_website_seo_optimization($html_content, $business_package) {
        $seo_provisioned = $business_package['seo_service_provisioned'] ?? array();
        
        if (empty($seo_provisioned['provisioned']) || !$seo_provisioned['provisioned']) {
            return $html_content;
        }
        
        $competitive_analysis = $seo_provisioned['competitive_analysis'] ?? array();
        $seo_strategy = $seo_provisioned['seo_strategy'] ?? array();
        
        $processed_content = $html_content;
        
        // 1. Apply meta tag optimizations based on competitive analysis
        $processed_content = $this->apply_competitive_meta_tags($processed_content, $competitive_analysis, $seo_strategy);
        
        // 2. Add local SEO schema markup
        $processed_content = $this->add_competitive_schema_markup($processed_content, $competitive_analysis);
        
        // 3. Optimize content with target keywords from competitive analysis
        $processed_content = $this->apply_competitive_keyword_optimization($processed_content, $competitive_analysis, $seo_strategy);
        
        // 4. Add structured data for better search visibility
        $processed_content = $this->add_business_structured_data($processed_content, $business_package);
        
        // 5. Add competitive advantage elements
        $processed_content = $this->add_competitive_advantage_elements($processed_content, $competitive_analysis);
        
        // 6. Ensure mobile and page speed optimizations
        $processed_content = $this->apply_technical_seo_optimizations($processed_content);
        
        return $processed_content;
    }
    
    /**
     * Apply meta tags optimized against competitors
     */
    private function apply_competitive_meta_tags($html_content, $competitive_analysis, $seo_strategy) {
        // Extract primary keywords from competitive analysis
        $primary_keywords = $competitive_analysis['keyword_opportunities'] ?? array();
        
        if (empty($primary_keywords) && isset($seo_strategy['primary_keywords'])) {
            $primary_keywords = $seo_strategy['primary_keywords'];
        }
        
        if (empty($primary_keywords)) {
            return $html_content;
        }
        
        // Generate competitive meta description
        $meta_description = $this->generate_competitive_meta_description($primary_keywords, $competitive_analysis);
        
        // Generate competitive title optimization
        $competitive_title = $this->generate_competitive_title($primary_keywords, $competitive_analysis);
        
        $processed_content = $html_content;
        
        // Add or update meta description
        if (!empty($meta_description)) {
            if (preg_match('/<meta\s+name=["\']description["\'][^>]*>/i', $processed_content)) {
                $processed_content = preg_replace(
                    '/<meta\s+name=["\']description["\'][^>]*>/i',
                    '<meta name="description" content="' . htmlspecialchars($meta_description) . '">',
                    $processed_content
                );
            } else {
                // Add meta description if not present
                $processed_content = preg_replace(
                    '/(<head[^>]*>)/i',
                    '$1' . "
" . '<meta name="description" content="' . htmlspecialchars($meta_description) . '">',
                    $processed_content
                );
            }
        }
        
        // Update title if found and competitive title available
        if (!empty($competitive_title) && preg_match('/<title[^>]*>([^<]*)<\/title>/i', $processed_content, $matches)) {
            $current_title = $matches[1];
            // Enhance existing title with competitive keywords
            $enhanced_title = $competitive_title . ' | ' . $current_title;
            $processed_content = preg_replace(
                '/<title[^>]*>[^<]*<\/title>/i',
                '<title>' . htmlspecialchars($enhanced_title) . '</title>',
                $processed_content
            );
        }
        
        return $processed_content;
    }
    
    /**
     * Add schema markup based on competitive analysis
     */
    private function add_competitive_schema_markup($html_content, $competitive_analysis) {
        // Generate local business schema
        $schema_markup = $this->generate_local_business_schema($competitive_analysis);
        
        if (empty($schema_markup)) {
            return $html_content;
        }
        
        // Add schema markup before closing </head> tag
        $processed_content = preg_replace(
            '/(<\/head>)/i',
            $schema_markup . "
" . '$1',
            $html_content
        );
        
        return $processed_content;
    }
    
    /**
     * Apply keyword optimization based on competitive gaps
     */
    private function apply_competitive_keyword_optimization($html_content, $competitive_analysis, $seo_strategy) {
        $content_gaps = $competitive_analysis['content_gaps'] ?? array();
        $keyword_opportunities = $competitive_analysis['keyword_opportunities'] ?? array();
        
        if (empty($content_gaps) && empty($keyword_opportunities)) {
            return $html_content;
        }
        
        $processed_content = $html_content;
        
        // Add keyword-rich content sections that competitors are missing
        foreach ($content_gaps as $gap) {
            if (strlen($gap) > 10) { // Only meaningful gaps
                $gap_content = $this->generate_content_for_gap($gap, $keyword_opportunities);
                if (!empty($gap_content)) {
                    // Insert before closing </main> or </body> tag
                    $processed_content = preg_replace(
                        '/(<\/(?:main|body)>)/i',
                        $gap_content . "
" . '$1',
                        $processed_content,
                        1
                    );
                }
            }
        }
        
        return $processed_content;
    }
    
    /**
     * Add business structured data
     */
    private function add_business_structured_data($html_content, $business_package) {
        // Extract business information
        $business_analysis = $business_package['business_analysis'] ?? '';
        $phone_provisioned = $business_package['phone_service_provisioned'] ?? array();
        
        // Generate structured data
        $structured_data = array(
            '@context' => 'https://schema.org',
            '@type' => 'LocalBusiness',
            'name' => $this->extract_business_name_from_analysis($business_analysis),
            'description' => $this->extract_business_description_from_analysis($business_analysis),
            'url' => home_url(),
        );
        
        // Add phone number if available
        if (!empty($phone_provisioned['phone_number'])) {
            $structured_data['telephone'] = $phone_provisioned['phone_number'];
        }
        
        $schema_script = '<script type="application/ld+json">' . json_encode($structured_data, JSON_PRETTY_PRINT) . '</script>';
        
        // Add before closing </head> tag
        $processed_content = preg_replace(
            '/(<\/head>)/i',
            $schema_script . "
" . '$1',
            $html_content
        );
        
        return $processed_content;
    }
    
    /**
     * Add competitive advantage elements to content
     */
    private function add_competitive_advantage_elements($html_content, $competitive_analysis) {
        $local_opportunities = $competitive_analysis['local_seo_opportunities'] ?? array();
        
        if (empty($local_opportunities)) {
            return $html_content;
        }
        
        // Generate competitive advantage section
        $advantage_content = '<section class="competitive-advantages" style="padding: 40px 20px; background: #f8f9fa; margin: 20px 0;">';
        $advantage_content .= '<div class="container">';
        $advantage_content .= '<h2 style="text-align: center; color: #333; margin-bottom: 30px;">Why Choose Us Over Competitors</h2>';
        $advantage_content .= '<div class="advantages-grid" style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px;">';
        
        foreach (array_slice($local_opportunities, 0, 4) as $opportunity) {
            $advantage_content .= '<div class="advantage-item" style="padding: 20px; background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);">';
            $advantage_content .= '<h3 style="color: #007cba; margin-bottom: 10px;">Competitive Edge</h3>';
            $advantage_content .= '<p style="color: #555; line-height: 1.6;">' . htmlspecialchars($opportunity) . '</p>';
            $advantage_content .= '</div>';
        }
        
        $advantage_content .= '</div></div></section>';
        
        // Insert before closing </main> or </body> tag
        $processed_content = preg_replace(
            '/(<\/(?:main|body)>)/i',
            $advantage_content . "
" . '$1',
            $html_content,
            1
        );
        
        return $processed_content;
    }
    
    /**
     * Apply technical SEO optimizations
     */
    private function apply_technical_seo_optimizations($html_content) {
        $processed_content = $html_content;
        
        // Add viewport meta tag if not present
        if (strpos($processed_content, 'viewport') === false) {
            $processed_content = preg_replace(
                '/(<head[^>]*>)/i',
                '$1' . "
" . '<meta name="viewport" content="width=device-width, initial-scale=1.0">',
                $processed_content
            );
        }
        
        // Add performance optimizations
        $performance_meta = '
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="robots" content="index,follow">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>';
        
        $processed_content = preg_replace(
            '/(<\/head>)/i',
            $performance_meta . "
" . '$1',
            $processed_content
        );
        
        return $processed_content;
    }
    
    // ========================
    // SEO PROCESSING HELPER FUNCTIONS  
    // ========================
    
    /**
     * Generate competitive meta description
     */
    private function generate_competitive_meta_description($keywords, $competitive_analysis) {
        if (empty($keywords)) {
            return '';
        }

        $primary_keyword = is_array($keywords) ? trim((string)$keywords[0]) : trim((string)$keywords);
        $location = $this->extract_location_from_analysis($competitive_analysis);

        $differentiator = '';
        if (!empty($competitive_analysis['content_gaps']) && is_array($competitive_analysis['content_gaps'])) {
            $differentiator = (string)($competitive_analysis['content_gaps'][0] ?? '');
        }
        if ($differentiator === '' && !empty($competitive_analysis['summary'])) {
            $differentiator = (string)$competitive_analysis['summary'];
        }
        if ($differentiator === '' && !empty($competitive_analysis['raw_analysis'])) {
            $differentiator = (string)$competitive_analysis['raw_analysis'];
        }

        $parts = array_filter([
            $primary_keyword !== '' ? 'Leading ' . $primary_keyword . ($location ? ' in ' . $location : '') : '',
            $differentiator !== '' ? wp_strip_all_tags(mb_substr($differentiator, 0, 90)) : ''
        ]);

        if (empty($parts)) {
            $parts[] = 'Strategy-driven growth and measurable outcomes for modern businesses.';
        }

        return mb_substr(implode(' — ', $parts), 0, 155);
    }

    /**
     * Generate competitive page title
     */
    private function generate_competitive_title($keywords, $competitive_analysis) {
        if (empty($keywords)) {
            return '';
        }

        $primary_keyword = is_array($keywords) ? trim((string)$keywords[0]) : trim((string)$keywords);
        $location = $this->extract_location_from_analysis($competitive_analysis);
        $audience = '';
        if (!empty($competitive_analysis['target_audience'])) {
            $audience = is_array($competitive_analysis['target_audience'])
                ? implode(', ', array_slice(array_filter(array_map('trim', $competitive_analysis['target_audience'])), 0, 2))
                : (string)$competitive_analysis['target_audience'];
        }

        $title_parts = array_filter([
            $primary_keyword !== '' ? ucfirst($primary_keyword) : '',
            $location,
            $audience !== '' ? 'Trusted by ' . $audience : ''
        ]);

        if (empty($title_parts)) {
            $title_parts[] = 'Custom Business Growth Strategy';
        }

        return mb_substr(implode(' | ', $title_parts), 0, 60);
    }

    /**
     * Build LocalBusiness schema markup
     */
    private function generate_local_business_schema($competitive_analysis) {
        $raw_analysis = is_array($competitive_analysis) ? ($competitive_analysis['raw_analysis'] ?? '') : '';
        $location = $this->extract_location_from_analysis($competitive_analysis);
        $business_name = $this->extract_business_name_from_analysis($raw_analysis);
        $business_description = $this->extract_business_description_from_analysis($raw_analysis);

        if ($business_name === '' && $business_description === '' && $location === '') {
            return '';
        }

        $schema = array(
            '@context' => 'https://schema.org',
            '@type' => 'LocalBusiness',
            'name' => $business_name ?: get_bloginfo('name'),
            'description' => $business_description,
            'url' => home_url(),
        );

        if ($location !== '') {
            $schema['address'] = array(
                '@type' => 'PostalAddress',
                'addressLocality' => $location,
            );
        }

        return '<script type="application/ld+json">' . wp_json_encode($schema, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . '</script>';
    }
    
    /**
     * Generate content for identified gaps
     */
    private function generate_content_for_gap($gap, $keywords) {
        $keyword_context = is_array($keywords) && !empty($keywords) ? $keywords[0] : '';
        
        $gap_content = '<section class="content-gap-fill" style="padding: 30px 20px; margin: 20px 0; background: #f9f9f9;">';
        $gap_content .= '<div class="container">';
        $gap_content .= '<h2 style="color: #333; margin-bottom: 20px;">' . htmlspecialchars($gap) . '</h2>';
        $gap_content .= '<p style="color: #555; line-height: 1.6;">We specialize in providing solutions that our competitors overlook. ';
        
        if (!empty($keyword_context)) {
            $gap_content .= 'Our expertise in ' . htmlspecialchars($keyword_context) . ' ensures you get the best results. ';
        }
        
        $gap_content .= 'Contact us to learn how we can help you succeed where others fall short.</p>';
        $gap_content .= '</div></section>';
        
        return $gap_content;
    }
    
    /**
     * Extract location from competitive analysis
     */
    private function extract_location_from_analysis($competitive_analysis) {
        $raw_analysis = $competitive_analysis['raw_analysis'] ?? '';
        
        // Look for location patterns
        if (preg_match('/(?:in|location|area|city):\s*([^.
]+)/i', $raw_analysis, $matches)) {
            return trim($matches[1]);
        }
        
        return '';
    }
    
    /**
     * Extract business name from analysis
     */
    private function extract_business_name_from_analysis($business_analysis) {
        // Look for business name patterns
        if (preg_match('/(?:business name|company name):\s*([^.
]+)/i', $business_analysis, $matches)) {
            return trim($matches[1]);
        }
        
        return get_bloginfo('name');
    }
    
    /**
     * Extract business description from analysis
     */
    private function extract_business_description_from_analysis($business_analysis) {
        // Look for business description patterns
        if (preg_match('/(?:description|about|overview):\s*([^.
]{50,200})/i', $business_analysis, $matches)) {
            return trim($matches[1]);
        }
        
        return 'Professional business services with proven expertise and customer satisfaction.';
    }
    
    /**
     * Save generation results to database
     */
    public function persist_generation_results_from_business_data($business_data, $business_package, $website_result, $session_id = '') {
        $business_data = is_array($business_data) ? $business_data : array();
        $business_package = is_array($business_package) ? $business_package : array();
        $website_result = is_array($website_result) ? $website_result : array();

        $user_id = (int) ($business_data['user_id'] ?? get_current_user_id());
        $resolved_session_id = $session_id !== ''
            ? (string) $session_id
            : (string) ($business_data['session_id'] ?? '');

        if ($user_id <= 0 || $resolved_session_id === '') {
            return false;
        }

        $responses = $this->normalize_business_data_for_package_generation($business_data);

        if (empty($website_result['pages_created']) && !empty($website_result['pages_map']) && is_array($website_result['pages_map'])) {
            $website_result['pages_created'] = array_keys($website_result['pages_map']);
        }

        if (empty($website_result['payment_runtime']) || !is_array($website_result['payment_runtime'])) {
            $website_result['payment_runtime'] = $this->derive_payment_runtime_snapshot($responses, $website_result);
        }

        if (empty($business_package['payment_runtime']) && !empty($website_result['payment_runtime'])) {
            $business_package['payment_runtime'] = $website_result['payment_runtime'];
        }

        $this->save_generation_results(
            $user_id,
            $resolved_session_id,
            $business_package,
            $website_result,
            $responses
        );

        return true;
    }

    private function save_generation_results($user_id, $session_id, $business_package, $website_result, $business_intelligence = array()) {
        global $wpdb;
        // Align persistence with vmo_consultations used throughout the wizard
        $table_name = $wpdb->prefix . 'vmo_consultations';

        $operating_state = $this->build_business_operating_state_snapshot(
            $user_id,
            $session_id,
            is_array($business_package) ? $business_package : array(),
            is_array($website_result) ? $website_result : array(),
            is_array($business_intelligence) ? $business_intelligence : array()
        );

        $website_state = is_array($operating_state['website'] ?? null) ? $operating_state['website'] : array();
        $draft_assembled = !empty($website_state['draft_assembled']);
        $media_complete = !empty($website_state['media_complete']);
        $preview_ready = !empty($website_state['preview_ready']);
        $consultation_status = $preview_ready ? 'completed' : 'generating_media';

        $lead_generation_state = $this->maybe_generate_initial_consultation_leads($user_id, $session_id, $business_intelligence);
        if (!isset($operating_state['systems']) || !is_array($operating_state['systems'])) {
            $operating_state['systems'] = array();
        }
        $operating_state['systems']['lead_generation'] = $lead_generation_state;

        $research_results = $this->resolve_consultation_research_results($user_id, $session_id, $business_intelligence, $business_package);
        $business_manifest = $this->generate_business_operating_manifest(
            $user_id,
            $session_id,
            $business_intelligence,
            $business_package,
            $website_result,
            $research_results
        );
        if (!empty($business_manifest)) {
            $operating_state['business_manifest'] = array(
                'manifest_id' => $business_manifest['manifest_id'] ?? '',
                'version' => $business_manifest['version'] ?? '',
                'source_of_truth' => $business_manifest['source_of_truth'] ?? 'class-vmo-enhanced-business-wizard.php',
                'ready' => true,
                'updated_at' => $business_manifest['updated_at'] ?? current_time('mysql'),
            );
            $operating_state['manifest_contracts'] = $business_manifest['contracts'] ?? array();
            $business_package['business_manifest'] = $business_manifest;
        }

        if (function_exists('vmo_crm_initialize_from_consultation')) {
            try {
                $crm_state = vmo_crm_initialize_from_consultation($user_id, $session_id, $business_intelligence, is_array($business_intelligence) ? $business_intelligence : array(), $research_results);
                $operating_state['systems']['contact_management'] = array_merge(array('ready' => !empty($crm_state['blueprint_id'])), is_array($crm_state) ? $crm_state : array());
                $business_package['contact_management_initialized'] = $crm_state;
            } catch (\Throwable $e) {
                $operating_state['systems']['contact_management'] = array('ready' => false, 'status' => 'needs_attention', 'reason' => $e->getMessage());
                error_log('VMO generation persistence: CRM blueprint initialization failed: ' . $e->getMessage());
            }
        }

        $website_result_payload = is_array($website_result) ? $website_result : array();
        $website_result_payload['draft_assembled'] = $draft_assembled;
        $website_result_payload['media_complete'] = $media_complete;
        $website_result_payload['preview_ready'] = $preview_ready;
        $website_result_payload['draft_ready'] = $preview_ready;
        if (!empty($business_manifest)) {
            $website_result_payload['business_manifest_id'] = $business_manifest['manifest_id'] ?? '';
            $website_result_payload['business_manifest_source'] = 'vmo_business_manifest';
        }

        // Compose website_data payload
        $website_payload = array(
            'business_package' => $business_package,
            'website' => $website_result_payload,
            'operating_state' => $operating_state,
            'generated_at' => current_time('mysql'),
            'generation_draft_assembled' => $draft_assembled,
            'generation_completed' => $preview_ready,
            'media_complete' => $media_complete,
            'preview_ready' => $preview_ready,
        );
        if (!empty($business_manifest)) {
            $website_payload['business_manifest'] = $business_manifest;
        }
        if ($preview_ready) {
            $website_payload['generation_completed_at'] = current_time('mysql');
        }

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $updated = $wpdb->update(
            $table_name,
            array(
                'website_data' => wp_json_encode($website_payload),
                'status' => $consultation_status,
                'updated_at' => current_time('mysql')
            ),
            array('session_id' => $session_id, 'user_id' => $user_id),
            array('%s', '%s', '%s'),
            array('%s', '%d')
        );

        // If no row updated (edge: session row not created yet), insert one
        if (!$updated) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->insert(
                $table_name,
                array(
                    'user_id' => $user_id,
                    'session_id' => $session_id,
                    'consultation_data' => wp_json_encode(array()),
                    'website_data' => wp_json_encode($website_payload),
                    'status' => $consultation_status,
                    'created_at' => current_time('mysql'),
                    'updated_at' => current_time('mysql')
                ),
                array('%d','%s','%s','%s','%s','%s','%s')
            );
        }

        update_user_meta($user_id, 'vmo_business_operating_state', $operating_state);
        update_user_meta($user_id, 'vmo_business_operating_state_' . $session_id, $operating_state);
        
        // ✅ DASHBOARD WIRING: Initialize KPI Dashboard to "Come Alive" immediately
        if (!empty($business_intelligence)) {
            $dashboard_class_file = plugin_dir_path(__FILE__) . 'class-vmo-kpi-dashboard.php';
            if (file_exists($dashboard_class_file)) {
                require_once $dashboard_class_file;
                if (class_exists('VMO_KPI_Dashboard')) {
                    $kpi_dashboard = new VMO_KPI_Dashboard();
                    // Map responses to expected intelligence format if needed
                    // Arrays are compatible, so passing $business_intelligence direct is safe
                    $kpi_dashboard->initialize_business_kpis($user_id, $business_intelligence);

                    if (!empty($business_manifest['kpi_targets']) && is_array($business_manifest['kpi_targets'])) {
                        foreach ($business_manifest['kpi_targets'] as $metric => $target_config) {
                            if (!is_array($target_config)) {
                                continue;
                            }
                            $metric_key = sanitize_key((string) ($target_config['metric'] ?? $metric));
                            if ($metric_key === '') {
                                continue;
                            }
                            $kpi_dashboard->set_kpi($user_id, $metric_key . '_target', (string) ($target_config['target'] ?? ''), array(
                                'label' => ($target_config['label'] ?? ucwords(str_replace('_', ' ', $metric_key))) . ' Target',
                                'type' => is_numeric($target_config['target'] ?? null) ? 'number' : 'text',
                                'category' => $target_config['category'] ?? 'targets',
                            ));
                            $kpi_dashboard->set_kpi($user_id, $metric_key . '_gap', (string) ($target_config['gap'] ?? ''), array(
                                'label' => ($target_config['label'] ?? ucwords(str_replace('_', ' ', $metric_key))) . ' Gap',
                                'type' => is_numeric($target_config['gap'] ?? null) ? 'number' : 'text',
                                'category' => $target_config['category'] ?? 'targets',
                            ));
                            if (!empty($target_config['recommendation'])) {
                                $kpi_dashboard->set_kpi($user_id, $metric_key . '_recommendation', (string) $target_config['recommendation'], array(
                                    'label' => ($target_config['label'] ?? ucwords(str_replace('_', ' ', $metric_key))) . ' Recommendation',
                                    'type' => 'text',
                                    'category' => 'recommendations',
                                ));
                            }
                        }
                    }

                    $pages_count = is_array($website_result['pages_created'] ?? null) ? count($website_result['pages_created']) : 0;
                    $payment_state = is_array($operating_state['systems']['payments'] ?? null) ? $operating_state['systems']['payments'] : array();

                    $kpi_dashboard->set_kpi($user_id, 'pages_count', (string) $pages_count, array(
                        'label' => 'Total Pages',
                        'type' => 'number',
                        'category' => 'website',
                    ));
                    $website_status = !empty($operating_state['website']['draft_ready'])
                        ? 'draft_ready'
                        : (!empty($operating_state['website']['draft_assembled']) ? 'media_generation_in_progress' : 'draft');
                    $kpi_dashboard->set_kpi($user_id, 'website_status', $website_status, array(
                        'label' => 'Website Status',
                        'type' => 'status',
                        'category' => 'website',
                    ));
                    $next_milestone = !empty($operating_state['website']['media_complete'])
                        ? (!empty($operating_state['website']['publish_ready']) ? 'Review and Publish Website' : 'Resolve Draft QA and Publish Website')
                        : 'Generate and insert required media';
                    $kpi_dashboard->set_kpi($user_id, 'next_milestone', $next_milestone, array(
                        'label' => 'Next Milestone',
                        'type' => 'text',
                        'category' => 'progress',
                    ));

                    $setup_flags = array(
                        'payment_system_ready' => !empty($payment_state['checkout_ready']),
                        'phone_system_ready' => !empty($operating_state['systems']['phone']['ready']),
                        'email_marketing_ready' => !empty($operating_state['systems']['email_marketing']['ready']),
                        'seo_system_ready' => !empty($operating_state['systems']['seo']['ready']),
                        'contact_management_ready' => !empty($operating_state['systems']['contact_management']['ready']),
                        'lead_generation_ready' => (($operating_state['systems']['lead_generation']['status'] ?? '') === 'ready'),
                    );

                    foreach ($setup_flags as $metric => $is_ready) {
                        $kpi_dashboard->set_kpi($user_id, $metric, $is_ready ? '1' : '0', array(
                            'label' => ucwords(str_replace('_', ' ', $metric)),
                            'type' => 'boolean',
                            'category' => 'setup',
                        ));
                    }

                    $ready_count = count(array_filter($setup_flags));
                    $setup_progress = min(95, 60 + ($ready_count * 7));
                    $kpi_dashboard->set_kpi($user_id, 'setup_progress', (string) $setup_progress, array(
                        'label' => 'Setup Progress',
                        'type' => 'percentage',
                        'category' => 'progress',
                    ));

                    error_log("✅ VMO Dashboard: Wired and Alive for user {$user_id}");
                }
            }
        }

        // Update dashboard last update timestamp
        update_option('vmo_dashboard_last_update_ts', time());

        // Fire a persisted hook on every save, and reserve the completed hook for preview-ready completion.
        do_action('vmo_consultation_generation_persisted', $user_id, $session_id, $business_package, $website_result);
        if ($preview_ready) {
            do_action('vmo_consultation_generation_completed', $user_id, $session_id, $business_package, $website_result);

            // ── Jerreme → Jarvis Handoff ──
            // Send congratulatory email with explicit handoff to Jarvis for daily operations
            $this->send_handoff_email($user_id, $business_package);
        }
    }

    private function generate_business_operating_manifest($user_id, $session_id, $business_intelligence, $business_package, $website_result, $research_results = array()) {
        $user_id = (int) $user_id;
        $session_id = (string) $session_id;

        if ($user_id <= 0 || $session_id === '') {
            return array();
        }

        if (!class_exists('VMO_Business_Manifest_Generator')) {
            $manifest_file = plugin_dir_path(__FILE__) . 'class-vmo-business-manifest-generator.php';
            if (file_exists($manifest_file)) {
                require_once $manifest_file;
            }
        }

        if (!class_exists('VMO_Business_Manifest_Generator')) {
            return array();
        }

        try {
            $consultation_context = $this->hydrate_business_manifest_context($user_id, $session_id, is_array($business_intelligence) ? $business_intelligence : array());
            $generator = new VMO_Business_Manifest_Generator();
            $manifest = $generator->generate_manifest(
                $user_id,
                $session_id,
                $consultation_context,
                is_array($business_package) ? $business_package : array(),
                is_array($website_result) ? $website_result : array(),
                is_array($research_results) ? $research_results : array()
            );
            $generator->persist_manifest($user_id, $session_id, $manifest);
            $this->sync_manifest_backed_subsystems($user_id, $session_id, $manifest, $research_results);

            do_action('vmo_business_manifest_generated', $user_id, $session_id, $manifest);
            return $manifest;
        } catch (\Throwable $e) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log('VMO business manifest generation failed: ' . $e->getMessage());
            }
            return array();
        }
    }

    private function hydrate_business_manifest_context($user_id, $session_id, $business_intelligence = array()) {
        global $wpdb;

        $context = is_array($business_intelligence) ? $business_intelligence : array();
        $table_name = $wpdb->prefix . 'vmo_consultations';

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $row = $wpdb->get_row(
            $wpdb->prepare(
                "SELECT consultation_data, phase_state, website_data, business_name, business_type, status FROM {$table_name} WHERE user_id = %d AND session_id = %s LIMIT 1",
                $user_id,
                $session_id
            ),
            ARRAY_A
        );

        if (is_array($row)) {
            foreach (array('consultation_data', 'phase_state', 'website_data') as $json_field) {
                $decoded = json_decode((string) ($row[$json_field] ?? ''), true);
                if (is_array($decoded)) {
                    $context = array_replace_recursive($context, $decoded);
                }
            }

            if (!empty($row['business_name']) && empty($context['business_name'])) {
                $context['business_name'] = $row['business_name'];
            }
            if (!empty($row['business_type']) && empty($context['business_type'])) {
                $context['business_type'] = $row['business_type'];
            }
            if (!empty($row['status'])) {
                $context['consultation_status'] = $row['status'];
            }
        }

        if (empty($context['research_results'])) {
            $session_research = get_user_meta($user_id, 'vmo_research_results_' . $session_id, true);
            if (is_array($session_research) && !empty($session_research)) {
                $context['research_results'] = $session_research;
            }
        }

        return $context;
    }

    private function resolve_consultation_research_results($user_id, $session_id, $business_intelligence = array(), $business_package = array()) {
        $business_intelligence = is_array($business_intelligence) ? $business_intelligence : array();
        $business_package = is_array($business_package) ? $business_package : array();

        foreach (array(
            $business_intelligence['research_results'] ?? array(),
            $business_package['research_results'] ?? array(),
            get_user_meta($user_id, 'vmo_research_results_' . $session_id, true),
            get_user_meta($user_id, 'vmo_research_results', true),
        ) as $candidate) {
            if (is_array($candidate) && !empty($candidate)) {
                return $candidate;
            }
        }

        global $wpdb;
        $table_name = $wpdb->prefix . 'vmo_consultations';
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $row = $wpdb->get_row(
            $wpdb->prepare("SELECT consultation_data, phase_state FROM {$table_name} WHERE user_id = %d AND session_id = %s LIMIT 1", $user_id, $session_id),
            ARRAY_A
        );
        if (is_array($row)) {
            foreach (array('consultation_data', 'phase_state') as $json_field) {
                $decoded = json_decode((string) ($row[$json_field] ?? ''), true);
                if (is_array($decoded) && !empty($decoded['research_results']) && is_array($decoded['research_results'])) {
                    return $decoded['research_results'];
                }
            }
        }

        return array();
    }

    private function sync_manifest_backed_subsystems($user_id, $session_id, $manifest, $research_results = array()) {
        $manifest = is_array($manifest) ? $manifest : array();
        if (empty($manifest)) {
            return;
        }

        if (!class_exists('VMO_Automation_Engine')) {
            $automation_file = plugin_dir_path(__FILE__) . 'class-vmo-automation-engine.php';
            if (file_exists($automation_file)) {
                require_once $automation_file;
            }
        }
        if (class_exists('VMO_Automation_Engine')) {
            VMO_Automation_Engine::get_instance()->sync_from_manifest($user_id, $session_id, $manifest);
        }

        $this->generate_manifest_documents($user_id, $session_id, $manifest, $research_results);
    }

    private function generate_manifest_documents($user_id, $session_id, $manifest, $research_results = array()) {
        $generated_key = 'vmo_documents_generated_' . $session_id;
        if (get_user_meta($user_id, $generated_key, true)) {
            return;
        }

        if (!class_exists('VMO_Document_Generator')) {
            $document_file = plugin_dir_path(__FILE__) . 'class-vmo-document-generator.php';
            if (file_exists($document_file)) {
                require_once $document_file;
            }
        }

        if (!class_exists('VMO_Document_Generator')) {
            update_user_meta($user_id, 'vmo_document_generation_state_' . $session_id, array(
                'status' => 'blocked',
                'reason' => 'VMO_Document_Generator is not available.',
                'updated_at' => current_time('mysql'),
            ));
            return;
        }

        try {
            $document_generator = new VMO_Document_Generator();
            $documents = $document_generator->generate_all_documents(
                $user_id,
                is_array($manifest) ? $manifest : array(),
                is_array($research_results) ? $research_results : array()
            );
            update_user_meta($user_id, $generated_key, current_time('mysql'));
            update_user_meta($user_id, 'vmo_documents', $documents);
            update_user_meta($user_id, 'vmo_documents_' . $session_id, $documents);
            update_user_meta($user_id, 'vmo_document_generation_state_' . $session_id, array(
                'status' => 'completed',
                'count' => is_array($documents) ? count($documents) : 0,
                'updated_at' => current_time('mysql'),
            ));
        } catch (\Throwable $e) {
            update_user_meta($user_id, 'vmo_document_generation_state_' . $session_id, array(
                'status' => 'blocked',
                'reason' => $e->getMessage(),
                'updated_at' => current_time('mysql'),
            ));
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log('VMO manifest document generation failed: ' . $e->getMessage());
            }
        }
    }

    private function maybe_generate_initial_consultation_leads($user_id, $session_id, $business_intelligence = array()) {
        $user_id = (int) $user_id;
        $session_id = (string) $session_id;
        $business_intelligence = is_array($business_intelligence) ? $business_intelligence : array();

        $existing_count = $this->count_ai_discovery_leads($user_id);
        if ($existing_count > 0) {
            $state = array(
                'status' => 'ready',
                'count' => $existing_count,
                'source' => 'existing_crm',
                'updated_at' => current_time('mysql'),
            );
            update_user_meta($user_id, 'vmo_consultation_lead_generation_' . $session_id, $state);
            return $state;
        }

        if (!get_option('vmo_openai_api_key')) {
            $state = array(
                'status' => 'blocked',
                'count' => 0,
                'reason' => 'OpenAI API key is required for real lead discovery.',
                'updated_at' => current_time('mysql'),
            );
            update_user_meta($user_id, 'vmo_consultation_lead_generation_' . $session_id, $state);
            return $state;
        }

        if (!class_exists('VMO_Deep_Research')) {
            $deep_research_file = plugin_dir_path(__FILE__) . 'class-vmo-deep-research.php';
            if (file_exists($deep_research_file)) {
                require_once $deep_research_file;
            }
        }

        if (!class_exists('VMO_Deep_Research') || !method_exists('VMO_Deep_Research', 'get_instance')) {
            $state = array(
                'status' => 'blocked',
                'count' => 0,
                'reason' => 'Deep Research lead engine is unavailable.',
                'updated_at' => current_time('mysql'),
            );
            update_user_meta($user_id, 'vmo_consultation_lead_generation_' . $session_id, $state);
            return $state;
        }

        $profile = array(
            'business_name' => sanitize_text_field((string) ($business_intelligence['business_name'] ?? '')),
            'industry' => sanitize_text_field((string) ($business_intelligence['industry'] ?? '')),
            'location' => sanitize_text_field((string) ($business_intelligence['location'] ?? '')),
            'target_audience' => is_array($business_intelligence['target_audience'] ?? null)
                ? implode(', ', array_map('sanitize_text_field', $business_intelligence['target_audience']))
                : sanitize_text_field((string) ($business_intelligence['target_audience'] ?? '')),
            'unique_value_proposition' => sanitize_text_field((string) ($business_intelligence['unique_value_proposition'] ?? ($business_intelligence['value_proposition'] ?? ''))),
            'offerings' => is_array($business_intelligence['offerings'] ?? null)
                ? implode(', ', array_map('sanitize_text_field', $business_intelligence['offerings']))
                : sanitize_text_field((string) ($business_intelligence['offerings'] ?? '')),
            'products_inventory' => $business_intelligence['products_inventory'] ?? array(),
            'services_inventory' => $business_intelligence['services_inventory'] ?? array(),
        );

        try {
            $generation = VMO_Deep_Research::get_instance()->generate_leads($user_id, $profile, array(
                'research_results' => is_array($business_intelligence['research_profile'] ?? null) ? $business_intelligence['research_profile'] : array(),
            ));
            $total_count = $this->count_ai_discovery_leads($user_id);
            $state = array(
                'status' => $total_count > 0 ? 'ready' : 'blocked',
                'count' => $total_count,
                'inserted' => (int) ($generation['saved_count'] ?? 0),
                'source' => 'deep_research_real_leads',
                'lead_source_tiers' => is_array($generation['tiers'] ?? null) ? $generation['tiers'] : array(),
                'reason' => $total_count > 0 ? '' : 'Lead discovery did not create CRM contacts.',
                'updated_at' => current_time('mysql'),
            );
        } catch (\Throwable $e) {
            $state = array(
                'status' => 'blocked',
                'count' => 0,
                'reason' => $e->getMessage(),
                'updated_at' => current_time('mysql'),
            );
            error_log('VMO Lead Discovery during consultation generation failed: ' . $e->getMessage());
        }

        update_user_meta($user_id, 'vmo_consultation_lead_generation_' . $session_id, $state);
        return $state;
    }

    private function count_ai_discovery_leads($user_id) {
        global $wpdb;
        $user_id = (int) $user_id;
        if ($user_id <= 0) {
            return 0;
        }

        $contacts_table = $wpdb->prefix . 'vmo_contacts';
        if (!$this->builder_table_exists($contacts_table)) {
            return 0;
        }

        return (int) $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM {$contacts_table} WHERE user_id = %d AND source IN ('lead_generation','vmo_ai_discovery') AND (acquisition_details IS NULL OR acquisition_details NOT LIKE %s)",
            $user_id,
            '%template%'
        ));
    }

    private function derive_payment_runtime_snapshot($business_intelligence = array(), $website_result = array()) {
        $business_intelligence = is_array($business_intelligence) ? $business_intelligence : array();
        $website_result = is_array($website_result) ? $website_result : array();

        $payment_collection = is_array($business_intelligence['payment_collection'] ?? null)
            ? $business_intelligence['payment_collection']
            : array();
        $ecommerce = is_array($business_intelligence['ecommerce'] ?? null)
            ? $business_intelligence['ecommerce']
            : array();

        $mode = strtolower((string) (
            $payment_collection['mode']
            ?? $ecommerce['collection_mode']
            ?? 'none'
        ));

        $products = array();
        if (!empty($ecommerce['products']) && is_array($ecommerce['products'])) {
            $products = $ecommerce['products'];
        } elseif (!empty($business_intelligence['products_inventory']) && is_array($business_intelligence['products_inventory'])) {
            $products = $business_intelligence['products_inventory'];
        } elseif (!empty($business_intelligence['services_inventory']) && is_array($business_intelligence['services_inventory'])) {
            $products = $business_intelligence['services_inventory'];
        }

        $products_count = is_array($products) ? count($products) : 0;
        $collects_payments = !empty($payment_collection['collects_payments'])
            || !empty($business_intelligence['needs']['ecommerce'])
            || in_array($mode, array('products', 'donations'), true)
            || $products_count > 0;

        if (!in_array($mode, array('products', 'donations', 'none'), true)) {
            $mode = $collects_payments ? 'products' : 'none';
        }

        $payments_enabled = defined('VMO_PAYMENTS_ENABLED') && VMO_PAYMENTS_ENABLED;
        $checkout_ready = false;
        if ($payments_enabled) {
            if ($mode === 'donations') {
                $checkout_ready = $collects_payments;
            } elseif ($mode === 'products') {
                $checkout_ready = $products_count > 0;
            }
        }

        $default_product_id = 0;
        if (!empty($products[0]['id'])) {
            $default_product_id = (int) $products[0]['id'];
        }

        return array(
            'session_id' => (string) ($website_result['session_id'] ?? ''),
            'enabled' => $collects_payments,
            'mode' => $mode,
            'merchant_user_id' => get_current_user_id(),
            'business_name' => sanitize_text_field((string) ($business_intelligence['business_name'] ?? '')),
            'store_url' => (string) home_url('/store'),
            'donate_url' => (string) home_url('/donate'),
            'products' => is_array($products) ? $products : array(),
            'default_product_id' => $default_product_id,
            'donation_funds' => is_array($ecommerce['donation_funds'] ?? null) ? $ecommerce['donation_funds'] : array(),
            'account' => array(
                'attempted' => $collects_payments,
                'provisioned' => $checkout_ready,
                'account_id' => '',
                'reason' => $collects_payments ? 'Derived from consultation generation state' : '',
            ),
            'checkout_ready' => $checkout_ready,
        );
    }

    private function prepare_payment_runtime_package($user_id, $session_id, $responses, $business_package = array()) {
        $payment_collection = is_array($responses['payment_collection'] ?? null) ? $responses['payment_collection'] : array();
        $ecommerce = is_array($responses['ecommerce'] ?? null) ? $responses['ecommerce'] : array();
        $mode = 'none';

        foreach (array(
            strtolower((string) ($payment_collection['mode'] ?? '')),
            strtolower((string) ($ecommerce['collection_mode'] ?? '')),
        ) as $candidate) {
            if (in_array($candidate, array('products', 'donations', 'none'), true)) {
                $mode = $candidate;
                break;
            }
        }

        if ($mode === 'none' && !empty($payment_collection['collects_payments'])) {
            $mode = 'products';
        }
        if ($mode === 'none' && (!empty($responses['products_inventory']) || !empty($responses['services_inventory']) || !empty($responses['needs']['ecommerce']))) {
            $mode = 'products';
        }

        $runtime = array(
            'session_id' => (string) $session_id,
            'enabled' => $mode !== 'none',
            'mode' => $mode,
            'merchant_user_id' => (int) $user_id,
            'business_name' => sanitize_text_field((string) ($responses['business_name'] ?? '')),
            'store_url' => (string) home_url('/store'),
            'donate_url' => (string) home_url('/donate'),
            'products' => array(),
            'default_product_id' => 0,
            'donation_funds' => array(),
            'account' => array(
                'attempted' => false,
                'provisioned' => false,
                'account_id' => '',
                'reason' => '',
            ),
            'checkout_ready' => false,
        );

        if (!$runtime['enabled']) {
            return $runtime;
        }

        $runtime['account'] = $this->ensure_payment_connected_account($user_id, $responses, $mode);

        if ($mode === 'donations') {
            $runtime['donation_funds'] = $this->extract_payment_runtime_donation_funds($responses);
            $runtime['checkout_ready'] = !empty($runtime['account']['provisioned']);
            return $runtime;
        }

        $catalog = $this->build_payment_runtime_product_catalog($responses);
        if (!empty($catalog)) {
            $runtime['products'] = $this->upsert_payment_runtime_products($user_id, $catalog);
            if (!empty($runtime['products'][0]['id'])) {
                $runtime['default_product_id'] = (int) $runtime['products'][0]['id'];
            }
        }

        $runtime['checkout_ready'] = !empty($runtime['products']) && !empty($runtime['account']['provisioned']);

        return $runtime;
    }

    private function build_payment_runtime_product_catalog($responses) {
        $catalog = array();
        $products_inventory = is_array($responses['products_inventory'] ?? null) ? $responses['products_inventory'] : array();
        $services_inventory = is_array($responses['services_inventory'] ?? null) ? $responses['services_inventory'] : array();
        $global_pricing_text = (string) ($responses['pricing'] ?? '');
        $global_price_cents = $this->extract_price_cents_from_text($global_pricing_text);

        foreach ($products_inventory as $product) {
            if (!is_array($product)) {
                continue;
            }

            $name = sanitize_text_field((string) ($product['name'] ?? ''));
            if ($name === '') {
                continue;
            }

            $description = wp_kses_post((string) ($product['description'] ?? ($product['summary'] ?? '')));
            $price = isset($product['price']) ? (float) $product['price'] : 0.0;
            if ($price <= 0 && !empty($product['price_text'])) {
                $price_cents = $this->extract_price_cents_from_text((string) $product['price_text']);
                if ($price_cents > 0) {
                    $price = $price_cents / 100;
                }
            }
            if ($price <= 0 && $global_price_cents > 0) {
                $price = $global_price_cents / 100;
            }

            $currency = strtolower(sanitize_text_field((string) ($product['currency'] ?? 'usd')));
            $images = array();
            if (!empty($product['main_image']['url'])) {
                $images[] = esc_url_raw((string) $product['main_image']['url']);
            }
            if (!empty($product['gallery_images']) && is_array($product['gallery_images'])) {
                foreach ($product['gallery_images'] as $gallery_image) {
                    if (is_array($gallery_image) && !empty($gallery_image['url'])) {
                        $images[] = esc_url_raw((string) $gallery_image['url']);
                    } elseif (is_string($gallery_image) && $gallery_image !== '') {
                        $images[] = esc_url_raw($gallery_image);
                    }
                }
            }
            if (!empty($product['images']) && is_array($product['images'])) {
                foreach ($product['images'] as $image) {
                    if (is_array($image) && !empty($image['url'])) {
                        $images[] = esc_url_raw((string) $image['url']);
                    } elseif (is_string($image) && $image !== '') {
                        $images[] = esc_url_raw($image);
                    }
                }
            }
            $images = array_values(array_filter(array_unique($images)));

            $variants = is_array($product['variants'] ?? null) ? $product['variants'] : array();
            if (!empty($variants)) {
                foreach ($variants as $variant) {
                    if (!is_array($variant)) {
                        continue;
                    }
                    $variant_label = sanitize_text_field((string) ($variant['name'] ?? ($variant['label'] ?? ($variant['title'] ?? 'Variant'))));
                    if ($variant_label === '') {
                        $variant_label = 'Variant';
                    }
                    $variant_price = isset($variant['price']) ? (float) $variant['price'] : $price;
                    if ($variant_price <= 0 && $global_price_cents > 0) {
                        $variant_price = $global_price_cents / 100;
                    }

                    $catalog[] = array(
                        'name' => trim($name . ' - ' . $variant_label),
                        'slug' => sanitize_title($name . '-' . $variant_label),
                        'description' => wp_kses_post((string) ($variant['description'] ?? $description)),
                        'summary' => sanitize_text_field((string) ($variant['summary'] ?? $description)),
                        'price' => $variant_price,
                        'currency' => strtolower(sanitize_text_field((string) ($variant['currency'] ?? $currency ?: 'usd'))),
                        'sku' => sanitize_text_field((string) ($variant['sku'] ?? '')),
                        'images' => $images,
                        'image' => (string) ($images[0] ?? ''),
                        'track_stock' => !empty($product['stock_quantity']),
                        'stock_qty' => intval($product['stock_quantity'] ?? 0),
                        'shipping_required' => !empty($product['requires_shipping']),
                        'type' => !empty($product['is_digital']) ? 'downloadable' : 'item',
                    );
                }
                continue;
            }

            $catalog[] = array(
                'name' => $name,
                'slug' => sanitize_title((string) ($product['slug'] ?? $name)),
                'description' => $description,
                'summary' => sanitize_text_field((string) ($product['summary'] ?? $description)),
                'price' => $price,
                'currency' => $currency ?: 'usd',
                'sku' => sanitize_text_field((string) ($product['sku'] ?? '')),
                'images' => $images,
                'image' => (string) ($images[0] ?? ''),
                'track_stock' => !empty($product['stock_quantity']),
                'stock_qty' => intval($product['stock_quantity'] ?? 0),
                'shipping_required' => !empty($product['requires_shipping']),
                'type' => !empty($product['is_digital']) ? 'downloadable' : 'item',
            );
        }

        foreach ($services_inventory as $service) {
            if (!is_array($service)) {
                continue;
            }

            $name = sanitize_text_field((string) ($service['name'] ?? ''));
            if ($name === '') {
                continue;
            }

            $price = isset($service['price']) ? (float) $service['price'] : 0.0;
            if ($price <= 0 && $global_price_cents > 0) {
                $price = $global_price_cents / 100;
            }

            $images = array();
            if (!empty($service['images']) && is_array($service['images'])) {
                foreach ($service['images'] as $image) {
                    if (is_array($image) && !empty($image['url'])) {
                        $images[] = esc_url_raw((string) $image['url']);
                    } elseif (is_string($image) && $image !== '') {
                        $images[] = esc_url_raw($image);
                    }
                }
            }
            $images = array_values(array_filter(array_unique($images)));

            $catalog[] = array(
                'name' => $name,
                'slug' => sanitize_title((string) ($service['slug'] ?? $name)),
                'description' => wp_kses_post((string) ($service['description'] ?? '')),
                'summary' => sanitize_text_field((string) ($service['summary'] ?? ($service['description'] ?? ''))),
                'price' => $price,
                'currency' => strtolower(sanitize_text_field((string) ($service['currency'] ?? 'usd'))),
                'sku' => sanitize_text_field((string) ($service['sku'] ?? '')),
                'images' => $images,
                'image' => (string) ($images[0] ?? ''),
                'track_stock' => false,
                'stock_qty' => 0,
                'shipping_required' => false,
                'type' => 'service',
            );
        }

        if (empty($catalog)) {
            $catalog = $this->seed_payment_runtime_product_catalog($responses);
        }

        $positive_prices = 0;
        foreach ($catalog as $item) {
            $item_price = (float) ($item['price'] ?? 0);
            if ($item_price > 0) {
                $positive_prices++;
            }
        }

        if ($positive_prices === 0 && !empty($catalog)) {
            $fallback_prices = array(99.0, 149.0, 249.0);
            foreach ($catalog as $index => $item) {
                if ((float) ($item['price'] ?? 0) <= 0) {
                    $catalog[$index]['price'] = $fallback_prices[$index % count($fallback_prices)];
                }
            }
        }

        return $catalog;
    }

    private function seed_payment_runtime_product_catalog($responses) {
        if ($this->looks_like_vmo_plugin_blueprint_responses($responses)) {
            return $this->get_vmo_plan_seed_catalog();
        }

        $catalog = array();
        $offerings = is_array($responses['offerings'] ?? null) ? $responses['offerings'] : array();
        $offerings = array_slice(array_values(array_filter(array_map('sanitize_text_field', $offerings))), 0, 3);
        $pricing_text = (string) ($responses['pricing'] ?? '');
        $seed_price = $this->extract_price_cents_from_text($pricing_text);
        $seed_amount = $seed_price > 0 ? ($seed_price / 100) : 99.0;
        $default_prices = array($seed_amount, max($seed_amount, 149.0), max($seed_amount, 249.0));

        if (!empty($offerings)) {
            foreach ($offerings as $index => $offering) {
                $package_name = trim($offering);
                if ($package_name === '') {
                    continue;
                }
                $catalog[] = array(
                    'name' => $package_name,
                    'slug' => sanitize_title($package_name),
                    'description' => 'A premium ' . $package_name . ' offer prepared directly from the consultation so the website and checkout experience stay aligned.',
                    'summary' => 'Consultation-driven offer for ' . $package_name . '.',
                    'price' => $default_prices[$index % count($default_prices)],
                    'currency' => 'usd',
                    'images' => array(),
                    'image' => '',
                    'track_stock' => false,
                    'stock_qty' => 0,
                    'shipping_required' => false,
                    'type' => 'service',
                );
            }
        }

        if (!empty($catalog)) {
            return $catalog;
        }

        return array(
            array(
                'name' => 'Starter Package',
                'slug' => 'starter-package',
                'description' => 'A high-quality starter offer that delivers a fast, polished outcome and sets a strong foundation for launch.',
                'summary' => 'Fast, polished starter offer.',
                'price' => 99.0,
                'currency' => 'usd',
                'images' => array(),
                'image' => '',
                'track_stock' => false,
                'stock_qty' => 0,
                'shipping_required' => false,
                'type' => 'service',
            ),
            array(
                'name' => 'Growth Package',
                'slug' => 'growth-package',
                'description' => 'A growth-focused offer that adds depth, conversion improvements, and stronger operational support.',
                'summary' => 'Growth-focused offer for scaling.',
                'price' => 149.0,
                'currency' => 'usd',
                'images' => array(),
                'image' => '',
                'track_stock' => false,
                'stock_qty' => 0,
                'shipping_required' => false,
                'type' => 'service',
            ),
            array(
                'name' => 'Premium Package',
                'slug' => 'premium-package',
                'description' => 'A top-tier end-to-end offer featuring the most complete scope, deeper customization, and premium delivery.',
                'summary' => 'Top-tier, end-to-end premium offer.',
                'price' => 249.0,
                'currency' => 'usd',
                'images' => array(),
                'image' => '',
                'track_stock' => false,
                'stock_qty' => 0,
                'shipping_required' => false,
                'type' => 'service',
            ),
        );
    }

    private function looks_like_vmo_plugin_blueprint_responses($responses) {
        if (!is_array($responses)) {
            return false;
        }

        $signals = array(
            (string) ($responses['business_name'] ?? ''),
            (string) ($responses['business_type'] ?? ''),
            (string) ($responses['industry'] ?? ''),
            (string) ($responses['target_audience'] ?? ''),
            (string) ($responses['pricing'] ?? ''),
        );

        foreach (array('offerings', 'goals', 'custom_pages', 'special_features') as $key) {
            $value = $responses[$key] ?? null;
            if (is_array($value)) {
                $signals[] = implode(' ', array_map('strval', $value));
            } elseif (is_scalar($value)) {
                $signals[] = (string) $value;
            }
        }

        $signal = strtolower(implode(' | ', array_filter(array_map('strval', $signals))));
        if ($signal === '') {
            return false;
        }

        foreach (array('wordpress plugin', 'plugin for wordpress', 'quicklaunch', 'download vmo', 'getvmo', '12 ai agents', 'vmo pay', 'vmo phone agent') as $term) {
            if (strpos($signal, $term) !== false) {
                return true;
            }
        }

        $vmo_hits = substr_count($signal, 'vmo');
        $product_hits = 0;
        foreach (array('plugin', 'wordpress', 'download', 'pricing', 'affiliate', 'saas', 'software', 'plan', 'plans') as $term) {
            if (strpos($signal, $term) !== false) {
                $product_hits++;
            }
        }

        return $vmo_hits > 0 && $product_hits >= 2;
    }

    private function get_vmo_plan_seed_catalog() {
        $descriptions = array(
            'tester' => 'Best for a first hands-on VMO experience when someone wants to try the plugin on a real business idea without committing to heavier ongoing automation.',
            'starter' => 'Built for solo operators and side hustles who want a practical launch path with core AI-guided website and business setup.',
            'pro' => 'Recommended for serious business owners who want stronger operating capacity, deeper automation, and room to grow.',
            'elite' => 'Designed for heavy users running more aggressive operations, richer media generation, and broader business activity.',
            'enterprise' => 'For agencies, advanced teams, and high-volume operators who need the deepest capacity and the broadest room to scale.',
        );

        $summaries = array(
            'tester' => 'A low-friction way to experience VMO on a real project.',
            'starter' => 'Launch one business with the essential VMO workflow.',
            'pro' => 'The recommended plan for serious, growth-oriented use.',
            'elite' => 'For heavier usage and more aggressive operating demands.',
            'enterprise' => 'Maximum capacity for advanced teams and agencies.',
        );

        $fallback_prices = array(
            'tester' => 49.99,
            'starter' => 99.99,
            'pro' => 199.99,
            'elite' => 499.99,
            'enterprise' => 999.99,
        );

        $catalog = array();
        $all_plans = class_exists('VMO_Plan_Policy') ? VMO_Plan_Policy::get_all() : array();
        foreach (array('tester', 'starter', 'pro', 'elite', 'enterprise') as $slug) {
            $plan = is_array($all_plans[$slug] ?? null) ? $all_plans[$slug] : array();
            $catalog[] = array(
                'name' => sanitize_text_field((string) ($plan['name'] ?? ucfirst($slug))),
                'slug' => $slug,
                'description' => $descriptions[$slug],
                'summary' => $summaries[$slug],
                'price' => (float) ($plan['price'] ?? $fallback_prices[$slug]),
                'currency' => 'usd',
                'images' => array(),
                'image' => '',
                'track_stock' => false,
                'stock_qty' => 0,
                'shipping_required' => false,
                'type' => 'service',
            );
        }

        return $catalog;
    }

    private function extract_payment_runtime_donation_funds($responses) {
        $ecommerce = is_array($responses['ecommerce'] ?? null) ? $responses['ecommerce'] : array();
        $funds = is_array($ecommerce['donation_funds'] ?? null) ? $ecommerce['donation_funds'] : array();
        $normalized = array();

        foreach ($funds as $fund) {
            if (is_array($fund)) {
                $name = sanitize_text_field((string) ($fund['name'] ?? 'Donation'));
                $amounts = is_array($fund['amounts'] ?? null) ? array_values(array_filter(array_map('intval', $fund['amounts']))) : array();
                if (empty($amounts)) {
                    $amounts = array(2000, 5000, 10000);
                }
                $normalized[] = array(
                    'name' => $name !== '' ? $name : 'Donation',
                    'amounts' => $amounts,
                );
            } elseif (is_string($fund) && trim($fund) !== '') {
                $normalized[] = array(
                    'name' => sanitize_text_field($fund),
                    'amounts' => array(2000, 5000, 10000),
                );
            }
        }

        if (!empty($normalized)) {
            return $normalized;
        }

        return $this->default_payment_runtime_donation_funds($responses);
    }

    private function default_payment_runtime_donation_funds($responses) {
        $signal = strtolower(implode(' | ', array_filter(array(
            (string) ($responses['business_type'] ?? ''),
            (string) ($responses['industry'] ?? ''),
            is_array($responses['offerings'] ?? null) ? implode(' ', $responses['offerings']) : (string) ($responses['offerings'] ?? ''),
        ))));

        if (strpos($signal, 'campaign') !== false || strpos($signal, 'candidate') !== false || strpos($signal, 'election') !== false) {
            return array(
                array('name' => 'Campaign Contribution', 'amounts' => array(2500, 5000, 10000)),
                array('name' => 'Event Support', 'amounts' => array(1000, 2500, 5000)),
                array('name' => 'Volunteer and Outreach Fund', 'amounts' => array(1500, 3000, 7500)),
            );
        }

        if (strpos($signal, 'church') !== false || strpos($signal, 'ministry') !== false || strpos($signal, 'parish') !== false || strpos($signal, 'mission') !== false) {
            return array(
                array('name' => 'Tithes', 'amounts' => array(2500, 5000, 10000)),
                array('name' => 'Sunday Offering', 'amounts' => array(1000, 2000, 5000)),
                array('name' => 'Missions Support', 'amounts' => array(2000, 5000, 15000)),
            );
        }

        return array(
            array('name' => 'General Donation', 'amounts' => array(2000, 5000, 10000)),
            array('name' => 'Community Support', 'amounts' => array(2500, 5000, 10000)),
            array('name' => 'Relief Fund', 'amounts' => array(3000, 7500, 15000)),
        );
    }

    private function ensure_payment_connected_account($user_id, $responses, $mode) {
        $status = array(
            'attempted' => false,
            'provisioned' => false,
            'account_id' => '',
            'reason' => '',
        );

        if (!defined('VMO_PAYMENTS_ENABLED') || !VMO_PAYMENTS_ENABLED) {
            $status['reason'] = 'Payments disabled in plugin settings.';
            return $status;
        }

        if (!get_option('vmo_stripe_secret_key')) {
            $status['reason'] = 'Stripe secret key missing.';
            return $status;
        }

        $status['attempted'] = true;

        try {
            if (!class_exists('Stripe\\Stripe')) {
                require_once VMO_AI_BUILDER_DIR . 'stripe-sdk/init.php';
            }
            \Stripe\Stripe::setApiKey(get_option('vmo_stripe_secret_key'));

            $account_id = $this->payment_ensure_connected_account_record($user_id, array(
                'email' => $this->extract_owner_email_from_payment_context($responses),
                'business_type' => $this->infer_business_type_from_payment_context($responses),
                'business_name' => sanitize_text_field((string) ($responses['business_name'] ?? '')),
                'product_description' => $mode === 'donations'
                    ? $this->summarize_payment_runtime_reason($responses)
                    : $this->summarize_payment_runtime_products($responses),
            ));

            $status['account_id'] = (string) $account_id;
            $status['provisioned'] = $status['account_id'] !== '';
        } catch (\Throwable $e) {
            $status['reason'] = $e->getMessage();
            error_log('VMO Payment Runtime: account provisioning failed - ' . $e->getMessage());
        }

        return $status;
    }

    private function payment_ensure_connected_account_record($user_id, $kyc) {
        global $wpdb;

        $table = $wpdb->prefix . 'vmo_connected_accounts';
        $existing = $wpdb->get_row($wpdb->prepare("SELECT * FROM $table WHERE user_id = %d", $user_id), ARRAY_A);
        if ($existing && !empty($existing['stripe_account_id'])) {
            return $existing['stripe_account_id'];
        }

        $country = 'US';
        $email = sanitize_email($kyc['email'] ?? (wp_get_current_user()->user_email ?? ''));
        $business_type = in_array(($kyc['business_type'] ?? 'individual'), array('individual', 'company'), true)
            ? $kyc['business_type']
            : 'individual';

        $account = \Stripe\Account::create(array(
            'type' => 'custom',
            'country' => $country,
            'email' => $email,
            'business_type' => $business_type,
            'capabilities' => array(
                'card_payments' => array('requested' => true),
                'transfers' => array('requested' => true),
            ),
            'business_profile' => array(
                'name' => sanitize_text_field($kyc['business_name'] ?? ''),
                'product_description' => sanitize_text_field($kyc['product_description'] ?? 'Online payments via VMO'),
            ),
        ));

        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->insert($table, array(
            'user_id' => $user_id,
            'stripe_account_id' => $account->id,
            'account_type' => 'custom',
            'details_submitted' => intval($account->details_submitted),
            'payouts_enabled' => intval($account->payouts_enabled),
            'charges_enabled' => intval($account->charges_enabled),
            'capabilities' => wp_json_encode($account->capabilities),
        ));

        return $account->id;
    }

    private function upsert_payment_runtime_products($user_id, $products) {
        global $wpdb;

        $table = $wpdb->prefix . 'vmo_store_products';
        $out = array();

        foreach ($products as $product) {
            if (!is_array($product)) {
                continue;
            }

            $name = sanitize_text_field((string) ($product['name'] ?? 'Product'));
            if ($name === '') {
                continue;
            }

            $slug = sanitize_title((string) ($product['slug'] ?? $name));
            $description = wp_kses_post((string) ($product['description'] ?? ($product['summary'] ?? '')));
            $product_type = $this->map_payment_runtime_product_type($product);
            $price_cents = intval(round((float) ($product['price'] ?? 0) * 100));
            $currency = strtolower(sanitize_text_field((string) ($product['currency'] ?? 'usd')));
            $sku = sanitize_text_field((string) ($product['sku'] ?? ''));
            $stock_tracking = !empty($product['track_stock']) ? 1 : 0;
            $stock_qty = intval($product['stock_qty'] ?? 0);
            $shipping_required = !empty($product['shipping_required']) ? 1 : 0;
            $images = isset($product['images']) && is_array($product['images'])
                ? wp_json_encode(array_values(array_map('esc_url_raw', $product['images'])))
                : (isset($product['image']) && $product['image'] !== '' ? wp_json_encode(array(esc_url_raw($product['image']))) : null);

            $existing = $wpdb->get_var($wpdb->prepare("SELECT id FROM $table WHERE user_id = %d AND slug = %s", $user_id, $slug));
            if ($existing) {
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                $wpdb->update($table, array(
                    'name' => $name,
                    'description' => $description,
                    'product_type' => $product_type,
                    'price_cents' => $price_cents,
                    'currency' => $currency,
                    'sku' => $sku,
                    'stock_tracking' => $stock_tracking,
                    'stock_qty' => $stock_qty,
                    'shipping_required' => $shipping_required,
                    'images' => $images,
                ), array('id' => $existing));
                $product_id = (int) $existing;
            } else {
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                $wpdb->insert($table, array(
                    'user_id' => $user_id,
                    'name' => $name,
                    'slug' => $slug,
                    'description' => $description,
                    'product_type' => $product_type,
                    'price_cents' => $price_cents,
                    'currency' => $currency,
                    'sku' => $sku,
                    'stock_tracking' => $stock_tracking,
                    'stock_qty' => $stock_qty,
                    'shipping_required' => $shipping_required,
                    'images' => $images,
                ));
                $product_id = (int) $wpdb->insert_id;
            }

            $product['id'] = $product_id;
            $product['slug'] = $slug;
            $product['price_cents'] = $price_cents;
            $product['buy_url'] = $product_id > 0 ? add_query_arg(array('vmo_buy_product' => $product_id), home_url('/')) : '';
            $out[] = $product;
        }

        return $out;
    }

    private function map_payment_runtime_product_type($product) {
        if (!empty($product['download_url']) || (!empty($product['type']) && $product['type'] === 'downloadable')) {
            return 'downloadable';
        }
        if (!empty($product['interval']) || (!empty($product['type']) && $product['type'] === 'subscription')) {
            return 'subscription';
        }
        if (!empty($product['type']) && $product['type'] === 'service') {
            return 'service';
        }

        return 'item';
    }

    private function apply_payment_runtime_to_website_data($website_data, $payment_runtime) {
        if (!is_array($website_data) || empty($payment_runtime['enabled'])) {
            return $website_data;
        }

        if (!empty($website_data['website_code']) && is_string($website_data['website_code'])) {
            $website_data['website_code'] = $this->normalize_payment_markup_in_html($website_data['website_code'], $payment_runtime);
        }

        if (!empty($website_data['pages_created']) && is_array($website_data['pages_created'])) {
            foreach ($website_data['pages_created'] as $index => $page) {
                if (!is_array($page) || empty($page['html']) || !is_string($page['html'])) {
                    continue;
                }
                $website_data['pages_created'][$index]['html'] = $this->normalize_payment_markup_in_html($page['html'], $payment_runtime);
            }
        }

        $website_data['payment_runtime'] = $payment_runtime;

        return $website_data;
    }

    private function normalize_payment_markup_in_html($html, $payment_runtime) {
        if (!is_string($html) || $html === '' || empty($payment_runtime['enabled'])) {
            return $html;
        }

        $mode = (string) ($payment_runtime['mode'] ?? 'none');
        $products = is_array($payment_runtime['products'] ?? null) ? array_values($payment_runtime['products']) : array();
        $donation_funds = is_array($payment_runtime['donation_funds'] ?? null) ? array_values($payment_runtime['donation_funds']) : array();
        $merchant_user_id = intval($payment_runtime['merchant_user_id'] ?? 0);
        $business_name = sanitize_text_field((string) ($payment_runtime['business_name'] ?? 'Donation'));
        if ($business_name === '') {
            $business_name = 'Donation';
        }

        $product_cursor = 0;
        $donation_cursor = 0;

        return preg_replace_callback(
            '/(<(a|button)\b[^>]*(?:class|className)=("|\')[^"\']*\bvmo-pay-trigger\b[^"\']*\3[^>]*)(>)(.*?)(<\/\2>)/is',
            function($matches) use ($mode, $products, $donation_funds, $merchant_user_id, $business_name, &$product_cursor, &$donation_cursor) {
                $open_tag = $matches[1];
                $tag_name = strtolower($matches[2]);
                $inner_html = $matches[5];
                $extra_attributes = array();

                if ($mode === 'products' && !empty($products)) {
                    $selected_product = $this->select_payment_runtime_product($open_tag, $inner_html, $products, $product_cursor);
                    if (!empty($selected_product['id'])) {
                        $product_id = intval($selected_product['id']);
                        $checkout_url = add_query_arg(array('vmo_buy_product' => $product_id), home_url('/'));

                        if (stripos($open_tag, 'data-vmo-buy') === false) {
                            $extra_attributes[] = 'data-vmo-buy="1"';
                        }
                        if (stripos($open_tag, 'data-product-id=') === false) {
                            $extra_attributes[] = 'data-product-id="' . $product_id . '"';
                        }
                        if (stripos($open_tag, 'data-name=') === false) {
                            $extra_attributes[] = 'data-name="' . esc_attr((string) ($selected_product['name'] ?? 'Product')) . '"';
                        }
                        if ($tag_name === 'a' && stripos($open_tag, 'href=') === false) {
                            $extra_attributes[] = 'href="' . esc_url($checkout_url) . '"';
                        }
                    }
                } elseif ($mode === 'donations' && $merchant_user_id > 0) {
                    $fund = !empty($donation_funds)
                        ? $donation_funds[$donation_cursor % count($donation_funds)]
                        : array('name' => 'General Donation', 'amounts' => array(2000, 5000, 10000));
                    $available_amounts = is_array($fund['amounts'] ?? null) ? array_values(array_filter(array_map('intval', $fund['amounts']))) : array();
                    if (empty($available_amounts)) {
                        $available_amounts = array(2000, 5000, 10000);
                    }

                    $amount_cents = $this->extract_price_cents_from_text(wp_strip_all_tags($inner_html));
                    if ($amount_cents <= 0) {
                        $amount_cents = $available_amounts[$donation_cursor % count($available_amounts)];
                    }
                    $donation_cursor++;

                    if (stripos($open_tag, 'data-vmo-buy') === false) {
                        $extra_attributes[] = 'data-vmo-buy="1"';
                    }
                    if (stripos($open_tag, 'data-merchant=') === false) {
                        $extra_attributes[] = 'data-merchant="' . intval($merchant_user_id) . '"';
                    }
                    if (stripos($open_tag, 'data-amount=') === false) {
                        $extra_attributes[] = 'data-amount="' . intval($amount_cents) . '"';
                    }
                    if (stripos($open_tag, 'data-name=') === false) {
                        $extra_attributes[] = 'data-name="' . esc_attr((string) ($fund['name'] ?? 'Donation')) . '"';
                    }
                    if (stripos($open_tag, 'data-desc=') === false) {
                        $extra_attributes[] = 'data-desc="' . esc_attr('Donation to ' . $business_name) . '"';
                    }
                    if (stripos($open_tag, 'data-currency=') === false) {
                        $extra_attributes[] = 'data-currency="usd"';
                    }
                    if ($tag_name === 'a' && stripos($open_tag, 'href=') === false) {
                        $extra_attributes[] = 'href="#"';
                    }
                }

                if (empty($extra_attributes)) {
                    return $matches[0];
                }

                return rtrim($open_tag) . ' ' . implode(' ', $extra_attributes) . $matches[4] . $inner_html . $matches[6];
            },
            $html
        );
    }

    private function select_payment_runtime_product($open_tag, $inner_html, $products, &$cursor) {
        $search_space = strtolower(wp_strip_all_tags($open_tag . ' ' . $inner_html));

        foreach ($products as $product) {
            $product_name = strtolower((string) ($product['name'] ?? ''));
            $product_slug = strtolower((string) ($product['slug'] ?? ''));

            if (($product_name !== '' && strpos($search_space, $product_name) !== false)
                || ($product_slug !== '' && strpos($search_space, $product_slug) !== false)) {
                return $product;
            }
        }

        if (empty($products)) {
            return array();
        }

        $selected = $products[$cursor % count($products)];
        $cursor++;

        return $selected;
    }

    private function extract_price_cents_from_text($text) {
        $text = trim((string) $text);
        if ($text === '') {
            return 0;
        }

        if (preg_match('/\$\s*([0-9]+(?:\.[0-9]{1,2})?)/', $text, $matches)) {
            return intval(round(((float) $matches[1]) * 100));
        }

        if (preg_match('/([0-9]+(?:\.[0-9]{1,2})?)\s*(usd|dollars?)/i', $text, $matches)) {
            return intval(round(((float) $matches[1]) * 100));
        }

        return 0;
    }

    private function gather_website_markup_fragments($value, array &$fragments, $depth = 0) {
        if ($depth > 6) {
            return;
        }

        if (is_string($value)) {
            $trimmed = trim($value);
            if ($trimmed !== '') {
                $fragments[] = $trimmed;
            }
            return;
        }

        if (!is_array($value)) {
            return;
        }

        foreach ($value as $entry) {
            $this->gather_website_markup_fragments($entry, $fragments, $depth + 1);
        }
    }

    private function infer_website_media_complete($website_result = array(), $qa_report = array(), $draft_assembled = false) {
        $website_result = is_array($website_result) ? $website_result : array();
        $qa_report = is_array($qa_report) ? $qa_report : array();

        $checklist = is_array($qa_report['checklist'] ?? null) ? $qa_report['checklist'] : array();
        if (array_key_exists('media_complete', $checklist)) {
            return !empty($checklist['media_complete']);
        }

        $fragments = array();
        $this->gather_website_markup_fragments($website_result['homepage_html'] ?? null, $fragments);
        $this->gather_website_markup_fragments($website_result['content'] ?? null, $fragments);
        $this->gather_website_markup_fragments($website_result['website_code'] ?? null, $fragments);
        $this->gather_website_markup_fragments($website_result['pages_map'] ?? null, $fragments);
        $this->gather_website_markup_fragments($website_result['pages_created'] ?? null, $fragments);

        if (empty($fragments)) {
            return (bool) $draft_assembled;
        }

        $combined_markup = strtolower(implode("\n", $fragments));
        return !preg_match('/data-vmo-generate=|data-vmo-sora=|\[(generated_image|generated_video):|placeholder\.com|picsum\.photos|example\.com|via\.placeholder/i', $combined_markup);
    }

    private function build_business_operating_state_snapshot($user_id, $session_id, $business_package, $website_result, $business_intelligence = array()) {
        $payment_runtime = is_array($website_result['payment_runtime'] ?? null)
            ? $website_result['payment_runtime']
            : (is_array($business_package['payment_runtime'] ?? null) ? $business_package['payment_runtime'] : array());
        if (empty($payment_runtime)) {
            $payment_runtime = $this->derive_payment_runtime_snapshot($business_intelligence, $website_result);
        }
        $qa_report = is_array($business_package['latest_website_qa'] ?? null) ? $business_package['latest_website_qa'] : array();
        $pages_count = is_array($website_result['pages_created'] ?? null) ? count($website_result['pages_created']) : 0;
        if ($pages_count <= 0 && is_array($website_result['pages_map'] ?? null)) {
            $pages_count = count($website_result['pages_map']);
        }
        $draft_assembled = ($pages_count > 0) || !empty($website_result['preview_url']) || !empty($website_result['post_id']);
        $media_complete = $this->infer_website_media_complete($website_result, $qa_report, $draft_assembled);
        $preview_ready = $draft_assembled && $media_complete;

        return array(
            'user_id' => (int) $user_id,
            'session_id' => (string) $session_id,
            'generated_at' => current_time('mysql'),
            'business' => array(
                'name' => sanitize_text_field((string) ($business_intelligence['business_name'] ?? '')),
                'type' => sanitize_text_field((string) ($business_intelligence['business_type'] ?? '')),
                'industry' => sanitize_text_field((string) ($business_intelligence['industry'] ?? '')),
                'location' => sanitize_text_field((string) ($business_intelligence['location'] ?? '')),
            ),
            'website' => array(
                'draft_assembled' => $draft_assembled,
                'media_complete' => $media_complete,
                'preview_ready' => $preview_ready,
                'draft_ready' => $preview_ready,
                'pages_count' => $pages_count,
                'publish_ready' => $preview_ready && !empty($qa_report['publish_ready']),
                'qa_checklist' => is_array($qa_report['checklist'] ?? null) ? $qa_report['checklist'] : array(),
            ),
            'systems' => array(
                'payments' => array(
                    'enabled' => !empty($payment_runtime['enabled']),
                    'mode' => (string) ($payment_runtime['mode'] ?? 'none'),
                    'checkout_ready' => !empty($payment_runtime['checkout_ready']),
                    'products_count' => is_array($payment_runtime['products'] ?? null) ? count($payment_runtime['products']) : 0,
                    'account_id' => (string) ($payment_runtime['account']['account_id'] ?? ''),
                    'reason' => (string) ($payment_runtime['account']['reason'] ?? ''),
                ),
                'phone' => array(
                    'ready' => !empty($business_package['phone_service_provisioned']['provisioned']),
                    'details' => is_array($business_package['phone_service_provisioned'] ?? null) ? $business_package['phone_service_provisioned'] : array(),
                ),
                'seo' => array(
                    'ready' => !empty($business_package['seo_service_provisioned']['provisioned']),
                    'details' => is_array($business_package['seo_service_provisioned'] ?? null) ? $business_package['seo_service_provisioned'] : array(),
                ),
                'email_marketing' => array(
                    'ready' => (($business_package['email_marketing_service_provisioned']['status'] ?? '') === 'success'),
                    'details' => is_array($business_package['email_marketing_service_provisioned'] ?? null) ? $business_package['email_marketing_service_provisioned'] : array(),
                ),
                'contact_management' => array(
                    'ready' => !empty($business_package['contact_management_initialized']),
                    'details' => is_array($business_package['contact_management_initialized'] ?? null) ? $business_package['contact_management_initialized'] : array(),
                ),
            ),
        );
    }

    private function extract_owner_email_from_payment_context($responses) {
        $candidate = sanitize_email((string) ($responses['business_email'] ?? ''));
        if ($candidate !== '') {
            return $candidate;
        }

        $user = wp_get_current_user();
        return sanitize_email((string) ($user->user_email ?? ''));
    }

    private function infer_business_type_from_payment_context($responses) {
        $business_type = strtolower((string) ($responses['business_type'] ?? ''));
        if (strpos($business_type, 'llc') !== false || strpos($business_type, 'company') !== false || strpos($business_type, 'inc') !== false) {
            return 'company';
        }

        return 'individual';
    }

    private function summarize_payment_runtime_products($responses) {
        $names = array();
        $products = is_array($responses['products_inventory'] ?? null) ? $responses['products_inventory'] : array();
        foreach ($products as $product) {
            if (is_array($product) && !empty($product['name'])) {
                $names[] = sanitize_text_field((string) $product['name']);
            }
        }
        if (empty($names) && !empty($responses['offerings']) && is_array($responses['offerings'])) {
            $names = array_values(array_filter(array_map('sanitize_text_field', $responses['offerings'])));
        }

        return !empty($names)
            ? 'Sells ' . implode(', ', array_slice($names, 0, 5)) . (count($names) > 5 ? '...' : '')
            : 'Online payments via VMO';
    }

    private function summarize_payment_runtime_reason($responses) {
        $funds = $this->extract_payment_runtime_donation_funds($responses);
        $names = array();
        foreach ($funds as $fund) {
            if (is_array($fund) && !empty($fund['name'])) {
                $names[] = sanitize_text_field((string) $fund['name']);
            }
        }

        return !empty($names)
            ? 'Collects donations for ' . implode(', ', array_slice($names, 0, 5)) . (count($names) > 5 ? '...' : '')
            : 'Collects donations and contributions via VMO';
    }

    private function format_consultation_email($package) {
        ob_start();
        ?>
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <title>Your Business Package</title>
        </head>
        <body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
            <div style="max-width: 600px; margin: 0 auto; background: #f9f9f9; padding: 20px;">
                <header style="background: #1a1a2e; color: #00ffff; padding: 20px; text-align: center; border-radius: 8px 8px 0 0;">
                    <h1 style="margin: 0;">🏢 Your Comprehensive Business Package</h1>
                    <p style="margin: 10px 0 0 0; color: #ccffff;">VMO AI Business Intelligence has analyzed your consultation</p>
                </header>
                
                <main style="background: white; padding: 30px; border-radius: 0 0 8px 8px;">
                    <h2 style="color: #1a1a2e;">🎉 Congratulations!</h2>
                    <p>Your comprehensive business analysis is complete. We've generated a complete business package including:</p>
                    
                    <ul style="color: #444; line-height: 1.8;">
                        <li>📊 Complete Business Analysis with SWOT</li>
                        <li>🏗️ Website Architecture Plan</li>
                        <li>🔍 SEO Strategy & Implementation</li>
                        <li>📄 Legal Documentation Requirements</li>
                        <li>📈 Digital Marketing Strategy</li>
                        <li>⚙️ Operations & Systems Plan</li>
                        <li>🗺️ Implementation Roadmap</li>
                    </ul>
                    
                    <div style="background: #f0f8ff; padding: 20px; border-radius: 8px; margin: 20px 0;">
                        <h3 style="margin: 0 0 10px 0; color: #1a1a2e;">📋 Executive Summary</h3>
                        <p style="margin: 0;"><?php echo nl2br(esc_html(substr($package['executive_summary'] ?? '', 0, 500))); ?>...</p>
                    </div>
                    
                    <div style="text-align: center; margin: 30px 0;">
                        <a href="<?php echo admin_url('admin.php?page=vmo-ai-builder-main'); ?>" 
                           style="background: #00ffff; color: #1a1a2e; padding: 15px 30px; text-decoration: none; border-radius: 5px; font-weight: bold; display: inline-block;">
                            🚀 View Your Complete Website & Business Package
                        </a>
                    </div>
                    
                    <footer style="background: #f0f8ff; padding: 20px; border-radius: 8px; text-align: center; margin-top: 30px;">
                        <p style="margin: 0; color: #1a1a2e;"><strong>Visit your VMO AI Builder to see your fully generated website and access all business documents.</strong></p>
                        <p style="margin: 10px 0 0 0; color: #666; font-size: 0.9em;">All recommendations are provided in good faith using AI analysis. Please consult with professional advisors for specific legal, financial, or business decisions.</p>
                    </footer>
                </main>
            </div>
        </body>
        </html>
        <?php
        return ob_get_clean();
    }
    
    /**
     * Generate phone service recommendation based on business analysis
     */
    private function generate_phone_service_recommendation($responses) {
        $business_context = $this->format_business_context($responses);
        
        // ALWAYS recommend phone service with compelling business case
        $prompt = "As a business consultant, create a compelling recommendation for why this business NEEDS a professional AI phone service. Focus on the benefits and ROI:

BUSINESS CONTEXT:
" . $business_context . "

Create a strong business case explaining why a professional AI phone service with 24/7 availability, multi-language support, and intelligent call routing will significantly benefit ANY business. Include:

1. RECOMMENDED: Yes (always)
2. CONFIDENCE: High (always) 
3. REASONS: Compelling business benefits specific to their industry
4. IMPLEMENTATION_PRIORITY: High (always)
5. EXPECTED_ROI: Specific benefits like increased leads, better customer service, 24/7 availability
6. COMPETITIVE_ADVANTAGE: How this gives them an edge over competitors

Format as JSON for easy parsing.";

        $ai_response = $this->call_openai_api($prompt, 1000);
        
        // Try to parse JSON response, ensure phone service is always recommended
        $phone_recommendation = json_decode($ai_response, true);
        if (!$phone_recommendation) {
            $phone_recommendation = array(
                'raw_analysis' => $ai_response,
                'RECOMMENDED' => 'Yes',
                'CONFIDENCE' => 'High',
                'REASONS' => ['Professional customer service', '24/7 availability', 'Lead capture', 'Competitive advantage'],
                'IMPLEMENTATION_PRIORITY' => 'High',
                'EXPECTED_ROI' => 'Increased customer satisfaction and lead conversion'
            );
        }
        
        return $phone_recommendation;
    }
    
    /**
     * Generate SEO service recommendation (always recommend)
     */
    private function generate_seo_service_recommendation($responses) {
        $business_context = $this->format_business_context($responses);
        
        // ALWAYS recommend SEO services with compelling business case
        $prompt = "As an SEO specialist, create a compelling recommendation for why this business NEEDS comprehensive SEO services. Focus on the benefits and ROI:

BUSINESS CONTEXT:
" . $business_context . "

Create a strong business case explaining why professional SEO services with keyword optimization, local search visibility, and content marketing will significantly benefit ANY business. Include:

1. RECOMMENDED: Yes (always)
2. CONFIDENCE: High (always) 
3. REASONS: Compelling SEO benefits specific to their industry
4. IMPLEMENTATION_PRIORITY: High (always)
5. EXPECTED_ROI: Specific benefits like increased organic traffic, higher search rankings, more leads
6. COMPETITIVE_ADVANTAGE: How SEO gives them an edge over competitors
7. LOCAL_SEO_BENEFITS: Why local search optimization is crucial for any business
8. CONTENT_STRATEGY: How content marketing drives organic growth

Format as JSON for easy parsing.";

        $ai_response = $this->call_openai_api($prompt, 1000);
        
        // Try to parse JSON response, ensure SEO service is always recommended
        $seo_recommendation = json_decode($ai_response, true);
        if (!$seo_recommendation) {
            $seo_recommendation = array(
                'raw_analysis' => $ai_response,
                'RECOMMENDED' => 'Yes',
                'CONFIDENCE' => 'High',
                'REASONS' => ['Increased organic traffic', 'Higher search rankings', 'Local search visibility', 'Competitive advantage'],
                'IMPLEMENTATION_PRIORITY' => 'High',
                'EXPECTED_ROI' => 'More website visitors and higher quality leads from search engines',
                'LOCAL_SEO_BENEFITS' => 'Dominate local search results and attract nearby customers',
                'CONTENT_STRATEGY' => 'Strategic content that ranks well and converts visitors'
            );
        }
        
        return $seo_recommendation;
    }
    
    /**
     * Generate email marketing service recommendation (always recommend)
     */
    private function generate_email_marketing_recommendation($responses) {
        $business_context = $this->format_business_context($responses);
        
        // ALWAYS recommend email marketing services with compelling business case
        $prompt = "As an email marketing specialist, create a compelling recommendation for why this business NEEDS professional email marketing campaigns. Focus on the benefits and ROI:

BUSINESS CONTEXT:
" . $business_context . "

Create a strong business case explaining why email marketing with automation, segmentation, and conversion optimization will significantly benefit ANY business. Include:

1. RECOMMENDED: Yes (always)
2. CONFIDENCE: High (always) 
3. REASONS: Compelling email marketing benefits specific to their industry
4. IMPLEMENTATION_PRIORITY: High (always)
5. EXPECTED_ROI: Specific benefits like customer retention, repeat sales, lead nurturing
6. COMPETITIVE_ADVANTAGE: How email marketing gives them an edge over competitors
7. AUTOMATION_BENEFITS: How automated sequences save time while increasing sales
8. CUSTOMER_RETENTION: Why email is the best channel for keeping customers engaged

Format as JSON for easy parsing.";

        $ai_response = $this->call_openai_api($prompt, 1000);
        
        // Try to parse JSON response, ensure email marketing is always recommended
        $email_recommendation = json_decode($ai_response, true);
        if (!$email_recommendation) {
            $email_recommendation = array(
                'raw_analysis' => $ai_response,
                'RECOMMENDED' => 'Yes',
                'CONFIDENCE' => 'High',
                'REASONS' => ['Higher customer retention', 'Automated lead nurturing', 'Increased repeat sales', 'Direct customer communication'],
                'IMPLEMENTATION_PRIORITY' => 'High',
                'EXPECTED_ROI' => 'Higher lifetime customer value and increased repeat business',
                'AUTOMATION_BENEFITS' => 'Save time while maintaining consistent customer engagement',
                'CUSTOMER_RETENTION' => 'Keep customers engaged and coming back for more business'
            );
        }
        
        return $email_recommendation;
    }
    
    /**
     * Handle automatic email marketing service provisioning with competitive analysis
     */
    private function handle_email_marketing_service_provisioning($package, $responses) {
        try {
            // Email marketing service is ALWAYS provisioned automatically with enhanced research
            $user_id = get_current_user_id();
            
            error_log("VMO Email Marketing: Starting automatic email marketing provisioning with enhanced AI research for user {$user_id}");
            
            // Step 1: Conduct deep competitive analysis using enhanced OpenAI research system
            $competitive_analysis = $this->conduct_deep_competitive_analysis($responses);
            
            // Step 2: Generate competitive email marketing strategy with enhanced insights
            $email_strategy = $this->generate_competitive_email_strategy($competitive_analysis, [
                'user_id' => $user_id,
                'business_name' => $this->extract_business_info($responses, 'business_name')
            ], $responses);
            
            // Step 3: Set up email marketing campaigns automatically with AI optimization
            $this->setup_email_marketing_campaigns($email_strategy, ['user_id' => $user_id]);
            $this->setup_lead_nurturing_sequences($email_strategy, ['user_id' => $user_id]);
            $this->setup_email_analytics_tracking(['user_id' => $user_id]);
            $this->setup_ongoing_email_optimization(['user_id' => $user_id]);
            
            // Step 4: Schedule ongoing email marketing automation with enhanced monitoring
            $this->schedule_email_marketing_automation(['user_id' => $user_id]);
            
            // Step 5: Log successful provisioning with research quality metrics
            error_log("VMO Email Marketing: Successfully provisioned automatic email marketing with enhanced research for user {$user_id}");
            
            return [
                'status' => 'success',
                'message' => 'Email marketing service automatically provisioned with enhanced AI competitive analysis',
                'research_system' => 'enhanced_openai_deep_research',
                'competitive_insights' => $competitive_analysis,
                'email_strategy' => $email_strategy,
                'research_quality_score' => $competitive_analysis['analysis_quality_score'] ?? 0,
                'strategic_advantages' => $competitive_analysis['strategic_insights'] ?? [],
                'provisioned_at' => current_time('mysql')
            ];

        } catch (\Throwable $e) {
            error_log('VMO Email Marketing: Enhanced provisioning error - ' . $e->getMessage());
            return [
                'status' => 'error',
                'message' => 'Failed to automatically provision email marketing service: ' . $e->getMessage()
            ];
        }
    }

    private function generate_competitive_email_strategy($competitive_analysis, $user_data, $consultation_data) {
        try {
            $prompt = "Based on this enhanced competitive analysis, generate a comprehensive email marketing strategy:

ENHANCED COMPETITIVE ANALYSIS:
" . json_encode($competitive_analysis, JSON_PRETTY_PRINT) . "

USER BUSINESS DATA:
" . json_encode($user_data, JSON_PRETTY_PRINT) . "

CONSULTATION DATA:
" . json_encode($consultation_data, JSON_PRETTY_PRINT) . "

Generate a detailed email marketing strategy that leverages the competitive intelligence for:
1. COMPETITIVE EMAIL POSITIONING - How to differentiate from competitors based on analysis
2. TARGET AUDIENCE SEGMENTATION - Based on competitive gaps and opportunities
3. EMAIL CAMPAIGN TYPES - Welcome series, nurture sequences, promotional campaigns
4. CONTENT STRATEGY - Subject lines, messaging, CTAs that outperform competitors
5. AUTOMATION SEQUENCES - Drip campaigns, abandoned cart, re-engagement
6. FREQUENCY AND TIMING - Optimal send times based on competitive research
7. PERSONALIZATION TACTICS - Dynamic content and behavioral triggers
8. INTEGRATION STRATEGY - How email marketing connects with website and phone services
9. PERFORMANCE METRICS - KPIs to track and competitive benchmarks
10. CONTENT CALENDAR - 3-month strategic email content plan

Format as detailed JSON strategy that can be implemented automatically.";

            // Use enhanced research system for strategy generation
            $response = $this->call_openai_deep_research($prompt, 'email_marketing', [
                'competitive_data' => $competitive_analysis,
                'user_context' => $user_data,
                'consultation_context' => $consultation_data
            ]);

            // Parse the enhanced strategy response
            if (is_array($response)) {
                return $response;
            } elseif (is_string($response)) {
                $parsed_strategy = json_decode($response, true);
                return $parsed_strategy ?? $this->get_default_email_strategy();
            }
            
            return $this->get_default_email_strategy();

        } catch (\Throwable $e) {
            error_log('Error generating enhanced email marketing strategy: ' . $e->getMessage());
            return $this->get_default_email_strategy();
        }
    }

    private function setup_email_marketing_campaigns($email_strategy, $user_data) {
        // Create email campaign structure in WordPress
        $campaign_data = [
            'user_id' => $user_data['user_id'] ?? get_current_user_id(),
            'business_name' => $user_data['business_name'] ?? 'Business',
            'email_strategy' => $email_strategy,
            'campaign_types' => $email_strategy['campaign_types'] ?? [],
            'automation_sequences' => $email_strategy['automation_sequences'] ?? [],
            'content_calendar' => $email_strategy['content_calendar'] ?? [],
            'created_at' => current_time('mysql'),
            'status' => 'active'
        ];

        // Store email marketing configuration
        update_option('vmo_email_marketing_' . $user_data['user_id'], $campaign_data);

        // Set up initial email templates with AI-generated designs
        $this->create_ai_generated_email_templates($email_strategy, $user_data);

        // Configure advanced email automation triggers
        $this->setup_advanced_email_automation_triggers($email_strategy, $user_data);

        // Set up weekly deep research automation for email marketing
        $this->setup_weekly_email_marketing_automation($user_data);
    }

    /**
     * Set up comprehensive weekly email marketing automation with deep research
     */
    private function setup_weekly_email_marketing_automation($user_data) {
        $user_id = $user_data['user_id'] ?? get_current_user_id();
        
        // Schedule weekly deep research for email marketing
        $automation_config = [
            'user_id' => $user_id,
            'research_schedule' => 'weekly',
            'research_day' => 'monday',
            'research_time' => '09:00:00',
            'automation_enabled' => true,
            'campaign_generation' => true,
            'auto_send_approval' => false, // User approval required
            'lead_generation' => true,
            'competitor_monitoring' => true,
            'performance_optimization' => true,
            'follow_up_automation' => true,
            'created_at' => current_time('mysql')
        ];
        
        update_option('vmo_weekly_email_automation_' . $user_id, $automation_config);
        
        if (function_exists('vmo_get_background_ops_config')) {
            $config = vmo_get_background_ops_config($user_id);
            $interval = $config['cron_interval'] ?: 'weekly';
        } else {
            $interval = 'weekly';
        }

        // Schedule the first weekly research (check if not already scheduled to prevent duplicates)
        if (!wp_next_scheduled('vmo_weekly_email_research', [$user_id])) {
            wp_schedule_event(strtotime('next monday 9:00 AM'), $interval, 'vmo_weekly_email_research', [$user_id]);
            error_log("VMO Email Marketing: Weekly automation scheduled for user {$user_id} with interval {$interval}");
        } else {
            error_log("VMO Email Marketing: Weekly automation already scheduled for user {$user_id}");
        }
    }

    /**
     * Create AI-generated email templates with advanced design
     */
    private function create_ai_generated_email_templates($email_strategy, $user_data) {
        $user_id = $user_data['user_id'] ?? get_current_user_id();
        $business_name = $user_data['business_name'] ?? 'Business';
        
        $template_prompt = "Create comprehensive email templates with advanced design for this business:

BUSINESS: {$business_name}
EMAIL STRATEGY: " . json_encode($email_strategy, JSON_PRETTY_PRINT) . "

Generate 5 different email template categories:
1. **WELCOME SERIES TEMPLATE** - Warm, professional welcome email with company branding
2. **NURTURE CAMPAIGN TEMPLATE** - Educational content template with value propositions
3. **PROMOTIONAL TEMPLATE** - Sales-focused template with compelling CTAs
4. **FOLLOW-UP TEMPLATE** - Re-engagement template with personalization
5. **NEWSLETTER TEMPLATE** - Regular update template with multiple content blocks

For each template provide:
- Complete HTML email code with inline CSS
- Responsive design for mobile optimization
- Professional color scheme matching business type
- Custom graphics suggestions and placeholders
- Dynamic content areas for personalization
- Advanced features: animations, interactive elements
- A/B testing variations for subject lines and content

Format as JSON with template_name, html_code, design_notes, and usage_guidelines.";

        $templates_response = $this->call_openai_deep_research($template_prompt, 'email_marketing', [
            'focus' => 'template_design',
            'business_context' => $user_data,
            'email_strategy' => $email_strategy
        ]);
        
        // Store generated templates
        update_option('vmo_ai_email_templates_' . $user_id, [
            'templates' => $templates_response,
            'generated_date' => current_time('mysql'),
            'business_name' => $business_name,
            'template_count' => 5
        ]);
        
        error_log("VMO Email Marketing: AI-generated email templates created for user {$user_id}");
    }

    private function setup_lead_nurturing_sequences($email_strategy, $user_data) {
        $sequences = $email_strategy['automation_sequences'] ?? [];
        
        foreach ($sequences as $sequence_type => $sequence_data) {
            $nurture_data = [
                'user_id' => $user_data['user_id'],
                'sequence_type' => $sequence_type,
                'sequence_data' => $sequence_data,
                'emails' => $this->generate_nurture_email_content($sequence_data, $user_data),
                'triggers' => $this->define_sequence_triggers($sequence_type),
                'timing' => $this->calculate_email_timing($sequence_type),
                'created_at' => current_time('mysql')
            ];

            update_option('vmo_nurture_sequence_' . $sequence_type . '_' . $user_data['user_id'], $nurture_data);
        }
    }

    private function setup_email_analytics_tracking($user_data) {
        $analytics_config = [
            'user_id' => $user_data['user_id'],
            'tracking_enabled' => true,
            'metrics_to_track' => [
                'open_rate',
                'click_rate',
                'conversion_rate',
                'unsubscribe_rate',
                'bounce_rate',
                'revenue_attribution'
            ],
            'reporting_frequency' => 'weekly',
            'benchmark_tracking' => true,
            'competitor_monitoring' => true,
            'created_at' => current_time('mysql')
        ];

        update_option('vmo_email_analytics_' . $user_data['user_id'], $analytics_config);
    }

    private function setup_ongoing_email_optimization($user_data) {
        $optimization_config = [
            'user_id' => $user_data['user_id'],
            'auto_optimization' => true,
            'ab_testing_enabled' => true,
            'subject_line_optimization' => true,
            'send_time_optimization' => true,
            'content_optimization' => true,
            'frequency_optimization' => true,
            'segmentation_refinement' => true,
            'created_at' => current_time('mysql')
        ];

        update_option('vmo_email_optimization_' . $user_data['user_id'], $optimization_config);
    }

    private function schedule_email_marketing_automation($user_data) {
        $user_id = $user_data['user_id'] ?? get_current_user_id();

        // Schedule weekly email performance analysis
        if (!wp_next_scheduled('vmo_email_performance_analysis', [$user_id])) {
            wp_schedule_event(time(), 'weekly', 'vmo_email_performance_analysis', [$user_id]);
        }

        // Schedule daily email optimization checks
        if (!wp_next_scheduled('vmo_email_optimization_check', [$user_id])) {
            wp_schedule_event(time(), 'daily', 'vmo_email_optimization_check', [$user_id]);
        }

        // Schedule bi-weekly competitive email analysis
        if (!wp_next_scheduled('vmo_email_competitive_analysis', [$user_id])) {
            wp_schedule_event(time(), 'twicedaily', 'vmo_email_competitive_analysis', [$user_id]);
        }

        // Schedule monthly email strategy review
        if (!wp_next_scheduled('vmo_email_strategy_review', [$user_id])) {
            wp_schedule_event(time(), 'monthly', 'vmo_email_strategy_review', [$user_id]);
        }
    }

    // ============== EMAIL MARKETING AUTOMATION EXECUTION FUNCTIONS ==============

    public function execute_email_performance_analysis($user_id) {
        try {
            $email_config = get_option('vmo_email_marketing_' . $user_id, []);
            if (empty($email_config)) return;

            $analytics_data = $this->analyze_email_performance($user_id);
            $insights = $this->generate_email_insights($analytics_data);
            $optimizations = $this->recommend_email_optimizations($insights);

            // Apply automatic optimizations
            $this->apply_email_optimizations($optimizations, $user_id);

            // Store analysis results
            update_option('vmo_email_performance_' . $user_id . '_' . date('Y_m_d'), [
                'analytics' => $analytics_data,
                'insights' => $insights,
                'optimizations' => $optimizations,
                'analyzed_at' => current_time('mysql')
            ]);

            error_log("VMO Email Marketing: Performance analysis completed for user {$user_id}");

        } catch (\Throwable $e) {
            error_log('Email performance analysis error: ' . $e->getMessage());
        }
    }

    public function execute_email_optimization_check($user_id) {
        try {
            $optimization_config = get_option('vmo_email_optimization_' . $user_id, []);
            if (empty($optimization_config) || !$optimization_config['auto_optimization']) return;

            // Check for optimization opportunities
            $opportunities = $this->identify_email_optimization_opportunities($user_id);
            
            if (!empty($opportunities)) {
                $this->apply_email_optimizations($opportunities, $user_id);
                
                error_log("VMO Email Marketing: Applied " . count($opportunities) . " optimizations for user {$user_id}");
            }

        } catch (\Throwable $e) {
            error_log('Email optimization check error: ' . $e->getMessage());
        }
    }

    public function execute_email_competitive_analysis($user_id) {
        try {
            $email_config = get_option('vmo_email_marketing_' . $user_id, []);
            if (empty($email_config)) return;

            $business_data = $email_config['email_strategy'] ?? [];
            $location = $business_data['location'] ?? 'USA';
            $business_type = $business_data['business_type'] ?? 'business';

            // Re-analyze competitors for email marketing updates
            // ✅ FIX: conduct_deep_competitive_analysis expects a $responses array, not 3 strings.
            // Build a compatible responses array from the email strategy data.
            $competitive_analysis = $this->conduct_deep_competitive_analysis(array(
                array('question' => 'business_name', 'answer' => $business_type),
                array('question' => 'location', 'answer' => $location),
                array('question' => 'industry', 'answer' => 'email_marketing')
            ));
            
            // Update email strategy based on new competitive insights
            $updated_strategy = $this->refine_email_strategy_from_competitors($competitive_analysis, $email_config);
            
            // Apply strategy updates
            $this->update_email_campaigns_from_strategy($updated_strategy, $user_id);

            error_log("VMO Email Marketing: Competitive analysis updated for user {$user_id}");

        } catch (\Throwable $e) {
            error_log('Email competitive analysis error: ' . $e->getMessage());
        }
    }

    /**
     * Execute email strategy review (called by WordPress cron)
     * v8.2.1: Now validates plan-based access before running
     */
    public function execute_email_strategy_review($user_id) {
        // ✅ PLAN CHECK v8.2.1: Verify user's plan supports background ops
        if (function_exists('vmo_can_run_background_ops') && !vmo_can_run_background_ops($user_id)) {
            error_log("VMO Email Strategy: User {$user_id} plan does not support background ops - skipping");
            if (function_exists('vmo_send_background_ops_upgrade_prompt')) {
                vmo_send_background_ops_upgrade_prompt($user_id, 'email_strategy_review');
            }
            // Clear this schedule since user shouldn't have background ops
            wp_clear_scheduled_hook('vmo_monthly_email_strategy_review', array($user_id));
            return;
        }
        
        try {
            // ✅ TOKEN DEDUCTION v8.1.0: Charge for monthly email strategy review (2,000 tokens)
            if (function_exists('vmo_deduct_tokens')) {
                $cost = defined('VMO_TOKENS_EMAIL_STRATEGY_REVIEW') ? VMO_TOKENS_EMAIL_STRATEGY_REVIEW : 2000;
                if (!vmo_deduct_tokens($user_id, $cost, 'email_strategy_review', 'Monthly email strategy review')) {
                    error_log("VMO Email Strategy: Insufficient tokens for user {$user_id}");
                    return;
                }
            }
            
            $email_config = get_option('vmo_email_marketing_' . $user_id, []);
            if (empty($email_config)) return;

            // Comprehensive monthly email strategy review
            $performance_data = $this->get_email_performance_history($user_id);
            $competitive_landscape = $this->get_latest_competitive_analysis($user_id, 'email_marketing');
            
            $strategy_review = $this->generate_email_strategy_review($performance_data, $competitive_landscape);
            
            // Update email strategy based on review
            $this->implement_strategy_updates($strategy_review, $user_id);

            error_log("VMO Email Marketing: Monthly strategy review completed for user {$user_id}");

        } catch (\Throwable $e) {
            error_log('Email strategy review error: ' . $e->getMessage());
        }
    }

    // ============== WEEKLY EMAIL AUTOMATION HELPER FUNCTIONS ==============

    private function get_user_business_context($user_id) {
        $email_config = get_option('vmo_email_marketing_' . $user_id, []);
        return $email_config['email_strategy'] ?? [];
    }

    private function calculate_research_quality($research_data) {
        if (empty($research_data)) return 0;
        
        $quality_score = 0;
        $content_length = is_string($research_data) ? strlen($research_data) : 0;
        
        if ($content_length > 2000) $quality_score += 30;
        elseif ($content_length > 1000) $quality_score += 20;
        else $quality_score += 10;
        
        // Add points for key research elements
        $research_text = is_string($research_data) ? $research_data : json_encode($research_data);
        $key_elements = ['competitor', 'trend', 'opportunity', 'strategy', 'insight'];
        
        foreach ($key_elements as $element) {
            if (stripos($research_text, $element) !== false) {
                $quality_score += 14;
            }
        }
        
        return min($quality_score, 100);
    }

    private function extract_actionable_insights($research_data) {
        $research_text = is_string($research_data) ? $research_data : json_encode($research_data);
        $insights = [];
        
        // Extract actionable patterns
        if (preg_match_all('/should\s+[^.]{20,100}\./i', $research_text, $matches)) {
            $insights['recommendations'] = array_slice($matches[0], 0, 5);
        }
        
        if (preg_match_all('/opportunity\s+[^.]{20,100}\./i', $research_text, $matches)) {
            $insights['opportunities'] = array_slice($matches[0], 0, 5);
        }
        
        return $insights;
    }

    private function simulate_lead_generation($user_id, $lead_strategy) {
        global $wpdb;

        $max_leads = 25;
        $leads = array();
        $seen = array();
        $contacts_table = $wpdb->prefix . 'vmo_contacts';

        if ($wpdb->get_var("SHOW TABLES LIKE '{$contacts_table}'") === $contacts_table) {
            $contact_rows = $wpdb->get_results($wpdb->prepare(
                "SELECT id, full_name, first_name, last_name, email, company, source, lead_score, lifecycle_stage, created_at
                 FROM {$contacts_table}
                 WHERE user_id = %d AND lifecycle_stage <> 'customer'
                 ORDER BY lead_score DESC, created_at DESC
                 LIMIT %d",
                $user_id,
                $max_leads
            ), ARRAY_A);

            foreach ((array) $contact_rows as $contact) {
                $name = trim((string) ($contact['full_name'] ?? ''));
                if ($name === '') {
                    $name = trim((string) (($contact['first_name'] ?? '') . ' ' . ($contact['last_name'] ?? '')));
                }

                $email = is_email($contact['email'] ?? '') ? sanitize_email($contact['email']) : '';
                $dedupe_key = strtolower($email !== '' ? $email : ($name !== '' ? $name : 'contact_' . (int) ($contact['id'] ?? 0)));
                if (isset($seen[$dedupe_key])) {
                    continue;
                }
                $seen[$dedupe_key] = true;

                $score = max(0, min(100, (int) ($contact['lead_score'] ?? 0)));
                $segment = sanitize_key((string) ($contact['lifecycle_stage'] ?? ''));
                if ($segment === '' || in_array($segment, array('new', 'engaged'), true)) {
                    $segment = $score >= 85 ? 'high_value' : ($score >= 70 ? 'warm_lead' : 'cold_lead');
                }

                $leads[] = array(
                    'id' => 'contact_' . (int) ($contact['id'] ?? 0),
                    'name' => $name,
                    'email' => $email,
                    'company' => sanitize_text_field((string) ($contact['company'] ?? '')),
                    'source' => sanitize_text_field((string) ($contact['source'] ?? 'crm')),
                    'score' => $score,
                    'segment' => $segment,
                    'generated_at' => sanitize_text_field((string) ($contact['created_at'] ?? current_time('mysql'))),
                );
            }
        }

        if (count($leads) < $max_leads && $this->deep_research instanceof VMO_Deep_Research && method_exists($this->deep_research, 'get_latest_research')) {
            $latest_research = $this->deep_research->get_latest_research($user_id);
            $research_leads = is_array($latest_research['potential_leads'] ?? null) ? $latest_research['potential_leads'] : array();

            foreach ($research_leads as $lead) {
                $name = sanitize_text_field((string) ($lead['contact_person'] ?? $lead['full_name'] ?? $lead['company_name'] ?? $lead['company'] ?? ''));
                $email = is_email($lead['email'] ?? '') ? sanitize_email($lead['email']) : '';
                $website = esc_url_raw((string) ($lead['website'] ?? ''));
                $dedupe_key = strtolower($email !== '' ? $email : ($website !== '' ? $website : $name));
                if ($dedupe_key === '' || isset($seen[$dedupe_key])) {
                    continue;
                }
                $seen[$dedupe_key] = true;

                $leads[] = array(
                    'id' => 'research_' . substr(md5($dedupe_key), 0, 12),
                    'name' => $name,
                    'email' => $email,
                    'company' => sanitize_text_field((string) ($lead['company_name'] ?? $lead['company'] ?? '')),
                    'website' => $website,
                    'source' => sanitize_text_field((string) ($lead['evidence_source'] ?? $lead['source_url'] ?? 'deep_research')),
                    'score' => max(0, min(100, (int) ($lead['lead_score'] ?? 65))),
                    'segment' => sanitize_key((string) ($lead['priority_level'] ?? 'warm_lead')),
                    'generated_at' => current_time('mysql'),
                );

                if (count($leads) >= $max_leads) {
                    break;
                }
            }
        }

        return array_slice($leads, 0, $max_leads);
    }

    private function determine_lead_segment() {
        $segments = ['hot_prospect', 'warm_lead', 'cold_lead', 'nurture_sequence', 'high_value'];
        return $segments[array_rand($segments)];
    }

    private function update_user_leads_database($user_id, $new_leads) {
        $existing_leads = get_option('vmo_user_leads_' . $user_id, []);
        $updated_leads = array_merge($existing_leads, $new_leads);
        
        update_option('vmo_user_leads_' . $user_id, $updated_leads);
        error_log("VMO Email Automation: Updated leads database for user {$user_id} with " . count($new_leads) . " new leads");
    }

    private function analyze_lead_segments($leads) {
        $segments = [];
        foreach ($leads as $lead) {
            $segment = $lead['segment'] ?? 'general';
            $segments[$segment] = ($segments[$segment] ?? 0) + 1;
        }
        return $segments;
    }

    private function parse_campaign_specifications($campaigns) {
        // Parse and structure campaign data from AI response
        if (is_string($campaigns)) {
            $parsed = json_decode($campaigns, true);
            return $parsed ?? ['default_campaign' => ['type' => 'nurture', 'emails' => 3]];
        }
        return is_array($campaigns) ? $campaigns : [];
    }

    private function process_generated_email_content($email_content, $user_id) {
        // Process and format email content for storage
        $processed_content = [
            'user_id' => $user_id,
            'content_data' => $email_content,
            'generated_at' => current_time('mysql'),
            'status' => 'ready_for_approval'
        ];
        
        return $processed_content;
    }

    private function generate_email_preview_links($email_content) {
        // Generate preview links for email content
        $preview_links = [];
        
        if (is_array($email_content) && isset($email_content['content_data'])) {
            foreach ($email_content['content_data'] as $campaign => $content) {
                $preview_links[$campaign] = admin_url('admin.php?page=vmo-ai-builder-main&preview_email=' . base64_encode($campaign)); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- data encoding
            }
        }
        
        return $preview_links;
    }

    private function notify_user_pending_approval($user_id, $approval_queue_id) {
        // Notify user about pending email campaigns
        $user = get_user_by('id', $user_id);
        if (!$user) return;
        
        // Store notification
        update_option('vmo_pending_notification_' . $user_id, [
            'type' => 'email_approval_required',
            'queue_id' => $approval_queue_id,
            'message' => 'Your weekly AI-generated email campaigns are ready for approval',
            'created_at' => current_time('mysql')
        ]);
        
        error_log("VMO Email Automation: Approval notification created for user {$user_id}");
    }

    private function get_user_leads($user_id) {
        return get_option('vmo_user_leads_' . $user_id, []);
    }

    private function send_campaign_emails($campaign_data, $leads, $user_id) {
        // Legacy cloud sender removed: send via local/SMTP mailer.
        error_log("VMO Email Automation: Sending campaign emails for user {$user_id} to " . count($leads) . " leads");

        require_once dirname(__FILE__) . '/class-vmo-email-router.php';
        $router = VMO_Email_Router::get_instance();
        
        // Prepare recipient list
        $recipients = [];
        foreach ($leads as $lead) {
            if (isset($lead['email']) && is_email($lead['email'])) {
                $recipients[] = $lead['email'];
            }
        }
        
        if (empty($recipients)) {
            error_log("VMO Email Automation: No valid email addresses found");
            return false;
        }

        $sent = 0;
        $failed = 0;
        foreach ($recipients as $to) {
            $res = $router->send_email(
                $to,
                $campaign_data['subject'],
                $campaign_data['html_content'],
                'marketing',
                [ 'headers' => [ 'Content-Type: text/html; charset=UTF-8' ] ]
            );
            if (is_array($res) && !empty($res['success'])) {
                $sent++;
            } else {
                $failed++;
            }
        }

        $result = [
            'success' => ($sent > 0 && $failed === 0),
            'sent' => $sent,
            'tokens_used' => 0,
        ];
        
        // Track email sends
        update_option('vmo_campaign_sent_' . $user_id . '_' . time(), [
            'campaign' => $campaign_data,
            'leads_count' => count($leads),
            'sent_count' => $result['sent'],
            'tokens_used' => $result['tokens_used'],
            'sent_at' => current_time('mysql'),
            'success' => $result['success']
        ]);
        
        // Send confirmation to user
        if ($result['success']) {
            $user = get_userdata($user_id);
            if ($user) {
                require_once dirname(__FILE__) . '/class-vmo-glassy-email-templates.php';
                $glassy_templates = new VMO_Glassy_Email_Templates();

                $remaining = null;
                $confirmation_data = [
                    'emails_sent' => $result['sent'],
                    'tokens_used' => $result['tokens_used'],
                    'subject' => $campaign_data['subject'],
                    'remaining_balance' => $remaining
                ];

                $router->send_email(
                    $user->user_email,
                    'Campaign Sent - ' . $campaign_data['campaign_name'],
                    $glassy_templates->get_email_sent_notification($confirmation_data),
                    'transactional',
                    [ 'headers' => [ 'Content-Type: text/html; charset=UTF-8' ] ]
                );
            }
        }
        
        error_log("VMO Email Automation: Campaign sent - {$result['sent']} emails, {$result['tokens_used']} tokens used");
        
        return $result['success'];
    }

    private function setup_campaign_follow_ups($campaign_data, $leads, $user_id) {
        // Set up automated follow-up sequences
        foreach ($leads as $lead) {
            wp_schedule_single_event(strtotime('+3 days'), 'vmo_campaign_followup', [
                $user_id, 
                $lead['id'], 
                $campaign_data
            ]);
        }
    }

    private function track_campaign_performance($campaign_id, $user_id) {
        // Initialize campaign performance tracking
        update_option('vmo_campaign_tracking_' . $campaign_id, [
            'user_id' => $user_id,
            'campaign_id' => $campaign_id,
            'started_at' => current_time('mysql'),
            'metrics' => [
                'sent' => 0,
                'opened' => 0,
                'clicked' => 0,
                'converted' => 0
            ]
        ]);
    }

    private function setup_weekly_follow_up_sequences($user_id, $leads) {
        // Set up weekly follow-up automation for each lead segment
        $segments = $this->analyze_lead_segments($leads);
        
        foreach ($segments as $segment => $count) {
            wp_schedule_event(
                strtotime('next week'),
                'weekly',
                'vmo_segment_followup',
                [$user_id, $segment]
            );
        }
    }

    // ============== EMAIL MARKETING HELPER FUNCTIONS ==============

    // ============== COMPREHENSIVE WEEKLY EMAIL AUTOMATION WITH DEEP RESEARCH ==============

    /**
     * Execute comprehensive weekly email marketing automation with deep research
     * This is the main function that runs weekly for each user
     * v8.2.1: Now validates plan-based access before running
     */
    public function execute_weekly_email_research($user_id) {
        // ✅ PLAN CHECK v8.2.1: Verify user's plan supports background ops
        if (function_exists('vmo_can_run_background_ops') && !vmo_can_run_background_ops($user_id)) {
            error_log("VMO Weekly Email: User {$user_id} plan does not support background ops - skipping");
            if (function_exists('vmo_send_background_ops_upgrade_prompt')) {
                vmo_send_background_ops_upgrade_prompt($user_id, 'weekly_email_research');
            }
            // Clear this schedule since user shouldn't have background ops
            wp_clear_scheduled_hook('vmo_weekly_email_research', array($user_id));
            return;
        }
        
        try {
            // ✅ TOKEN DEDUCTION v8.1.0: Charge for weekly email research (10,000 tokens)
            // This includes competitive analysis, lead generation, campaign creation, and content generation
            if (function_exists('vmo_deduct_tokens')) {
                $cost = defined('VMO_TOKENS_WEEKLY_EMAIL_RESEARCH') ? VMO_TOKENS_WEEKLY_EMAIL_RESEARCH : 10000;
                if (!vmo_deduct_tokens($user_id, $cost, 'weekly_email_research', 'Weekly email marketing automation')) {
                    error_log("VMO Weekly Email: Insufficient tokens for user {$user_id}");
                    return;
                }
            }
            
            error_log("VMO Weekly Email Automation: Starting comprehensive weekly research for user {$user_id}");
            
            // Step 0: Sync all contacts from leads, phone calls, and manual entries
            $this->execute_contact_sync($user_id);
            
            // Step 1: Conduct weekly deep research (competitive, SEO, market trends)
            $weekly_research = $this->conduct_weekly_deep_research($user_id);
            
            // Step 2: Generate and update leads list from contact database
            $updated_leads = $this->generate_weekly_leads($user_id, $weekly_research);
            
            // Step 3: Create new email campaigns based on research insights and contact categories
            $new_campaigns = $this->create_weekly_email_campaigns($user_id, $weekly_research, $updated_leads);
            
            // Step 4: Generate email content and templates
            $email_content = $this->generate_weekly_email_content($user_id, $new_campaigns, $weekly_research);
            
            // Step 5: Queue campaigns for user approval
            $approval_queue = $this->queue_campaigns_for_approval($user_id, $email_content, $new_campaigns);
            
            // Step 6: Set up follow-up automation sequences
            $this->setup_weekly_follow_up_sequences($user_id, $updated_leads);
            
            // Step 7: Update contact analytics and insights
            $contact_insights = $this->get_contact_analytics($user_id);
            
            // Store weekly automation results with contact data
            update_option('vmo_weekly_email_results_' . $user_id . '_' . date('Y_W'), [
                'research_data' => $weekly_research,
                'leads_generated' => count($updated_leads),
                'campaigns_created' => count($new_campaigns),
                'approval_queue_id' => $approval_queue,
                'contact_insights' => $contact_insights,
                'automation_date' => current_time('mysql'),
                'status' => 'completed'
            ]);
            
            error_log("VMO Weekly Email Automation: Successfully completed for user {$user_id}");
            
        } catch (\Throwable $e) {
            error_log('VMO Weekly Email Automation Error: ' . $e->getMessage());
        }
    }

    /**
     * Conduct comprehensive weekly deep research for email marketing
     */
    private function conduct_weekly_deep_research($user_id) {
        $user_business_data = $this->get_user_business_context($user_id);
        
        $research_prompt = "Conduct comprehensive weekly deep research for email marketing optimization:

BUSINESS CONTEXT:
" . json_encode($user_business_data, JSON_PRETTY_PRINT) . "

RESEARCH AREAS:
1. **COMPETITIVE EMAIL INTELLIGENCE**:
   - New competitor email campaigns this week
   - Competitor subject line strategies and performance
   - Competitor content themes and messaging
   - Competitive timing and frequency analysis

2. **MARKET TRENDS & OPPORTUNITIES**:
   - Industry trends affecting email marketing
   - Seasonal opportunities and events
   - Customer behavior pattern changes
   - Emerging email marketing techniques

3. **SEO-EMAIL INTEGRATION INSIGHTS**:
   - Keywords trending in email marketing
   - Content topics generating high engagement
   - SEO-driven email content opportunities
   - Website traffic patterns affecting email strategy

4. **LEAD GENERATION OPPORTUNITIES**:
   - New lead sources and channels
   - High-converting lead magnets in industry
   - Competitor lead generation strategies
   - Customer journey optimization points

5. **PERFORMANCE OPTIMIZATION INSIGHTS**:
   - Industry benchmark updates
   - Best performing email elements this week
   - A/B testing opportunities
   - Deliverability and engagement optimization

Provide actionable intelligence for immediate implementation.";

        $weekly_research = $this->call_openai_deep_research($research_prompt, 'competitive_intelligence', [
            'research_type' => 'weekly_email_automation',
            'user_context' => $user_business_data,
            'focus_areas' => ['email_marketing', 'lead_generation', 'competitive_analysis']
        ]);
        
        return [
            'research_data' => $weekly_research,
            'research_date' => current_time('mysql'),
            'research_quality_score' => $this->calculate_research_quality($weekly_research),
            'actionable_insights' => $this->extract_actionable_insights($weekly_research)
        ];
    }

    /**
     * Generate and update leads list based on research insights
     */
    private function generate_weekly_leads($user_id, $weekly_research) {
        $lead_generation_prompt = "Based on this week's research, identify and generate new leads:

RESEARCH INSIGHTS:
" . json_encode($weekly_research, JSON_PRETTY_PRINT) . "

LEAD GENERATION TASKS:
1. **IDENTIFY NEW LEAD SOURCES** based on competitive analysis
2. **CREATE LEAD MAGNETS** that outperform competitors
3. **OPTIMIZE LEAD CAPTURE** based on industry best practices
4. **SEGMENT EXISTING LEADS** based on new behavioral insights
5. **SCORE AND PRIORITIZE LEADS** using research intelligence

Generate specific lead generation strategies and tactics.";

        $lead_strategy = $this->call_openai_deep_research($lead_generation_prompt, 'market_research', [
            'focus' => 'lead_generation',
            'research_context' => $weekly_research
        ]);
        
        // Refresh the weekly leads set from real CRM and deep-research data.
        $new_leads = $this->simulate_lead_generation($user_id, $lead_strategy);
        
        // Update leads database
        $this->update_user_leads_database($user_id, $new_leads);
        
        return $new_leads;
    }

    /**
     * Create weekly email campaigns based on research insights
     */
    private function create_weekly_email_campaigns($user_id, $weekly_research, $updated_leads) {
        $campaign_creation_prompt = "Create weekly email campaigns based on research and lead data:

WEEKLY RESEARCH:
" . json_encode($weekly_research, JSON_PRETTY_PRINT) . "

LEADS DATA:
Lead count: " . count($updated_leads) . "
Lead segments: " . json_encode($this->analyze_lead_segments($updated_leads), JSON_PRETTY_PRINT) . "

CREATE CAMPAIGN STRATEGY:
1. **COMPETITIVE ADVANTAGE CAMPAIGNS** - Campaigns that outperform competitors
2. **TREND-BASED CAMPAIGNS** - Campaigns leveraging market trends
3. **LEAD NURTURE CAMPAIGNS** - Targeted campaigns for different lead segments
4. **RE-ENGAGEMENT CAMPAIGNS** - Campaigns to reactivate dormant leads
5. **PROMOTIONAL CAMPAIGNS** - Sales campaigns with competitive positioning

For each campaign provide:
- Campaign objective and target audience
- Email sequence (3-5 emails)
- Timing and frequency strategy
- Success metrics and KPIs
- Competitive differentiation elements

Format as detailed campaign specifications.";

        $campaigns = $this->call_openai_deep_research($campaign_creation_prompt, 'email_marketing', [
            'focus' => 'campaign_creation',
            'research_context' => $weekly_research,
            'leads_context' => $updated_leads
        ]);
        
        return $this->parse_campaign_specifications($campaigns);
    }

    /**
     * Generate comprehensive email content and templates
     */
    private function generate_weekly_email_content($user_id, $campaigns, $weekly_research) {
        $content_generation_prompt = "Generate complete email content and templates for these campaigns:

CAMPAIGNS:
" . json_encode($campaigns, JSON_PRETTY_PRINT) . "

RESEARCH CONTEXT:
" . json_encode($weekly_research, JSON_PRETTY_PRINT) . "

FOR EACH CAMPAIGN EMAIL, CREATE:
1. **COMPELLING SUBJECT LINES** (5 variations for A/B testing)
2. **EMAIL COPY** - Persuasive, research-driven content
3. **HTML EMAIL TEMPLATE** - Professional, responsive design
4. **PERSONALIZATION ELEMENTS** - Dynamic content areas
5. **CALL-TO-ACTION OPTIMIZATION** - High-converting CTAs
6. **VISUAL ELEMENTS** - Images, graphics, design specifications
7. **MOBILE OPTIMIZATION** - Mobile-first design approach
8. **ACCESSIBILITY FEATURES** - Inclusive design elements

Include special effects like:
- Animated elements for engagement
- Interactive components
- Social proof integration
- Urgency and scarcity elements
- Video/GIF integration suggestions

Format as complete email packages ready for sending.";

        $email_content = $this->call_openai_deep_research($content_generation_prompt, 'email_marketing', [
            'focus' => 'content_generation',
            'campaigns_context' => $campaigns,
            'research_context' => $weekly_research
        ]);
        
        return $this->process_generated_email_content($email_content, $user_id);
    }

    /**
     * Queue campaigns for user approval
     */
    private function queue_campaigns_for_approval($user_id, $email_content, $campaigns) {
        $approval_queue_id = 'vmo_email_approval_' . $user_id . '_' . time();
        
        $approval_data = [
            'user_id' => $user_id,
            'campaigns' => $campaigns,
            'email_content' => $email_content,
            'approval_status' => 'pending',
            'created_at' => current_time('mysql'),
            'expires_at' => date('Y-m-d H:i:s', strtotime('+7 days')),
            'auto_approve_after' => date('Y-m-d H:i:s', strtotime('+24 hours')), // Auto-approve if no action
            'preview_links' => $this->generate_email_preview_links($email_content)
        ];
        
        update_option($approval_queue_id, $approval_data);
        
        // Notify user about pending approval (email, dashboard notification, etc.)
        $this->notify_user_pending_approval($user_id, $approval_queue_id);
        
        return $approval_queue_id;
    }

    /**
     * Auto-send approved campaigns
     */
    public function process_approved_campaigns($user_id, $approval_queue_id, $approved_campaigns) {
        try {
            $approval_data = get_option($approval_queue_id);
            if (!$approval_data) return false;
            
            $leads = $this->get_user_leads($user_id);
            
            foreach ($approved_campaigns as $campaign_id => $campaign_data) {
                // Send emails to appropriate lead segments
                $this->send_campaign_emails($campaign_data, $leads, $user_id);
                
                // Set up follow-up sequences
                $this->setup_campaign_follow_ups($campaign_data, $leads, $user_id);
                
                // Track campaign performance
                $this->track_campaign_performance($campaign_id, $user_id);
            }
            
            // Mark as completed
            $approval_data['approval_status'] = 'completed';
            $approval_data['processed_at'] = current_time('mysql');
            update_option($approval_queue_id, $approval_data);
            
            error_log("VMO Email Marketing: Approved campaigns processed for user {$user_id}");
            return true;
            
        } catch (\Throwable $e) {
            error_log('Campaign processing error: ' . $e->getMessage());
            return false;
        }
    }

    private function get_default_email_strategy() {
        return [
            'positioning' => 'Professional email marketing with competitive advantage',
            'segmentation' => ['leads', 'customers', 'prospects', 'subscribers'],
            'campaign_types' => ['welcome', 'nurture', 'promotional', 'educational', 'reactivation'],
            'automation_sequences' => ['onboarding', 'nurture', 'reactivation', 'abandoned_cart'],
            'content_strategy' => [
                'subject_lines' => 'Compelling and personalized subject lines',
                'messaging' => 'Value-driven content with clear CTAs',
                'personalization' => 'Dynamic content based on user behavior'
            ],
            'frequency' => [
                'welcome_series' => 'Days 1, 3, 7, 14, 30',
                'newsletters' => 'Weekly',
                'promotional' => 'Bi-weekly',
                'nurture' => 'Every 3-4 days'
            ],
            'performance_metrics' => [
                'target_open_rate' => '25%',
                'target_click_rate' => '5%',
                'target_conversion_rate' => '2%'
            ]
        ];
    }

    private function create_email_templates($email_strategy, $user_data) {
        $templates = [
            'welcome_series' => $this->generate_welcome_email_series($email_strategy, $user_data),
            'nurture_sequence' => $this->generate_nurture_email_sequence($email_strategy, $user_data),
            'promotional_emails' => $this->generate_promotional_email_templates($email_strategy, $user_data),
            'newsletter_template' => $this->generate_newsletter_template($email_strategy, $user_data)
        ];

        update_option('vmo_email_templates_' . $user_data['user_id'], $templates);
    }

    private function setup_email_automation_triggers($email_strategy, $user_data) {
        $triggers = [
            'new_subscriber' => 'welcome_series',
            'website_visit' => 'nurture_sequence',
            'cart_abandonment' => 'abandoned_cart_sequence',
            'purchase_complete' => 'post_purchase_sequence',
            'inactive_subscriber' => 'reactivation_sequence'
        ];

        update_option('vmo_email_triggers_' . $user_data['user_id'], $triggers);
    }

    private function generate_nurture_email_content($sequence_data, $user_data) {
        try {
            $prompt = "Generate a comprehensive email nurture sequence based on this strategy:

SEQUENCE DATA:
" . json_encode($sequence_data, JSON_PRETTY_PRINT) . "

USER DATA:
" . json_encode($user_data, JSON_PRETTY_PRINT) . "

Create a series of 5 email templates for this nurture sequence including:
1. Subject lines that get opened
2. Email content that provides value
3. Clear call-to-actions
4. Personalization variables
5. Mobile-optimized formatting

Format as JSON array of email objects.";

            $response = $this->call_openai_api($prompt, 2000);
            return json_decode($response, true) ?? [];

        } catch (\Throwable $e) {
            error_log('Error generating nurture email content: ' . $e->getMessage());
            return [];
        }
    }

    private function define_sequence_triggers($sequence_type) {
        $triggers = [
            'onboarding' => ['new_subscriber', 'signup_complete'],
            'nurture' => ['website_visit', 'content_download'],
            'reactivation' => ['inactive_30_days', 'no_engagement'],
            'abandoned_cart' => ['cart_abandonment', 'checkout_incomplete']
        ];

        return $triggers[$sequence_type] ?? ['manual_trigger'];
    }

    private function calculate_email_timing($sequence_type) {
        $timing_schedules = [
            'onboarding' => ['immediately', '+1 day', '+3 days', '+7 days', '+14 days'],
            'nurture' => ['+1 day', '+4 days', '+7 days', '+14 days', '+21 days'],
            'reactivation' => ['immediately', '+3 days', '+7 days'],
            'abandoned_cart' => ['+1 hour', '+1 day', '+3 days']
        ];

        return $timing_schedules[$sequence_type] ?? ['+1 day', '+7 days'];
    }

    private function analyze_email_performance($user_id) {
        global $wpdb;

        $now = current_time('timestamp');
        $from = date('Y-m-d H:i:s', strtotime('-30 days', $now));
        $to = date('Y-m-d H:i:s', $now);
        $previous_from = date('Y-m-d H:i:s', strtotime('-60 days', $now));

        $stats = function_exists('vmo_get_email_campaign_stats')
            ? vmo_get_email_campaign_stats($user_id, $from, $to)
            : array('sent' => 0, 'open_rate' => 0, 'click_rate' => 0, 'campaigns' => 0, 'revenue' => 0);

        $campaign_names = array();
        $campaigns_table = $wpdb->prefix . 'vmo_email_campaigns';
        if ($wpdb->get_var("SHOW TABLES LIKE '{$campaigns_table}'") === $campaigns_table) {
            $campaign_rows = $wpdb->get_results($wpdb->prepare(
                "SELECT campaign_name
                 FROM {$campaigns_table}
                 WHERE user_id = %d AND sent_at BETWEEN %s AND %s
                 ORDER BY revenue_generated DESC, clicked_count DESC, opened_count DESC
                 LIMIT 3",
                $user_id,
                $from,
                $to
            ));

            foreach ((array) $campaign_rows as $campaign_row) {
                if (!empty($campaign_row->campaign_name)) {
                    $campaign_names[] = sanitize_text_field($campaign_row->campaign_name);
                }
            }
        }

        $orders_count = 0;
        $orders_table = $wpdb->prefix . 'vmo_store_orders';
        if ($wpdb->get_var("SHOW TABLES LIKE '{$orders_table}'") === $orders_table) {
            $orders_count = (int) $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM {$orders_table}
                 WHERE user_id = %d AND status = 'paid' AND created_at BETWEEN %s AND %s",
                $user_id,
                $from,
                $to
            ));
        }

        $subscriber_growth = 0.0;
        $contacts_table = $wpdb->prefix . 'vmo_contacts';
        if ($wpdb->get_var("SHOW TABLES LIKE '{$contacts_table}'") === $contacts_table) {
            $current_contacts = (int) $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM {$contacts_table} WHERE user_id = %d AND created_at BETWEEN %s AND %s",
                $user_id,
                $from,
                $to
            ));
            $previous_contacts = (int) $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM {$contacts_table} WHERE user_id = %d AND created_at BETWEEN %s AND %s",
                $user_id,
                $previous_from,
                $from
            ));
            if ($previous_contacts > 0) {
                $subscriber_growth = round((($current_contacts - $previous_contacts) / max(1, $previous_contacts)) * 100, 1);
            } elseif ($current_contacts > 0) {
                $subscriber_growth = 100.0;
            }
        }

        $open_rate = (float) ($stats['open_rate'] ?? 0);
        $click_rate = (float) ($stats['click_rate'] ?? 0);
        $conversion_rate = ($stats['sent'] ?? 0) > 0 ? round(($orders_count / max(1, (int) $stats['sent'])) * 100, 1) : 0.0;
        $engagement_rate = round(($open_rate + $click_rate) / 2, 1);
        $improvement_areas = array();

        if ($open_rate < 22) {
            $improvement_areas[] = 'subject_lines';
        }
        if ($click_rate < 4) {
            $improvement_areas[] = 'calls_to_action';
        }
        if (($stats['campaigns'] ?? 0) < 2) {
            $improvement_areas[] = 'campaign_frequency';
        }
        if (($stats['revenue'] ?? 0) <= 0) {
            $improvement_areas[] = 'offer_positioning';
        }
        if (empty($improvement_areas)) {
            $improvement_areas[] = 'maintain_current_performance';
        }

        return array(
            'sent' => (int) ($stats['sent'] ?? 0),
            'campaigns' => (int) ($stats['campaigns'] ?? 0),
            'open_rate' => $open_rate . '%',
            'click_rate' => $click_rate . '%',
            'conversion_rate' => $conversion_rate . '%',
            'unsubscribe_rate' => '0%',
            'revenue_generated' => '$' . number_format((float) ($stats['revenue'] ?? 0), 2),
            'top_performing_campaigns' => $campaign_names,
            'improvement_areas' => $improvement_areas,
            'subscriber_growth' => $subscriber_growth . '%',
            'engagement_rate' => $engagement_rate . '%',
        );
    }

    private function generate_email_insights($analytics_data) {
        try {
            $prompt = "Analyze this email marketing performance data and provide actionable insights:

PERFORMANCE DATA:
" . json_encode($analytics_data, JSON_PRETTY_PRINT) . "

Provide insights on:
1. Performance strengths and weaknesses
2. Optimization opportunities
3. Benchmark comparisons
4. Recommended actions
5. Content improvements
6. Timing optimizations
7. Segmentation recommendations
8. Revenue optimization opportunities

Format as structured JSON insights.";

            $response = $this->call_openai_api($prompt, 1000);
            return json_decode($response, true) ?? ['insights' => 'Performance analysis completed'];

        } catch (\Throwable $e) {
            error_log('Error generating email insights: ' . $e->getMessage());
            return ['insights' => 'Analysis error occurred'];
        }
    }

    private function recommend_email_optimizations($insights) {
        return [
            'subject_line_tests' => $insights['subject_line_recommendations'] ?? [],
            'send_time_optimization' => $insights['timing_recommendations'] ?? [],
            'content_improvements' => $insights['content_suggestions'] ?? [],
            'segmentation_refinements' => $insights['segmentation_updates'] ?? [],
            'automation_enhancements' => $insights['automation_improvements'] ?? []
        ];
    }

    private function apply_email_optimizations($optimizations, $user_id) {
        foreach ($optimizations as $optimization_type => $optimization_data) {
            switch ($optimization_type) {
                case 'subject_line_tests':
                    $this->setup_subject_line_ab_tests($optimization_data, $user_id);
                    break;
                case 'send_time_optimization':
                    $this->update_send_times($optimization_data, $user_id);
                    break;
                case 'content_improvements':
                    $this->update_email_content($optimization_data, $user_id);
                    break;
                case 'segmentation_refinements':
                    $this->refine_email_segmentation($optimization_data, $user_id);
                    break;
                case 'automation_enhancements':
                    $this->enhance_email_automation($optimization_data, $user_id);
                    break;
            }
        }
    }

    private function identify_email_optimization_opportunities($user_id) {
        $email_config = get_option('vmo_email_marketing_' . $user_id, []);
        $recent_performance = get_option('vmo_email_performance_' . $user_id . '_' . date('Y_m_d'), []);

        if (empty($recent_performance)) return [];

        $opportunities = [];

        // Check if open rates are below benchmark
        if (isset($recent_performance['analytics']['open_rate']) && 
            floatval(str_replace('%', '', $recent_performance['analytics']['open_rate'])) < 22) {
            $opportunities[] = [
                'type' => 'subject_line_optimization',
                'priority' => 'high',
                'current_performance' => $recent_performance['analytics']['open_rate'],
                'target_improvement' => '25%+'
            ];
        }

        // Check if click rates need improvement
        if (isset($recent_performance['analytics']['click_rate']) && 
            floatval(str_replace('%', '', $recent_performance['analytics']['click_rate'])) < 4) {
            $opportunities[] = [
                'type' => 'content_optimization',
                'priority' => 'medium',
                'current_performance' => $recent_performance['analytics']['click_rate'],
                'target_improvement' => '5%+'
            ];
        }

        return $opportunities;
    }

    private function refine_email_strategy_from_competitors($competitive_analysis, $email_config) {
        try {
            $prompt = "Refine this email marketing strategy based on new competitive analysis:

CURRENT STRATEGY:
" . json_encode($email_config['email_strategy'], JSON_PRETTY_PRINT) . "

NEW COMPETITIVE ANALYSIS:
" . json_encode($competitive_analysis, JSON_PRETTY_PRINT) . "

Update the strategy to:
1. Capitalize on competitor weaknesses in email marketing
2. Adopt successful competitor tactics
3. Maintain competitive differentiation
4. Improve targeting and messaging
5. Optimize campaign timing and frequency

Format as updated JSON strategy.";

            $response = $this->call_openai_api_with_tools($prompt, [
                'web_search' => [
                    'description' => 'Research latest email marketing trends and best practices',
                    'enabled' => true
                ]
            ]);

            return json_decode($response['content'], true) ?? $email_config['email_strategy'];

        } catch (\Throwable $e) {
            error_log('Error refining email strategy: ' . $e->getMessage());
            return $email_config['email_strategy'] ?? [];
        }
    }

    private function update_email_campaigns_from_strategy($updated_strategy, $user_id) {
        $email_config = get_option('vmo_email_marketing_' . $user_id, []);
        $email_config['email_strategy'] = $updated_strategy;
        $email_config['last_updated'] = current_time('mysql');

        update_option('vmo_email_marketing_' . $user_id, $email_config);

        // Update email templates based on new strategy
        $this->create_email_templates($updated_strategy, ['user_id' => $user_id]);
    }

    private function get_email_performance_history($user_id) {
        $performance_history = [];
        
        // Get last 30 days of performance data
        for ($i = 0; $i < 30; $i++) {
            $date = date('Y_m_d', strtotime("-{$i} days"));
            $daily_data = get_option('vmo_email_performance_' . $user_id . '_' . $date, null);
            
            if ($daily_data) {
                $performance_history[$date] = $daily_data;
            }
        }

        return $performance_history;
    }

    private function generate_email_strategy_review($performance_data, $competitive_landscape) {
        try {
            $prompt = "Conduct a comprehensive email marketing strategy review:

PERFORMANCE HISTORY:
" . json_encode($performance_data, JSON_PRETTY_PRINT) . "

COMPETITIVE LANDSCAPE:
" . json_encode($competitive_landscape, JSON_PRETTY_PRINT) . "

Provide a strategic review including:
1. PERFORMANCE TRENDS - What's working and what's not
2. COMPETITIVE POSITION - How we compare to competitors
3. STRATEGIC RECOMMENDATIONS - Major strategy adjustments needed
4. CAMPAIGN OPTIMIZATIONS - Specific campaign improvements
5. CONTENT STRATEGY UPDATES - New content directions
6. AUTOMATION ENHANCEMENTS - Workflow improvements
7. TARGETING REFINEMENTS - Audience optimization
8. INTEGRATION OPPORTUNITIES - Cross-channel synergies

Format as actionable JSON strategy updates.";

            $response = $this->call_openai_api_with_tools($prompt, [
                'web_search' => [
                    'description' => 'Research current email marketing industry trends and benchmarks',
                    'enabled' => true
                ]
            ]);

            return json_decode($response['content'], true) ?? ['status' => 'review_completed'];

        } catch (\Throwable $e) {
            error_log('Error generating email strategy review: ' . $e->getMessage());
            return ['status' => 'review_error'];
        }
    }

    private function implement_strategy_updates($strategy_review, $user_id) {
        if (isset($strategy_review['strategic_recommendations'])) {
            foreach ($strategy_review['strategic_recommendations'] as $recommendation) {
                $this->implement_email_strategy_change($recommendation, $user_id);
            }
        }

        if (isset($strategy_review['campaign_optimizations'])) {
            $this->apply_campaign_optimizations($strategy_review['campaign_optimizations'], $user_id);
        }

        if (isset($strategy_review['content_strategy_updates'])) {
            $this->update_email_content_strategy($strategy_review['content_strategy_updates'], $user_id);
        }
    }

    private function generate_welcome_email_series($email_strategy, $user_data) {
        $series = [];
        $welcome_sequence = $email_strategy['automation_sequences']['onboarding'] ?? [];
        
        for ($i = 1; $i <= 5; $i++) {
            $series["welcome_email_{$i}"] = [
                'subject' => "Welcome to {business_name} - Step {$i}",
                'content' => $this->generate_welcome_email_content($i, $user_data),
                'delay' => $this->calculate_email_timing('onboarding')[$i-1] ?? "+{$i} days",
                'personalization' => ['business_name', 'user_name', 'location']
            ];
        }

        return $series;
    }

    private function generate_nurture_email_sequence($email_strategy, $user_data) {
        $sequence = [];
        $nurture_data = $email_strategy['automation_sequences']['nurture'] ?? [];
        
        for ($i = 1; $i <= 7; $i++) {
            $sequence["nurture_email_{$i}"] = [
                'subject' => $this->generate_nurture_subject_line($i, $user_data),
                'content' => $this->generate_nurture_email_content_detailed($i, $user_data),
                'delay' => $this->calculate_email_timing('nurture')[$i-1] ?? "+{$i} days",
                'call_to_action' => $this->generate_nurture_cta($i)
            ];
        }

        return $sequence;
    }

    private function generate_promotional_email_templates($email_strategy, $user_data) {
        return [
            'seasonal_promotion' => [
                'subject' => 'Exclusive {season} Offer for {business_name} Customers',
                'content' => $this->generate_promotional_content('seasonal', $user_data),
                'cta' => 'Claim Your Exclusive Offer'
            ],
            'product_announcement' => [
                'subject' => 'Exciting News from {business_name}!',
                'content' => $this->generate_promotional_content('announcement', $user_data),
                'cta' => 'Learn More'
            ],
            'limited_time_offer' => [
                'subject' => 'Limited Time: Special Offer Inside',
                'content' => $this->generate_promotional_content('limited_time', $user_data),
                'cta' => 'Get This Deal Now'
            ]
        ];
    }

    private function generate_newsletter_template($email_strategy, $user_data) {
        return [
            'subject' => '{business_name} Newsletter - {month} {year}',
            'sections' => [
                'company_updates' => 'Latest news and updates',
                'industry_insights' => 'Valuable industry information',
                'customer_spotlight' => 'Featured customer success stories',
                'tips_and_advice' => 'Helpful tips for customers',
                'upcoming_events' => 'Events and announcements'
            ],
            'personalization' => ['business_name', 'user_name', 'month', 'year'],
            'cta' => 'Visit Our Website'
        ];
    }

    private function generate_welcome_email_content($email_number, $user_data) {
        $content_map = [
            1 => "Welcome to {business_name}! We're excited to have you join our community.",
            2 => "Here's what you can expect from {business_name} and how we can help you succeed.",
            3 => "Meet our team and learn about our commitment to your success.",
            4 => "Discover the full range of services and solutions we offer.",
            5 => "Take the next step - let's schedule your complimentary consultation!"
        ];

        return $content_map[$email_number] ?? "Welcome email content for step {$email_number}";
    }

    private function generate_nurture_subject_line($email_number, $user_data) {
        $subjects = [
            1 => "Your success journey starts here, {user_name}",
            2 => "{business_name}: Here's how we can help you grow",
            3 => "3 ways {business_name} customers achieve success",
            4 => "Behind the scenes at {business_name}",
            5 => "Your {business_name} success plan is ready",
            6 => "Don't miss out on this opportunity, {user_name}",
            7 => "Final invitation: Let's discuss your goals"
        ];

        return $subjects[$email_number] ?? "Message #{$email_number} from {business_name}";
    }

    private function generate_nurture_email_content_detailed($email_number, $user_data) {
        return "Detailed nurture email content for email #{$email_number} personalized for the business.";
    }

    private function generate_nurture_cta($email_number) {
        $ctas = [
            1 => "Explore Our Services",
            2 => "Learn More About Us", 
            3 => "See Success Stories",
            4 => "Meet Our Team",
            5 => "Get Your Success Plan",
            6 => "Schedule Consultation",
            7 => "Book Your Call Now"
        ];

        return $ctas[$email_number] ?? "Contact Us Today";
    }

    private function generate_promotional_content($promotion_type, $user_data) {
        $content_map = [
            'seasonal' => "Take advantage of our exclusive seasonal offer designed specifically for businesses like yours.",
            'announcement' => "We're excited to announce new services that will help your business grow even faster.",
            'limited_time' => "This special offer is only available for a limited time - don't miss out on these savings!"
        ];

        return $content_map[$promotion_type] ?? "Promotional content for {business_name} customers.";
    }

    private function setup_subject_line_ab_tests($optimization_data, $user_id) {
        $ab_tests = [
            'test_id' => 'subject_line_' . time(),
            'user_id' => $user_id,
            'variations' => $optimization_data,
            'test_duration' => '7 days',
            'success_metric' => 'open_rate',
            'created_at' => current_time('mysql')
        ];

        update_option('vmo_email_ab_tests_' . $user_id, $ab_tests);
    }

    private function update_send_times($timing_data, $user_id) {
        $email_config = get_option('vmo_email_marketing_' . $user_id, []);
        $email_config['optimized_send_times'] = $timing_data;
        $email_config['last_timing_update'] = current_time('mysql');

        update_option('vmo_email_marketing_' . $user_id, $email_config);
    }

    private function update_email_content($content_data, $user_id) {
        $templates = get_option('vmo_email_templates_' . $user_id, []);
        
        foreach ($content_data as $template_type => $new_content) {
            if (isset($templates[$template_type])) {
                $templates[$template_type]['content'] = $new_content;
                $templates[$template_type]['updated_at'] = current_time('mysql');
            }
        }

        update_option('vmo_email_templates_' . $user_id, $templates);
    }

    private function refine_email_segmentation($segmentation_data, $user_id) {
        $email_config = get_option('vmo_email_marketing_' . $user_id, []);
        $email_config['refined_segmentation'] = $segmentation_data;
        $email_config['segmentation_updated'] = current_time('mysql');

        update_option('vmo_email_marketing_' . $user_id, $email_config);
    }

    private function enhance_email_automation($automation_data, $user_id) {
        $email_config = get_option('vmo_email_marketing_' . $user_id, []);
        $email_config['enhanced_automation'] = $automation_data;
        $email_config['automation_updated'] = current_time('mysql');

        update_option('vmo_email_marketing_' . $user_id, $email_config);
    }

    private function get_latest_competitive_analysis($user_id, $analysis_type) {
        return get_option('vmo_competitive_analysis_' . $analysis_type . '_' . $user_id, []);
    }

    private function implement_email_strategy_change($recommendation, $user_id) {
        $email_config = get_option('vmo_email_marketing_' . $user_id, []);
        
        if (isset($recommendation['strategy_update'])) {
            $email_config['email_strategy'] = array_merge(
                $email_config['email_strategy'] ?? [],
                $recommendation['strategy_update']
            );
        }

        $email_config['last_strategy_update'] = current_time('mysql');
        update_option('vmo_email_marketing_' . $user_id, $email_config);
    }

    private function apply_campaign_optimizations($optimizations, $user_id) {
        foreach ($optimizations as $campaign_type => $optimization) {
            $this->optimize_specific_campaign($campaign_type, $optimization, $user_id);
        }
    }

    private function update_email_content_strategy($content_updates, $user_id) {
        $email_config = get_option('vmo_email_marketing_' . $user_id, []);
        
        if (isset($email_config['email_strategy']['content_strategy'])) {
            $email_config['email_strategy']['content_strategy'] = array_merge(
                $email_config['email_strategy']['content_strategy'],
                $content_updates
            );
        }

        update_option('vmo_email_marketing_' . $user_id, $email_config);
    }

    private function optimize_specific_campaign($campaign_type, $optimization, $user_id) {
        $templates = get_option('vmo_email_templates_' . $user_id, []);
        
        if (isset($templates[$campaign_type])) {
            $templates[$campaign_type] = array_merge($templates[$campaign_type], $optimization);
            $templates[$campaign_type]['optimized_at'] = current_time('mysql');
            
            update_option('vmo_email_templates_' . $user_id, $templates);
        }
    }
    
    /**
     * Handle automatic SEO service provisioning with competitive analysis
     */
    private function handle_seo_service_provisioning($package, $responses) {
        try {
            // SEO service is ALWAYS provisioned automatically with enhanced research
            $user_id = get_current_user_id();
            
            error_log("VMO SEO: Starting automatic SEO provisioning with enhanced AI research for user {$user_id}");
            
            // Step 1: Conduct deep competitive analysis using enhanced OpenAI research system
            $competitive_analysis = $this->conduct_deep_competitive_analysis($responses);
            
            // Step 2: Generate comprehensive SEO strategy based on enhanced competitive intelligence
            $seo_strategy = $this->generate_competitive_seo_strategy($competitive_analysis, $responses);
            
            // Step 3: Apply SEO optimizations to user's website automatically with AI insights
            $seo_implementation = $this->apply_automatic_seo_optimizations($seo_strategy, $user_id);
            
            // Step 4: Set up ongoing SEO monitoring with enhanced research capabilities
            $monitoring_setup = $this->setup_ongoing_seo_monitoring($seo_strategy, $user_id);
            
            // Step 5: Schedule enhanced SEO audit with deep competitive tracking
            $this->schedule_initial_seo_audit($user_id, $seo_strategy);
            
            return array(
                'provisioned' => true,
                'research_system' => 'enhanced_openai_deep_research',
                'competitive_analysis' => $competitive_analysis,
                'seo_strategy' => $seo_strategy,
                'implementation_results' => $seo_implementation,
                'monitoring_setup' => $monitoring_setup,
                'service_details' => $this->get_seo_service_details(),
                'activation_date' => current_time('mysql'),
                'next_audit_date' => date('Y-m-d H:i:s', strtotime('+7 days')),
                'blog_generation_scheduled' => true,
                'research_quality_score' => $competitive_analysis['analysis_quality_score'] ?? 0
            );
            
        } catch (\Throwable $e) {
            error_log('VMO SEO Provisioning Error: ' . $e->getMessage());
            return array(
                'provisioned' => false,
                'reason' => 'SEO provisioning system error',
                'recommended' => true,
                'error' => $e->getMessage()
            );
        }
    }
    
    /**
     * Conduct deep competitive analysis using OpenAI web search
     */
    private function conduct_deep_competitive_analysis($responses) {
        $business_context = $this->format_business_context($responses);
        
        // Extract key business information for competitive research
        $business_name = $this->extract_business_info($responses, 'business_name');
        $industry = $this->extract_business_info($responses, 'industry');
        $location = $this->extract_business_info($responses, 'location');
        $services = $this->extract_business_info($responses, 'services');
        
        // Use enhanced OpenAI deep research system for comprehensive competitive analysis
        $research_prompt = "Conduct a comprehensive competitive intelligence analysis for this business:

BUSINESS INFORMATION:
" . $business_context . "

REQUIRED ANALYSIS DEPTH:
1. **DIRECT COMPETITORS IDENTIFICATION**:
   - Find top 10 direct competitors in {$location} offering similar services
   - Include competitor names, websites, and business profiles
   - Analyze market share and positioning strategies

2. **COMPREHENSIVE SEO INTELLIGENCE**:
   - Keyword gap analysis and ranking opportunities
   - Content strategy assessment and competitive gaps
   - Local SEO optimization opportunities
   - Technical SEO competitive advantages

3. **MARKET POSITIONING & PRICING**:
   - Competitive pricing analysis and positioning strategies
   - Unique value proposition identification
   - Customer sentiment analysis from reviews and social media
   - Market differentiation opportunities

4. **DIGITAL PRESENCE ASSESSMENT**:
   - Website quality, UX, and conversion optimization analysis
   - Social media engagement and content strategy review
   - Online reputation management and review analysis
   - Mobile optimization and technical performance comparison

5. **STRATEGIC OPPORTUNITY MAPPING**:
   - Underserved market segments and customer needs
   - Emerging trends and future market opportunities
   - Competitive vulnerabilities and strategic advantages
   - Growth vectors and expansion opportunities

Provide comprehensive, actionable intelligence with specific data points, competitor insights, and strategic recommendations.";

        // Use the enhanced deep research system with competitive intelligence agent
        $competitive_data = $this->call_openai_deep_research(
            $research_prompt, 
            'competitive_intelligence',
            [
                'research_target' => $business_name,
                'industry' => $industry,
                'location' => $location,
                'services' => $services,
                'analysis_areas' => ['pricing', 'positioning', 'seo', 'digital_presence', 'opportunities']
            ]
        );
        $raw_analysis = is_string($competitive_data) ? $competitive_data : wp_json_encode($competitive_data, JSON_PRETTY_PRINT);
        $competitors = $this->extract_competitors_from_analysis($raw_analysis);
        $competitor_keywords = array_values((array) $this->extract_keywords_from_analysis($raw_analysis));
        $content_gaps = array_values((array) $this->extract_content_gaps($raw_analysis));
        $local_opportunities = array_values((array) $this->extract_local_opportunities($raw_analysis));
        $recommended_actions = $this->extract_actionable_recommendations($raw_analysis);
        $recommended_action_list = array();
        foreach ((array) $recommended_actions as $action_group) {
            if (is_array($action_group)) {
                $recommended_action_list = array_merge($recommended_action_list, $action_group);
            } elseif (is_string($action_group)) {
                $recommended_action_list[] = $action_group;
            }
        }
        
        // Parse and structure the competitive analysis with enhanced data
        return array(
            'raw_analysis' => $raw_analysis,
            'enhanced_analysis' => $competitive_data,
            'research_agent' => 'competitive_intelligence',
            'research_date' => current_time('mysql'),
            'business_context' => [
                'name' => $business_name,
                'industry' => $industry,
                'location' => $location,
                'services' => $services
            ],
            'analysis_quality_score' => $this->calculate_analysis_quality_score($raw_analysis),
            'strategic_insights' => $this->extract_strategic_insights($raw_analysis),
            'actionable_recommendations' => $recommended_actions,
            'recommended_actions' => $recommended_action_list,
            'competitors' => $competitors,
            'competitors_identified' => $competitors,
            'competitor_keywords' => $competitor_keywords,
            'keyword_gaps' => $competitor_keywords,
            'content_gaps' => $content_gaps,
            'local_opportunities' => $local_opportunities,
            'technical_issues' => array_values(array_filter($recommended_action_list, static function($action) {
                return is_string($action) && preg_match('/technical|speed|schema|crawl|index|mobile|sitemap|robots/i', $action);
            }))
        );
    }
    
    /**
     * Generate competitive SEO strategy based on analysis
     */
    private function generate_competitive_seo_strategy($competitive_analysis, $responses) {
        $business_context = $this->format_business_context($responses);
        
        $strategy_prompt = "Based on the enhanced competitive intelligence analysis, create a comprehensive SEO strategy that will outrank competitors:

BUSINESS CONTEXT:
" . $business_context . "

ENHANCED COMPETITIVE ANALYSIS:
" . json_encode($competitive_analysis, JSON_PRETTY_PRINT) . "

CREATE A COMPETITIVE SEO STRATEGY USING DEEP RESEARCH INSIGHTS:

1. **STRATEGIC KEYWORD TARGETING**:
   - 10-15 primary keywords identified through competitive gap analysis
   - High-opportunity keywords from competitive research
   - Local SEO keywords with location modifiers based on competitor analysis

2. **CONTENT STRATEGY WITH COMPETITIVE ADVANTAGES**:
   - Blog topics that exploit competitor content gaps identified in research
   - Service pages that outperform competitor pages based on analysis
   - Local content that establishes area expertise using competitive insights

3. **TECHNICAL SEO PRIORITIES FROM COMPETITIVE ASSESSMENT**:
   - On-page optimization based on competitor weaknesses
   - Site structure improvements that outperform competition
   - Local SEO schema markup requirements from competitive research

4. **STRATEGIC COMPETITIVE ADVANTAGE TACTICS**:
   - Unique angles competitors are missing (from research analysis)
   - Better user experience elements than competitors
   - Superior content depth and quality based on competitive gaps

5. **IMPLEMENTATION TIMELINE WITH COMPETITIVE BENCHMARKS**:
   - Week 1: Critical on-page optimizations targeting competitor weaknesses
   - Week 2-4: Content creation exploiting competitive content gaps
   - Month 2-3: Local SEO strategy based on competitor analysis
   - Ongoing: Content calendar and competitive monitoring

FORMAT: Provide specific, implementable recommendations with exact keywords, meta descriptions, and actionable steps based on competitive intelligence.";

        // Use enhanced research system for strategy generation
        $seo_strategy_response = $this->call_openai_deep_research($strategy_prompt, 'seo_strategist', [
            'competitive_intelligence' => $competitive_analysis,
            'business_context' => $business_context,
            'research_focus' => 'seo_strategy_development'
        ]);
        
        return array(
            'enhanced_strategy_document' => $seo_strategy_response,
            'research_system' => 'enhanced_openai_deep_research',
            'competitive_intelligence_source' => $competitive_analysis,
            'created_date' => current_time('mysql'),
            'strategic_quality_score' => $competitive_analysis['analysis_quality_score'] ?? 0,
            'competitive_advantages' => $competitive_analysis['strategic_insights'] ?? [],
            'primary_keywords' => $this->extract_primary_keywords($seo_strategy_response),
            'content_calendar' => $this->generate_content_calendar($seo_strategy_response),
            'technical_checklist' => $this->extract_technical_seo_tasks($seo_strategy_response)
        );
    }
    
    /**
     * Apply automatic SEO optimizations to user's website
     */
    private function apply_automatic_seo_optimizations($seo_strategy, $user_id) {
        $optimizations_applied = array();
        
        try {
            // Get user's website content/data
            $website_data = $this->get_user_website_data($user_id);
            
            // 1. Apply meta tags and structured data
            $meta_optimization = $this->apply_meta_optimizations($website_data, $seo_strategy);
            $optimizations_applied['meta_tags'] = $meta_optimization;
            
            // 2. Optimize page titles and descriptions
            $title_optimization = $this->optimize_page_titles($website_data, $seo_strategy);
            $optimizations_applied['page_titles'] = $title_optimization;
            
            // 3. Add local SEO schema markup
            $schema_optimization = $this->add_local_seo_schema($website_data, $seo_strategy);
            $optimizations_applied['schema_markup'] = $schema_optimization;
            
            // 4. Optimize images and alt tags
            $image_optimization = $this->optimize_images_for_seo($website_data, $seo_strategy);
            $optimizations_applied['image_seo'] = $image_optimization;
            
            // 5. Create optimized content structure
            $content_optimization = $this->optimize_content_structure($website_data, $seo_strategy);
            $optimizations_applied['content_structure'] = $content_optimization;
            
            // 6. Set up Google Analytics and Search Console tracking
            $tracking_setup = $this->setup_seo_tracking($user_id);
            $optimizations_applied['tracking'] = $tracking_setup;
            
            return $optimizations_applied;
            
        } catch (\Throwable $e) {
            error_log('VMO SEO Optimization Error: ' . $e->getMessage());
            return array('error' => $e->getMessage());
        }
    }
    
    /**
     * Set up ongoing SEO monitoring and blog generation
     */
    private function setup_ongoing_seo_monitoring($seo_strategy, $user_id) {
        try {
            // 1. Schedule weekly SEO audits
            $this->schedule_seo_audits($user_id);
            
            // 2. Set up automated blog post generation
            $blog_setup = $this->setup_automated_blog_generation($seo_strategy, $user_id);
            
            // 3. Configure competitor monitoring
            $competitor_monitoring = $this->setup_competitor_monitoring($seo_strategy, $user_id);
            
            // 4. Set up ranking tracking
            $ranking_tracking = $this->setup_ranking_tracking($seo_strategy, $user_id);
            
            return array(
                'audits_scheduled' => true,
                'blog_automation' => $blog_setup,
                'competitor_monitoring' => $competitor_monitoring,
                'ranking_tracking' => $ranking_tracking,
                'monitoring_frequency' => 'weekly'
            );
            
        } catch (\Throwable $e) {
            error_log('VMO SEO Monitoring Setup Error: ' . $e->getMessage());
            return array('error' => $e->getMessage());
        }
    }
    
    /**
     * Enhanced OpenAI Deep Research API with Specialized AI Agents
     */
    private function call_openai_deep_research($prompt, $research_type = 'competitive_analysis', $tools = array(), $depth = 0) {
        if (empty($this->api_key)) {
            return "AI API key not configured";
        }
        
        // Create specialized system prompts for different research agents
        $research_agents = $this->get_research_agent_profiles();
        $agent_profile = $research_agents[$research_type] ?? $research_agents['default'];
        
        $messages = array(
            array(
                'role' => 'system',
                'content' => $agent_profile['system_prompt']
            ),
            array(
                'role' => 'user',
                'content' => $prompt
            )
        );
        
        // ✅ FIX: Use centralized model registry — strategic for deep research
        $_dr_model = function_exists('vmo_get_model') ? vmo_get_model('strategic') : (defined('VMO_OPENAI_MODEL') ? VMO_OPENAI_MODEL : 'gpt-5.5');
        $data = array_merge(array(
            'model' => $_dr_model,
            'messages' => $messages,
            'top_p' => 0.9,
            'frequency_penalty' => 0.1,
            'presence_penalty' => 0.1
        ), function_exists('vmo_get_token_param') ? vmo_get_token_param($_dr_model, 4000) : array('max_tokens' => 4000));
        $data = $this->apply_safe_openai_temperature($data, $_dr_model, $agent_profile['temperature'] ?? null);
        
        // Enhanced tool integration for deep research
        // ✅ FIX: Only include tools at depth 0 to prevent infinite recursion.
        // compile_deep_research_report() calls back into this method at depth 1 for synthesis;
        // that synthesis call must NOT include tools or it can loop forever.
        if ($depth === 0 && (!empty($tools) || $agent_profile['requires_tools'])) {
            $data['tools'] = $this->build_advanced_research_tools($research_type);
            $data['tool_choice'] = 'auto';
        }
        
        $response = $this->execute_openai_request($data);

        // Fallback model for reliability when primary fails
        $_fb_model = function_exists('vmo_get_model') ? vmo_get_model('fallback') : 'gpt-5.5-nano';
        if (is_array($response) && isset($response['error']) && $data['model'] !== $_fb_model) {
            error_log('VMO Deep Research: Primary model failed, retrying with fallback. Error: ' . $response['error']);
            $data['model'] = $_fb_model;
            $response = $this->execute_openai_request($data);
        }

        if (is_array($response) && isset($response['error'])) {
            return 'Error: ' . (is_string($response['error']) ? $response['error'] : wp_json_encode($response['error']));
        }
        
        // If the response includes tool calls, process them for deeper research
        if (
            is_array($response)
            && isset($response['choices'][0]['message']['tool_calls'])
            && is_array($response['choices'][0]['message']['tool_calls'])
        ) {
            $tool_report = $this->process_deep_research_tool_calls($response, $research_type, $prompt);
            return $this->normalize_deep_research_response($tool_report);
        }
        
        return $this->normalize_deep_research_response($response);
    }

    private function normalize_deep_research_response($response) {
        if (is_string($response)) {
            return trim($response);
        }

        if (is_array($response)) {
            if (
                isset($response['choices'][0]['message']['content'])
                && is_string($response['choices'][0]['message']['content'])
            ) {
                return trim($response['choices'][0]['message']['content']);
            }

            if (isset($response['comprehensive_analysis'])) {
                return $this->normalize_deep_research_response($response['comprehensive_analysis']);
            }

            if (isset($response['error'])) {
                return 'Error: ' . (is_string($response['error']) ? $response['error'] : wp_json_encode($response['error']));
            }

            $encoded = wp_json_encode($response);
            return is_string($encoded) ? $encoded : '';
        }

        return '';
    }

    /**
     * Get specialized AI agent profiles for different research types
     */
    private function get_research_agent_profiles() {
        return [
            'competitive_analysis' => [
                'system_prompt' => 'You are a senior competitive intelligence analyst with 15+ years of experience in market research. You specialize in deep competitor analysis, market positioning, and strategic intelligence gathering. You have access to real-time web search and advanced research tools. Your analysis should be comprehensive, data-driven, and actionable.

                YOUR EXPERTISE:
                - Competitive landscape mapping and positioning analysis
                - Market share analysis and revenue estimation
                - Pricing strategy evaluation and optimization
                - Digital presence and marketing strategy assessment
                - SWOT analysis and competitive advantage identification
                - Market trends and opportunity identification
                
                RESEARCH METHODOLOGY:
                1. Conduct multi-source data collection and verification
                2. Perform comprehensive competitor profiling
                3. Analyze market positioning and differentiation strategies
                4. Evaluate digital marketing and SEO performance
                5. Assess customer sentiment and review analysis
                6. Identify market gaps and opportunities
                
                OUTPUT FORMAT: Provide structured JSON with detailed competitive insights, specific recommendations, and actionable intelligence.',
                'temperature' => 0.2,
                'requires_tools' => true
            ],
            
            'market_research' => [
                'system_prompt' => 'You are a market research specialist with expertise in industry analysis, consumer behavior, and market trend identification. You conduct comprehensive market studies using advanced research methodologies and data analysis techniques.

                YOUR EXPERTISE:
                - Market size and growth analysis
                - Consumer behavior and demographic analysis
                - Industry trend identification and forecasting
                - Market segmentation and targeting strategies
                - Demand analysis and market potential assessment
                - Regulatory and economic impact analysis
                
                RESEARCH APPROACH:
                1. Multi-source market data collection and analysis
                2. Industry report synthesis and trend identification
                3. Consumer sentiment and behavior pattern analysis
                4. Market opportunity and threat assessment
                5. Economic and regulatory impact evaluation
                6. Growth projection and market forecast development
                
                OUTPUT FORMAT: Comprehensive market analysis with data-backed insights and strategic recommendations.',
                'temperature' => 0.25,
                'requires_tools' => true
            ],
            
            'seo_research' => [
                'system_prompt' => 'You are an advanced SEO strategist and digital marketing analyst specializing in competitive SEO analysis and search optimization strategy. You conduct deep technical SEO audits and competitive keyword analysis.

                YOUR EXPERTISE:
                - Comprehensive keyword research and gap analysis
                - Technical SEO audit and optimization recommendations
                - Competitive SERP analysis and ranking strategies
                - Content strategy and optimization opportunities
                - Link building strategy and authority analysis
                - Local SEO and geographic targeting optimization
                
                RESEARCH METHODOLOGY:
                1. Comprehensive keyword landscape analysis
                2. Competitor SEO performance evaluation
                3. Technical SEO opportunity identification
                4. Content gap analysis and optimization recommendations
                5. Backlink profile and authority assessment
                6. Local search and geographic optimization analysis
                
                OUTPUT FORMAT: Detailed SEO strategy with specific implementation recommendations and competitive insights.',
                'temperature' => 0.15,
                'requires_tools' => true
            ],
            
            'email_marketing_research' => [
                'system_prompt' => 'You are an email marketing strategist with expertise in campaign optimization, automation workflows, and competitive email analysis. You specialize in creating high-converting email strategies based on industry best practices and competitive intelligence.

                YOUR EXPERTISE:
                - Email campaign strategy and optimization
                - Marketing automation workflow design
                - Competitive email strategy analysis
                - List segmentation and targeting optimization
                - A/B testing strategy and performance analysis
                - Email deliverability and reputation management
                
                RESEARCH APPROACH:
                1. Competitive email strategy analysis and benchmarking
                2. Industry best practice identification and adaptation
                3. Audience segmentation and targeting optimization
                4. Content strategy and personalization recommendations
                5. Automation workflow optimization and trigger analysis
                6. Performance metrics and optimization opportunities
                
                OUTPUT FORMAT: Comprehensive email marketing strategy with specific campaign recommendations and competitive insights.',
                'temperature' => 0.3,
                'requires_tools' => true
            ],
            
            'default' => [
                'system_prompt' => 'You are a comprehensive business research analyst with expertise across multiple domains. You conduct thorough research using advanced methodologies and provide actionable business insights.',
                'temperature' => 0.25,
                'requires_tools' => true
            ]
        ];
    }

    /**
     * Build advanced research tools for different research types
     */
    private function build_advanced_research_tools($research_type) {
        $base_tools = [
            [
                'type' => 'function',
                'function' => [
                    'name' => 'advanced_web_search',
                    'description' => 'Perform comprehensive web search with advanced query optimization for business research',
                    'parameters' => [
                        'type' => 'object',
                        'properties' => [
                            'query' => [
                                'type' => 'string',
                                'description' => 'The search query optimized for business research'
                            ],
                            'search_type' => [
                                'type' => 'string',
                                'enum' => ['competitor_analysis', 'market_research', 'industry_trends', 'customer_reviews', 'pricing_analysis'],
                                'description' => 'The type of search to optimize results'
                            ],
                            'depth' => [
                                'type' => 'string',
                                'enum' => ['surface', 'deep', 'comprehensive'],
                                'description' => 'The depth of research required'
                            ]
                        ],
                        'required' => ['query', 'search_type']
                    ]
                ]
            ],
            [
                'type' => 'function',
                'function' => [
                    'name' => 'multi_source_research',
                    'description' => 'Conduct multi-source research across different platforms and databases',
                    'parameters' => [
                        'type' => 'object',
                        'properties' => [
                            'research_target' => [
                                'type' => 'string',
                                'description' => 'The primary research target (company, industry, market segment)'
                            ],
                            'sources' => [
                                'type' => 'array',
                                'items' => [
                                    'type' => 'string',
                                    'enum' => ['web', 'news', 'social_media', 'reviews', 'financial_data', 'industry_reports']
                                ],
                                'description' => 'Types of sources to research'
                            ],
                            'focus_areas' => [
                                'type' => 'array',
                                'items' => ['type' => 'string'],
                                'description' => 'Specific areas to focus the research on'
                            ]
                        ],
                        'required' => ['research_target', 'sources']
                    ]
                ]
            ]
        ];

        // Add specialized tools based on research type
        switch ($research_type) {
            case 'competitive_analysis':
                $base_tools[] = [
                    'type' => 'function',
                    'function' => [
                        'name' => 'competitor_deep_dive',
                        'description' => 'Perform comprehensive competitor analysis including pricing, positioning, and strategy',
                        'parameters' => [
                            'type' => 'object',
                            'properties' => [
                                'competitor_name' => ['type' => 'string'],
                                'analysis_areas' => [
                                    'type' => 'array',
                                    'items' => [
                                        'type' => 'string',
                                        'enum' => ['pricing', 'positioning', 'marketing', 'products', 'reviews', 'seo', 'social_media']
                                    ]
                                ]
                            ],
                            'required' => ['competitor_name', 'analysis_areas']
                        ]
                    ]
                ];
                break;
                
            case 'seo_research':
                $base_tools[] = [
                    'type' => 'function',
                    'function' => [
                        'name' => 'seo_competitive_analysis',
                        'description' => 'Analyze competitor SEO strategies, keywords, and ranking performance',
                        'parameters' => [
                            'type' => 'object',
                            'properties' => [
                                'target_keywords' => ['type' => 'array', 'items' => ['type' => 'string']],
                                'competitors' => ['type' => 'array', 'items' => ['type' => 'string']],
                                'analysis_depth' => ['type' => 'string', 'enum' => ['basic', 'comprehensive', 'deep']]
                            ],
                            'required' => ['target_keywords']
                        ]
                    ]
                ];
                break;
        }

        return $base_tools;
    }

    /**
     * Process deep research tool calls with multiple iterations
     */
    private function process_deep_research_tool_calls($response, $research_type, $original_prompt) {
        $tool_calls = $response['choices'][0]['message']['tool_calls'];
        $research_results = [];
        
        foreach ($tool_calls as $tool_call) {
            $function_name = $tool_call['function']['name'];
            $arguments = json_decode($tool_call['function']['arguments'], true);
            
            // Execute the research tool
            $tool_result = $this->execute_research_tool($function_name, $arguments, $research_type);
            
            if ($tool_result) {
                $research_results[] = [
                    'tool' => $function_name,
                    'query' => $arguments,
                    'results' => $tool_result
                ];
            }
        }
        
        // Compile comprehensive research report
        return $this->compile_deep_research_report($research_results, $research_type, $original_prompt);
    }

    /**
     * Execute specific research tools
     */
    private function execute_research_tool($function_name, $arguments, $research_type) {
        switch ($function_name) {
            case 'advanced_web_search':
                return $this->perform_advanced_web_search($arguments);
                
            case 'multi_source_research':
                return $this->conduct_multi_source_research($arguments);
                
            case 'competitor_deep_dive':
                return $this->perform_competitor_deep_dive($arguments);
                
            case 'seo_competitive_analysis':
                return $this->analyze_seo_competition($arguments);
                
            default:
                return $this->fallback_research_tool($function_name, $arguments);
        }
    }

    /**
     * Perform advanced web search with query optimization
     */
    private function perform_advanced_web_search($arguments) {
        $query = $arguments['query'] ?? '';
        $search_type = $arguments['search_type'] ?? 'general';
        $depth = $arguments['depth'] ?? 'surface';
        
        // Optimize query based on search type
        $optimized_query = $this->optimize_search_query($query, $search_type);
        
        // Perform the web search using the existing web search functionality
        $search_results = $this->execute_web_search($optimized_query, $depth);
        
        return [
            'query' => $optimized_query,
            'search_type' => $search_type,
            'depth' => $depth,
            'results' => $search_results,
            'timestamp' => current_time('mysql')
        ];
    }
    
    /**
     * Conduct multi-source research across different platforms
     */
    private function conduct_multi_source_research($arguments) {
        $target = $arguments['research_target'] ?? '';
        $sources = $arguments['sources'] ?? ['web'];
        $focus_areas = $arguments['focus_areas'] ?? [];
        
        $research_data = [];
        
        foreach ($sources as $source) {
            $source_data = $this->research_from_source($target, $source, $focus_areas);
            if ($source_data) {
                $research_data[$source] = $source_data;
            }
        }
        
        return [
            'target' => $target,
            'sources_researched' => $sources,
            'focus_areas' => $focus_areas,
            'data' => $research_data,
            'analysis_timestamp' => current_time('mysql')
        ];
    }
    
    /**
     * Perform comprehensive competitor deep dive analysis
     */
    private function perform_competitor_deep_dive($arguments) {
        $competitor = $arguments['competitor_name'] ?? '';
        $analysis_areas = $arguments['analysis_areas'] ?? ['pricing', 'positioning', 'marketing'];
        
        $competitor_intelligence = [];
        
        foreach ($analysis_areas as $area) {
            $area_analysis = $this->analyze_competitor_area($competitor, $area);
            if ($area_analysis) {
                $competitor_intelligence[$area] = $area_analysis;
            }
        }
        
        return [
            'competitor' => $competitor,
            'analysis_areas' => $analysis_areas,
            'intelligence' => $competitor_intelligence,
            'competitive_score' => $this->calculate_competitive_score($competitor_intelligence),
            'analysis_timestamp' => current_time('mysql')
        ];
    }
    
    /**
     * Analyze SEO competition and keyword opportunities
     */
    private function analyze_seo_competition($arguments) {
        $keywords = $arguments['target_keywords'] ?? [];
        $competitors = $arguments['competitors'] ?? [];
        $depth = $arguments['analysis_depth'] ?? 'basic';
        
        $seo_intelligence = [
            'keyword_analysis' => [],
            'competitor_rankings' => [],
            'content_gaps' => [],
            'optimization_opportunities' => []
        ];
        
        foreach ($keywords as $keyword) {
            $keyword_data = $this->analyze_keyword_competition($keyword, $competitors, $depth);
            if ($keyword_data) {
                $seo_intelligence['keyword_analysis'][$keyword] = $keyword_data;
            }
        }
        
        // Analyze competitor SEO strategies
        foreach ($competitors as $competitor) {
            $competitor_seo = $this->analyze_competitor_seo($competitor, $keywords);
            if ($competitor_seo) {
                $seo_intelligence['competitor_rankings'][$competitor] = $competitor_seo;
            }
        }
        
        return [
            'keywords' => $keywords,
            'competitors' => $competitors,
            'analysis_depth' => $depth,
            'intelligence' => $seo_intelligence,
            'opportunities_score' => $this->calculate_seo_opportunity_score($seo_intelligence),
            'analysis_timestamp' => current_time('mysql')
        ];
    }
    
    /**
     * Fallback research tool for unrecognized functions
     */
    private function fallback_research_tool($function_name, $arguments) {
        return [
            'function' => $function_name,
            'arguments' => $arguments,
            'result' => 'Function not implemented, using general research approach',
            'timestamp' => current_time('mysql')
        ];
    }

    /**
     * Execute OpenAI API request with proper error handling
     */
    private function execute_openai_request($data) {
        $args = [
            'headers' => [
                'Authorization' => 'Bearer ' . $this->api_key,
                'Content-Type' => 'application/json',
            ],
            'body' => wp_json_encode($data),
            'method' => 'POST',
            'timeout' => 300
        ];
        
        $response = wp_remote_post('https://api.openai.com/v1/chat/completions', $args);
        
        if (is_wp_error($response)) {
            return ['error' => $response->get_error_message()];
        }

        $http_code = wp_remote_retrieve_response_code($response);
        
        $body = wp_remote_retrieve_body($response);
        $result = json_decode($body, true);

        if ($http_code >= 400) {
            $error_message = is_array($result)
                ? ($result['error']['message'] ?? ('AI HTTP error ' . $http_code))
                : ('AI HTTP error ' . $http_code);

            return [
                'error' => $error_message,
                'http_code' => $http_code
            ];
        }
        
        return $result ?? ['error' => 'Unable to parse AI response'];
    }

    /**
     * Compile comprehensive deep research report
     */
    private function compile_deep_research_report($research_results, $research_type, $original_prompt) {
        if (empty($research_results)) {
            return ['error' => 'No research results available'];
        }
        
        // Create a comprehensive analysis prompt
        $analysis_prompt = "Based on the following deep research data, provide a comprehensive analysis and strategic recommendations:

ORIGINAL REQUEST:
{$original_prompt}

RESEARCH TYPE: {$research_type}

RESEARCH DATA:
" . json_encode($research_results, JSON_PRETTY_PRINT) . "

Please provide:
1. Executive Summary of key findings
2. Detailed analysis of research data
3. Strategic recommendations and action items
4. Competitive advantages and opportunities
5. Implementation priorities and timeline
6. Risk assessment and mitigation strategies

Format as comprehensive JSON report with structured insights and actionable recommendations.";

        // Use the appropriate research agent for final analysis
        // ✅ FIX: Pass depth=1 to prevent recursive tool-call loop
        $final_analysis = $this->call_openai_deep_research($analysis_prompt, $research_type . '_synthesis', [], 1);
        
        return [
            'research_type' => $research_type,
            'raw_research_data' => $research_results,
            'comprehensive_analysis' => $final_analysis,
            'research_timestamp' => current_time('mysql'),
            'research_agent' => $research_type
        ];
    }
    
    /**
     * Extract business information from consultation responses
     */
    private function extract_business_info($responses, $info_type) {
        foreach ($responses as $category => $answers) {
            if (is_array($answers)) {
                foreach ($answers as $question => $answer) {
                    if (!empty($answer)) {
                        switch ($info_type) {
                            case 'business_name':
                                if (stripos($question, 'business name') !== false || stripos($question, 'company name') !== false) {
                                    return $answer;
                                }
                                break;
                            case 'industry':
                                if (stripos($question, 'industry') !== false || stripos($question, 'business type') !== false) {
                                    return $answer;
                                }
                                break;
                            case 'location':
                                if (stripos($question, 'location') !== false || stripos($question, 'where') !== false) {
                                    return $answer;
                                }
                                break;
                            case 'services':
                                if (stripos($question, 'services') !== false || stripos($question, 'products') !== false) {
                                    return $answer;
                                }
                                break;
                        }
                    }
                }
            }
        }
        return '';
    }

    private function call_openai_api_with_tools($prompt, $tools = array(), $max_tokens = 2000) {
        $tools_context = '';
        if (!empty($tools) && is_array($tools)) {
            $tools_context = "\n\nRESEARCH TOOLS CONTEXT:\n" . wp_json_encode($tools);
        }

        return $this->call_openai_api(
            $prompt . $tools_context,
            $max_tokens,
            true,
            false,
            null,
            null,
            true,
            null,
            false
        );
    }

    /**
     * Optimize search query based on search type
     */
    private function optimize_search_query($query, $search_type) {
        switch ($search_type) {
            case 'competitor':
                return "competitor analysis {$query} market share pricing strategy";
            case 'market':
                return "market research {$query} industry trends customer needs";
            case 'seo':
                return "SEO analysis {$query} keywords ranking content strategy";
            case 'pricing':
                return "pricing strategy {$query} cost analysis competitive pricing";
            default:
                return $query;
        }
    }
    
    /**
     * Execute web search with depth control
     */
    private function execute_web_search($query, $depth = 'surface') {
        $limit = $depth === 'deep' ? 10 : ($depth === 'comprehensive' ? 8 : 5);
        $page_scan_limit = $depth === 'surface' ? 0 : 2;

        if ($this->deep_research instanceof VMO_Deep_Research && method_exists($this->deep_research, 'perform_live_search')) {
            $search_data = $this->deep_research->perform_live_search($query, array(
                'limit' => $limit,
                'page_scan_limit' => $page_scan_limit,
            ));

            return array(
                'query' => $query,
                'depth' => $depth,
                'provider' => $search_data['source'] ?? 'live_search',
                'results' => $search_data['results'] ?? array(),
                'total_results' => count($search_data['results'] ?? array()),
                'search_timestamp' => $search_data['retrieved_at'] ?? current_time('mysql'),
            );
        }

        $search_data = $this->call_openai_api_with_tools(
            "Conduct a comprehensive web search for: {$query}. Depth: {$depth}",
            []
        );

        return array(
            'query' => $query,
            'depth' => $depth,
            'provider' => 'ai_tools_fallback',
            'results' => is_array($search_data) ? $search_data : array($search_data),
            'total_results' => is_array($search_data) ? count($search_data) : 1,
            'search_timestamp' => current_time('mysql'),
        );
    }
    
    /**
     * Research from specific source
     */
    private function research_from_source($target, $source, $focus_areas) {
        $focus_context = implode(', ', $focus_areas);
        
        $research_prompt = "Research {$target} from {$source} source, focusing on: {$focus_context}. Provide comprehensive data.";
        
        return $this->call_openai_api_with_tools($research_prompt, []);
    }
    
    /**
     * Analyze specific competitor area
     */
    private function analyze_competitor_area($competitor, $area) {
        $analysis_prompt = "Analyze {$competitor} in the area of {$area}. Provide detailed insights about their strategy, positioning, and performance.";
        
        return $this->call_openai_api_with_tools($analysis_prompt, []);
    }
    
    /**
     * Calculate competitive score
     */
    private function calculate_competitive_score($intelligence_data) {
        $score = 0;
        $factors = count($intelligence_data);
        
        foreach ($intelligence_data as $area => $data) {
            // Simple scoring based on data completeness and quality
            if (is_string($data) && strlen($data) > 100) {
                $score += 20;
            } elseif (is_array($data) && count($data) > 3) {
                $score += 20;
            } else {
                $score += 10;
            }
        }
        
        return $factors > 0 ? round($score / $factors, 1) : 0;
    }
    
    /**
     * Analyze keyword competition
     */
    private function analyze_keyword_competition($keyword, $competitors, $depth) {
        $competitors_list = implode(', ', $competitors);
        
        $analysis_prompt = "Analyze keyword '{$keyword}' competition against: {$competitors_list}. Analysis depth: {$depth}. Provide ranking difficulty, search volume estimates, and competitive insights.";
        
        return $this->call_openai_api_with_tools($analysis_prompt, []);
    }
    
    /**
     * Analyze competitor SEO strategy
     */
    private function analyze_competitor_seo($competitor, $keywords) {
        $keywords_list = implode(', ', $keywords);
        
        $seo_prompt = "Analyze {$competitor}'s SEO strategy for keywords: {$keywords_list}. Identify their content strategy, backlink profile, and ranking patterns.";
        
        return $this->call_openai_api_with_tools($seo_prompt, []);
    }
    
    /**
     * Calculate SEO opportunity score
     */
    private function calculate_seo_opportunity_score($seo_intelligence) {
        $total_score = 0;
        $factor_count = 0;
        
        foreach ($seo_intelligence as $category => $data) {
            if (!empty($data)) {
                $category_score = is_array($data) ? count($data) * 5 : strlen((string)$data) / 50;
                $total_score += min($category_score, 25); // Cap each category at 25 points
                $factor_count++;
            }
        }
        
        return $factor_count > 0 ? round($total_score / $factor_count, 1) : 0;
    }

    /**
     * Calculate analysis quality score
     */
    private function calculate_analysis_quality_score($analysis_data) {
        if (empty($analysis_data) || !is_string($analysis_data)) {
            return 0;
        }
        
        $score = 0;
        $content_length = strlen($analysis_data);
        
        // Length factor (up to 30 points)
        if ($content_length > 5000) $score += 30;
        elseif ($content_length > 3000) $score += 25;
        elseif ($content_length > 1500) $score += 20;
        elseif ($content_length > 500) $score += 15;
        else $score += 10;
        
        // Content quality indicators (up to 70 points)
        $quality_indicators = [
            'competitor' => 10,
            'keyword' => 10,
            'SEO' => 10,
            'pricing' => 10,
            'opportunity' => 10,
            'recommendation' => 10,
            'strategy' => 10
        ];
        
        foreach ($quality_indicators as $indicator => $points) {
            if (stripos($analysis_data, $indicator) !== false) {
                $score += $points;
            }
        }
        
        return min($score, 100); // Cap at 100
    }
    
    /**
     * Extract strategic insights from analysis
     */
    private function extract_strategic_insights($analysis_data) {
        if (empty($analysis_data)) {
            return [];
        }
        
        // Extract insights using pattern matching
        $insights = [];
        
        // Look for opportunity mentions
        if (preg_match_all('/opportunity[^.]*\./i', $analysis_data, $matches)) {
            $insights['opportunities'] = array_slice($matches[0], 0, 5);
        }
        
        // Look for competitive advantages
        if (preg_match_all('/advantage[^.]*\./i', $analysis_data, $matches)) {
            $insights['advantages'] = array_slice($matches[0], 0, 5);
        }
        
        // Look for market gaps
        if (preg_match_all('/gap[^.]*\./i', $analysis_data, $matches)) {
            $insights['market_gaps'] = array_slice($matches[0], 0, 5);
        }
        
        return $insights;
    }
    
    /**
     * Extract actionable recommendations from analysis
     */
    private function extract_actionable_recommendations($analysis_data) {
        if (empty($analysis_data)) {
            return [];
        }
        
        $recommendations = [];
        
        // Look for recommendation patterns
        if (preg_match_all('/recommend[^.]*\./i', $analysis_data, $matches)) {
            $recommendations['general'] = array_slice($matches[0], 0, 10);
        }
        
        // Look for should/must patterns
        if (preg_match_all('/(should|must)[^.]*\./i', $analysis_data, $matches)) {
            $recommendations['priorities'] = array_slice($matches[0], 0, 10);
        }
        
        // Look for action items
        if (preg_match_all('/implement[^.]*\./i', $analysis_data, $matches)) {
            $recommendations['implementations'] = array_slice($matches[0], 0, 10);
        }
        
        return $recommendations;
    }
    
    /**
     * Schedule initial SEO audit and first blog post
     */
    private function schedule_initial_seo_audit($user_id, $seo_strategy) {
        // Schedule first SEO audit for 7 days from now
        wp_schedule_single_event(
            time() + (7 * DAY_IN_SECONDS),
            'vmo_seo_audit_event',
            array($user_id, $seo_strategy)
        );
        
        // Schedule first blog post generation for 3 days from now
        wp_schedule_single_event(
            time() + (3 * DAY_IN_SECONDS),
            'vmo_generate_seo_blog_post',
            array($user_id, $seo_strategy)
        );
        
        error_log("VMO SEO: Scheduled initial audit and blog post for user {$user_id}");
    }
    
    /**
     * Extract competitors from analysis text
     */
    private function extract_competitors_from_analysis($analysis_text) {
        // Use regex to find competitor names and websites
        $competitors = array();
        
        // Look for patterns like "Competitor: Name (website.com)"
        preg_match_all('/(?:competitor|business):\s*([^(]+)\s*\(([^)]+)\)/i', $analysis_text, $matches);
        
        for ($i = 0; $i < count($matches[1]); $i++) {
            $competitors[] = array(
                'name' => trim($matches[1][$i]),
                'website' => trim($matches[2][$i])
            );
        }
        
        return $competitors;
    }
    
    /**
     * Extract keywords from analysis text
     */
    private function extract_keywords_from_analysis($analysis_text) {
        $keywords = array();
        
        // Look for keyword patterns in the text
        preg_match_all('/(?:keyword|target):\s*"([^"]+)"/i', $analysis_text, $matches);
        
        foreach ($matches[1] as $keyword) {
            $keywords[] = trim($keyword);
        }
        
        return array_unique($keywords);
    }
    
    /**
     * Extract content gaps from analysis
     */
    private function extract_content_gaps($analysis_text) {
        $gaps = array();
        
        // Look for content gap mentions
        if (preg_match('/content gaps?:(.+?)(?:

|$)/is', $analysis_text, $matches)) {
            $gap_text = $matches[1];
            $gap_lines = explode("
", $gap_text);
            
            foreach ($gap_lines as $line) {
                if (trim($line) && !empty(trim($line))) {
                    $gaps[] = trim(str_replace(array('-', '*', '•'), '', $line));
                }
            }
        }
        
        return $gaps;
    }
    
    /**
     * Extract local SEO opportunities
     */
    private function extract_local_opportunities($analysis_text) {
        $opportunities = array();
        
        // Look for local SEO mentions
        if (preg_match('/local seo.{0,20}opportunit.{0,50}:(.+?)(?:

|$)/is', $analysis_text, $matches)) {
            $opp_text = $matches[1];
            $opp_lines = explode("
", $opp_text);
            
            foreach ($opp_lines as $line) {
                if (trim($line) && !empty(trim($line))) {
                    $opportunities[] = trim(str_replace(array('-', '*', '•'), '', $line));
                }
            }
        }
        
        return $opportunities;
    }
    
    /**
     * Get SEO service details and benefits
     */
    private function get_seo_service_details() {
        return array(
            'features' => array(
                'Real-time Competitive Analysis',
                'Automatic On-page SEO Optimization',
                'Local SEO Schema Markup',
                'Keyword Research & Targeting',
                'Content Gap Analysis',
                'Automated Blog Generation',
                'Weekly SEO Audits',
                'Competitor Monitoring',
                'Google Analytics Integration',
                'Search Console Setup',
                'Meta Tags Optimization',
                'Image SEO Optimization',
                'Site Structure Analysis',
                'Ranking Tracking',
                'SEO Performance Reports'
            ),
            'benefits' => array(
                'Outrank Local Competitors',
                'Increase Organic Website Traffic',
                'Higher Search Engine Rankings',
                'More Qualified Leads from Search',
                'Improved Local Visibility',
                'Automated Content Marketing',
                'Continuous SEO Improvements',
                'Data-Driven SEO Strategy'
            ),
            'automation_features' => array(
                'weekly_audits' => 'Automatic SEO health checks',
                'blog_generation' => 'AI-generated blog posts based on competitor gaps',
                'competitor_monitoring' => 'Track competitor changes and opportunities',
                'ranking_updates' => 'Monitor keyword ranking improvements',
                'content_suggestions' => 'AI-powered content recommendations'
            )
        );
    }
    
    // ========================
    // SEO IMPLEMENTATION HELPER FUNCTIONS
    // ========================
    
    /**
     * Get user's website data for SEO optimization
     */
    private function get_user_website_data($user_id) {
        $posts = get_posts(array(
            'author' => $user_id,
            'post_type' => array('page', 'post'),
            'post_status' => array('publish', 'draft', 'private'),
            'posts_per_page' => 50,
            'orderby' => 'modified',
            'order' => 'DESC',
        ));

        $pages = array();
        $content_parts = array();
        foreach ((array) $posts as $post) {
            $pages[] = array(
                'id' => (int) $post->ID,
                'title' => get_the_title($post),
                'url' => get_permalink($post),
                'status' => $post->post_status,
                'content' => (string) $post->post_content,
                'excerpt' => (string) $post->post_excerpt,
                'meta_title' => (string) get_post_meta($post->ID, 'vmo_seo_title', true),
                'meta_description' => (string) get_post_meta($post->ID, 'vmo_seo_description', true),
                'featured_image_id' => (int) get_post_thumbnail_id($post->ID),
            );
            $content_parts[] = wp_strip_all_tags((string) $post->post_content);
        }

        return array(
            'pages' => $pages,
            'content' => trim(implode("\n\n", array_filter($content_parts))),
            'meta_data' => array(
                'page_count' => count($pages),
                'home_page_id' => (int) get_option('page_on_front'),
            ),
            'structure' => array(
                'page_ids' => wp_list_pluck($pages, 'id'),
                'statuses' => array_values(array_unique(wp_list_pluck($pages, 'status'))),
            ),
        );
    }
    
    /**
     * Apply meta optimizations to website
     */
    private function apply_meta_optimizations($website_data, $seo_strategy) {
        $keywords = array_values(array_filter(array_map('sanitize_text_field', (array) ($seo_strategy['primary_keywords'] ?? array()))));
        $changes = array();

        foreach ((array) ($website_data['pages'] ?? array()) as $page) {
            $description = $this->build_page_meta_description($page, $keywords);
            if ($description === '') {
                continue;
            }

            update_post_meta((int) $page['id'], 'vmo_seo_description', $description);
            $changes[] = array(
                'post_id' => (int) $page['id'],
                'meta_description' => $description,
            );
        }

        return array(
            'status' => empty($changes) ? 'no_pages_found' : 'applied',
            'changes' => $changes,
        );
    }
    
    /**
     * Optimize page titles and descriptions
     */
    private function optimize_page_titles($website_data, $seo_strategy) {
        $keywords = array_values(array_filter(array_map('sanitize_text_field', (array) ($seo_strategy['primary_keywords'] ?? array()))));
        $updated = 0;

        foreach ((array) ($website_data['pages'] ?? array()) as $page) {
            $seo_title = $this->build_page_seo_title($page, $keywords);
            if ($seo_title === '') {
                continue;
            }

            update_post_meta((int) $page['id'], 'vmo_seo_title', $seo_title);
            $updated++;
        }

        return array('status' => 'optimized', 'pages_updated' => $updated);
    }
    
    /**
     * Add local SEO schema markup
     */
    private function add_local_seo_schema($website_data, $seo_strategy) {
        $pages = (array) ($website_data['pages'] ?? array());
        if (empty($pages)) {
            return array('status' => 'no_pages_found', 'schema_types' => array());
        }

        $target_page = $pages[0];
        foreach ($pages as $page) {
            if ((int) ($page['id'] ?? 0) === (int) ($website_data['meta_data']['home_page_id'] ?? 0)) {
                $target_page = $page;
                break;
            }
        }

        $schema = array(
            '@context' => 'https://schema.org',
            '@type' => 'LocalBusiness',
            'name' => sanitize_text_field((string) ($seo_strategy['business_name'] ?? get_bloginfo('name'))),
            'url' => esc_url_raw((string) ($target_page['url'] ?? home_url('/'))),
            'description' => $this->build_page_meta_description($target_page, array_values(array_filter((array) ($seo_strategy['primary_keywords'] ?? array())))),
            'email' => sanitize_email((string) get_option('admin_email')),
        );

        $schema_json = wp_json_encode($schema, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        update_post_meta((int) $target_page['id'], 'vmo_seo_json_ld', $schema_json);

        return array(
            'status' => 'added',
            'schema_types' => array('LocalBusiness'),
            'post_id' => (int) $target_page['id'],
        );
    }
    
    /**
     * Optimize images for SEO
     */
    private function optimize_images_for_seo($website_data, $seo_strategy) {
        $keywords = array_values(array_filter(array_map('sanitize_text_field', (array) ($seo_strategy['primary_keywords'] ?? array()))));
        $processed = 0;
        $seen_images = array();

        foreach ((array) ($website_data['pages'] ?? array()) as $page) {
            foreach ($this->collect_page_image_ids($page) as $image_id) {
                if ($image_id <= 0 || isset($seen_images[$image_id])) {
                    continue;
                }

                $seen_images[$image_id] = true;
                $current_alt = (string) get_post_meta($image_id, '_wp_attachment_image_alt', true);
                if ($current_alt !== '') {
                    continue;
                }

                $alt_text = trim((string) ($page['title'] ?? ''));
                if (!empty($keywords)) {
                    $alt_text = trim($alt_text . ' ' . $keywords[0]);
                }
                $alt_text = sanitize_text_field($alt_text);
                if ($alt_text === '') {
                    continue;
                }

                update_post_meta($image_id, '_wp_attachment_image_alt', $alt_text);
                $processed++;
            }
        }

        return array('status' => 'optimized', 'images_processed' => $processed);
    }
    
    /**
     * Optimize content structure
     */
    private function optimize_content_structure($website_data, $seo_strategy) {
        $keywords = array_values(array_filter(array_map('sanitize_text_field', (array) ($seo_strategy['primary_keywords'] ?? array()))));
        $improvements = array();

        foreach ((array) ($website_data['pages'] ?? array()) as $page) {
            $post_id = (int) ($page['id'] ?? 0);
            if ($post_id <= 0) {
                continue;
            }

            if (trim((string) ($page['excerpt'] ?? '')) === '') {
                $excerpt = $this->build_page_meta_description($page, $keywords);
                if ($excerpt !== '') {
                    wp_update_post(array(
                        'ID' => $post_id,
                        'post_excerpt' => $excerpt,
                    ));
                    $improvements[] = array(
                        'post_id' => $post_id,
                        'change' => 'excerpt_added',
                    );
                }
            }

            if (!empty($keywords)) {
                update_post_meta($post_id, 'vmo_focus_keyword', $keywords[0]);
            }
        }

        return array('status' => 'optimized', 'improvements' => $improvements);
    }
    
    /**
     * Set up SEO tracking (Analytics, Search Console)
     */
    private function setup_seo_tracking($user_id) {
        $tracking_status = array(
            'analytics' => class_exists('VMO_Analytics_Tracker') ? 'self_hosted_active' : 'unavailable',
            'search_console' => get_option('blog_public') ? 'manual_verification_required' : 'site_private',
            'checked_at' => current_time('mysql'),
        );

        update_user_meta($user_id, 'vmo_seo_tracking_status', $tracking_status);

        return $tracking_status;
    }

    private function build_page_meta_description($page, $keywords = array()) {
        $source = trim((string) ($page['meta_description'] ?? ''));
        if ($source === '') {
            $source = trim((string) ($page['excerpt'] ?? ''));
        }
        if ($source === '') {
            $source = trim(wp_strip_all_tags((string) ($page['content'] ?? '')));
        }
        if ($source === '') {
            $source = trim((string) ($page['title'] ?? ''));
        }

        if ($source === '') {
            return '';
        }

        $description = wp_trim_words($source, 26, '');
        if (!empty($keywords) && stripos($description, $keywords[0]) === false) {
            $description = trim($keywords[0] . ' - ' . $description);
        }

        return sanitize_textarea_field(wp_html_excerpt($description, 155, ''));
    }

    private function build_page_seo_title($page, $keywords = array()) {
        $title = trim((string) ($page['meta_title'] ?? $page['title'] ?? ''));
        if ($title === '') {
            return '';
        }

        if (!empty($keywords) && stripos($title, $keywords[0]) === false) {
            $title .= ' | ' . $keywords[0];
        }

        $site_name = get_bloginfo('name');
        if ($site_name !== '' && stripos($title, $site_name) === false) {
            $title .= ' | ' . $site_name;
        }

        return sanitize_text_field(wp_html_excerpt($title, 60, ''));
    }

    private function collect_page_image_ids($page) {
        $image_ids = array();

        if (!empty($page['featured_image_id'])) {
            $image_ids[] = (int) $page['featured_image_id'];
        }

        if (!empty($page['content']) && preg_match_all('/wp-image-(\d+)/', (string) $page['content'], $matches)) {
            foreach ((array) ($matches[1] ?? array()) as $image_id) {
                $image_ids[] = (int) $image_id;
            }
        }

        return array_values(array_unique(array_filter($image_ids)));
    }
    
    /**
     * Schedule SEO audits
     * v8.2.1: Now respects plan-based frequency (Tester: none, Starter: biweekly, Pro/Elite: weekly)
     */
    private function schedule_seo_audits($user_id) {
        // Check if user's plan supports background ops
        if (!function_exists('vmo_get_background_ops_config')) {
            return false;
        }
        
        $config = vmo_get_background_ops_config($user_id);
        
        if (!$config['enabled']) {
            // Tester/Free plan - send upgrade prompt instead of scheduling
            if (function_exists('vmo_send_background_ops_upgrade_prompt')) {
                vmo_send_background_ops_upgrade_prompt($user_id, 'seo_audit');
            }
            error_log("VMO Background Ops: SEO audit not scheduled for user {$user_id} - plan does not support background ops");
            return false;
        }
        
        // Clear any existing schedule to ensure correct frequency
        wp_clear_scheduled_hook('vmo_weekly_seo_audit', array($user_id));
        
        // Schedule based on plan frequency
        $interval = $config['cron_interval']; // 'weekly' or 'twicemonthly'
        $next_run = strtotime('+1 day 10:00 AM'); // Start tomorrow at 10 AM
        
        wp_schedule_event($next_run, $interval, 'vmo_weekly_seo_audit', array($user_id));
        
        error_log("VMO Background Ops: SEO audit scheduled for user {$user_id} at {$config['frequency']} frequency");
        return true;
    }
    
    /**
     * Set up automated blog generation
     * v8.2.1: Now respects plan-based frequency
     */
    private function setup_automated_blog_generation($seo_strategy, $user_id) {
        // Check if user's plan supports background ops
        if (function_exists('vmo_can_run_background_ops') && !vmo_can_run_background_ops($user_id)) {
            if (function_exists('vmo_send_background_ops_upgrade_prompt')) {
                vmo_send_background_ops_upgrade_prompt($user_id, 'blog_generation');
            }
            error_log("VMO Background Ops: Blog generation not scheduled for user {$user_id} - plan does not support background ops");
            return array('status' => 'disabled', 'reason' => 'Plan does not include automated blog generation');
        }
        
        // Clear any existing schedule
        wp_clear_scheduled_hook('vmo_monthly_blog_generation', array($user_id, $seo_strategy));
        
        // Schedule monthly blog post generation (monthly for all plans that have background ops)
        wp_schedule_event(strtotime('+30 days 9:00 AM'), 'monthly', 'vmo_monthly_blog_generation', array($user_id, $seo_strategy));
        
        error_log("VMO Background Ops: Blog generation scheduled for user {$user_id} monthly");
        return array('status' => 'scheduled', 'frequency' => 'monthly');
    }
    
    /**
     * Set up competitor monitoring
     * v8.2.1: Now respects plan-based frequency (Tester: none, Starter: biweekly, Pro/Elite: weekly)
     */
    private function setup_competitor_monitoring($seo_strategy, $user_id) {
        // Check if user's plan supports background ops
        if (!function_exists('vmo_get_background_ops_config')) {
            return array('status' => 'disabled', 'reason' => 'Plan check not available');
        }
        
        $config = vmo_get_background_ops_config($user_id);
        
        if (!$config['enabled']) {
            // Tester/Free plan - send upgrade prompt instead of scheduling
            if (function_exists('vmo_send_background_ops_upgrade_prompt')) {
                vmo_send_background_ops_upgrade_prompt($user_id, 'competitor_monitoring');
            }
            error_log("VMO Background Ops: Competitor monitoring not scheduled for user {$user_id} - plan does not support background ops");
            return array('status' => 'disabled', 'reason' => 'Plan does not include competitor monitoring');
        }
        
        // Clear any existing schedule to ensure correct frequency
        wp_clear_scheduled_hook('vmo_competitor_monitoring', array($user_id, $seo_strategy));
        
        // Schedule based on plan frequency (Starter: biweekly, Pro/Elite: weekly)
        $interval = $config['cron_interval']; // 'weekly' or 'twicemonthly'
        $next_run = strtotime('+2 days 11:00 AM'); // Start in 2 days at 11 AM
        
        wp_schedule_event($next_run, $interval, 'vmo_competitor_monitoring', array($user_id, $seo_strategy));
        
        error_log("VMO Background Ops: Competitor monitoring scheduled for user {$user_id} at {$config['frequency']} frequency");
        return array('status' => 'monitoring', 'frequency' => $config['frequency']);
    }
    
    /**
     * Set up ranking tracking
     * v8.2.1: Now respects plan-based frequency (Tester: none, Starter: biweekly, Pro/Elite: weekly)
     */
    private function setup_ranking_tracking($seo_strategy, $user_id) {
        // Check if user's plan supports background ops
        if (!function_exists('vmo_get_background_ops_config')) {
            return array('status' => 'disabled', 'reason' => 'Plan check not available');
        }
        
        $config = vmo_get_background_ops_config($user_id);
        
        if (!$config['enabled']) {
            // Tester/Free plan - send upgrade prompt
            if (function_exists('vmo_send_background_ops_upgrade_prompt')) {
                vmo_send_background_ops_upgrade_prompt($user_id, 'ranking_tracking');
            }
            error_log("VMO Background Ops: Ranking tracking not scheduled for user {$user_id} - plan does not support background ops");
            return array('status' => 'disabled', 'reason' => 'Plan does not include ranking tracking');
        }
        
        // Clear any existing schedule to ensure correct frequency
        wp_clear_scheduled_hook('vmo_ranking_tracking', array($user_id, $seo_strategy));
        
        // Schedule based on plan frequency
        $interval = $config['cron_interval']; // 'weekly' or 'twicemonthly'
        $next_run = strtotime('+3 days 10:00 AM'); // Start in 3 days at 10 AM
        
        wp_schedule_event($next_run, $interval, 'vmo_ranking_tracking', array($user_id, $seo_strategy));
        
        error_log("VMO Background Ops: Ranking tracking scheduled for user {$user_id} at {$config['frequency']} frequency");
        return array('status' => 'tracking', 'frequency' => $config['frequency']);
    }
    
    /**
     * Extract primary keywords from strategy
     */
    private function extract_primary_keywords($strategy_text) {
        $keywords = array();
        
        // Look for primary keywords section
        if (preg_match('/primary keywords?:(.+?)(?:

|$)/is', $strategy_text, $matches)) {
            $keyword_text = $matches[1];
            $keyword_lines = explode("
", $keyword_text);
            
            foreach ($keyword_lines as $line) {
                if (trim($line) && !empty(trim($line))) {
                    $keywords[] = trim(str_replace(array('-', '*', '•', '"'), '', $line));
                }
            }
        }
        
        return array_unique($keywords);
    }
    
    /**
     * Generate content calendar from strategy
     */
    private function generate_content_calendar($strategy_text) {
        // Extract blog topics and create monthly calendar
        $calendar = array();
        
        for ($i = 1; $i <= 12; $i++) {
            $calendar[date('Y-m', strtotime("+{$i} months"))] = array(
                'blog_posts' => 2,
                'topics' => array("Industry topic {$i}A", "Local topic {$i}B")
            );
        }
        
        return $calendar;
    }
    
    /**
     * Extract technical SEO tasks
     */
    private function extract_technical_seo_tasks($strategy_text) {
        $tasks = array();
        
        // Look for technical SEO section
        if (preg_match('/technical seo.{0,20}:(.+?)(?:

|$)/is', $strategy_text, $matches)) {
            $task_text = $matches[1];
            $task_lines = explode("
", $task_text);
            
            foreach ($task_lines as $line) {
                if (trim($line) && !empty(trim($line))) {
                    $tasks[] = trim(str_replace(array('-', '*', '•'), '', $line));
                }
            }
        }
        
        return $tasks;
    }
    
    // ========================
    // SEO AUTOMATION EXECUTION FUNCTIONS
    // ========================

    private function seo_table_exists($table) {
        global $wpdb;
        return $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table)) === $table;
    }

    private function insert_seo_trace_row($table, array $row) {
        global $wpdb;
        if (!$this->seo_table_exists($table)) {
            return false;
        }
        $columns = $wpdb->get_col("SHOW COLUMNS FROM {$table}", 0);
        if (is_array($columns) && !empty($columns)) {
            $row = array_intersect_key($row, array_flip(array_map('strval', $columns)));
        }
        return !empty($row) ? (bool) $wpdb->insert($table, $row) : false;
    }

    private function summarize_seo_audit_updates($updates_applied) {
        if (!is_array($updates_applied)) {
            return sanitize_text_field((string) $updates_applied);
        }
        $parts = array();
        foreach ($updates_applied as $key => $value) {
            $label = ucwords(str_replace('_', ' ', (string) $key));
            if (is_array($value)) {
                $count = count(array_filter($value));
                $parts[] = $label . ': ' . ($count > 0 ? $count . ' item(s)' : 'none');
            } else {
                $value_text = sanitize_text_field((string) $value);
                $parts[] = $label . ': ' . ($value_text !== '' ? $value_text : 'none');
            }
        }
        return implode('; ', array_slice($parts, 0, 8));
    }

    private function log_seo_audit_trace($user_id, array $audit_data, $updates_applied, $blog_recommendations) {
        global $wpdb;
        $summary = $this->summarize_seo_audit_updates($updates_applied);
        $priority = strtolower((string) ($blog_recommendations['priority'] ?? 'normal'));
        $priority = in_array($priority, array('urgent', 'high', 'normal', 'low'), true) ? $priority : 'normal';
        $description = 'Recurring SEO audit completed. Low-risk updates and findings: ' . ($summary ?: 'No automatic changes needed.') . ' Blog/content priority: ' . $priority . '.';
        $payload = wp_json_encode(array(
            'audit_date' => $audit_data['audit_date'] ?? current_time('mysql'),
            'updates_applied' => $updates_applied,
            'blog_recommendations' => $blog_recommendations,
            'report_source' => 'recurring_seo_audit',
        ));

        $this->insert_seo_trace_row($wpdb->prefix . 'vmo_actions', array(
            'user_id' => $user_id,
            'title' => 'Review recurring SEO audit updates',
            'description' => $description,
            'source' => 'seo',
            'source_id' => 0,
            'priority' => $priority === 'urgent' ? 'urgent' : ($priority === 'high' ? 'high' : 'normal'),
            'category' => 'marketing',
            'status' => 'pending',
            'due_date' => date('Y-m-d', strtotime('+1 day')),
            'created_at' => current_time('mysql'),
            'updated_at' => current_time('mysql'),
        ));

        $this->insert_seo_trace_row($wpdb->prefix . 'vmo_activity_log', array(
            'user_id' => $user_id,
            'action_type' => 'seo_scan',
            'description' => $description,
            'source_system' => 'seo',
            'related_id' => 0,
            'metadata' => $payload,
            'created_at' => current_time('mysql'),
        ));

        $this->insert_seo_trace_row($wpdb->prefix . 'vmo_approval_queue', array(
            'user_id' => $user_id,
            'type' => 'seo',
            'title' => 'SEO: Recurring audit trace',
            'payload' => $payload,
            'status' => 'approved',
            'notes' => 'recurring_audit_low_risk_changes_logged',
            'created_at' => current_time('mysql'),
            'updated_at' => current_time('mysql'),
        ));

        $trace_history = get_user_meta($user_id, 'vmo_seo_audit_trace_history', true);
        if (!is_array($trace_history)) {
            $trace_history = array();
        }
        $trace_history[] = array(
            'date' => current_time('mysql'),
            'summary' => $summary,
            'priority' => $priority,
        );
        update_user_meta($user_id, 'vmo_seo_audit_trace_history', array_slice($trace_history, -24));
    }
    
    /**
     * Execute SEO audit (triggered by cron)
     */
    public function execute_seo_audit($user_id, $seo_strategy) {
        try {
            // ✅ TOKEN DEDUCTION v8.1.0: Charge for SEO audit (6,000 tokens)
            if (function_exists('vmo_deduct_tokens')) {
                $cost = defined('VMO_TOKENS_WEEKLY_SEO_AUDIT') ? VMO_TOKENS_WEEKLY_SEO_AUDIT : 6000;
                if (!vmo_deduct_tokens($user_id, $cost, 'seo_audit', 'Comprehensive SEO audit and strategy update')) {
                    error_log("VMO SEO Audit: Insufficient tokens for user {$user_id}");
                    return;
                }
            }
            
            error_log("VMO SEO: Executing audit for user {$user_id}");
            
            // Get current website state
            $website_data = $this->get_user_website_data($user_id);
            
            // Conduct new competitive analysis
            $responses = $this->get_user_consultation_responses($user_id);
            $new_competitive_analysis = $this->conduct_deep_competitive_analysis($responses);
            
            // Compare with previous analysis and identify changes
            $analysis_comparison = $this->compare_competitive_analyses($user_id, $new_competitive_analysis);
            
            // Generate updated SEO recommendations
            $updated_strategy = $this->generate_competitive_seo_strategy($new_competitive_analysis, $responses);
            
            // Apply necessary updates
            $updates_applied = $this->apply_seo_updates_from_audit($website_data, $updated_strategy, $analysis_comparison);
            
            // Check if new blog content is needed
            $blog_recommendations = $this->analyze_blog_content_needs($new_competitive_analysis, $user_id);
            
            $audit_data = array(
                'audit_date' => current_time('mysql'),
                'competitive_analysis' => $new_competitive_analysis,
                'strategy_updates' => $updated_strategy,
                'changes_applied' => $updates_applied,
                'blog_recommendations' => $blog_recommendations
            );

            // Save audit results
            $this->save_seo_audit_results($user_id, $audit_data);
            if (function_exists('vmo_seo_build_report_snapshot')) {
                vmo_seo_build_report_snapshot($user_id, true);
            }
            $this->log_seo_audit_trace($user_id, $audit_data, $updates_applied, $blog_recommendations);
            
            // Send audit report email
            $this->send_seo_audit_email($user_id, $updates_applied, $blog_recommendations);
            
        } catch (\Throwable $e) {
            error_log('VMO SEO Audit Error: ' . $e->getMessage());
        }
    }
    
    /**
     * Generate automated blog post (triggered by cron)
     */
    public function generate_automated_blog_post($user_id, $seo_strategy) {
        try {
            // ✅ TOKEN DEDUCTION v8.1.0: Charge for automated blog post (4,000 tokens)
            if (function_exists('vmo_deduct_tokens')) {
                $cost = defined('VMO_TOKENS_MONTHLY_BLOG') ? VMO_TOKENS_MONTHLY_BLOG : 4000;
                if (!vmo_deduct_tokens($user_id, $cost, 'automated_blog', 'AI-generated SEO blog post')) {
                    error_log("VMO Blog Generation: Insufficient tokens for user {$user_id}");
                    return;
                }
            }
            
            error_log("VMO SEO: Generating automated blog post for user {$user_id}");
            
            // Get user's consultation responses
            $responses = $this->get_user_consultation_responses($user_id);
            
            // Get latest competitive analysis
            $competitive_analysis = $this->get_latest_competitive_analysis($user_id);
            
            // Generate blog topic based on content gaps and opportunities
            $blog_topic = $this->generate_strategic_blog_topic($competitive_analysis, $responses);
            
            // Generate full blog post optimized for SEO
            $blog_content = $this->generate_seo_optimized_blog_post($blog_topic, $competitive_analysis, $responses);
            
            // Save blog post to user's website
            $blog_published = $this->publish_blog_post_to_website($user_id, $blog_content);
            
            // Update content calendar
            $this->update_content_calendar($user_id, $blog_topic, $blog_published);
            
            // Send blog notification email
            $this->send_new_blog_notification($user_id, $blog_content, $blog_published);
            
        } catch (\Throwable $e) {
            error_log('VMO Blog Generation Error: ' . $e->getMessage());
        }
    }
    
    /**
     * Execute weekly SEO audit (called by WordPress cron)
     * v8.2.1: Now validates plan-based access before running
     */
    public function execute_weekly_seo_audit($user_id) {
        // ✅ PLAN CHECK v8.2.1: Verify user's plan supports background ops
        if (function_exists('vmo_can_run_background_ops') && !vmo_can_run_background_ops($user_id)) {
            error_log("VMO SEO Audit: User {$user_id} plan does not support background ops - skipping");
            if (function_exists('vmo_send_background_ops_upgrade_prompt')) {
                vmo_send_background_ops_upgrade_prompt($user_id, 'seo_audit');
            }
            // Clear this schedule since user shouldn't have background ops
            wp_clear_scheduled_hook('vmo_weekly_seo_audit', array($user_id));
            return;
        }
        
        $seo_strategy = $this->get_user_seo_strategy($user_id);
        if ($seo_strategy) {
            $this->execute_seo_audit($user_id, $seo_strategy);
        }
    }
    
    /**
     * Execute monthly blog generation (called by WordPress cron)
     * v8.2.1: Now validates plan-based access before running
     */
    public function execute_monthly_blog_generation($user_id, $seo_strategy) {
        // ✅ PLAN CHECK v8.2.1: Verify user's plan supports background ops
        if (function_exists('vmo_can_run_background_ops') && !vmo_can_run_background_ops($user_id)) {
            error_log("VMO Blog Generation: User {$user_id} plan does not support background ops - skipping");
            if (function_exists('vmo_send_background_ops_upgrade_prompt')) {
                vmo_send_background_ops_upgrade_prompt($user_id, 'blog_generation');
            }
            // Clear this schedule since user shouldn't have background ops
            wp_clear_scheduled_hook('vmo_monthly_blog_generation', array($user_id, $seo_strategy));
            return;
        }
        
        $this->generate_automated_blog_post($user_id, $seo_strategy);
    }
    
    /**
     * Execute competitor monitoring (called by WordPress cron)
     * v8.2.1: Now validates plan-based access before running
     */
    public function execute_competitor_monitoring($user_id, $seo_strategy) {
        // ✅ PLAN CHECK v8.2.1: Verify user's plan supports background ops
        if (function_exists('vmo_can_run_background_ops') && !vmo_can_run_background_ops($user_id)) {
            error_log("VMO Competitor Monitoring: User {$user_id} plan does not support background ops - skipping");
            if (function_exists('vmo_send_background_ops_upgrade_prompt')) {
                vmo_send_background_ops_upgrade_prompt($user_id, 'competitor_monitoring');
            }
            // Clear this schedule since user shouldn't have background ops
            wp_clear_scheduled_hook('vmo_competitor_monitoring', array($user_id, $seo_strategy));
            return;
        }
        
        try {
            // ✅ TOKEN DEDUCTION v8.1.0: Charge for competitor monitoring (4,000 tokens)
            if (function_exists('vmo_deduct_tokens')) {
                $cost = defined('VMO_TOKENS_COMPETITOR_MONITORING') ? VMO_TOKENS_COMPETITOR_MONITORING : 4000;
                if (!vmo_deduct_tokens($user_id, $cost, 'competitor_monitoring', 'Competitor monitoring and analysis')) {
                    error_log("VMO Competitor Monitoring: Insufficient tokens for user {$user_id}");
                    return;
                }
            }
            
            // Get user consultation data
            $responses = $this->get_user_consultation_responses($user_id);
            
            // Conduct fresh competitive analysis
            $new_analysis = $this->conduct_deep_competitive_analysis($responses);
            
            // Compare with previous analysis
            $changes = $this->detect_competitor_changes($user_id, $new_analysis);
            
            // If significant changes detected, trigger SEO audit
            if (!empty($changes)) {
                $this->execute_seo_audit($user_id, $seo_strategy);
                $this->send_competitor_changes_alert($user_id, $changes);
            }
            
        } catch (\Throwable $e) {
            error_log('VMO Competitor Monitoring Error: ' . $e->getMessage());
        }
    }
    
    /**
     * Execute ranking tracking (called by WordPress cron)
     * v8.2.1: Now validates plan-based access before running
     */
    public function execute_ranking_tracking($user_id, $seo_strategy) {
        // ✅ PLAN CHECK v8.2.1: Verify user's plan supports background ops
        if (function_exists('vmo_can_run_background_ops') && !vmo_can_run_background_ops($user_id)) {
            error_log("VMO Ranking Tracking: User {$user_id} plan does not support background ops - skipping");
            if (function_exists('vmo_send_background_ops_upgrade_prompt')) {
                vmo_send_background_ops_upgrade_prompt($user_id, 'ranking_tracking');
            }
            // Clear this schedule since user shouldn't have background ops
            wp_clear_scheduled_hook('vmo_ranking_tracking', array($user_id, $seo_strategy));
            return;
        }
        
        try {
            // Track keyword rankings from live search results against the site's own URLs.
            $ranking_data = $this->check_keyword_rankings($user_id, $seo_strategy);
            
            // Save ranking history
            $this->save_ranking_data($user_id, $ranking_data);
            
            // Send monthly ranking report
            if (date('j') == 1) { // First day of month
                $this->send_monthly_ranking_report($user_id, $ranking_data);
            }
            
        } catch (\Throwable $e) {
            error_log('VMO Ranking Tracking Error: ' . $e->getMessage());
        }
    }
    
    // ========================
    // SEO AUTOMATION HELPER FUNCTIONS
    // ========================
    
    /**
     * Get user consultation responses from database
     */
    private function get_user_consultation_responses($user_id) {
        // Get the latest consultation for this user
        $consultation = $this->consultation_db->get_latest_consultation_by_user($user_id);
        
        if ($consultation && $consultation->responses) {
            return json_decode($consultation->responses, true);
        }
        
        return array();
    }
    
    
    /**
     * Get user SEO strategy from database
     */
    private function get_user_seo_strategy($user_id) {
        $strategy = get_user_meta($user_id, 'vmo_seo_strategy', true);
        return $strategy ?: array();
    }

    /**
     * Return readable competitive analysis text from any supported schema.
     */
    private function get_competitive_analysis_text($competitive_analysis) {
        if (is_string($competitive_analysis)) {
            return $competitive_analysis;
        }
        if (!is_array($competitive_analysis)) {
            return '';
        }
        if (!empty($competitive_analysis['raw_analysis'])) {
            return is_string($competitive_analysis['raw_analysis']) ? $competitive_analysis['raw_analysis'] : wp_json_encode($competitive_analysis['raw_analysis'], JSON_PRETTY_PRINT);
        }
        if (!empty($competitive_analysis['enhanced_analysis'])) {
            return is_string($competitive_analysis['enhanced_analysis']) ? $competitive_analysis['enhanced_analysis'] : wp_json_encode($competitive_analysis['enhanced_analysis'], JSON_PRETTY_PRINT);
        }
        return wp_json_encode($competitive_analysis, JSON_PRETTY_PRINT);
    }

    /**
     * Normalize competitor rows to plain names for comparisons.
     */
    private function get_competitor_names_from_analysis($competitive_analysis) {
        if (!is_array($competitive_analysis)) {
            return array();
        }
        $competitor_rows = $competitive_analysis['competitors_identified'] ?? $competitive_analysis['competitors'] ?? array();
        $names = array();
        foreach ((array) $competitor_rows as $competitor_row) {
            if (is_array($competitor_row)) {
                $names[] = sanitize_text_field((string) ($competitor_row['name'] ?? $competitor_row['business'] ?? $competitor_row['title'] ?? ''));
            } else {
                $names[] = sanitize_text_field((string) $competitor_row);
            }
        }
        return array_values(array_unique(array_filter($names)));
    }
    
    /**
     * Compare competitive analyses to detect changes
     */
    private function compare_competitive_analyses($user_id, $new_analysis) {
        $previous_analysis = get_user_meta($user_id, 'vmo_previous_competitive_analysis', true);
        
        // Save current as previous for next comparison
        update_user_meta($user_id, 'vmo_previous_competitive_analysis', $new_analysis);
        
        if (!$previous_analysis) {
            return array('first_analysis' => true);
        }
        
        $current_competitors = $this->get_competitor_names_from_analysis($new_analysis);
        $previous_competitors = $this->get_competitor_names_from_analysis($previous_analysis);

        $current_index = array_map('strtolower', $current_competitors);
        $previous_index = array_map('strtolower', $previous_competitors);

        $new_competitors = array_values(array_diff($current_index, $previous_index));
        $lost_competitors = array_values(array_diff($previous_index, $current_index));
        $changes = array();

        if (count($current_competitors) !== count($previous_competitors)) {
            $changes[] = 'Competitor landscape changed';
        }
        if (!empty($new_competitors)) {
            $changes[] = 'New competitors detected';
        }
        if (!empty($lost_competitors)) {
            $changes[] = 'Previously tracked competitors dropped out';
        }

        return array(
            'first_analysis' => false,
            'changes' => $changes,
            'new_competitors' => $new_competitors,
            'lost_competitors' => $lost_competitors,
            'competitor_count_delta' => count($current_competitors) - count($previous_competitors),
        );
    }
    
    /**
     * Apply SEO updates based on audit results
     */
    private function apply_seo_updates_from_audit($website_data, $updated_strategy, $analysis_comparison) {
        return array(
            'meta_updates' => $this->apply_meta_optimizations($website_data, $updated_strategy),
            'title_updates' => $this->optimize_page_titles($website_data, $updated_strategy),
            'schema_updates' => $this->add_local_seo_schema($website_data, $updated_strategy),
            'content_updates' => $this->optimize_content_structure($website_data, $updated_strategy),
            'competitive_changes' => is_array($analysis_comparison) ? ($analysis_comparison['changes'] ?? $analysis_comparison) : array(),
        );
    }
    
    /**
     * Analyze blog content needs based on competitive analysis
     */
    private function analyze_blog_content_needs($competitive_analysis, $user_id) {
        $business_context = $this->format_business_context($this->get_user_consultation_responses($user_id));
        $analysis_text = $this->get_competitive_analysis_text($competitive_analysis);
        
        $analysis_prompt = "Based on the competitive analysis, determine if new blog content is needed:

COMPETITIVE ANALYSIS:
    " . $analysis_text . "

BUSINESS CONTEXT:
" . $business_context . "

DETERMINE:
1. Are there content gaps we can fill with blog posts?
2. What blog topics would help outrank competitors?
3. What questions are customers asking that competitors aren't answering?
4. What local/industry topics are underserved?

RECOMMEND: Specific blog post titles and brief descriptions that would improve SEO rankings.";

        $blog_analysis = $this->call_openai_api($analysis_prompt, 1000);
        
        return array(
            'analysis' => $blog_analysis,
            'recommended_posts' => $this->extract_blog_recommendations($blog_analysis),
            'priority' => $this->determine_blog_priority($blog_analysis)
        );
    }
    
    /**
     * Save SEO audit results
     */
    private function save_seo_audit_results($user_id, $audit_data) {
        // Save audit results to user meta
        $audit_history = get_user_meta($user_id, 'vmo_seo_audit_history', true);
        if (!is_array($audit_history)) {
            $audit_history = array();
        }
        
        $audit_history[] = $audit_data;
        
        // Keep only last 12 audits
        if (count($audit_history) > 12) {
            $audit_history = array_slice($audit_history, -12);
        }
        
        update_user_meta($user_id, 'vmo_seo_audit_history', $audit_history);
        update_user_meta($user_id, 'vmo_latest_competitive_analysis', $audit_data['competitive_analysis']);
        update_user_meta($user_id, 'vmo_seo_strategy', $audit_data['strategy_updates']);
    }
    
    /**
     * Send SEO audit email to user
     */
    private function send_seo_audit_email($user_id, $updates_applied, $blog_recommendations) {
        $user = get_userdata($user_id);
        if (!$user) return false;
        
        $subject = "🚀 Your SEO Audit Results & Website Improvements";
        
        $message = "
        <h2>Your Weekly SEO Audit is Complete!</h2>
        <p>We've analyzed your competitors and updated your website's SEO automatically.</p>
        
        <h3>🔍 What We Found:</h3>
        <ul>
            <li>Competitive landscape changes detected</li>
            <li>SEO opportunities identified</li>
            <li>Website optimizations applied</li>
        </ul>
        
        <h3>📈 Improvements Applied:</h3>
        <ul>
            <li>Meta tags updated: " . ($updates_applied['meta_updates'] ?? 'No changes needed') . "</li>
            <li>Content optimized: " . ($updates_applied['content_updates'] ?? 'No changes needed') . "</li>
            <li>Schema markup: " . ($updates_applied['schema_updates'] ?? 'No changes needed') . "</li>
        </ul>
        
        <h3>📝 Blog Recommendations:</h3>
        <p>" . ($blog_recommendations['analysis'] ?? 'No new blog content needed at this time') . "</p>
        ";
        
        $headers = array('Content-Type: text/html; charset=UTF-8');
        return wp_mail($user->user_email, $subject, $message, $headers);
    }
    
    /**
     * Generate strategic blog topic based on competitive gaps
     */
    private function generate_strategic_blog_topic($competitive_analysis, $responses) {
        $business_context = $this->format_business_context($responses);
        $analysis_text = $this->get_competitive_analysis_text($competitive_analysis);
        
        $topic_prompt = "Based on competitive analysis, generate a high-impact blog post topic:

COMPETITIVE ANALYSIS:
    " . $analysis_text . "

BUSINESS CONTEXT:
" . $business_context . "

GENERATE A BLOG TOPIC THAT:
1. Fills a content gap competitors are missing
2. Targets keywords with SEO opportunity
3. Addresses customer pain points
4. Establishes business expertise
5. Has strong local SEO potential

PROVIDE:
- Exact blog post title
- Target keywords (3-5 primary)
- Brief content outline
- SEO optimization strategy
- Expected ranking potential";

        return $this->call_openai_api($topic_prompt, 800);
    }
    
    /**
     * Generate full SEO-optimized blog post
     */
    private function generate_seo_optimized_blog_post($blog_topic, $competitive_analysis, $responses) {
        $business_context = $this->format_business_context($responses);
        $analysis_text = $this->get_competitive_analysis_text($competitive_analysis);
        
        $content_prompt = "Create a comprehensive, SEO-optimized blog post:

BLOG TOPIC & STRATEGY:
" . $blog_topic . "

BUSINESS CONTEXT:
" . $business_context . "

COMPETITIVE INSIGHTS:
" . $analysis_text . "

CREATE A BLOG POST THAT:
1. Is 1500-2000 words for SEO authority
2. Uses target keywords naturally throughout
3. Includes local SEO elements
4. Has engaging headings and subheadings
5. Provides genuine value to readers
6. Includes clear calls-to-action
7. Has optimized meta description
8. Uses schema markup suggestions

FORMAT:
- Title (H1)
- Meta Description
- Introduction with keyword
- Main content with H2/H3 headings
- Conclusion with CTA
- Suggested schema markup";

        return $this->call_openai_api($content_prompt, 2500);
    }
    
    /**
     * Publish blog post to user's website
     */
    private function publish_blog_post_to_website($user_id, $blog_content) {
        try {
            // Extract title from blog content
            $title = $this->extract_blog_title($blog_content);
            
            // Create WordPress post
            $post_data = array(
                'post_title' => $title,
                'post_content' => $blog_content,
                'post_status' => 'draft',
                'post_type' => 'post',
                'post_author' => $user_id,
                'meta_input' => array(
                    'vmo_generated_blog' => true,
                    'vmo_seo_optimized' => true,
                    'vmo_generation_date' => current_time('mysql')
                )
            );
            
            $post_id = wp_insert_post($post_data);
            
            if ($post_id && !is_wp_error($post_id)) {
                return array(
                    'published' => false,
                    'draft_created' => true,
                    'post_id' => $post_id,
                    'post_url' => get_permalink($post_id),
                    'post_status' => 'draft',
                    'title' => $title
                );
            } else {
                return array('published' => false, 'error' => 'Failed to create post');
            }
            
        } catch (\Throwable $e) {
            return array('published' => false, 'error' => $e->getMessage());
        }
    }
    
    /**
     * Extract blog title from content
     */
    private function extract_blog_title($blog_content) {
        // Look for title in various formats
        if (preg_match('/(?:title|h1):\s*(.+?)(?:
|$)/i', $blog_content, $matches)) {
            return trim($matches[1]);
        }
        
        // Fallback to first line
        $lines = explode("
", $blog_content);
        return trim($lines[0]);
    }
    
    /**
     * Extract blog recommendations from analysis
     */
    private function extract_blog_recommendations($blog_analysis) {
        $recommendations = array();
        
        // Extract recommended blog topics
        if (preg_match_all('/(?:topic|title):\s*"([^"]+)"/i', $blog_analysis, $matches)) {
            foreach ($matches[1] as $topic) {
                $recommendations[] = trim($topic);
            }
        }
        
        return $recommendations;
    }
    
    /**
     * Determine blog content priority
     */
    private function determine_blog_priority($blog_analysis) {
        // Analyze urgency based on competitive gaps
        if (stripos($blog_analysis, 'urgent') !== false || stripos($blog_analysis, 'critical') !== false) {
            return 'high';
        } elseif (stripos($blog_analysis, 'opportunity') !== false) {
            return 'medium';
        }
        return 'normal';
    }
    
    /**
     * Update content calendar
     */
    private function update_content_calendar($user_id, $blog_topic, $blog_published) {
        $calendar = get_user_meta($user_id, 'vmo_content_calendar', true);
        if (!is_array($calendar)) {
            $calendar = array();
        }
        
        $calendar[date('Y-m')] = array(
            'topic' => $blog_topic,
            'published' => $blog_published,
            'date' => current_time('mysql')
        );
        
        update_user_meta($user_id, 'vmo_content_calendar', $calendar);
    }
    
    /**
     * Send new blog notification
     */
    private function send_new_blog_notification($user_id, $blog_content, $blog_published) {
        if (empty($blog_published['published']) && empty($blog_published['draft_created'])) return false;
        
        $user = get_userdata($user_id);
        if (!$user) return false;
        
        $is_draft = !empty($blog_published['draft_created']);
        $subject = ($is_draft ? "New SEO Blog Draft Ready: " : "New SEO Blog Post Published: ") . $blog_published['title'];
        
        $message = "
        <h2>Your New SEO-Optimized Blog " . ($is_draft ? "Draft is Ready" : "Post is Live") . "</h2>
        <p>We've created a new blog post designed to improve your search rankings and attract customers. " . ($is_draft ? "It is saved as a draft for review before publishing." : "It has been published on your website.") . "</p>
        
        <h3>📝 Blog Post Details:</h3>
        <ul>
            <li><strong>Title:</strong> " . $blog_published['title'] . "</li>
            <li><strong>URL:</strong> <a href='" . $blog_published['post_url'] . "'>" . $blog_published['post_url'] . "</a></li>
            <li><strong>Status:</strong> " . ($is_draft ? "Draft" : "Published") . "</li>
            <li><strong>Created:</strong> " . current_time('mysql') . "</li>
        </ul>
        
        <p><a href='" . $blog_published['post_url'] . "' style='background: #0073aa; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px;'>" . ($is_draft ? "Preview Draft" : "View Blog Post") . "</a></p>
        ";
        
        $headers = array('Content-Type: text/html; charset=UTF-8');
        return wp_mail($user->user_email, $subject, $message, $headers);
    }
    
    /**
     * Detect competitor changes
     */
    private function detect_competitor_changes($user_id, $new_analysis) {
        $previous = get_user_meta($user_id, 'vmo_previous_competitive_analysis', true);
        
        if (!$previous) {
            return array(); // No previous data to compare
        }
        
        $changes = array();
        
        // Detect new competitors
        $new_competitors = array_diff(
            $this->get_competitor_names_from_analysis($new_analysis),
            $this->get_competitor_names_from_analysis($previous)
        );
        
        if (!empty($new_competitors)) {
            $changes[] = 'New competitors detected: ' . implode(', ', $new_competitors);
        }
        
        return $changes;
    }
    
    /**
     * Send competitor changes alert
     */
    private function send_competitor_changes_alert($user_id, $changes) {
        $user = get_userdata($user_id);
        if (!$user) return false;
        
        $subject = "🚨 Competitor Changes Detected - SEO Update Applied";
        
        $message = "
        <h2>Competitor Landscape Changes Detected</h2>
        <p>Our monitoring system detected changes in your competitive landscape and automatically updated your SEO strategy.</p>
        
        <h3>🔍 Changes Detected:</h3>
        <ul>";
        
        foreach ($changes as $change) {
            $message .= "<li>" . $change . "</li>";
        }
        
        $message .= "
        </ul>
        
        <p>We've automatically adjusted your website's SEO to maintain your competitive advantage.</p>
        ";
        
        $headers = array('Content-Type: text/html; charset=UTF-8');
        return wp_mail($user->user_email, $subject, $message, $headers);
    }
    
    /**
    * Check keyword rankings from live search results.
     */
    private function check_keyword_rankings($user_id, $seo_strategy) {
        $keywords = array_values(array_unique(array_filter(array_map('sanitize_text_field', (array) ($seo_strategy['primary_keywords'] ?? array())))));
        $keywords = array_slice($keywords, 0, 10);
        if (empty($keywords)) {
            return array(
                'rankings_checked' => true,
                'keywords_tracked' => 0,
                'average_position' => 0,
                'improvements' => 0,
                'keywords' => array(),
            );
        }

        $website_data = $this->get_user_website_data($user_id);
        $target_hosts = array();
        foreach ((array) ($website_data['pages'] ?? array()) as $page) {
            $host = wp_parse_url((string) ($page['url'] ?? ''), PHP_URL_HOST);
            if (!empty($host)) {
                $target_hosts[] = strtolower((string) $host);
            }
        }
        $home_host = wp_parse_url(home_url('/'), PHP_URL_HOST);
        if (!empty($home_host)) {
            $target_hosts[] = strtolower((string) $home_host);
        }
        $target_hosts = array_values(array_unique(array_filter($target_hosts)));

        $keyword_rankings = array();
        foreach ($keywords as $keyword) {
            $search = $this->execute_web_search($keyword, 'surface');
            $position = 0;
            $matched_url = '';

            foreach ((array) ($search['results'] ?? array()) as $result) {
                $result_url = esc_url_raw((string) ($result['url'] ?? ''));
                $result_host = strtolower((string) wp_parse_url($result_url, PHP_URL_HOST));
                if ($result_host === '') {
                    continue;
                }

                foreach ($target_hosts as $target_host) {
                    if ($result_host === $target_host || substr($result_host, -strlen('.' . $target_host)) === '.' . $target_host) {
                        $position = (int) ($result['position'] ?? 0);
                        $matched_url = $result_url;
                        break 2;
                    }
                }
            }

            $keyword_rankings[] = array(
                'keyword' => $keyword,
                'position' => $position,
                'url' => $matched_url,
            );
        }

        $positions = array_values(array_filter(array_map(static function ($ranking) {
            return (int) ($ranking['position'] ?? 0);
        }, $keyword_rankings)));
        $average_position = !empty($positions) ? round(array_sum($positions) / count($positions), 1) : 0;

        $history = get_user_meta($user_id, 'vmo_ranking_history', true);
        $last_snapshot = array();
        if (is_array($history) && !empty($history)) {
            $latest_entry = end($history);
            if (is_array($latest_entry['keywords'] ?? null)) {
                foreach ($latest_entry['keywords'] as $keyword_row) {
                    $last_snapshot[(string) ($keyword_row['keyword'] ?? '')] = (int) ($keyword_row['position'] ?? 0);
                }
            }
        }

        $improvements = 0;
        foreach ($keyword_rankings as $ranking) {
            $previous_position = (int) ($last_snapshot[$ranking['keyword']] ?? 0);
            $current_position = (int) ($ranking['position'] ?? 0);
            if ($current_position > 0 && ($previous_position === 0 || $current_position < $previous_position)) {
                $improvements++;
            }
        }

        return array(
            'rankings_checked' => true,
            'keywords_tracked' => count($keywords),
            'average_position' => $average_position,
            'improvements' => $improvements,
            'tracked_domain' => $target_hosts[0] ?? '',
            'keywords' => $keyword_rankings,
        );
    }
    
    /**
     * Save ranking data
     */
    private function save_ranking_data($user_id, $ranking_data) {
        $history = get_user_meta($user_id, 'vmo_ranking_history', true);
        if (!is_array($history)) {
            $history = array();
        }
        
        $history[date('Y-m-d')] = $ranking_data;
        
        // Keep only last 90 days
        if (count($history) > 90) {
            $history = array_slice($history, -90, null, true);
        }
        
        update_user_meta($user_id, 'vmo_ranking_history', $history);
    }
    
    /**
     * Send monthly ranking report
     */
    private function send_monthly_ranking_report($user_id, $ranking_data) {
        $user = get_userdata($user_id);
        if (!$user) return false;
        
        $subject = "📊 Monthly SEO Ranking Report - " . date('F Y');
        
        $message = "
        <h2>Your Monthly SEO Performance Report</h2>
        
        <h3>📈 This Month's Highlights:</h3>
        <ul>
            <li>Keywords tracked: " . $ranking_data['keywords_tracked'] . "</li>
            <li>Average position: " . $ranking_data['average_position'] . "</li>
            <li>Position improvements: " . $ranking_data['improvements'] . "</li>
        </ul>
        
        <p>Our automated SEO system continues to monitor and optimize your website for better search rankings.</p>
        ";
        
        $headers = array('Content-Type: text/html; charset=UTF-8');
        return wp_mail($user->user_email, $subject, $message, $headers);
    }
    
    /**
     * Handle automatic phone service provisioning if recommended
     */
    private function handle_phone_service_provisioning($package, $responses) {
        $phone_recommendation = $package['phone_service_recommendation'] ?? array();
        
        // ALWAYS recommend phone service for ALL businesses
        $should_provision = true;
        
        // Override any previous recommendation - phone service is always beneficial
        
        // Validate Twilio configuration
        $twilio_configured = $this->validate_twilio_configuration();
        if (!$twilio_configured) {
            return array(
                'provisioned' => false,
                'reason' => 'Twilio API not configured. Please configure Twilio settings in VMO Admin.',
                'recommended' => true,
                'manual_setup_required' => true
            );
        }
        
        try {
            // Initialize Twilio client for auto-provisioning
            $twilio_client = new VMO_Twilio_Enhanced_Client();
            
            // Get user ID and calculate token cost
            $user_id = get_current_user_id();
            $existing_numbers = $twilio_client->get_all_active_phone_numbers($user_id);
            if (!empty($existing_numbers)) {
                $existing_phone = (string) $existing_numbers[0];
                $existing_payload = array(
                    'provisioned' => true,
                    'already_provisioned' => true,
                    'phone_number' => $existing_phone,
                    'tokens_charged' => 0,
                    'service_details' => $this->get_phone_service_details(),
                    'activation_date' => (string) get_user_meta($user_id, 'vmo_phone_activation_date', true),
                    'reason' => 'Existing business phone number already active.'
                );
                update_user_meta($user_id, 'vmo_phone_service', $existing_payload);
                return $existing_payload;
            }

            $phone_activation_cost = $this->calculate_phone_activation_cost();
            
            // Check if user has enough tokens
            $current_balance = (int) get_user_meta($user_id, 'vmo_token_balance', true);
            if ($phone_activation_cost > 0 && $current_balance < $phone_activation_cost) {
                return array(
                    'provisioned' => false,
                    'reason' => 'Insufficient tokens for phone service activation',
                    'recommended' => true,
                    'tokens_required' => $phone_activation_cost,
                    'current_balance' => $current_balance
                );
            }
            
            // Attempt to provision phone number
            $phone_result = $twilio_client->provision_business_phone_number($user_id, $responses);
            
            if (!empty($phone_result['success'])) {
                // Deduct tokens
                if ($phone_activation_cost > 0 && empty($phone_result['already_provisioned'])) {
                    $new_balance = max(0, $current_balance - $phone_activation_cost);
                    update_user_meta($user_id, 'vmo_token_balance', $new_balance);

                    // Log token usage
                    $this->log_token_usage($user_id, $phone_activation_cost, 'phone_service_activation');
                }

                // Send notification email only when a new phone is provisioned.
                if (empty($phone_result['already_provisioned']) && !empty($phone_result['phone_number'])) {
                    $this->send_phone_activation_email($user_id, $phone_result['phone_number']);
                }

                $provisioned_payload = array(
                    'provisioned' => true,
                    'already_provisioned' => !empty($phone_result['already_provisioned']),
                    'phone_number' => (string) ($phone_result['phone_number'] ?? ''),
                    'tokens_charged' => (!empty($phone_result['already_provisioned']) || $phone_activation_cost <= 0) ? 0 : $phone_activation_cost,
                    'service_details' => $this->get_phone_service_details(),
                    'activation_date' => current_time('mysql'),
                    'reason' => (string) ($phone_result['message'] ?? 'Phone provisioning completed successfully.'),
                    'status' => (string) ($phone_result['status'] ?? 'active')
                );
                update_user_meta($user_id, 'vmo_phone_service', $provisioned_payload);
                return $provisioned_payload;
            } else {
                return array(
                    'provisioned' => false,
                    'reason' => 'Phone provisioning failed: ' . (string) ($phone_result['error'] ?? 'Unknown error'),
                    'recommended' => true
                );
            }
            
        } catch (\Throwable $e) {
            error_log('VMO Phone Provisioning Error: ' . $e->getMessage());
            return array(
                'provisioned' => false,
                'reason' => 'Phone provisioning system error',
                'recommended' => true,
                'error' => $e->getMessage()
            );
        }
    }
    
    /**
     * Validate Twilio configuration for auto-provisioning
     */
    private function validate_twilio_configuration() {
        $required_settings = array(
            'vmo_twilio_account_sid',
            'vmo_twilio_api_key_sid',
            'vmo_twilio_api_key_secret',
            'vmo_twilio_voice_webhook_url'
        );
        
        foreach ($required_settings as $setting) {
            if (empty(get_option($setting))) {
                return false;
            }
        }
        
        return true;
    }
    
    /**
     * Calculate phone activation cost in tokens
     */
    private function calculate_phone_activation_cost() {
        if (defined('VMO_PHONE_ACTIVATION_FREE') && VMO_PHONE_ACTIVATION_FREE) {
            return 0;
        }

        if (class_exists('VMO_Twilio_Token_Economics') && method_exists('VMO_Twilio_Token_Economics', 'PHONE_ACTIVATION')) {
            return (int) VMO_Twilio_Token_Economics::PHONE_ACTIVATION();
        }

        if (defined('VMO_TOKENS_PHONE_ACTIVATION')) {
            return (int) VMO_TOKENS_PHONE_ACTIVATION;
        }

        return 3750;
    }
    
    /**
     * Get phone service benefits and details
     */
    private function get_phone_service_details() {
        return array(
            'features' => array(
                '24/7 AI Human-like Receptionist',
                'Professional Business Phone Number',
                'Automatic Website Integration',
                'Customer Call Recording & Transcription',
                'Customer Database Building',
                'Lead Generation & Qualification',
                'Call Transfer to Owner Phone',
                'Voicemail to Email',
                'Business Hours Management',
                'Call Analytics & Insights',
                'Integration with Business Website'
            ),
            'benefits' => array(
                'Never Miss a Customer Call',
                'Professional Business Presence',
                'Automated Customer Support',
                'Generate Leads While You Sleep',
                'Reduce Administrative Workload',
                'Improve Customer Satisfaction',
                'Track Customer Interactions',
                'Scale Customer Service Instantly'
            ),
            'cost_structure' => array(
                'activation_fee' => '30,000 tokens (one-time)',
                'monthly_fee' => '9,000 tokens (as long as you have tokens)',
                'no_contracts' => 'Cancel anytime by letting tokens run out',
                'trial_period' => '3 days to test the service',
                'auto_cancellation' => 'Number released if unused for 3 days'
            )
        );
    }
    
    /**
     * Send phone activation notification email
     */
    private function send_phone_activation_email($user_id, $phone_number) {
        $user = get_userdata($user_id);
        if (!$user) return false;
        
        $subject = "🎉 Your Business Phone Number is Ready: " . $phone_number;
        
        ob_start();
        ?>
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Your VMO Business Phone is Active</title>
        </head>
        <body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #1a1a2e; margin: 0; padding: 0; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
            <div style="max-width: 600px; margin: 20px auto; background: white; border-radius: 16px; overflow: hidden; box-shadow: 0 20px 60px rgba(0,0,0,0.1);">
                
                <header style="background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%); color: white; padding: 40px 30px; text-align: center;">
                    <h1 style="margin: 0; font-size: 2.2em; font-weight: 700;">📞 Your Business Phone is Live!</h1>
                    <p style="margin: 15px 0 0 0; font-size: 1.1em; opacity: 0.9;">Professional AI receptionist activated</p>
                </header>
                
                <main style="padding: 40px 30px;">
                    <div style="text-align: center; margin-bottom: 30px;">
                        <div style="background: linear-gradient(135deg, #00ffff 0%, #0099cc 100%); color: #1a1a2e; padding: 20px; border-radius: 12px; font-size: 1.8em; font-weight: bold; margin-bottom: 20px;">
                            <?php echo esc_html($phone_number); ?>
                        </div>
                        <p style="font-size: 1.1em; color: #333; margin: 0;">Your new business phone number is ready to receive calls!</p>
                    </div>
                    
                    <div style="background: #f8f9ff; padding: 25px; border-radius: 12px; margin: 30px 0;">
                        <h3 style="color: #1a1a2e; margin: 0 0 15px 0;">✨ What's Working Right Now:</h3>
                        <ul style="list-style: none; padding: 0; margin: 0;">
                            <li style="margin: 8px 0; padding: 0 0 0 20px; position: relative;">
                                <span style="position: absolute; left: 0;">🤖</span>
                                <strong>AI Receptionist:</strong> Answers calls 24/7 with human-like conversation
                            </li>
                            <li style="margin: 8px 0; padding: 0 0 0 20px; position: relative;">
                                <span style="position: absolute; left: 0;">🌐</span>
                                <strong>Website Integration:</strong> Phone number automatically added to your website with click-to-call buttons
                            </li>
                            <li style="margin: 8px 0; padding: 0 0 0 20px; position: relative;">
                                <span style="position: absolute; left: 0;">📞</span>
                                <strong>Call Transfer:</strong> AI can transfer important calls to your phone
                            </li>
                            <li style="margin: 8px 0; padding: 0 0 0 20px; position: relative;">
                                <span style="position: absolute; left: 0;">📝</span>
                                <strong>Call Recording:</strong> All conversations recorded and transcribed
                            </li>
                            <li style="margin: 8px 0; padding: 0 0 0 20px; position: relative;">
                                <span style="position: absolute; left: 0;">📊</span>
                                <strong>Customer Database:</strong> Building customer profiles automatically
                            </li>
                        </ul>
                    </div>
                    
                    <div style="background: #e8f5e8; border: 1px solid #4caf50; border-radius: 12px; padding: 20px; margin: 25px 0;">
                        <h3 style="color: #2e7d32; margin: 0 0 15px 0;">🌐 Automatic Website Integration</h3>
                        <p style="margin: 0 0 15px 0; font-weight: 600;">Your phone number has been automatically integrated into your business website:</p>
                        <ul style="list-style: none; padding: 0; margin: 0;">
                            <li style="margin: 8px 0; padding: 0 0 0 20px; position: relative;">
                                <span style="position: absolute; left: 0;">📱</span>
                                <strong>Click-to-Call:</strong> All phone numbers are clickable for mobile users
                            </li>
                            <li style="margin: 8px 0; padding: 0 0 0 20px; position: relative;">
                                <span style="position: absolute; left: 0;">🎯</span>
                                <strong>Strategic Placement:</strong> Header, footer, contact page, and service sections
                            </li>
                            <li style="margin: 8px 0; padding: 0 0 0 20px; position: relative;">
                                <span style="position: absolute; left: 0;">🔘</span>
                                <strong>Call Now Buttons:</strong> Prominent conversion-focused call buttons
                            </li>
                            <li style="margin: 8px 0; padding: 0 0 0 20px; position: relative;">
                                <span style="position: absolute; left: 0;">📞</span>
                                <strong>Professional Display:</strong> Formatted as <?php echo esc_html( $this->format_phone_for_display($phone_number) ); ?>
                            </li>
                        </ul>
                    </div>
                    
                    <div style="background: #fff8e1; border-left: 4px solid #ffb300; padding: 20px; margin: 25px 0;">
                        <h4 style="margin: 0 0 10px 0; color: #e65100;">💰 Token Usage & Billing</h4>
                        <p style="margin: 0 0 10px 0;">• <strong>Activation Fee:</strong> 30,000 tokens (charged once)</p>
                        <p style="margin: 0 0 10px 0;">• <strong>Monthly Usage:</strong> ~9,000 tokens (deducted automatically)</p>
                        <p style="margin: 0 0 10px 0;">• <strong>Plan Required:</strong> Active subscription needed for phone features</p>
                        <p style="margin: 0;">• <strong>Auto-Release:</strong> Number released if unused for 3 days</p>
                    </div>
                    
                    <div style="text-align: center; margin: 35px 0;">
                        <a href="<?php echo admin_url('admin.php?page=vmo-ai-dashboard#phone'); ?>" 
                           style="background: linear-gradient(135deg, #00ffff 0%, #0099cc 100%); color: #1a1a2e; padding: 15px 30px; text-decoration: none; border-radius: 8px; font-weight: bold; display: inline-block; margin: 10px;">
                            📊 View Call Dashboard
                        </a>
                        <a href="<?php echo admin_url('admin.php?page=vmo-ai-builder-main'); ?>" 
                           style="background: #1a1a2e; color: white; padding: 15px 30px; text-decoration: none; border-radius: 8px; font-weight: bold; display: inline-block; margin: 10px;">
                            🌐 View Your Website
                        </a>
                    </div>
                    
                    <div style="background: #e8f5e8; border: 1px solid #4caf50; border-radius: 8px; padding: 20px; margin: 25px 0;">
                        <h4 style="margin: 0 0 10px 0; color: #2e7d32;">🎯 Quick Start Guide</h4>
                        <ol style="margin: 0; padding-left: 20px;">
                            <li>Test your number by calling it right now</li>
                            <li>Visit the Call Dashboard to see live activity</li>
                            <li>Add your personal phone number for call transfers</li>
                            <li>Review AI responses and customize if needed</li>
                            <li>Share your new business number with customers</li>
                        </ol>
                    </div>
                    
                </main>
                
                <footer style="background: #f0f8ff; padding: 20px; text-align: center;">
                    <p style="margin: 0; color: #1a1a2e; font-size: 0.9em;">
                        <strong>Need Help?</strong> Visit your VMO Admin panel or contact support
                    </p>
                </footer>
            </div>
        </body>
        </html>
        <?php
        
        $message = ob_get_clean();
        $headers = array('Content-Type: text/html; charset=UTF-8');
        
        return wp_mail($user->user_email, $subject, $message, $headers);
    }
    
    /**
     * Send website publication notification email
     * ✅ NEW: Branded email when user publishes their website
     */
    private function send_website_published_email($user_id, $website_data) {
        $user = get_userdata($user_id);
        if (!$user) return false;
        
        $subject = "🚀 Your Website is Now Live! - " . $website_data['website_name'];
        $logo_url = plugins_url('assets/vmo-icon.svg', dirname(__FILE__));
        $dashboard_url = admin_url('admin.php?page=vmo-ai-builder');
        
        ob_start();
        ?>
        <!DOCTYPE html>
        <html>
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>Your Website is Live</title>
        </head>
        <body style="font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #e6f3ff; margin: 0; padding: 0; background: #07121a;">
            <div style="max-width: 600px; margin: 0 auto; background: #0a0f16; border-radius: 16px; overflow: hidden; box-shadow: 0 20px 60px rgba(15,225,255,0.2);">
                
                <!-- Header with Logo -->
                <header style="background: linear-gradient(135deg, #0fe1ff 0%, #7c5cff 100%); padding: 40px 30px; text-align: center; position: relative;">
                    <img src="<?php echo esc_url($logo_url); ?>" alt="VMO" style="width: 60px; height: 60px; margin-bottom: 15px;" />
                    <h1 style="margin: 0; font-size: 2.2em; font-weight: 700; color: #0a0f16; text-shadow: 0 2px 10px rgba(255,255,255,0.3);">🚀 Your Website is Live!</h1>
                    <p style="margin: 15px 0 0 0; font-size: 1.1em; color: rgba(10,15,22,0.9);">Congratulations! Your website is now published on the internet</p>
                </header>
                
                <main style="padding: 40px 30px;">
                    <!-- Live URL Display -->
                    <div style="text-align: center; margin-bottom: 30px;">
                        <div style="background: linear-gradient(135deg, rgba(15,225,255,0.15), rgba(124,92,255,0.15)); padding: 25px; border-radius: 12px; border: 1px solid rgba(15,225,255,0.3); margin-bottom: 20px;">
                            <div style="font-size: 0.9em; color: #65f1ff; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 10px;">🌐 Your Website</div>
                            <a href="<?php echo esc_url($website_data['live_url']); ?>" style="color: #0fe1ff; text-decoration: none; font-size: 1.4em; font-weight: bold; display: block; word-break: break-all;">
                                <?php echo esc_html($website_data['website_name']); ?>
                            </a>
                            <p style="margin: 12px 0 0 0; color: #9fb2c8; font-size: 0.95em; word-break: break-all;">
                                <?php echo esc_html($website_data['live_url']); ?>
                            </p>
                        </div>
                        <a href="<?php echo esc_url($website_data['live_url']); ?>" style="display: inline-block; background: linear-gradient(135deg, #0fe1ff 0%, #7c5cff 100%); color: #0a0f16; padding: 15px 40px; text-decoration: none; border-radius: 8px; font-weight: bold; font-size: 1.1em; box-shadow: 0 4px 15px rgba(15,225,255,0.4);">
                            View Your Live Site →
                        </a>
                    </div>
                    
                    <!-- Published Pages -->
                    <div style="background: rgba(15,225,255,0.08); padding: 25px; border-radius: 12px; margin: 30px 0; border-left: 4px solid #0fe1ff;">
                        <h3 style="color: #0fe1ff; margin: 0 0 15px 0; font-size: 1.2em;">📋 Published Pages:</h3>
                        <ul style="list-style: none; padding: 0; margin: 0;">
                            <li style="margin: 10px 0; color: #e6f3ff; padding-left: 25px; position: relative;">
                                <span style="position: absolute; left: 0; color: #0fe1ff;">✅</span>
                                <strong>Home Page</strong> - Your website's front door
                            </li>
                            <li style="margin: 10px 0; color: #e6f3ff; padding-left: 25px; position: relative;">
                                <span style="position: absolute; left: 0; color: #0fe1ff;">✅</span>
                                <strong>About Page</strong> - Your business story
                            </li>
                            <li style="margin: 10px 0; color: #e6f3ff; padding-left: 25px; position: relative;">
                                <span style="position: absolute; left: 0; color: #0fe1ff;">✅</span>
                                <strong>Services/Products</strong> - What you offer
                            </li>
                            <li style="margin: 10px 0; color: #e6f3ff; padding-left: 25px; position: relative;">
                                <span style="position: absolute; left: 0; color: #0fe1ff;">✅</span>
                                <strong>Contact Page</strong> - How to reach you
                            </li>
                        </ul>
                    </div>
                    
                    <!-- Next Steps -->
                    <div style="background: rgba(124,92,255,0.08); padding: 25px; border-radius: 12px; margin: 30px 0; border-left: 4px solid #7c5cff;">
                        <h3 style="color: #7c5cff; margin: 0 0 15px 0; font-size: 1.2em;">🎯 Next Steps to Maximize Your Success:</h3>
                        <ol style="margin: 0; padding-left: 20px; color: #e6f3ff;">
                            <li style="margin: 12px 0;"><strong style="color: #65f1ff;">Share Your Website:</strong> Post on social media, add to email signatures</li>
                            <li style="margin: 12px 0;"><strong style="color: #65f1ff;">Submit to Search Engines:</strong> Google Search Console, Bing Webmaster Tools</li>
                            <li style="margin: 12px 0;"><strong style="color: #65f1ff;">Set Up Analytics:</strong> Track visitors and understand your audience</li>
                            <li style="margin: 12px 0;"><strong style="color: #65f1ff;">Test Everything:</strong> Forms, phone numbers, payment buttons</li>
                            <li style="margin: 12px 0;"><strong style="color: #65f1ff;">Monitor Performance:</strong> Use your Strategic Dashboard for insights</li>
                        </ol>
                    </div>
                    
                    <!-- Dashboard Button -->
                    <div style="text-align: center; margin: 40px 0;">
                        <a href="<?php echo esc_url($dashboard_url); ?>" style="display: inline-block; background: linear-gradient(135deg, rgba(15,225,255,0.2), rgba(124,92,255,0.2)); color: #0fe1ff; padding: 15px 40px; text-decoration: none; border-radius: 8px; font-weight: bold; font-size: 1.1em; border: 1px solid rgba(15,225,255,0.4);">
                            🎛️ Go to Dashboard
                        </a>
                    </div>
                    
                    <!-- Pro Tips -->
                    <div style="background: rgba(15,225,255,0.05); padding: 20px; border-radius: 12px; margin: 30px 0; border: 1px solid rgba(15,225,255,0.2);">
                        <h4 style="margin: 0 0 12px 0; color: #0fe1ff; font-size: 1.1em;">💡 Pro Tips:</h4>
                        <ul style="margin: 0; padding-left: 20px; font-size: 0.95em; color: #9fb2c8;">
                            <li style="margin: 8px 0;">Keep content fresh - update regularly for better SEO</li>
                            <li style="margin: 8px 0;">Respond quickly to contact form submissions</li>
                            <li style="margin: 8px 0;">Monitor your phone AI for customer interactions</li>
                            <li style="margin: 8px 0;">Use the editor to make quick changes anytime</li>
                        </ul>
                    </div>
                    
                    <!-- Support -->
                    <p style="color: #646e7d; text-align: center; margin: 30px 0 0 0; font-size: 0.9em;">
                        <?php $support_email = get_option('vmo_support_email', get_option('admin_email')); ?>
                        Need help? Contact <a href="mailto:<?php echo esc_attr($support_email); ?>" style="color: #0fe1ff; text-decoration: none;"><?php echo esc_html($support_email); ?></a>
                    </p>
                </main>
                
                <!-- Footer -->
                <footer style="background: rgba(0,0,0,0.3); padding: 25px 30px; text-align: center; border-top: 1px solid rgba(15,225,255,0.1);">
                    <p style="margin: 0 0 8px 0; color: #646e7d; font-size: 0.9em;">Published on <?php echo date('F j, Y \a\t g:i A'); ?></p>
                    <p style="margin: 0; color: #9fb2c8; font-size: 0.85em;">
                        <strong style="color: #0fe1ff;">VMO AI Builder</strong> - Powered by Virtual Media Office
                    </p>
                </footer>
                
            </div>
        </body>
        </html>
        <?php
        
        $message = ob_get_clean();
        $headers = array('Content-Type: text/html; charset=UTF-8');
        
        return wp_mail($user->user_email, $subject, $message, $headers);
    }
    
    /**
     * Log token usage for tracking
     */
    private function log_token_usage($user_id, $tokens_used, $action) {
        // Add to existing token usage tracking system
        $usage_log = get_user_meta($user_id, 'vmo_token_usage_log', true);
        if (!is_array($usage_log)) {
            $usage_log = array();
        }
        
        $usage_log[] = array(
            'timestamp' => current_time('mysql'),
            'action' => $action,
            'tokens_used' => $tokens_used,
            'source' => 'enhanced_business_wizard'
        );
        
        // Keep only last 100 entries
        if (count($usage_log) > 100) {
            $usage_log = array_slice($usage_log, -100);
        }
        
        update_user_meta($user_id, 'vmo_token_usage_log', $usage_log);
    }
    
    // VOICE SYSTEM REMOVED: generate_voice_response and ajax_process_voice_message_enhanced methods removed for No Voice version

    // ============== AI-DRIVEN CONTACT MANAGEMENT SYSTEM ==============

    /**
     * Initialize comprehensive contact management system for user
     */
    public function initialize_contact_management_system($user_id, $business_type, $business_context) {
        try {
            // Generate AI-driven contact categories based on business type
            $contact_categories = $this->generate_business_specific_contact_categories($business_type, $business_context);
            
            // Set up contact management configuration
            $contact_config = [
                'user_id' => $user_id,
                'business_type' => $business_type,
                'business_context' => $business_context,
                'contact_categories' => $contact_categories,
                'auto_categorization' => true,
                'ai_learning_enabled' => true,
                'data_sources' => ['leads', 'phone_calls', 'manual_entry', 'website_forms', 'social_media'],
                'created_at' => current_time('mysql'),
                'last_updated' => current_time('mysql')
            ];

            if (function_exists('vmo_crm_initialize_from_consultation')) {
                $crm_blueprint_state = vmo_crm_initialize_from_consultation((int) $user_id, '', is_array($business_context) ? $business_context : array(), array(), array());
                $contact_config['crm_blueprint'] = $crm_blueprint_state;
                $contact_config['crm_blueprint_id'] = is_array($crm_blueprint_state) ? ($crm_blueprint_state['blueprint_id'] ?? '') : '';
            }
            
            update_option('vmo_contact_management_' . $user_id, $contact_config);
            
            // Create contact database tables if needed
            $this->create_contact_database_tables($user_id);
            
            // Set up automatic contact sync from all sources
            $this->setup_contact_sync_automation($user_id);
            
            error_log("VMO Contact Management: System initialized for user {$user_id} with business type: {$business_type}");
            
            return $contact_config;
            
        } catch (\Throwable $e) {
            error_log('VMO Contact Management Error: ' . $e->getMessage());
            return false;
        }
    }

    /**
     * Generate AI-driven contact categories based on specific business type
     */
    private function generate_business_specific_contact_categories($business_type, $business_context) {
        $categorization_prompt = "Create intelligent contact categories for this specific business type:

BUSINESS TYPE: {$business_type}
BUSINESS CONTEXT: " . json_encode($business_context, JSON_PRETTY_PRINT) . "

Generate 6-10 contact categories that are SPECIFIC to this business type, not generic categories. Consider:

1. **BUSINESS-SPECIFIC STAKEHOLDERS**: Who does this business type typically interact with?
2. **CUSTOMER LIFECYCLE STAGES**: Different stages customers go through in this industry
3. **RELATIONSHIP TYPES**: Different types of relationships this business has
4. **TRANSACTION STAGES**: Pre-purchase, during service, post-service, repeat customers
5. **INDUSTRY-SPECIFIC ROLES**: Unique roles or types specific to this industry

For example:
- Medical Practice: Patients, Referring Doctors, Insurance Providers, Specialists, Vendors, Emergency Contacts
- Restaurant: Diners, Food Suppliers, Delivery Partners, Catering Clients, Event Organizers, Staff
- Law Firm: Active Clients, Potential Clients, Referral Partners, Expert Witnesses, Court Personnel
- Real Estate: Home Buyers, Home Sellers, Investors, Contractors, Lenders, Inspectors
- E-commerce: Active Customers, Prospects, Wholesale Buyers, Suppliers, Affiliates, Returns

For each category provide:
1. **Category Name**: Specific to the business type
2. **Description**: What type of contacts belong here
3. **Auto-categorization Rules**: How to automatically identify contacts for this category
4. **Engagement Strategy**: How to best communicate with this category
5. **Lifecycle Actions**: What actions to take at different stages
6. **Priority Level**: High, Medium, or Low priority for business growth

Format as detailed JSON with category specifications.";

        $categories_response = $this->call_openai_deep_research($categorization_prompt, 'market_research', [
            'focus' => 'contact_categorization',
            'business_type' => $business_type,
            'business_context' => $business_context
        ]);

        // Parse and validate categories
        $categories = $this->parse_and_validate_contact_categories($categories_response);
        
        return $categories;
    }

    /**
     * Automatically categorize a contact based on AI analysis
     */
    public function auto_categorize_contact($contact_data, $user_id, $source = 'unknown') {
        $contact_config = get_option('vmo_contact_management_' . $user_id);
        if (!$contact_config) return 'uncategorized';
        
        $business_type = $contact_config['business_type'];
        $categories = $contact_config['contact_categories'];
        
        $categorization_prompt = "Analyze this contact and determine the most appropriate category:

CONTACT DATA:
" . json_encode($contact_data, JSON_PRETTY_PRINT) . "

CONTACT SOURCE: {$source}
BUSINESS TYPE: {$business_type}

AVAILABLE CATEGORIES:
" . json_encode($categories, JSON_PRETTY_PRINT) . "

Based on the contact information, source, and business context, determine:
1. **Primary Category**: The most appropriate category for this contact
2. **Confidence Score**: How confident you are in this categorization (0-100)
3. **Reasoning**: Why this category was chosen
4. **Secondary Categories**: Alternative categories if applicable
5. **Engagement Recommendations**: How to best engage with this contact
6. **Lifecycle Stage**: Where they are in the customer journey

Provide specific analysis based on the available data and business context.";

        $categorization = $this->call_openai_deep_research($categorization_prompt, 'market_research', [
            'focus' => 'contact_analysis',
            'contact_data' => $contact_data,
            'business_context' => $contact_config
        ]);
        
        return $this->parse_categorization_result($categorization);
    }

    private function require_crm_ajax_security() {
        if (function_exists('vmo_require_crm_ajax_security')) {
            vmo_require_crm_ajax_security();
            return;
        }

        check_ajax_referer('vmo_nonce', 'nonce');
    }

    private function normalize_legacy_crm_source($source) {
        $source = sanitize_key((string) $source);
        $source_map = [
            'manual' => 'manual_entry',
            'csv_import' => 'manual_entry',
            'lead' => 'lead_generation',
            'lead_sync' => 'lead_generation',
            'phone' => 'phone_call',
            'call' => 'phone_call',
            'email' => 'email_marketing',
            'website' => 'website_form',
        ];
        $source = $source_map[$source] ?? $source;

        return function_exists('vmo_crm_valid_source') ? vmo_crm_valid_source($source) : $source;
    }

    private function legacy_contact_identity(array $contact_data) {
        $name = $contact_data['full_name'] ?? $contact_data['name'] ?? $contact_data['caller_name'] ?? $contact_data['client_name'] ?? '';
        $phone = $contact_data['phone'] ?? $contact_data['caller_number'] ?? $contact_data['from'] ?? '';

        return [
            'full_name' => sanitize_text_field((string) $name),
            'email' => sanitize_email((string) ($contact_data['email'] ?? $contact_data['client_email'] ?? '')),
            'phone' => sanitize_text_field((string) $phone),
            'company' => sanitize_text_field((string) ($contact_data['company'] ?? $contact_data['organization'] ?? '')),
        ];
    }

    private function contact_row_to_legacy_record(array $contact, array $contact_data = [], array $categorization = []) {
        $primary_category = (string) ($contact['primary_category'] ?? ($categorization['primary_category'] ?? 'lead'));

        return [
            'user_id' => (int) ($contact['user_id'] ?? 0),
            'contact_id' => (string) ($contact['contact_id'] ?? ''),
            'source' => (string) ($contact['source'] ?? 'manual_entry'),
            'primary_category' => $primary_category,
            'confidence_score' => (int) ($contact['confidence_score'] ?? ($categorization['confidence_score'] ?? 50)),
            'contact_data' => $contact_data,
            'categorization_analysis' => $categorization,
            'engagement_strategy' => $categorization['engagement_recommendations'] ?? [],
            'lifecycle_stage' => (string) ($contact['lifecycle_stage'] ?? 'new'),
            'priority_level' => (string) ($contact['priority_level'] ?? 'medium'),
            'tags' => array_values(array_filter(array_map('trim', explode(',', (string) ($contact['tags'] ?? ''))))),
            'created_at' => (string) ($contact['created_at'] ?? current_time('mysql')),
            'last_updated' => (string) ($contact['updated_at'] ?? current_time('mysql')),
            'last_contact' => $contact['last_contact'] ?? null,
            'interaction_history' => [],
            'central_crm' => true,
            'central_contact' => $contact,
        ];
    }

    private function get_central_contact_record($user_id, $contact_id) {
        if (!function_exists('vmo_crm_find_contact')) {
            return false;
        }

        $contact = is_numeric($contact_id)
            ? vmo_crm_find_contact((int) $user_id, ['id' => absint($contact_id)])
            : vmo_crm_find_contact((int) $user_id, ['contact_id' => sanitize_text_field((string) $contact_id)]);

        if (!$contact) {
            return false;
        }

        $contact_data = [
            'name' => (string) ($contact['full_name'] ?? ''),
            'email' => (string) ($contact['email'] ?? ''),
            'phone' => (string) ($contact['phone'] ?? ''),
            'company' => (string) ($contact['company'] ?? ''),
            'job_title' => (string) ($contact['job_title'] ?? ''),
        ];

        return $this->contact_row_to_legacy_record($contact, $contact_data, []);
    }

    /**
     * Add contact to unified database from any source.
     */
    public function add_contact_to_database($contact_data, $user_id, $source = 'manual') {
        try {
            if (!function_exists('vmo_crm_upsert_contact')) {
                error_log('VMO Contact Management: central CRM helper unavailable; contact was not saved to legacy options.');
                return false;
            }

            $contact_data = is_array($contact_data) ? $contact_data : [];
            $categorization = $this->auto_categorize_contact($contact_data, $user_id, $source);
            if (!is_array($categorization)) {
                $categorization = [];
            }

            $enriched_contact = $this->enrich_contact_data($contact_data, $categorization);
            $identity = $this->legacy_contact_identity($enriched_contact);
            if ($identity['full_name'] === '' && $identity['email'] === '' && $identity['phone'] === '' && $identity['company'] === '') {
                return false;
            }

            $legacy_source = sanitize_key((string) $source);
            $central_source = $this->normalize_legacy_crm_source($legacy_source);
            $requested_stage = sanitize_key((string) ($enriched_contact['lifecycle_stage'] ?? $enriched_contact['type'] ?? $categorization['lifecycle_stage'] ?? 'new'));
            $owner_confirmed_customer = in_array($legacy_source, ['manual', 'manual_entry', 'csv_import'], true) && $requested_stage === 'customer';
            $lifecycle_stage = $owner_confirmed_customer ? 'customer' : (function_exists('vmo_crm_valid_lifecycle_stage') ? vmo_crm_valid_lifecycle_stage($requested_stage, 'new') : 'new');
            if ($lifecycle_stage === 'customer' && !$owner_confirmed_customer) {
                $lifecycle_stage = 'new';
            }

            $primary_category = sanitize_key((string) ($enriched_contact['primary_category'] ?? $categorization['primary_category'] ?? ($owner_confirmed_customer ? 'customer' : 'lead')));
            if (!$owner_confirmed_customer && in_array($primary_category, ['customer', 'active_customers', 'past_customers'], true)) {
                $primary_category = 'lead';
            }
            if ($primary_category === '') {
                $primary_category = $owner_confirmed_customer ? 'customer' : 'lead';
            }

            $contact = vmo_crm_upsert_contact((int) $user_id, $identity, [
                'source' => $central_source,
                'primary_category' => $primary_category,
                'lifecycle_stage' => $lifecycle_stage,
                'confidence_score' => (int) ($categorization['confidence_score'] ?? 50),
                'priority_level' => $this->determine_contact_priority($categorization),
                'lead_score' => (int) ($enriched_contact['lead_score'] ?? $categorization['lead_score'] ?? 50),
                'engagement_level' => $enriched_contact['engagement_level'] ?? 'medium',
                'value_potential' => (int) ($enriched_contact['value_potential'] ?? 50),
                'tags' => $this->generate_contact_tags($enriched_contact, $categorization),
                'notes' => sanitize_textarea_field((string) ($enriched_contact['notes'] ?? 'Legacy wizard contact routed into central CRM.')),
                'acquisition_details' => [
                    'source' => $legacy_source ?: 'manual',
                    'legacy_contact_sync' => true,
                    'source_details' => $enriched_contact['source_details'] ?? [],
                ],
                'categorization_analysis' => $categorization,
                'engagement_strategy' => $categorization['engagement_recommendations'] ?? [],
            ]);

            if (!$contact) {
                return false;
            }

            if ($owner_confirmed_customer && function_exists('vmo_crm_convert_to_customer')) {
                vmo_crm_convert_to_customer((int) $user_id, (string) $contact['contact_id'], [
                    'type' => 'manual',
                    'summary' => 'Legacy wizard contact was explicitly marked as customer by the owner.',
                ]);
            }

            if (function_exists('vmo_crm_log_interaction')) {
                $interaction_type = $central_source === 'phone_call' ? 'phone_call' : ($central_source === 'email_marketing' ? 'email' : 'manual_note');
                vmo_crm_log_interaction((int) $user_id, (string) $contact['contact_id'], [
                    'interaction_type' => $interaction_type,
                    'subtype' => 'legacy_' . ($legacy_source ?: 'manual') . '_sync',
                    'subject' => 'Legacy CRM path routed to central CRM',
                    'summary' => 'Contact was saved through a legacy wizard path and routed into the central CRM record.',
                    'outcome' => 'successful',
                    'metadata' => [
                        'legacy_source' => $legacy_source,
                        'central_source' => $central_source,
                    ],
                ]);
            }

            $contact_record = $this->contact_row_to_legacy_record($contact, $enriched_contact, $categorization);
            $this->trigger_contact_engagement_sequence((int) $user_id, $contact_record);
            $this->update_contact_analytics((int) $user_id, $contact_record);

            error_log("VMO Contact Management: Routed {$legacy_source} contact to central CRM for user {$user_id} - Category: " . $contact_record['primary_category']);

            return $contact_record;

        } catch (\Throwable $e) {
            error_log('VMO Contact Management Add Error: ' . $e->getMessage());
            return false;
        }
    }

    /**
     * Sync contacts from leads system
     */
    public function sync_leads_to_contact_database($user_id) {
        $leads = $this->get_user_leads($user_id);
        
        foreach ($leads as $lead) {
            $lead_contact_data = [
                'name' => $lead['name'] ?? 'Lead',
                'email' => $lead['email'] ?? '',
                'phone' => $lead['phone'] ?? '',
                'source_details' => $lead,
                'lead_score' => $lead['score'] ?? 0,
                'lead_segment' => $lead['segment'] ?? 'general'
            ];
            
            $this->add_contact_to_database($lead_contact_data, $user_id, 'lead_generation');
        }
    }

    /**
     * Sync contacts from phone system
     */
    public function sync_phone_contacts_to_database($user_id) {
        // Get phone calls from Twilio integration
        $phone_calls = $this->get_user_phone_calls($user_id);
        
        foreach ($phone_calls as $call) {
            $phone_contact_data = [
                'name' => $call['caller_name'] ?? 'Phone Caller',
                'phone' => $call['from'] ?? '',
                'call_duration' => $call['duration'] ?? 0,
                'call_purpose' => $call['purpose'] ?? 'inquiry',
                'call_outcome' => $call['outcome'] ?? 'pending',
                'source_details' => $call,
                'interaction_quality' => $call['quality_score'] ?? 50
            ];
            
            $this->add_contact_to_database($phone_contact_data, $user_id, 'phone_call');
        }
    }

    /**
     * Get categorized contacts for user
     */
    public function get_categorized_contacts($user_id, $category = null) {
        global $wpdb;

        $contacts_table = $wpdb->prefix . 'vmo_contacts';
        if (!function_exists('vmo_table_exists') || !vmo_table_exists($contacts_table)) {
            return [];
        }

        $where = 'user_id = %d';
        $params = [(int) $user_id];
        if ($category) {
            $where .= ' AND primary_category = %s';
            $params[] = sanitize_key((string) $category);
        } else {
            $where .= " AND primary_category <> 'website_visitor'";
        }

        $sql = $wpdb->prepare("SELECT * FROM {$contacts_table} WHERE {$where} ORDER BY updated_at DESC LIMIT 500", $params);
        $rows = $wpdb->get_results($sql, ARRAY_A) ?: [];
        $contacts_data = [];
        foreach ($rows as $row) {
            $record = $this->contact_row_to_legacy_record($row, [
                'name' => (string) ($row['full_name'] ?? ''),
                'email' => (string) ($row['email'] ?? ''),
                'phone' => (string) ($row['phone'] ?? ''),
                'company' => (string) ($row['company'] ?? ''),
            ], []);
            $contacts_data[(string) ($row['contact_id'] ?? $row['id'])] = $record;
        }

        return $contacts_data;
    }

    /**
     * Get contact analytics and insights
     */
    public function get_contact_analytics($user_id) {
        $contacts = $this->get_categorized_contacts($user_id);
        $contact_config = get_option('vmo_contact_management_' . $user_id);
        
        $analytics = [
            'total_contacts' => count($contacts),
            'categories_breakdown' => [],
            'source_breakdown' => [],
            'priority_breakdown' => [],
            'lifecycle_breakdown' => [],
            'engagement_stats' => [],
            'growth_trends' => [],
            'recommendations' => []
        ];
        
        // Calculate breakdowns
        foreach ($contacts as $contact) {
            // Category breakdown
            $category = $contact['primary_category'];
            $analytics['categories_breakdown'][$category] = ($analytics['categories_breakdown'][$category] ?? 0) + 1;
            
            // Source breakdown
            $source = $contact['source'];
            $analytics['source_breakdown'][$source] = ($analytics['source_breakdown'][$source] ?? 0) + 1;
            
            // Priority breakdown
            $priority = $contact['priority_level'];
            $analytics['priority_breakdown'][$priority] = ($analytics['priority_breakdown'][$priority] ?? 0) + 1;
            
            // Lifecycle breakdown
            $lifecycle = $contact['lifecycle_stage'];
            $analytics['lifecycle_breakdown'][$lifecycle] = ($analytics['lifecycle_breakdown'][$lifecycle] ?? 0) + 1;
        }
        
        // Generate AI insights
        $analytics['ai_insights'] = $this->generate_contact_insights($analytics, $contact_config);
        
        return $analytics;
    }

    /**
     * AJAX: Add manual contact
     */
    public function ajax_add_manual_contact() {
        $this->require_crm_ajax_security();
        $user_id = get_current_user_id();
        
        $contact_data = array(
            'name' => sanitize_text_field(wp_unslash($_POST['name'])),
            'email' => sanitize_email($_POST['email']),
            'phone' => sanitize_text_field(wp_unslash($_POST['phone'])),
            'company' => sanitize_text_field(wp_unslash($_POST['company'])),
            'address' => sanitize_textarea_field($_POST['address']),
            'lifecycle_stage' => sanitize_text_field(wp_unslash($_POST['type'])), // lead vs customer
            'tags' => array_map('sanitize_text_field', $_POST['tags'] ?? []),
            'manual_entry' => true
        );

        if (function_exists('vmo_crm_upsert_contact')) {
            $contact_type = sanitize_key($contact_data['lifecycle_stage'] ?: 'lead');
            $contact = vmo_crm_upsert_contact((int) $user_id, array(
                'full_name' => $contact_data['name'],
                'email' => $contact_data['email'],
                'phone' => $contact_data['phone'],
                'company' => $contact_data['company'],
            ), array(
                'source' => 'manual_entry',
                'primary_category' => $contact_type === 'customer' ? 'customer' : 'lead',
                'lifecycle_stage' => $contact_type === 'customer' ? 'customer' : 'new',
                'address' => $contact_data['address'],
                'tags' => $contact_data['tags'],
                'notes' => 'Legacy manual contact endpoint routed into central CRM.',
            ));
            if ($contact_type === 'customer' && !empty($contact['contact_id']) && function_exists('vmo_crm_convert_to_customer')) {
                vmo_crm_convert_to_customer((int) $user_id, (string) $contact['contact_id'], array(
                    'type' => 'manual',
                    'summary' => 'Legacy manual contact endpoint marked as customer with owner confirmation.',
                ));
            }
            if ($contact) {
                wp_send_json_success($contact);
            }
            wp_send_json_error('Failed to add contact');
        }
        
        // Pass standard structure to existing add function
        $enriched_data = array(
            'name' => $contact_data['name'],
            'email' => $contact_data['email'],
            'phone' => $contact_data['phone'],
            'company' => $contact_data['company'],
            'address' => $contact_data['address'],
            'lifecycle_stage' => $contact_data['lifecycle_stage'],
            'tags' => $contact_data['tags']
        );
        
        $result = $this->add_contact_to_database($enriched_data, $user_id, 'manual_entry');
        
        if ($result) {
            wp_send_json_success($result);
        } else {
            wp_send_json_error('Failed to add contact');
        }
    }

    /**
     * AJAX: Import contacts CSV
     */
    public function ajax_import_contacts_csv() {
        $this->require_crm_ajax_security();
        $user_id = get_current_user_id();
        
        if (empty($_FILES['csv_file'])) {
            wp_send_json_error('No file uploaded');
        }
        
        $file = $_FILES['csv_file']['tmp_name'];
        $handle = fopen($file, "r");
        
        // Basic CSV parsing
        $headers = fgetcsv($handle); // Assume first row header
        $count = 0;
        
        while (($data = fgetcsv($handle)) !== FALSE) {
            // Map common columns (Name, Email, Phone, Company)
            // Ideally should be dynamic mapping UI, but for V1 we assume standard format or auto-detect
            $contact = [
                'name' => $data[0] ?? 'Unknown',
                'email' => $data[1] ?? '',
                'phone' => $data[2] ?? '',
                'company' => $data[3] ?? '',
                'lifecycle_stage' => 'new'
            ];

            if (function_exists('vmo_crm_upsert_contact')) {
                $saved = vmo_crm_upsert_contact((int) $user_id, array(
                    'full_name' => $contact['name'],
                    'email' => $contact['email'],
                    'phone' => $contact['phone'],
                    'company' => $contact['company'],
                ), array(
                    'source' => 'manual_entry',
                    'primary_category' => 'lead',
                    'lifecycle_stage' => 'new',
                    'acquisition_details' => array('source' => 'legacy_csv_import'),
                ));
                if ($saved) {
                    $count++;
                }
            } else {
                $this->add_contact_to_database($contact, $user_id, 'csv_import');
                $count++;
            }
        }
        fclose($handle);
        
        wp_send_json_success(["count" => $count]);
    }

    /**
     * Update contact based on new interaction
     */
    public function update_contact_interaction($user_id, $contact_id, $interaction_data) {
        if (!function_exists('vmo_crm_log_interaction')) {
            return false;
        }

        $contact = $this->get_central_contact_record((int) $user_id, $contact_id);
        $canonical_contact_id = $contact ? (string) $contact['contact_id'] : sanitize_text_field((string) $contact_id);

        return vmo_crm_log_interaction((int) $user_id, $canonical_contact_id, [
            'interaction_type' => $interaction_data['interaction_type'] ?? $interaction_data['type'] ?? 'manual_note',
            'subtype' => $interaction_data['subtype'] ?? 'legacy_contact_update',
            'subject' => $interaction_data['subject'] ?? 'Contact interaction',
            'summary' => $interaction_data['summary'] ?? $interaction_data['notes'] ?? 'Legacy wizard contact interaction routed into central CRM.',
            'full_details' => $interaction_data,
            'outcome' => $interaction_data['outcome'] ?? 'successful',
        ]);
    }

    // ============== CONTACT MANAGEMENT HELPER FUNCTIONS ==============

    private function parse_and_validate_contact_categories($categories_response) {
        if (is_string($categories_response)) {
            $parsed = json_decode($categories_response, true);
            if ($parsed) return $parsed;
        } elseif (is_array($categories_response)) {
            return $categories_response;
        }
        
        // Fallback default categories
        return [
            'prospects' => [
                'name' => 'Prospects',
                'description' => 'Potential customers showing interest',
                'priority' => 'high'
            ],
            'active_customers' => [
                'name' => 'Active Customers',
                'description' => 'Current paying customers',
                'priority' => 'high'
            ],
            'past_customers' => [
                'name' => 'Past Customers',
                'description' => 'Previous customers for re-engagement',
                'priority' => 'medium'
            ]
        ];
    }

    private function parse_categorization_result($categorization) {
        if (is_string($categorization)) {
            $parsed = json_decode($categorization, true);
            if ($parsed) return $parsed;
        } elseif (is_array($categorization)) {
            return $categorization;
        }
        
        return [
            'primary_category' => 'uncategorized',
            'confidence_score' => 0,
            'reasoning' => 'Unable to categorize contact'
        ];
    }

    private function enrich_contact_data($contact_data, $categorization) {
        // Add additional information based on categorization
        $enriched = $contact_data;
        $enriched['categorization_confidence'] = $categorization['confidence_score'] ?? 0;
        $enriched['engagement_level'] = $this->determine_engagement_level($contact_data);
        $enriched['value_potential'] = $this->calculate_contact_value_potential($contact_data, $categorization);
        
        return $enriched;
    }

    private function generate_contact_id($user_id) {
        return 'vmo_contact_' . $user_id . '_' . time() . '_' . wp_rand(1000, 9999);
    }

    private function determine_contact_priority($categorization) {
        $confidence = $categorization['confidence_score'] ?? 0;
        $category_priority = $categorization['priority_level'] ?? 'medium';
        
        if ($confidence > 80 && $category_priority === 'high') return 'high';
        if ($confidence > 60 && $category_priority !== 'low') return 'medium';
        return 'low';
    }

    private function generate_contact_tags($contact_data, $categorization) {
        $tags = [];
        
        // Add source-based tags
        if (isset($contact_data['source_details']['lead_score']) && $contact_data['source_details']['lead_score'] > 80) {
            $tags[] = 'high_value_lead';
        }
        
        // Add engagement tags
        if (isset($contact_data['call_duration']) && $contact_data['call_duration'] > 300) {
            $tags[] = 'engaged_caller';
        }
        
        // Add category-specific tags
        $category = $categorization['primary_category'] ?? 'general';
        $tags[] = 'category_' . $category;
        
        return $tags;
    }

    private function store_contact_in_database($user_id, $contact_record) {
        if (!function_exists('vmo_crm_upsert_contact') || !is_array($contact_record)) {
            return false;
        }

        $contact_data = is_array($contact_record['contact_data'] ?? null) ? $contact_record['contact_data'] : $contact_record;
        $identity = $this->legacy_contact_identity($contact_data);
        if ($identity['full_name'] === '' && $identity['email'] === '' && $identity['phone'] === '' && $identity['company'] === '') {
            return false;
        }

        return vmo_crm_upsert_contact((int) $user_id, $identity, [
            'contact_id' => sanitize_text_field((string) ($contact_record['contact_id'] ?? '')),
            'source' => $this->normalize_legacy_crm_source($contact_record['source'] ?? 'manual_entry'),
            'primary_category' => sanitize_key((string) ($contact_record['primary_category'] ?? 'lead')),
            'lifecycle_stage' => function_exists('vmo_crm_valid_lifecycle_stage') ? vmo_crm_valid_lifecycle_stage($contact_record['lifecycle_stage'] ?? 'new') : 'new',
            'tags' => $contact_record['tags'] ?? [],
            'acquisition_details' => ['legacy_store_contact_call' => true],
        ]);
    }

    private function trigger_contact_engagement_sequence($user_id, $contact_record) {
        $category = $contact_record['primary_category'];
        $engagement_strategy = $contact_record['engagement_strategy'];
        
        // Schedule appropriate engagement actions based on category
        if (is_array($engagement_strategy) && !empty($engagement_strategy)) {
            foreach ($engagement_strategy as $action) {
                wp_schedule_single_event(
                    strtotime('+1 hour'),
                    'vmo_contact_engagement_action',
                    [$user_id, $contact_record['contact_id'], $action]
                );
            }
        }
    }

    private function update_contact_analytics($user_id, $contact_record) {
        $analytics = get_option('vmo_contact_analytics_' . $user_id, [
            'total_contacts' => 0,
            'monthly_growth' => 0,
            'category_trends' => [],
            'source_performance' => []
        ]);
        
        $analytics['total_contacts']++;
        $analytics['category_trends'][$contact_record['primary_category']] = 
            ($analytics['category_trends'][$contact_record['primary_category']] ?? 0) + 1;
        $analytics['source_performance'][$contact_record['source']] = 
            ($analytics['source_performance'][$contact_record['source']] ?? 0) + 1;
        
        update_option('vmo_contact_analytics_' . $user_id, $analytics);
    }

    private function get_user_phone_calls($user_id) {
        // Get phone calls from database or Twilio integration
        global $wpdb;
        $calls_table = $wpdb->prefix . 'vmo_phone_calls';
        
        $calls = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM $calls_table WHERE user_id = %d ORDER BY created_at DESC LIMIT 100",
            $user_id
        ), ARRAY_A);
        
        return $calls ?: [];
    }

    private function determine_engagement_level($contact_data) {
        $score = 0;
        
        if (isset($contact_data['email']) && !empty($contact_data['email'])) $score += 20;
        if (isset($contact_data['phone']) && !empty($contact_data['phone'])) $score += 20;
        if (isset($contact_data['lead_score']) && $contact_data['lead_score'] > 70) $score += 30;
        if (isset($contact_data['call_duration']) && $contact_data['call_duration'] > 180) $score += 30;
        
        if ($score >= 70) return 'high';
        if ($score >= 40) return 'medium';
        return 'low';
    }

    private function calculate_contact_value_potential($contact_data, $categorization) {
        $base_score = 50;
        
        // Adjust based on category priority
        $category_priority = $categorization['priority_level'] ?? 'medium';
        if ($category_priority === 'high') $base_score += 30;
        elseif ($category_priority === 'low') $base_score -= 20;
        
        // Adjust based on engagement indicators
        if (isset($contact_data['lead_score'])) {
            $base_score += ($contact_data['lead_score'] - 50) * 0.5;
        }
        
        return min(max($base_score, 0), 100);
    }

    private function determine_lifecycle_stage($contact_record) {
        $interactions = $contact_record['interaction_history'] ?? [];
        $interaction_count = count($interactions);
        
        if ($interaction_count === 0) return 'new';
        if ($interaction_count <= 2) return 'engaged';
        if ($interaction_count <= 5) return 'nurturing';
        return 'active_relationship';
    }

    private function generate_contact_insights($analytics, $contact_config) {
        $insights_prompt = "Analyze this contact database and provide strategic insights:

CONTACT ANALYTICS:
" . json_encode($analytics, JSON_PRETTY_PRINT) . "

BUSINESS CONTEXT:
" . json_encode($contact_config, JSON_PRETTY_PRINT) . "

Provide insights on:
1. **Growth Opportunities**: Which contact categories to focus on for growth
2. **Engagement Optimization**: How to better engage with different categories
3. **Conversion Potential**: Which contacts have highest conversion potential
4. **Resource Allocation**: Where to focus marketing and sales efforts
5. **Category Performance**: Which categories are performing best/worst
6. **Actionable Recommendations**: Specific next steps to improve results

Provide practical, actionable insights for business growth.";

        $insights = $this->call_openai_deep_research($insights_prompt, 'market_research', [
            'focus' => 'contact_analysis',
            'analytics_data' => $analytics,
            'business_context' => $contact_config
        ]);
        
        return $insights;
    }

    private function create_contact_database_tables($user_id) {
        if (!get_option('vmo_contact_analytics_' . $user_id)) {
            update_option('vmo_contact_analytics_' . $user_id, [
                'total_contacts' => 0,
                'categories_breakdown' => [],
                'source_breakdown' => [],
                'storage' => 'central_crm_tables',
                'created_at' => current_time('mysql')
            ]);
        }
    }

    private function setup_contact_sync_automation($user_id) {
        // Schedule regular contact synchronization from all sources
        if (!wp_next_scheduled('vmo_sync_all_contacts', [$user_id])) {
            wp_schedule_event(time(), 'daily', 'vmo_sync_all_contacts', [$user_id]);
        }
    }

    /**
     * Execute daily contact synchronization from all sources
     */
    public function execute_contact_sync($user_id) {
        try {
            // Sync from leads
            $this->sync_leads_to_contact_database($user_id);
            
            // Sync from phone calls
            $this->sync_phone_contacts_to_database($user_id);
            
            // Update analytics
            $this->update_daily_contact_analytics($user_id);
            
            error_log("VMO Contact Management: Daily sync completed for user {$user_id}");
            
        } catch (\Throwable $e) {
            error_log('VMO Contact Sync Error: ' . $e->getMessage());
        }
    }

    private function update_daily_contact_analytics($user_id) {
        $analytics = $this->get_contact_analytics($user_id);
        update_option('vmo_daily_contact_analytics_' . $user_id . '_' . date('Y_m_d'), $analytics);
    }

    /**
     * Execute contact engagement action
     */
    public function execute_contact_engagement($user_id, $contact_id, $action) {
        try {
            $contact = $this->get_central_contact_record((int) $user_id, $contact_id);

            if (!$contact) {
                error_log("VMO Contact Management: Contact {$contact_id} not found for user {$user_id}");
                return;
            }

            $category = $contact['primary_category'];
            
            // Execute engagement action based on contact category and action type
            switch ($action['type'] ?? 'email') {
                case 'email':
                    $this->send_category_specific_email($contact, $action);
                    break;
                case 'follow_up':
                    $this->schedule_contact_follow_up($contact, $action);
                    break;
                case 'nurture':
                    $this->add_to_nurture_sequence($contact, $action);
                    break;
            }
            
            // Update interaction history
            $this->update_contact_interaction($user_id, $contact_id, [
                'type' => 'automated_engagement',
                'action' => $action,
                'timestamp' => current_time('mysql')
            ]);
            
            error_log("VMO Contact Management: Engagement action executed for contact {$contact_id}");
            
        } catch (\Throwable $e) {
            error_log('VMO Contact Engagement Error: ' . $e->getMessage());
        }
    }

    private function send_category_specific_email($contact, $action) {
        // Send personalized email based on contact category
        $category = $contact['primary_category'];
        $email = $contact['contact_data']['email'] ?? '';
        
        if (empty($email)) return;
        
        error_log("VMO Contact Management: Sending {$category} email to {$email}");
        // Email sending logic would go here
    }

    private function schedule_contact_follow_up($contact, $action) {
        // Schedule follow-up based on contact lifecycle stage
        $follow_up_delay = $action['delay'] ?? '+3 days';
        
        wp_schedule_single_event(
            strtotime($follow_up_delay),
            'vmo_contact_follow_up',
            [$contact['user_id'], $contact['contact_id']]
        );
    }

    private function add_to_nurture_sequence($contact, $action) {
        // Add contact to appropriate nurture sequence
        $sequence_type = $action['sequence'] ?? 'general';
        $user_id = $contact['user_id'];
        
        update_option('vmo_nurture_queue_' . $user_id, [
            'contact_id' => $contact['contact_id'],
            'sequence_type' => $sequence_type,
            'added_at' => current_time('mysql')
        ]);
    }

    // ============== CONTACT DETAIL SYSTEM & AJAX HANDLERS ==============

    /**
     * Get detailed contact information including all interactions
     */
    public function ajax_get_contact_details() {
        $this->require_crm_ajax_security();
        
        $contact_id = sanitize_text_field(wp_unslash($_POST['contact_id']));
        $user_id = get_current_user_id();
        
        if (empty($contact_id)) {
            wp_send_json_error('Contact ID is required');
            return;
        }
        
        $contact_details = $this->get_comprehensive_contact_details($user_id, $contact_id);
        
        if ($contact_details) {
            wp_send_json_success($contact_details);
        } else {
            wp_send_json_error('Contact not found');
        }
    }

    /**
     * Get comprehensive contact details from database
     */
    private function get_comprehensive_contact_details($user_id, $contact_id) {
        global $wpdb;
        
        // Get main contact information
        $contacts_table = $wpdb->prefix . 'vmo_contacts';
        $contact = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $contacts_table WHERE user_id = %d AND contact_id = %s",
            $user_id, $contact_id
        ), ARRAY_A);
        
        if (!$contact) return false;
        
        // Get interaction history
        $interactions_table = $wpdb->prefix . 'vmo_contact_interactions';
        $interactions = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM $interactions_table WHERE user_id = %d AND contact_id = %s ORDER BY created_at DESC",
            $user_id, $contact_id
        ), ARRAY_A);
        
        // Get purchase history
        $purchases_table = $wpdb->prefix . 'vmo_contact_purchases';
        $purchases = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM $purchases_table WHERE user_id = %d AND contact_id = %s ORDER BY purchase_date DESC",
            $user_id, $contact_id
        ), ARRAY_A);
        
        // Get communication preferences
        $preferences_table = $wpdb->prefix . 'vmo_contact_preferences';
        $preferences = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $preferences_table WHERE user_id = %d AND contact_id = %s",
            $user_id, $contact_id
        ), ARRAY_A);
        
        // Parse acquisition details and other JSON fields
        $contact['acquisition_details'] = json_decode($contact['acquisition_details'] ?? '{}', true);
        $contact['categorization_analysis'] = json_decode($contact['categorization_analysis'] ?? '{}', true);
        $contact['engagement_strategy'] = json_decode($contact['engagement_strategy'] ?? '{}', true);
        $contact['tags'] = explode(',', $contact['tags'] ?? '');
        
        // Process interactions for display
        foreach ($interactions as &$interaction) {
            $interaction['full_details'] = json_decode($interaction['full_details'] ?? '{}', true);
            $interaction['ai_analysis'] = json_decode($interaction['ai_analysis'] ?? '{}', true);
            $interaction['metadata'] = json_decode($interaction['metadata'] ?? '{}', true);
            $interaction['attachments'] = json_decode($interaction['attachments'] ?? '[]', true);
        }
        
        // Calculate contact insights
        $contact_insights = $this->generate_contact_insights_detailed($contact, $interactions, $purchases);
        
        return [
            'contact' => $contact,
            'interactions' => $interactions,
            'purchases' => $purchases,
            'preferences' => $preferences,
            'insights' => $contact_insights,
            'stats' => [
                'total_interactions' => count($interactions),
                'total_purchases' => count($purchases),
                'total_spent' => array_sum(array_column($purchases, 'amount')),
                'last_interaction' => $interactions[0]['created_at'] ?? null,
                'acquisition_date' => $contact['created_at'],
                'days_since_last_contact' => $this->calculate_days_since_contact($contact['last_contact'])
            ]
        ];
    }

    /**
     * Update contact information
     */
    public function ajax_update_contact() {
        $this->require_crm_ajax_security();
        
        $contact_id = sanitize_text_field(wp_unslash($_POST['contact_id']));
        $user_id = get_current_user_id();
        $updated_data = wp_unslash($_POST['contact_data']);  // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized downstream
        
        if (empty($contact_id)) {
            wp_send_json_error('Contact ID is required');
            return;
        }
        
        $result = $this->update_contact_in_database($user_id, $contact_id, $updated_data);
        
        if ($result) {
            wp_send_json_success(['message' => 'Contact updated successfully']);
        } else {
            wp_send_json_error('Failed to update contact');
        }
    }

    /**
     * Add new interaction to contact
     */
    public function ajax_add_contact_interaction() {
        $this->require_crm_ajax_security();
        
        $contact_id = sanitize_text_field(wp_unslash($_POST['contact_id']));
        $user_id = get_current_user_id();
        $interaction_data = wp_unslash($_POST['interaction_data']);  // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized downstream
        
        if (empty($contact_id)) {
            wp_send_json_error('Contact ID is required');
            return;
        }
        
        // Add AI analysis to the interaction
        $interaction_data['ai_analysis'] = $this->analyze_interaction_with_ai($interaction_data, $contact_id);
        
        $result = $this->add_contact_interaction_to_database($user_id, $contact_id, $interaction_data);
        
        if ($result) {
            wp_send_json_success(['message' => 'Interaction added successfully', 'interaction_id' => $result]);
        } else {
            wp_send_json_error('Failed to add interaction');
        }
    }

    /**
     * Get contact interactions with pagination
     */
    public function ajax_get_contact_interactions() {
        $this->require_crm_ajax_security();
        
        $contact_id = sanitize_text_field(wp_unslash($_POST['contact_id']));
        $user_id = get_current_user_id();
        $page = intval($_POST['page'] ?? 1);
        $per_page = intval($_POST['per_page'] ?? 20);
        
        $interactions = $this->get_paginated_contact_interactions($user_id, $contact_id, $page, $per_page);
        
        wp_send_json_success($interactions);
    }

    /**
     * Get contacts list with filtering and pagination
     */
    public function ajax_get_contacts_list() {
        $this->require_crm_ajax_security();
        
        $user_id = get_current_user_id();
        $filters = wp_unslash($_POST['filters'] ?? []);  // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized downstream
        $page = intval($_POST['page'] ?? 1);
        $per_page = intval($_POST['per_page'] ?? 50);
        $search = sanitize_text_field($_POST['search'] ?? '');
        
        $contacts = $this->get_filtered_contacts_list($user_id, $filters, $page, $per_page, $search);
        
        wp_send_json_success($contacts);
    }

    /**
     * Search contacts by name, email, phone, or other criteria
     */
    public function ajax_search_contacts() {
        $this->require_crm_ajax_security();
        
        $user_id = get_current_user_id();
        $search_term = sanitize_text_field(wp_unslash($_POST['search_term']));
        $limit = intval($_POST['limit'] ?? 10);
        
        if (strlen($search_term) < 2) {
            wp_send_json_error('Search term must be at least 2 characters');
            return;
        }
        
        $results = $this->search_contacts_database($user_id, $search_term, $limit);
        
        wp_send_json_success($results);
    }

    /**
     * Delete contact and all associated data
     */
    public function ajax_delete_contact() {
        $this->require_crm_ajax_security();
        
        $contact_id = sanitize_text_field(wp_unslash($_POST['contact_id']));
        $user_id = get_current_user_id();
        
        if (empty($contact_id)) {
            wp_send_json_error('Contact ID is required');
            return;
        }
        
        $result = $this->delete_contact_completely($user_id, $contact_id);
        
        if ($result) {
            wp_send_json_success(['message' => 'Contact deleted successfully']);
        } else {
            wp_send_json_error('Failed to delete contact');
        }
    }

    /**
     * Update contact interaction
     */
    public function ajax_update_contact_interaction() {
        $this->require_crm_ajax_security();
        
        $interaction_id = intval($_POST['interaction_id']);
        $user_id = get_current_user_id();
        $updated_data = wp_unslash($_POST['interaction_data']);  // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- sanitized downstream
        
        if (empty($interaction_id)) {
            wp_send_json_error('Interaction ID is required');
            return;
        }
        
        $result = $this->update_interaction_in_database($user_id, $interaction_id, $updated_data);
        
        if ($result) {
            wp_send_json_success(['message' => 'Interaction updated successfully']);
        } else {
            wp_send_json_error('Failed to update interaction');
        }
    }

    /**
     * Bulk actions on contacts
     */
    public function ajax_bulk_contact_action() {
        $this->require_crm_ajax_security();
        
        // ✅ FIX: $_POST['action'] is always 'vmo_bulk_contact_action' (WordPress AJAX routing).
        // The actual bulk action must come from a separate field: 'bulk_action'.
        $action = sanitize_text_field(wp_unslash($_POST['bulk_action'] ?? ''));
        $raw_ids = isset($_POST['contact_ids']) && is_array($_POST['contact_ids']) ? wp_unslash($_POST['contact_ids']) : array();
        $contact_ids = array_map('sanitize_text_field', $raw_ids);
        $user_id = get_current_user_id();
        
        if (empty($action)) {
            wp_send_json_error('No bulk action specified');
            return;
        }
        
        if (empty($contact_ids)) {
            wp_send_json_error('No contacts selected');
            return;
        }
        
        $result = $this->execute_bulk_contact_action($user_id, $action, $contact_ids);
        
        wp_send_json_success(['message' => "Bulk action '{$action}' completed", 'affected_count' => $result]);
    }

    public function ajax_get_contact_insights() {
        $this->require_crm_ajax_security();
        
        if (!current_user_can('manage_options')) {
            wp_send_json_error('Insufficient permissions');
        }
        
        $contact_id = sanitize_text_field($_POST['contact_id'] ?? '');
        $refresh = sanitize_text_field($_POST['refresh'] ?? '0') === '1';
        
        if (empty($contact_id)) {
            wp_send_json_error('Contact ID is required');
        }
        
        $user_id = get_current_user_id();
        
        // Check if we have cached insights and not refreshing
        $cache_key = 'vmo_contact_insights_' . $user_id . '_' . $contact_id;
        
        if (!$refresh) {
            $cached_insights = get_transient($cache_key);
            if ($cached_insights) {
                wp_send_json_success($cached_insights);
                return;
            }
        }
        
        // ✅ TOKEN DEDUCTION v8.1.0: Only charge when generating NEW insights (not cached)
        if (function_exists('vmo_deduct_tokens_for_operation')) {
            if (!vmo_deduct_tokens_for_operation($user_id, 'contact_insights')) {
                wp_send_json_error('Insufficient tokens for AI insights. Please top up your balance.');
                return;
            }
        }
        
        // Get comprehensive contact details
        $contact_details = $this->get_comprehensive_contact_details($user_id, $contact_id);
        
        if (!$contact_details['contact']) {
            wp_send_json_error('Contact not found');
        }
        
        // Generate AI insights
        $insights = $this->generate_contact_insights_detailed(
            $contact_details['contact'],
            array_slice($contact_details['interactions'], 0, 10), // Latest 10 interactions
            $contact_details['purchases']
        );
        
        // Parse insights for structured display
        $structured_insights = [
            'value_assessment' => $this->extract_insight_section($insights, 'Contact Value Assessment'),
            'engagement_analysis' => $this->extract_insight_section($insights, 'Engagement Analysis'),
            'conversion_opportunities' => $this->extract_insight_section($insights, 'Conversion Opportunities'),
            'risk_assessment' => $this->extract_insight_section($insights, 'Risk Assessment'),
            'next_best_actions' => $this->extract_insight_section($insights, 'Next Best Actions'),
            'lifecycle_stage_assessment' => $this->extract_insight_section($insights, 'Lifecycle Stage Assessment'),
            'relationship_strength' => $this->extract_insight_section($insights, 'Relationship Strength'),
            'communication_optimization' => $this->extract_insight_section($insights, 'Communication Optimization'),
            'recommendations' => $this->extract_recommendations($insights)
        ];
        
        // Cache insights for 1 hour
        set_transient($cache_key, $structured_insights, HOUR_IN_SECONDS);
        
        wp_send_json_success($structured_insights);
    }

    private function extract_insight_section($insights_text, $section_name) {
        // Extract specific section from AI insights
        $pattern = '/\*\*' . preg_quote($section_name, '/') . '\*\*:?\s*(.*?)(?=\*\*|\Z)/is';
        
        if (preg_match($pattern, $insights_text, $matches)) {
            return trim($matches[1]);
        }
        
        return 'No analysis available for this section.';
    }

    private function extract_recommendations($insights_text) {
        $recommendations = [];
        
        // Look for numbered recommendations or bullet points
        $patterns = [
            '/(?:^|
)\d+\.\s*([^
]+)/m',
            '/(?:^|
)[•-]\s*([^
]+)/m',
            '/(?:^|
)→\s*([^
]+)/m'
        ];
        
        foreach ($patterns as $pattern) {
            if (preg_match_all($pattern, $insights_text, $matches)) {
                $recommendations = array_merge($recommendations, $matches[1]);
            }
        }
        
        // If no structured recommendations found, try to extract from Next Best Actions section
        if (empty($recommendations)) {
            $next_actions = $this->extract_insight_section($insights_text, 'Next Best Actions');
            if ($next_actions && $next_actions !== 'No analysis available for this section.') {
                $recommendations = [
                    substr($next_actions, 0, 200) . (strlen($next_actions) > 200 ? '...' : '')
                ];
            }
        }
        
        return array_slice($recommendations, 0, 5); // Limit to 5 recommendations
    }

    // ============== VOICE CONVERSATION SYSTEM ==============
    // VOICE SYSTEM REMOVED: ajax_start_voice_conversation, ajax_continue_voice_conversation, 
    // ajax_end_voice_conversation, and ajax_transcribe_voice_to_text methods removed for No Voice version

    /**
     * ✅ NEW v9.0: Extract structured information from natural conversation
     * This allows the AI to collect information flexibly without rigid formats
     */
    public function ajax_extract_consultation_info() {
        // Allow both logged-in and non-logged-in users for consultation
        if (!isset($_POST['security']) || !wp_verify_nonce(sanitize_text_field(wp_unslash($_POST['security'])), 'vmo_builder_nonce')) {
            wp_send_json_error(array('message' => 'Security check failed'));
            return;
        }
        
        // ✅ TOKEN DEDUCTION v8.1.0: Charge for AI information extraction (300 tokens)
        $user_id = get_current_user_id();
        if (function_exists('vmo_deduct_tokens')) {
            $token_cost = function_exists('vmo_get_token_cost') ? (int) vmo_get_token_cost('info_extraction') : 300;
            if (!vmo_deduct_tokens($user_id, $token_cost, 'info_extraction', 'AI consultation information extraction')) {
                wp_send_json_error(array(
                    'message' => 'Insufficient tokens for information extraction. Please top up your balance.',
                    'status' => 'insufficient_tokens'
                ));
                return;
            }
        }
        
        try {
            $prompt = sanitize_textarea_field($_POST['prompt']);
            $session_id = sanitize_text_field(wp_unslash($_POST['session_id']));
            
            if (empty($prompt)) {
                wp_send_json_error(array('message' => 'No prompt provided'));
                return;
            }
            
            // Call OpenAI to extract structured data from conversation
            $extraction_result = $this->call_openai_api($prompt, 800);
            
            // Try to parse JSON response
            $extracted_data = json_decode($extraction_result, true);
            
            if (json_last_error() !== JSON_ERROR_NONE) {
                // If not valid JSON, try to extract it from the response
                preg_match('/\{[^}]+\}/', $extraction_result, $matches);
                if (!empty($matches[0])) {
                    $extracted_data = json_decode($matches[0], true);
                }
            }
            
            if (empty($extracted_data)) {
                wp_send_json_error(array('message' => 'Failed to extract information'));
                return;
            }
            
            // Save extracted information to consultation session
            $this->save_extracted_information($session_id, $extracted_data);
            
            wp_send_json_success(array(
                'extracted' => $extracted_data,
                'session_id' => $session_id
            ));
            
        } catch (\Throwable $e) {
            error_log('VMO Information Extraction Error: ' . $e->getMessage());
            wp_send_json_error(array('message' => 'Extraction failed: ' . $e->getMessage()));
        }
    }
    
    /**
     * Save extracted information to consultation database
     */
    private function save_extracted_information($session_id, $extracted_data) {
        // ✅ CRITICAL: Ensure $extracted_data is an array to prevent type errors
        if (!is_array($extracted_data)) {
            error_log('VMO: save_extracted_information received non-array data, skipping. Type: ' . gettype($extracted_data));
            return;
        }
        
        $user_id = get_current_user_id();
        
        // Get existing consultation
        $consultation = $this->consultation_db->get_consultation_by_session($session_id);
        
        if ($consultation) {
            $consultation_data = json_decode($consultation->consultation_data, true) ?: [];

            // Normalize into extracted_information bucket for consistent context injection
            if (!isset($consultation_data['extracted_information']) || !is_array($consultation_data['extracted_information'])) {
                $consultation_data['extracted_information'] = [];
            }
            foreach ($extracted_data as $k => $v) {
                if ($k === 'topicCovered') continue; // internal helper
                // Merge arrays intelligently
                if (is_array($v)) {
                    $existing = isset($consultation_data['extracted_information'][$k]) && is_array($consultation_data['extracted_information'][$k])
                        ? $consultation_data['extracted_information'][$k] : [];
                    // Merge arrays intelligently
                    $merged = array_merge($existing, $v);
                    // ✅ FIXED: Use SORT_REGULAR to prevent "Array to string conversion" error on nested structures
                    $merged = array_values(array_unique($merged, SORT_REGULAR));
                    $consultation_data['extracted_information'][$k] = $merged;
                } else {
                    $consultation_data['extracted_information'][$k] = $v;
                }
            }
            $consultation_data = $this->reconcile_intake_memory($consultation_data, '');
            
            $this->consultation_db->save_consultation($user_id, $session_id, array(
                'consultation_data' => $consultation_data
            ));
        } else {
            // Create new consultation with extracted data
            $this->consultation_db->save_consultation($user_id, $session_id, array(
                'consultation_data' => ['extracted_information' => $extracted_data],
                'conversation_log' => [],
                'status' => 'in_progress'
            ));
        }
    }

    private function get_ai_voice_response($user_message, $business_info, $conversation_id) {
        // Get conversation history for context
        global $wpdb;
        $conversation_history = $wpdb->get_results($wpdb->prepare(
            "SELECT user_message, ai_response FROM {$wpdb->prefix}vmo_voice_conversation_turns 
             WHERE conversation_id = %s ORDER BY timestamp ASC LIMIT 10",
            $conversation_id
        ));

        // Build context for AI
        $context = "Business Information: {$business_info}

";
        $context .= "Recent Conversation:
";
        
        foreach ($conversation_history as $turn) {
            $context .= "User: {$turn->user_message}
";
            $context .= "AI: {$turn->ai_response}

";
        }
        
        $context .= "Current User Message: {$user_message}

";

        $prompt = "You are VMO AI conducting a business consultation. Based on the conversation history and current message, provide a helpful, concise response (2-3 sentences max) that:

1. Addresses the user's current question/statement
2. Provides valuable business insight or recommendation
3. Asks one relevant follow-up question to continue the productive consultation

Context:
{$context}

Respond naturally as if speaking in a business consultation. Be professional, consultative, and keep the conversation flowing toward website creation decisions.";

        return $this->call_openai_deep_research($prompt, 'business_consultant', [
            'focus' => 'voice_conversation',
            'conversation_context' => $context,
            'user_message' => $user_message,
            'max_tokens' => 150  // Keep responses concise for voice
        ]);
    }

    private function transcribe_audio_with_openai($audio_content) {
        $api_key = get_option('vmo_openai_api_key');
        if (empty($api_key)) {
            return new WP_Error('no_api_key', 'AI API key not configured');
        }

        // Save audio to temporary file (Whisper API requires file upload)
        $temp_file = wp_tempnam('vmo_audio_');
        vmo_get_filesystem()->put_contents($temp_file, $audio_content, FS_CHMOD_FILE);

        $boundary = wp_generate_password(24, false);
        $file_content = vmo_get_filesystem()->get_contents($temp_file);
        $body  = "--{$boundary}\r\n";
        $body .= "Content-Disposition: form-data; name=\"file\"; filename=\"audio.webm\"\r\n";
        $body .= "Content-Type: audio/webm\r\n\r\n";
        $body .= $file_content . "\r\n";
        $body .= "--{$boundary}\r\n";
        $body .= "Content-Disposition: form-data; name=\"model\"\r\n\r\n";
        $body .= "whisper-1\r\n";
        $body .= "--{$boundary}\r\n";
        $body .= "Content-Disposition: form-data; name=\"language\"\r\n\r\n";
        $body .= "en\r\n";
        $body .= "--{$boundary}--\r\n";

        $wp_response = wp_remote_post('https://api.openai.com/v1/audio/transcriptions', [
            'timeout' => 30,
            'headers' => [
                'Authorization' => 'Bearer ' . $api_key,
                'Content-Type'  => 'multipart/form-data; boundary=' . $boundary,
            ],
            'body' => $body,
        ]);

        // Clean up temp file
        unlink($temp_file);

        if (is_wp_error($wp_response)) {
            return new WP_Error('api_error', 'Failed to connect to AI API: ' . $wp_response->get_error_message());
        }

        $response  = wp_remote_retrieve_body($wp_response);
        $http_code = wp_remote_retrieve_response_code($wp_response);

        $data = json_decode($response, true);

        if ($http_code !== 200) {
            $error_message = isset($data['error']['message']) ? $data['error']['message'] : 'Unknown API error';
            return new WP_Error('api_error', $error_message);
        }

        return $data['text'] ?? '';
    }

    // ============== CONTACT DETAIL SYSTEM HELPER FUNCTIONS ==============

    private function generate_contact_insights_detailed($contact, $interactions, $purchases) {
        $insights_prompt = "Analyze this contact's complete profile and provide strategic insights:

CONTACT PROFILE:
" . json_encode($contact, JSON_PRETTY_PRINT) . "

INTERACTION HISTORY:
" . json_encode(array_slice($interactions, 0, 10), JSON_PRETTY_PRINT) . "

PURCHASE HISTORY:
" . json_encode($purchases, JSON_PRETTY_PRINT) . "

Provide detailed insights on:
1. **Contact Value Assessment**: Current and potential value to the business
2. **Engagement Analysis**: Communication patterns and preferences
3. **Conversion Opportunities**: Potential upsell or cross-sell opportunities
4. **Risk Assessment**: Likelihood of churn or disengagement
5. **Next Best Actions**: Specific recommendations for next interactions
6. **Lifecycle Stage Assessment**: Where they are in the customer journey
7. **Relationship Strength**: Quality of the business relationship
8. **Communication Optimization**: Best ways to engage with this contact

Provide actionable recommendations for maximizing this relationship.";

        $insights = $this->call_openai_deep_research($insights_prompt, 'market_research', [
            'focus' => 'individual_contact_analysis',
            'contact_data' => $contact,
            'interaction_data' => $interactions,
            'purchase_data' => $purchases
        ]);
        
        return $insights;
    }

    private function calculate_days_since_contact($last_contact_date) {
        if (!$last_contact_date) return null;
        
        $last_contact = strtotime($last_contact_date);
        $now = time();
        
        return floor(($now - $last_contact) / (60 * 60 * 24));
    }

    private function update_contact_in_database($user_id, $contact_id, $updated_data) {
        global $wpdb;
        
        $contacts_table = $wpdb->prefix . 'vmo_contacts';
        
        // Sanitize and prepare update data
        $update_fields = [];
        $allowed_fields = [
            'first_name', 'last_name', 'full_name', 'email', 'phone', 'company',
            'job_title', 'address', 'city', 'state', 'zip', 'country',
            'primary_category', 'secondary_category', 'priority_level',
            'lifecycle_stage', 'engagement_level', 'notes', 'tags'
        ];
        
        foreach ($allowed_fields as $field) {
            if (isset($updated_data[$field])) {
                $update_fields[$field] = sanitize_text_field($updated_data[$field]);
            }
        }
        
        $update_fields['updated_at'] = current_time('mysql');
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $result = $wpdb->update(
            $contacts_table,
            $update_fields,
            [
                'user_id' => $user_id,
                'contact_id' => $contact_id
            ]
        );
        
        return $result !== false;
    }

    private function analyze_interaction_with_ai($interaction_data, $contact_id) {
        $analysis_prompt = "Analyze this contact interaction and provide insights:

INTERACTION DATA:
" . json_encode($interaction_data, JSON_PRETTY_PRINT) . "

Provide analysis on:
1. **Interaction Quality**: How productive was this interaction?
2. **Customer Sentiment**: What was the customer's mood/satisfaction?
3. **Key Topics**: What were the main discussion points?
4. **Follow-up Requirements**: What actions are needed?
5. **Opportunity Assessment**: Any sales or service opportunities identified?
6. **Risk Indicators**: Any concerning signals or issues?
7. **Next Best Action**: Specific recommendation for next step
8. **Priority Level**: How urgent is any required follow-up?

Provide structured analysis for CRM tracking and team visibility.";

        $analysis = $this->call_openai_deep_research($analysis_prompt, 'market_research', [
            'focus' => 'interaction_analysis',
            'interaction_data' => $interaction_data,
            'contact_id' => $contact_id
        ]);
        
        return $analysis;
    }

    private function add_contact_interaction_to_database($user_id, $contact_id, $interaction_data) {
        global $wpdb;
        
        $interactions_table = $wpdb->prefix . 'vmo_contact_interactions';
        
        $insert_data = [
            'user_id' => $user_id,
            'contact_id' => $contact_id,
            'interaction_type' => sanitize_text_field($interaction_data['type'] ?? 'other'),
            'interaction_subtype' => sanitize_text_field($interaction_data['subtype'] ?? ''),
            'direction' => sanitize_text_field($interaction_data['direction'] ?? 'inbound'),
            'subject' => sanitize_text_field($interaction_data['subject'] ?? ''),
            'summary' => sanitize_textarea_field($interaction_data['summary'] ?? ''),
            'full_details' => json_encode($interaction_data['details'] ?? []),
            'outcome' => sanitize_text_field($interaction_data['outcome'] ?? 'pending'),
            'next_action' => sanitize_textarea_field($interaction_data['next_action'] ?? ''),
            'follow_up_date' => $interaction_data['follow_up_date'] ?? null,
            'duration_minutes' => intval($interaction_data['duration'] ?? 0),
            'ai_analysis' => json_encode($interaction_data['ai_analysis'] ?? []),
            'satisfaction_score' => isset($interaction_data['satisfaction']) ? intval($interaction_data['satisfaction']) : null,
            'conversion_value' => isset($interaction_data['conversion_value']) ? floatval($interaction_data['conversion_value']) : null,
            'related_campaign' => sanitize_text_field($interaction_data['campaign'] ?? ''),
            'attachments' => json_encode($interaction_data['attachments'] ?? []),
            'metadata' => json_encode($interaction_data['metadata'] ?? []),
            'created_at' => current_time('mysql')
        ];
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $result = $wpdb->insert($interactions_table, $insert_data);
        
        if ($result) {
            // Update contact's last contact time
            $this->update_contact_last_contact_time($user_id, $contact_id);
            return $wpdb->insert_id;
        }
        
        return false;
    }

    private function update_contact_last_contact_time($user_id, $contact_id) {
        global $wpdb;
        
        $contacts_table = $wpdb->prefix . 'vmo_contacts';
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->update(
            $contacts_table,
            ['last_contact' => current_time('mysql')],
            [
                'user_id' => $user_id,
                'contact_id' => $contact_id
            ]
        );
    }

    private function get_paginated_contact_interactions($user_id, $contact_id, $page = 1, $per_page = 20) {
        global $wpdb;
        
        $interactions_table = $wpdb->prefix . 'vmo_contact_interactions';
        $offset = ($page - 1) * $per_page;
        
        $interactions = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM $interactions_table 
             WHERE user_id = %d AND contact_id = %s 
             ORDER BY created_at DESC 
             LIMIT %d OFFSET %d",
            $user_id, $contact_id, $per_page, $offset
        ), ARRAY_A);
        
        $total_count = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM $interactions_table 
             WHERE user_id = %d AND contact_id = %s",
            $user_id, $contact_id
        ));
        
        // Process interactions for display
        foreach ($interactions as &$interaction) {
            $interaction['full_details'] = json_decode($interaction['full_details'] ?? '{}', true);
            $interaction['ai_analysis'] = json_decode($interaction['ai_analysis'] ?? '{}', true);
            $interaction['metadata'] = json_decode($interaction['metadata'] ?? '{}', true);
            $interaction['attachments'] = json_decode($interaction['attachments'] ?? '[]', true);
        }
        
        return [
            'interactions' => $interactions,
            'total_count' => intval($total_count),
            'current_page' => $page,
            'per_page' => $per_page,
            'total_pages' => ceil($total_count / $per_page)
        ];
    }

    private function get_filtered_contacts_list($user_id, $filters, $page, $per_page, $search) {
        global $wpdb;
        
        $contacts_table = $wpdb->prefix . 'vmo_contacts';
        $where_clauses = ["user_id = %d"];
        $where_values = [$user_id];
        
        // Apply filters
        if (!empty($filters['category'])) {
            $where_clauses[] = "primary_category = %s";
            $where_values[] = $filters['category'];
        }
        
        if (!empty($filters['priority'])) {
            $where_clauses[] = "priority_level = %s";
            $where_values[] = $filters['priority'];
        }
        
        if (!empty($filters['lifecycle'])) {
            $where_clauses[] = "lifecycle_stage = %s";
            $where_values[] = $filters['lifecycle'];
        }
        
        if (!empty($filters['source'])) {
            $where_clauses[] = "source = %s";
            $where_values[] = $filters['source'];
        }
        
        // Apply search
        if (!empty($search)) {
            $where_clauses[] = "(COALESCE(full_name, CONCAT(COALESCE(first_name,''),' ',COALESCE(last_name,'')), '') LIKE %s OR email LIKE %s OR phone LIKE %s OR company LIKE %s)";
            $search_term = '%' . $wpdb->esc_like($search) . '%';
            $where_values[] = $search_term;
            $where_values[] = $search_term;
            $where_values[] = $search_term;
            $where_values[] = $search_term;
        }
        
        $where_sql = implode(' AND ', $where_clauses);
        $offset = ($page - 1) * $per_page;
        
        // Get contacts
        $contacts = $wpdb->get_results($wpdb->prepare(
            "SELECT * FROM $contacts_table 
             WHERE $where_sql 
             ORDER BY created_at DESC 
             LIMIT %d OFFSET %d",
            array_merge($where_values, [$per_page, $offset])
        ), ARRAY_A);
        
        // Get total count
        $total_count = $wpdb->get_var($wpdb->prepare(
            "SELECT COUNT(*) FROM $contacts_table WHERE $where_sql",
            $where_values
        ));
        
        // Process contacts for display
        foreach ($contacts as &$contact) {
            $contact['tags'] = explode(',', $contact['tags'] ?? '');
            $contact['acquisition_details'] = json_decode($contact['acquisition_details'] ?? '{}', true);
        }
        
        return [
            'contacts' => $contacts,
            'total_count' => intval($total_count),
            'current_page' => $page,
            'per_page' => $per_page,
            'total_pages' => ceil($total_count / $per_page)
        ];
    }

    private function search_contacts_database($user_id, $search_term, $limit) {
        global $wpdb;
        
        $contacts_table = $wpdb->prefix . 'vmo_contacts';
        $search_pattern = '%' . $wpdb->esc_like($search_term) . '%';
        
        // Use column-safe query: SELECT * avoids Unknown column errors from schema drift
        $contacts = $wpdb->get_results($wpdb->prepare(
            "SELECT * 
             FROM $contacts_table 
             WHERE user_id = %d 
             AND (COALESCE(full_name,'') LIKE %s OR COALESCE(first_name,'') LIKE %s OR COALESCE(email,'') LIKE %s OR COALESCE(phone,'') LIKE %s OR COALESCE(company,'') LIKE %s)
             ORDER BY COALESCE(full_name, CONCAT(COALESCE(first_name,''),' ',COALESCE(last_name,'')), '') ASC 
             LIMIT %d",
            $user_id, $search_pattern, $search_pattern, $search_pattern, $search_pattern, $search_pattern, $limit
        ), ARRAY_A);
        
        return $contacts;
    }

    private function delete_contact_completely($user_id, $contact_id) {
        global $wpdb;
        
        // Start transaction
        // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->query('START TRANSACTION');
        
        try {
            // Delete from all related tables
            $tables = [
                $wpdb->prefix . 'vmo_contact_interactions',
                $wpdb->prefix . 'vmo_contact_purchases',
                $wpdb->prefix . 'vmo_contact_preferences',
                $wpdb->prefix . 'vmo_contacts'
            ];
            
            foreach ($tables as $table) {
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                $wpdb->delete($table, [
                    'user_id' => $user_id,
                    'contact_id' => $contact_id
                ]);
            }
            
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->query('COMMIT');
            return true;
            
        } catch (\Throwable $e) {
            // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->query('ROLLBACK');
            return false;
        }
    }

    private function update_interaction_in_database($user_id, $interaction_id, $updated_data) {
        global $wpdb;
        
        $interactions_table = $wpdb->prefix . 'vmo_contact_interactions';
        
        $update_fields = [];
        $allowed_fields = [
            'interaction_type', 'interaction_subtype', 'subject', 'summary',
            'outcome', 'next_action', 'follow_up_date', 'satisfaction_score'
        ];
        
        foreach ($allowed_fields as $field) {
            if (isset($updated_data[$field])) {
                $update_fields[$field] = sanitize_text_field($updated_data[$field]);
            }
        }
        
        $update_fields['updated_at'] = current_time('mysql');
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $result = $wpdb->update(
            $interactions_table,
            $update_fields,
            [
                'id' => $interaction_id,
                'user_id' => $user_id
            ]
        );
        
        return $result !== false;
    }

    private function execute_bulk_contact_action($user_id, $action, $contact_ids) {
        global $wpdb;
        
        $contacts_table = $wpdb->prefix . 'vmo_contacts';
        $affected_count = 0;
        
        foreach ($contact_ids as $contact_id) {
            switch ($action) {
                case 'delete':
                    if ($this->delete_contact_completely($user_id, $contact_id)) {
                        $affected_count++;
                    }
                    break;
                    
                case 'mark_high_priority':
                    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                    $result = $wpdb->update(
                        $contacts_table,
                        ['priority_level' => 'high'],
                        ['user_id' => $user_id, 'contact_id' => $contact_id]
                    );
                    if ($result) $affected_count++;
                    break;
                    
                case 'mark_low_priority':
                    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                    $result = $wpdb->update(
                        $contacts_table,
                        ['priority_level' => 'low'],
                        ['user_id' => $user_id, 'contact_id' => $contact_id]
                    );
                    if ($result) $affected_count++;
                    break;
                    
                case 'add_to_nurture':
                    // Add to nurture sequence
                    $this->add_contact_to_nurture_sequence($user_id, $contact_id);
                    $affected_count++;
                    break;
            }
        }
        
        return $affected_count;
    }

    private function add_contact_to_nurture_sequence($user_id, $contact_id) {
        // Add contact to appropriate nurture sequence based on category
        update_option('vmo_nurture_queue_' . $user_id . '_' . time(), [
            'contact_id' => $contact_id,
            'sequence_type' => 'general_nurture',
            'added_at' => current_time('mysql')
        ]);
    }

    /**
     * Generate business summary from consultation data
     */
    public function ajax_generate_business_summary() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        // ✅ TOKEN DEDUCTION v8.1.0: Charge for AI business summary generation (600 tokens)
        $user_id = get_current_user_id();
        if (function_exists('vmo_deduct_tokens')) {
            $token_cost = function_exists('vmo_get_token_cost') ? (int) vmo_get_token_cost('business_summary') : 600;
            if (!vmo_deduct_tokens($user_id, $token_cost, 'business_summary', 'AI business summary generation')) {
                wp_send_json_error(array(
                    'message' => 'Insufficient tokens for business summary. Please top up your balance.',
                    'status' => 'insufficient_tokens'
                ));
                return;
            }
        }
        
        $business_data = isset($_POST['business_data']) ? json_decode(wp_unslash($_POST['business_data']), true) : [];
        $consultation_history = isset($_POST['consultation_history']) ? json_decode(wp_unslash($_POST['consultation_history']), true) : [];
        $session_id = isset($_POST['session_id']) ? sanitize_text_field(wp_unslash($_POST['session_id'])) : '';
        
        if (empty($business_data) && empty($consultation_history)) {
            wp_send_json_error(['message' => 'No business data or consultation history provided']);
            return;
        }
        
        try {
            // Build comprehensive summary prompt
            $prompt = "Based on this business consultation, create a comprehensive business summary:

";
            
            if (!empty($business_data)) {
                $prompt .= "COLLECTED INFORMATION:
";
                $prompt .= json_encode($business_data, JSON_PRETTY_PRINT) . "

";
            }
            
            if (!empty($consultation_history)) {
                $prompt .= "CONVERSATION HISTORY:
";
                foreach ($consultation_history as $msg) {
                    $role = isset($msg['role']) ? ucfirst($msg['role']) : 'Unknown';
                    $content = isset($msg['content']) ? $msg['content'] : '';
                    $prompt .= "$role: $content
";
                }
            }
            
            $prompt .= "

Create a professional business summary that includes:
1. Business Overview (name, industry, what they do)
2. Target Market (who they serve)
3. Products/Services Offered
4. Business Goals & Vision
5. Website Requirements
6. Recommended Features (ecommerce, booking, phone system, etc.)

Format as clean, professional text with clear sections.";
            
            // Call OpenAI to generate summary
            $openai_api_key = get_option('vmo_openai_api_key');
            if (empty($openai_api_key)) {
                wp_send_json_error(['message' => 'AI API key not configured']);
                return;
            }
            
            // ✅ FIX: Use centralized model registry
            $_bs_model = function_exists('vmo_get_model') ? vmo_get_model('mini') : (defined('VMO_OPENAI_MODEL') ? VMO_OPENAI_MODEL : 'gpt-5.5-mini');
            $_bs_body = array_merge([
                'model' => $_bs_model,
                'messages' => [
                    ['role' => 'system', 'content' => 'You are a professional business consultant creating comprehensive business summaries.'],
                    ['role' => 'user', 'content' => $prompt]
                ],
            ], function_exists('vmo_get_token_param') ? vmo_get_token_param($_bs_model, 1500) : ['max_tokens' => 1500]);
            $_bs_body = $this->apply_safe_openai_temperature($_bs_body, $_bs_model, 0.7);

            $response = wp_remote_post('https://api.openai.com/v1/chat/completions', [
                'headers' => [
                    'Authorization' => 'Bearer ' . $openai_api_key,
                    'Content-Type' => 'application/json',
                ],
                'body' => json_encode($_bs_body),
                'timeout' => 120 // Increased for GPT-5.5 reasoning
            ]);
            
            if (is_wp_error($response)) {
                wp_send_json_error(['message' => 'API request failed: ' . $response->get_error_message()]);
                return;
            }
            
            $body = json_decode(wp_remote_retrieve_body($response), true);
            
            if (isset($body['choices'][0]['message']['content'])) {
                $summary = $body['choices'][0]['message']['content'];
                
                // Save summary to database
                if (!empty($session_id)) {
                    update_option('vmo_business_summary_' . $session_id, $summary);
                }
                
                wp_send_json_success([
                    'summary' => $summary,
                    'session_id' => $session_id
                ]);
            } else {
                wp_send_json_error(['message' => 'Invalid API response']);
            }
            
        } catch (\Throwable $e) {
            wp_send_json_error(['message' => 'Summary generation failed: ' . $e->getMessage()]);
        }
    }
    
    // ═════════════════════════════════════════════════════════════════════
    // PERSISTENT BUSINESS PROFILE MANAGEMENT (Cross-session Memory)
    // ═════════════════════════════════════════════════════════════════════
    
    /**
     * Get or create business profile for user
     * Returns complete accumulated business knowledge
     */
    public function get_business_profile($user_id = null) {
        if (!$user_id) {
            $user_id = get_current_user_id();
        }
        
        global $wpdb;
        $table = $wpdb->prefix . 'vmo_business_profiles';
        
        // Check if table exists first
        $table_exists = $wpdb->get_var("SHOW TABLES LIKE '$table'");
        if (!$table_exists) {
            error_log("VMO: Business profiles table doesn't exist. Please deactivate and reactivate the plugin to create it.");
            return null; // Return null instead of throwing error
        }
        
        $profile = $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM $table WHERE user_id = %d",
            $user_id
        ), ARRAY_A);
        
        // Create profile if doesn't exist
        if (!$profile) {
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->insert($table, [
                'user_id' => $user_id,
                'created_at' => current_time('mysql')
            ]);
            
            return $this->get_business_profile($user_id); // Recursive call to get newly created profile
        }
        
        // Decode JSON fields
        $json_fields = [
            'uploaded_documents', 'key_milestones', 'current_goals',
            'customer_personas', 'product_catalog', 'marketing_campaigns',
            'sales_data', 'competitor_analysis', 'extracted_info'
        ];
        
        foreach ($json_fields as $field) {
            if (!empty($profile[$field])) {
                $profile[$field] = json_decode($profile[$field], true);
            } else {
                $profile[$field] = [];
            }
        }
        
        return $profile;
    }
    
    /**
     * Update business profile with new information
     */
    public function update_business_profile($user_id, $updates) {
        global $wpdb;
        $table = $wpdb->prefix . 'vmo_business_profiles';
        
        // JSON encode array fields
        $json_fields = [
            'uploaded_documents', 'key_milestones', 'current_goals',
            'customer_personas', 'product_catalog', 'marketing_campaigns',
            'sales_data', 'competitor_analysis', 'extracted_info'
        ];
        
        foreach ($json_fields as $field) {
            if (isset($updates[$field]) && is_array($updates[$field])) {
                $updates[$field] = json_encode($updates[$field]);
            }
        }
        
        $updates['updated_at'] = current_time('mysql');
        
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->update(
            $table,
            $updates,
            ['user_id' => $user_id]
        );
    }
    
    /**
     * Add document to business profile
     */
    public function add_document_to_profile($user_id, $document_data) {
        $profile = $this->get_business_profile($user_id);
        
        $document_entry = [
            'title' => $document_data['title'] ?? 'Untitled Document',
            'type' => $document_data['type'] ?? 'general',
            'uploaded_date' => current_time('mysql'),
            'file_url' => $document_data['url'] ?? '',
            'summary' => $document_data['summary'] ?? '',
            'key_points' => $document_data['key_points'] ?? []
        ];
        
        $profile['uploaded_documents'][] = $document_entry;
        
        $this->update_business_profile($user_id, [
            'uploaded_documents' => $profile['uploaded_documents']
        ]);
    }
    
    /**
     * Add milestone to business profile
     */
    public function add_milestone($user_id, $milestone_text, $date = null) {
        $profile = $this->get_business_profile($user_id);
        
        $milestone = [
            'date' => $date ?? current_time('mysql'),
            'description' => $milestone_text,
            'added_at' => current_time('mysql')
        ];
        
        $profile['key_milestones'][] = $milestone;
        
        $this->update_business_profile($user_id, [
            'key_milestones' => $profile['key_milestones']
        ]);
    }
    
    /**
     * Update consultation metadata (track consultation history)
     */
    public function update_consultation_metadata($user_id, $topic = '') {
        $profile = $this->get_business_profile($user_id);
        
        // Skip if profile doesn't exist (table not created yet)
        if (!$profile) {
            return;
        }
        
        $this->update_business_profile($user_id, [
            'total_consultations' => ($profile['total_consultations'] ?? 0) + 1,
            'last_consultation_date' => current_time('mysql'),
            'last_consultation_topic' => $topic
        ]);
    }
    
    /**
     * Build system prompt with business profile context
     * THIS is what makes the AI remember EVERYTHING!
     */
    public function build_profile_aware_system_prompt($user_id) {
        $profile = $this->get_business_profile($user_id);
        
        // Return empty string if profile doesn't exist (table not created yet)
        if (!$profile) {
            return '';
        }
        
        $prompt = "═══════════════════════════════════════════════════
";
        $prompt .= "BUSINESS PROFILE FOR USER #$user_id
";
        $prompt .= "═══════════════════════════════════════════════════

";
        
        // Business basics
        if (!empty($profile['business_name'])) {
            $prompt .= "Business Name: {$profile['business_name']}
";
        }
        if (!empty($profile['industry'])) {
            $prompt .= "Industry: {$profile['industry']}
";
        }
        if (!empty($profile['founded_date'])) {
            $prompt .= "Founded: {$profile['founded_date']}
";
        }
        $prompt .= "
";
        
        // Business summary
        if (!empty($profile['business_summary'])) {
            $prompt .= "BUSINESS SUMMARY:
{$profile['business_summary']}

";
        }
        
        // Uploaded documents
        if (!empty($profile['uploaded_documents'])) {
            $prompt .= "UPLOADED DOCUMENTS (" . count($profile['uploaded_documents']) . " total):
";
            foreach (array_slice($profile['uploaded_documents'], -5) as $doc) {
                $prompt .= "- {$doc['title']} (uploaded {$doc['uploaded_date']})
";
                if (!empty($doc['summary'])) {
                    $prompt .= "  Summary: " . substr($doc['summary'], 0, 150) . "...
";
                }
            }
            $prompt .= "
";
        }
        
        // Key milestones
        if (!empty($profile['key_milestones'])) {
            $prompt .= "KEY MILESTONES:
";
            foreach (array_slice($profile['key_milestones'], -10) as $milestone) {
                $prompt .= "- {$milestone['date']}: {$milestone['description']}
";
            }
            $prompt .= "
";
        }
        
        // Current goals
        if (!empty($profile['current_goals'])) {
            $prompt .= "CURRENT GOALS:
";
            foreach ($profile['current_goals'] as $goal) {
                if (is_array($goal)) {
                    $prompt .= "- {$goal['description']} (Target: {$goal['target_date']})
";
                } else {
                    $prompt .= "- $goal
";
                }
            }
            $prompt .= "
";
        }
        
        // Customer personas
        if (!empty($profile['customer_personas'])) {
            $prompt .= "TARGET CUSTOMERS:
";
            foreach ($profile['customer_personas'] as $persona) {
                if (is_array($persona)) {
                    $prompt .= "- {$persona['name']}: {$persona['description']}
";
                }
            }
            $prompt .= "
";
        }
        
        // Products/services
        if (!empty($profile['product_catalog'])) {
            $prompt .= "PRODUCTS/SERVICES (" . count($profile['product_catalog']) . " total):
";
            foreach (array_slice($profile['product_catalog'], 0, 5) as $product) {
                if (is_array($product)) {
                    $prompt .= "- {$product['name']}: {$product['description']}
";
                }
            }
            $prompt .= "
";
        }
        
        // Consultation history
        $total_consultations = $profile['total_consultations'] ?? 0;
        if ($total_consultations > 0) {
            $prompt .= "PREVIOUS CONSULTATIONS:
";
            $prompt .= "- Total consultations: $total_consultations
";
            if (!empty($profile['last_consultation_date'])) {
                $last_date = human_time_diff(strtotime($profile['last_consultation_date']), current_time('timestamp'));
                $prompt .= "- Last consultation: $last_date ago
";
                if (!empty($profile['last_consultation_topic'])) {
                    $prompt .= "- Last topic discussed: {$profile['last_consultation_topic']}
";
                }
            }
            $prompt .= "
";
        }
        
        // Relationship context
        if (!empty($profile['created_at'])) {
            $working_together = human_time_diff(strtotime($profile['created_at']), current_time('timestamp'));
            $prompt .= "We've been working together for: $working_together

";
        }
        
        // ✅ PHONE SERVICE AWARENESS: Detect if user already has a provisioned business phone number
        // This lets the AI consultant know about existing phone service so it doesn't re-recommend it.
        $phone_service_meta = get_user_meta($user_id, 'vmo_phone_service', true);
        if (!empty($phone_service_meta)) {
            $prompt .= "EXISTING PHONE SERVICE: ✅ Already Provisioned
";
            if (is_array($phone_service_meta) && !empty($phone_service_meta['phone_number'])) {
                $prompt .= "- Business Phone Number: " . $phone_service_meta['phone_number'] . "
";
            }
            $prompt .= "- Do NOT recommend setting up a new phone system — they already have one.
";
            $prompt .= "- Reference this number when discussing contact page, header, or customer communication.

";
        } else {
            // Also check the vmo_user_phone_numbers DB table for manually provisioned numbers
            global $wpdb;
            $phone_table = $wpdb->prefix . 'vmo_user_phone_numbers';
            try {
                $active_phone = $wpdb->get_row($wpdb->prepare(
                    "SELECT phone_number FROM $phone_table WHERE user_id = %d AND status = 'active' ORDER BY id DESC LIMIT 1",
                    $user_id
                ));
                if ($active_phone && !empty($active_phone->phone_number)) {
                    $prompt .= "EXISTING PHONE SERVICE: ✅ Manually Provisioned
";
                    $prompt .= "- Business Phone Number: " . $active_phone->phone_number . "
";
                    $prompt .= "- This number was manually provisioned (not via consultation).
";
                    $prompt .= "- Do NOT recommend setting up a new phone system — they already have one.
";
                    $prompt .= "- Reference this number when discussing contact page, header, or customer communication.

";
                }
            } catch (\Throwable $e) {
                // Table may not exist yet — skip silently
            }
        }
        
        $prompt .= "═══════════════════════════════════════════════════
";
        $prompt .= "NOW STARTING NEW CONSULTATION...
";
        $prompt .= "Reference the above business profile in your responses.
";
        $prompt .= "The user doesn't need to re-explain their business!
";
        $prompt .= "═══════════════════════════════════════════════════

";
        
        return $prompt;
    }
    
    /**
     * ═══════════════════════════════════════════════════════════════════════════
     * V2 CONSULTATION SYSTEM - SIMPLE, PERSISTENT, WORKS LIKE THE MVP
     * ═══════════════════════════════════════════════════════════════════════════
     */
    
    /**
     * Load complete consultation data from database
     * Returns EVERYTHING: messages, blueprintRaw, businessProfile, documents, phase
     */
    public function ajax_load_consultation_v2() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        $session_id = sanitize_text_field($_POST['session_id'] ?? '');
        if (empty($session_id)) {
            wp_send_json_error(['message' => 'Missing session_id']);
        }
        
        // Get from database
        $row = $this->consultation_db->get_consultation_by_session($session_id);
        
        if (!$row) {
            // No existing consultation
            wp_send_json_success([
                'messages' => [],
                'blueprintRaw' => null,
                'blueprintSummary' => null,
                'businessProfile' => [],
                'documents' => [],
                'currentPhase' => 0
            ]);
            return;
        }
        
        // Decode consultation_data
        $data = json_decode($row->consultation_data ?? '{}', true);
        if (!is_array($data)) $data = [];
        
        // Return EVERYTHING
        wp_send_json_success([
            'messages' => $data['messages'] ?? [],
            'blueprintRaw' => $data['blueprintRaw'] ?? null,
            'blueprintSummary' => $data['blueprintSummary'] ?? null,
            'businessProfile' => $data['businessProfile'] ?? [],
            'documents' => $data['documents'] ?? [],
            'media_files' => $data['media_files'] ?? [],
            'currentPhase' => $data['currentPhase'] ?? 0,
            'scenario' => $data['scenario'] ?? null
        ]);
    }
    
    /**
     * Save complete consultation data to database
     * Saves EVERYTHING in one atomic operation
     */
    public function ajax_save_consultation_v2() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        $session_id = sanitize_text_field($_POST['session_id'] ?? '');
        $data_json = wp_unslash($_POST['data'] ?? '{}');
        
        if (empty($session_id)) {
            wp_send_json_error(['message' => 'Missing session_id']);
        }
        
        // Parse data
        $data = json_decode($data_json, true);
        if (!is_array($data)) {
            wp_send_json_error(['message' => 'Invalid data format']);
        }
        
        // Save to database
        $user_id = get_current_user_id();
        $saved = $this->consultation_db->save_consultation($user_id, $session_id, [
            'consultation_data' => $data
        ]);
        
        if ($saved) {
            error_log("VMO V2: Saved consultation $session_id - " . 
                     (isset($data['messages']) ? count($data['messages']) : 0) . " messages, " .
                     "blueprintRaw: " . (isset($data['blueprintRaw']) ? 'YES' : 'NO'));
            wp_send_json_success(['message' => 'Saved']);
        } else {
            wp_send_json_error(['message' => 'Save failed']);
        }
    }
    
    /**
     * Chat endpoint - sends message to AI with FULL context
     */
    public function ajax_consultation_chat_v2() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        $message = sanitize_textarea_field($_POST['message'] ?? '');
        $context_json = wp_unslash($_POST['context'] ?? '{}');
        $session_id = sanitize_text_field($_POST['session_id'] ?? '');
        
        if (empty($message)) {
            wp_send_json_error(['message' => 'Empty message']);
        }
        
        // Parse context
        $context = json_decode($context_json, true);
        if (!is_array($context)) $context = [];
        
        // Build AI prompt with COMPLETE context
        $system_prompt = $this->build_v2_system_prompt($context);
        $user_prompt = $message;
        
        // Call OpenAI — ✅ Increased to 4000 tokens for comprehensive, context-rich responses
        $response = $this->call_openai_api($user_prompt, 4000, false, false, $system_prompt);
        
        if ($response) {
            // Extract business info from response if AI mentions it
            $extracted_info = $this->extract_business_info_from_text($response);
            
            wp_send_json_success([
                'response' => $response,
                'extractedInfo' => $extracted_info
            ]);
        } else {
            wp_send_json_error(['message' => 'AI response failed']);
        }
    }
    
    /**
     * Build system prompt with FULL context for V2
     */
    private function build_v2_system_prompt($context) {
        $prompt = "You are VMO, an expert AI business consultant.

";
        
        $prompt .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
";
        $prompt .= "🚨 CONSULTATION SCENARIO & FLOW 🚨
";
        $prompt .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

";
        
        $scenario = $context['scenario'] ?? 'A';
        $scenario_name = $context['scenarioName'] ?? 'First-Time Entrepreneur';
        $current_phase = $context['currentPhase'] ?? 0;
        
        $phase_names = [
            0 => 'Phase 1: Business Foundations',
            1 => 'Phase 2: Website Vision',
            2 => 'Phase 3: Deep Market Research',
            3 => 'Phase 4: Site Architecture'
        ];
        $current_phase_name = $phase_names[$current_phase] ?? "Phase $current_phase";

        $prompt .= "ACTIVE SCENARIO: $scenario - $scenario_name
";
        $prompt .= "CURRENT PHASE: $current_phase_name

";
        
        // Scenario-specific behavior
        if ($scenario === 'A') {
            $prompt .= "SCENARIO A BEHAVIOR:
";
            $prompt .= "- User is starting from scratch with just an idea
";
            $prompt .= "- Ask discovery questions to understand their vision
";
            $prompt .= "- Help them define their business model
";
            $prompt .= "- Guide them through market research
";
            $prompt .= "- Be patient and educational

";
        } elseif ($scenario === 'B') {
            $prompt .= "SCENARIO B BEHAVIOR:
";
            $prompt .= "- User has an existing business that needs improvement
";
            $prompt .= "- Assess current state and identify gaps
";
            $prompt .= "- Focus on optimization and growth strategies
";
            $prompt .= "- Compare current vs. desired state
";
            $prompt .= "- Leverage their existing experience

";
        } elseif ($scenario === 'C') {
            $prompt .= "SCENARIO C BEHAVIOR (DOCUMENT-BASED):
";
            $prompt .= "- User has uploaded a complete business plan/document
";
            $prompt .= "- Phase 1: Extract ALL information from the document
";
            $prompt .= "- Phase 2: Confirm details and ask for refinements
";
            $prompt .= "- Phase 3: CRITICAL - Perform DEEP market research:
";
            $prompt .= "  • Identify top 5-10 competitors in their industry
";
            $prompt .= "  • Analyze competitor websites and positioning
";
            $prompt .= "  • Find market gaps and opportunities
";
            $prompt .= "  • Compare pricing strategies
";
            $prompt .= "  • Provide research-backed recommendations
";
            $prompt .= "- Phase 4: Design CUSTOM website based on Phase 3 research
";
            $prompt .= "- NEVER re-ask for information already in the document!
";
            $prompt .= "- If website style or features are mentioned in the document, quote them for confirmation instead of asking";
            $prompt .= "- If email addresses are found in the document, list them for confirmation instead of asking";
            $prompt .= "- NEVER give generic website structures - base on research!

";
        }
        
        // Phase-specific instructions
        if ($scenario === 'C') {
            if ($current_phase == 0) {
                $prompt .= "PHASE 1 TASK (Document Analysis):
";
                $prompt .= "- Thoroughly analyze the uploaded business plan
";
                $prompt .= "- Extract: business name, products/services, pricing, target audience, goals, website preferences, and requested features\n";
                $prompt .= "- Present findings in clear Q&A format\n";
                $prompt .= "- If website preferences/features are found in the doc, explicit confirm them: 'I found these website preferences: [list]. Are these correct?'\n";
                $prompt .= "- Ask ONLY if information is truly missing\n";
                $prompt .= "- Wait for user confirmation before proceeding
";
                $prompt .= "- When user confirms details, append [[PHASE_COMPLETE]] to proceed to Phase 2.

";
            } elseif ($current_phase == 1) {
                $prompt .= "PHASE 2 TASK (Website Vision & Custom Pages):
";
                $prompt .= "- Check 'UPLOADED ASSETS' and 'EXTRACTED INFORMATION' first!
";
                $prompt .= "- If a logo is listed in UPLOADED ASSETS, acknowledge it. Do NOT ask if they have one.
";
                $prompt .= "- If goals/preferences exist, LIST them for confirmation. Do NOT ask for them as if missing.
";
                $prompt .= "- Focus on visual style, specific custom pages, and animations.
";
                $prompt .= "- Do NOT summarize Phase 1 business info again unless requested.
";
                $prompt .= "- Goal: Finalize the sitemap and visual direction.
";
                $prompt .= "- When user confirms, append [[PHASE_COMPLETE]] to proceed to Phase 3.

";
            } elseif ($current_phase == 2) {
                $prompt .= "PHASE 3 TASK (DEEP MARKET RESEARCH):\n";
                $prompt .= "🚨 THE COMPREHENSIVE RESEARCH IS HANDLED BY THE SYSTEM UI 🚨\n\n";
                $prompt .= "1. You have collected all necessary inputs for research.\n";
                $prompt .= "2. DO NOT ACTUALLY CONDUCT OR TYPE OUT THE COMPETITOR RESEARCH YOURSELF.\n";
                $prompt .= "3. Tell the user excitedly: 'I have successfully prepared your business profile for deep analysis! I am now ready to launch the 25-step Comprehensive Competitive Intelligence engine. Please confirm to boot up the visual research scanner below!'\n";
                $prompt .= "4. DO NOT APPEND [[PHASE_COMPLETE]] yetself! Wait for the user to click the button!\n";
            } elseif ($current_phase == 3) {
                $prompt .= "PHASE 4 TASK (Custom Website Architecture):
";
                $prompt .= "- Design CUSTOM website structure based on Phase 3 research
";
                $prompt .= "- Every feature must be justified by competitive analysis
";
                $prompt .= "- Tailor to this business's unique value proposition
";
                $prompt .= "- Explain WHY each section/feature is recommended
";
                $prompt .= "- NO generic templates!
";
                $prompt .= "- Once architecture is confirmed, append [[PHASE_COMPLETE]] to finish consultation.

";
            }
        }
        
        // Add uploaded files/media context
        $uploaded_files = $context['documents'] ?? ($context['uploaded_media'] ?? ($context['media_files'] ?? []));
        // Also check if 'uploaded_logo' has been explicitly flagged in context
        if (!empty($context['has_logo']) || !empty($context['logo_url'])) {
             $uploaded_files[] = 'Logo (Uploaded)';
        }

        if (!empty($uploaded_files)) {
            $prompt .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
";
            $prompt .= "📂 UPLOADED ASSETS & FILES (Already Provided)
";
            $prompt .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

";
            foreach ($uploaded_files as $file) {
                // Handle various file object structures
                $name = is_string($file) ? $file : ($file['name'] ?? ($file['filename'] ?? 'Unknown File'));
                $prompt .= "- $name
";
            }
            $prompt .= "
";
        }

        // Add uploaded document context
        if (!empty($context['blueprintRaw'])) {
            $prompt .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
";
            $prompt .= "📄 UPLOADED BUSINESS PLAN DOCUMENT
";
            $prompt .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

";
            $prompt .= substr($context['blueprintRaw'], 0, 120000) . "

";
        }
        
        // Add business profile
        if (!empty($context['businessProfile'])) {
            $prompt .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
";
            $prompt .= "💼 EXTRACTED BUSINESS INFORMATION
";
            $prompt .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

";
            foreach ($context['businessProfile'] as $key => $value) {
                if (!empty($value)) {
                    $prompt .= ucfirst(str_replace('_', ' ', $key)) . ": $value
";
                }
            }
            $prompt .= "
";
        }
        
        // Add recent conversation history — ✅ Increased from 10 to 40 for richer context
        if (!empty($context['conversationHistory'])) {
            $recent = array_slice($context['conversationHistory'], -40);
            $prompt .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
";
            $prompt .= "💬 RECENT CONVERSATION
";
            $prompt .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

";
            foreach ($recent as $msg) {
                $role = $msg['role'] === 'user' ? 'USER' : 'YOU';
                $content = substr($msg['content'], 0, 500);
                $prompt .= "$role: $content

";
            }
        }
        
        $prompt .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

";
        
        $prompt .= "CRITICAL RULES:
";
        $prompt .= "1. NEVER re-ask for information in uploaded documents or assets lists
";
        $prompt .= "2. CHECK 'UPLOADED ASSETS' before asking for files (like logos)
";
        $prompt .= "3. If user says 'continue' (or equivalent in any language), Assume Agreement and move to next topic
";
        $prompt .= "4. Reference specific details from the business plan significantly
";
        $prompt .= "5. Be conversational but efficient - do not over-confirm
";
        $prompt .= "6. Remember EVERYTHING - this is a persistent consultation
";
        $prompt .= "7. IMPORTANT: Analyze user intent regardless of language (e.g. 'sigue', 'avancer', 'weiter' = continue).
";
        $prompt .= "8. FORMATTING: When outputting extensive summaries, document extractions, long lists, or deep research data to the user, ALWAYS wrap that large block of text entirely within a standard Markdown code block (using ```markdown and ```). Keep conversational intro/outro text outside the code block.
";
        $prompt .= "9. DATA EXTRACTION: If you identify new business info (Name, Industry, Audience, Goals, etc), OUTPUT it at the very end of your response in this hidden block format:
";
        $prompt .= "   [[DATA]]
";
        $prompt .= "   {
";
        $prompt .= "     \"businessName\": \"...\",
";
        $prompt .= "     \"industry\": \"...\",
";
        $prompt .= "     \"targetAudience\": \"...\",
";
        $prompt .= "     \"goals\": \"...\"
";
        $prompt .= "   }
";
        $prompt .= "   [[/DATA]]
";
        $prompt .= "9. TRANSITIONS: When you have confirmed all necessary details for the current phase, append [[PHASE_COMPLETE]] to the very end of your response to move to the next phase (WARNING: NEVER append this in Phase 3! Phase 3 research must be triggered by the user clicking the button).
";
        
        return $prompt;
    }
    
    /**
     * Analyze uploaded document
     */
    public function ajax_analyze_document_v2() {
        check_ajax_referer('vmo_builder_nonce', 'security');
        
        $filename = sanitize_text_field($_POST['filename'] ?? 'document');
        $text = wp_unslash($_POST['text'] ?? '');
        $session_id = sanitize_text_field($_POST['session_id'] ?? '');
        
        if (empty($text)) {
            wp_send_json_error(['message' => 'No text provided']);
        }
        
        // Truncate if too long — GPT-5.5 can handle large documents
        if (strlen($text) > 200000) {
            $text = substr($text, 0, 200000) . "

[Document truncated]";
        }
        
        // Build analysis prompt
        $prompt = "You are analyzing a business document titled: $filename
";
        $prompt .= "1. Summarize the business in a friendly way.
";
        $prompt .= "2. EXTRACT these specific fields into a JSON block at the end (regardless of document language):
";
        $prompt .= "   [[DATA]]
";
        $prompt .= "   {
";
        $prompt .= "     \"businessName\": \"...\",
";
        $prompt .= "     \"industry\": \"...\",
";
        $prompt .= "     \"offerings\": \"...\",
";
        $prompt .= "     \"targetAudience\": \"...\",
";
        $prompt .= "     \"pricing\": \"...\",
";
        $prompt .= "     \"goals\": \"...\"
";
        $prompt .= "   }
";
        $prompt .= "   [[/DATA]]
";

        $prompt .= "Document content:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
";
        $prompt .= "$text
";
        $prompt .= "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
";
        
        // Call AI
        $summary = $this->call_openai_api($prompt, 1000, false, false);
        
        if ($summary) {
            // Extract business info from AI Summary (which contains the [[DATA]] block)
            $extracted_info = $this->extract_business_info_from_text($summary);
            
            wp_send_json_success([
                'summary' => $summary,
                'extractedInfo' => $extracted_info
            ]);
        } else {
            wp_send_json_error(['message' => 'Analysis failed']);
        }
    }
    
    /**
     * Extract business information from text
     * Supports strict JSON block extraction [[DATA]]...[[/DATA]] for multilingual reliability
     */
    private function extract_business_info_from_text($text) {
        $info = [];
        
        // 1. Priority: Look for structured JSON block defined in system prompt
        // Format: [[DATA]]{"key": "value"}[[/DATA]]
        if (preg_match('/\[\[DATA\]\](.*?)\[\[\/DATA\]\]/s', $text, $matches)) {
            $json_str = trim($matches[1]);
            $decoded = json_decode($json_str, true);
            if (is_array($decoded)) {
                return $decoded;
            }
        }
        
        // 2. Fallback: Simple keyword extraction (Legacy English-only support)
        if (preg_match('/business name:?\s*([^
]+)/i', $text, $matches)) {
            $info['businessName'] = trim($matches[1]);
        }
        
        if (preg_match('/industry:?\s*([^
]+)/i', $text, $matches)) {
            $info['industry'] = trim($matches[1]);
        }
        
        if (preg_match('/target (market|audience):?\s*([^
]+)/i', $text, $matches)) {
            $info['targetAudience'] = trim($matches[2]);
        }
        
        return $info;
    }
}

// Initialize enhanced business wizard
VMO_Enhanced_Business_Wizard::get_instance();
