// Provider data
        let providers = {};
        let selectedProvider = null;
        let selectedModel = null;

        // Primary contact info from PHP
        const primaryContactName = '<?= addslashes($primaryContactName ?: '') ?>';
        const primaryContactEmail = '<?= addslashes($primaryContactEmail ?: '') ?>';

        // ============================================
        // AI AGENTS
        // ============================================
        const AI_AGENTS = {
            general: {
                name: 'General',
                icon: '\uD83D\uDCAC',
                systemPrompt: 'You are a highly capable business intelligence AI assistant. Analyze the provided email conversations thoroughly. Provide clear, actionable insights. Be direct and specific.',
                quickPrompts: [
                    'Summarize the key points',
                    'What are the action items?',
                    'What is the overall sentiment?',
                    'What should I reply?'
                ]
            },
            quote_analyst: {
                name: 'Quotes',
                icon: '\uD83D\uDCB0',
                systemPrompt: 'You are a pricing and quotation analyst. Focus on:\n- All quoted prices, amounts, and financial figures\n- Price changes over time and negotiation patterns\n- Scope of work vs pricing alignment\n- Competitor pricing if mentioned\n- Negotiation leverage points\n- Budget signals and willingness to pay\nAlways present findings with specific numbers and dates.',
                quickPrompts: [
                    'What prices have been quoted?',
                    'Show the negotiation history',
                    'What is the pricing trend?',
                    'What should we quote next?'
                ]
            },
            deception_detector: {
                name: 'Deception',
                icon: '\uD83D\uDD0D',
                systemPrompt: 'You are a deception and inconsistency detection specialist. Analyze the emails for:\n- Contradictions between different messages\n- Promises made but not kept\n- Vague or evasive responses to direct questions\n- Sudden changes in tone or position\n- Pressure tactics or urgency manipulation\n- Information that doesn\'t add up\n- Omissions of relevant details\nRate the overall deception risk as LOW/MEDIUM/HIGH with evidence.',
                quickPrompts: [
                    'Any contradictions detected?',
                    'Rate the deception risk level',
                    'Are there broken promises?',
                    'Any pressure tactics used?'
                ]
            },
            contact_profiler: {
                name: 'Profiler',
                icon: '\uD83D\uDC64',
                systemPrompt: 'You are a contact profiling specialist. Build a psychological and business profile of the contact based on their emails:\n- Communication style (formal/casual, verbose/concise)\n- Decision-making patterns\n- Negotiation style and tendencies\n- Responsiveness and reliability\n- Priorities and motivations\n- Relationship health indicators\n- Loyalty signals\nProvide specific examples from the emails to support your assessment.',
                quickPrompts: [
                    'Build a full profile',
                    'Communication style analysis',
                    'How loyal is this contact?',
                    'What motivates them?'
                ]
            },
            email_composer: {
                name: 'Composer',
                icon: '\u270D\uFE0F',
                systemPrompt: 'You are a professional email composer. Based on the conversation context:\n- Draft a professional, well-structured reply\n- Match the appropriate tone (formal/casual based on the thread)\n- Address all open questions or action items\n- Be concise but thorough\n- Include appropriate greetings and sign-offs\nOutput ONLY the email draft, ready to send.',
                quickPrompts: [
                    'Draft a reply',
                    'Write a follow-up email',
                    'Compose a polite decline',
                    'Draft a meeting request'
                ]
            }
        };

        let selectedAgent = 'general';

        // ============================================
        // TOKEN ESTIMATION
        // ============================================
        const MODEL_LIMITS = {
            'claude-sonnet-4-5-20250929': 200000,
            'claude-3-5-sonnet-20241022': 200000,
            'claude-opus-4-5-20250929': 200000,
            'claude-3-opus-20240229': 200000,
            'claude-3-5-haiku-20241022': 200000,
            'gpt-4o': 128000,
            'gpt-4o-mini': 128000,
            'gpt-4-turbo': 128000,
            'gpt-4.1': 1000000,
            'gpt-4.1-mini': 1000000,
            'o3': 200000,
            'o3-mini': 200000,
            'o4-mini': 200000,
            'gemini-2.0-flash': 1048576,
            'gemini-2.5-pro-preview-05-06': 1048576,
            'gemini-2.5-flash-preview-04-17': 1048576,
            'grok-3': 131072,
            'grok-3-mini': 131072,
            'deepseek-chat': 64000,
            'deepseek-reasoner': 64000,
            'qwen-max': 32000,
            'qwen-plus': 131000,
            'mistral-large-latest': 128000,
            'command-a-03-2025': 256000,
            'command-r-plus': 128000
        };

        function getSmallestModelLimit() {
            var selectedProviders = getSelectedProviders();
            var minLimit = Infinity;
            selectedProviders.forEach(function(prov) {
                var modelSelect = document.getElementById('model_' + prov);
                var model = modelSelect ? modelSelect.value : '';
                var limit = MODEL_LIMITS[model] || 128000;
                if (limit < minLimit) minLimit = limit;
            });
            return minLimit === Infinity ? 128000 : minLimit;
        }

        function estimateCurrentTokens() {
            var content = buildEmailContent();
            var question = (document.getElementById('ai-question').value || '').trim();
            var context = (document.getElementById('ai-context').value || '').trim();
            var agent = AI_AGENTS[selectedAgent];
            var total = content.length;
            if (agent) total += agent.systemPrompt.length;
            if (question) total += question.length;
            if (context) total += context.length;
            // Add conversation history estimate
            total += buildConversationContext().length;
            // Add context threads estimate (rough: each thread ~2000 chars)
            total += contextThreads.length * 2000;
            return Math.ceil(total / 3.5);
        }

        function updateTokenEstimate() {
            var el = document.getElementById('token-estimate');
            if (!el) return;
            var tokens = estimateCurrentTokens();
            var limit = getSmallestModelLimit();
            var pct = (tokens / limit) * 100;
            var formatted = tokens.toLocaleString();

            if (pct < 60) {
                el.className = 'token-estimate ok';
                el.textContent = 'Estimated: ~' + formatted + ' tokens (OK)';
            } else if (pct < 90) {
                el.className = 'token-estimate warning';
                el.textContent = 'Estimated: ~' + formatted + ' tokens (approaching limit)';
            } else {
                el.className = 'token-estimate danger';
                el.textContent = 'Estimated: ~' + formatted + ' tokens (EXCEEDS limit!)';
            }
        }

        // ============================================
        // CONTEXT BUILDER
        // ============================================
        var contextThreads = []; // {threadId, subject, emailCount}
        var contextSearchTimeout = null;
        var contactMemories = [];

        function toggleContextBuilder() {
            var el = document.getElementById('context-builder');
            if (el.style.display === 'none') {
                el.style.display = 'block';
                document.getElementById('context-search').focus();
            } else {
                el.style.display = 'none';
            }
        }

        function debounceContextSearch() {
            clearTimeout(contextSearchTimeout);
            contextSearchTimeout = setTimeout(function() {
                var q = document.getElementById('context-search').value.trim();
                if (q.length >= 2) {
                    searchContext(q);
                } else {
                    document.getElementById('context-search-results').innerHTML = '';
                }
            }, 300);
        }

        function searchContext(query) {
            var resultsEl = document.getElementById('context-search-results');
            resultsEl.innerHTML = '
Searching...
';

            // Search contacts and threads in parallel
            Promise.all([
                fetch('api.php?action=contacts&search=' + encodeURIComponent(query) + '&limit=5').then(function(r) { return r.json(); }),
                fetch('api.php?action=threads&search=' + encodeURIComponent(query) + '&limit=5').then(function(r) { return r.json(); })
            ]).then(function(results) {
                var contacts = results[0].contacts || [];
                var threads = results[1].threads || [];
                var html = '';

                if (contacts.length > 0) {
                    html += '
CONTACTS
';
                    contacts.forEach(function(c) {
                        html += '
';
                        html += '
';
                        html += '' + escapeHtml(c.name || c.email) + '';
                        html += '
' + escapeHtml(c.email || '') + ' | ' + (c.email_count || 0) + ' emails
';
                        html += '
';
                        html += '<button class="context-add-btn" onclick="loadContactThreads(' + (c.id || c.contact_id) + ', \'' + escapeHtml(c.name || '') + '\')">View Threads</button>';
                        html += '
';
                    });
                }

                if (threads.length > 0) {
                    html += '
THREADS
';
                    threads.forEach(function(t) {
                        var already = contextThreads.some(function(ct) { return ct.threadId === t.thread_id; });
                        var isCurrent = t.thread_id === currentThreadId;
                        html += '
';
                        html += '
';
                        html += '' + escapeHtml(t.subject || '(No Subject)') + '';
                        html += '
' + (t.email_count || 0) + ' emails | ' + escapeHtml(t.contact_name || '') + '
';
                        html += '
';
                        if (isCurrent) {
                            html += 'Current';
                        } else if (already) {
                            html += 'Added';
                        } else {
                            html += '<button class="context-add-btn" onclick="addThreadToContext(\'' + t.thread_id + '\', \'' + escapeHtml(t.subject || '').replace(/'/g, "\\'") + '\', ' + (t.email_count || 0) + ')">+ Add</button>';
                        }
                        html += '
';
                    });
                }

                if (!html) {
                    html = '
No results found
';
                }

                resultsEl.innerHTML = html;
            }).catch(function(err) {
                resultsEl.innerHTML = '
Search failed
';
            });
        }

        function loadContactThreads(contactId, contactName) {
            var resultsEl = document.getElementById('context-search-results');
            resultsEl.innerHTML = '
Loading threads for ' + escapeHtml(contactName) + '...
';

            fetch('api.php?action=threads&contact=' + contactId + '&limit=20')
                .then(function(r) { return r.json(); })
                .then(function(data) {
                    var threads = data.threads || [];
                    var html = '
Threads from ' + escapeHtml(contactName) + '
';

                    if (threads.length > 1) {
                        html += '
<button class="context-add-btn" onclick="addAllContactThreads()" style="width:100%;text-align:center;">Add All ' + threads.length + ' Threads</button>
';
                    }

                    // Store for "Add All"
                    window._lastContactThreads = threads;

                    threads.forEach(function(t) {
                        var already = contextThreads.some(function(ct) { return ct.threadId === t.thread_id; });
                        var isCurrent = t.thread_id === currentThreadId;
                        html += '
';
                        html += '
';
                        html += '' + escapeHtml(t.subject || '(No Subject)') + '';
                        html += '
' + (t.email_count || 0) + ' emails
';
                        html += '
';
                        if (isCurrent) {
                            html += 'Current';
                        } else if (already) {
                            html += 'Added';
                        } else {
                            html += '<button class="context-add-btn" onclick="addThreadToContext(\'' + t.thread_id + '\', \'' + escapeHtml(t.subject || '').replace(/'/g, "\\'") + '\', ' + (t.email_count || 0) + ')">+ Add</button>';
                        }
                        html += '
';
                    });

                    resultsEl.innerHTML = html;

                    // Also load contact memories
                    loadContactMemories(contactId);
                });
        }

        function addAllContactThreads() {
            var threads = window._lastContactThreads || [];
            threads.forEach(function(t) {
                if (t.thread_id !== currentThreadId && !contextThreads.some(function(ct) { return ct.threadId === t.thread_id; })) {
                    contextThreads.push({
                        threadId: t.thread_id,
                        subject: t.subject || '(No Subject)',
                        emailCount: t.email_count || 0
                    });
                }
            });
            updateContextCart();
            updateTokenEstimate();
            // Refresh search results to show "Added" labels
            var q = document.getElementById('context-search').value.trim();
            if (q) searchContext(q);
        }

        function addThreadToContext(threadId, subject, emailCount) {
            if (threadId === currentThreadId) return;
            if (contextThreads.some(function(ct) { return ct.threadId === threadId; })) return;
            contextThreads.push({
                threadId: threadId,
                subject: subject,
                emailCount: emailCount
            });
            updateContextCart();
            updateTokenEstimate();
            // Refresh search results
            var q = document.getElementById('context-search').value.trim();
            if (q) searchContext(q);
        }

        function removeThreadFromContext(threadId) {
            contextThreads = contextThreads.filter(function(ct) { return ct.threadId !== threadId; });
            updateContextCart();
            updateTokenEstimate();
        }

        function updateContextCart() {
            var el = document.getElementById('context-cart');
            if (contextThreads.length === 0) {
                el.innerHTML = '';
                return;
            }
            var totalEmails = contextThreads.reduce(function(sum, ct) { return sum + ct.emailCount; }, 0);
            var html = '
';
            html += '
' + contextThreads.length + ' additional thread(s), ~' + totalEmails + ' emails
';
            contextThreads.forEach(function(ct) {
                html += '
';
                html += '' + escapeHtml(ct.subject) + ' (' + ct.emailCount + ')';
                html += '<button class="context-cart-remove" onclick="removeThreadFromContext(\'' + ct.threadId + '\')">×</button>';
                html += '
';
            });
            html += '
';
            el.innerHTML = html;
        }

        // ============================================
        // CONTACT MEMORY
        // ============================================
        function loadContactMemories(contactId) {
            fetch('api.php?action=ai_contact_memories&contact_id=' + contactId)
                .then(function(r) { return r.json(); })
                .then(function(data) {
                    contactMemories = data.memories || [];
                    renderContactMemories();
                })
                .catch(function() { contactMemories = []; });
        }

        function renderContactMemories() {
            var el = document.getElementById('contact-memories');
            if (!contactMemories.length) {
                el.innerHTML = '';
                return;
            }
            var html = '
Known Facts
';
            html += '
';
            contactMemories.forEach(function(m) {
                html += '' + escapeHtml(m.fact || m) + '';
            });
            html += '
';
            el.innerHTML = html;
        }

        // ============================================
        // AGENT SELECTION
        // ============================================
        function initAgentButtons() {
            var container = document.getElementById('agent-buttons');
            if (!container) return;
            var html = '';
            for (var id in AI_AGENTS) {
                var agent = AI_AGENTS[id];
                html += '<button class="agent-btn' + (id === selectedAgent ? ' active' : '') + '" data-agent="' + id + '" onclick="selectAgent(\'' + id + '\')">';
                html += agent.icon + ' ' + agent.name;
                html += '</button>';
            }
            container.innerHTML = html;
            updateAgentQuickPrompts();
        }

        function selectAgent(agentId) {
            selectedAgent = agentId;
            // Update button styles
            document.querySelectorAll('.agent-btn').forEach(function(btn) {
                btn.classList.toggle('active', btn.dataset.agent === agentId);
            });
            updateAgentQuickPrompts();
            updateTokenEstimate();
        }

        function updateAgentQuickPrompts() {
            var container = document.getElementById('agent-quick-prompts');
            if (!container) return;
            var agent = AI_AGENTS[selectedAgent];
            if (!agent || !agent.quickPrompts || !agent.quickPrompts.length) {
                container.style.display = 'none';
                return;
            }
            container.style.display = 'flex';
            var html = '';
            agent.quickPrompts.forEach(function(prompt) {
                html += '<button class="quick-prompt-btn" onclick="useQuickPrompt(\'' + prompt.replace(/'/g, "\\'") + '\')">' + escapeHtml(prompt) + '</button>';
            });
            container.innerHTML = html;
        }

        function useQuickPrompt(prompt) {
            document.getElementById('ai-question').value = prompt;
            updateTokenEstimate();
        }

        // ============================================
        // DEBUG LOGGING
        // ============================================
        function logAiDebug(data) {
            try {
                fetch('api.php?action=log_ai_debug', {
                    method: 'POST',
                    headers: { 'Content-Type': 'application/json' },
                    body: JSON.stringify(data)
                });
            } catch (e) {
                console.error('Debug log failed:', e);
            }
        }

        // ============================================
        // CONVERSATION MEMORY - ChatGPT-like history
        // ============================================