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

/**
 * VMO Cross-System Integration Hub
 *
 * Phase 9 — Wires every subsystem into the Today Tab, Reports, KPI Interpretation,
 * Jerreme Recommendations, and Jarvis Feature Navigation.
 */
class VMO_Integration_Hub {

    private static $instance = null;

    public static function get_instance() {
        if (null === self::$instance) self::$instance = new self();
        return self::$instance;
    }

    public function init() {
        // 9A: Unified action logger (was called but never defined)
        // 9B: Enhanced reports endpoint
        add_action('wp_ajax_vmo_get_reports_enhanced', [$this, 'ajax_get_enhanced_reports']);
        // 9C: AI KPI interpretation
        add_action('wp_ajax_vmo_ai_kpi_interpretation', [$this, 'ajax_ai_kpi_interpretation']);
        // 9D: Jerreme recommendations
        add_action('wp_ajax_vmo_get_jerreme_recommendations', [$this, 'ajax_get_recommendations']);
        // 9E: Jarvis command router
        add_action('wp_ajax_vmo_jarvis_command', [$this, 'ajax_jarvis_command']);
    }

    /* ═══════════════════════════════════════════
     *  9A — UNIFIED ACTION LOGGER
     *  Called from all subsystem events to feed the Today Tab
     * ═══════════════════════════════════════════ */

    /**
     * Log a unified action to the vmo_actions table.
     * This is the GLOBAL helper that all subsystems call.
     *
     * @param int    $user_id
     * @param string $source    e.g. 'crm', 'email', 'sms', 'payment', 'shipping', 'form', 'auto_reply'
     * @param array  $data      [title, description, priority, category, source_id]
     */
    public static function log_action($user_id, $source, $data = []) {
        if (!$user_id || empty($data['title'])) return false;

        global $wpdb;
        $table = $wpdb->prefix . 'vmo_actions';

        // Ensure table exists
        $charset = $wpdb->get_charset_collate();
        $wpdb->query("CREATE TABLE IF NOT EXISTS {$table} (
            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            user_id BIGINT UNSIGNED NOT NULL,
            title VARCHAR(500) NOT NULL,
            description TEXT NULL,
            source VARCHAR(50) DEFAULT 'manual',
            source_id VARCHAR(100) NULL,
            priority VARCHAR(20) DEFAULT 'normal',
            category VARCHAR(50) NULL,
            status VARCHAR(20) DEFAULT 'pending',
            due_date DATETIME NULL,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            completed_at DATETIME NULL,
            updated_at DATETIME NULL,
            INDEX idx_user_status (user_id, status),
            INDEX idx_source (source)
        ) {$charset}");

        return $wpdb->insert($table, [
            'user_id'     => $user_id,
            'title'       => sanitize_text_field(substr($data['title'], 0, 500)),
            'description' => isset($data['description']) ? sanitize_textarea_field($data['description']) : null,
            'source'      => sanitize_key($source),
            'source_id'   => isset($data['source_id']) ? sanitize_text_field($data['source_id']) : null,
            'priority'    => sanitize_key($data['priority'] ?? 'normal'),
            'category'    => sanitize_key($data['category'] ?? $source),
            'status'      => 'pending',
            'due_date'    => !empty($data['due_date']) ? sanitize_text_field($data['due_date']) : null,
            'created_at'  => current_time('mysql'),
        ], ['%d', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s']);
    }

    /* ═══════════════════════════════════════════
     *  9B — ENHANCED REPORTS (Payment, Shipping, CRM, Forms)
     * ═══════════════════════════════════════════ */

    public function ajax_get_enhanced_reports() {
        if (!current_user_can('read')) wp_send_json_error(['message' => 'Unauthorized'], 403);
        $user_id = get_current_user_id();
        $type = sanitize_key($_POST['report_type'] ?? '');
        global $wpdb;

        $html = '';
        switch ($type) {
            case 'payments':
                $html = $this->report_payments($user_id, $wpdb);
                break;
            case 'shipping':
                $html = $this->report_shipping($user_id, $wpdb);
                break;
            case 'crm':
                $html = $this->report_crm($user_id, $wpdb);
                break;
            case 'forms':
                $html = $this->report_forms($user_id, $wpdb);
                break;
        }

        if (empty($html)) {
            $html = '<p style="color:var(--muted);padding:12px;">No ' . esc_html(str_replace('-', ' ', $type)) . ' data available yet.</p>';
        }
        wp_send_json_success(['html' => $html]);
    }

    private function report_payments($uid, $wpdb) {
        $orders = $wpdb->prefix . 'vmo_store_orders';
        if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $orders)) !== $orders) {
            return '<p style="color:var(--muted);">Payment tracking will appear when you start receiving orders.</p>';
        }

        $stats = $wpdb->get_row($wpdb->prepare(
            "SELECT COUNT(*) as total_orders, COALESCE(SUM(amount),0) as total_revenue,
                    COALESCE(AVG(amount),0) as avg_order,
                    COUNT(CASE WHEN status='completed' THEN 1 END) as completed,
                    COUNT(CASE WHEN status='failed' THEN 1 END) as failed,
                    COUNT(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 END) as orders_30d
             FROM {$orders} WHERE user_id = %d",
            $uid
        ));

        $recent = $wpdb->get_results($wpdb->prepare(
            "SELECT customer_email, amount, currency, status, created_at
             FROM {$orders} WHERE user_id = %d ORDER BY created_at DESC LIMIT 10",
            $uid
        ));

        $html = '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:10px;margin-bottom:16px;">';
        $html .= $this->kpi_card('Total Revenue', '$' . number_format($stats->total_revenue / 100, 2));
        $html .= $this->kpi_card('Orders (30d)', $stats->orders_30d);
        $html .= $this->kpi_card('Avg Order', '$' . number_format($stats->avg_order / 100, 2));
        $html .= $this->kpi_card('Success Rate', $stats->total_orders > 0 ? round(($stats->completed / $stats->total_orders) * 100) . '%' : '—');
        $html .= '</div>';

