// ==========================================
// Debug Logger & Modal
// ==========================================
// Console interceptor, fetch logger, state inspector.
// Adapted from ai-editor's error-logger.js + llm-debug-modal.js
// for no-devtools "hardmode" debugging.
//
// Activate: Ctrl+K → "debug" or tap the 🐛 badge (appears after init)
// ==========================================
//
// Exports: window.DebugLog,window.openDebugModal,window.switchDebugTab,window.clearDebugLog,window.copyDebugLog,window.exportDebugLog,window.runDebugDiagnostics,window.purgeCache


const DebugLog = {
    entries: [],
    maxEntries: 500,
    networkLog: [],
    maxNetwork: 200,
    _origConsole: {},
    _origFetch: null,
    _initTime: Date.now(),
    _errorCount: 0,

    // ── Bootstrap ────────────────────────────

    init() {
        this._interceptConsole();
        this._interceptFetch();
        this._interceptErrors();
        this._injectBadge();
        this.log('DEBUG', '🐛 Debug logger initialized');
        this.log('DEBUG', `Page: ${location.href}`);
        this.log('DEBUG', `UA: ${navigator.userAgent.slice(0, 120)}`);
    },

    // ── Console Interception ────────────────

    _interceptConsole() {
        ['log', 'warn', 'error', 'info', 'debug'].forEach(method => {
            this._origConsole[method] = console[method].bind(console);
            console[method] = (...args) => {
                const type = method.toUpperCase();
                const message = args.map(a => this._serialize(a)).join(' ');
                this._addEntry(type, message);
                this._origConsole[method](...args);
            };
        });
    },

    // ── Fetch Interception ──────────────────

    _interceptFetch() {
        this._origFetch = window.fetch.bind(window);
        const self = this;

        window.fetch = async function(input, init) {
            const url = typeof input === 'string' ? input : input?.url || String(input);
            const method = init?.method || 'GET';
            const entry = {
                id: Date.now(),
                ts: new Date().toISOString(),
                method,
                url: url.slice(0, 300),
                status: null,
                statusText: '',
                contentType: null,
                duration: null,
                error: null,
                responsePreview: null,
                requestBodyPreview: null,
                proxyDetected: false
            };

            // Capture request body preview (redact keys)
            if (init?.body) {
                try {
                    const bodyStr = typeof init.body === 'string' ? init.body : '';
                    const parsed = bodyStr ? JSON.parse(bodyStr) : {};
                    // Redact sensitive fields
                    const safe = { ...parsed };
                    ['password', 'api_key', 'apiKey', 'refresh_token', 'access_token'].forEach(k => {
                        if (safe[k]) safe[k] = '***';
                    });
                    entry.requestBodyPreview = JSON.stringify(safe).slice(0, 300);
                } catch (e) {
                    entry.requestBodyPreview = '(non-JSON body)';
                }
            }

            self.networkLog.push(entry);
            if (self.networkLog.length > self.maxNetwork) {
                self.networkLog.shift();
            }

            const start = performance.now();
            try {
                const resp = await self._origFetch(input, init);
                entry.status = resp.status;
                entry.statusText = resp.statusText;
                entry.contentType = resp.headers.get('content-type') || null;
                entry.duration = Math.round(performance.now() - start);

                // Detect proxy interception: API call returned HTML
                const isApiCall = url.includes('/api/') || url.includes('/health');
                if (isApiCall && entry.contentType && entry.contentType.includes('text/html')) {
                    entry.proxyDetected = true;
                    self._addEntry('NET:PROXY', `${method} ${url.slice(0, 80)} → 🛡️ PROXY HTML (${entry.contentType}) (${entry.duration}ms)`);
                    try {
                        const clone = resp.clone();
                        const text = await clone.text();
                        entry.responsePreview = text.slice(0, 500);
                    } catch (e) { /* */ }
                } else {
                    // Log non-ok responses at higher severity
                    const logType = resp.ok ? 'NET' : 'NET:ERR';
                    self._addEntry(logType, `${method} ${url.slice(0, 80)} → ${resp.status} (${entry.duration}ms)`);
                }

                // Clone and peek at error responses
                if (!resp.ok && !entry.proxyDetected) {
                    try {
                        const clone = resp.clone();
                        const text = await clone.text();
                        entry.responsePreview = text.slice(0, 500);
                    } catch (e) { /* can't peek */ }
                }

                return resp;
            } catch (err) {
                entry.duration = Math.round(performance.now() - start);
                entry.error = err.message || String(err);
                self._addEntry('NET:ERR', `${method} ${url.slice(0, 80)} → FAILED: ${entry.error} (${entry.duration}ms)`);
                throw err;
            }
        };
    },

    // ── Global Error Handlers ───────────────

    _interceptErrors() {
        window.addEventListener('error', (e) => {
            this._addEntry('EXCEPTION', `${e.message} at ${e.filename || '?'}:${e.lineno || 0}:${e.colno || 0}`);
            this._errorCount++;
            this._updateBadge();
        });

        window.addEventListener('unhandledrejection', (e) => {
            const msg = e.reason?.message || e.reason || 'Unknown rejection';
            this._addEntry('REJECTION', String(msg));
            this._errorCount++;
            this._updateBadge();
        });
    },

    // ── Entry Management ────────────────────

    _addEntry(type, message) {
        const entry = {
            ts: new Date().toISOString(),
            elapsed: Date.now() - this._initTime,
            type,
            message
        };
        this.entries.push(entry);
        if (this.entries.length > this.maxEntries) {
            this.entries.shift();
        }

        if (type === 'ERROR' || type === 'EXCEPTION' || type === 'REJECTION' || type === 'NET:ERR' || type === 'NET:PROXY') {
            this._errorCount++;
            this._updateBadge();
        }

        // Auto-refresh if modal is open
        if (document.getElementById('debugModal')?.classList.contains('active')) {
            this._scheduleRender();
        }
    },

    _renderTimer: null,
    _scheduleRender() {
        if (this._renderTimer) return;
        this._renderTimer = setTimeout(() => {
            this._renderTimer = null;
            this.render();
        }, 250);
    },

    log(type, message) {
        this._addEntry(type, message);
    },

    clear() {
        this.entries = [];
        this.networkLog = [];
        this._errorCount = 0;
        this._updateBadge();
    },

    // ── Serialization ───────────────────────

    _serialize(arg) {
        if (arg === null) return 'null';
        if (arg === undefined) return 'undefined';
        if (arg instanceof Error) {
            return `${arg.name}: ${arg.message}`;
        }
        if (typeof arg === 'object') {
            try { return JSON.stringify(arg, null, 0); }
            catch (e) { return String(arg); }
        }
        return String(arg);
    },

    // ── State Snapshot ──────────────────────

    getStateSnapshot() {
        const snap = {
            env: window.__ENV__ || 'unknown',
            version: window.__VERSION__ || 'unknown',
            basePath: window.__BASE__ || '(root)',
            url: location.href,
        };

        // Preact SDK state (v0.37.14: replaces old API/App globals)
        if (window.sw) {
            snap.sdk = {
                user: window.sw.auth?.user ? {
                    username: window.sw.auth.user.username,
                    role: window.sw.auth.user.role,
                    display_name: window.sw.auth.user.display_name
                } : null,
                isAuthed: !!window.sw.auth?.token,
                wsConnected: window.sw.events?.connected ?? false,
                theme: window.sw.theme?.resolved ?? 'unknown',
            };
        }

        // localStorage keys
        try {
            snap.storageKeys = Object.keys(localStorage).filter(k =>
                k.startsWith('chatSwitchboard') || k.startsWith('switchboard') || k.startsWith('sb_')
            );
        } catch (e) {
            snap.storageKeys = '(error reading)';
        }

        return snap;
    },

    // ── Connection Diagnostics ──────────────

    async runDiagnostics() {
        this.log('DIAG', '── Starting connection diagnostics ──');
        this.log('DIAG', `Environment: ${window.__ENV__ || 'unknown'} | Version: ${window.__VERSION__ || 'unknown'} | Base: ${window.__BASE__ || '(root)'}`);

        const baseUrl = window.location.origin + (window.__BASE__ || '');

        // Test 1: Basic fetch to same origin
        this.log('DIAG', `Test 1: Health check → ${baseUrl}/api/v1/health`);
        try {
            const start = performance.now();
            const resp = await this._origFetch(baseUrl + '/api/v1/health', {
                signal: AbortSignal.timeout(10000)
            });
            const dur = Math.round(performance.now() - start);
            const contentType = resp.headers.get('content-type') || '(none)';
            const text = await resp.text();
            this.log('DIAG', `  Status: ${resp.status} (${dur}ms)`);
            this.log('DIAG', `  Content-Type: ${contentType}`);
            this.log('DIAG', `  Body: ${text.slice(0, 300)}`);

            if (contentType.includes('text/html')) {
                this.log('DIAG', '  ⚠️ Got HTML instead of JSON — likely a PROXY interception page');
            }
            if (!resp.ok) {
                this.log('DIAG', `  ⚠️ Non-200 status: ${resp.status} ${resp.statusText}`);
            }
            try {
                const json = JSON.parse(text);
                this.log('DIAG', `  ✅ Valid JSON: database=${json.database}, registration=${json.registration_enabled}`);
            } catch (e) {
                this.log('DIAG', `  ❌ NOT valid JSON — proxy or wrong endpoint`);
            }
        } catch (err) {
            this.log('DIAG', `  ❌ Fetch failed: ${err.message}`);
            if (err.name === 'TimeoutError' || err.name === 'AbortError') {
                this.log('DIAG', '  ⚠️ Request timed out — proxy may be blocking');
            }
        }

        // Test 2: Check if we're getting CORS issues
        this.log('DIAG', 'Test 2: OPTIONS preflight check');
        try {
            const resp = await this._origFetch(baseUrl + '/api/v1/health', {
                method: 'OPTIONS',
                signal: AbortSignal.timeout(5000)
            });
            this.log('DIAG', `  Status: ${resp.status}`);
            this.log('DIAG', `  CORS headers: ${resp.headers.get('access-control-allow-origin') || '(none)'}`);
        } catch (err) {
            this.log('DIAG', `  ❌ OPTIONS failed: ${err.message}`);
        }

        // Test 3: Token validity via Preact SDK
        if (window.sw?.auth?.token) {
            this.log('DIAG', 'Test 3: Token validation');
            try {
                const resp = await this._origFetch(baseUrl + '/api/v1/profile', {
                    headers: {
                        'Authorization': `Bearer ${window.sw.auth.token}`,
                        'Content-Type': 'application/json'
                    },
                    signal: AbortSignal.timeout(5000)
                });
                this.log('DIAG', `  Profile endpoint: ${resp.status}`);
                if (resp.status === 401) {
                    this.log('DIAG', '  ⚠️ Token expired or invalid');
                }
            } catch (err) {
                this.log('DIAG', `  ❌ Token check failed: ${err.message}`);
            }
        } else {
            this.log('DIAG', 'Test 3: Skipped (no token)');
        }

        // Test 4: WebSocket connectivity via Preact SDK
        this.log('DIAG', `Test 4: SDK WebSocket = ${window.sw?.events?.connected ? 'connected' : 'disconnected'}`);

        // Test 5: Service Worker & Cache
        this.log('DIAG', 'Test 6: Service Worker cache');
        try {
            if ('serviceWorker' in navigator) {
                const reg = await navigator.serviceWorker.getRegistration();
                this.log('DIAG', `  SW registered: ${reg ? 'yes' : 'no'}${reg ? ` (scope: ${reg.scope})` : ''}`);
                if (reg?.active) {
                    this.log('DIAG', `  SW state: ${reg.active.state}`);
                }
            } else {
                this.log('DIAG', '  SW not supported');
            }
            const keys = await caches.keys();
            this.log('DIAG', `  Caches (${keys.length}): ${keys.join(', ') || '(none)'}`);
            for (const key of keys) {
                const cache = await caches.open(key);
                const entries = await cache.keys();
                this.log('DIAG', `    ${key}: ${entries.length} entries`);
            }
        } catch (e) {
            this.log('DIAG', `  Cache check error: ${e.message}`);
        }

        this.log('DIAG', '── Diagnostics complete ──');
        this.render();
    },

    // ── Badge ───────────────────────────────

    _injectBadge() {
        // Keyboard shortcut only — visual indicator is the avatar 🐛
        document.addEventListener('keydown', (e) => {
            if (e.ctrlKey && e.shiftKey && e.key === 'L') {
                e.preventDefault();
                openDebugModal();
            }
        });
    },

    _updateBadge() {
        // Show error count on avatar bug in sidebar
        const bug = document.querySelector('.avatar-bug');
        if (!bug) return;
        if (this._errorCount > 0) {
            bug.textContent = '🐛';
            bug.title = `${this._errorCount} error${this._errorCount > 1 ? 's' : ''} — click for Debug Log`;
            bug.classList.add('has-errors');
        } else {
            bug.textContent = '🐛';
            bug.title = 'Debug available';
            bug.classList.remove('has-errors');
        }
    },

    // ── Modal Rendering ─────────────────────

    render() {
        this._renderConsoleTab();
        this._renderNetworkTab();
        this._renderStateTab();
    },

    _renderConsoleTab() {
        const container = document.getElementById('debugConsoleContent');
        if (!container) return;

        const count = document.getElementById('debugConsoleCount');
        if (count) count.textContent = this.entries.length;

        if (this.entries.length === 0) {
            container.innerHTML = '<div class="debug-empty">No log entries yet</div>';
            return;
        }

        const typeColors = {
            'ERROR': '#e74c3c', 'EXCEPTION': '#e74c3c', 'REJECTION': '#e74c3c',
            'NET:ERR': '#e74c3c', 'NET:PROXY': '#ff6b35',
            'WARN': '#f39c12', 'WARNING': '#f39c12',
            'LOG': '#95a5a6', 'INFO': '#3498db', 'DEBUG': '#7f8c8d',
            'NET': '#2ecc71',
            'DIAG': '#9b59b6'
        };

        let html = '';
        const entries = document.getElementById('debugFilterErrors')?.checked
            ? this.entries.filter(e => ['ERROR', 'EXCEPTION', 'REJECTION', 'NET:ERR', 'NET:PROXY', 'WARN'].includes(e.type))
            : this.entries;

        for (const entry of entries) {
            const color = typeColors[entry.type] || '#95a5a6';
            const time = entry.ts.slice(11, 23);
            const elapsed = (entry.elapsed / 1000).toFixed(1);
            html += `<div class="debug-entry">`;
            html += `<span class="debug-time">${time} +${elapsed}s</span> `;
            html += `<span style="color:${color};font-weight:bold;">[${this._esc(entry.type)}]</span> `;
            html += `<span class="debug-msg">${this._esc(entry.message)}</span>`;
            html += `</div>`;
        }
        container.innerHTML = html;

        if (document.getElementById('debugAutoScroll')?.checked) {
            container.scrollTop = container.scrollHeight;
        }
    },

    _renderNetworkTab() {
        const container = document.getElementById('debugNetworkContent');
        if (!container) return;

        const count = document.getElementById('debugNetworkCount');
        if (count) count.textContent = this.networkLog.length;

        if (this.networkLog.length === 0) {
            container.innerHTML = '<div class="debug-empty">No network requests logged</div>';
            return;
        }

        let html = '';
        for (const req of [...this.networkLog].reverse()) {
            const isErr = req.error || (req.status && req.status >= 400) || req.proxyDetected;
            const statusColor = req.proxyDetected ? '#ff6b35' : (isErr ? '#e74c3c' : '#2ecc71');
            const statusText = req.proxyDetected
                ? `🛡️ PROXY (${req.status})`
                : req.error
                ? `❌ ${req.error}`
                : (req.status ? `${req.status} ${req.statusText}` : '⏳ pending');

            html += `<details class="debug-net-entry">`;
            html += `<summary>`;
            html += `<span class="debug-net-method">${req.method}</span> `;
            html += `<span class="debug-net-url">${this._esc(req.url)}</span> `;
            html += `<span style="color:${statusColor}">${statusText}</span>`;
            if (req.duration != null) html += ` <span class="debug-time">${req.duration}ms</span>`;
            html += `</summary>`;
            html += `<div class="debug-net-detail">`;
            if (req.contentType) {
                html += `<div><strong>Content-Type:</strong> <code>${this._esc(req.contentType)}</code></div>`;
            }
            if (req.requestBodyPreview) {
                html += `<div><strong>Request:</strong> <code>${this._esc(req.requestBodyPreview)}</code></div>`;
            }
            if (req.responsePreview) {
                html += `<div><strong>Response:</strong> <pre class="debug-pre">${this._esc(req.responsePreview)}</pre></div>`;
            }
            html += `<div class="debug-time">${req.ts}</div>`;
            html += `</div></details>`;
        }
        container.innerHTML = html;
    },

    _renderStateTab() {
        const container = document.getElementById('debugStateContent');
        if (!container) return;

        const snap = this.getStateSnapshot();
        container.innerHTML = `<pre class="debug-pre debug-state-pre">${this._esc(JSON.stringify(snap, null, 2))}</pre>`;
    },

    _esc(s) {
        if (!s) return '';
        return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
    },

    // ── Export ───────────────────────────────

    exportText() {
        let text = `=== Chat Switchboard Debug Export ===\n`;
        text += `Exported: ${new Date().toISOString()}\n`;
        text += `URL: ${location.href}\n`;
        text += `ENV: ${window.__ENV__ || 'unknown'} | VERSION: ${window.__VERSION__ || 'unknown'} | BASE: ${window.__BASE__ || '(root)'}\n`;
        text += `UA: ${navigator.userAgent}\n\n`;

        text += `--- STATE ---\n`;
        text += JSON.stringify(this.getStateSnapshot(), null, 2) + '\n\n';

        text += `--- CONSOLE LOG (${this.entries.length}) ---\n`;
        for (const e of this.entries) {
            text += `[${e.ts}] [${e.type}] ${e.message}\n`;
        }
        text += '\n';

        text += `--- NETWORK LOG (${this.networkLog.length}) ---\n`;
        for (const r of this.networkLog) {
            const proxy = r.proxyDetected ? ' [PROXY]' : '';
            text += `[${r.ts}] ${r.method} ${r.url} → ${r.status || 'FAILED'}${proxy} ${r.error || ''} (${r.duration || '?'}ms)`;
            if (r.contentType) text += ` [${r.contentType}]`;
            text += '\n';
            if (r.responsePreview) text += `  Response: ${r.responsePreview.slice(0, 200)}\n`;
        }

        return text;
    }
};

