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

/**
 * MULTI-GPT MODEL INTEGRATION FOR SPECIALIZED TASKS
 * Each AI agent uses the optimal GPT model for their expertise
 */

// ══════════════════════════════════════════════════════════════
// ✅  SSE Streaming helper — used by both Jarvis and Consultation
// ══════════════════════════════════════════════════════════════
/**
 * Extract a useful OpenAI error message from a raw API response body.
 *
 * @param string $raw_body Raw HTTP response body
 * @param int    $http_code HTTP status code
 * @param string $fallback Fallback message if no specific error is found
 * @return string
 */
function vmo_extract_openai_error_message($raw_body, $http_code = 0, $fallback = '') {
    $raw_body = is_string($raw_body) ? trim($raw_body) : '';

    if ($raw_body !== '') {
        $decoded = json_decode($raw_body, true);
        $decoded_error = vmo_openai_decoded_error_message($decoded);
        if ($decoded_error !== '') {
            return $decoded_error;
        }

        foreach (preg_split('/\r?\n/', $raw_body) as $line) {
            $line = trim((string) $line);
            if ($line === '' || strpos($line, 'data: ') !== 0) {
                continue;
            }

            $json_str = substr($line, 6);
            if ($json_str === '[DONE]') {
                continue;
            }

            $decoded = json_decode($json_str, true);
            $decoded_error = vmo_openai_decoded_error_message($decoded);
            if ($decoded_error !== '') {
                return $decoded_error;
            }
        }

        if (stripos($raw_body, '<html') === false && stripos($raw_body, '<!doctype') === false && strlen($raw_body) <= 600) {
            return $raw_body;
        }
    }

    if ($fallback !== '') {
        return $fallback;
    }

    return $http_code > 0 ? "AI API error (HTTP {$http_code})" : 'AI API error';
}

if (!function_exists('vmo_openai_decoded_error_message')) {
    function vmo_openai_decoded_error_message($decoded, $fallback = '') {
        if (is_array($decoded)) {
            $error = $decoded['error'] ?? null;
            if (is_array($error)) {
                $message = $error['message'] ?? $error['detail'] ?? '';
                if (is_scalar($message) && trim((string) $message) !== '') {
                    return trim((string) $message);
                }
            } elseif (is_scalar($error) && trim((string) $error) !== '') {
                return trim((string) $error);
            }

            $message = $decoded['message'] ?? $decoded['detail'] ?? '';
            if (is_scalar($message) && trim((string) $message) !== '') {
                return trim((string) $message);
            }
        } elseif (is_scalar($decoded) && trim((string) $decoded) !== '') {
            return trim((string) $decoded);
        }

        return is_scalar($fallback) ? trim((string) $fallback) : '';
    }
}

if (!function_exists('vmo_openai_choice_message_content')) {
    function vmo_openai_choice_message_content($decoded) {
        if (!is_array($decoded)) {
            return '';
        }
        $choices = $decoded['choices'] ?? null;
        if (!is_array($choices) || !isset($choices[0]) || !is_array($choices[0])) {
            return '';
        }
        $message = $choices[0]['message'] ?? null;
        if (is_array($message) && isset($message['content']) && is_scalar($message['content'])) {
            return (string) $message['content'];
        }
        return '';
    }
}

if (!function_exists('vmo_openai_stream_delta_content')) {
    function vmo_openai_stream_delta_content($decoded) {
        if (!is_array($decoded)) {
            return '';
        }
        $choices = $decoded['choices'] ?? null;
        if (!is_array($choices) || !isset($choices[0]) || !is_array($choices[0])) {
            return '';
        }
        $delta = $choices[0]['delta'] ?? null;
        if (is_array($delta) && isset($delta['content']) && is_scalar($delta['content'])) {
            return (string) $delta['content'];
        }
        return '';
    }
}

if (!function_exists('vmo_openai_finish_reason')) {
    function vmo_openai_finish_reason($decoded) {
        if (!is_array($decoded)) {
            return 'unknown';
        }
        $choices = $decoded['choices'] ?? null;
        if (!is_array($choices) || !isset($choices[0]) || !is_array($choices[0])) {
            return 'unknown';
        }
        $reason = $choices[0]['finish_reason'] ?? 'unknown';
        return is_scalar($reason) && trim((string) $reason) !== '' ? (string) $reason : 'unknown';
    }
}

if (!function_exists('vmo_openai_resolved_model_value')) {
    function vmo_openai_resolved_model_value($resolved, $fallback) {
        if (is_array($resolved) && isset($resolved['model']) && is_scalar($resolved['model']) && trim((string) $resolved['model']) !== '') {
            return (string) $resolved['model'];
        }
        if (is_scalar($resolved) && trim((string) $resolved) !== '') {
            return (string) $resolved;
        }
        return (string) $fallback;
    }
}

if (!function_exists('vmo_model_supports_custom_temperature')) {
    function vmo_model_supports_custom_temperature($model) {
        $model = strtolower(trim((string) $model));
        if ($model === '') {
            return true;
        }

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

if (!function_exists('vmo_apply_safe_temperature_to_payload')) {
    function vmo_apply_safe_temperature_to_payload(array $payload, $model, $temperature) {
        unset($payload['temperature']);

        if (!vmo_model_supports_custom_temperature($model)) {
            unset($payload['top_p'], $payload['frequency_penalty'], $payload['presence_penalty']);
            return $payload;
        }

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

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

if (!function_exists('vmo_prepare_long_generation_runtime')) {
    function vmo_prepare_long_generation_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');
        }
    }
}

/**
 * Return the public-facing assistant label for the active chat experience.
 *
 * @param string $chat_type Assistant chat type
 * @return string
 */
function vmo_get_public_assistant_label($chat_type = 'jarvis') {
    return $chat_type === 'consultant' ? 'The consultation assistant' : 'Jarvis';
}

/**
 * Return a generic, brand-safe public error message for the active assistant.
 *
 * @param string $chat_type Assistant chat type
 * @param string $variant Public message variant
 * @return string
 */
function vmo_get_public_assistant_error_message($chat_type = 'jarvis', $variant = 'request_failed') {
    $assistant_label = vmo_get_public_assistant_label($chat_type);

    switch ($variant) {
        case 'unavailable':
            return $assistant_label . " isn't available right now. Please try again in a moment.";
        case 'timeout':
            return 'Sorry, that took too long. Please try again.';
        case 'request_failed':
        default:
            return $assistant_label . " couldn't complete that just now. Please try again in a moment.";
    }
}

/**
 * Build a live system-status context block so Jarvis can answer with full
 * awareness of the user's current VMO system state.
 */
function vmo_build_jarvis_system_context( int $user_id ): string {
    global $wpdb;
    $ctx = [];

    // ── Plan & Tokens ────────────────────────────────────────────
    $plan      = get_user_meta( $user_id, 'vmo_subscription_status', true ) ?: 'none';
    $tokens    = (int) get_user_meta( $user_id, 'vmo_token_balance', true );
    $ctx[]     = "Plan: {$plan} | Token balance: {$tokens}";

    // ── Phone / SMS status ───────────────────────────────────────
    $phone_active  = get_user_meta( $user_id, 'vmo_phone_service', true );
    $phone_number  = get_user_meta( $user_id, 'vmo_business_phone', true );
    $sms_status    = get_user_meta( $user_id, 'vmo_sms_campaign_status', true );
    $ctx[]         = 'Phone: ' . ( $phone_number ? "active ({$phone_number})" : 'not provisioned' )
                   . ' | SMS A2P: ' . ( $sms_status ?: 'not registered' );

    // ── Email status ─────────────────────────────────────────────
    $email_provisioned = get_user_meta( $user_id, 'vmo_zoho_email_provisioned', true );
    $brevo_key         = get_user_meta( $user_id, 'vmo_brevo_api_key', true );
    $ctx[]             = 'Email: ' . ( $email_provisioned ? 'provisioned' : 'not provisioned' )
                       . ' | Bulk email provider: ' . ( $brevo_key ? 'connected' : 'not connected' );

    // ── VMO Pay ─────────────────────────────────────────────────
    $stripe_id   = get_user_meta( $user_id, 'vmo_stripe_account_id', true );
    $stripe_ok   = get_user_meta( $user_id, 'vmo_stripe_onboarding_complete', true );
    $ctx[]       = 'VMO Pay: ' . ( $stripe_ok ? 'active' : ( $stripe_id ? 'pending onboarding' : 'not connected' ) );

    // ── Shipping ─────────────────────────────────────────────────
    $carriers     = get_user_meta( $user_id, 'vmo_shipping_settings', true );
    $active_count = 0;
    if ( is_array( $carriers ) ) {
        foreach ( $carriers as $c ) {
            if ( ! empty( $c['enabled'] ) ) { $active_count++; }
        }
    }
    $pending_table = $wpdb->prefix . 'vmo_internal_store_orders';
    $pending_orders = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM {$pending_table} WHERE user_id = %d AND fulfillment_status = 'pending'",
        $user_id
    ) );
    $ctx[] = "Shipping: {$active_count} carrier(s) enabled | {$pending_orders} pending shipment(s)";

    // ── Website / Publishing ─────────────────────────────────────
    $pub_sites = get_user_meta( $user_id, 'vmo_published_sites', true );
    $pub_count = is_array( $pub_sites ) ? count( $pub_sites ) : 0;
    $ctx[]     = "Published websites: {$pub_count}";

    // ── SEO ──────────────────────────────────────────────────────
    $seo_auto = get_user_meta( $user_id, 'vmo_seo_auto_execute', true );
    $ctx[]    = 'SEO auto-execute: ' . ( $seo_auto ? 'ON' : 'OFF' );

    // ── CRM snapshot ─────────────────────────────────────────────
    $contacts_table = $wpdb->prefix . 'vmo_contacts';
    $leads     = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM {$contacts_table} WHERE user_id = %d AND lifecycle_stage NOT IN ('customer')",
        $user_id
    ) );
    $customers = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM {$contacts_table} WHERE user_id = %d AND lifecycle_stage = 'customer'",
        $user_id
    ) );
    $ctx[] = "CRM: {$leads} lead(s), {$customers} customer(s)";

    // ── Store products ───────────────────────────────────────────
    $products_table = $wpdb->prefix . 'vmo_products';
    $product_count  = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM {$products_table} WHERE user_id = %d",
        $user_id
    ) );
    $ctx[] = "Store products: {$product_count}";

    // ── Today's tasks ────────────────────────────────────────────
    $today     = current_time( 'Y-m-d' );
    $tasks_tbl = $wpdb->prefix . 'vmo_daily_activities';
    $tasks_due = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM {$tasks_tbl} WHERE user_id = %d AND scheduled_date = %s AND status != 'done'",
        $user_id,
        $today
    ) );
    $tasks_done = (int) $wpdb->get_var( $wpdb->prepare(
        "SELECT COUNT(*) FROM {$tasks_tbl} WHERE user_id = %d AND scheduled_date = %s AND status = 'done'",
        $user_id,
        $today
    ) );
    $ctx[] = "Today: {$tasks_due} task(s) remaining, {$tasks_done} completed";

    // ── Feature navigation map (compact) ─────────────────────────
    $nav = "FEATURE LOCATIONS: "
         . "Overview=Overview tab | Today/Tasks=Today sub-tab | Strategic Dashboard=Strategic tab | "
         . "Reports=Reports tab | Phone & SMS=Phone tab | CRM=CRM tab | "
         . "SEO=SEO tab | Email=Emails tab | Shipping=Shipping tab | "
         . "Documents=Documents tab | CFO/Finance=CFO tab | "
         . "Promotion=Promotion tab (Coming Soon) | "
         . "Bulk Email=Tools>Bulk Email Marketing | Power Dialer=Tools>Autonomous Robo Sales | "
         . "Bulk SMS=Tools>Bulk SMS Marketing | Executive Report=Tools>Business Status Report | "
         . "PDF Publisher=Tools>HTML to PDF Publisher | "
         . "VMO Pay/Stripe=CFO tab>VMO Pay section | "
         . "Plans/Subscription=header plan badge or Plans page | "
         . "Business Wizard=red Business Wizard button (top) | "
         . "Publish=Publish button (header) | Save=Save button (header) | "
         . "Website Editor=Live Preview (main area)";

    $block  = "\n\n**LIVE SYSTEM STATUS** (queried just now):\n" . implode( "\n", $ctx );
    $block .= "\n\n**" . $nav . "**";

    return $block;
}

/**
 * Resolve the OpenAI API key from configured sources using one consistent selection path.
 * Prefers a valid option value, then falls back to a valid constant override.
 *
 * @return array{key:string,source:string,valid:bool}
 */
function vmo_resolve_openai_api_key() {
    if (defined('VMO_CLIENT_MODE') && VMO_CLIENT_MODE === true) {
        return [
            'key' => 'vmo-client-proxy',
            'source' => 'client_proxy',
            'valid' => true,
        ];
    }

    $candidates = [];

    $option_key = trim((string) get_option('vmo_openai_api_key', ''));
    if ($option_key !== '') {
        $candidates['option'] = $option_key;
    }

    if (defined('VMO_OPENAI_API_KEY')) {
        $constant_key = trim((string) VMO_OPENAI_API_KEY);
        if ($constant_key !== '') {
            $candidates['constant'] = $constant_key;
        }
    }

    foreach ($candidates as $source => $candidate) {
        if (preg_match('/^sk-[a-zA-Z0-9\-_]{20,}$/', $candidate)) {
            return [
                'key' => $candidate,
                'source' => $source,
                'valid' => true,
            ];
        }
    }

    $first_source = function_exists('array_key_first') ? array_key_first($candidates) : null;
    $first_key = $first_source !== null ? (string) $candidates[$first_source] : '';

    return [
        'key' => $first_key,
        'source' => $first_source ?: 'none',
        'valid' => false,
    ];
}

function vmo_openai_uses_master_proxy() {
    return defined('VMO_CLIENT_MODE') && VMO_CLIENT_MODE === true && function_exists('vmo_api_request');
}

function vmo_openai_chat_completion_request(array $payload, $api_key, $timeout = 60) {
    $args = [
        'headers' => [
            'Content-Type' => 'application/json',
        ],
        'body' => wp_json_encode($payload),
        'timeout' => $timeout,
        'vmo_user_id' => get_current_user_id(),
    ];

    if (vmo_openai_uses_master_proxy()) {
        return vmo_api_request('openai', 'v1/chat/completions', $args, 'POST');
    }

    $args['headers']['Authorization'] = 'Bearer ' . $api_key;
    unset($args['vmo_user_id']);

    return wp_remote_post('https://api.openai.com/v1/chat/completions', $args);
}

function vmo_stream_openai_via_master_proxy($messages, $api_key, $model, $max_tokens = 1000, $temperature = 0.7, $fallback_model = null) {
    if (function_exists('vmo_resolve_model')) {
        $resolved = vmo_resolve_model(get_current_user_id(), $model);
        $model = vmo_openai_resolved_model_value($resolved, $model);
    }

    $payload = [
        'model' => $model,
        'messages' => $messages,
        'top_p' => 0.9,
    ];
    $payload = array_merge($payload, function_exists('vmo_get_token_param') ? vmo_get_token_param($model, $max_tokens) : ['max_completion_tokens' => $max_tokens]);
    $payload = vmo_apply_safe_temperature_to_payload($payload, $model, $temperature);

    $response = vmo_openai_chat_completion_request($payload, $api_key, 90);

    if ((is_wp_error($response) || wp_remote_retrieve_response_code($response) >= 400) && !empty($fallback_model) && $fallback_model !== $model) {
        error_log("VMO: Proxied streaming conversation model '{$model}' failed. Retrying with fallback '{$fallback_model}'.");
        return vmo_stream_openai_via_master_proxy($messages, $api_key, $fallback_model, $max_tokens, $temperature, null);
    }

    if (is_wp_error($response)) {
        return ['error' => $response->get_error_message(), 'text' => ''];
    }

    $http_code = (int) wp_remote_retrieve_response_code($response);
    $raw_body = wp_remote_retrieve_body($response);
    if ($http_code >= 400) {
        return ['error' => vmo_extract_openai_error_message($raw_body, $http_code), 'text' => ''];
    }

    $data = json_decode($raw_body, true);
    $content = trim(vmo_openai_choice_message_content($data));
    if ($content === '') {
        $detail = vmo_openai_decoded_error_message($data, 'AI API returned unexpected format.');
        return ['error' => $detail, 'text' => ''];
    }

    echo 'data: ' . wp_json_encode(['t' => $content]) . "\n\n";
    if (ob_get_level()) ob_flush();
    flush();

    return ['error' => null, 'text' => $content];
}

/**
 * Stream an OpenAI chat completion as Server-Sent Events.
 * Must be called AFTER SSE headers have been set and output buffers cleaned.
 *
 * @param array       $messages       OpenAI messages array
 * @param string      $api_key        OpenAI API key
 * @param string      $model          Model name (gpt-5.5, gpt-5.5-mini, etc.)
 * @param int         $max_tokens     Max completion tokens
 * @param float       $temperature
 * @param string|null $fallback_model Optional fallback model if the primary model fails before streaming tokens
 * @return array ['error' => string|null, 'text' => string]
 */
function vmo_stream_openai($messages, $api_key, $model, $max_tokens = 1000, $temperature = 0.7, $fallback_model = null) {
    if (vmo_openai_uses_master_proxy()) {
        return vmo_stream_openai_via_master_proxy($messages, $api_key, $model, $max_tokens, $temperature, $fallback_model);
    }

    // ── Guardrail 5A: Adaptive model fallback ──
    // If caller passes a strategic model but user balance is low, auto-downgrade
    if (function_exists('vmo_resolve_model')) {
        $resolved = vmo_resolve_model(get_current_user_id(), $model);
        $model = vmo_openai_resolved_model_value($resolved, $model);
    }

    $data = [
        'model'       => $model,
        'messages'    => $messages,
        'stream'      => true,
    ];
    $data = vmo_apply_safe_temperature_to_payload($data, $model, $temperature);

    // GPT-5.x → max_completion_tokens; older models → max_tokens
    if (strpos($model, 'gpt-5') !== false || strpos($model, 'o1') !== false || strpos($model, 'o3') !== false) {
        $data['max_completion_tokens'] = $max_tokens;
    } else {
        $data['max_tokens'] = $max_tokens;
    }

    // ═══════════════════════════════════════════════════════════════
    // ✅ TRUE REAL-TIME STREAMING via cURL CURLOPT_WRITEFUNCTION
    //
    // Previously this used wp_remote_post() which buffers the ENTIRE
    // OpenAI response before returning — meaning all tokens arrived
    // at the client simultaneously after a long wait.
    //
    // Now we use cURL directly with a write callback that echoes each
    // token to the browser THE INSTANT it arrives from OpenAI.
    // This enables progressive TTS to start speaking the first
    // sentence while the rest of the response is still generating.
    // ═══════════════════════════════════════════════════════════════

    $full_response = '';
    $sse_line_buffer = '';   // Accumulates partial SSE lines across cURL chunks
    $raw_response = '';
    $stream_error = '';

    $ch = curl_init('https://api.openai.com/v1/chat/completions');
    if (!$ch) {
        // cURL unavailable — fall back to wp_remote_post (buffered)
        return vmo_stream_openai_fallback($messages, $api_key, $model, $max_tokens, $temperature, $fallback_model);
    }

    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_HTTPHEADER     => [
            'Authorization: Bearer ' . $api_key,
            'Content-Type: application/json',
        ],
        CURLOPT_POSTFIELDS     => json_encode($data),
        CURLOPT_RETURNTRANSFER => false,  // Don't buffer — use write callback
        CURLOPT_TIMEOUT        => 120,
        CURLOPT_CONNECTTIMEOUT => 15,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS      => 3,

        // ✅ KEY: Write callback fires for EVERY chunk received from OpenAI
        CURLOPT_WRITEFUNCTION  => function ($ch, $chunk) use (&$full_response, &$sse_line_buffer, &$raw_response, &$stream_error) {
            $raw_response .= $chunk;
            $sse_line_buffer .= $chunk;

            // Split on newlines — last element may be an incomplete line
            $lines = explode("\n", $sse_line_buffer);
            $sse_line_buffer = array_pop($lines); // Keep incomplete tail

            foreach ($lines as $line) {
                $line = trim($line);
                if ($line === '' || strpos($line, 'data: ') !== 0) continue;
                $json_str = substr($line, 6);
                if ($json_str === '[DONE]') continue;

                $json = json_decode($json_str, true);

                $token = vmo_openai_stream_delta_content($json);
                if ($token !== '') {
                    $full_response .= $token;

                    // ✅ FIX: Filter out [[PHASE_COMPLETE]] markers from streamed tokens
                    $display_token = str_replace('[[PHASE_COMPLETE]]', '', $token);
                    if (strpos($full_response, '[[PHASE_COMPLETE]]') !== false) {
                        $full_response = str_replace('[[PHASE_COMPLETE]]', '', $full_response);
                    }

                    if ($display_token !== '') {
                        echo 'data: ' . wp_json_encode(['t' => $display_token]) . "\n\n";
                        if (ob_get_level()) ob_flush();
                        flush();
                    }
                }

                // Surface API errors that arrive mid-stream
                $decoded_error = vmo_openai_decoded_error_message($json);
                if ($decoded_error !== '') {
                    $stream_error = $decoded_error;
                }
            }

            return strlen($chunk); // Must return bytes processed
        },
    ]);

    $curl_ok = curl_exec($ch);
    $curl_err = curl_error($ch);
    $http_code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    // Process any remaining data in the buffer
    if ($sse_line_buffer !== '') {
        $line = trim($sse_line_buffer);
        if ($line !== '' && strpos($line, 'data: ') === 0) {
            $json_str = substr($line, 6);
            if ($json_str !== '[DONE]') {
                $json = json_decode($json_str, true);
                $token = vmo_openai_stream_delta_content($json);
                if ($token !== '') {
                    $full_response .= $token;
                    $display_token = str_replace('[[PHASE_COMPLETE]]', '', $token);
                    if ($display_token !== '') {
                        echo 'data: ' . wp_json_encode(['t' => $display_token]) . "\n\n";
                        if (ob_get_level()) ob_flush();
                        flush();
                    }
                }
                $decoded_error = vmo_openai_decoded_error_message($json);
                if ($decoded_error !== '') {
                    $stream_error = $decoded_error;
                }
            }
        }
    }

    if (!$curl_ok && $full_response === '') {
        $err_msg = $stream_error ?: vmo_extract_openai_error_message($raw_response, $http_code, $curl_err ?: "AI API error (HTTP {$http_code})");
        if (!empty($fallback_model) && $fallback_model !== $model) {
            error_log("VMO: Streaming conversation model '{$model}' failed. Retrying with fallback '{$fallback_model}'.");
            return vmo_stream_openai($messages, $api_key, $fallback_model, $max_tokens, $temperature, null);
        }
        return ['error' => $err_msg, 'text' => ''];
    }

    if ($http_code >= 400 && $full_response === '') {
        $err_msg = $stream_error ?: vmo_extract_openai_error_message($raw_response, $http_code);
        if (!empty($fallback_model) && $fallback_model !== $model) {
            error_log("VMO: Streaming conversation model '{$model}' returned HTTP {$http_code}. Retrying with fallback '{$fallback_model}'.");
            return vmo_stream_openai($messages, $api_key, $fallback_model, $max_tokens, $temperature, null);
        }
        return ['error' => $err_msg, 'text' => ''];
    }

    return ['error' => null, 'text' => $full_response];
}

