/* ===================================================
   JAESWIFT.XYZ — JAE-AI Agentic Chat Terminal
   Tiered model routing + tool calls + wallet auth
   =================================================== */

(function () {
    'use strict';

    const chatMessages = document.getElementById('chatMessages');
    const chatInput = document.getElementById('chatInput');
    const chatSend = document.getElementById('chatSend');
    const chatStatus = document.getElementById('chatStatus');
    const chatHeader = document.querySelector('.chat-terminal .panel-header');

    if (!chatMessages || !chatInput || !chatSend) return;

    const mem = window.chatMemory || null;

    let history = [];
    let isWaiting = false;
    let currentTier = 'anonymous';
    let currentAddress = null;

    // ─── Utility ──────────────────────────────────────
    function escapeHtml(s) {
        return String(s || '').replace(/[&<>"']/g, function (c) {
            return ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c];
        });
    }

    // ─── Minimal safe Markdown → HTML (assistant messages only) ──
    function md(text) {
        if (!text) return '';
        let html = String(text).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
        // Fenced code blocks ```...```
        html = html.replace(/```([\s\S]*?)```/g, function (_m, code) {
            return '<pre><code>' + code + '</code></pre>';
        });
        // Inline code
        html = html.replace(/`([^`\n]+)`/g, '<code>$1</code>');
        // Bold **...**
        html = html.replace(/\*\*([^*\n]+)\*\*/g, '<strong>$1</strong>');
        // Italic *...* (avoid double-asterisk leftovers)
        html = html.replace(/(^|[^*])\*([^*\n]+)\*(?!\*)/g, '$1<em>$2</em>');
        // Links [text](url)
        html = html.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
        // Headings
        html = html.replace(/^### (.+)$/gm, '<h4>$1</h4>');
        html = html.replace(/^## (.+)$/gm, '<h3>$1</h3>');
        html = html.replace(/^# (.+)$/gm, '<h2>$1</h2>');
        // Bullet list lines: "- item" / "* item" → <li>
        html = html.replace(/^(?:-|\*) (.+)$/gm, '<li>$1</li>');
        html = html.replace(/(<li>[\s\S]*?<\/li>)(?!\s*<li>)/g, '<ul>$1</ul>');
        // Line breaks (avoid inside <pre>)
        html = html.replace(/(<pre>[\s\S]*?<\/pre>)|\n/g, function (m, pre) {
            return pre ? pre : '<br>';
        });
        return html;
    }

    // ─── Render a message bubble ──────────────────────
    function addMessage(role, text) {
        const welcome = chatMessages.querySelector('.chat-welcome');
        if (welcome) welcome.remove();

        const msg = document.createElement('div');
        msg.className = `chat-msg chat-msg-${role}`;

        const label = document.createElement('span');
        label.className = 'chat-msg-label';
        label.textContent = role === 'user' ? 'YOU' : 'JAE-AI';

        const body = document.createElement('div');
        body.className = 'chat-msg-body';
        if (role === 'assistant') { body.innerHTML = md(text); } else { body.textContent = text; }

        msg.appendChild(label);
        msg.appendChild(body);
        chatMessages.appendChild(msg);
        chatMessages.scrollTop = chatMessages.scrollHeight;
        return body;
    }

    function addToolCallCard(call) {
        const wrap = document.createElement('div');
        wrap.className = 'agent-tool-call';
        const argsStr = escapeHtml(JSON.stringify(call.args || {}, null, 0));
        const result = call.result || {};
        const errored = !!result.error;
        const statusIcon = errored ? '❌' : '✅';
        let summary = errored ? (result.error || 'error') : 'ok';
        if (!errored) {
            if (Array.isArray(result.results)) summary = `${result.results.length} result${result.results.length === 1 ? '' : 's'}`;
            else if (Array.isArray(result.entries)) summary = `${result.entries.length} entries`;
            else if (typeof result.price_usd === 'number') summary = `$${result.price_usd.toFixed(2)} (${result.change_24h_pct > 0 ? '+' : ''}${(result.change_24h_pct || 0).toFixed(2)}%)`;
            else if (result.effect) summary = `effect: ${result.effect}`;
            else if (result.balance_sol !== undefined) summary = `${result.balance_sol} SOL`;
        }
        const resultStr = escapeHtml(JSON.stringify(result, null, 2).slice(0, 1400));
        wrap.innerHTML = `
            <div class="atc-header">
                <span class="atc-icon">🔧</span>
                <span class="atc-name">${escapeHtml(call.name)}</span>
                <span class="atc-args">(${argsStr})</span>
            </div>
            <div class="atc-status">├─ ${statusIcon} ${escapeHtml(summary)}</div>
            <details class="atc-details"><summary>└─ (click to expand)</summary><pre>${resultStr}</pre></details>
        `;
        chatMessages.appendChild(wrap);
        chatMessages.scrollTop = chatMessages.scrollHeight;
    }

    // ─── Typing indicator ─────────────────────────────
    function showTyping() {
        const indicator = document.createElement('div');
        indicator.className = 'chat-msg chat-msg-assistant chat-typing-indicator';
        indicator.id = 'chatTyping';
        indicator.innerHTML = `
            <span class="chat-msg-label">JAE-AI</span>
            <div class="chat-msg-body">
                <span class="typing-dot"></span><span class="typing-dot"></span><span class="typing-dot"></span>
            </div>`;
        chatMessages.appendChild(indicator);
        chatMessages.scrollTop = chatMessages.scrollHeight;
    }
    function hideTyping() {
        const el = document.getElementById('chatTyping');
        if (el) el.remove();
    }

    // ─── Typewriter effect ────────────────────────────
    function typewriterEffect(element, text, speed) {
        speed = speed || 10;
        element.innerHTML = '';
        // Bypass animation for long replies — render full markdown immediately
        if (!text || text.length > 600) {
            element.innerHTML = md(text || '');
            chatMessages.scrollTop = chatMessages.scrollHeight;
            return Promise.resolve();
        }
        let i = 0;
        return new Promise(function (resolve) {
            function tick() {
                if (i < text.length) {
                    i++;
                    element.innerHTML = md(text.slice(0, i));
                    chatMessages.scrollTop = chatMessages.scrollHeight;
                    setTimeout(tick, speed);
                } else {
                    // Dispatch event so voice-mode / others can hook
                    try { document.dispatchEvent(new CustomEvent('jae-agent-reply', { detail: { text: text } })); } catch (e) {}
                    resolve();
                }
            }
            tick();
        });
    }

    // ─── History restore ──────────────────────────────
    function restoreHistory() {
        if (!mem) return false;
        const saved = mem.getHistory();
        if (!saved || saved.length === 0) return false;
        const welcome = chatMessages.querySelector('.chat-welcome');
        if (welcome) welcome.remove();
        saved.forEach(function (m) {
            const role = m.role === 'user' ? 'user' : 'assistant';
            const label = document.createElement('span');
            label.className = 'chat-msg-label';
            label.textContent = role === 'user' ? 'YOU' : 'JAE-AI';
            const body = document.createElement('div');
            body.className = 'chat-msg-body';
            if (role === 'assistant') { body.innerHTML = md(m.content || ''); } else { body.textContent = m.content || ''; }
            const el = document.createElement('div');
            el.className = `chat-msg chat-msg-${role}`;
            el.appendChild(label);
            el.appendChild(body);
            chatMessages.appendChild(el);
        });
        const divider = document.createElement('div');
        divider.className = 'chat-divider';
        divider.innerHTML = '<span>— RESTORED FROM MEMORY —</span>';
        chatMessages.appendChild(divider);
        chatMessages.scrollTop = chatMessages.scrollHeight;
        history = saved.slice();
        return true;
    }

    // ─── Tier badge ───────────────────────────────────
    const TIER_DISPLAY = {
        anonymous: { label: 'ANONYMOUS', icon: '👁' },
        operator:  { label: 'OPERATOR',  icon: '🎖️' },
        elite:     { label: 'ELITE',     icon: '⭐' },
        admin:     { label: 'ADMIN',     icon: '🛠️' },
    };

    function renderTierBadge() {
        if (!chatHeader) return;
        let badge = document.getElementById('chatTierBadge');
        if (!badge) {
            badge = document.createElement('span');
            badge.id = 'chatTierBadge';
            badge.className = 'tier-badge';
            chatHeader.appendChild(badge);
        }
        const info = TIER_DISPLAY[currentTier] || TIER_DISPLAY.anonymous;
        badge.className = 'tier-badge tier-' + currentTier;
        badge.textContent = info.icon + ' ' + info.label;
        badge.title = currentAddress ? (currentAddress.slice(0, 4) + '…' + currentAddress.slice(-4)) : 'Not authenticated';
    }

    // ─── Wallet auth handshake ────────────────────────
    async function refreshWhoAmI() {
        try {
            const r = await fetch('/api/auth/whoami', { credentials: 'same-origin' });
            if (!r.ok) return;
            const j = await r.json();
            currentTier = j.tier || 'anonymous';
            currentAddress = j.address || null;
            renderTierBadge();
        } catch (e) { /* silent */ }
    }

    async function walletSignIn(address, provider) {
        if (!address || !provider) return;
        try {
            // 1. Get nonce
            const nr = await fetch('/api/auth/nonce', {
                method: 'POST', headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ address }),
            });
            if (!nr.ok) { console.warn('nonce fetch failed'); return; }
            const { message, nonce } = await nr.json();

            // 2. Sign message
            const encoded = new TextEncoder().encode(message);
            let sigBytes;
            if (typeof provider.signMessage === 'function') {
                const res = await provider.signMessage(encoded, 'utf8');
                sigBytes = res && (res.signature || res);
            } else if (provider.features && provider.features['solana:signMessage']) {
                const feat = provider.features['solana:signMessage'];
                const res = await feat.signMessage({ message: encoded });
                sigBytes = Array.isArray(res) ? res[0].signature : res.signature;
            } else {
                console.warn('wallet does not support signMessage');
                return;
            }
            if (!sigBytes) return;
            // sigBytes -> base58
            const sigB58 = bs58encode(sigBytes);

            // 3. Verify
            const vr = await fetch('/api/auth/verify', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                credentials: 'same-origin',
                body: JSON.stringify({ address, signature: sigB58, nonce }),
            });
            if (!vr.ok) {
                const err = await vr.json().catch(() => ({}));
                console.warn('verify failed:', err);
                return;
            }
            const j = await vr.json();
            currentTier = j.tier || 'operator';
            currentAddress = j.address || address;
            renderTierBadge();
            const info = TIER_DISPLAY[currentTier];
            addMessage('assistant', `🔓 Wallet verified. Tier: ${info.icon} ${info.label}. Model: ${j.model || 'default'}.`);
        } catch (e) {
            console.warn('walletSignIn error', e);
        }
    }

    // Minimal bs58 encoder (no external deps)
    const BS58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
    function bs58encode(bytes) {
        if (!bytes) return '';
        if (!(bytes instanceof Uint8Array)) bytes = new Uint8Array(bytes);
        const digits = [0];
        for (let i = 0; i < bytes.length; i++) {
            let carry = bytes[i];
            for (let j = 0; j < digits.length; j++) {
                carry += digits[j] << 8;
                digits[j] = carry % 58;
                carry = (carry / 58) | 0;
            }
            while (carry > 0) { digits.push(carry % 58); carry = (carry / 58) | 0; }
        }
        let str = '';
        for (let k = 0; k < bytes.length && bytes[k] === 0; k++) str += '1';
        for (let q = digits.length - 1; q >= 0; q--) str += BS58_ALPHABET[digits[q]];
        return str;
    }

    // Listen to wallet-connect events
    window.addEventListener('wallet-connected', function (e) {
        const detail = e.detail || {};
        const addr = detail.address || (window.solWallet && window.solWallet.address);
        const provider = detail.provider || (window.solWallet && window.solWallet.provider);
        if (addr && provider) walletSignIn(addr, provider);
    });
    window.addEventListener('wallet-disconnected', async function () {
        try { await fetch('/api/auth/logout', { method: 'POST', credentials: 'same-origin' }); } catch (e) {}
        currentTier = 'anonymous';
        currentAddress = null;
        renderTierBadge();
    });

    // ─── Send message to agent ────────────────────────
    async function sendMessage() {
        const text = chatInput.value.trim();
        if (!text || isWaiting) return;

        // CLI interception (slash commands, or nested CLI mode)
        const inCliMode = window.__jaeCLI && typeof window.__jaeCLI.isInMode === 'function' && window.__jaeCLI.isInMode();
        if ((text.startsWith('/') || inCliMode) && window.__jaeCLI && typeof window.__jaeCLI.handle === 'function') {
            isWaiting = true;
            chatInput.value = '';
            const userBubble = addMessage('user', text);
            const userMsg = userBubble && userBubble.parentElement;
            if (userMsg) userMsg.classList.add('chat-msg-user-cli');
            try {
                const res = await window.__jaeCLI.handle(text);
                if (res && res.handled) {
                    if (res.output) {
                        const body = addMessage('assistant', '');
                        const msgEl = body && body.parentElement;
                        if (msgEl) msgEl.classList.add('chat-msg-cli');
                        body.innerHTML = md(res.output);
                        chatMessages.scrollTop = chatMessages.scrollHeight;
                    }
                }
            } catch (e) {
                const body = addMessage('assistant', 'cli error: ' + e.message);
                const msgEl = body && body.parentElement;
                if (msgEl) msgEl.classList.add('chat-msg-cli');
            }
            isWaiting = false;
            chatInput.focus();
            return;
        }

        isWaiting = true;
        chatInput.value = '';
        chatStatus.textContent = '● PROCESSING';
        chatStatus.classList.remove('status-green');
        chatStatus.classList.add('status-amber');

        addMessage('user', text);
        history.push({ role: 'user', content: text });
        if (mem) mem.appendHistory({ role: 'user', content: text });

        showTyping();

        // Build conversation window (last 20 exchanges)
        const convo = history.slice(-20).map(function (m) {
            return { role: m.role, content: m.content };
        });

        const memoryBlock = mem ? mem.getContextBlock(text) : '';

        try {
            const resp = await fetch('/api/agent/chat', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                credentials: 'same-origin',
                body: JSON.stringify({
                    messages: convo,
                    memory_context: memoryBlock,
                }),
            });

            hideTyping();

            if (!resp.ok) {
                const err = await resp.json().catch(function () { return {}; });
                addMessage('assistant', 'ERROR: ' + (err.error || ('HTTP ' + resp.status)));
                chatStatus.textContent = '● ERROR';
                chatStatus.classList.remove('status-amber');
                chatStatus.classList.add('status-red');
                isWaiting = false;
                return;
            }

            const data = await resp.json();

            // Update tier from response
            if (data.tier) { currentTier = data.tier; renderTierBadge(); }

            // Render tool calls
            if (Array.isArray(data.tool_calls)) {
                data.tool_calls.forEach(addToolCallCard);
            }

            const reply = data.content || '(no reply)';
            history.push({ role: 'assistant', content: reply });
            if (mem) mem.appendHistory({ role: 'assistant', content: reply });
            if (history.length > 40) history = history.slice(-30);

            const bodyEl = addMessage('assistant', '');
            bodyEl.parentElement.classList.add('chat-msg-agent');
            bodyEl.parentElement.setAttribute('data-model', data.model_used || '');
            await typewriterEffect(bodyEl, reply, 8);

            // Execute frontend actions (effects etc.)
            if (Array.isArray(data.frontend_actions)) {
                data.frontend_actions.forEach(function (act) {
                    if (act.action === 'trigger_effect' && window.__jaeEffects && typeof window.__jaeEffects.toggle === 'function') {
                        try { window.__jaeEffects.toggle(act.effect); } catch (e) { console.warn('effect toggle failed', e); }
                    }
                });
            }

            if (mem) {
                mem.incrementUserMsgAndMaybeExtract().catch(function () { /* silent */ });
            }
        } catch (e) {
            hideTyping();
            addMessage('assistant', 'ERROR: Network failure — ' + e.message);
            chatStatus.textContent = '● OFFLINE';
            chatStatus.classList.remove('status-amber');
            chatStatus.classList.add('status-red');
        }

        chatStatus.textContent = '● ONLINE';
        chatStatus.classList.remove('status-amber', 'status-red');
        chatStatus.classList.add('status-green');
        isWaiting = false;
        chatInput.focus();
    }

    // ─── Memory modal ─────────────────────────────────
    function formatDate(ts) {
        try { return new Date(ts).toLocaleString('en-GB'); } catch (e) { return ''; }
    }

    function renderMemoryModal() {
        if (!mem) return;
        const existing = document.getElementById('memModal');
        if (existing) { existing.remove(); return; }

        const modal = document.createElement('div');
        modal.id = 'memModal';
        modal.className = 'mem-modal';

        const all = mem.getAll();
        const grouped = {};
        all.forEach(function (m) {
            const c = m.category || 'other';
            if (!grouped[c]) grouped[c] = [];
            grouped[c].push(m);
        });

        const categories = ['identity', 'preference', 'project', 'skill', 'goal', 'relationship', 'other'];
        const order = categories.filter(function (c) { return grouped[c]; })
            .concat(Object.keys(grouped).filter(function (c) { return categories.indexOf(c) === -1; }));

        let bodyHTML = '';
        if (all.length === 0) {
            bodyHTML = '<div class="mem-empty">NO MEMORIES STORED // CHAT TO BUILD PROFILE</div>';
        } else {
            order.forEach(function (cat) {
                bodyHTML += '<div class="mem-group"><div class="mem-group-title">' + cat.toUpperCase() + ' <span class="mem-count">(' + grouped[cat].length + ')</span></div>';
                grouped[cat].sort(function (a, b) { return (b.timestamp || 0) - (a.timestamp || 0); }).forEach(function (m) {
                    bodyHTML += '<div class="mem-item" data-id="' + m.id + '">' +
                        '<div class="mem-item-text">' + escapeHtml(m.text) + '</div>' +
                        '<div class="mem-item-meta">imp: ' + (m.importance || 0).toFixed(2) + ' // ' + formatDate(m.timestamp) + '</div>' +
                        '<button class="mem-del" data-id="' + m.id + '" title="Delete">×</button>' +
                        '</div>';
                });
                bodyHTML += '</div>';
            });
        }

        modal.innerHTML = `
            <div class="mem-modal-inner">
                <div class="mem-modal-header">
                    <span class="mem-modal-title">🧠 JAE-AI MEMORY VAULT</span>
                    <button class="mem-close" id="memClose">× CLOSE</button>
                </div>
                <div class="mem-modal-subtitle">STORED LOCALLY // ${all.length} MEMORIES // NEVER LEAVES DEVICE</div>
                <div class="mem-modal-body">${bodyHTML}</div>
                <div class="mem-modal-footer">
                    <button class="mem-btn mem-btn-export" id="memExport">⬇ EXPORT JSON</button>
                    <button class="mem-btn mem-btn-extract" id="memExtract">⚡ FORCE EXTRACT</button>
                    <button class="mem-btn mem-btn-clearhist" id="memClearHist">🗑 CLEAR HISTORY</button>
                    <button class="mem-btn mem-btn-clear" id="memClear">⚠ CLEAR ALL MEMORIES</button>
                </div>
            </div>`;

        document.body.appendChild(modal);

        document.getElementById('memClose').addEventListener('click', function () { modal.remove(); });
        modal.addEventListener('click', function (e) { if (e.target === modal) modal.remove(); });

        modal.querySelectorAll('.mem-del').forEach(function (btn) {
            btn.addEventListener('click', function () {
                const id = btn.getAttribute('data-id');
                mem.remove(id);
                renderMemoryModal(); renderMemoryModal();
            });
        });
        document.getElementById('memExport').addEventListener('click', function () {
            const blob = new Blob([mem.exportJSON()], { type: 'application/json' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url; a.download = 'jae-ai-memory-' + Date.now() + '.json'; a.click();
            setTimeout(function () { URL.revokeObjectURL(url); }, 1000);
        });
        document.getElementById('memExtract').addEventListener('click', function () {
            const btn = document.getElementById('memExtract');
            btn.disabled = true; btn.textContent = '⌛ EXTRACTING...';
            mem.extractFromRecentChat().then(function () {
                renderMemoryModal(); renderMemoryModal();
            }).catch(function () { btn.textContent = '✖ EXTRACT FAILED'; });
        });
        document.getElementById('memClearHist').addEventListener('click', function () {
            if (confirm('Clear chat history? Memories will be kept.')) {
                mem.clearHistory(); alert('Chat history cleared. Reload the page.');
            }
        });
        document.getElementById('memClear').addEventListener('click', function () {
            if (confirm('PERMANENTLY DELETE ALL MEMORIES? This cannot be undone.')) {
                mem.clear(); renderMemoryModal(); renderMemoryModal();
            }
        });
    }

    // ─── Header UI injection ──────────────────────────
    function injectMemoryUI() {
        if (!chatHeader) return;
        if (document.getElementById('memBtn')) return;

        const btn = document.createElement('button');
        btn.id = 'memBtn';
        btn.className = 'chat-mem-btn';
        btn.title = 'View / manage local memories';
        btn.innerHTML = '🧠 <span class="chat-mem-count" id="memBtnCount">0</span>';
        btn.addEventListener('click', renderMemoryModal);

        const info = document.createElement('a');
        info.href = '#';
        info.className = 'chat-mem-info';
        info.title = 'All memories stored locally in your browser. Nothing leaves your device except current message context.';
        info.textContent = 'ℹ';
        info.addEventListener('click', function (e) {
            e.preventDefault();
            alert('PRIVACY NOTICE\n\nAll JAE-AI memories are stored locally in your browser (localStorage). They never leave your device.\n\nPer message, only the current text plus a small set of relevant memories is sent as context to Venice AI to provide continuity.\n\nYou can view, export, or clear all memories via the 🧠 button.');
        });

        chatHeader.appendChild(info);
        chatHeader.appendChild(btn);
        updateMemCount();
    }

    function updateMemCount() {
        const el = document.getElementById('memBtnCount');
        if (el && mem) el.textContent = mem.getAll().length;
    }

    // ─── Event listeners ──────────────────────────────
    chatSend.addEventListener('click', sendMessage);
    chatInput.addEventListener('keydown', function (e) {
        if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); sendMessage(); }
    });

    // ─── Init ─────────────────────────────────────────
    injectMemoryUI();
    renderTierBadge();
    refreshWhoAmI();
    setInterval(updateMemCount, 5000);

    const restored = restoreHistory();
    if (!restored) {
        setTimeout(function () {
            showTyping();
            setTimeout(function () {
                hideTyping();
                const greeting = 'Welcome to JAESWIFT.XYZ — I\'m JAE-AI. I can now search the site, fetch crypto prices, scan Solana wallets, look up .sol domains, pull SITREPs, and trigger visual effects. Connect your wallet for the Elite tier (1+ SOL) with Kimi-K2-Thinking. Ask me anything.';
                history.push({ role: 'assistant', content: greeting });
                if (mem) mem.appendHistory({ role: 'assistant', content: greeting });
                const bodyEl = addMessage('assistant', '');
                typewriterEffect(bodyEl, greeting, 10);
            }, 1500);
        }, 2000);
    }

    // Expose minimal API
    window.__jaeChat = {
        refreshWhoAmI: refreshWhoAmI,
        getTier: function () { return currentTier; },
        getAddress: function () { return currentAddress; },
    };
})();