// ── Inlined from ui-primitives.js (Scorched Earth III, v0.37.13) ──

function _escHtml(s) {
    const d = document.createElement('div');
    d.textContent = s;
    return d.innerHTML;
}

function openModal(id) {
    const el = document.getElementById(id);
    if (!el) return;
    el.classList.add('active');
}

function closeModal(id) {
    const el = document.getElementById(id);
    if (el) el.classList.remove('active');
}

function showConfirm(message, opts = {}) {
    return new Promise(resolve => {
        const title  = opts.title  || 'Confirm';
        const okText = opts.ok     || 'Confirm';
        const caText = opts.cancel || 'Cancel';
        const danger = opts.danger !== false;
        const overlay = document.createElement('div');
        overlay.className = 'confirm-overlay';
        overlay.innerHTML = `
            <div class="confirm-dialog">
                <div class="confirm-header">${_escHtml(title)}</div>
                <div class="confirm-body">${_escHtml(message)}</div>
                <div class="confirm-footer">
                    <button class="btn-small" data-action="cancel">${_escHtml(caText)}</button>
                    <button class="btn-small ${danger ? 'btn-danger' : 'btn-primary'}" data-action="ok">${_escHtml(okText)}</button>
                </div>
            </div>`;
        function close(result) { overlay.remove(); document.removeEventListener('keydown', onKey); resolve(result); }
        function onKey(e) { if (e.key === 'Escape') close(false); if (e.key === 'Enter') close(true); }
        overlay.querySelector('[data-action="ok"]').addEventListener('click', () => close(true));
        overlay.querySelector('[data-action="cancel"]').addEventListener('click', () => close(false));
        overlay.addEventListener('click', (e) => { if (e.target === overlay) close(false); });
        document.addEventListener('keydown', onKey);
        document.body.appendChild(overlay);
        overlay.querySelector('[data-action="cancel"]').focus();
    });
}