/**
 * Fallback streaming function using wp_remote_post (buffered).
 * Used only if cURL extension is unavailable.
 */
function vmo_stream_openai_fallback($messages, $api_key, $model, $max_tokens = 1000, $temperature = 0.7, $fallback_model = null) {
    $data = [
        'model'       => $model,
        'messages'    => $messages,
        'stream'      => true,
    ];
    $data = vmo_apply_safe_temperature_to_payload($data, $model, $temperature);

    if (strpos($model, 'gpt-5') !== false || strpos($model, 'o1') !== false || strpos($model, 'o3') !== false) {
        $data['max_completion_tokens'] = $max_tokens;
    } else {
        $data['max_tokens'] = $max_tokens;
    }

    $full_response = '';

    $wp_response = wp_remote_post('https://api.openai.com/v1/chat/completions', [
        'timeout' => 90,
        'headers' => [
            'Authorization' => 'Bearer ' . $api_key,
            'Content-Type'  => 'application/json',
        ],
        'body' => wp_json_encode($data),
    ]);

    if (is_wp_error($wp_response)) {
        if (!empty($fallback_model) && $fallback_model !== $model) {
            error_log("VMO: Buffered streaming model '{$model}' failed. Retrying with fallback '{$fallback_model}'.");
            return vmo_stream_openai_fallback($messages, $api_key, $fallback_model, $max_tokens, $temperature, null);
        }
        return ['error' => $wp_response->get_error_message(), 'text' => ''];
    }

    $http_code = wp_remote_retrieve_response_code($wp_response);
    $raw_body  = wp_remote_retrieve_body($wp_response);
    $stream_error = '';

    if ($http_code >= 400 && empty($raw_body)) {
        if (!empty($fallback_model) && $fallback_model !== $model) {
            error_log("VMO: Buffered streaming model '{$model}' returned HTTP {$http_code}. Retrying with fallback '{$fallback_model}'.");
            return vmo_stream_openai_fallback($messages, $api_key, $fallback_model, $max_tokens, $temperature, null);
        }
        return ['error' => "AI API error (HTTP {$http_code})", 'text' => ''];
    }

    $lines = explode("\n", $raw_body);
    foreach ($lines as $line) {
        $line = trim($line);
        if ($line === '' || strpos($line, 'data: ') !== 0) continue;
        $json_str = substr($line, 6);
        if ($json_str === '[DONE]') continue;
        $json = json_decode($json_str, true);
        $token = vmo_openai_stream_delta_content($json);
        if ($token !== '') {
            $full_response .= $token;
            $display_token = str_replace('[[PHASE_COMPLETE]]', '', $token);
            if (strpos($full_response, '[[PHASE_COMPLETE]]') !== false) {
                $full_response = str_replace('[[PHASE_COMPLETE]]', '', $full_response);
            }
            if ($display_token !== '') {
                echo 'data: ' . wp_json_encode(['t' => $display_token]) . "\n\n";
                if (ob_get_level()) ob_flush();
                flush();
            }
        }
        $decoded_error = vmo_openai_decoded_error_message($json);
        if ($decoded_error !== '') {
            $stream_error = $decoded_error;
        }
    }

    if ($http_code >= 400 && $full_response === '') {
        $err_msg = $stream_error ?: vmo_extract_openai_error_message($raw_body, $http_code);
        if (!empty($fallback_model) && $fallback_model !== $model) {
            error_log("VMO: Buffered streaming model '{$model}' failed with HTTP {$http_code}. Retrying with fallback '{$fallback_model}'.");
            return vmo_stream_openai_fallback($messages, $api_key, $fallback_model, $max_tokens, $temperature, null);
        }
        return ['error' => $err_msg, 'text' => ''];
    }

    return ['error' => null, 'text' => $full_response];
}

/**
 * Get optimal GPT model for specific task type
 */
function vmo_get_optimal_model($task_type = 'general', $context = []) {
    if (function_exists('vmo_get_routed_model')) {
        if (!array_key_exists('skip_log', $context)) {
            $context['skip_log'] = true;
        }
        return vmo_get_routed_model($task_type, $context);
    }

    return defined('VMO_OPENAI_MODEL') ? VMO_OPENAI_MODEL : 'gpt-5.5';
}

/**
 * Enhanced OpenAI API call with model optimization
 */
function vmo_call_specialized_ai($prompt, $task_type = 'general', $additional_params = []) {
    $api_key_bundle = function_exists('vmo_resolve_openai_api_key')
        ? vmo_resolve_openai_api_key()
        : [
            'key' => get_option('vmo_openai_api_key', ''),
            'valid' => (bool) get_option('vmo_openai_api_key', ''),
        ];
    $api_key = (string) ($api_key_bundle['key'] ?? '');
    if (!$api_key || empty($api_key_bundle['valid'])) {
        return ['error' => 'AI API key is missing'];
    }
    
    // ✅ TOKEN DEDUCTION v8.1.0: Charge for specialized AI calls based on task type
    $user_id = get_current_user_id();
    $token_costs = [
        'strategy_analysis' => 800,
        'legal_documents' => 700,
        'seo_optimization' => 500,
        'content_creation' => 600,
        'business_planning' => 800,
        'website_building' => 1000,
        'consultation' => 300,
        'competitor_research' => 600,
        'general' => 400
    ];
    $token_cost = $token_costs[$task_type] ?? 400;
    
    if (function_exists('vmo_deduct_tokens')) {
        if (!vmo_deduct_tokens($user_id, $token_cost, $task_type, 'Specialized AI: ' . $task_type)) {
            return ['error' => 'Insufficient tokens. Please top up your balance.'];
        }
    }

    $route_context = [
        'prompt' => $prompt,
        'user_id' => $user_id,
        'plan' => get_user_meta($user_id, 'vmo_subscription_status', true),
        'requires_generation' => in_array($task_type, ['content_creation', 'website_building', 'legal_documents'], true),
    ];
    $route = function_exists('vmo_get_openai_route')
        ? vmo_get_openai_route($task_type, $route_context)
        : ['model' => vmo_get_optimal_model($task_type, $route_context), 'fallback_model' => defined('VMO_OPENAI_FAST_MODEL') ? VMO_OPENAI_FAST_MODEL : null, 'task' => $task_type];

    $model = $route['model'];
    $fallback_model = $route['fallback_model'] ?? null;
    $actual_model_used = $model;
    
    // Task-specific configurations
    // Token limits per task type (temperature + token budget)
    $config = [
        'strategy_analysis' => ['temperature' => 0.3, '_token_limit' => 4000],
        'legal_documents' => ['temperature' => 0.1, '_token_limit' => 4000],
        'seo_optimization' => ['temperature' => 0.2, '_token_limit' => 3000],
        'content_creation' => ['temperature' => 0.7, '_token_limit' => 4000],
        'business_planning' => ['temperature' => 0.3, '_token_limit' => 4000],
        'website_building' => ['temperature' => 0.4, '_token_limit' => 4000],
        'consultation' => ['temperature' => 0.5, '_token_limit' => 2000],
        'competitor_research' => ['temperature' => 0.2, '_token_limit' => 4000],
    ];

    $task_config = $config[$task_type] ?? ['temperature' => 0.7, '_token_limit' => 2000];

    // ✅ FIX: GPT-5.x requires max_completion_tokens, not max_tokens
    $_tl = $task_config['_token_limit'] ?? 2000;
    unset($task_config['_token_limit']);
    $task_config += function_exists('vmo_get_token_param') ? vmo_get_token_param($model, $_tl) : ['max_tokens' => $_tl];
    
    // Attach consultation profile context when available
    $profile = array();
    try {
        if (function_exists('vmo_get_consultation_profile')) {
            $profile = vmo_get_consultation_profile();
        }
    } catch (Exception $e) {
        error_log('VMO: Could not load consultation profile: ' . $e->getMessage());
    }
    
    $profile_system = '';
    if (!empty($profile)) {
        $profile_system = "BUSINESS CONSULTATION PROFILE (use this as ground truth):\n" . substr(wp_json_encode($profile, JSON_PRETTY_PRINT), 0, 6000);
    }

    $body = array_merge([
        'model' => $model,
        'messages' => [
            [
                'role' => 'system',
                'content' => trim(vmo_get_system_prompt($task_type) . "\n\n" . $profile_system)
            ],
            [
                'role' => 'user', 
                'content' => $prompt
            ]
        ],
    ], $task_config, $additional_params);
    $body = vmo_apply_safe_temperature_to_payload($body, $model, $body['temperature'] ?? null);

    $response = vmo_openai_chat_completion_request($body, $api_key, 120);

    if ((is_wp_error($response) || wp_remote_retrieve_response_code($response) >= 400) && !empty($fallback_model) && $fallback_model !== $model) {
        error_log("VMO: Model '$model' failed in vmo_call_specialized_ai. Retrying with {$fallback_model}.");

        $body['model'] = $fallback_model;
        $fallback_temperature = $body['temperature'] ?? ($task_config['temperature'] ?? null);
        unset($body['max_tokens'], $body['max_completion_tokens']);
        $body += function_exists('vmo_get_token_param') ? vmo_get_token_param($fallback_model, $_tl) : ['max_tokens' => $_tl];
        $body = vmo_apply_safe_temperature_to_payload($body, $fallback_model, $fallback_temperature);

        $response = vmo_openai_chat_completion_request($body, $api_key, 120);
        $actual_model_used = $fallback_model;
    }

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

    $response_body = wp_remote_retrieve_body($response);
    $data = json_decode($response_body, true);

    $specialized_content = vmo_openai_choice_message_content($data);
    if ($specialized_content !== '') {
        return [
            'success' => true,
            'content' => $specialized_content,
            'model_used' => $actual_model_used,
            'task_type' => $task_type,
            'route_used' => $route['task'] ?? $task_type
        ];
    }

    return ['error' => 'Invalid API response', 'raw_response' => $data];
}

/**
 * Get specialized system prompts for each task type
 */
function vmo_get_system_prompt($task_type) {
    $prompts = [
        'strategy_analysis' => "You are a McKinsey-level strategy consultant with 20+ years experience. Provide data-driven insights, specific competitor analysis, and actionable strategic recommendations with realistic timelines and market share projections.",
        
        'legal_documents' => "You are a corporate legal specialist with expertise in business formation, compliance, and risk management. Generate accurate, comprehensive legal documents appropriate for the specific business type and jurisdiction.",
        
        'seo_optimization' => "You are an SEO expert specializing in technical optimization, local search, and competitive analysis. Provide specific, implementable SEO strategies that will dominate search rankings.",
        
        'content_creation' => "You are a conversion-focused copywriter and content strategist. Create compelling, persuasive content that converts visitors into customers while maintaining brand voice and expertise.",
        
        'business_planning' => "You are a business planning expert with MBA-level financial modeling skills. Create realistic financial projections, growth strategies, and comprehensive business plans with specific metrics and timelines.",
        
        'website_building' => "You are a premium digital experience director and full-stack web developer. Generate cinematic, conversion-optimized websites that feel custom, media-rich, mobile-first, and SEO-optimized. Avoid brochure-basic layouts. Use coherent text color systems, premium motion, layered compositions, glass or translucent CTA surfaces, and strong visual storytelling. Never use multicolor or gradient text for headings or body copy.",
        
        'consultation' => "You are a world-class business consultant combining McKinsey expertise with startup mentorship. Ask probing questions that reveal business potential and guide strategic thinking.",
        
        'competitor_research' => "You are a competitive intelligence analyst. Provide detailed competitor research with specific company names, strategies, market positioning, and actionable insights for competitive advantage."
    ];

    return $prompts[$task_type] ?? "You are a helpful AI assistant specializing in business and website development.";
}

/**
 * Replace [GENERATED_IMAGE: desc] / [GENERATED_VIDEO: desc]
 * with placeholder DIVs your CSS/JS can style & hook into.
 * Each placeholder gets a unique media ID for replace button functionality.
 */
function vmo_process_generated_images( $html ) {
    // Image placeholders with unique IDs
    $html = preg_replace_callback(
        '/\[GENERATED_IMAGE:\s*(.*?)\]/i',
        function ( $m ) {
            $desc = sanitize_text_field( $m[1] );
            $mediaId = 'media_' . wp_generate_password(8, false);
            return '<div class="vmo-media-placeholder vmo-media-placeholder-image" data-type="image" data-media-id="' 
                . esc_attr( $mediaId ) . '" aria-label="'
                . esc_attr( $desc )
                . '" data-vmo-generate="true" data-vmo-prompt="'
                . esc_attr( $desc )
                . '"><span>Image<br><small>' . esc_html( $desc ) . '</small></span></div>';
        },
        $html
    );
    // Video placeholders with unique IDs
    $html = preg_replace_callback(
        '/\[GENERATED_VIDEO:\s*(.*?)\]/i',
        function ( $m ) {
            $desc = sanitize_text_field( $m[1] );
            $mediaId = 'media_' . wp_generate_password(8, false);
            return '<div class="vmo-media-placeholder vmo-media-placeholder-video" data-type="video" data-media-id="' 
                . esc_attr( $mediaId ) . '" aria-label="'
                . esc_attr( $desc )
                . '" data-vmo-sora="true" data-vmo-sora-prompt="'
                . esc_attr( $desc )
                . '"><span>Video<br><small>' . esc_html( $desc ) . '</small></span></div>';
        },
        $html
    );
    return $html;
}

/**
 * Master system prompt: Maximum creativity, intelligence, and sophisticated web design capabilities.
 */
$VMO_STYLE_DIRECTIVES = <<<'STYLE'
You are Jerreme, an advanced AI with exceptional creative abilities, expert copywriting skills, and professional knowledge in modern web design, content creation, and user experience. You MUST follow instructions EXACTLY and provide complete, professional copy content as requested.

**CORE INTELLIGENCE CAPABILITIES**:

1. **PROFESSIONAL COPYWRITING**: Write compelling, engaging, and professional copy for any topic, industry, or business. Generate complete articles, blog posts, marketing copy, and business content with appropriate tone, style, and length as specified by the user.

2. **CONTENT STRUCTURE MASTERY**: Create well-organized content with proper headings, subheadings, paragraphs, lists, and logical flow. Always include engaging introductions, detailed body content, and strong conclusions.

3. **INDUSTRY EXPERTISE**: Demonstrate deep knowledge across all industries and topics. Research-level understanding of business, technology, photography, automotive, healthcare, finance, and any subject matter requested.

4. **PRECISE WORD COUNT CONTROL**: Generate content to exact specifications - whether user wants 500 words, 2000 words, or any specific length. Always match the requested word count or character count precisely.

5. **TONE AND STYLE ADAPTATION**: Adapt writing style to match user requirements - professional, casual, technical, marketing-focused, educational, persuasive, or any specified tone.

6. **COMPLETE SECTION GENERATION**: When creating sections like "features," "about us," "services," etc., provide comprehensive content with detailed descriptions, benefits, and engaging copy that businesses can use directly.

**CONTENT GENERATION RULES**:
- **COMPLETE COPY**: Always provide full, professional copy content for every section, feature, or element requested
- **DETAILED DESCRIPTIONS**: Write comprehensive descriptions, not just placeholders or bullet points
- **ENGAGING HEADLINES**: Create compelling headlines and subheadings that grab attention
- **CALL-TO-ACTION**: Include strong calls-to-action where appropriate
- **BUSINESS FOCUS**: When writing for businesses, emphasize benefits, solutions, and competitive advantages
- **COMPREHENSIVE COVERAGE**: Cover all aspects of the requested topic thoroughly
- **TEXT COLOR DISCIPLINE**: Keep headings and body copy on a coherent single text-color system. Never use rainbow text, multiple heading colors, or gradient-filled text.

**TECHNICAL MASTERY**: Expert in HTML5, CSS3, modern JavaScript (ES6+), React 18, Next.js App Router, popular frameworks (GSAP, Splide, AOS, Three.js, Framer Motion, etc.), and advanced CSS techniques (Grid, Flexbox, custom properties, animations, transforms). Structure React components using Next.js conventions ('use client' directives, page/layout patterns).

**CRITICAL EXECUTION RULES**:
- Follow instructions with surgical precision
- Generate complete, professional copy content as requested
- Never add explanatory text unless requested
- Understand context and user intent perfectly
- Be as intelligent as the most advanced AI models
- Users should feel they're working with the smartest AI possible
- Match exact word count or length specifications when provided

**ELEMENT ISOLATION & PROTECTION**:
- **PRESERVE OTHER ELEMENTS**: NEVER modify, move, or affect elements that are not being specifically targeted
- **SURGICAL MODIFICATIONS**: Only change the exact element or section being refined
- **Z-INDEX PRESERVATION**: Maintain existing z-index layers unless specifically asked to change them
- **CONTENT ISOLATION**: When modifying one element, ensure surrounding elements remain untouched
- **LAYER MANAGEMENT**: Allow multiple elements to coexist on the same spot without conflicts through proper z-index management
- **REFINEMENT BOUNDARIES**: Changes should be confined to the specific element or section mentioned in the user's request

**REFINEMENT PRECISION RULES**:
- **EXACT TARGET MODIFICATIONS**: When user says "change the button color in section 2", ONLY modify that specific button, leave everything else untouched
- **SECTION BOUNDARY RESPECT**: When modifying section 2, NEVER affect sections 1, 3, 4, etc.
- **ELEMENT-LEVEL PRECISION**: When changing one flip card, preserve all other flip cards exactly as they are
- **PRESERVE FUNCTIONALITY**: When replacing images, maintain the existing HTML structure and functionality
- **NO SCOPE CREEP**: Never add sections, elements, or content not explicitly requested
- **MAINTAIN STATE**: Preserve all existing content, styling, and interactions unless specifically asked to change them

**MEDIA REPLACEMENT PROTECTION**:
- **PRESERVE HTML STRUCTURE**: When user replaces image in flip card, maintain the existing div structure, classes, and functionality
- **PROTECT SIBLINGS**: Changing one element must never affect its siblings or parent containers
- **MAINTAIN FLIP FUNCTIONALITY**: Image replacement should not break flip animations or interactions
- **PRESERVE TEXT AREAS**: Keep text content areas separate from image areas during media replacement

**SEMANTIC MARKUP & STRUCTURE**
- Always wrap sections in: `<div class="vmo-section" id="section-N"><!-- SECTION N --> ... <!-- END SECTION N --></div>`
- Use semantic HTML5 elements: `<header>`, `<main>`, `<section>`, `<article>`, `<aside>`, `<nav>`, `<footer>`
- Proper heading hierarchy (`<h1>`, `<h2>`, `<h3>`) with meaningful content
- Rich text formatting with `<p>`, `<ul>`, `<ol>`, `<blockquote>`, `<strong>`, `<em>`
- Interactive elements: `<button class="vmo-btn">`, `<form>`, `<input>`, `<textarea>`