        if ($recent) {
            $html .= '<table style="width:100%;font-size:12px;border-collapse:collapse;">';
            $html .= '<tr style="color:var(--muted);border-bottom:1px solid var(--border);"><th style="text-align:left;padding:6px;">Customer</th><th>Amount</th><th>Status</th><th>Date</th></tr>';
            foreach ($recent as $r) {
                $color = $r->status === 'completed' ? '#22c55e' : ($r->status === 'failed' ? '#ef4444' : '#eab308');
                $html .= '<tr style="border-bottom:1px solid rgba(255,255,255,.05);">';
                $html .= '<td style="padding:6px;">' . esc_html($r->customer_email ?: '—') . '</td>';
                $html .= '<td style="text-align:center;">$' . number_format($r->amount / 100, 2) . '</td>';
                $html .= '<td style="text-align:center;"><span style="color:' . $color . ';font-weight:600;">' . esc_html(ucfirst($r->status)) . '</span></td>';
                $html .= '<td style="text-align:right;color:var(--muted);">' . esc_html(date('M j', strtotime($r->created_at))) . '</td>';
                $html .= '</tr>';
            }
            $html .= '</table>';
        }
        return $html;
    }

    private function report_shipping($uid, $wpdb) {
        $table = $wpdb->prefix . 'vmo_shipments';
        if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table)) !== $table) {
            return '<p style="color:var(--muted);">Shipping data will appear when you start fulfilling orders.</p>';
        }

        $stats = $wpdb->get_row($wpdb->prepare(
            "SELECT COUNT(*) as total, 
                    COUNT(CASE WHEN status='delivered' THEN 1 END) as delivered,
                    COUNT(CASE WHEN status='in_transit' THEN 1 END) as in_transit,
                    COUNT(CASE WHEN status='pending' THEN 1 END) as pending_ship
             FROM {$table} WHERE user_id = %d",
            $uid
        ));

        $html = '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:10px;margin-bottom:16px;">';
        $html .= $this->kpi_card('Total Shipments', $stats->total);
        $html .= $this->kpi_card('Delivered', $stats->delivered);
        $html .= $this->kpi_card('In Transit', $stats->in_transit);
        $html .= $this->kpi_card('Pending', $stats->pending_ship);
        $html .= '</div>';

        $recent = $wpdb->get_results($wpdb->prepare(
            "SELECT tracking_number, carrier, status, created_at FROM {$table} WHERE user_id = %d ORDER BY created_at DESC LIMIT 8",
            $uid
        ));
        if ($recent) {
            $html .= '<table style="width:100%;font-size:12px;border-collapse:collapse;">';
            $html .= '<tr style="color:var(--muted);border-bottom:1px solid var(--border);"><th style="text-align:left;padding:6px;">Tracking #</th><th>Carrier</th><th>Status</th><th>Date</th></tr>';
            foreach ($recent as $r) {
                $html .= '<tr style="border-bottom:1px solid rgba(255,255,255,.05);">';
                $html .= '<td style="padding:6px;font-family:monospace;font-size:11px;">' . esc_html(substr($r->tracking_number ?? '—', 0, 20)) . '</td>';
                $html .= '<td style="text-align:center;">' . esc_html($r->carrier ?? '—') . '</td>';
                $html .= '<td style="text-align:center;">' . esc_html(ucfirst($r->status ?? '—')) . '</td>';
                $html .= '<td style="text-align:right;color:var(--muted);">' . esc_html(date('M j', strtotime($r->created_at))) . '</td>';
                $html .= '</tr>';
            }
            $html .= '</table>';
        }
        return $html;
    }

    private function report_crm($uid, $wpdb) {
        $table = $wpdb->prefix . 'vmo_contacts';
        if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table)) !== $table) {
            return '<p style="color:var(--muted);">CRM data will appear as contacts are created.</p>';
        }

        $stats = $wpdb->get_row($wpdb->prepare(
            "SELECT COUNT(*) as total,
                    COUNT(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) THEN 1 END) as new_7d,
                    COUNT(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 END) as new_30d,
                    COUNT(CASE WHEN source LIKE '%%form%%' THEN 1 END) as from_forms,
                    COUNT(CASE WHEN source LIKE '%%email%%' THEN 1 END) as from_email,
                    COUNT(CASE WHEN source LIKE '%%stripe%%' THEN 1 END) as from_stripe
             FROM {$table} WHERE user_id = %d",
            $uid
        ));

        $html = '<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));gap:10px;margin-bottom:16px;">';
        $html .= $this->kpi_card('Total Contacts', $stats->total);
        $html .= $this->kpi_card('New (7d)', $stats->new_7d);
        $html .= $this->kpi_card('New (30d)', $stats->new_30d);
        $html .= $this->kpi_card('From Forms', $stats->from_forms);
        $html .= '</div>';

        $html .= '<div style="font-size:12px;color:var(--muted);margin-top:8px;">';
        $html .= '<strong>Sources:</strong> Forms: ' . intval($stats->from_forms) . ' · Email: ' . intval($stats->from_email) . ' · Stripe: ' . intval($stats->from_stripe);
        $html .= '</div>';
        return $html;
    }

    private function report_forms($uid, $wpdb) {
        $table = $wpdb->prefix . 'vmo_emails';
        if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $table)) !== $table) {
            return '<p style="color:var(--muted);">Form submission data will appear when visitors submit forms on your website.</p>';
        }

        $stats = $wpdb->get_row($wpdb->prepare(
            "SELECT COUNT(*) as total,
                    COUNT(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY) THEN 1 END) as week,
                    COUNT(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 END) as month
             FROM {$table} WHERE user_id = %d AND type = 'transactional' AND folder = 'inbox' AND subject LIKE '%%Form%%'",
            $uid
        ));

        $html = '<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:16px;">';
        $html .= $this->kpi_card('Total Submissions', $stats->total);
        $html .= $this->kpi_card('This Week', $stats->week);
        $html .= $this->kpi_card('This Month', $stats->month);
        $html .= '</div>';
        return $html;
    }

    private function kpi_card($label, $value) {
        return '<div style="background:rgba(15,225,255,.04);border:1px solid rgba(15,225,255,.12);border-radius:8px;padding:12px;text-align:center;">'
            . '<div style="font-size:20px;font-weight:700;color:var(--cyan);">' . esc_html($value) . '</div>'
            . '<div style="font-size:11px;color:var(--muted);margin-top:4px;">' . esc_html($label) . '</div></div>';
    }

    /* ═══════════════════════════════════════════
     *  9C — AI KPI INTERPRETATION
     * ═══════════════════════════════════════════ */

    public function ajax_ai_kpi_interpretation() {
        if (!current_user_can('read')) wp_send_json_error(['message' => 'Unauthorized'], 403);
        $uid = get_current_user_id();

        // Gather cross-system KPIs
        $kpis = $this->gather_all_kpis($uid);

        // Generate AI interpretation
        if (!function_exists('vmo_openai_chat')) {
            wp_send_json_success(['interpretation' => $this->fallback_interpretation($kpis)]);
            return;
        }

        $business_name = get_bloginfo('name');
        if (function_exists('vmo_get_consultation_profile')) {
            $profile = vmo_get_consultation_profile($uid);
            $ext = is_array($profile['extracted_information'] ?? null) ? $profile['extracted_information'] : [];
            foreach (['business_name', 'businessName'] as $k) {
                if (!empty($ext[$k])) { $business_name = $ext[$k]; break; }
            }
        }

        $system = "You are a strategic business intelligence analyst for \"{$business_name}\".\n"
            . "Analyze the following KPI data and provide a concise, actionable executive summary.\n"
            . "Focus on: what's working, what needs attention, and 2-3 specific next steps.\n"
            . "Keep it under 200 words. Use a confident, supportive tone. Format with bullet points.\n"
            . "If data is limited, acknowledge it and suggest what to track.";

        $kpi_text = "CURRENT KPIs:\n" . wp_json_encode($kpis, JSON_PRETTY_PRINT);

        $model = function_exists('vmo_get_optimal_model') ? vmo_get_optimal_model('kpi_analysis') : 'gpt-5.5-mini';

        $resp = vmo_openai_chat([
            ['role' => 'system', 'content' => $system],
            ['role' => 'user', 'content' => $kpi_text],
        ], $model, ['temperature' => 0.5, 'max_tokens' => 500, 'timeout' => 20]);

        if (is_wp_error($resp)) {
            wp_send_json_success(['interpretation' => $this->fallback_interpretation($kpis)]);
            return;
        }

        $data = json_decode(wp_remote_retrieve_body($resp), true);
        $text = trim($data['choices'][0]['message']['content'] ?? '');

        wp_send_json_success(['interpretation' => $text ?: $this->fallback_interpretation($kpis)]);
    }

    private function gather_all_kpis($uid) {
        global $wpdb;
        $kpis = [];

        // Revenue
        $orders = $wpdb->prefix . 'vmo_store_orders';
        if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $orders)) === $orders) {
            $rev = $wpdb->get_row($wpdb->prepare(
                "SELECT COALESCE(SUM(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN amount END),0) as rev_30d,
                        COUNT(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 END) as orders_30d
                 FROM {$orders} WHERE user_id = %d AND status = 'completed'",
                $uid
            ));
            $kpis['revenue_30d'] = '$' . number_format(($rev->rev_30d ?? 0) / 100, 2);
            $kpis['orders_30d'] = intval($rev->orders_30d ?? 0);
        }

        // Email
        $emails = $wpdb->prefix . 'vmo_email_sends';
        if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $emails)) === $emails) {
            $em = $wpdb->get_row($wpdb->prepare(
                "SELECT COUNT(*) as sent, COUNT(opened_at) as opened, COUNT(clicked_at) as clicked
                 FROM {$emails} WHERE user_id = %d AND sent_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)",
                $uid
            ));
            $kpis['emails_sent_30d'] = intval($em->sent ?? 0);
            $kpis['email_open_rate'] = $em->sent > 0 ? round(($em->opened / $em->sent) * 100) . '%' : '—';
            $kpis['email_click_rate'] = $em->sent > 0 ? round(($em->clicked / $em->sent) * 100) . '%' : '—';
        }

        // CRM
        $contacts = $wpdb->prefix . 'vmo_contacts';
        if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $contacts)) === $contacts) {
            $cr = $wpdb->get_row($wpdb->prepare(
                "SELECT COUNT(*) as total, COUNT(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) THEN 1 END) as new_30d
                 FROM {$contacts} WHERE user_id = %d",
                $uid
            ));
            $kpis['total_contacts'] = intval($cr->total ?? 0);
            $kpis['new_contacts_30d'] = intval($cr->new_30d ?? 0);
        }

        // Tasks
        $follow = $wpdb->prefix . 'vmo_follow_ups';
        if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $follow)) === $follow) {
            $tasks = $wpdb->get_row($wpdb->prepare(
                "SELECT COUNT(*) as total, COUNT(CASE WHEN status='completed' THEN 1 END) as done,
                        COUNT(CASE WHEN due_date < NOW() AND status != 'completed' THEN 1 END) as overdue
                 FROM {$follow} WHERE user_id = %d",
                $uid
            ));
            $kpis['task_completion_rate'] = $tasks->total > 0 ? round(($tasks->done / $tasks->total) * 100) . '%' : '—';
            $kpis['overdue_tasks'] = intval($tasks->overdue ?? 0);
        }

        // Calls
        $calls = $wpdb->prefix . 'vmo_call_logs';
        if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $calls)) === $calls) {
            $cl = $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM {$calls} WHERE user_id = %d AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)",
                $uid
            ));
            $kpis['calls_30d'] = intval($cl);
        }

        // SMS
        $sms = $wpdb->prefix . 'vmo_sms_logs';
        if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $sms)) === $sms) {
            $sm = $wpdb->get_var($wpdb->prepare(
                "SELECT COUNT(*) FROM {$sms} WHERE user_id = %d AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)",
                $uid
            ));
            $kpis['sms_30d'] = intval($sm);
        }

        // Shipments
        $ship = $wpdb->prefix . 'vmo_shipments';
        if ($wpdb->get_var($wpdb->prepare("SHOW TABLES LIKE %s", $ship)) === $ship) {
            $sh = $wpdb->get_row($wpdb->prepare(
                "SELECT COUNT(*) as total, COUNT(CASE WHEN status='delivered' THEN 1 END) as delivered
                 FROM {$ship} WHERE user_id = %d AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)",
                $uid
            ));
            $kpis['shipments_30d'] = intval($sh->total ?? 0);
            $kpis['delivery_rate'] = $sh->total > 0 ? round(($sh->delivered / $sh->total) * 100) . '%' : '—';
        }

        return $kpis;
    }

    private function fallback_interpretation($kpis) {
        $parts = [];
        if (!empty($kpis['orders_30d'])) $parts[] = "• {$kpis['orders_30d']} orders this month generating {$kpis['revenue_30d']} in revenue.";
        if (!empty($kpis['new_contacts_30d'])) $parts[] = "• {$kpis['new_contacts_30d']} new contacts added in the last 30 days.";
        if (!empty($kpis['email_open_rate']) && $kpis['email_open_rate'] !== '—') $parts[] = "• Email open rate: {$kpis['email_open_rate']}.";
        if (!empty($kpis['overdue_tasks'])) $parts[] = "• ⚠️ {$kpis['overdue_tasks']} overdue tasks need attention.";
        if (empty($parts)) $parts[] = 'Start building business activity to unlock AI-powered strategic insights.';
        return implode("\n", $parts);
    }

    /* ═══════════════════════════════════════════
     *  9D — JERREME RECOMMENDATIONS
     * ═══════════════════════════════════════════ */

    public function ajax_get_recommendations() {
        if (!current_user_can('read')) wp_send_json_error(['message' => 'Unauthorized'], 403);
        $uid = get_current_user_id();

        $kpis = $this->gather_all_kpis($uid);
        $recs = [];

        // Rule-based recommendations from cross-system KPIs
        if (empty($kpis['total_contacts']) || $kpis['total_contacts'] < 5) {
            $recs[] = ['icon' => '👥', 'title' => 'Build Your Contact List', 'body' => 'You have fewer than 5 contacts. Add a contact form to your website and start collecting leads.', 'action' => 'crm', 'priority' => 'high'];
        }
        if (empty($kpis['emails_sent_30d']) || $kpis['emails_sent_30d'] < 1) {
            $recs[] = ['icon' => '✉️', 'title' => 'Launch Your First Email Campaign', 'body' => 'You haven\'t sent any emails this month. Use the AI email writer to create and send your first campaign.', 'action' => 'email', 'priority' => 'high'];
        }
        if (!empty($kpis['email_open_rate']) && $kpis['email_open_rate'] !== '—') {
            $rate = intval($kpis['email_open_rate']);
            if ($rate < 20) {
                $recs[] = ['icon' => '📧', 'title' => 'Improve Email Open Rates', 'body' => "Your email open rate is {$rate}%. Try personalizing subject lines and sending at optimal times.", 'action' => 'email', 'priority' => 'medium'];
            }
        }
        if (!empty($kpis['overdue_tasks']) && intval($kpis['overdue_tasks']) > 3) {
            $recs[] = ['icon' => '⏰', 'title' => 'Clear Overdue Tasks', 'body' => "You have {$kpis['overdue_tasks']} overdue tasks. Prioritize or reschedule them to stay on track.", 'action' => 'overview', 'priority' => 'urgent'];
        }
        if (empty($kpis['orders_30d']) || $kpis['orders_30d'] < 1) {
            $recs[] = ['icon' => '💳', 'title' => 'Set Up Payment Processing', 'body' => 'No orders this month. Create payment links or add products to start generating revenue.', 'action' => 'payments', 'priority' => 'high'];
        }
        if (empty($kpis['calls_30d']) || $kpis['calls_30d'] < 1) {
            $recs[] = ['icon' => '📞', 'title' => 'Make Your First Sales Call', 'body' => 'Use the Power Dialer to reach out to your contacts and start building relationships.', 'action' => 'phone', 'priority' => 'medium'];
        }
        if (empty($kpis['sms_30d']) || $kpis['sms_30d'] < 1) {
            $recs[] = ['icon' => '💬', 'title' => 'Try SMS Marketing', 'body' => 'SMS has 95%+ open rates. Send a quick campaign to your contacts from the Tools tab.', 'action' => 'sms', 'priority' => 'medium'];
        }

        // Auto-reply recommendation
        $ar_enabled = get_user_meta($uid, 'vmo_ai_auto_reply_enabled', true);
        if (empty($ar_enabled)) {
            $recs[] = ['icon' => '🤖', 'title' => 'Enable AI Auto-Reply', 'body' => 'Let AI handle routine email inquiries automatically. Toggle it on in the Emails tab sidebar.', 'action' => 'email', 'priority' => 'low'];
        }

        // Sort by priority
        $priority_order = ['urgent' => 0, 'high' => 1, 'medium' => 2, 'low' => 3];
        usort($recs, function($a, $b) use ($priority_order) {
            return ($priority_order[$a['priority']] ?? 4) - ($priority_order[$b['priority']] ?? 4);
        });

        wp_send_json_success(['recommendations' => array_slice($recs, 0, 6)]);
    }

    /* ═══════════════════════════════════════════
     *  9E — JARVIS COMMAND ROUTER
     * ═══════════════════════════════════════════ */

    /**
     * AJAX: Process a Jarvis text/voice command and return navigation or action.
     */
    public function ajax_jarvis_command() {
        if (!current_user_can('read')) wp_send_json_error(['message' => 'Unauthorized'], 403);
        $raw_command = trim(sanitize_text_field($_POST['command'] ?? ''));
        if ($raw_command === '') wp_send_json_error('Empty command');

        $result = $this->process_jarvis_command($raw_command, get_current_user_id());
        wp_send_json_success($result);
    }

    /**
     * Core Jarvis command processor — shared by the default-mode admin-ajax
     * handler (ajax_jarvis_command) AND the Premium Live Voice (Realtime/WebRTC)
     * function-tool bridge (vmo_handle_realtime_tool → 'jarvis_command'). Both
     * modes therefore resolve email/navigation/action intents through the EXACT
     * same logic, guaranteeing Jarvis has identical capabilities regardless of
     * voice mode. Returns a result array (never emits output).
     *
     * @param string $raw_command Natural-language command from the user.
     * @param int    $uid         Acting user id.
     * @return array
     */
    public function process_jarvis_command($raw_command, $uid) {
        $raw_command = trim((string) $raw_command);
        $command = strtolower($raw_command);
        $uid = (int) $uid;
        if ($command === '') {
            return ['no_match' => true, 'message' => 'I did not catch a command. Try "show unread emails", "reply to that email", or "go to CRM".'];
        }

        $email_command = $this->maybe_handle_jarvis_email_command($command, $raw_command, $uid);
        if (is_array($email_command)) {
            return $email_command;
        }

        // Feature navigation map
        $nav_map = [
            // Tabs
            'overview'    => ['tab' => 'overview'],
            'home'        => ['tab' => 'overview'],
            'dashboard'   => ['tab' => 'overview'],
            'strategic'   => ['tab' => 'strategic'],
            'strategy'    => ['tab' => 'strategic'],
            'reports'     => ['tab' => 'reports'],
            'report'      => ['tab' => 'reports'],
            'phone'       => ['tab' => 'phone'],
            'calls'       => ['tab' => 'phone'],
            'dialer'      => ['tab' => 'phone'],
            'email'       => ['tab' => 'email'],
            'emails'      => ['tab' => 'email'],
            'inbox'       => ['tab' => 'email'],
            'sms'         => ['tab' => 'sms'],
            'text'        => ['tab' => 'sms'],
            'messages'    => ['tab' => 'sms'],
            'payments'    => ['tab' => 'payments'],
            'pay'         => ['tab' => 'payments'],
            'invoices'    => ['tab' => 'payments'],
            'revenue'     => ['tab' => 'payments'],
            'shipping'    => ['tab' => 'shipping'],
            'orders'      => ['tab' => 'shipping'],
            'fulfillment' => ['tab' => 'shipping'],
            'seo'         => ['tab' => 'seo'],
            'analytics'   => ['tab' => 'seo'],
            'crm'         => ['tab' => 'crm'],
            'contacts'    => ['tab' => 'crm'],
            'leads'       => ['tab' => 'crm'],
            'promotions'  => ['tab' => 'promotions'],
            // Actions
            'compose'     => ['action' => 'vmoOpenComposeModal'],
            'new email'   => ['action' => 'vmoOpenComposeModal'],
            'write email' => ['action' => 'vmoOpenComposeModal'],
            'power dialer' => ['action' => 'VMOBusinessDashboard.openPowerDialer'],
            'send sms'    => ['action' => 'vmoOpenSmsCompose'],
        ];

        // Direct match
        if (isset($nav_map[$command])) {
            return $nav_map[$command];
        }

        // Fuzzy match — find best matching key
        $best_match = null;
        $best_score = 0;
        foreach ($nav_map as $key => $value) {
            // Check if command contains the key
            if (strpos($command, $key) !== false) {
                $score = strlen($key);
                if ($score > $best_score) {
                    $best_score = $score;
                    $best_match = $value;
                }
            }
        }

        if ($best_match) {
            return $best_match;
        }

        // Pattern matching for common phrases
        $patterns = [
            '/show\s*(me\s*)?my\s*payment/i'   => ['tab' => 'payments'],
            '/show\s*(me\s*)?my\s*email/i'      => ['tab' => 'email'],
            '/show\s*(me\s*)?my\s*contact/i'    => ['tab' => 'crm'],
            '/show\s*(me\s*)?my\s*report/i'     => ['tab' => 'reports'],
            '/show\s*(me\s*)?my\s*order/i'      => ['tab' => 'shipping'],
            '/show\s*(me\s*)?my\s*seo/i'        => ['tab' => 'seo'],
            '/open\s*(the\s*)?dialer/i'         => ['action' => 'VMOBusinessDashboard.openPowerDialer'],
            '/send\s*(an?\s*)?email/i'           => ['action' => 'vmoOpenComposeModal'],
            '/create\s*(a\s*)?campaign/i'        => ['action' => 'vmoOpenComposeModal'],
            '/check\s*(my\s*)?inbox/i'           => ['tab' => 'email'],
            '/go\s*to\s*(\w+)/i'                => null, // handled below
        ];

        foreach ($patterns as $pattern => $result) {
            if ($result && preg_match($pattern, $command)) {
                return $result;
            }
        }

        // "go to X" pattern
        if (preg_match('/go\s*to\s*(\w+)/i', $command, $m)) {
            $target = strtolower($m[1]);
            if (isset($nav_map[$target])) {
                return $nav_map[$target];
            }
        }

        // No match — return hint
        return [
            'no_match' => true,
            'message' => 'I didn\'t recognize that command. Try: "show payments", "open inbox", "go to CRM", or "send email".',
        ];
    }

    private function maybe_handle_jarvis_email_command($command, $raw_command, $uid) {
        if (!$uid || !preg_match('/\b(email|emails|inbox|mail|message|messages|reply|draft)\b/i', $command)) {
            return null;
        }

        if (preg_match('/\b(sync|refresh|pull|fetch)\b.*\b(email|emails|inbox|mailbox|mailboxes|mail)\b/i', $command)) {
            $result = array('synced' => 0, 'stored' => 0, 'errors' => array('Inbound sync service is not loaded.'));
            if (class_exists('VMO_Inbound_Email_Sync')) {
                $result = VMO_Inbound_Email_Sync::get_instance()->sync_all_active_mailboxes($uid);
            }
            return array(
                'type' => 'email_operator',
                'operation' => 'sync_inbox',
                'tab' => 'email',
                'message' => 'Mailbox sync finished. ' . intval($result['stored'] ?? 0) . ' new emails were imported.',
                'sync' => $result,
            );
        }

        if (preg_match('/\b(send|send now)\b.*\b(reply|draft)\b/i', $command)) {
            return $this->jarvis_send_current_email_draft($uid);
        }

        if (preg_match('/\b(mark|set)\b.*\b(email|message)\b.*\b(done|read|complete|completed)\b/i', $command)) {
            return $this->jarvis_mark_email_done($uid, $command);
        }

        if (preg_match('/\b(reply|respond|draft)\b.*\b(email|message|reply)\b/i', $command) || preg_match('/\breply\s+to\b/i', $command)) {
            $draft = $this->jarvis_draft_email_reply($uid, $command, $raw_command);
            if (preg_match('/\b(send|send now)\b/i', $command) && !empty($draft['draft_id'])) {
                return $this->jarvis_send_current_email_draft($uid);
            }
            return $draft;
        }

        if (preg_match('/\b(email|emails|messages)\s+from\s+(.+)$/i', $raw_command, $m)) {
            $contact = trim($m[2]);
            return $this->jarvis_list_email_summary($uid, array('contact' => $contact, 'limit' => 8), 'Emails from ' . $contact);
        }

        if (preg_match('/\b(read|open)\b.*\b(email|message)\b.*(number|#|\d|this|that|first|second|third|fourth|fifth)/i', $command)) {
            $row = $this->jarvis_resolve_email_reference($uid, $command);
            if (!$row) {
                return $this->jarvis_email_message('I could not find that email. Ask me to show unread emails first, then refer to the number I give you.');
            }
            $this->remember_jarvis_email_context($uid, array($row));
            return array(
                'type' => 'email_operator',
                'operation' => 'read_email',
                'tab' => 'email',
                'email' => $this->format_jarvis_email_row($row, 1, true),
                'message' => $this->email_row_summary_sentence($row, true),
            );
        }

        if (preg_match('/\b(read|show|check|summarize|summary|what)\b.*\b(email|emails|inbox|unread|mail|messages)\b/i', $command)) {
            return $this->jarvis_list_email_summary($uid, array(
                'unread' => strpos($command, 'unread') !== false,
                'today' => strpos($command, 'today') !== false,
                'limit' => 8,
            ), strpos($command, 'summar') !== false ? 'Email summary' : 'Mailbox results');
        }

        return null;
    }

    private function jarvis_list_email_summary($uid, array $filters = array(), $title = 'Mailbox results') {
        $rows = $this->jarvis_email_rows($uid, $filters);
        $this->remember_jarvis_email_context($uid, $rows);
        if (empty($rows)) {
            return $this->jarvis_email_message('I did not find matching emails in the VMO inbox yet. Run mailbox sync if you expected new external mail.');
        }

        $emails = array();
        foreach ($rows as $index => $row) {
            $emails[] = $this->format_jarvis_email_row($row, $index + 1, false);
        }

        $message = $title . ': ' . count($emails) . ' email' . (count($emails) === 1 ? '' : 's') . ' found. ';
        $message .= implode(' ', array_map(function($row) {
            return '#' . intval($row['index']) . ' from ' . ($row['from_email'] ?: 'unknown') . ': ' . ($row['subject'] ?: '(No subject)') . '.';
        }, array_slice($emails, 0, 4)));

        return array(
            'type' => 'email_operator',
            'operation' => 'list_emails',
            'tab' => 'email',
            'message' => $message,
            'emails' => $emails,
        );
    }

    private function jarvis_email_rows($uid, array $filters = array()) {
        global $wpdb;
        $table = $wpdb->prefix . 'vmo_emails';
        if ($wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table)) !== $table) {
            return array();
        }

        $where = array('user_id = %d', "folder = 'inbox'");
        $params = array(intval($uid));
        if (!empty($filters['unread'])) {
            $where[] = 'is_read = 0';
        }
        if (!empty($filters['today'])) {
            $where[] = 'created_at >= %s';
            $params[] = current_time('Y-m-d') . ' 00:00:00';
        }
        if (!empty($filters['contact'])) {
            $needle = '%' . $wpdb->esc_like(sanitize_text_field((string) $filters['contact'])) . '%';
            $where[] = '(from_email LIKE %s OR to_email LIKE %s)';
            $params[] = $needle;
            $params[] = $needle;
        }
        $limit = max(1, min(20, intval($filters['limit'] ?? 8)));
        $params[] = $limit;

        $sql = "SELECT id, mailbox_id, from_email, to_email, subject, message, is_read, created_at
                FROM {$table}
                WHERE " . implode(' AND ', $where) . "
                ORDER BY created_at DESC
                LIMIT %d";
        return $wpdb->get_results($wpdb->prepare($sql, $params), ARRAY_A) ?: array();
    }

    private function remember_jarvis_email_context($uid, array $rows) {
        $ids = array_values(array_filter(array_map(function($row) { return intval($row['id'] ?? 0); }, $rows)));
        update_user_meta($uid, 'vmo_jarvis_email_context', array(
            'ids' => $ids,
            'updated_at' => time(),
        ));
    }

    private function jarvis_resolve_email_reference($uid, $command) {
        global $wpdb;
        $table = $wpdb->prefix . 'vmo_emails';
        if ($wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table)) !== $table) {
            return null;
        }

        $context = get_user_meta($uid, 'vmo_jarvis_email_context', true);
        $ids = is_array($context) && is_array($context['ids'] ?? null) ? array_values(array_map('intval', $context['ids'])) : array();
        if (preg_match('/(?:email|message|number|#)\s*(\d+)/i', $command, $m)) {
            $index = max(1, intval($m[1])) - 1;
            if (!empty($ids[$index])) {
                return $wpdb->get_row($wpdb->prepare("SELECT * FROM {$table} WHERE user_id = %d AND id = %d LIMIT 1", $uid, intval($ids[$index])), ARRAY_A);
            }
        }
        $ordinals = array('first' => 0, 'second' => 1, 'third' => 2, 'fourth' => 3, 'fifth' => 4);
        foreach ($ordinals as $word => $index) {
            if (strpos($command, $word) !== false && !empty($ids[$index])) {
                return $wpdb->get_row($wpdb->prepare("SELECT * FROM {$table} WHERE user_id = %d AND id = %d LIMIT 1", $uid, intval($ids[$index])), ARRAY_A);
            }
        }

        return $wpdb->get_row($wpdb->prepare(
            "SELECT * FROM {$table} WHERE user_id = %d AND folder = 'inbox' ORDER BY is_read ASC, created_at DESC LIMIT 1",
            $uid
        ), ARRAY_A);
    }

    private function jarvis_mark_email_done($uid, $command) {
        global $wpdb;
        $row = $this->jarvis_resolve_email_reference($uid, $command);
        if (!$row) {
            return $this->jarvis_email_message('I could not find the email to mark done.');
        }
        $wpdb->update($wpdb->prefix . 'vmo_emails', array('is_read' => 1, 'updated_at' => current_time('mysql')), array('id' => intval($row['id']), 'user_id' => intval($uid)));
        return array(
            'type' => 'email_operator',
            'operation' => 'mark_done',
            'tab' => 'email',
            'email_id' => intval($row['id']),
            'message' => 'Marked that email as done: ' . sanitize_text_field($row['subject'] ?? '(No subject)'),
        );
    }

    private function jarvis_draft_email_reply($uid, $command, $raw_command) {
        $row = $this->jarvis_resolve_email_reference($uid, $command);
        if (!$row) {
            return $this->jarvis_email_message('I could not find an email to reply to. Ask me to show unread emails first, then say which number to reply to.');
        }

        if (!class_exists('VMO_Email_Storage')) {
            $storage_file = dirname(__FILE__) . '/class-vmo-email-storage.php';
            if (file_exists($storage_file) && is_readable($storage_file)) {
                require_once $storage_file;
            }
        }
        if (!class_exists('VMO_Email_Storage')) {
            return $this->jarvis_email_message('Email storage is not available, so I could not save the reply draft.');
        }

        $instruction = '';
        if (preg_match('/(?:tell\s+(?:him|her|them|[a-z0-9._%+\-]+)?\s*|say\s+|saying\s+|that\s+)(.+)$/i', $raw_command, $m)) {
            $instruction = trim($m[1]);
        }
        if ($instruction === '') {
            $instruction = 'Thank you for reaching out. We received your message and will follow up shortly.';
        }

        $reply = $this->jarvis_generate_reply_content($uid, $row, $instruction, $raw_command);
        $subject = sanitize_text_field($reply['subject'] ?? ('Re: ' . preg_replace('/^re:\s*/i', '', sanitize_text_field($row['subject'] ?? 'Your email'))));
        $body = wp_kses_post($reply['html_body'] ?? '');
        if ($body === '') {
            $body = '<p>Hi,</p><p>' . esc_html($instruction) . '</p><p>Best,<br>VMO</p>';
        }
        $from_email = sanitize_email((string) ($row['to_email'] ?? ''));
        $to_email = sanitize_email((string) ($row['from_email'] ?? ''));

        $storage = new VMO_Email_Storage();
        $saved = $storage->save_email(array(
            'user_id' => $uid,
            'mailbox_id' => isset($row['mailbox_id']) ? intval($row['mailbox_id']) : null,
            'folder' => 'drafts',
            'type' => 'transactional',
            'from_email' => $from_email,
            'to_email' => $to_email,
            'subject' => $subject,
            'message' => wp_kses_post($body),
            'status' => 'draft',
            'reply_to_email_id' => intval($row['id'] ?? 0),
            'is_read' => 1,
            'is_ai_generated' => 1,
        ));
        if (!$saved) {
            return $this->jarvis_email_message('I could not save the reply draft.');
        }

        global $wpdb;
        $draft_id = intval($wpdb->insert_id);
        update_user_meta($uid, 'vmo_jarvis_email_pending_reply', array(
            'draft_id' => $draft_id,
            'original_email_id' => intval($row['id'] ?? 0),
            'updated_at' => time(),
        ));

        return array(
            'type' => 'email_operator',
            'operation' => 'draft_reply',
            'tab' => 'email',
            'draft_id' => $draft_id,
            'original_email_id' => intval($row['id'] ?? 0),
            'subject' => $subject,
            'draft_preview' => sanitize_text_field($this->jarvis_text_excerpt(wp_strip_all_tags($body), 300)),
            'message' => 'I drafted the reply and saved it for approval. Say "send this reply" when you want me to send it.',
        );
    }

    private function jarvis_generate_reply_content($uid, array $row, $instruction, $raw_command) {
        $subject = 'Re: ' . preg_replace('/^re:\s*/i', '', sanitize_text_field($row['subject'] ?? 'Your email'));
        $from_email = sanitize_email((string) ($row['to_email'] ?? ''));
        $to_email = sanitize_email((string) ($row['from_email'] ?? ''));
        $business = $this->jarvis_business_context($uid);
        $identity = $this->jarvis_mailbox_identity($uid, intval($row['mailbox_id'] ?? 0), $from_email);
        $history = $this->jarvis_contact_history($uid, $to_email);
        $signature = $this->jarvis_signature_html($uid, $from_email);
        $fallback = $this->jarvis_fallback_reply_html($instruction, $business, $signature);

        if (!function_exists('vmo_openai_chat')) {
            return array('subject' => $subject, 'html_body' => $fallback);
        }

        $system = "You are Jarvis/VMO, an AI business email operator. Draft a polished reply on behalf of the business.\n"
            . "Use the original email, the user's instruction, business context, mailbox identity, signature, and recent contact history.\n"
            . "Do not invent prices, dates, availability, discounts, refunds, legal promises, or delivery guarantees. When scope, pricing, booking, or availability is uncertain, ask for details or say the team will confirm.\n"
            . "Return only JSON with keys: subject, html_body, plain_summary.";

        $user_message = "BUSINESS CONTEXT\n" . wp_json_encode($business) . "\n\n"
            . "MAILBOX IDENTITY\n" . wp_json_encode($identity) . "\n\n"
            . "RECENT CONTACT HISTORY\n" . wp_json_encode($history) . "\n\n"
            . "ORIGINAL EMAIL\n"
            . "From: " . sanitize_email($row['from_email'] ?? '') . "\n"
            . "To: " . sanitize_email($row['to_email'] ?? '') . "\n"
            . "Subject: " . sanitize_text_field($row['subject'] ?? '') . "\n"
            . "Body:\n" . wp_strip_all_tags(substr((string) ($row['message'] ?? ''), 0, 3500)) . "\n\n"
            . "USER INSTRUCTION\n" . sanitize_textarea_field($instruction) . "\n\n"
            . "RAW VOICE/TEXT COMMAND\n" . sanitize_textarea_field($raw_command) . "\n\n"
            . "SIGNATURE HTML TO APPEND IF APPROPRIATE\n" . wp_strip_all_tags($signature);

        $model = function_exists('vmo_get_optimal_model') ? vmo_get_optimal_model('email_generation') : 'gpt-5.5-mini';
        $response = vmo_openai_chat(
            array(
                array('role' => 'system', 'content' => $system),
                array('role' => 'user', 'content' => $user_message),
            ),
            $model,
            array('temperature' => 0.45, 'max_tokens' => 900, 'timeout' => 25)
        );
        if (is_wp_error($response)) {
            return array('subject' => $subject, 'html_body' => $fallback);
        }

        $data = json_decode(wp_remote_retrieve_body($response), true);
        $raw = $data['choices'][0]['message']['content'] ?? '';
        $parsed = json_decode($raw, true);
        if (!is_array($parsed) && preg_match('/\{.*\}/s', $raw, $match)) {
            $parsed = json_decode($match[0], true);
        }
        if (!is_array($parsed)) {
            return array('subject' => $subject, 'html_body' => $fallback);
        }

        $html = wp_kses_post($parsed['html_body'] ?? $parsed['body'] ?? '');
        if ($html === '' && !empty($parsed['plain_body'])) {
            $html = wpautop(esc_html((string) $parsed['plain_body']));
        }
        if ($html === '') {
            $html = $fallback;
        }
        if ($signature && stripos($html, wp_strip_all_tags($signature)) === false) {
            $html .= $signature;
        }

        return array(
            'subject' => sanitize_text_field($parsed['subject'] ?? $subject),
            'html_body' => wp_kses_post($html),
        );
    }

    private function jarvis_business_context($uid) {
        $context = array(
            'business_name' => get_bloginfo('name'),
            'website' => home_url('/'),
        );
        if (function_exists('vmo_get_consultation_profile')) {
            $profile = vmo_get_consultation_profile($uid);
            $extracted = is_array($profile['extracted_information'] ?? null) ? $profile['extracted_information'] : array();
            foreach (array('business_name', 'businessName', 'company_name') as $key) {
                if (!empty($extracted[$key])) {
                    $context['business_name'] = sanitize_text_field(is_array($extracted[$key]) ? implode(', ', $extracted[$key]) : $extracted[$key]);
                    break;
                }
            }
            foreach (array('industry', 'business_type', 'niche', 'services', 'offerings', 'business_hours', 'hours', 'phone', 'business_phone', 'email', 'business_email', 'address', 'location') as $key) {
                if (!empty($extracted[$key])) {
                    $context[$key] = sanitize_text_field(is_array($extracted[$key]) ? implode(', ', $extracted[$key]) : $extracted[$key]);
                }
            }
        }

        global $wpdb;
        $products_table = $wpdb->prefix . 'vmo_store_products';
        if ($wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $products_table)) === $products_table) {
            $products = $wpdb->get_results($wpdb->prepare(
                "SELECT name, description, price_cents, currency
                 FROM {$products_table}
                 WHERE user_id = %d AND status = 'active'
                 ORDER BY created_at DESC
                 LIMIT 8",
                $uid
            ), ARRAY_A);
            if ($products) {
                $context['products'] = array_map(function($product) {
                    $currency = strtoupper(sanitize_text_field($product['currency'] ?: 'USD'));
                    return trim(sanitize_text_field($product['name'] ?? '') . ' - ' . $currency . ' ' . number_format(((int) ($product['price_cents'] ?? 0)) / 100, 2) . ' - ' . sanitize_text_field(wp_strip_all_tags((string) ($product['description'] ?? ''))));
                }, $products);
            }
        }

        return $context;
    }

    private function jarvis_mailbox_identity($uid, $mailbox_id, $fallback_email) {
        global $wpdb;
        $identity = array('email' => sanitize_email($fallback_email), 'display_name' => get_bloginfo('name'));
        $accounts_table = $wpdb->prefix . 'vmo_email_accounts';
        if ($mailbox_id > 0 && $wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $accounts_table)) === $accounts_table) {
            $row = $wpdb->get_row($wpdb->prepare(
                "SELECT email, email_address, display_name, role, category, provider_preset
                 FROM {$accounts_table}
                 WHERE user_id = %d AND id = %d
                 LIMIT 1",
                $uid,
                $mailbox_id
            ), ARRAY_A);
            if ($row) {
                $identity = array(
                    'email' => sanitize_email($row['email'] ?: ($row['email_address'] ?? $fallback_email)),
                    'display_name' => sanitize_text_field($row['display_name'] ?: get_bloginfo('name')),
                    'role' => sanitize_text_field($row['role'] ?: ($row['category'] ?? '')),
                    'provider' => sanitize_text_field($row['provider_preset'] ?? ''),
                );
            }
        }
        return $identity;
    }

    private function jarvis_contact_history($uid, $contact_email) {
        global $wpdb;
        $contact_email = sanitize_email($contact_email);
        if (!$contact_email) {
            return array();
        }
        $contact_id = md5(strtolower(trim($contact_email)) . '|' . intval($uid));
        $table = $wpdb->prefix . 'vmo_contact_interactions';
        if ($wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table)) !== $table) {
            return array();
        }
        return $wpdb->get_results($wpdb->prepare(
            "SELECT interaction_type, interaction_subtype, direction, subject, summary, outcome, created_at
             FROM {$table}
             WHERE user_id = %d AND contact_id = %s
             ORDER BY created_at DESC
             LIMIT 8",
            $uid,
            $contact_id
        ), ARRAY_A) ?: array();
    }

    private function jarvis_signature_html($uid, $account) {
        global $wpdb;
        $table = $wpdb->prefix . 'vmo_email_signatures';
        if ($wpdb->get_var($wpdb->prepare('SHOW TABLES LIKE %s', $table)) !== $table) {
            return '';
        }
        $account = sanitize_email($account) ?: 'default';
        $signature = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$table} WHERE user_id = %d AND account = %s LIMIT 1", $uid, $account), ARRAY_A);
        if (!$signature && $account !== 'default') {
            $signature = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$table} WHERE user_id = %d AND account = %s LIMIT 1", $uid, 'default'), ARRAY_A);
        }
        if (!$signature) {
            return '';
        }
        $name = sanitize_text_field($signature['name'] ?? '');
        $designation = sanitize_text_field($signature['designation'] ?? '');
        $website = esc_url($signature['website_url'] ?? '');
        $logo = esc_url($signature['logo_url'] ?? '');
        $color = sanitize_hex_color($signature['theme_color'] ?? '') ?: '#111827';
        $html = '<div style="margin-top:18px;padding-top:12px;border-top:1px solid #e5e7eb;color:#111827;font-family:Arial,sans-serif;font-size:14px;line-height:1.45;">';
        if ($logo) {
            $html .= '<img src="' . $logo . '" alt="" style="max-width:120px;height:auto;margin-bottom:8px;display:block;">';
        }
        if ($name) {
            $html .= '<strong style="color:' . esc_attr($color) . ';">' . esc_html($name) . '</strong><br>';
        }
        if ($designation) {
            $html .= '<span>' . esc_html($designation) . '</span><br>';
        }
        if ($website) {
            $html .= '<a href="' . $website . '" style="color:' . esc_attr($color) . ';">' . esc_html(preg_replace('#^https?://#', '', $website)) . '</a>';
        }
        $html .= '</div>';
        return $html;
    }

    private function jarvis_fallback_reply_html($instruction, array $business, $signature) {
        $business_name = sanitize_text_field($business['business_name'] ?? get_bloginfo('name'));
        $instruction = trim(wp_strip_all_tags((string) $instruction));
        $body = '<p>Hi,</p>';
        $body .= '<p>Thank you for reaching out to ' . esc_html($business_name ?: 'us') . '.</p>';
        if ($instruction !== '') {
            $body .= '<p>' . esc_html($instruction) . '</p>';
        }
        $body .= '<p>To make sure we give you the right next step, please send over any helpful details and we will follow up shortly.</p>';
        $body .= $signature ?: '<p>Best,<br>' . esc_html($business_name ?: 'VMO') . '</p>';
        return wp_kses_post($body);
    }

    private function jarvis_send_current_email_draft($uid) {
        global $wpdb;
        $pending = get_user_meta($uid, 'vmo_jarvis_email_pending_reply', true);
        $draft_id = is_array($pending) ? intval($pending['draft_id'] ?? 0) : 0;
        if ($draft_id <= 0) {
            return $this->jarvis_email_message('I do not have a pending Jarvis reply draft to send.');
        }

        $table = $wpdb->prefix . 'vmo_emails';
        $draft = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$table} WHERE user_id = %d AND id = %d AND folder = 'drafts' LIMIT 1", $uid, $draft_id), ARRAY_A);
        if (!$draft) {
            return $this->jarvis_email_message('That reply draft is no longer available.');
        }

        if (!class_exists('VMO_Email_Router')) {
            $router_file = dirname(__FILE__) . '/class-vmo-email-router.php';
            if (file_exists($router_file) && is_readable($router_file)) {
                require_once $router_file;
            }
        }

        $sent = false;
        if (class_exists('VMO_Email_Router')) {
            $result = VMO_Email_Router::get_instance()->send_email(
                sanitize_email($draft['to_email'] ?? ''),
                sanitize_text_field($draft['subject'] ?? ''),
                wp_kses_post($draft['message'] ?? ''),
                'transactional',
                array(
                    'user_id' => $uid,
                    'mailbox_id' => isset($draft['mailbox_id']) ? intval($draft['mailbox_id']) : null,
                    'from_email' => sanitize_email($draft['from_email'] ?? ''),
                    'reply_to_email_id' => intval($draft['reply_to_email_id'] ?? 0),
                    'context' => 'jarvis_email_command',
                    'skip_storage' => true,
                )
            );
            $sent = is_array($result) ? !empty($result['success']) : (bool) $result;
        } else {
            $headers = array('Content-Type: text/html; charset=UTF-8');
            if (!empty($draft['from_email'])) {
                $headers[] = 'From: ' . sanitize_email($draft['from_email']);
                $headers[] = 'Reply-To: ' . sanitize_email($draft['from_email']);
            }
            $sent = wp_mail(sanitize_email($draft['to_email'] ?? ''), sanitize_text_field($draft['subject'] ?? ''), wp_kses_post($draft['message'] ?? ''), $headers);
        }

        if (!$sent) {
            return $this->jarvis_email_message('I could not send that reply. Please check the selected mailbox SMTP settings.');
        }

        $wpdb->update($table, array(
            'folder' => 'sent',
            'status' => 'sent',
            'sent_at' => current_time('mysql'),
            'updated_at' => current_time('mysql'),
        ), array('id' => $draft_id, 'user_id' => $uid));
        if (!empty($draft['reply_to_email_id'])) {
            $wpdb->update($table, array('is_read' => 1, 'updated_at' => current_time('mysql')), array('id' => intval($draft['reply_to_email_id']), 'user_id' => $uid));
        }
        delete_user_meta($uid, 'vmo_jarvis_email_pending_reply');

        return array(
            'type' => 'email_operator',
            'operation' => 'send_reply',
            'tab' => 'email',
            'sent_email_id' => $draft_id,
            'message' => 'Sent the reply to ' . sanitize_email($draft['to_email'] ?? '') . '.',
        );
    }

    private function format_jarvis_email_row(array $row, $index, $include_body = false) {
        $message = wp_strip_all_tags((string) ($row['message'] ?? ''));
        $out = array(
            'index' => intval($index),
            'id' => intval($row['id'] ?? 0),
            'from_email' => sanitize_email((string) ($row['from_email'] ?? '')),
            'to_email' => sanitize_email((string) ($row['to_email'] ?? '')),
            'subject' => sanitize_text_field((string) ($row['subject'] ?? '')),
            'snippet' => sanitize_text_field($this->jarvis_text_excerpt($message, 220)),
            'is_read' => !empty($row['is_read']) ? 1 : 0,
            'created_at' => sanitize_text_field((string) ($row['created_at'] ?? '')),
        );
        if ($include_body) {
            $out['body'] = $message;
        }
        return $out;
    }

    private function email_row_summary_sentence(array $row, $include_body = false) {
        $subject = sanitize_text_field((string) ($row['subject'] ?? '(No subject)'));
        $from = sanitize_email((string) ($row['from_email'] ?? 'unknown'));
        $snippet = sanitize_text_field($this->jarvis_text_excerpt(wp_strip_all_tags((string) ($row['message'] ?? '')), $include_body ? 700 : 180));
        return 'Email from ' . $from . ' about "' . $subject . '". ' . $snippet;
    }

    private function jarvis_text_excerpt($text, $length) {
        $text = (string) $text;
        $length = max(1, intval($length));
        if (function_exists('mb_substr')) {
            return mb_substr($text, 0, $length);
        }
        return substr($text, 0, $length);
    }

    private function jarvis_email_message($message) {
        return array(
            'type' => 'email_operator',
            'operation' => 'message',
            'tab' => 'email',
            'message' => $message,
        );
    }

    /**
     * Get the Jarvis navigation map for client-side JS.
     */
    public static function get_js_nav_map() {
        return [
            'overview' => 'overview', 'home' => 'overview', 'dashboard' => 'overview',
            'strategic' => 'strategic', 'strategy' => 'strategic',
            'reports' => 'reports', 'report' => 'reports',
            'phone' => 'phone', 'calls' => 'phone', 'dialer' => 'phone',
            'email' => 'email', 'emails' => 'email', 'inbox' => 'email',
            'sms' => 'sms', 'text' => 'sms', 'messages' => 'sms',
            'payments' => 'payments', 'pay' => 'payments', 'invoices' => 'payments', 'revenue' => 'payments',
            'shipping' => 'shipping', 'orders' => 'shipping', 'fulfillment' => 'shipping',
            'seo' => 'seo', 'analytics' => 'seo',
            'crm' => 'crm', 'contacts' => 'crm', 'leads' => 'crm',
            'promotions' => 'promotions',
        ];
    }
}

/**
 * GLOBAL HELPER: Log a unified action from any subsystem.
 * This is the function that Phase 1-8 classes call.
 */
if (!function_exists('vmo_log_unified_action')) {
    function vmo_log_unified_action($user_id, $source, $data = []) {
        // Normalize: if $data is an array with non-title keys, wrap it
        if (is_array($data) && !isset($data['title'])) {
            // Legacy callers pass ['to_email' => ..., 'subject' => ...]
            $title = '';
            if (!empty($data['subject'])) $title = sanitize_text_field($data['subject']);
            elseif (!empty($data['to_email'])) $title = 'Email to ' . sanitize_email($data['to_email']);
            elseif (!empty($data['form_type'])) $title = ucfirst($data['form_type']) . ' form from ' . ($data['sender'] ?? 'visitor');
            else $title = ucfirst(str_replace('_', ' ', $source));

            $data = ['title' => $title, 'description' => wp_json_encode($data), 'category' => $source];
        }

        return VMO_Integration_Hub::log_action($user_id, $source, $data);
    }
}