// ── Modal Controls ──────────────────────────

function openDebugModal() {
    DebugLog.render();
    openModal('debugModal');
}

function closeDebugModal() {
    closeModal('debugModal');
}

function switchDebugTab(tab) {
    document.querySelectorAll('.debug-tab').forEach(t => t.classList.remove('active'));
    document.querySelectorAll('.debug-tab-content').forEach(c => c.style.display = 'none');
    document.querySelector(`.debug-tab[data-tab="${tab}"]`)?.classList.add('active');
    const panel = document.getElementById(`debug${tab.charAt(0).toUpperCase() + tab.slice(1)}Tab`);
    if (panel) panel.style.display = 'flex';
    DebugLog.render();
}

async function clearDebugLog() {
    if (await showConfirm('Clear all debug logs?')) {
        DebugLog.clear();
        DebugLog.render();
    }
}

function copyDebugLog() {
    const text = DebugLog.exportText();
    navigator.clipboard.writeText(text)
        .then(() => { DebugLog.log('DEBUG', '📋 Debug log copied to clipboard'); })
        .catch(() => { DebugLog.log('ERROR', 'Copy to clipboard failed'); });
}

function exportDebugLog() {
    const text = DebugLog.exportText();
    const blob = new Blob([text], { type: 'text/plain' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `switchboard-debug-${new Date().toISOString().replace(/[:.]/g, '-')}.txt`;
    a.click();
    URL.revokeObjectURL(url);
}

function runDebugDiagnostics() {
    switchDebugTab('console');
    DebugLog.runDiagnostics();
}

async function purgeCache() {
    if (!await showConfirm(
        'Purge all cached files and reload?\n\n' +
        'This clears the Service Worker cache and forces a fresh download of all assets. ' +
        'Useful when the UI feels stale after a deploy.',
        { danger: true }
    )) return;

    DebugLog.log('DIAG', '🧹 Purging Service Worker caches...');

    try {
        // Delete all SW caches
        const keys = await caches.keys();
        const deleted = await Promise.all(keys.map(k => caches.delete(k)));
        DebugLog.log('DIAG', `  Deleted ${deleted.filter(Boolean).length} cache(s): ${keys.join(', ') || '(none)'}`);

        // Unregister all service workers
        if ('serviceWorker' in navigator) {
            const registrations = await navigator.serviceWorker.getRegistrations();
            for (const reg of registrations) {
                await reg.unregister();
                DebugLog.log('DIAG', `  Unregistered SW: ${reg.scope}`);
            }
        }

        DebugLog.log('DIAG', '  ✅ Cache purged — reloading...');

        // Brief delay so the user sees the log, then hard reload
        setTimeout(() => {
            location.reload();
        }, 500);
    } catch (e) {
        DebugLog.log('DIAG', `  ❌ Purge failed: ${e.message}`);
        if (typeof showToast === 'function') showToast('Cache purge failed: ' + e.message, 'error');
    }
}

// Initialize ASAP — before app.js init() so we capture everything
DebugLog.init();

// ── Exports (v0.37.14: window.* replaces sb.register) ──
window.DebugLog           = DebugLog;
window.openDebugModal     = openDebugModal;
window.closeModal         = closeModal;
window.openModal          = openModal;
window.showConfirm        = showConfirm;
window.switchDebugTab     = switchDebugTab;
window.clearDebugLog      = clearDebugLog;
window.copyDebugLog       = copyDebugLog;
window.exportDebugLog     = exportDebugLog;
window.runDebugDiagnostics = runDebugDiagnostics;
window.purgeCache         = purgeCache;