**MEDIA PLACEHOLDER SYSTEM - CRITICAL**
- For **every** image: `[GENERATED_IMAGE: Detailed description of the image needed]`
- For **every** video: `[GENERATED_VIDEO: Detailed description of the video content]`
- Make descriptions specific and contextual to the content
- NEVER use `<img>` or `<video>` tags directly - only placeholders
- ALWAYS generate placeholders for carousels, galleries, hero images, backgrounds, logos, etc.
- For carousels: Generate multiple placeholders with exact counts (e.g., 10 placeholders for 10-image carousel)
- For interactive elements: Ensure placeholders behave like final media (timing, transitions, etc.)

**MEDIA PLACEHOLDER DIMENSIONS & STYLING - ESSENTIAL**:
- **MAINTAIN ASPECT RATIOS**: Every media placeholder must have explicit dimensions that match its intended use
- **ECOMMERCE PRODUCT IMAGES**: Use 300x300px for grid thumbnails, 600x600px for hero product images, 150x150px for gallery thumbnails
- **HERO SECTION IMAGES**: Use full viewport dimensions (100vw, 70vh) with proper object-fit cover
- **CARD IMAGES**: Use consistent aspect ratios (16:9 for landscape, 1:1 for square, 4:3 for traditional)
- **ICON PLACEHOLDERS**: Use 80x80px for feature icons, 40x40px for small icons, 120x120px for large feature icons
- **PLACEHOLDER STYLING**: Include these critical CSS properties on every placeholder:
  ```css
  .vmo-media-placeholder {
    width: [specific-width];
    height: [specific-height];
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    border-radius: 8px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    font-weight: bold;
    text-align: center;
    object-fit: cover;
    transition: all 0.3s ease;
  }
  ```
- **PERFECT REPLACEMENT BEHAVIOR**: Placeholders must be styled so that when replaced with actual media, the layout remains identical
- **RESPONSIVE PLACEHOLDERS**: Use CSS that maintains proportions across all device sizes
- **CONSISTENT CONTAINER STRUCTURE**: Wrap placeholders in proper containers that preserve layout integrity

**FLIP CARD & INTERACTIVE ELEMENT STRUCTURE**:
- **SEPARATE IMAGE AND TEXT AREAS**: In flip cards, keep image placeholders in dedicated containers separate from text content
- **PRESERVE FLIP STRUCTURE**: Use proper front/back div structure for flip animations
- **MAINTAIN HOVER FUNCTIONALITY**: Ensure flip interactions work correctly with placeholders
- **PROTECT TEXT CONTENT**: Keep feature names, descriptions, and details in separate text-only containers
- **EXAMPLE FLIP CARD STRUCTURE**:
```html
<div class="flip-card">
  <div class="flip-card-inner">
    <div class="flip-card-front">
      <div class="image-container">[GENERATED_IMAGE: BMW 7 Series exterior detail]</div>
      <h3>Advanced Lighting</h3>
    </div>
    <div class="flip-card-back">
      <h3>Advanced Lighting Technology</h3>
      <p>Detailed description of the lighting features...</p>
    </div>
  </div>
</div>
```

**SECTION-AWARE OPERATIONS**
- **SECTION NUMBERING**: Always maintain sequential section numbers (1, 2, 3, etc.)
- **SECTION MODIFICATION**: When asked to modify "section 3", only change that specific section
- **SECTION INSERTION**: When inserting into "section 2 background", modify existing section 2
- **NEW SECTION CREATION**: Only when explicitly requested or when logical
- **SECTION POSITIONING**: When creating new sections, place them where instructed (above/below specific sections)
- **SECTION RENUMBERING**: After any structural changes, renumber all sections sequentially

**ADVANCED VISUAL TECHNIQUES**
- **Parallax & Scroll Effects**: Implement with GSAP ScrollTrigger, CSS transforms, and scroll-timeline
- **Complex Animations**: Use CSS animations, GSAP, and custom JavaScript for sophisticated interactions
- **Responsive Design**: Mobile-first approach with sophisticated breakpoints
- **Visual Hierarchy**: Expert typography, spacing, and color relationships
- **Micro-interactions**: Hover effects, loading states, and smooth transitions
- **PREMIUM SURFACES**: Use glass, translucent, editorial, or dimensional button and card treatments instead of ordinary flat controls
- **MEDIA-LED STORYTELLING**: Use overlays, depth, and cinematic media composition so the website feels premium rather than brochure-basic

**COMPLEX LAYOUT CAPABILITIES**
- **Advanced Grid Systems**: Custom CSS Grid and Flexbox layouts
- **Layered Compositions**: Z-index management, overlays, and complex positioning
- **Background Techniques**: Video backgrounds, image overlays, gradient combinations
- **Background Image Placement**: CRITICAL - When inserting background images, ensure they are positioned with proper CSS:
  - Use `background-image: url()` on section elements with MANDATORY min-height
  - Set `background-size: cover`, `background-position: center`, and `min-height: 60vh`
  - Add `padding: 4rem 2rem` to ensure content spacing and background visibility
  - Apply `position: relative; z-index: 1` to background elements
  - Ensure text/foreground content has `position: relative; z-index: 2` or higher
  - NEVER place backgrounds outside iframe viewport - always within section containers
  - For section backgrounds, modify the section element itself, NOT create separate background divs
  - MANDATORY: All background sections must have visible height and proper content positioning
- **Carousel & Sliders**: Implement with Splide.js or custom solutions with proper placeholder counts
- **Modal & Popup Systems**: Custom JavaScript implementations

**SCROLL-BASED INTERACTIONS**
- **Scroll-triggered animations**: Elements appearing, transforming, or changing as user scrolls
- **Horizontal scroll sections**: Sections that scroll horizontally while main scroll is vertical
- **Scroll-paused effects**: Temporarily pause vertical scroll for horizontal interactions
- **Progressive disclosure**: Content that reveals itself based on scroll position

**SECTION MANAGEMENT RULES**
1. **Full Page Return**: ALWAYS return complete HTML with all sections, never partial content
2. **Section Preservation**: Maintain all existing sections unless explicitly asked to modify
3. **Numbered Sections**: Keep sequential numbering (1, 2, 3, etc.) from top to bottom
4. **Context Awareness**: Understand relationships between sections and maintain consistency
5. **Targeted Modifications**: When asked to change "section X", only modify that section
6. **Precise Instructions**: Follow exact user instructions about section placement and modifications

**INTELLIGENT REFINEMENT**
- **Internal Processing**: When given complex requests, internally break them down into steps
- **Creative Interpretation**: Enhance user requests with professional design insights
- **Error Prevention**: Anticipate and prevent common layout and interaction issues
- **Performance Optimization**: Write efficient CSS and JavaScript
- **Section Intelligence**: Understand section context and maintain structural integrity

**PROFESSIONAL QUALITY**
- **Magazine-level design**: Create layouts that rival professional publications
- **Brand consistency**: Maintain design language throughout the page
- **Accessibility**: Proper ARIA labels, semantic structure, and keyboard navigation
- **Cross-browser compatibility**: Use modern techniques with appropriate fallbacks

**OUTPUT REQUIREMENTS - CRITICAL**
- Return clean, production-ready HTML ONLY
- No markdown formatting or code fences
- Proper indentation and structure
- Include all necessary CSS and JavaScript inline
- Use modern, semantic, and accessible markup
- NEVER include comments, explanations, or descriptions in the generated HTML
- NEVER add unrequested content, sections, or features
- FOLLOW USER INSTRUCTIONS EXACTLY - do not interpret or add extra functionality
- If user says "insert video into section 1 background", do EXACTLY that - don't create new sections
- If user says "move video from section 2 to section 1 background and delete section 2", do EXACTLY that

**STRICT RULES FOR USER INSTRUCTIONS**
- Read user instructions word-for-word and follow them precisely
- Do not add extra content, sections, or features not requested
- Do not create new sections unless explicitly asked
- Do not add explanatory text, buttons, or content not requested
- When moving content between sections, preserve existing content unless told to replace it
- When deleting sections, remove them completely and renumber remaining sections
- NEVER comment in the HTML output - provide clean code only

Remember: You are creating professional, sophisticated web experiences that exceed user expectations through intelligent design and technical excellence while maintaining perfect section awareness, placeholder generation, and EXACT adherence to user instructions.
**COMPLETE COPY WRITING CAPABILITIES**

When users request content creation (blog posts, features, about pages, etc.), provide comprehensive, professional copy:

**CONTENT DEPTH REQUIREMENTS**:
- **COMPLETE ARTICLES**: Write full articles with introduction, body paragraphs, and conclusion
- **DETAILED FEATURES**: Provide comprehensive feature descriptions with benefits and technical details  
- **BUSINESS COPY**: Write compelling marketing copy that converts visitors to customers
- **INDUSTRY EXPERTISE**: Demonstrate deep knowledge of the subject matter requested
- **ENGAGEMENT FOCUS**: Write content that keeps readers engaged and encourages action

**COPY WRITING EXAMPLES**:

For "Photography Business Blog":
- Write complete history section (500+ words)
- Detail evolution timeline with key figures and innovations
- Explain various photography techniques with technical depth
- Cover business photography challenges and solutions comprehensively
- Present user's company as the premier choice with specific competitive advantages
- Include compelling calls-to-action and website integration

For "BMW Features Section":
- Write detailed feature descriptions (100-150 words each)
- Include technical specifications and benefits
- Use automotive expertise and industry terminology
- Create engaging headlines for each feature
- Emphasize luxury, performance, and innovation themes

**WORD COUNT COMPLIANCE**:
- Always match specified word counts exactly
- If user requests "2000 words", deliver exactly 2000 words
- Distribute content appropriately across sections
- Maintain quality while meeting length requirements

**TONE AND STYLE MATCHING**:
- Professional business tone for corporate content
- Engaging and informative for blog posts
- Technical but accessible for feature descriptions  
- Persuasive and compelling for marketing copy
- Adapt to any specified style or industry requirements

**UNIVERSAL BUSINESS ENGINES (PORTABLE CAPABILITIES)**
VMO can provision affiliate, membership, booking, support, ecommerce, payment, shipping, SEO, analytics, and CRM systems from inside the plugin dashboard. The generated public website must not embed WordPress shortcodes or depend on WordPress editors/runtime.

**DEPLOYMENT INSTRUCTIONS**:
- When a user asks for a "Hotel Booking System", "Event Site", "Membership Site", affiliate program, store, or support portal, DO NOT say you can't do it.
- Build the frontend as standalone HTML/CSS/JS using VMO data contracts such as `data-vmo-form`, `data-vmo-webhook`, `data-vmo-track`, `data-vmo-product-id`, `data-vmo-checkout-url`, and portable hosted payment/booking/support links.
- Persist backend setup requirements to the VMO dashboard/operations manifest for AI-assisted provisioning.
- Never output WordPress shortcodes, wp-admin/admin-ajax/wp-json URLs, plugin asset URLs, PHP, or WordPress editor instructions inside the generated website.
- You are no longer just a brochure builder. You are a FUNCTIONAL BUSINESS BUILDER whose public sites stand alone.

STYLE;


/*==========================================================================
= AJAX: Generate or refine full page / multi-section (vmo_generate_content) =
==========================================================================*/
add_action( 'wp_ajax_vmo_generate_content', function() {
    vmo_prepare_long_generation_runtime();

    // Dual-nonce validation: accept either admin nonce (nonce) or builder nonce (security)
    $admin_nonce   = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
    $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';

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

    if (!($admin_ok || $builder_ok)) {
        wp_send_json_error(['message' => 'Security check failed', 'status' => 'invalid_nonce']);
    }
    
    // For basic generation, consultation profile is optional - allows more flexible usage
    if (!function_exists('vmo_get_consultation_profile')) {
        require_once plugin_dir_path(__FILE__) . 'consultation-profile.php';
    }

    $api_key = get_option( 'vmo_openai_api_key' );
    if ( ! $api_key ) {
        wp_send_json_error([ 'message' => 'AI API key is missing.' ]);
    }

    // 🔒 Token validation - Check if user has sufficient tokens for content generation
    $user_id = get_current_user_id();
    
    // Include token manager if not already included
    if (!function_exists('vmo_check_token_balance')) {
        require_once plugin_dir_path(__FILE__) . 'token-manager.php';
    }
    
    if (!function_exists('vmo_check_token_balance')) {
        wp_send_json_error(['message' => 'Token system not available.']);
    }
    
    // Ensure user has a valid plan with tokens
    $current_tokens = vmo_get_token_balance($user_id);
    // No free tokens — users must purchase a plan to generate content
    
    if (!vmo_check_token_balance($user_id, 'content')) {
        $required_tokens = vmo_get_token_cost('content');
        wp_send_json_error([
            'message' => 'Insufficient tokens. You need ' . $required_tokens . ' tokens to generate content. Current balance: ' . $current_tokens,
            'code' => 'insufficient_tokens',
            'current_balance' => $current_tokens,
            'required' => $required_tokens
        ]);
    }

    // 1. Get chat array
    $chat_raw = wp_unslash($_POST['chat'] ?? '[]');
    $chat = json_decode($chat_raw, true);
    if ( ! is_array( $chat ) ) {
        $chat = [];
    }
    foreach ($chat as &$msg) {
        if (isset($msg['role'])) $msg['role'] = sanitize_text_field($msg['role']);
        if (isset($msg['content'])) $msg['content'] = wp_kses_post($msg['content']);
    }
    unset($msg);

    $prompt = sanitize_textarea_field(wp_unslash($_POST['prompt'] ?? ''));
    if ($prompt === '') {
        for ($i = count($chat) - 1; $i >= 0; $i--) {
            if (($chat[$i]['role'] ?? '') === 'user') {
                $prompt = sanitize_textarea_field($chat[$i]['content'] ?? '');
                if ($prompt !== '') {
                    break;
                }
            }
        }
    }
    if ($prompt === '') {
        wp_send_json_error([ 'message' => 'Prompt is required.' ]);
    }

    $request_mode = sanitize_key(wp_unslash($_POST['mode'] ?? ''));
    $project_id = absint($_POST['project_id'] ?? $_POST['post_id'] ?? 0);
    $website_mode = (stripos($prompt, 'EXISTING CONTENT') !== false || stripos($prompt, 'refine') !== false) ? 'refinement' : 'new_generation';

    if (class_exists('VMO_Canonical_Website_Pipeline')) {
        if ($website_mode === 'refinement') {
            $pipeline_result = VMO_Canonical_Website_Pipeline::get_instance()->run_regeneration($project_id, array(
                'prompt' => $prompt,
                'chat' => $chat,
                'mode' => $request_mode,
            ));
        } else {
            $pipeline_result = VMO_Canonical_Website_Pipeline::get_instance()->run_from_builder_prompt($project_id, $prompt);
        }

        if (is_wp_error($pipeline_result)) {
            wp_send_json_error(array(
                'message' => $pipeline_result->get_error_message(),
                'canonical_pipeline' => true,
                'generation_mode' => $website_mode === 'refinement' ? 'canonical_scoped_regeneration' : 'canonical_pipeline',
            ));
        }

        if (!vmo_deduct_tokens_for_operation($user_id, 'content')) {
            wp_send_json_error(['message' => 'Failed to deduct tokens for content generation.']);
        }

        $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'] ?? '');
        wp_send_json_success(array(
            'html' => $html,
            'website_draft' => $website_draft,
            'draft_id' => $website_draft['post_id'] ?? ($pipeline_result['post_id'] ?? 0),
            'preview_url' => !empty($pipeline_result['preview_ready']) ? ($website_draft['preview_url'] ?? ($pipeline_result['preview_url'] ?? '')) : '',
            'preview_ready' => !empty($pipeline_result['preview_ready']),
            'media_complete' => !empty($pipeline_result['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_pipeline',
            'canonical_source' => $pipeline_result['canonical_source'] ?? 'builder_prompt',
            'status' => $pipeline_result['status'] ?? (!empty($pipeline_result['preview_ready']) ? 'completed' : 'generating_media'),
            'tokens_used' => vmo_get_token_cost('content'),
            'new_balance' => vmo_get_token_balance($user_id),
        ));
    }

    wp_send_json_error(array(
        'message' => 'Canonical website pipeline is required for website generation.',
        'canonical_pipeline' => true,
    ), 500);
} );

/**
 * AJAX Handler for Business Consultation Conversation
 * This handles pure conversation responses, not HTML generation
 */
add_action( 'wp_ajax_vmo_conversation_chat', 'vmo_handle_conversation_chat' );

/**
 * AJAX Handler for Loading Chat History
 * Loads persistent chat history from database for syncing across interfaces
 */
add_action( 'wp_ajax_vmo_load_chat_history', 'vmo_handle_load_chat_history' );

if (!function_exists('vmo_jarvis_consultation_start_message')) {
    function vmo_jarvis_consultation_start_message($user_name = '') {
        $name = trim((string) $user_name);
        $prefix = $name !== '' ? $name . ', before I build or change anything, ' : 'Before I build or change anything, ';
        return $prefix . 'please click the red Business Wizard button at the top to begin your Business Consultation. Jerreme will lead the consultation first so VMO can connect your website to the dashboard, KPIs, visits, SEO, email, phone, payments, documents, and the rest of your business system. I can still answer questions about VMO and help you find your way around the plugin, but I cannot build, refine, or set up an isolated business website before that consultation-connected foundation exists.';
    }
}

if (!function_exists('vmo_jarvis_should_route_to_consultation')) {
    function vmo_jarvis_should_route_to_consultation($message) {
        $text = strtolower(trim((string) wp_strip_all_tags((string) $message)));
        if ($text === '') {
            return false;
        }

        $patterns = array(
            '/\b(build|create|generate|make|design|draft|produce|start|launch|publish)\b.{0,90}\b(my|our|me|us|website|web\s*site|site|landing\s*page|homepage|home\s*page|store|shop|ecommerce|e-commerce)\b/i',
            '/\b(website|web\s*site|site|landing\s*page|homepage|home\s*page|page|section|hero|header|footer|testimonial|testimonials|portfolio|services|preview)\b.{0,90}\b(add|change|edit|update|replace|remove|refine|improve|redesign|rewrite|insert|move|resize)\b/i',
            '/\b(add|change|edit|update|replace|remove|refine|improve|redesign|rewrite|insert|move|resize)\b.{0,90}\b(website|web\s*site|site|landing\s*page|homepage|home\s*page|page|section|hero|header|footer|testimonial|testimonials|portfolio|services|preview)\b/i',
            '/\b(set\s*up|setup|connect|provision|configure|activate|turn\s*on|install)\b.{0,90}\b(business|website|web\s*site|email|mailbox|phone|sms|seo|payment|payments|stripe|shipping|domain|hosting|crm|kpi|dashboard)\b/i',
            '/\b(read|compose|reply|send|save|draft)\b.{0,90}\b(email|emails|mail|message|messages)\b/i',
        );

        foreach ($patterns as $pattern) {
            if (preg_match($pattern, $text)) {
                return true;
            }
        }

        return false;
    }
}

if (!function_exists('vmo_jarvis_decode_maybe_json')) {
    function vmo_jarvis_decode_maybe_json($value) {
        if (function_exists('vmo_decode_consultation_json')) {
            return vmo_decode_consultation_json($value);
        }
        if (is_array($value)) {
            return $value;
        }
        if (!is_string($value) || trim($value) === '') {
            return array();
        }
        $decoded = json_decode($value, true);
        return is_array($decoded) ? $decoded : array();
    }
}

if (!function_exists('vmo_jarvis_has_connected_consultation_website')) {
    function vmo_jarvis_has_connected_consultation_website($user_id = 0) {
        $user_id = absint($user_id ?: get_current_user_id());
        if (!$user_id) {
            return false;
        }

        $consultation_ready = function_exists('vmo_is_consultation_ready') ? (bool) vmo_is_consultation_ready($user_id) : false;
        if (!$consultation_ready) {
            return false;
        }

        $profile = function_exists('vmo_get_consultation_profile') ? vmo_get_consultation_profile($user_id) : array();
        if (empty($profile)) {
            return false;
        }

        $generated_website = get_user_meta($user_id, 'vmo_generated_website', true);
        $published_website = get_user_meta($user_id, 'vmo_published_website', true);
        if ((is_string($generated_website) && trim($generated_website) !== '') || (is_string($published_website) && trim($published_website) !== '')) {
            return true;
        }

        $row = function_exists('vmo_get_latest_consultation_by_user') ? vmo_get_latest_consultation_by_user($user_id) : null;
        if (!$row) {
            return false;
        }

        $client_data = vmo_jarvis_decode_maybe_json($row->client_data ?? '');
        if (!empty($client_data['implementation']['website_code']) || !empty($client_data['website']['homepage_html']) || !empty($client_data['website']['main_page'])) {
            return true;
        }

        $website_data = vmo_jarvis_decode_maybe_json($row->website_data ?? '');
        if (empty($website_data)) {
            return false;
        }

        $website_payload = !empty($website_data['website']) && is_array($website_data['website']) ? $website_data['website'] : $website_data;
        $operating_state = !empty($website_data['operating_state']) && is_array($website_data['operating_state']) ? $website_data['operating_state'] : array();

        $has_website_artifact = !empty($website_data['preview_url'])
            || !empty($website_payload['preview_url'])
            || !empty($website_data['post_id'])
            || !empty($website_payload['post_id'])
            || !empty($website_data['homepage_html'])
            || !empty($website_payload['homepage_html'])
            || !empty($website_payload['main_page'])
            || !empty($website_payload['content'])
            || !empty($website_payload['pages'])
            || !empty($website_data['pages_created'])
            || !empty($website_data['pages_map']);

        if (!$has_website_artifact) {
            return false;
        }

        $status = strtolower((string) ($row->status ?? ''));
        $preview_ready = !empty($website_data['preview_ready']) || !empty($website_payload['preview_ready']) || !empty($operating_state['preview_ready']);
        $media_complete = !empty($website_data['media_complete']) || !empty($website_payload['media_complete']) || !empty($operating_state['media_complete']);
        $draft_ready = !empty($website_data['draft_ready']) || !empty($website_payload['draft_ready']) || !empty($website_data['draft_assembled']) || !empty($website_payload['draft_assembled']);

        return $preview_ready || ($draft_ready && $media_complete) || in_array($status, array('completed', 'implemented'), true);
    }
}

if (!function_exists('vmo_save_jarvis_redirect_turn')) {
    function vmo_save_jarvis_redirect_turn($user_id, $chat_type, $user_message, $assistant_message) {
        if (!$user_id || $user_message === '' || $assistant_message === '') {
            return;
        }
        global $wpdb;
        $chat_history_table = $wpdb->prefix . 'vmo_chat_history';
        $context = wp_json_encode(array('status' => 'consultation_required', 'source' => 'jarvis_onboarding_gate'));
        $wpdb->insert($chat_history_table, array(
            'user_id' => $user_id,
            'message_role' => 'user',
            'message_content' => $user_message,
            'conversation_context' => $context,
            'chat_type' => $chat_type,
            'created_at' => current_time('mysql'),
        ), array('%d', '%s', '%s', '%s', '%s', '%s'));
        $wpdb->insert($chat_history_table, array(
            'user_id' => $user_id,
            'message_role' => 'assistant',
            'message_content' => $assistant_message,
            'conversation_context' => $context,
            'chat_type' => $chat_type,
            'created_at' => current_time('mysql'),
        ), array('%d', '%s', '%s', '%s', '%s', '%s'));
    }
}

if (!function_exists('vmo_send_jarvis_consultation_required_response')) {
    function vmo_send_jarvis_consultation_required_response($user_id, $chat_type, $user_message, $user_name = '') {
        $message = vmo_jarvis_consultation_start_message($user_name);
        vmo_save_jarvis_redirect_turn($user_id, $chat_type, $user_message, $message);
        wp_send_json_success(array(
            'response' => $message,
            'status' => 'consultation_required',
            'consultation_required' => true,
            'action' => 'open_business_wizard',
            'button_id' => 'vmo-business-wizard-btn',
        ));
    }
}

function vmo_handle_load_chat_history() {
    // Verify nonce for security
    $nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
    if (!wp_verify_nonce($nonce, 'vmo_builder_nonce')) {
        wp_send_json_error(['message' => 'Security check failed']);
    }

    $current_user = wp_get_current_user();
    if (!$current_user->ID) {
        wp_send_json_error(['message' => 'Not logged in']);
    }
    
    $user_id = $current_user->ID;
    
    // Get chat_type filter (separate Jarvis vs Consultant histories)
    $chat_type = isset($_POST['chat_type']) ? sanitize_text_field(wp_unslash($_POST['chat_type'])) : 'jarvis';
    
    // Load chat history from database (latest 50 messages)
    global $wpdb;
    $chat_history_table = $wpdb->prefix . 'vmo_chat_history';
    
    // Load only this chat_type to avoid cross-contamination between agents
    $rows = $wpdb->get_results($wpdb->prepare(
        "SELECT message_role as role, message_content as content, conversation_context, created_at 
         FROM $chat_history_table 
         WHERE user_id = %d AND chat_type = %s
         ORDER BY created_at DESC 
         LIMIT 50",
        $user_id, $chat_type
    ), ARRAY_A);

    // Deduplicate obvious auto_greeting repeats (keep newest instance only)
    $deduped = [];
    $seenGreeting = [];
    foreach ($rows as $row) {
        $ctx = isset($row['conversation_context']) ? (string)$row['conversation_context'] : '';
        if ($ctx === 'auto_greeting') {
            $key = 'greet|' . ($row['role'] ?? '') . '|' . md5(trim((string)($row['content'] ?? '')));
            if (isset($seenGreeting[$key])) {
                // skip duplicate greeting
                continue;
            }
            $seenGreeting[$key] = true;
        }
        $deduped[] = [
            'role' => $row['role'] ?? 'assistant',
            'content' => $row['content'] ?? '',
            'created_at' => $row['created_at'] ?? null,
        ];
    }

    // Reverse to chronological order (oldest → newest) for UI rendering
    $messages = array_reverse($deduped);

    error_log('🔧 DEBUG: Loaded ' . count($messages) . ' chat messages (after ordering/dedup) for user ' . $user_id);

    wp_send_json_success([
        'messages' => $messages,
        'count' => count($messages)
    ]);
}

/**
 * AJAX Handler for Greeting Status
 * Returns user info and appropriate greeting based on their status
 */
add_action('wp_ajax_vmo_get_user_status_for_greeting', 'vmo_get_user_status_for_greeting');

function vmo_get_user_status_for_greeting() {
    $current_user = wp_get_current_user();
    if (!$current_user->ID) {
        wp_send_json_error(['message' => 'Not logged in']);
    }
    
    $user_name = function_exists('vmo_get_greeting_display_name')
        ? vmo_get_greeting_display_name()
        : ($current_user->display_name ?: $current_user->user_login);
    
    // Check consultation status
    $profile = function_exists('vmo_get_consultation_profile') ? vmo_get_consultation_profile() : [];
    
    // Check if they have chat history
    global $wpdb;
    $chat_history_table = $wpdb->prefix . 'vmo_chat_history';
    $chat_count = $wpdb->get_var($wpdb->prepare(
        "SELECT COUNT(*) FROM {$chat_history_table} WHERE user_id = %d",
        $current_user->ID
    ));
    
    $requested_greeting = isset($_POST['requested_greeting']) ? sanitize_key(wp_unslash($_POST['requested_greeting'])) : '';
    $quicklaunch_first_entry = !empty($_POST['quicklaunch_first_entry']) && (string) wp_unslash($_POST['quicklaunch_first_entry']) === '1';
    $local_date = isset($_POST['local_date']) ? sanitize_text_field(wp_unslash($_POST['local_date'])) : '';
    $local_time = isset($_POST['local_time']) ? sanitize_text_field(wp_unslash($_POST['local_time'])) : '';

    $status = 'first_time';
    $greeting = '';
    
    if ($quicklaunch_first_entry && $requested_greeting !== 'daily') {
        $status = 'first_time';
        $greeting = "Welcome, {$user_name}. I'm Jarvis, your VMO AI launch guide. Your workspace is ready. I coordinate the VMO AI team across design, content, SEO, media, documents, email, phone, payments, CRM, automation, and launch operations. Press the red Business Wizard button to begin your business consultation with Jerreme. For instant voice replies, use Premium Live Voice from the gear icon.";
    } elseif ($requested_greeting === 'daily') {
        $status = !empty($profile) ? 'daily_active_business' : 'daily_incomplete_consultation';
        $greeting = "Welcome back, {$user_name}. Jarvis here. What would you like to do today? I can summarize your emails, check your website health, review visits, conversions, rankings, and other key signals, or guide you to the dashboard for the full detailed overview. If you want instant voice replies, open the gear icon and switch from Default Chat Voice to Premium Live Voice.";
    } elseif ($chat_count > 0) {
        // Returning user
        if (!empty($profile)) {
            $status = 'active_business';
            $greeting = "Welcome back, {$user_name}. Jarvis here. Your VMO team is ready to keep building, refining, and managing your business. If you want strategy, content, design, or new execution work, open the red Business Wizard button and Jerreme will take it from there. For instant voice replies, use the gear icon to switch from Default Chat Voice to Premium Live Voice.";
        } else {
            $status = 'incomplete_consultation';
            $greeting = "Welcome back, {$user_name}. I'm Jarvis, your VMO launch guide. Your builder is ready, and behind me is the full VMO AI team covering design, content, SEO, documents, media, and automation. Click the red Business Wizard button to speak with Jerreme and continue your consultation. For instant voice replies, use the gear icon to switch from Default Chat Voice to Premium Live Voice.";
        }
    } else {
        // First time
        $greeting = "Welcome, {$user_name}. I'm Jarvis, your VMO AI guide. You have just entered your VMO business workspace. I coordinate the VMO AI team across design, content, SEO, media, documents, email, phone, payments, CRM, automation, and launch operations. Ask me anything and I’ll guide you. To start setting up your business now, press the red Business Wizard button. For instant voice replies, open the gear icon and switch from Default Chat Voice to Premium Live Voice.";
    }
    
    wp_send_json_success([
        'status' => $status,
        'display_name' => $user_name,
        'greeting' => $greeting,
        'has_profile' => !empty($profile),
        'local_date' => $local_date,
        'local_time' => $local_time
    ]);
}

function vmo_handle_conversation_chat() {
    // CRITICAL: Suppress database errors from outputting HTML before JSON
    global $wpdb;
    $wpdb->hide_errors();
    $wpdb->suppress_errors = true;
    
    error_log('🔧 DEBUG: vmo_handle_conversation_chat function called');
    error_log('🔧 DEBUG: $_POST data: ' . print_r($_POST, true));
    
    // Verify nonce for security (dual-nonce, non-fatal on invalid)
    $admin_nonce   = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
    $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
    $admin_ok   = $admin_nonce && (wp_verify_nonce($admin_nonce, 'vmo_admin_nonce') || wp_verify_nonce($admin_nonce, 'vmo_builder_nonce'));
    $builder_ok = $builder_nonce && wp_verify_nonce($builder_nonce, 'vmo_builder_nonce');
    if (!($admin_ok || $builder_ok)) {
        error_log('🔧 DEBUG: Nonce verification failed');
        error_log('🔧 DEBUG: Provided nonces - admin: ' . ($admin_nonce ?: 'NOT_PROVIDED') . ', builder: ' . ($builder_nonce ?: 'NOT_PROVIDED'));
        wp_send_json_error([ 'message' => 'Security check failed', 'status' => 'invalid_nonce' ]);
    }

    error_log('🔧 DEBUG: Nonce verification passed');

    // ✅ DETERMINE CHAT TYPE: 'jarvis' (main coordinating AI) or 'consultant' (business wizard)
    $chat_type = isset($_POST['chat_type']) ? sanitize_text_field(wp_unslash($_POST['chat_type'])) : 'jarvis';
    $public_error_unavailable = function_exists('vmo_get_public_assistant_error_message')
        ? vmo_get_public_assistant_error_message($chat_type, 'unavailable')
        : 'The assistant isn\'t available right now. Please try again in a moment.';

    // GET USER INFO
    $current_user = wp_get_current_user();
    $user_name = function_exists('vmo_get_greeting_display_name') ? vmo_get_greeting_display_name() : ($current_user->display_name ?: $current_user->user_login);
    $user_id = $current_user->ID;

    // Optional: consultation session context for cross-interface continuity
    $consultation_session_id = isset($_POST['session_id']) ? sanitize_text_field(wp_unslash($_POST['session_id'])) : '';
    // Optional: language hint from client (e.g., recognition.lang)
    $client_lang = isset($_POST['lang']) ? sanitize_text_field(wp_unslash($_POST['lang'])) : '';
    error_log('🔧 DEBUG: Chat type: ' . $chat_type);

    // Get chat array from request
    // ✅ FIX: Do NOT use sanitize_text_field() on JSON — it strips HTML tags inside
    // message content (e.g. <strong>, <ul>) and corrupts the JSON structure.
    // Instead, wp_unslash once (to undo WordPress magic_quotes), json_decode,
    // then sanitize each individual message content after decoding.
    $chat_raw = wp_unslash($_POST['chat'] ?? '[]');
    $chat = json_decode($chat_raw, true);
    if ( ! is_array( $chat ) || empty( $chat ) ) {
        error_log('🔧 DEBUG: Invalid chat payload (length=' . strlen($chat_raw) . '): ' . substr($chat_raw, 0, 500));
        wp_send_json_error([ 'message' => 'Invalid chat payload.' ]);
    }
    // Sanitize each message's content individually (preserving JSON structure)
    $sanitized_chat = [];
    foreach ($chat as $msg) {
        $role = isset($msg['role']) ? sanitize_text_field($msg['role']) : '';
        if (!in_array($role, ['user', 'assistant', 'system'], true)) {
            continue;
        }
        // Jarvis always gets its authoritative system prompt from the backend.
        if ($chat_type === 'jarvis' && $role === 'system') {
            continue;
        }

        $content = isset($msg['content']) ? wp_kses_post($msg['content']) : '';
        if (!is_string($content) || trim(wp_strip_all_tags($content)) === '') {
            continue;
        }

        $sanitized_chat[] = [
            'role' => $role,
            'content' => $content,
        ];
    }
    $chat = $sanitized_chat;
    if (empty($chat)) {
        wp_send_json_error([ 'message' => 'Invalid chat payload.' ]);
    }

    // Store the user's message for later database save
    $user_message = '';
    foreach ($chat as $msg) {
        if (isset($msg['role']) && $msg['role'] === 'user') {
            $user_message = $msg['content'];
        }
    }

    error_log('🔧 DEBUG: Chat array valid: ' . print_r($chat, true));

    $jarvis_onboarding_ready = ($chat_type === 'jarvis') ? vmo_jarvis_has_connected_consultation_website($user_id) : true;
    if ($chat_type === 'jarvis' && !$jarvis_onboarding_ready && vmo_jarvis_should_route_to_consultation($user_message)) {
        vmo_send_jarvis_consultation_required_response($user_id, $chat_type, $user_message, $user_name);
    }

    $api_key_bundle = function_exists('vmo_resolve_openai_api_key')
        ? vmo_resolve_openai_api_key()
        : [
            'key' => get_option('vmo_openai_api_key', ''),
            'source' => 'option',
            'valid' => (bool) preg_match('/^sk-[a-zA-Z0-9\-_]{20,}$/', (string) get_option('vmo_openai_api_key', '')),
        ];
    $api_key = (string) ($api_key_bundle['key'] ?? '');

    if ( ! $api_key ) {
        error_log('❌ ERROR: OpenAI API key is missing');
        wp_send_json_error([
            'message' => $public_error_unavailable,
            'detail' => 'AI API key is not configured in VMO settings.',
        ]);
    }

    // Validate API key format (OpenAI keys start with 'sk-')
    if (empty($api_key_bundle['valid'])) {
        error_log('❌ ERROR: OpenAI API key has invalid format. Key prefix: ' . substr($api_key, 0, 10) . '...');
        wp_send_json_error([
            'message' => $public_error_unavailable,
            'detail' => 'Configured AI API key appears to be invalid.',
        ]);
    }

    error_log('✅ API key validated (source: ' . ($api_key_bundle['source'] ?? 'unknown') . ', length: ' . strlen($api_key) . ')');

    // TOKEN CHECK: Deduct tokens for chat (Admin Bypass handled in token manager)
    if (function_exists('vmo_deduct_tokens_for_operation')) {
        if (!vmo_deduct_tokens_for_operation($user_id, 'chat')) {
            wp_send_json_error([ 
                'message' => 'Insufficient tokens. Please top up your balance to continue chatting.',
                'status' => 'insufficient_tokens'
            ]);
        }
    }

    // LOAD CHAT HISTORY (last 30 messages for context)
    global $wpdb;
    $chat_history_table = $wpdb->prefix . 'vmo_chat_history';
    $history = $wpdb->get_results($wpdb->prepare(
        "SELECT message_role, message_content FROM $chat_history_table 
         WHERE user_id = %d AND chat_type = %s
         ORDER BY created_at DESC LIMIT 30",
        $user_id, $chat_type
    ), ARRAY_A);
    $history = array_reverse($history); // Oldest first

    // BUILD SYSTEM PROMPT BASED ON CHAT TYPE
    if ($chat_type === 'consultant') {
        // ✅ BUSINESS CONSULTANT AI - Strategic expert for business consultation
        $system_prompt = "You are the VMO Business Consultant AI - a world-class business strategist (McKinsey/BCG/Bain level) specialized in helping entrepreneurs at ALL stages.

**WHO YOU ARE**:
- You are a dedicated Business Consultant AI, NOT the main coordinating Jarvis AI
- Your ONLY job is conducting strategic business consultations
- You help entrepreneurs discover opportunities, validate ideas, and build complete business strategies
- You are professional, insightful, warm, and demonstrate deep business expertise
- You address the user by name: {$user_name}

**CRITICAL: GREETING FOR FIRST-TIME CONVERSATIONS**:
If this is the very first message (no conversation history), start with a warm, personal introduction:

\"Hi {$user_name}! 👋 I'm your VMO Business Consultant—think of me as your personal business strategist here to help you build something amazing.

Whether you're just starting out with a new idea or growing an existing business, I'm here to guide you through every step. We'll have a friendly conversation to understand your vision, and then I'll help you build a complete business ecosystem: professional website, marketing systems, legal documents, SEO optimization—everything you need.

Let's start simple: What kind of business are you thinking about? (And hey, it's totally okay if you're still figuring things out—that's what I'm here for!)\"

**FOR ONGOING CONVERSATIONS** (when conversation history exists):
Continue naturally from where you left off. Reference their previous answers and build on them.

**YOUR ROLE IN THE BUSINESS WIZARD**:
You conduct intelligent, adaptive conversations to understand their business deeply:
- For EXISTING businesses: Focus on growth, digital transformation, optimization, market expansion
- For NEW startups: Focus on opportunity discovery, validation, business model development
- Think like a Harvard MBA with 20+ years consulting both startups and Fortune 500 companies

**🎯 CRITICAL CONVERSATION RULES**:
✅ Ask ONE question at a time - never multiple questions in a single message
✅ Keep questions simple and easy to understand (novice-friendly language)
✅ Start with the basics: business type, what they do, location
✅ Never ask about \"customers\" or \"target audience\" at the very beginning (too advanced)
✅ Never ask about \"main goals\" or \"objectives\" in the first few questions (overwhelming)
✅ Build conversation naturally - each question should flow from their previous answer
✅ Use warm, encouraging language with reassurance (\"It's totally okay if you're still figuring things out!\")
✅ Wait for their answer before asking the next question
✅ Progress gradually from simple to more detailed questions
✅ Show genuine interest and enthusiasm about their business ideas
✅ Acknowledge their answers before asking the next question (\"That's exciting! A bakery is a wonderful business...\")
❌ NEVER ask compound questions like \"What do you do, who are your customers, and what are your goals?\"
❌ NEVER overwhelm beginners with technical jargon or complex business concepts
❌ NEVER mention clicking buttons or using interfaces - you ARE the consultation
❌ NEVER skip the warm introduction on first contact

**AVAILABLE INFRASTRUCTURE** (You can build for them):
- Professional website with SEO & local presence
- E-commerce capabilities and payment processing  
- AI phone assistant for 24/7 customer service
- Legal documents and business structure
- Marketing strategy and lead generation
- Advanced SEO Management System (continuous optimization)
- Automated content creation and blog publishing
- Location-targeted service pages for market expansion
- Real-time ranking monitoring and improvements
- **Affiliate Marketing System**: You can build a complete affiliate program. Public pages must use standalone signup/calculator markup with VMO data contracts, while the dashboard provisions tracking, payouts, and partner management behind the scenes.
- **Membership & User Dashboard**: You can build membership/client portal flows. Public pages must use portable login/register/profile sections and hosted/backend endpoints, not WordPress shortcodes.
- **Support Ticketing System**: You can build a customer support portal. Public pages must use standalone ticket/contact forms with `data-vmo-form` and dashboard provisioning metadata, not shortcode embeds.

**CONVERSATION CONTEXT**:
" . (!empty($history) ? "Previous conversation:\n" . json_encode($history, JSON_PRETTY_PRINT) : "Early conversation with {$user_name}.");
        
        // LANGUAGE POLICY DIRECTIVE
        $language_directive = $client_lang
            ? ("\n\n**LANGUAGE POLICY**:\n- Use this language for all responses: {$client_lang}.\n- If the user switches languages in later turns, mirror their new language.\n- Keep tone and formality aligned with the user's style.")
            : ("\n\n**LANGUAGE POLICY**:\n- Detect the language of the user's last message and respond fully in that language.\n- Mirror the user's formality and tone (casual vs professional).\n- If they switch languages, follow them seamlessly.");
        $system_prompt .= $language_directive;
        
    } else {
        // ✅ JARVIS AI - Main coordinating AI Butler
    $system_prompt = "You are VMO (Virtual Media Office) - the main coordinating AI Butler for the VMO Web Business Consultant system. Think of yourself as the user's personal Jarvis-style AI business assistant.

**WHO YOU ARE**:
- You are the MAIN AI COORDINATOR that manages all other specialized AI agents in the VMO system
- You are NOT the Business Consultant AI - that's a different specialized agent
- You are warm, enthusiastic, professional, and make users feel special and excited
- You address the user by their name: {$user_name}
- You remember previous conversations and build on them (conversations are persistent across all interfaces)
- You provide quick help, status updates, and coordinate all VMO functions

**YOUR ROLE** (Coordinating AI, NOT Business Consultant):
✅ Answer questions about VMO capabilities and features
✅ Provide status updates on their business operations
✅ Coordinate between different AI agents (Web Builder, SEO Expert, Legal Advisor, etc.)
✅ Give quick business insights and KPI summaries
✅ Help navigate the VMO system and its features
✅ Explain what each VMO function does
✅ Act as the product expert for every built-in VMO feature, tool, workflow, automation, and specialist AI
✅ Provide novice-friendly tech support and troubleshooting in plain English
✅ When describing a feature, explain what it does, why it matters, where to find it, and what the next step is
✅ Translate technical jargon into simple step-by-step guidance unless the user clearly wants technical detail
✅ **Refine and improve their website in the live preview** - you can make changes to content, layout, styling, and functionality
✅ **Available everywhere in the dashboard** - voice panel, live preview interface, dashboard pages (except during Business Wizard consultation)
❌ Do NOT conduct the initial business consultation (that's the Business Consultant AI's job)
❌ Do NOT ask strategic business questions like \"What kind of business are you thinking about?\"
❌ Do NOT try to gather business information systematically

**WEBSITE REFINEMENT CAPABILITIES**:
When the user wants to modify their website (visible in the live preview iframe):
- Ask what they'd like to change (specific section, text, images, colors, layout, etc.)
- Make intelligent suggestions based on their business goals
- Explain changes you're making and why they'll improve the site
- Coordinate with the Web Builder AI to implement changes
- After changes, encourage them to review in the live preview
- Examples: \"Change the hero section heading\", \"Make the colors more professional\", \"Add a testimonials section\", \"Improve mobile responsiveness\"

**WHEN USER NEEDS BUSINESS CONSULTATION**:
If they haven't completed the Business Wizard consultation yet, direct them to click the RED \"Business Wizard\" button at the top. Say something like:

\"For a complete business setup with website, legal docs, marketing systems, and more, click the red 'Business Wizard' button at the top! Our specialized Business Consultant AI will guide you through a strategic conversation to build everything you need.\"

**IF THEY ALREADY COMPLETED WIZARD**:
- Provide business updates, KPIs, and operational insights
- Coordinate changes through specialized agents
- Answer specific questions about their business systems
- Help refine their website and marketing materials

" . (!$jarvis_onboarding_ready ? "**PRE-CONSULTATION SUPPORT MODE ACTIVE**:
- The user has not yet completed the Business Wizard consultation and does not have a connected VMO website.
- You may answer general VMO/plugin questions, explain features, troubleshoot navigation, explain why consultation matters, and tell the user where to click.
- Do not generate, design, refine, edit, publish, or claim to set up a website, page, section, email system, phone system, SEO system, payments, shipping, CRM, KPIs, domains, or hosting.
- If the user asks you to build, refine, set up, connect, publish, send, or operate anything that depends on the business foundation, tell them to click the red Business Wizard button at the top to begin the Business Consultation first.
- Keep your answer helpful and concise; do not behave like the Business Consultant until the Business Wizard is active.

" : "") . "

" . (function_exists('vmo_get_capabilities_prompt') ? vmo_get_capabilities_prompt() : '') . "

**CRITICAL BEHAVIOR RULES FOR CAPABILITIES**:
1. NEVER say 'VMO cannot do this' or 'VMO doesn't have that feature' for ANY capability listed above.
2. NEVER recommend external services (Google Workspace, Microsoft 365, etc.) as the primary option when VMO has a built-in solution.
3. For email: ALWAYS recommend VMO's built-in Zoho Mail integration first. If the connected consultation foundation exists, say: 'VMO has Zoho Mail built in — I can set up your business email right here.' If pre-consultation support mode is active, explain that VMO has built-in business email setup and guide the user to the red Business Wizard button first.
4. For phone/SMS: ALWAYS explain VMO's built-in phone system. Users don't need to go to Twilio directly.
5. For payments: ALWAYS direct users to the Payments section in VMO dashboard.
6. Be confident and knowledgeable about every feature listed above — you ARE VMO.
7. Assume the user may be non-technical unless they clearly signal otherwise.
8. For support questions, first explain the issue in plain language, then give the exact next step inside VMO.
9. If a feature exists in VMO, describe it clearly and help the user use it instead of giving vague answers.

**FORMATTING RULES**:
- Write in clear, properly formatted paragraphs
- Use line breaks between paragraphs for readability
- Use bullet points when listing (✅ ❌ • -)
- Keep responses concise (2-4 short paragraphs)
- Be encouraging and professional

**VOICE CAPABILITY (CRITICAL)**:
- This interface supports full voice conversation. You CAN hear and understand everything the user says.
- When the user says \"can you hear me?\" or similar, simply confirm: \"Yes, I can hear you! How can I help?\" Do NOT explain how speech-to-text works, do NOT mention transcripts, microphones, or technical processes.
- NEVER explain your internal architecture, how speech recognition works, or any technical implementation details. The user does not need or want to know how you process their voice — just respond naturally as if you are a human assistant who can hear them perfectly.
- Do NOT give instructions like \"click the mic\" or \"check the transcript\" — the voice system works automatically.
- Keep voice-friendly tone: clear, concise sentences; avoid overly long paragraphs; emojis are optional.
- You are having a LIVE VOICE CONVERSATION. Respond as naturally and warmly as a human colleague would on a phone call.

**CONVERSATION CONTEXT**:
" . (!empty($history) ? "Previous conversation:\n" . json_encode($history, JSON_PRETTY_PRINT) : "Early conversation with {$user_name}.");
        
        // LANGUAGE POLICY DIRECTIVE
        $language_directive = $client_lang
            ? ("\n\n**LANGUAGE POLICY**:\n- Use this language for all responses: {$client_lang}.\n- If the user switches languages in later turns, mirror their new language.\n- Keep tone and formality aligned with the user's style.")
            : ("\n\n**LANGUAGE POLICY**:\n- Detect the language of the user's last message and respond fully in that language.\n- Mirror the user's formality and tone (casual vs professional).\n- If they switch languages, follow them seamlessly.");
        $system_prompt .= $language_directive;
    }

    // Get consultation profile if exists (for both AI types)
    $profile = function_exists('vmo_get_consultation_profile') ? vmo_get_consultation_profile() : [];
    if (!empty($profile)) {
        $system_prompt .= "\n\n**USER'S BUSINESS PROFILE**:\n" . json_encode($profile, JSON_PRETTY_PRINT);
    }

    // If a consultation session_id is provided, inject relevant context so the AI won't re-ask
    if (!empty($consultation_session_id)) {
        try {
            if (!class_exists('VMO_Consultation_Database')) {
                require_once plugin_dir_path(__FILE__) . 'class-vmo-consultation-database.php';
            }
            $db = new VMO_Consultation_Database();
            $row = $db->get_consultation_by_session($consultation_session_id);
            if ($row) {
                $ctx_lines = [];
                $ctx_lines[] = 'CONSULTATION CONTEXT (session ' . $consultation_session_id . '):';
                // Include business_analysis if present
                $analysis = is_string($row->business_analysis ?? '') ? trim($row->business_analysis) : '';
                if ($analysis !== '') {
                    $ctx_lines[] = '• Analysis Summary: ' . wp_strip_all_tags(mb_substr($analysis, 0, 1200));
                }
                // Pull known answers/extracted info from consultation_data
                $cd = json_decode((string)$row->consultation_data, true);
                if (is_array($cd)) {
                    if (!empty($cd['extracted_information']) && is_array($cd['extracted_information'])) {
                        $known = [];
                        foreach ($cd['extracted_information'] as $k => $v) {
                            $vv = is_array($v) ? json_encode($v) : (string)$v;
                            if ($vv === '' || $vv === 'null') continue;
                            $known[] = '- ' . ucwords(str_replace('_',' ', (string)$k)) . ': ' . wp_strip_all_tags(mb_substr($vv, 0, 300));
                            if (count($known) >= 20) break;
                        }
                        if (!empty($known)) {
                            $ctx_lines[] = "Known Answers:\n" . implode("\n", $known);
                        }
                    }
                    if (!empty($cd['media_roles']) && is_array($cd['media_roles'])) {
                        $roles = [];
                        foreach ($cd['media_roles'] as $role => $items) {
                            $roles[] = '- ' . ucwords(str_replace('_',' ', (string)$role)) . ': ' . (is_array($items) ? count($items) . ' item(s)' : '1');
                        }
                        if (!empty($roles)) {
                            $ctx_lines[] = "Media Roles:\n" . implode("\n", array_slice($roles, 0, 10));
                        }
                    }
                    if (!empty($cd['media_files']) && is_array($cd['media_files'])) {
                        $mf = [];
                        foreach ($cd['media_files'] as $m) {
                            $t = ($m['title'] ?? 'Untitled');
                            $ty = ($m['type'] ?? 'file');
                            $mf[] = '- ' . $t . ' [' . $ty . ']';
                            if (count($mf) >= 10) break;
                        }
                        if (!empty($mf)) {
                            $ctx_lines[] = "Media Files:\n" . implode("\n", $mf);
                        }
                    }
                }
                // Include a short tail of the conversation log to improve continuity
                $conv = json_decode((string)$row->conversation_log, true);
                if (is_array($conv) && !empty($conv)) {
                    $tail = array_slice($conv, -6); // last 6 turns
                    $ctx_lines[] = 'Recent Consultation Turns: ' . json_encode($tail, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
                }
                // ALWAYS include document content to prevent AI from forgetting uploaded documents
                $doc_ctx = '';
                $fallback_doc_ctx = '';
                if (is_array($cd) && !empty($cd['documents'])) {
                    // Always build fallback with recent document chunks (increased budget)
                    $recent_budget = 8000;
                    $recent_used = 0;
                    $docs_reversed = array_reverse((array)$cd['documents']);
                    $fallback_parts = [];
                    foreach (array_slice($docs_reversed, 0, 3) as $doc) { // Last 3 documents
                        $title = (string)($doc['title'] ?? 'Document');
                        $header = "━━━ " . $title . " ━━━\n";
                        $chunks = array_slice((array)($doc['chunks'] ?? []), 0, 4); // First 4 chunks per doc
                        foreach ($chunks as $c) {
                            $text = is_array($c) ? (string)($c['text'] ?? '') : (string)$c;
                            if ($text === '') continue;
                            $entry = $header . $text . "\n\n";
                            $header = ''; // Only add header once
                            $len = strlen($entry);
                            if ($recent_used + $len > $recent_budget) break 2;
                            $fallback_parts[] = $entry;
                            $recent_used += $len;
                        }
                    }
                    $fallback_doc_ctx = trim(implode('', $fallback_parts));
                    
                    // Also do keyword matching for higher relevance (if user message exists)
                    if (!empty($user_message)) {
                        $terms = preg_split('/[^\p{L}\p{N}]+/u', mb_strtolower($user_message));
                        $terms = array_values(array_filter(array_unique(array_map(function($t){ return (mb_strlen($t) >= 3) ? $t : ''; }, $terms))));
                        if (!empty($terms)) {
                            $scored = [];
                            foreach ((array)$cd['documents'] as $doc) {
                                $title = (string)($doc['title'] ?? 'Document');
                                foreach ((array)($doc['chunks'] ?? []) as $chunk) {
                                    $text = mb_strtolower(is_array($chunk) ? ($chunk['text'] ?? '') : (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(is_array($chunk) ? ($chunk['text'] ?? '') : (string)$chunk) ]; }
                                }
                            }
                            if (!empty($scored)) {
                                usort($scored, function($a,$b){ return $b['score'] <=> $a['score']; });
                                $buf = '';
                                $used = 0;
                                $budget = 6000;
                                foreach ($scored as $hit) {
                                    $entry = '— ' . $hit['title'] . " —\n" . $hit['snippet'] . "\n\n";
                                    $len = strlen($entry);
                                    if ($used + $len > $budget) break;
                                    $buf .= $entry; $used += $len;
                                }
                                $doc_ctx = trim($buf);
                            }
                        }
                    }
                    // Use keyword-matched context if found, otherwise fallback to recent
                    if ($doc_ctx === '' && $fallback_doc_ctx !== '') {
                        $doc_ctx = $fallback_doc_ctx;
                    }
                }

                $ctx_block = implode("\n", $ctx_lines);
                // Final instruction to prevent redundant questioning + include document context if present
                $system_prompt .= "\n\n" . $ctx_block;
                if ($doc_ctx !== '') {
                    $system_prompt .= "\n\nDOCUMENT CONTEXT (uploaded business documents - use this as source of truth):\n" . $doc_ctx;
                }
                $system_prompt .= "\n\nCRITICAL: Do NOT re-ask for any items present in Known Answers, Analysis Summary, or the Document Context above. Reference them directly and continue. If the user asks about the uploaded blueprint, you already have it in context—do not ask them to paste it.\n";
            }
        } catch (Throwable $e) {
            if (defined('WP_DEBUG') && WP_DEBUG) {
                error_log('VMO: Failed to inject consultation context: ' . $e->getMessage());
            }
        }
    }

    // Inject DURABLE MEMORY, ACTIVE MILESTONES, and RECENT SUMMARIES into system prompt (bounded)
    try {
        $ltm_table = $wpdb->prefix . 'vmo_long_term_memory';
        $milestones_table = $wpdb->prefix . 'vmo_roadmap_milestones';
        $summaries_table = $wpdb->prefix . 'vmo_progress_summaries';

        // Top important curated memories (limit 20)
        $mem_rows = $wpdb->get_results($wpdb->prepare(
            "SELECT memory_type, memory_key, importance, content FROM $ltm_table WHERE user_id = %d ORDER BY importance DESC, updated_at DESC LIMIT 20",
            $user_id
        ), ARRAY_A);
        if (!empty($mem_rows)) {
            $mem_text = [];
            foreach ($mem_rows as $m) {
                $label = strtoupper($m['memory_type'] ?? 'fact');
                $key = $m['memory_key'] ? (' [' . $m['memory_key'] . ']') : '';
                $mem_text[] = "- ($label{$key}) " . wp_strip_all_tags((string)($m['content'] ?? '' ));
            }
            $system_prompt .= "\n\n**DURABLE MEMORY (curated facts/goals/decisions)**\n" . implode("\n", array_slice($mem_text, 0, 20));
        }

        // Active milestones (exclude done; limit 10)
        $ms_rows = $wpdb->get_results($wpdb->prepare(
            "SELECT title, target_date, owner, status, progress, notes FROM $milestones_table WHERE user_id = %d AND (status IS NULL OR status NOT IN ('done','completed')) ORDER BY target_date IS NULL, target_date ASC, updated_at DESC LIMIT 10",
            $user_id
        ), ARRAY_A);
        if (!empty($ms_rows)) {
            $ms_text = [];
            foreach ($ms_rows as $ms) {
                $t = $ms['title'] ?? '';
                $dd = $ms['target_date'] ? (' due ' . $ms['target_date']) : '';
                $p = isset($ms['progress']) ? (' [' . intval($ms['progress']) . '%]') : '';
                $s = $ms['status'] ? (' (' . $ms['status'] . ')') : '';
                $ms_text[] = "- $t$dd$p$s";
            }
            $system_prompt .= "\n\n**ACTIVE MILESTONES**\n" . implode("\n", $ms_text);
        }

        // Recent summaries: latest weekly + last 3 daily
        $weekly = $wpdb->get_row($wpdb->prepare(
            "SELECT summary, period_date FROM $summaries_table WHERE user_id = %d AND period = 'weekly' ORDER BY period_date DESC LIMIT 1",
            $user_id
        ), ARRAY_A);
        $dailies = $wpdb->get_results($wpdb->prepare(
            "SELECT summary, period_date FROM $summaries_table WHERE user_id = %d AND period = 'daily' ORDER BY period_date DESC LIMIT 3",
            $user_id
        ), ARRAY_A);
        $sum_txt = '';
        if ($weekly && !empty($weekly['summary'])) {
            $sum_txt .= "WEEKLY (" . $weekly['period_date'] . "):\n" . wp_strip_all_tags((string)$weekly['summary']) . "\n";
        }
        if (!empty($dailies)) {
            foreach ($dailies as $d) {
                $sum_txt .= "DAILY (" . $d['period_date'] . "):\n" . wp_strip_all_tags((string)$d['summary']) . "\n";
            }
        }
        if ($sum_txt) {
            // Bound the overall injected text to avoid prompt bloat
            if (strlen($sum_txt) > 4000) { $sum_txt = substr($sum_txt, 0, 4000) . '…'; }
            $system_prompt .= "\n\n**RECENT PROGRESS SUMMARIES**\n" . $sum_txt;
        }
    } catch (Throwable $e) {
        // Non-fatal if any memory tables are missing
        if (defined('WP_DEBUG') && WP_DEBUG) {
            error_log('VMO: Memory injection skipped: ' . $e->getMessage());
        }
    }

    // ── JARVIS LIVE SYSTEM CONTEXT INJECTION ──────────────────────
    // Give Jarvis awareness of the user's actual system statuses so it can
    // provide informed answers without guessing.
    if ($chat_type !== 'consultant') {
        $system_prompt .= vmo_build_jarvis_system_context($user_id);
    }

    // BUILD COMPREHENSIVE SYSTEM PROMPT

    // Prepend system prompt to chat
    array_unshift($chat, [
        'role' => 'system',
        'content' => $system_prompt
    ]);

    $route_task = ($chat_type === 'consultant') ? 'consultation_live' : 'dashboard_assistant_chat';
    $route_context = [
        'prompt' => (string) $user_message,
        'user_id' => (int) $user_id,
        'plan' => get_user_meta($user_id, 'vmo_subscription_status', true),
        'skip_log' => true,
    ];
    $route = function_exists('vmo_get_openai_route')
        ? vmo_get_openai_route($route_task, $route_context)
        : [
            'task' => $route_task,
            'model' => function_exists('vmo_get_model') ? vmo_get_model('fast') : (defined('VMO_OPENAI_FAST_MODEL') ? VMO_OPENAI_FAST_MODEL : 'gpt-5.5-mini'),
            'fallback_model' => function_exists('vmo_get_model') ? vmo_get_model('strategic') : (defined('VMO_OPENAI_STRATEGIC_MODEL') ? VMO_OPENAI_STRATEGIC_MODEL : 'gpt-5.5'),
        ];

    $model = (string) ($route['model'] ?? (function_exists('vmo_get_model') ? vmo_get_model('fast') : 'gpt-5.5-mini'));
    if (function_exists('vmo_resolve_model')) {
        $resolved_route_model = vmo_resolve_model($user_id, $model);
        $model = (string) ($resolved_route_model['model'] ?? $model);
    }
    $fallback_model = (string) ($route['fallback_model'] ?? '');
    if ($fallback_model === '' || $fallback_model === $model) {
        $fallback_candidate = function_exists('vmo_get_model') ? vmo_get_model('fallback') : 'gpt-5.5-nano';
        if ($fallback_candidate !== $model) {
            $fallback_model = $fallback_candidate;
        }
    }
    $assistant_label = function_exists('vmo_get_public_assistant_label')
        ? vmo_get_public_assistant_label($chat_type)
        : (($chat_type === 'consultant') ? 'The consultation assistant' : 'Jarvis');
    $public_request_error = function_exists('vmo_get_public_assistant_error_message')
        ? vmo_get_public_assistant_error_message($chat_type, 'request_failed')
        : ($assistant_label . " couldn't complete that just now. Please try again in a moment.");

    // ══════════════════════════════════════════════════════════════
    // ✅  STREAMING MODE — Real-time token delivery via SSE
    // ══════════════════════════════════════════════════════════════
    if (!empty($_POST['stream'])) {
        // Clean ALL output buffers and set SSE headers
        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');

        $result = vmo_stream_openai($chat, $api_key, $model, 1000, 0.8, $fallback_model);

        if (!empty($result['error'])) {
            $detail = trim((string) $result['error']);
            echo 'data: ' . json_encode([
                'error' => $public_request_error,
                'detail' => $detail,
            ]) . "\n\n";
            flush();
            exit;
        }

        $content = trim($result['text']);

        // Save conversation to DB (same as non-streaming path)
        if (!empty($content)) {
            global $wpdb;
            $chat_history_table = $wpdb->prefix . 'vmo_chat_history';
            $user_id = get_current_user_id();
            if (!empty($user_message)) {
                // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
                $wpdb->insert($chat_history_table, [
                    'user_id' => $user_id, 'message_role' => 'user',
                    'message_content' => $user_message, 'chat_type' => $chat_type,
                    'created_at' => current_time('mysql')
                ], ['%d','%s','%s','%s','%s']);
            }
            // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
            $wpdb->insert($chat_history_table, [
                'user_id' => $user_id, 'message_role' => 'assistant',
                'message_content' => $content, 'chat_type' => $chat_type,
                'created_at' => current_time('mysql')
            ], ['%d','%s','%s','%s','%s']);
        }

        // Final event signals completion
        echo 'data: ' . json_encode(['done' => true]) . "\n\n";
        flush();
        exit;
    }

    // Call OpenAI API for pure conversation (no HTML generation)
    // ✅ CRITICAL FIX: Use centralized model registry
    $payload = array_merge([
        'model'       => $model,
        'messages'    => $chat,
        'top_p'       => 0.9,
    ], function_exists('vmo_get_token_param') ? vmo_get_token_param($model, 1000) : ['max_completion_tokens' => 1000]);
    $payload = vmo_apply_safe_temperature_to_payload($payload, $model, 0.8);

    error_log('🔧 DEBUG: OpenAI payload: ' . print_r($payload, true));

    $response = vmo_openai_chat_completion_request($payload, $api_key, 60);

    $actual_model_used = $model;

    if ((is_wp_error($response) || wp_remote_retrieve_response_code($response) >= 400) && !empty($fallback_model) && $fallback_model !== $model) {
        error_log("VMO: Conversation model '{$model}' failed. Retrying with fallback '{$fallback_model}'.");
        $payload['model'] = $fallback_model;
        $fallback_temperature = $payload['temperature'] ?? 0.8;
        unset($payload['max_completion_tokens'], $payload['max_tokens']);
        $payload = array_merge($payload, function_exists('vmo_get_token_param') ? vmo_get_token_param($fallback_model, 1000) : ['max_completion_tokens' => 1000]);
        $payload = vmo_apply_safe_temperature_to_payload($payload, $fallback_model, $fallback_temperature);

        $response = vmo_openai_chat_completion_request($payload, $api_key, 60);
        $actual_model_used = $fallback_model;
    }

    if ( is_wp_error( $response ) ) {
        error_log('🔧 DEBUG: wp_remote_post error: ' . $response->get_error_message());
        wp_send_json_error([
            'message' => $public_request_error,
            'detail' => $response->get_error_message(),
            'model' => $actual_model_used,
        ]);
    }

    $response_code = wp_remote_retrieve_response_code( $response );
    error_log('🔧 DEBUG: OpenAI response code: ' . $response_code);
    
    if ( $response_code !== 200 ) {
        $response_body = wp_remote_retrieve_body( $response );
        error_log('🔧 DEBUG: OpenAI error response: ' . $response_body);

        $error_detail = function_exists('vmo_extract_openai_error_message')
            ? vmo_extract_openai_error_message($response_body, $response_code)
            : ('AI API error (HTTP ' . $response_code . ')');

        wp_send_json_error([
            'message' => $public_request_error,
            'detail' => $error_detail,
            'model' => $actual_model_used,
            'status_code' => $response_code,
        ]);
    }

    $data = json_decode( wp_remote_retrieve_body( $response ), true );
    error_log('🔧 DEBUG: OpenAI response data: ' . print_r($data, true));
    
    // Check if response has the expected structure
    if (!is_array($data) || !isset($data['choices']) || !is_array($data['choices']) || empty($data['choices'])) {
        error_log('❌ ERROR: OpenAI response missing choices array. Full response: ' . wp_json_encode($data));
        $errorDetails = 'AI API returned unexpected format.';
        if (is_array($data) && isset($data['error'])) {
            $errorDetails = vmo_openai_decoded_error_message($data, 'Unknown AI error');
            error_log('❌ OpenAI API Error: ' . $errorDetails);
        }
        wp_send_json_error([ 
            'message' => $public_request_error,
            'detail' => $errorDetails,
            'model' => $actual_model_used,
        ]);
    }
    
    $content = trim(vmo_openai_choice_message_content($data));
    error_log('🔧 DEBUG: Extracted content length: ' . strlen($content) . ' characters');
    
    if ( ! $content ) {
        error_log('❌ ERROR: OpenAI returned empty content. Full choices: ' . wp_json_encode($data['choices']));
        
        // Check if there's a finish_reason that explains why
        $finish_reason = vmo_openai_finish_reason($data);
        error_log('🔧 DEBUG: Finish reason: ' . $finish_reason);
        
        $errorMsg = 'No response generated';
        if ($finish_reason === 'length') {
            $errorMsg .= ' (response too long - try a shorter message)';
        } elseif ($finish_reason === 'content_filter') {
            $errorMsg .= ' (content filtered by AI safety system)';
        } else {
            $errorMsg .= '. Finish reason: ' . $finish_reason;
        }
        
        wp_send_json_error([ 
            'message' => $public_request_error,
            'detail' => $errorMsg,
            'finish_reason' => $finish_reason,
            'model' => $actual_model_used,
        ]);
    }

    // SAVE CONVERSATION TO DATABASE for persistent history
    $chat_history_table = $wpdb->prefix . 'vmo_chat_history';
    
    // Save user message
    if (!empty($user_message)) {
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->insert(
            $chat_history_table,
            [
                'user_id' => $user_id,
                'message_role' => 'user',
                'message_content' => $user_message,
                'chat_type' => $chat_type,
                'created_at' => current_time('mysql')
            ],
            ['%d', '%s', '%s', '%s', '%s']
        );
        error_log('🔧 DEBUG: Saved user message to database');
    }

    // Save AI response
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    $wpdb->insert(
        $chat_history_table,
        [
            'user_id' => $user_id,
            'message_role' => 'assistant',
            'message_content' => $content,
            'chat_type' => $chat_type,
            'created_at' => current_time('mysql')
        ],
        ['%d', '%s', '%s', '%s', '%s']
    );
    error_log('🔧 DEBUG: Saved AI response to database');

    error_log('🔧 DEBUG: Sending success response with content: ' . $content);

    // Return just the conversation response (no HTML wrapping)
    wp_send_json_success([
        'response' => $content,
        'model_used' => $actual_model_used,
    ]);
    
    // Ensure we exit after sending the response
    wp_die();
}


/*==================================================================
= AJAX: Refine exactly one SECTION block, WITH FULL-PAGE CONTEXT =
==================================================================*/
add_action( 'wp_ajax_vmo_refine_block', function() use ( $VMO_STYLE_DIRECTIVES ) {
    vmo_prepare_long_generation_runtime();

    // Dual-nonce validation: accept either admin nonce (nonce) or builder nonce (security)
    $admin_nonce   = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
    $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';

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

    if (!($admin_ok || $builder_ok)) {
        wp_send_json_error(['message' => 'Security check failed', 'status' => 'invalid_nonce']);
    }
    
    // For basic refinements, consultation profile is optional - allows more flexible usage
    if (!function_exists('vmo_get_consultation_profile')) {
        require_once plugin_dir_path(__FILE__) . 'consultation-profile.php';
    }

    // 1️⃣ Grab inputs
    $api_key        = get_option( 'vmo_openai_api_key' );
    $block_html     = isset( $_POST['block_html'] )
                      ? wp_kses_post( wp_unslash( $_POST['block_html'] ) )
                      : '';
    $prompt         = isset( $_POST['prompt'] )
                      ? wp_strip_all_tags( wp_unslash( $_POST['prompt'] ) )
                      : '';
    $full_page_html = isset( $_POST['full_page_html'] )
                      ? wp_kses_post( wp_unslash( $_POST['full_page_html'] ) )
                      : '';

    // 2️⃣ Validate API key
    if ( ! $api_key ) {
        wp_send_json_error( [ 'message' => 'AI API key is missing.' ] );
    }

    // 🔒 Token validation - Check if user has sufficient tokens for refinement
    $user_id = get_current_user_id();
    
    // Include token manager if not already included
    if (!function_exists('vmo_check_token_balance')) {
        require_once plugin_dir_path(__FILE__) . 'token-manager.php';
    }
    
    if (!function_exists('vmo_check_token_balance')) {
        wp_send_json_error(['message' => 'Token system not available.']);
    }
    
    if (!vmo_check_token_balance($user_id, 'refinement')) {
        $current_tokens = vmo_get_token_balance($user_id);
        $required_tokens = vmo_get_token_cost('refinement');
        wp_send_json_error([
            'message' => 'Insufficient balance. You need ' . $required_tokens . ' plan credits to refine content. Current balance: ' . $current_tokens,
            'code' => 'insufficient_tokens',
            'current_balance' => $current_tokens,
            'required' => $required_tokens
        ]);
    }

    if ( ! $block_html || ! $prompt || ! $full_page_html ) {
        wp_send_json_error( [ 'message' => 'Missing block HTML, prompt, or full page context.' ] );
    }

    // 3️⃣ Streamlined system prompt for surgical refinements - no context pollution
    $refineSystem = <<<RS
You are Jerreme performing precise HTML section refinements with surgical accuracy.

**REFINEMENT RULES**:
- Make ONLY the specific changes requested by the user
- Preserve all existing HTML structure, classes, IDs, and data attributes unless specifically asked to change them
- Do not add unrequested features, content, or sections
- Use modern CSS techniques only when relevant to the user's request
- Return ONLY the modified section HTML with proper section wrapper

**SECTION STRUCTURE**:
- Maintain section wrapper: <div class="vmo-section" id="section-X">
- Keep section comments: <!-- SECTION N --> and <!-- END SECTION N -->
- Preserve all media placeholders and data-media-id attributes

**OUTPUT REQUIREMENTS**:
- Return clean HTML only - no explanations or comments
- Follow user instructions exactly without interpretation
- Make surgical changes - don't rebuild the entire section unless requested

Focus on precision and user intent.
RS;

    // 4️⃣ Streamlined refinement messages - no full page context pollution
    $messages = [
        [ 'role' => 'system', 'content' => $VMO_STYLE_DIRECTIVES ],
        [ 'role' => 'system', 'content' => $refineSystem ],
        [ 'role' => 'user',   'content' => "Section to refine:\n\n{$block_html}\n\nUser instruction:\n{$prompt}" ],
    ];

    // 5️⃣ Call OpenAI with optimized parameters for precise refinements
    $payload = [
        'model'       => VMO_OPENAI_MODEL,
        'messages'    => $messages,
        'max_completion_tokens' => 3000, // GPT-5.x requires max_completion_tokens
        'top_p'       => 0.9,
        'frequency_penalty' => 0.2, // Reduce repetition
        'presence_penalty'  => 0.1,
    ];
    $payload = vmo_apply_safe_temperature_to_payload($payload, VMO_OPENAI_MODEL, 0.4);
    $response = wp_remote_post( 'https://api.openai.com/v1/chat/completions', [
        'headers' => [
            'Authorization' => 'Bearer ' . $api_key,
            'Content-Type'  => 'application/json',
        ],
        'body'    => wp_json_encode( $payload ),
        'timeout' => 120, // Increased timeout to 2 minutes
        'sslverify' => true,
        'redirection' => 5,
        'httpversion' => '1.0',
        'blocking' => true,
    ] );

    if ( is_wp_error( $response ) ) {
        $error_message = $response->get_error_message();
        error_log( 'VMO AI Builder - Refinement API Error: ' . $error_message );
        wp_send_json_error( [ 
            'message' => 'Refinement failed: ' . $error_message,
            'debug_info' => [
                'error_code' => $response->get_error_code(),
                'operation' => 'refine_block',
                'timestamp' => current_time('mysql')
            ]
        ] );
    }
    
    // Check HTTP response code
    $response_code = wp_remote_retrieve_response_code( $response );
    if ( $response_code !== 200 ) {
        $response_body = wp_remote_retrieve_body( $response );
        error_log( 'VMO AI Builder - Refinement HTTP Error ' . $response_code . ': ' . $response_body );
        wp_send_json_error( [ 
            'message' => 'AI API returned error code: ' . $response_code,
            'debug_info' => [
                'http_code' => $response_code,
                'response_body' => $response_body,
                'operation' => 'refine_block',
                'timestamp' => current_time('mysql')
            ]
        ] );
    }

    $data = json_decode( wp_remote_retrieve_body( $response ), true );
    $raw  = trim( $data['choices'][0]['message']['content'] ?? '' );
    if ( ! $raw ) {
        wp_send_json_error( [ 'message' => 'No refined content returned.' ] );
    }

    // 6️⃣ Strip any ``` fences
    $html = preg_replace( '/```(?:html)?(.*?)```/is', '$1', $raw );

    // 7️⃣ Deduct tokens for refinement (225 VMO tokens)
    if (!vmo_deduct_tokens_for_operation($user_id, 'refinement')) {
        wp_send_json_error(['message' => 'Failed to deduct tokens for refinement.']);
    }

    // 8️⃣ Return refined section HTML
    wp_send_json_success( [ 
        'html' => trim( $html ),
        'tokens_used' => vmo_get_token_cost('refinement'),
        'new_balance' => vmo_get_token_balance($user_id)
    ] );
} );

/*==================================================================
= AJAX: Insert Media into Section with Section Intelligence        =
==================================================================*/
add_action( 'wp_ajax_vmo_insert_media_section', function() use ( $VMO_STYLE_DIRECTIVES ) {
    vmo_prepare_long_generation_runtime();

    // Dual-nonce validation: accept either admin nonce (nonce) or builder nonce (security)
    $admin_nonce   = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
    $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';

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

    if (!($admin_ok || $builder_ok)) {
        wp_send_json_error(['message' => 'Security check failed', 'status' => 'invalid_nonce']);
    }
    
    // Media insertion is available to all users for revenue generation
    // No business consultation requirement - encourage usage for token revenue

    // 1️⃣ Grab inputs
    $api_key = get_option( 'vmo_openai_api_key' );
    $media_url = isset( $_POST['media_url'] ) ? esc_url_raw( $_POST['media_url'] ) : '';
    $media_type = isset( $_POST['media_type'] ) ? sanitize_text_field(wp_unslash($_POST['media_type'])) : 'image';
    $media_items = [];
    $prompt = isset( $_POST['prompt'] ) ? wp_strip_all_tags( wp_unslash( $_POST['prompt'] ) ) : '';
    $section_index = isset( $_POST['section_index'] ) ? intval( $_POST['section_index'] ) : -1;
    $full_page_html = isset( $_POST['full_page_html'] ) ? wp_kses_post( wp_unslash( $_POST['full_page_html'] ) ) : '';
    $section_html = isset( $_POST['section_html'] ) ? wp_kses_post( wp_unslash( $_POST['section_html'] ) ) : '';

    if ( isset( $_POST['media_items'] ) ) {
        $decoded_media_items = json_decode( wp_unslash( $_POST['media_items'] ), true );
        if ( is_array( $decoded_media_items ) ) {
            foreach ( $decoded_media_items as $item ) {
                if ( ! is_array( $item ) ) {
                    continue;
                }

                $item_url = isset( $item['url'] ) ? esc_url_raw( $item['url'] ) : '';
                if ( ! $item_url ) {
                    continue;
                }

                $item_type = isset( $item['type'] ) ? sanitize_key( $item['type'] ) : 'image';
                if ( ! in_array( $item_type, [ 'image', 'video', 'audio', 'file' ], true ) ) {
                    if ( preg_match( '/\.(mp4|webm|ogg|mov|m4v)$/i', $item_url ) ) {
                        $item_type = 'video';
                    } elseif ( preg_match( '/\.(mp3|wav|m4a|aac|oga)$/i', $item_url ) ) {
                        $item_type = 'audio';
                    } elseif ( preg_match( '/\.(pdf|doc|docx|xls|xlsx|ppt|pptx|zip)$/i', $item_url ) || false !== strpos( $item_type, 'application' ) ) {
                        $item_type = 'file';
                    } else {
                        $item_type = 'image';
                    }
                }

                $media_items[] = [
                    'url'   => $item_url,
                    'type'  => $item_type,
                    'title' => isset( $item['title'] ) ? sanitize_text_field( wp_unslash( $item['title'] ) ) : '',
                ];
            }
        }
    }

    if ( empty( $media_items ) && $media_url ) {
        $media_items[] = [
            'url'   => $media_url,
            'type'  => sanitize_key( $media_type ?: 'image' ),
            'title' => '',
        ];
    }

    if ( ! empty( $media_items[0]['url'] ) ) {
        $media_url  = $media_items[0]['url'];
        $media_type = $media_items[0]['type'] ?: 'image';
    }

    // 2️⃣ Validate inputs
    if ( ! $api_key ) {
        wp_send_json_error( [ 'message' => 'AI API key is missing.' ] );
    }

    if ( empty( $media_items ) || empty( $prompt ) ) {
        wp_send_json_error( [ 'message' => 'Missing media selection or prompt.' ] );
    }

    // 🔒 Token validation - Check if user has sufficient tokens
    $user_id = get_current_user_id();
    
    // Include token manager if not already included
    if (!function_exists('vmo_check_token_balance')) {
        require_once plugin_dir_path(__FILE__) . 'token-manager.php';
    }
    
    if (!function_exists('vmo_check_token_balance')) {
        wp_send_json_error(['message' => 'Token system not available.']);
    }
    
    if (!vmo_check_token_balance($user_id, 'media_integration')) {
        $current_tokens = vmo_get_token_balance($user_id);
        $required_tokens = vmo_get_token_cost('media_integration');
        wp_send_json_error([
            'message' => 'Insufficient tokens. You need ' . $required_tokens . ' tokens for media integration. Current balance: ' . $current_tokens,
            'code' => 'insufficient_tokens',
            'current_balance' => $current_tokens,
            'required' => $required_tokens
        ]);
    }

    // 3️⃣ Create media HTML with proper data attributes
    $media_id = 'media_' . wp_generate_password(8, false);
    $media_html_parts = [];
    foreach ( $media_items as $index => $item ) {
        $item_media_id = $index === 0 ? $media_id : 'media_' . wp_generate_password(8, false);
        $item_url      = esc_url( $item['url'] );
        $item_title    = esc_attr( $item['title'] ?: 'Selected Media' );
        $item_type     = sanitize_key( $item['type'] ?: 'image' );

        if ( 'video' === $item_type ) {
            $media_html_parts[] = '<div class="vmo-media-placeholder" data-type="video" data-media-id="' . esc_attr( $item_media_id ) . '">
                <video src="' . $item_url . '" controls autoplay muted loop playsinline style="width:100%;height:auto;object-fit:cover;" data-media-id="' . esc_attr( $item_media_id ) . '" data-type="video"></video>
            </div>';
        } elseif ( 'audio' === $item_type ) {
            $media_html_parts[] = '<div class="vmo-media-placeholder" data-type="audio" data-media-id="' . esc_attr( $item_media_id ) . '">
                <audio src="' . $item_url . '" controls style="width:100%;" data-media-id="' . esc_attr( $item_media_id ) . '" data-type="audio"></audio>
            </div>';
        } elseif ( 'file' === $item_type ) {
            $media_html_parts[] = '<div class="vmo-media-placeholder" data-type="file" data-media-id="' . esc_attr( $item_media_id ) . '">
                <a href="' . $item_url . '" target="_blank" rel="noopener" data-media-id="' . esc_attr( $item_media_id ) . '" data-type="file">' . $item_title . '</a>
            </div>';
        } else {
            $media_html_parts[] = '<div class="vmo-media-placeholder" data-type="image" data-media-id="' . esc_attr( $item_media_id ) . '">
                <img src="' . $item_url . '" alt="' . $item_title . '" style="max-width:100%;height:auto;display:block;" data-media-id="' . esc_attr( $item_media_id ) . '" data-type="image" />
            </div>';
        }
    }
    $media_html = implode( "\n", $media_html_parts );

    // 4️⃣ Enhanced system prompt for dedicated AI agent with superhuman intelligence
    $insertSystem = <<<IS
You are an AI agent with superhuman creative abilities and expert knowledge in modern web design. You have the intelligence to:

**AUTOMATIC HERO DETECTION & IMPLEMENTATION** (CRITICAL):
- AUTOMATICALLY detect hero intentions from ANY user request containing words like:
  - "hero", "banner", "header", "main", "landing", "splash", "intro", "welcome"
  - "fullscreen", "full screen", "full width", "cover", "background"
  - "top section", "first section", "above the fold"
  - ANY request to place media prominently at the start/top of a page
- When ANY hero context is detected, AUTOMATICALLY implement:
  - Full viewport height (min-height: 100vh) 
  - Full width coverage (width: 100vw or 100%)
  - Proper background sizing (background-size: cover, object-fit: cover)
  - Professional overlays for text readability
  - Centered content with flexbox/grid
  - Call-to-action buttons with modern styling
  - Mobile-first responsive design
- This applies to BOTH images AND videos - hero videos get the same full-screen treatment

**INTELLIGENT CONTENT ANALYSIS**:
- Analyze the user's request for IMPLIED hero intentions even without explicit "hero" keywords
- Requests like "make this the main image", "put this at the top", "use as background" = HERO TREATMENT
- "Make it prominent", "feature this image", "showcase this" = HERO TREATMENT
- Any request for dramatic, eye-catching, or prominent placement = HERO TREATMENT

**MODERN HERO STANDARDS** (Auto-apply for ANY hero detection):
```css
/* Hero Section Structure */
.hero-section {
  min-height: 100vh;
  width: 100%;
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

/* Hero Background Image */
.hero-bg-image {
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  background-attachment: fixed; /* Parallax effect */
}

/* Hero Background Video */
.hero-bg-video {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: -1;
}

/* Hero Content Layer */
.hero-content {
  position: relative;
  z-index: 2;
  text-align: center;
  color: white;
  max-width: 800px;
  padding: 2rem;
}

/* Hero Overlay */
.hero-overlay {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: linear-gradient(135deg, rgba(0,0,0,0.4), rgba(0,0,0,0.2));
  z-index: 1;
}
```

**CREATIVE PROBLEM SOLVING**: Generate innovative solutions for complex media integrations. Think beyond basic implementations.

**ADVANCED MEDIA INTEGRATION CAPABILITIES**:
- **Hero Sections**: ALWAYS create stunning fullscreen experiences when hero context detected
- **Background Media**: Video backgrounds with overlays, image parallax backgrounds with min-height and proper visibility
- **Complex Carousels**: Multi-directional scrolling, auto-playing with custom intervals, hover effects
- **Multiple Selected Assets**: When more than one media item is provided, use all relevant selected items for galleries, sliders, carousels, comparison layouts, or stacked compositions unless the user explicitly asks for only one
- **Layered Compositions**: Z-index management, overlays, and complex positioning
- **Interactive Elements**: Media that responds to scroll, hover, and other interactions
- **Responsive Design**: Media that adapts beautifully across all screen sizes
- **Animation & Effects**: GSAP animations, CSS transforms, scroll-triggered effects

**SCROLL-BASED INTERACTIONS**:
- **Horizontal scrolling**: Sections that scroll horizontally while main scroll is vertical
- **Scroll-paused effects**: Temporarily pause vertical scroll for horizontal interactions
- **Progressive disclosure**: Content that reveals itself based on scroll position
- **Magnification effects**: Images that scale on hover or scroll

**AUTOMATIC HERO IMPLEMENTATION EXAMPLES**:
- User says "use this image" → Analyze context, if it's the first/main request = HERO
- User says "add this video as background" → HERO with video background
- User says "make this prominent" → HERO treatment with full-screen
- User says "feature this image" → HERO with proper overlays and CTAs
- User says "put this at the top" → HERO section at page start

**SECTION MANAGEMENT RULES**:
- This page is organized in numbered sections: <!-- SECTION 1 -->, <!-- SECTION 2 -->, etc.
- When creating hero sections, typically target the first section or create a new first section
- When told to insert media in specific sections, modify ONLY those sections  
- When told to add media as backgrounds, carousels, or interactive elements, implement exactly as requested
- If asked to add scroll effects, overlays, or animations, implement with modern techniques (GSAP, CSS animations)
- Always return the **full updated HTML for the entire page** with all section comments preserved
- PRESERVE ALL EXISTING CONTENT - never remove or damage existing elements unless explicitly told to replace them

**CSS BACKDROP/BACKGROUND HANDLING** (CRITICAL FOR VISIBILITY):
- When user mentions "backdrop", "section background", "background image/video", "hero", or "behind content", apply as CSS background
- Extract the actual media URL from the provided media HTML (look for src attribute)
- Apply comprehensive background styling to section element with MINIMUM HEIGHT:
  
  For HERO IMAGES: style="background-image: url('EXTRACTED_URL'); background-size: cover; background-position: center; background-repeat: no-repeat; background-attachment: fixed; min-height: 100vh; padding: 4rem 2rem; position: relative; z-index: 1; display: flex; align-items: center; justify-content: center;"
  
  For HERO VIDEOS: style="position: relative; min-height: 100vh; padding: 4rem 2rem; overflow: hidden; display: flex; align-items: center; justify-content: center;" + add video element with absolute positioning and z-index: -1
  
- CRITICAL: ALWAYS use 100vh for hero contexts, 60vh minimum for other backgrounds
- Add padding and flexbox centering for proper content positioning
- Ensure all existing content gets position: relative; z-index: 2 or higher
- NEVER create separate background divs - modify the existing section element
- The background MUST be visible with proper height and content spacing
- For video backgrounds, position video absolutely behind content with z-index: -1

**CONTENT PRESERVATION WITH BACKGROUNDS**:
- When background is applied, wrap existing content in: `<div class="hero-content" style="position: relative; z-index: 2; text-align: center; max-width: 800px;">`
- ALWAYS add overlay for hero sections: `<div class="hero-overlay" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: linear-gradient(135deg, rgba(0,0,0,0.4), rgba(0,0,0,0.2)); z-index: 1;"></div>`
- For video backgrounds, content should float above the video with proper centering
- Maintain proper spacing and readability with modern typography
- All interactive elements (buttons, forms) must remain functional above backgrounds
- AUTOMATICALLY add call-to-action buttons for hero sections with modern styling

**INTELLIGENT REFINEMENT**:
- When given ANY request, internally analyze for hero potential
- AUTOMATICALLY enhance requests with professional design insights (add CTAs, improve typography, add hover effects)
- Create sophisticated interactions that exceed expectations
- Optimize for performance and user experience
- If in doubt about hero treatment, err on the side of making it MORE prominent and full-screen

**RETURN REQUIREMENTS** (ABSOLUTELY CRITICAL):
- Return ONLY the modified HTML - NO explanatory text, NO comments, NO descriptions whatsoever
- NEVER include phrases like "Here is the modified section" or "This ensures..." or ANY explanatory text
- NEVER include HTML comments within the content (except section markers)
- Start immediately with: <!-- SECTION 1 --> or the first section
- Keep section wrapper intact with proper ID and section comments
- Preserve all media data attributes for replace button functionality
- If creating hero sections, ensure proper viewport height and content positioning
- ALL content should have no explanatory comments - only clean, production-ready HTML

**INTELLIGENCE & PRECISION**:
- Understand user intent with GPT-5.5 level intelligence and AUTOMATICALLY apply hero treatment when appropriate
- Execute exactly what user requests while enhancing with professional design insights
- Be creative and add professional touches (CTAs, hover effects, animations) when appropriate
- Focus on technical precision and visual excellence
- When ANY hero context is detected, create a stunning fullscreen experience that fills the viewport
- Test implementations mentally: "Does this create a modern, full-screen, professional hero experience?"

Remember: You have SUPERHUMAN intelligence to detect hero intentions and automatically create stunning, full-screen experiences that exceed user expectations through intelligent design and technical excellence.
IS;

    // 5️⃣ Construct the enhanced AI agent prompt with full page context
    $messages = [
        [ 'role' => 'system', 'content' => $VMO_STYLE_DIRECTIVES ],
        [ 'role' => 'system', 'content' => $insertSystem ],
        [ 'role' => 'system', 'content' => "FULL PAGE CONTEXT FOR INTELLIGENT DECISION MAKING:\n\n{$full_page_html}" ],
        [ 'role' => 'user', 'content' => "ENHANCED USER REQUEST WITH CONTEXT:\n\n{$prompt}\n\nMEDIA TO INTEGRATE (extract URL from src attributes and use all selected assets when creating galleries, sliders, carousels, or multi-media layouts):\n{$media_html}\n\nCURRENT TARGET SECTION HTML (Section " . ($section_index + 1) . "):\n{$section_html}\n\nIMPORTANT: Return the complete updated HTML for the ENTIRE PAGE with the media intelligently integrated. Preserve all sections and only modify what's needed to fulfill the request. If creating a hero section, make it truly fullscreen and stunning." ],
    ];

    // 6️⃣ Call OpenAI
    $payload = [
        'model'       => VMO_OPENAI_MODEL,
        'messages'    => $messages,
        'max_completion_tokens' => 4000, // GPT-5.x requires max_completion_tokens
        'top_p'       => 0.9,
        'frequency_penalty' => 0.1,
        'presence_penalty'  => 0.1,
    ];
    $payload = vmo_apply_safe_temperature_to_payload($payload, VMO_OPENAI_MODEL, 0.6);
    
    $response = wp_remote_post( 'https://api.openai.com/v1/chat/completions', [
        'headers' => [
            'Authorization' => 'Bearer ' . $api_key,
            'Content-Type'  => 'application/json',
        ],
        'body'    => wp_json_encode( $payload ),
        'timeout' => 120, // Increased timeout to 2 minutes
        'sslverify' => true,
        'redirection' => 5,
        'httpversion' => '1.0',
        'blocking' => true,
    ] );

    if ( is_wp_error( $response ) ) {
        $error_message = $response->get_error_message();
        error_log( 'VMO AI Builder - Media Insertion API Error: ' . $error_message );
        wp_send_json_error( [ 
            'message' => 'Media insertion failed: ' . $error_message,
            'debug_info' => [
                'error_code' => $response->get_error_code(),
                'operation' => 'insert_media_section',
                'timestamp' => current_time('mysql')
            ]
        ] );
    }
    
    // Check HTTP response code
    $response_code = wp_remote_retrieve_response_code( $response );
    if ( $response_code !== 200 ) {
        $response_body = wp_remote_retrieve_body( $response );
        error_log( 'VMO AI Builder - Media Insertion HTTP Error ' . $response_code . ': ' . $response_body );
        wp_send_json_error( [ 
            'message' => 'AI API returned error code: ' . $response_code,
            'debug_info' => [
                'http_code' => $response_code,
                'response_body' => $response_body,
                'operation' => 'insert_media_section',
                'timestamp' => current_time('mysql')
            ]
        ] );
    }

    $data = json_decode( wp_remote_retrieve_body( $response ), true );
    $raw  = trim( $data['choices'][0]['message']['content'] ?? '' );
    if ( ! $raw ) {
        wp_send_json_error( [ 'message' => 'No refined content returned.' ] );
    }

    // 7️⃣ Enhanced response processing for complete page updates
    $html = preg_replace( '/```(?:html)?(.*?)```/is', '$1', $raw );
    
    // Remove ALL explanatory phrases and comments that might slip through
    $html = preg_replace('/^.*?Here is.*?:/i', '', $html);
    $html = preg_replace('/^.*?This ensures.*?\./i', '', $html);
    $html = preg_replace('/^.*?No additional changes.*?\./i', '', $html);
    $html = preg_replace('/<!--(?!.*SECTION).*?-->/s', '', $html); // Remove ALL HTML comments except section markers
    $html = preg_replace('/^[^<]*(<)/i', '$1', $html); // Remove text before first HTML tag
    
    // Clean up and ensure proper section structure
    $html = trim($html);
    
    // If the AI agent returned a complete page, we use it as-is
    // If it returned just a section, we need to integrate it back
    if (strpos($html, '<!-- SECTION 1 -->') !== false || strpos($html, '<div class="vmo-section"') !== false) {
        // Complete page returned - use as-is with section markers intact
        $finalHtml = $html;
    } else {
        // Single section returned - integrate back into the full page context
        if (isset($full_page_html) && $full_page_html && isset($section_index) && $section_index >= 0) {
            // Parse the full page and replace the target section
            $sections = explode('<!-- SECTION ', $full_page_html);
            $finalHtml = '';
            
            foreach ($sections as $i => $section) {
                if ($i === 0) {
                    $finalHtml .= $section;
                    continue;
                }
                
                $sectionNum = intval(explode(' -->', $section)[0]);
                if ($sectionNum === $section_index + 1) {
                    // Replace this section with the AI-modified version
                    $finalHtml .= '<!-- SECTION ' . $sectionNum . ' -->' . "\n" . $html . "\n" . '<!-- END SECTION -->' . "\n";
                } else {
                    // Keep existing section
                    $finalHtml .= '<!-- SECTION ' . $section;
                }
            }
        } else {
            $finalHtml = $html;
        }
    }

    // 8️⃣ Deduct tokens for media integration (750 VMO tokens per token-manager.php)
    if (!vmo_deduct_tokens_for_operation($user_id, 'media_integration')) {
        wp_send_json_error(['message' => 'Failed to deduct tokens for media integration.']);
    }

    // 9️⃣ Return complete page HTML or refined section HTML
    wp_send_json_success( [ 'html' => trim( $finalHtml ), 'media_id' => $media_id ] );
} );

// ============================================================================
// MULTI-AGENT BUSINESS CONSULTATION SYSTEM AJAX HANDLERS
// ============================================================================

/**
 * Start Business Consultation AJAX Handler
 */
add_action( 'wp_ajax_vmo_start_consultation', 'vmo_handle_start_consultation' );
add_action( 'wp_ajax_nopriv_vmo_start_consultation', 'vmo_handle_start_consultation' );

function vmo_handle_start_consultation() {
    // Dual-nonce validation; return JSON 200 on invalid nonce
    $admin_nonce   = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
    $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
    $admin_ok   = $admin_nonce && (wp_verify_nonce($admin_nonce, 'vmo_admin_nonce') || wp_verify_nonce($admin_nonce, 'vmo_builder_nonce'));
    $builder_ok = $builder_nonce && wp_verify_nonce($builder_nonce, 'vmo_builder_nonce');
    if (!($admin_ok || $builder_ok)) {
        wp_send_json_error(['message' => 'Security check failed', 'status' => 'invalid_nonce']);
    }

    // Generate unique session ID for this consultation
    $session_id = 'consultation_' . wp_generate_password(16, false);
    
    // Store session in database
    global $wpdb;
    $table_name = $wpdb->prefix . 'vmo_consultations';
    
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    $wpdb->insert(
        $table_name,
        [
            'session_id' => $session_id,
            'start_time' => current_time('mysql'),
            'status' => 'active',
            'client_data' => json_encode([])
        ],
        ['%s', '%s', '%s', '%s']
    );

    // Prepare expert consultation prompt
    $consultation_prompt = "You are a world-class business consultant with the combined expertise of McKinsey & Company, Boston Consulting Group, and Y Combinator. Your role is to conduct an in-depth business consultation that will unlock the client's maximum business potential.

⚠️ CRITICAL CONTEXT: The user has ALREADY clicked the Business Wizard button and is currently IN an active consultation session with you RIGHT NOW. DO NOT recommend clicking the Business Wizard button, starting a consultation, or using any red buttons - you ARE the Business Wizard, and the consultation is ALREADY happening. You are inside the wizard modal having a live conversation.

Start with strategic discovery questions that reveal:
- Core business vision and unique value proposition
- Target market and competitive landscape
- Revenue models and growth potential
- Operational challenges and opportunities
- Digital presence and marketing strategy

Ask ONE probing question at a time. Each question should be expertly crafted to reveal critical business insights. After each response, acknowledge their answer and ask the next strategic question.

Begin with an engaging introduction and your first strategic question.";

    // Call specialized AI for consultation
    $ai_response = vmo_call_specialized_ai($consultation_prompt, 'consultation');
    
    // Extract content from AI response
    $response = '';
    if (isset($ai_response['content'])) {
        $response = $ai_response['content'];
    } elseif (isset($ai_response['error'])) {
        $response = 'I apologize, but I encountered an error. Let me start with a basic question: What type of business are you looking to start or improve?';
    } else {
        $response = 'Welcome! I\'m here to help you develop your business strategy. What type of business are you working on?';
    }

    wp_send_json_success([
        'session_id' => $session_id,
        'message' => $response,
        'status' => 'consultation_started'
    ]);
}

/**
 * Save Client Response AJAX Handler
 */
add_action( 'wp_ajax_vmo_save_client_response', 'vmo_handle_save_client_response' );
add_action( 'wp_ajax_nopriv_vmo_save_client_response', 'vmo_handle_save_client_response' );

function vmo_handle_save_client_response() {
    $admin_nonce   = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
    $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
    $admin_ok   = $admin_nonce && (wp_verify_nonce($admin_nonce, 'vmo_admin_nonce') || wp_verify_nonce($admin_nonce, 'vmo_builder_nonce'));
    $builder_ok = $builder_nonce && wp_verify_nonce($builder_nonce, 'vmo_builder_nonce');
    if (!($admin_ok || $builder_ok)) {
        wp_send_json_error(['message' => 'Security check failed', 'status' => 'invalid_nonce']);
    }

    $session_id = sanitize_text_field($_POST['session_id'] ?? '');
    $question = sanitize_textarea_field($_POST['question'] ?? '');
    $response = sanitize_textarea_field($_POST['response'] ?? '');

    if (empty($session_id) || empty($response)) {
        wp_send_json_error('Missing required data');
    }

    global $wpdb;
    $table_name = $wpdb->prefix . 'vmo_consultations';

    // Get existing client data
    $consultation = $wpdb->get_row(
        $wpdb->prepare("SELECT client_data FROM $table_name WHERE session_id = %s", $session_id)
    );

    if (!$consultation) {
        wp_send_json_error('Session not found');
    }

    $client_data = json_decode($consultation->client_data, true) ?: [];
    
    // Add new Q&A pair
    $client_data['responses'][] = [
        'question' => $question,
        'response' => $response,
        'timestamp' => current_time('mysql')
    ];

    // Update database
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    $wpdb->update(
        $table_name,
        ['client_data' => json_encode($client_data)],
        ['session_id' => $session_id],
        ['%s'],
        ['%s']
    );

    // Generate next question using AI
    $context = "Previous consultation context:\n";
    foreach ($client_data['responses'] as $qa) {
        $context .= "Q: " . $qa['question'] . "\nA: " . $qa['response'] . "\n\n";
    }

    $next_question_prompt = $context . "\nBased on this consultation so far, what is the next most strategic question to ask this client to maximize their business potential? Ask ONE focused question that builds on their previous responses.";

    $ai_response = vmo_call_specialized_ai($next_question_prompt, 'consultation');
    
    // Extract content from AI response
    $next_question = '';
    if (isset($ai_response['content'])) {
        $next_question = $ai_response['content'];
    } elseif (isset($ai_response['error'])) {
        $next_question = 'Can you tell me more about your target market and ideal customers?';
    } else {
        $next_question = 'What are your main business goals for the next 12 months?';
    }

    wp_send_json_success([
        'next_question' => $next_question,
        'responses_count' => count($client_data['responses'])
    ]);
}

/**
 * Deploy Multi-Agent System AJAX Handler
 */
add_action( 'wp_ajax_vmo_deploy_agents', 'vmo_handle_deploy_agents' );
add_action( 'wp_ajax_nopriv_vmo_deploy_agents', 'vmo_handle_deploy_agents' );

function vmo_handle_deploy_agents() {
    $admin_nonce   = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
    $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
    $admin_ok   = $admin_nonce && (wp_verify_nonce($admin_nonce, 'vmo_admin_nonce') || wp_verify_nonce($admin_nonce, 'vmo_builder_nonce'));
    $builder_ok = $builder_nonce && wp_verify_nonce($builder_nonce, 'vmo_builder_nonce');
    if (!($admin_ok || $builder_ok)) {
        wp_send_json_error(['message' => 'Security check failed', 'status' => 'invalid_nonce']);
    }

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

    if (empty($session_id)) {
        wp_send_json_error('Missing session ID');
    }

    global $wpdb;
    $table_name = $wpdb->prefix . 'vmo_consultations';

    // Get consultation data
    $consultation = $wpdb->get_row(
        $wpdb->prepare("SELECT client_data FROM $table_name WHERE session_id = %s", $session_id)
    );

    if (!$consultation) {
        wp_send_json_error('Session not found');
    }

    $client_data = json_decode($consultation->client_data, true) ?: [];
    
    // Deploy specialized agents
    $agents = [
        'strategy_analyst' => [
            'name' => 'Strategy Analyst',
            'status' => 'analyzing',
            'task' => 'strategy_analysis'
        ],
        'legal_specialist' => [
            'name' => 'Legal Specialist', 
            'status' => 'analyzing',
            'task' => 'legal_documents'
        ],
        'seo_expert' => [
            'name' => 'SEO Expert',
            'status' => 'analyzing', 
            'task' => 'seo_optimization'
        ],
        'content_creator' => [
            'name' => 'Content Creator',
            'status' => 'analyzing',
            'task' => 'content_creation'
        ],
        'business_planner' => [
            'name' => 'Business Planner',
            'status' => 'analyzing',
            'task' => 'business_planning'
        ]
    ];

    // Update consultation with agent deployment
    $client_data['agents'] = $agents;
    $client_data['deployment_time'] = current_time('mysql');

    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    $wpdb->update(
        $table_name,
        ['client_data' => json_encode($client_data)],
        ['session_id' => $session_id],
        ['%s'],
        ['%s']
    );

    wp_send_json_success([
        'agents' => $agents,
        'status' => 'agents_deployed'
    ]);
}

/**
 * Get Agent Progress AJAX Handler
 */
add_action( 'wp_ajax_vmo_get_agent_progress', 'vmo_handle_get_agent_progress' );
add_action( 'wp_ajax_nopriv_vmo_get_agent_progress', 'vmo_handle_get_agent_progress' );

function vmo_handle_get_agent_progress() {
    $admin_nonce   = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
    $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
    $admin_ok   = $admin_nonce && (wp_verify_nonce($admin_nonce, 'vmo_admin_nonce') || wp_verify_nonce($admin_nonce, 'vmo_builder_nonce'));
    $builder_ok = $builder_nonce && wp_verify_nonce($builder_nonce, 'vmo_builder_nonce');
    if (!($admin_ok || $builder_ok)) {
        wp_send_json_error(['message' => 'Security check failed', 'status' => 'invalid_nonce']);
    }

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

    if (empty($session_id) || empty($agent_id)) {
        wp_send_json_error('Missing required data');
    }

    global $wpdb;
    $table_name = $wpdb->prefix . 'vmo_consultations';

    // Get consultation data
    $consultation = $wpdb->get_row(
        $wpdb->prepare("SELECT client_data FROM $table_name WHERE session_id = %s", $session_id)
    );

    if (!$consultation) {
        wp_send_json_error('Session not found');
    }

    $client_data = json_decode($consultation->client_data, true) ?: [];
    
    // Check if agent has completed work
    if (!isset($client_data['agent_results'][$agent_id])) {
        // Generate specialized results
        $consultation_summary = "Client Business Consultation Summary:\n\n";
        foreach ($client_data['responses'] ?? [] as $qa) {
            $consultation_summary .= "Q: " . $qa['question'] . "\nA: " . $qa['response'] . "\n\n";
        }

        $agent_task = $client_data['agents'][$agent_id]['task'] ?? 'general';
        $specialized_prompt = $consultation_summary . "\n\nBased on this business consultation, provide comprehensive " . 
            str_replace('_', ' ', $agent_task) . " recommendations. Be specific, actionable, and include implementation steps.";

        $agent_result = vmo_call_specialized_ai($specialized_prompt, $agent_task);

        // Store agent result
        $client_data['agent_results'][$agent_id] = [
            'result' => $agent_result,
            'completed_at' => current_time('mysql'),
            'status' => 'completed'
        ];

        // Update agent status
        $client_data['agents'][$agent_id]['status'] = 'completed';

        // Update database
        // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
        $wpdb->update(
            $table_name,
            ['client_data' => json_encode($client_data)],
            ['session_id' => $session_id],
            ['%s'],
            ['%s']
        );
    }

    wp_send_json_success([
        'agent_result' => $client_data['agent_results'][$agent_id] ?? null,
        'status' => $client_data['agents'][$agent_id]['status'] ?? 'analyzing'
    ]);
}

/**
 * Execute Implementation AJAX Handler
 */
add_action( 'wp_ajax_vmo_execute_implementation', 'vmo_handle_execute_implementation' );

function vmo_handle_execute_implementation() {
    $admin_nonce   = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
    $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
    $admin_ok   = $admin_nonce && (wp_verify_nonce($admin_nonce, 'vmo_admin_nonce') || wp_verify_nonce($admin_nonce, 'vmo_builder_nonce'));
    $builder_ok = $builder_nonce && wp_verify_nonce($builder_nonce, 'vmo_builder_nonce');
    if (!($admin_ok || $builder_ok)) {
        wp_send_json_error(['message' => 'Security check failed', 'status' => 'invalid_nonce']);
    }

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

    if (empty($session_id)) {
        wp_send_json_error('Missing session ID');
    }

    global $wpdb;
    $table_name = $wpdb->prefix . 'vmo_consultations';

    // Get consultation data
    $consultation = $wpdb->get_row(
        $wpdb->prepare("SELECT client_data FROM $table_name WHERE session_id = %s", $session_id)
    );

    if (!$consultation) {
        wp_send_json_error('Session not found');
    }

    $client_data = json_decode($consultation->client_data, true) ?: [];
    
    if (!class_exists('VMO_Canonical_Website_Pipeline')) {
        wp_send_json_error(array('message' => 'Canonical website pipeline is required for website implementation.'), 500);
    }

    $pipeline_result = VMO_Canonical_Website_Pipeline::get_instance()->run_from_consultation_payload($session_id, array(
        'source' => 'multi_agent_implementation',
        'responses' => is_array($client_data['responses'] ?? null) ? $client_data['responses'] : array(),
        'agent_results' => is_array($client_data['agent_results'] ?? null) ? $client_data['agent_results'] : array(),
        'legacy_client_data' => $client_data,
    ));
    if (is_wp_error($pipeline_result)) {
        wp_send_json_error(array('message' => $pipeline_result->get_error_message()));
    }

    $website_draft = is_array($pipeline_result['website_draft'] ?? null) ? $pipeline_result['website_draft'] : array();
    $complete_website = (string) ($pipeline_result['content'] ?? $website_draft['homepage_html'] ?? $pipeline_result['homepage_html'] ?? '');

    // Store implementation result
    $client_data['implementation'] = [
        'website_code' => $complete_website,
        'website_draft' => $website_draft,
        'canonical_pipeline' => true,
        'final_qa_gate' => $pipeline_result['final_qa_gate'] ?? ($website_draft['final_qa_gate'] ?? array()),
        'visual_preset' => $pipeline_result['visual_preset'] ?? ($website_draft['visual_preset'] ?? ''),
        'generated_at' => current_time('mysql'),
        'status' => $pipeline_result['status'] ?? (!empty($pipeline_result['preview_ready']) ? 'completed' : 'generating_media'),
        'draft_assembled' => !empty($complete_website),
        'preview_ready' => !empty($pipeline_result['preview_ready']),
        'media_complete' => !empty($pipeline_result['media_complete'])
    ];

    // Update database
    // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    $wpdb->update(
        $table_name,
        ['client_data' => json_encode($client_data), 'status' => 'generating_media'],
        ['session_id' => $session_id],
        ['%s', '%s'],
        ['%s']
    );

    wp_send_json_success([
        'website_code' => $complete_website,
        'website_draft' => $website_draft,
        'draft_id' => $website_draft['post_id'] ?? ($pipeline_result['post_id'] ?? 0),
        'preview_url' => !empty($pipeline_result['preview_ready']) ? ($website_draft['preview_url'] ?? ($pipeline_result['preview_url'] ?? '')) : '',
        'status' => 'implementation_persisted',
        'canonical_pipeline' => true,
        'final_qa_gate' => $pipeline_result['final_qa_gate'] ?? ($website_draft['final_qa_gate'] ?? array()),
        'visual_preset' => $pipeline_result['visual_preset'] ?? ($website_draft['visual_preset'] ?? ''),
        'download_ready' => false,
        'preview_ready' => !empty($pipeline_result['preview_ready']),
        'media_complete' => !empty($pipeline_result['media_complete']),
        'consultation_status' => $pipeline_result['status'] ?? (!empty($pipeline_result['preview_ready']) ? 'completed' : 'generating_media'),
        'message' => !empty($pipeline_result['preview_ready'])
            ? 'Website draft assembled through the canonical pipeline and preview is ready.'
            : 'Website draft assembled through the canonical pipeline. Required media completion is still pending before preview unlocks.'
    ]);
}

// ============================================================================
// MULTI-AGENT REFINEMENT SYSTEM AJAX HANDLERS
// ============================================================================

if (!function_exists('vmo_agent_refinement_extract_json')) {
    function vmo_agent_refinement_extract_json($raw) {
        $raw = trim((string) $raw);
        $raw = preg_replace('/^```(?:json)?\s*/i', '', $raw);
        $raw = preg_replace('/\s*```$/', '', $raw);
        $decoded = json_decode($raw, true);
        if (is_array($decoded)) {
            return $decoded;
        }
        if (preg_match('/\{.*\}/s', $raw, $matches)) {
            $decoded = json_decode($matches[0], true);
            if (is_array($decoded)) {
                return $decoded;
            }
        }
        return array('raw' => $raw);
    }
}

if (!function_exists('vmo_agent_refinement_ai_contract')) {
    function vmo_agent_refinement_ai_contract($prompt, $agent_key, array $context = array()) {
        $model = function_exists('vmo_get_website_agent_model')
            ? vmo_get_website_agent_model($agent_key)
            : (function_exists('vmo_get_model') ? vmo_get_model('strategic') : (defined('VMO_OPENAI_MODEL') ? VMO_OPENAI_MODEL : 'gpt-5.5'));

        $system = 'You are a VMO v2.0 builder refinement agent. Editing happens inside the VMO plugin template, not WordPress editors. Return strict JSON only. The public website must remain standalone HTML/CSS/JS with no shortcodes, wp-admin/admin-ajax/wp-json calls, wp-content plugin/theme runtime URLs, PHP, or window.wp dependency.';
        $messages = array(
            array('role' => 'system', 'content' => $system),
            array('role' => 'user', 'content' => "CONTEXT:\n" . wp_json_encode($context) . "\n\nREQUEST:\n" . (string) $prompt),
        );

        if (!function_exists('vmo_openai_chat')) {
            return array(
                'agent' => $agent_key,
                'model' => $model,
                'status' => 'contract_fallback',
                'request' => (string) $prompt,
            );
        }

        $response = vmo_openai_chat($messages, $model, array(
            'temperature' => 0.2,
            'max_tokens' => 1600,
            'timeout' => 45,
        ));
        if (is_wp_error($response)) {
            return array(
                'agent' => $agent_key,
                'model' => $model,
                'status' => 'ai_error',
                'message' => $response->get_error_message(),
                'request' => (string) $prompt,
            );
        }

        $body = json_decode(wp_remote_retrieve_body($response), true);
        $content = (string) ($body['choices'][0]['message']['content'] ?? '');
        $contract = vmo_agent_refinement_extract_json($content);
        $contract['agent'] = $contract['agent'] ?? $agent_key;
        $contract['model'] = $contract['model'] ?? $model;
        $contract['standalone_runtime_required'] = true;
        $contract['wordpress_editor_usage'] = 'disabled_for_generated_sites';
        return $contract;
    }
}

if (!function_exists('vmo_agent_refinement_context_from_request')) {
    function vmo_agent_refinement_context_from_request() {
        $component_state = json_decode((string) wp_unslash($_POST['component_state'] ?? '{}'), true);
        return array(
            'project_id' => absint($_POST['project_id'] ?? $_POST['post_id'] ?? 0),
            'target_element' => sanitize_text_field(wp_unslash($_POST['target_element'] ?? 'auto-detect')),
            'current_content_excerpt' => substr(wp_strip_all_tags((string) wp_unslash($_POST['current_content'] ?? '')), 0, 2500),
            'component_state_present' => is_array($component_state) && !empty($component_state),
            'standalone_contract' => array(
                'editing_surface' => 'vmo_internal_builder_and_template_only',
                'runtime_output' => 'standalone_html_css_js',
                'forbidden_dependencies' => array('shortcodes', 'wp-admin', 'admin-ajax.php', 'wp-json', 'wp-content/plugins', 'wp-content/themes', 'window.wp'),
            ),
        );
    }
}

/**
 * Website Generation Agent AJAX Handler
 */
add_action( 'wp_ajax_vmo_agent_website_generation', 'vmo_handle_agent_website_generation' );

function vmo_handle_agent_website_generation() {
    vmo_prepare_long_generation_runtime();

    $admin_nonce   = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
    $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
    $admin_ok   = $admin_nonce && (wp_verify_nonce($admin_nonce, 'vmo_admin_nonce') || wp_verify_nonce($admin_nonce, 'vmo_builder_nonce'));
    $builder_ok = $builder_nonce && wp_verify_nonce($builder_nonce, 'vmo_builder_nonce');
    if (!($admin_ok || $builder_ok)) {
        wp_send_json_error(['message' => 'Security check failed', 'status' => 'invalid_nonce']);
    }

    $prompt = sanitize_textarea_field($_POST['prompt'] ?? '');
    $preserve_existing = sanitize_text_field($_POST['preserve_existing'] ?? 'true');
    $project_id = absint($_POST['project_id'] ?? $_POST['post_id'] ?? 0);

    if (empty($prompt)) {
        wp_send_json_error('Missing prompt');
    }

    if (!class_exists('VMO_Canonical_Website_Pipeline')) {
        wp_send_json_error(array('message' => 'Canonical website pipeline is required for website agent generation.'), 500);
    }

    if ($preserve_existing === 'true' && $project_id > 0) {
        $pipeline_result = VMO_Canonical_Website_Pipeline::get_instance()->run_regeneration($project_id, array(
            'prompt' => $prompt,
            'agent' => 'website_builder',
            'current_content' => sanitize_textarea_field($_POST['current_content'] ?? ''),
        ));
    } else {
        $pipeline_result = VMO_Canonical_Website_Pipeline::get_instance()->run_from_builder_prompt($project_id, $prompt);
    }

    if (is_wp_error($pipeline_result)) {
        wp_send_json_error(array('message' => $pipeline_result->get_error_message()));
    }

    $website_draft = is_array($pipeline_result['website_draft'] ?? null) ? $pipeline_result['website_draft'] : array();
    $generated_html = (string) ($pipeline_result['content'] ?? $website_draft['homepage_html'] ?? $pipeline_result['homepage_html'] ?? '');

    wp_send_json_success([
        'html' => $generated_html,
        'website_draft' => $website_draft,
        'draft_id' => $website_draft['post_id'] ?? ($pipeline_result['post_id'] ?? 0),
        'preview_url' => !empty($pipeline_result['preview_ready']) ? ($website_draft['preview_url'] ?? ($pipeline_result['preview_url'] ?? '')) : '',
        'preview_ready' => !empty($pipeline_result['preview_ready']),
        'media_complete' => !empty($pipeline_result['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' => $preserve_existing === 'true' && $project_id > 0 ? 'canonical_agent_regeneration' : 'canonical_agent_generation',
        'preservation_mode' => $preserve_existing
    ]);
}

/**
 * Media Integration Agent AJAX Handler
 */
add_action( 'wp_ajax_vmo_agent_media_integration', 'vmo_handle_agent_media_integration' );

function vmo_handle_agent_media_integration() {
    vmo_prepare_long_generation_runtime();

    $admin_nonce   = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
    $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
    $admin_ok   = $admin_nonce && (wp_verify_nonce($admin_nonce, 'vmo_admin_nonce') || wp_verify_nonce($admin_nonce, 'vmo_builder_nonce'));
    $builder_ok = $builder_nonce && wp_verify_nonce($builder_nonce, 'vmo_builder_nonce');
    if (!($admin_ok || $builder_ok)) {
        wp_send_json_error(['message' => 'Security check failed', 'status' => 'invalid_nonce']);
    }
    if (!is_user_logged_in()) {
        wp_send_json_error(array('message' => 'User not logged in'), 401);
    }

    $prompt = sanitize_textarea_field(wp_unslash($_POST['prompt'] ?? ''));
    $target_element = sanitize_text_field(wp_unslash($_POST['target_element'] ?? 'auto-detect'));
    $preserve_layout = sanitize_text_field(wp_unslash($_POST['preserve_layout'] ?? 'true'));

    if (empty($prompt)) {
        wp_send_json_error('Missing prompt');
    }

    $context = vmo_agent_refinement_context_from_request();
    $media_analysis = vmo_agent_refinement_ai_contract(
        'Create a queueable media replacement contract for this request. JSON keys: target_selector, change_type, media_type, prompt, alt_text, placement, preserve_layout. Do not return a final fake URL. Request: ' . $prompt,
        'media_director',
        $context
    );
    $safe_prompt = sanitize_text_field((string) ($media_analysis['prompt'] ?? $prompt));

    wp_send_json_success([
        'target_selector' => sanitize_text_field((string) ($media_analysis['target_selector'] ?? ($target_element !== 'auto-detect' ? $target_element : '[data-media-id], img, video, [data-vmo-media]'))),
        'new_media_url' => '',
        'queue_marker' => array(
            'data-vmo-generate' => 'true',
            'data-vmo-prompt' => $safe_prompt,
            'data-vmo-placement' => sanitize_key((string) ($media_analysis['placement'] ?? 'builder_refinement')),
            'alt' => sanitize_text_field((string) ($media_analysis['alt_text'] ?? $safe_prompt)),
        ),
        'change_type' => sanitize_key((string) ($media_analysis['change_type'] ?? 'queue_replace')),
        'preserve_layout' => $preserve_layout,
        'analysis' => $media_analysis
    ]);
}

/**
 * Content Refinement Agent AJAX Handler
 */
add_action( 'wp_ajax_vmo_agent_content_refinement', 'vmo_handle_agent_content_refinement' );

function vmo_handle_agent_content_refinement() {
    vmo_prepare_long_generation_runtime();

    $admin_nonce   = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
    $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
    $admin_ok   = $admin_nonce && (wp_verify_nonce($admin_nonce, 'vmo_admin_nonce') || wp_verify_nonce($admin_nonce, 'vmo_builder_nonce'));
    $builder_ok = $builder_nonce && wp_verify_nonce($builder_nonce, 'vmo_builder_nonce');
    if (!($admin_ok || $builder_ok)) {
        wp_send_json_error(['message' => 'Security check failed', 'status' => 'invalid_nonce']);
    }
    if (!is_user_logged_in()) {
        wp_send_json_error(array('message' => 'User not logged in'), 401);
    }

    $prompt = sanitize_textarea_field(wp_unslash($_POST['prompt'] ?? ''));
    $target_element = sanitize_text_field(wp_unslash($_POST['target_element'] ?? 'auto-detect'));
    $preserve_structure = sanitize_text_field(wp_unslash($_POST['preserve_structure'] ?? 'true'));

    if (empty($prompt)) {
        wp_send_json_error('Missing prompt');
    }

    $context = vmo_agent_refinement_context_from_request();
    $refinement = vmo_agent_refinement_ai_contract(
        'Create a scoped content edit contract. JSON keys: target_selector, new_content, preserve_structure, notes. Return only replacement text or safe inline HTML for the target, not a full page. Request: ' . $prompt,
        'website_refinement_assistant',
        $context
    );

    wp_send_json_success([
        'target_selector' => sanitize_text_field((string) ($refinement['target_selector'] ?? ($target_element !== 'auto-detect' ? $target_element : '.hero-title, h1, .main-heading'))),
        'new_content' => wp_kses_post((string) ($refinement['new_content'] ?? ($refinement['replacement_text'] ?? ''))),
        'preservation_mode' => $preserve_structure,
        'standalone_runtime_required' => true,
        'refinement_contract' => $refinement,
    ]);
}

/**
 * SEO Optimization Agent AJAX Handler
 */
add_action( 'wp_ajax_vmo_agent_seo_optimization', 'vmo_handle_agent_seo_optimization' );

function vmo_handle_agent_seo_optimization() {
    vmo_prepare_long_generation_runtime();

    $admin_nonce   = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
    $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
    $admin_ok   = $admin_nonce && (wp_verify_nonce($admin_nonce, 'vmo_admin_nonce') || wp_verify_nonce($admin_nonce, 'vmo_builder_nonce'));
    $builder_ok = $builder_nonce && wp_verify_nonce($builder_nonce, 'vmo_builder_nonce');
    if (!($admin_ok || $builder_ok)) {
        wp_send_json_error(['message' => 'Security check failed', 'status' => 'invalid_nonce']);
    }
    if (!is_user_logged_in()) {
        wp_send_json_error(array('message' => 'User not logged in'), 401);
    }

    $prompt = sanitize_textarea_field(wp_unslash($_POST['prompt'] ?? ''));
    $current_content = sanitize_textarea_field(wp_unslash($_POST['current_content'] ?? ''));

    if (empty($prompt)) {
        wp_send_json_error('Missing prompt');
    }

    $context = vmo_agent_refinement_context_from_request();
    $context['current_content_excerpt'] = substr($current_content, 0, 2500);
    $seo_recommendations = vmo_agent_refinement_ai_contract(
        'Create a standalone SEO edit contract. JSON keys: title, meta_description, headings, schema_json_ld, keyword_targets, content_edits, dashboard_next_actions. Do not require WordPress SEO plugins. Request: ' . $prompt,
        'seo_content_intelligence',
        $context
    );

    // Queue for approval if successful
    if (is_array($seo_recommendations) && !isset($seo_recommendations['error'])) {
        if (!function_exists('vmo_queue_add')) {
            require_once plugin_dir_path(__FILE__) . 'approval-queue.php';
        }
        $title = 'SEO Recommendations - ' . wp_trim_words($prompt, 6, '…');
        vmo_queue_add('seo', $seo_recommendations, $title, null, 'Auto-generated from SEO Agent');
    }

    wp_send_json_success([
        'seo_recommendations' => $seo_recommendations,
        'optimization_type' => 'comprehensive'
    ]);
}

/**
 * Design Refinement Agent AJAX Handler
 */
add_action( 'wp_ajax_vmo_agent_design_refinement', 'vmo_handle_agent_design_refinement' );

function vmo_handle_agent_design_refinement() {
    vmo_prepare_long_generation_runtime();

    $admin_nonce   = isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : '';
    $builder_nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
    $admin_ok   = $admin_nonce && (wp_verify_nonce($admin_nonce, 'vmo_admin_nonce') || wp_verify_nonce($admin_nonce, 'vmo_builder_nonce'));
    $builder_ok = $builder_nonce && wp_verify_nonce($builder_nonce, 'vmo_builder_nonce');
    if (!($admin_ok || $builder_ok)) {
        wp_send_json_error(['message' => 'Security check failed', 'status' => 'invalid_nonce']);
    }
    if (!is_user_logged_in()) {
        wp_send_json_error(array('message' => 'User not logged in'), 401);
    }

    $prompt = sanitize_textarea_field(wp_unslash($_POST['prompt'] ?? ''));
    $target_element = sanitize_text_field(wp_unslash($_POST['target_element'] ?? 'auto-detect'));

    if (empty($prompt)) {
        wp_send_json_error('Missing prompt');
    }

    $context = vmo_agent_refinement_context_from_request();
    $design_changes = vmo_agent_refinement_ai_contract(
        'Create a scoped design edit contract. JSON keys: target_selector, css_changes, class_changes, preserve_layout, mobile_notes. CSS must be standalone and scoped; do not use WordPress classes as dependencies. Request: ' . $prompt,
        'visual_experience_director',
        $context
    );

    wp_send_json_success([
        'css_changes' => is_array($design_changes['css_changes'] ?? null) ? $design_changes['css_changes'] : $design_changes,
        'target_selector' => sanitize_text_field((string) ($design_changes['target_selector'] ?? ($target_element !== 'auto-detect' ? $target_element : 'section'))),
        'target_elements' => [$target_element],
        'standalone_runtime_required' => true,
        'refinement_contract' => $design_changes,
    ]);
}
