// static/shared/js/dev-toolbar.js
// noqa: SEC015 - dev-only toolbar, innerHTML used with trusted/constructed content only
// noqa: JS-001 - console references are intentional: this file intercepts and wraps console methods
// noqa: JS-005 - init guard not needed: runs inside DOMContentLoaded IIFE
/**
 * Dev-Mode Debug Toolbar
 *
 * Full-featured debug toolbar for multi-tenant app development.
 * Bottom-docked, resizable panel with 4 tabs:
 *   - Platform: context from all sources (migrated from platform-diag.js)
 *   - API Calls: live log of intercepted fetch requests
 *   - Request Info: current page metadata and globals
 *   - Console: captured console.log/warn/error/info
 *
 * Toggle: Ctrl+Alt+D
 * Only loads on localhost — auto-hidden in production.
 * Theme: Catppuccin Mocha
 */
(function () {
    'use strict';

    // ── Localhost guard ──
    var host = window.location.hostname;
    if (host !== 'localhost' && host !== '127.0.0.1') return;

    // ── Constants ──
    var STORAGE_HEIGHT_KEY = '_dev_toolbar_height';
    var STORAGE_TAB_KEY = '_dev_toolbar_tab';
    var DEFAULT_HEIGHT = 320;
    var MIN_HEIGHT = 150;
    var MAX_HEIGHT_RATIO = 0.8;
    var MAX_API_CALLS = 200;
    var MAX_CONSOLE_LOGS = 500;
    var TRUNCATE_LIMIT = 2048;

    // Catppuccin Mocha palette
    var C = {
        base: '#1e1e2e',
        surface0: '#313244',
        surface1: '#45475a',
        text: '#cdd6f4',
        subtext: '#a6adc8',
        blue: '#89b4fa',
        mauve: '#cba6f7',
        green: '#a6e3a1',
        red: '#f38ba8',
        peach: '#fab387',
        yellow: '#f9e2af',
        teal: '#94e2d5',
        sky: '#89dceb',
    };

    // ── State ──
    var apiCalls = [];
    var consoleLogs = [];
    var activeTab = localStorage.getItem(STORAGE_TAB_KEY) || 'platform';
    var panelHeight = parseInt(localStorage.getItem(STORAGE_HEIGHT_KEY), 10) || DEFAULT_HEIGHT;
    var toolbarEl = null;
    var contentEl = null;
    var isExpanded = false;
    var consoleFilter = 'all';
    var expandedApiRows = {};

    // ── Utilities ──
    function escapeHtml(str) {
        return String(str).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
    }

    function truncate(str, limit) {
        if (typeof str !== 'string') {
            try { str = JSON.stringify(str); } catch (e) { str = String(str); }
        }
        if (str.length > limit) return str.slice(0, limit) + '\u2026 [truncated]';
        return str;
    }

    function decodeJwtPayload(token) {
        try {
            var parts = token.split('.');
            if (parts.length !== 3) return null;
            var payload = atob(parts[1].replace(/-/g, '+').replace(/_/g, '/'));
            return JSON.parse(payload);
        } catch (e) {
            return null;
        }
    }

    function formatDuration(ms) {
        if (ms < 1000) return ms + 'ms';
        return (ms / 1000).toFixed(1) + 's';
    }

    function formatTime(ts) {
        var d = new Date(ts);
        return d.toLocaleTimeString(undefined, { hour12: false }) + '.' +
            String(d.getMilliseconds()).padStart(3, '0');
    }

    function highlightPlatform(value) {
        if (value === 'oms') return C.green;
        if (value === 'loyalty') return C.mauve;
        if (value === 'hosting') return C.sky;
        if (value === 'main') return C.peach;
        if (value === '(none)' || value === '(empty)' || value === '(undefined)' ||
            value === '(null — Source 3 fallback)') return C.red;
        return C.yellow;
    }

    function statusColor(status) {
        if (status === 'ERR') return C.red;
        if (status >= 200 && status < 300) return C.green;
        if (status >= 300 && status < 400) return C.blue;
        if (status >= 400 && status < 500) return C.yellow;
        if (status >= 500) return C.red;
        return C.subtext;
    }

    function levelColor(level) {
        if (level === 'error') return C.red;
        if (level === 'warn') return C.yellow;
        if (level === 'info') return C.blue;
        return C.subtext;
    }

    function levelBadge(level) {
        return '<span style="display:inline-block;padding:0 4px;border-radius:3px;font-size:9px;font-weight:bold;' +
            'background:' + levelColor(level) + ';color:' + C.base + '">' + level.toUpperCase() + '</span>';
    }

    // ── HTML Helpers ──
    function sectionHeader(title) {
        return '<div style="margin-top:10px;margin-bottom:4px;color:' + C.blue +
            ';font-weight:bold;font-size:11px;border-bottom:1px solid ' + C.surface0 +
            ';padding-bottom:2px">' + escapeHtml(title) + '</div>';
    }

    function row(label, value, highlightFn) {
        var color = highlightFn ? highlightFn(value) : C.text;
        return '<div class="_dev_row" style="display:flex;justify-content:space-between;padding:1px 0">' +
            '<span style="color:' + C.subtext + '">' + escapeHtml(label) + '</span>' +
            '<span style="color:' + color + ';font-weight:bold">' + escapeHtml(String(value)) + '</span></div>';
    }

    // ── Interceptors (installed immediately at parse time) ──

    // Fetch interceptor
    var _originalFetch = window.fetch;
    window.fetch = function (input, init) {
        init = init || {};
        var entry = {
            id: apiCalls.length,
            timestamp: Date.now(),
            method: (init.method || 'GET').toUpperCase(),
            url: typeof input === 'string' ? input : (input && input.url ? input.url : String(input)),
            requestBody: init.body ? truncate(init.body, TRUNCATE_LIMIT) : null,
            requestHeaders: null,
            status: null,
            duration: null,
            responseBody: null,
            error: null,
        };

        // Capture request headers
        if (init.headers) {
            try {
                if (init.headers instanceof Headers) {
                    var h = {};
                    init.headers.forEach(function (v, k) { h[k] = v; });
                    entry.requestHeaders = h;
                } else {
                    entry.requestHeaders = Object.assign({}, init.headers);
                }
            } catch (e) { /* ignore */ }
        }

        apiCalls.push(entry);
        if (apiCalls.length > MAX_API_CALLS) apiCalls.shift();

        var start = performance.now();
        return _originalFetch.call(this, input, init).then(function (response) {
            entry.status = response.status;
            entry.duration = Math.round(performance.now() - start);
            var clone = response.clone();
            clone.text().then(function (text) {
                entry.responseBody = truncate(text, TRUNCATE_LIMIT);
                refreshIfActive('api');
            }).catch(function () {});
            refreshIfActive('api');
            return response;
        }).catch(function (err) {
            entry.status = 'ERR';
            entry.duration = Math.round(performance.now() - start);
            entry.error = err.message;
            refreshIfActive('api');
            throw err;
        });
    };

    // Console interceptor
    var _origConsole = {
        log: console.log,
        info: console.info,
        warn: console.warn,
        error: console.error,
    };

    ['log', 'info', 'warn', 'error'].forEach(function (level) {
        console[level] = function () {
            var args = Array.prototype.slice.call(arguments);
            consoleLogs.push({
                timestamp: Date.now(),
                level: level,
                args: args.map(function (a) {
                    if (typeof a === 'object') {
                        try { return JSON.stringify(a, null, 2); } catch (e) { return String(a); }
                    }
                    return String(a);
                }),
            });
            if (consoleLogs.length > MAX_CONSOLE_LOGS) consoleLogs.shift();
            refreshIfActive('console');
            _origConsole[level].apply(console, args);
        };
    });

    // Capture uncaught errors and unhandled rejections
    window.addEventListener('error', function (e) {
        var msg = e.message || 'Unknown error';
        if (e.filename) msg += ' at ' + e.filename.split('/').pop() + ':' + e.lineno;
        consoleLogs.push({
            timestamp: Date.now(),
            level: 'error',
            args: [msg],
        });
        if (consoleLogs.length > MAX_CONSOLE_LOGS) consoleLogs.shift();
        refreshIfActive('console');
    });

    window.addEventListener('unhandledrejection', function (e) {
        var msg = e.reason ? (e.reason.message || String(e.reason)) : 'Unhandled promise rejection';
        consoleLogs.push({
            timestamp: Date.now(),
            level: 'error',
            args: [msg],
        });
        if (consoleLogs.length > MAX_CONSOLE_LOGS) consoleLogs.shift();
        refreshIfActive('console');
    });

    // ── Refresh helper ──
    function refreshIfActive(tab) {
        if (isExpanded && activeTab === tab && contentEl) {
            renderTab();
            // Also update tab badges
            updateBadges();
        }
    }

    // ── Data Collectors ──
    function gatherPlatformContext() {
        var storeToken = localStorage.getItem('store_token');
        var jwt = storeToken ? decodeJwtPayload(storeToken) : null;
        return {
            windowPlatformCode: window.STORE_PLATFORM_CODE,
            windowStoreCode: window.STORE_CODE,
            lsPlatform: localStorage.getItem('store_platform') || '',
            lsStoreCode: localStorage.getItem('storeCode') || '',
            lsToken: storeToken,
            jwt: jwt,
            pathname: window.location.pathname,
            host: window.location.host,
            loginWouldSend: window.STORE_PLATFORM_CODE || localStorage.getItem('store_platform') || null,
        };
    }

    function fetchAuthMe() {
        var token = localStorage.getItem('store_token');
        if (!token) return Promise.resolve({ error: 'No store_token in localStorage' });
        return _originalFetch.call(window, '/api/v1/store/auth/me', {
            headers: { 'Authorization': 'Bearer ' + token },
        }).then(function (resp) {
            if (!resp.ok) return { error: resp.status + ' ' + resp.statusText };
            return resp.json();
        }).catch(function (e) {
            return { error: e.message };
        });
    }

    function gatherRequestInfo() {
        return {
            url: window.location.href,
            pathname: window.location.pathname,
            host: window.location.host,
            protocol: window.location.protocol,
            storeCode: window.STORE_CODE || '(undefined)',
            storeConfig: window.STORE_CONFIG || null,
            userPermissions: window.USER_PERMISSIONS || null,
            storePlatformCode: window.STORE_PLATFORM_CODE || '(undefined)',
            lsPlatform: localStorage.getItem('store_platform') || '(empty)',
            logConfig: window.LogConfig || null,
            detectedFrontend: detectFrontend(),
            environment: 'localhost',
            tokensPresent: {
                store_token: !!localStorage.getItem('store_token'),
                admin_token: !!localStorage.getItem('admin_token'),
                merchant_token: !!localStorage.getItem('merchant_token'),
                customer_token: !!localStorage.getItem('customer_token'),
            },
        };
    }

    function detectFrontend() {
        // Prefer server-injected value (set in base templates)
        if (window.FRONTEND_TYPE) return window.FRONTEND_TYPE;

        // Fallback for pages without base template (e.g., API docs)
        var path = window.location.pathname;
        if (path.startsWith('/api/')) return 'api';
        return 'unknown';
    }

    // ── UI Builder ──
    function createToolbar() {
        // Collapse bar (always visible at bottom)
        var collapseBar = document.createElement('div');
        collapseBar.id = '_dev_toolbar_collapse';
        Object.assign(collapseBar.style, {
            position: 'fixed', bottom: '0', left: '0', right: '0', zIndex: '99999',
            height: '4px', background: C.mauve, cursor: 'pointer', transition: 'height 0.15s',
        });
        collapseBar.title = 'Dev Toolbar (Ctrl+Alt+D)';
        collapseBar.addEventListener('mouseenter', function () { collapseBar.style.height = '8px'; });
        collapseBar.addEventListener('mouseleave', function () {
            if (!isExpanded) collapseBar.style.height = '4px';
        });
        collapseBar.addEventListener('click', toggleToolbar);
        document.body.appendChild(collapseBar);

        // Main toolbar panel
        toolbarEl = document.createElement('div');
        toolbarEl.id = '_dev_toolbar';
        Object.assign(toolbarEl.style, {
            position: 'fixed', bottom: '0', left: '0', right: '0', zIndex: '99998',
            height: panelHeight + 'px', background: C.base, color: C.text,
            fontFamily: "'SF Mono', 'Fira Code', 'Cascadia Code', Consolas, monospace",
            fontSize: '11px', lineHeight: '1.5',
            borderTop: '2px solid ' + C.mauve, display: 'none',
            flexDirection: 'column',
        });

        // Drag handle
        var dragHandle = document.createElement('div');
        Object.assign(dragHandle.style, {
            height: '6px', cursor: 'ns-resize', background: C.surface0,
            display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: '0',
        });
        dragHandle.innerHTML = '<div style="width:40px;height:2px;background:' + C.surface1 + // noqa: SEC015
            ';border-radius:1px"></div>';
        setupDragResize(dragHandle);
        toolbarEl.appendChild(dragHandle);

        // Tab bar
        var tabBar = document.createElement('div');
        tabBar.id = '_dev_toolbar_tabs';
        Object.assign(tabBar.style, {
            display: 'flex', alignItems: 'center', background: C.surface0,
            borderBottom: '1px solid ' + C.surface1, flexShrink: '0', padding: '0 8px',
        });

        var tabs = [
            { id: 'platform', label: 'Platform' },
            { id: 'auth', label: 'Auth' },
            { id: 'api', label: 'API' },
            { id: 'request', label: 'Request' },
            { id: 'console', label: 'Console' },
        ];

        tabs.forEach(function (tab) {
            var btn = document.createElement('button');
            btn.id = '_dev_tab_' + tab.id;
            btn.dataset.tab = tab.id;
            Object.assign(btn.style, {
                padding: '4px 12px', border: 'none', cursor: 'pointer',
                fontSize: '11px', fontFamily: 'inherit', background: 'transparent',
                color: C.subtext, borderBottom: '2px solid transparent', transition: 'all 0.15s',
            });
            btn.innerHTML = tab.label + '<span id="_dev_badge_' + tab.id + '" style="margin-left:4px;font-size:9px;color:' + C.subtext + '"></span>'; // noqa: SEC015
            btn.addEventListener('click', function () { switchTab(tab.id); });
            tabBar.appendChild(btn);
        });

        // Close button (right-aligned)
        var spacer = document.createElement('div');
        spacer.style.flex = '1';
        tabBar.appendChild(spacer);

        var copyBtn = document.createElement('button');
        copyBtn.id = '_dev_copy_btn';
        Object.assign(copyBtn.style, {
            padding: '2px 8px', border: 'none', cursor: 'pointer', fontSize: '11px',
            fontFamily: 'inherit', background: C.teal, color: C.base,
            borderRadius: '3px', fontWeight: 'bold', margin: '2px 4px 2px 0',
        });
        copyBtn.textContent = '\u2398 Copy';
        copyBtn.addEventListener('click', copyTabContent);
        tabBar.appendChild(copyBtn);

        var closeBtn = document.createElement('button');
        Object.assign(closeBtn.style, {
            padding: '2px 8px', border: 'none', cursor: 'pointer', fontSize: '11px',
            fontFamily: 'inherit', background: C.red, color: C.base,
            borderRadius: '3px', fontWeight: 'bold', margin: '2px 0',
        });
        closeBtn.textContent = '\u2715 Close';
        closeBtn.addEventListener('click', toggleToolbar);
        tabBar.appendChild(closeBtn);

        toolbarEl.appendChild(tabBar);

        // Content area
        contentEl = document.createElement('div');
        contentEl.id = '_dev_toolbar_content';
        Object.assign(contentEl.style, {
            flex: '1', overflowY: 'auto', padding: '8px 12px',
        });
        toolbarEl.appendChild(contentEl);

        document.body.appendChild(toolbarEl);

        // Inject hover style for rows and content lines
        var style = document.createElement('style');
        style.textContent = '._dev_row:hover, #_dev_toolbar_content > div:hover { background: ' + C.surface1 + ' }';
        document.head.appendChild(style);

        updateTabStyles();
        updateBadges();
    }

    function setupDragResize(handle) {
        var startY, startHeight;
        handle.addEventListener('mousedown', function (e) {
            e.preventDefault();
            startY = e.clientY;
            startHeight = panelHeight;
            document.addEventListener('mousemove', onDrag);
            document.addEventListener('mouseup', onDragEnd);
            document.body.style.userSelect = 'none';
        });

        function onDrag(e) {
            var delta = startY - e.clientY;
            var newHeight = Math.max(MIN_HEIGHT, Math.min(window.innerHeight * MAX_HEIGHT_RATIO, startHeight + delta));
            panelHeight = Math.round(newHeight);
            if (toolbarEl) toolbarEl.style.height = panelHeight + 'px';
        }

        function onDragEnd() {
            document.removeEventListener('mousemove', onDrag);
            document.removeEventListener('mouseup', onDragEnd);
            document.body.style.userSelect = '';
            localStorage.setItem(STORAGE_HEIGHT_KEY, String(panelHeight));
        }
    }

    function toggleToolbar() {
        if (!toolbarEl) createToolbar();
        isExpanded = !isExpanded;
        toolbarEl.style.display = isExpanded ? 'flex' : 'none';
        if (isExpanded) {
            renderTab();
            updateBadges();
        }
    }

    function copyTabContent() {
        if (!contentEl) return;
        var text = contentEl.innerText || contentEl.textContent || '';
        navigator.clipboard.writeText(text).then(function () {
            var btn = document.getElementById('_dev_copy_btn');
            if (btn) {
                var orig = btn.textContent;
                btn.textContent = '\u2714 Copied!';
                btn.style.background = C.green;
                setTimeout(function () {
                    btn.textContent = orig;
                    btn.style.background = C.teal;
                }, 1500);
            }
        }).catch(function () {
            // Fallback for older browsers
            var ta = document.createElement('textarea');
            ta.value = text;
            ta.style.position = 'fixed';
            ta.style.opacity = '0';
            document.body.appendChild(ta);
            ta.select();
            document.execCommand('copy');
            document.body.removeChild(ta);
            var btn = document.getElementById('_dev_copy_btn');
            if (btn) {
                var orig = btn.textContent;
                btn.textContent = '\u2714 Copied!';
                btn.style.background = C.green;
                setTimeout(function () {
                    btn.textContent = orig;
                    btn.style.background = C.teal;
                }, 1500);
            }
        });
    }

    function switchTab(tabId) {
        activeTab = tabId;
        localStorage.setItem(STORAGE_TAB_KEY, tabId);
        updateTabStyles();
        renderTab();
    }

    function updateTabStyles() {
        var tabs = ['platform', 'auth', 'api', 'request', 'console'];
        tabs.forEach(function (id) {
            var btn = document.getElementById('_dev_tab_' + id);
            if (!btn) return;
            if (id === activeTab) {
                btn.style.color = C.mauve;
                btn.style.borderBottomColor = C.mauve;
                btn.style.background = C.base;
            } else {
                btn.style.color = C.subtext;
                btn.style.borderBottomColor = 'transparent';
                btn.style.background = 'transparent';
            }
        });
    }

    function updateBadges() {
        var apiBadge = document.getElementById('_dev_badge_api');
        if (apiBadge) {
            apiBadge.textContent = apiCalls.length > 0 ? '(' + apiCalls.length + ')' : '';
        }
        var consoleBadge = document.getElementById('_dev_badge_console');
        if (consoleBadge) {
            var errCount = consoleLogs.filter(function (l) { return l.level === 'error'; }).length;
            var warnCount = consoleLogs.filter(function (l) { return l.level === 'warn'; }).length;
            var parts = [];
            if (errCount > 0) parts.push('<span style="color:' + C.red + '">' + errCount + 'E</span>');
            if (warnCount > 0) parts.push('<span style="color:' + C.yellow + '">' + warnCount + 'W</span>');
            if (parts.length === 0 && consoleLogs.length > 0) parts.push(consoleLogs.length.toString());
            consoleBadge.innerHTML = parts.length > 0 ? '(' + parts.join('/') + ')' : ''; // noqa: SEC015
        }
    }

    // ── Auth Tab Helpers ──
    var TOKEN_MAP = {
        admin:      { key: 'admin_token',    endpoint: '/api/v1/admin/auth/me' },
        store:      { key: 'store_token',    endpoint: '/api/v1/store/auth/me' },
        merchant:   { key: 'merchant_token', endpoint: '/api/v1/merchants/auth/me' },
        storefront: { key: 'customer_token', endpoint: '/api/v1/storefront/profile' },
    };

    function getTokenForFrontend(frontend) {
        var info = TOKEN_MAP[frontend];
        return info ? localStorage.getItem(info.key) : null;
    }

    function getAuthMeEndpoint(frontend) {
        var info = TOKEN_MAP[frontend];
        return info ? info.endpoint : null;
    }

    function fetchFrontendAuthMe(frontend) {
        var token = getTokenForFrontend(frontend);
        var endpoint = getAuthMeEndpoint(frontend);
        if (!token || !endpoint) return Promise.resolve({ error: 'No token or endpoint for ' + frontend });
        return _originalFetch.call(window, endpoint, {
            headers: { 'Authorization': 'Bearer ' + token },
        }).then(function (resp) {
            if (!resp.ok) return { error: resp.status + ' ' + resp.statusText };
            return resp.json();
        }).catch(function (e) {
            return { error: e.message };
        });
    }

    function renderAuthTab() {
        var frontend = detectFrontend();
        var token = getTokenForFrontend(frontend);
        var jwt = token ? decodeJwtPayload(token) : null;
        var html = '';

        // 1. Detected Frontend badge
        var frontendColors = { admin: C.red, store: C.green, merchant: C.peach, storefront: C.blue };
        var badgeColor = frontendColors[frontend] || C.subtext;
        html += sectionHeader('Detected Frontend');
        html += '<div style="margin:2px 0"><span style="display:inline-block;padding:2px 10px;border-radius:3px;' +
            'font-weight:bold;font-size:11px;background:' + badgeColor + ';color:' + C.base + '">' +
            escapeHtml(frontend) + '</span></div>';

        // 2. Active Token
        var tokenInfo = TOKEN_MAP[frontend];
        html += sectionHeader('Active Token (' + (tokenInfo ? tokenInfo.key : 'n/a') + ')');
        if (token) {
            html += row('Status', 'present', function () { return C.green; });
            html += row('Last 20 chars', '...' + token.slice(-20));
        } else {
            html += row('Status', 'absent', function () { return C.red; });
        }

        // 3. JWT Decoded
        html += sectionHeader('JWT Decoded');
        if (jwt) {
            Object.keys(jwt).forEach(function (key) {
                var val = jwt[key];
                if (key === 'exp' || key === 'iat') {
                    val = new Date(val * 1000).toLocaleString() + ' (' + val + ')';
                }
                html += row(key, val ?? '(null)');
            });
        } else {
            html += '<div style="color:' + C.subtext + '">No JWT to decode</div>';
        }

        // 4. Token Expiry countdown
        html += sectionHeader('Token Expiry');
        if (jwt && jwt.exp) {
            var now = Math.floor(Date.now() / 1000);
            var remaining = jwt.exp - now;
            var expiryColor;
            var expiryText;
            if (remaining <= 0) {
                expiryColor = C.red;
                expiryText = 'EXPIRED (' + Math.abs(remaining) + 's ago)';
            } else if (remaining < 300) {
                expiryColor = C.yellow;
                var m = Math.floor(remaining / 60);
                var s = remaining % 60;
                expiryText = m + 'm ' + s + 's remaining';
            } else {
                expiryColor = C.green;
                var m2 = Math.floor(remaining / 60);
                var s2 = remaining % 60;
                expiryText = m2 + 'm ' + s2 + 's remaining';
            }
            html += row('Expires', expiryText, function () { return expiryColor; });
        } else {
            html += '<div style="color:' + C.subtext + '">No exp claim</div>';
        }

        // 5. All Tokens Overview
        html += sectionHeader('All Tokens Overview');
        Object.keys(TOKEN_MAP).forEach(function (fe) {
            var present = !!localStorage.getItem(TOKEN_MAP[fe].key);
            var color = present ? C.green : C.red;
            var label = present ? 'present' : 'absent';
            html += row(TOKEN_MAP[fe].key, label, function () { return color; });
        });

        // 6. Server Auth (/auth/me) — placeholder, filled async
        html += '<div id="_dev_auth_me_section"></div>';

        contentEl.innerHTML = html; // noqa: SEC015 // noqa: SEC015

        // Async fetch
        if (token && getAuthMeEndpoint(frontend)) {
            var meSection = document.getElementById('_dev_auth_me_section');
            if (meSection) meSection.innerHTML = sectionHeader('Server Auth (' + getAuthMeEndpoint(frontend) + ')') + // noqa: SEC015
                '<div style="color:' + C.subtext + '">Loading...</div>';

            fetchFrontendAuthMe(frontend).then(function (me) {
                if (activeTab !== 'auth') return;
                var meHtml = sectionHeader('Server Auth (' + getAuthMeEndpoint(frontend) + ')');
                if (me.error) {
                    meHtml += '<div style="color:' + C.red + '">' + escapeHtml(me.error) + '</div>';
                } else {
                    Object.keys(me).forEach(function (key) {
                        meHtml += row(key, me[key] ?? '(null)');
                    });

                    // 7. Consistency Check
                    if (jwt) {
                        meHtml += sectionHeader('Consistency Check (JWT vs /auth/me)');
                        var mismatches = [];
                        var checkFields = ['platform_code', 'store_code', 'username', 'user_id', 'role'];
                        checkFields.forEach(function (field) {
                            if (jwt[field] !== undefined && me[field] !== undefined &&
                                String(jwt[field]) !== String(me[field])) {
                                mismatches.push(field + ': JWT=' + jwt[field] + ' vs server=' + me[field]);
                            }
                        });
                        if (mismatches.length > 0) {
                            mismatches.forEach(function (msg) {
                                meHtml += '<div style="color:' + C.red + ';font-weight:bold">MISMATCH: ' + escapeHtml(msg) + '</div>';
                            });
                        } else {
                            meHtml += '<div style="color:' + C.green + '">All checked fields consistent</div>';
                        }
                    }
                }
                var section = document.getElementById('_dev_auth_me_section');
                if (section) section.innerHTML = meHtml; // noqa: SEC015
            });
        }
    }

    // ── Tab Renderers ──
    function renderTab() {
        if (!contentEl) return;
        switch (activeTab) {
            case 'platform': renderPlatformTab(); break;
            case 'auth': renderAuthTab(); break;
            case 'api': renderApiCallsTab(); break;
            case 'request': renderRequestInfoTab(); break;
            case 'console': renderConsoleTab(); break;
        }
    }

    function renderPlatformTab() {
        var ctx = gatherPlatformContext();
        var jwt = ctx.jwt;
        var html = '';

        html += sectionHeader('Client State');
        html += row('window.STORE_PLATFORM_CODE', ctx.windowPlatformCode ?? '(undefined)', highlightPlatform);
        html += row('window.STORE_CODE', ctx.windowStoreCode ?? '(undefined)');
        html += row('localStorage.store_platform', ctx.lsPlatform || '(empty)', highlightPlatform);
        html += row('localStorage.storeCode', ctx.lsStoreCode || '(empty)');
        html += row('localStorage.store_token', ctx.lsToken ? '...' + ctx.lsToken.slice(-20) : '(empty)');

        html += sectionHeader('JWT Token (decoded)');
        html += row('platform_code', jwt?.platform_code ?? '(none)', highlightPlatform);
        html += row('platform_id', jwt?.platform_id ?? '(none)');
        html += row('store_code', jwt?.store_code ?? '(none)');
        html += row('store_role', jwt?.store_role ?? '(none)');
        html += row('username', jwt?.username ?? '(none)');
        html += row('expires', jwt?.exp ? new Date(jwt.exp * 1000).toLocaleTimeString() : '(none)');

        html += sectionHeader('Login Would Send');
        var loginVal = ctx.loginWouldSend || '(null \u2014 Source 3 fallback)';
        html += row('platform_code', loginVal, highlightPlatform);
        html += '<div style="color:' + C.subtext + ';margin-top:2px;font-size:10px">' +
            'STORE_PLATFORM_CODE || localStorage.store_platform || null</div>';

        html += sectionHeader('URL');
        html += row('host', ctx.host);
        html += row('pathname', ctx.pathname);

        // Consistency check
        html += sectionHeader('Consistency Check');
        var jwtPlatform = jwt?.platform_code ?? '(none)';
        var lsPlatform = ctx.lsPlatform || '(empty)';
        var windowPlatform = ctx.windowPlatformCode;

        if (jwtPlatform !== '(none)' && lsPlatform !== '(empty)' && jwtPlatform !== lsPlatform) {
            html += '<div style="color:' + C.red + ';font-weight:bold">MISMATCH: JWT platform_code=' +
                escapeHtml(jwtPlatform) + ' but localStorage.store_platform=' + escapeHtml(lsPlatform) + '</div>';
        } else if (jwtPlatform !== '(none)' && windowPlatform !== undefined && windowPlatform !== null &&
                   String(windowPlatform) !== '(undefined)' && jwtPlatform !== windowPlatform) {
            html += '<div style="color:' + C.peach + ';font-weight:bold">WARNING: JWT platform_code=' +
                escapeHtml(jwtPlatform) + ' but window.STORE_PLATFORM_CODE=' + escapeHtml(String(windowPlatform)) + '</div>';
        } else {
            html += '<div style="color:' + C.green + '">All sources consistent</div>';
        }

        contentEl.innerHTML = html; // noqa: SEC015

        // Async /auth/me
        fetchAuthMe().then(function (me) {
            var meHtml = sectionHeader('/auth/me (server)');
            if (me.error) {
                meHtml += '<div style="color:' + C.red + '">' + escapeHtml(me.error) + '</div>';
            } else {
                meHtml += row('platform_code', me.platform_code ?? '(null)', highlightPlatform);
                meHtml += row('username', me.username || '(unknown)');
                meHtml += row('role', me.role || '(unknown)');
                if (me.platform_code && jwtPlatform !== '(none)' && me.platform_code !== jwtPlatform) {
                    meHtml += '<div style="color:' + C.red + ';font-weight:bold">MISMATCH: /me platform_code=' +
                        escapeHtml(me.platform_code) + ' but JWT=' + escapeHtml(jwtPlatform) + '</div>';
                }
            }
            if (contentEl && activeTab === 'platform') {
                contentEl.innerHTML += meHtml;
            }
        });
    }

    function renderApiCallsTab() {
        var html = '';

        // Toolbar row
        html += '<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:6px">';
        html += '<span style="color:' + C.subtext + '">' + apiCalls.length + ' requests captured</span>';
        html += '<button id="_dev_api_clear" style="padding:2px 8px;font-size:10px;border-radius:3px;' +
            'background:' + C.surface1 + ';color:' + C.text + ';border:none;cursor:pointer;font-family:inherit">Clear</button>';
        html += '</div>';

        if (apiCalls.length === 0) {
            html += '<div style="color:' + C.subtext + ';text-align:center;padding:20px">No API calls captured yet.</div>';
        } else {
            // Table header
            html += '<div style="display:flex;padding:2px 4px;color:' + C.subtext + ';font-size:10px;font-weight:bold;' +
                'border-bottom:1px solid ' + C.surface1 + '">';
            html += '<span style="width:50px">Method</span>';
            html += '<span style="flex:1;padding:0 4px">URL</span>';
            html += '<span style="width:50px;text-align:right">Status</span>';
            html += '<span style="width:60px;text-align:right">Time</span>';
            html += '<span style="width:70px;text-align:right">When</span>';
            html += '</div>';

            // Rows (newest first)
            for (var i = apiCalls.length - 1; i >= 0; i--) {
                var call = apiCalls[i];
                var isExpanded_ = expandedApiRows[call.id];
                var methodColor = call.method === 'GET' ? C.green : call.method === 'POST' ? C.blue :
                    call.method === 'PUT' ? C.peach : call.method === 'DELETE' ? C.red : C.text;

                html += '<div class="_dev_row" style="border-bottom:1px solid ' + C.surface0 + ';cursor:pointer" data-api-row="' + call.id + '">';
                html += '<div style="display:flex;padding:2px 4px;align-items:center" data-api-toggle="' + call.id + '">';
                html += '<span style="width:50px;color:' + methodColor + ';font-weight:bold;font-size:10px">' + call.method + '</span>';
                html += '<span style="flex:1;padding:0 4px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">' + escapeHtml(call.url) + '</span>';
                html += '<span style="width:50px;text-align:right;color:' + statusColor(call.status) + ';font-weight:bold">' +
                    (call.status !== null ? call.status : '\u2022\u2022\u2022') + '</span>';
                html += '<span style="width:60px;text-align:right">' +
                    (call.duration !== null ? formatDuration(call.duration) : '\u2022\u2022\u2022') + '</span>';
                html += '<span style="width:70px;text-align:right;color:' + C.subtext + ';font-size:10px">' + formatTime(call.timestamp) + '</span>';
                html += '</div>';

                // Expanded detail
                if (isExpanded_) {
                    html += '<div style="padding:4px 8px 8px;background:' + C.surface0 + ';font-size:10px">';
                    if (call.requestHeaders) {
                        html += '<div style="color:' + C.blue + ';font-weight:bold;margin-top:4px">Request Headers</div>';
                        html += '<pre style="margin:2px 0;white-space:pre-wrap;color:' + C.text + '">' +
                            escapeHtml(JSON.stringify(call.requestHeaders, null, 2)) + '</pre>';
                    }
                    if (call.requestBody) {
                        html += '<div style="color:' + C.blue + ';font-weight:bold;margin-top:4px">Request Body</div>';
                        html += '<pre style="margin:2px 0;white-space:pre-wrap;color:' + C.text + '">' + escapeHtml(formatJsonSafe(call.requestBody)) + '</pre>';
                    }
                    if (call.responseBody) {
                        html += '<div style="color:' + C.green + ';font-weight:bold;margin-top:4px">Response Body</div>';
                        html += '<pre style="margin:2px 0;white-space:pre-wrap;color:' + C.text + ';max-height:200px;overflow-y:auto">' +
                            escapeHtml(formatJsonSafe(call.responseBody)) + '</pre>';
                    }
                    if (call.error) {
                        html += '<div style="color:' + C.red + ';font-weight:bold;margin-top:4px">Error: ' + escapeHtml(call.error) + '</div>';
                    }
                    html += '</div>';
                }
                html += '</div>';
            }
        }

        contentEl.innerHTML = html; // noqa: SEC015

        // Attach event listeners
        var clearBtn = document.getElementById('_dev_api_clear');
        if (clearBtn) {
            clearBtn.addEventListener('click', function () {
                apiCalls.length = 0;
                expandedApiRows = {};
                renderApiCallsTab();
                updateBadges();
            });
        }

        contentEl.querySelectorAll('[data-api-toggle]').forEach(function (el) {
            el.addEventListener('click', function () {
                var id = parseInt(el.dataset.apiToggle, 10);
                expandedApiRows[id] = !expandedApiRows[id];
                renderApiCallsTab();
            });
        });
    }

    function formatJsonSafe(str) {
        try {
            var obj = JSON.parse(str);
            return JSON.stringify(obj, null, 2);
        } catch (e) {
            return str;
        }
    }

    function renderRequestInfoTab() {
        var info = gatherRequestInfo();
        var html = '';

        html += sectionHeader('Page');
        html += row('URL', info.url);
        html += row('pathname', info.pathname);
        html += row('host', info.host);
        html += row('protocol', info.protocol);
        html += row('detected frontend', info.detectedFrontend);
        html += row('environment', info.environment);

        html += sectionHeader('Platform Context');
        html += row('STORE_CODE', info.storeCode);
        html += row('STORE_PLATFORM_CODE', info.storePlatformCode, highlightPlatform);
        html += row('localStorage.store_platform', info.lsPlatform, highlightPlatform);

        html += sectionHeader('Store Config');
        if (info.storeConfig) {
            Object.keys(info.storeConfig).forEach(function (key) {
                html += row(key, info.storeConfig[key] ?? '(null)');
            });
        } else {
            html += '<div style="color:' + C.subtext + '">STORE_CONFIG not defined</div>';
        }

        html += sectionHeader('User Permissions');
        if (info.userPermissions && Array.isArray(info.userPermissions)) {
            if (info.userPermissions.length === 0) {
                html += '<div style="color:' + C.subtext + '">(empty array)</div>';
            } else {
                info.userPermissions.forEach(function (perm) {
                    html += '<div style="padding:1px 0;color:' + C.green + '">\u2022 ' + escapeHtml(String(perm)) + '</div>';
                });
            }
        } else {
            html += '<div style="color:' + C.subtext + '">USER_PERMISSIONS not defined</div>';
        }

        html += sectionHeader('Tokens');
        html += row('store_token', info.tokensPresent.store_token ? 'present' : 'absent');
        html += row('admin_token', info.tokensPresent.admin_token ? 'present' : 'absent');
        html += row('merchant_token', info.tokensPresent.merchant_token ? 'present' : 'absent');
        html += row('customer_token', info.tokensPresent.customer_token ? 'present' : 'absent');

        html += sectionHeader('Log Config');
        if (info.logConfig) {
            Object.keys(info.logConfig).forEach(function (key) {
                var val = info.logConfig[key];
                if (typeof val !== 'function') {
                    html += row(key, String(val));
                }
            });
        } else {
            html += '<div style="color:' + C.subtext + '">LogConfig not defined</div>';
        }

        contentEl.innerHTML = html; // noqa: SEC015
    }

    function renderConsoleTab() {
        var html = '';

        // Filter bar
        html += '<div style="display:flex;gap:6px;align-items:center;margin-bottom:6px">';
        var filters = ['all', 'log', 'info', 'warn', 'error'];
        filters.forEach(function (f) {
            var isActive = consoleFilter === f;
            var bg = isActive ? C.mauve : C.surface1;
            var fg = isActive ? C.base : C.text;
            html += '<button class="_dev_console_filter" data-filter="' + f + '" style="padding:2px 8px;font-size:10px;' +
                'border-radius:3px;background:' + bg + ';color:' + fg + ';border:none;cursor:pointer;font-family:inherit;font-weight:bold">' +
                f.toUpperCase() + '</button>';
        });
        html += '<div style="flex:1"></div>';
        html += '<button id="_dev_console_clear" style="padding:2px 8px;font-size:10px;border-radius:3px;' +
            'background:' + C.surface1 + ';color:' + C.text + ';border:none;cursor:pointer;font-family:inherit">Clear</button>';
        html += '</div>';

        // Filtered logs
        var filtered = consoleLogs;
        if (consoleFilter !== 'all') {
            filtered = consoleLogs.filter(function (l) { return l.level === consoleFilter; });
        }

        if (filtered.length === 0) {
            html += '<div style="color:' + C.subtext + ';text-align:center;padding:20px">No console output captured.</div>';
        } else {
            for (var i = filtered.length - 1; i >= 0; i--) {
                var entry = filtered[i];
                html += '<div class="_dev_row" style="display:flex;gap:6px;padding:2px 0;border-bottom:1px solid ' + C.surface0 + ';align-items:flex-start">';
                html += '<span style="flex-shrink:0;color:' + C.subtext + ';font-size:10px;width:75px">' + formatTime(entry.timestamp) + '</span>';
                html += '<span style="flex-shrink:0">' + levelBadge(entry.level) + '</span>';
                html += '<span style="flex:1;white-space:pre-wrap;word-break:break-all;color:' + levelColor(entry.level) + '">';
                html += escapeHtml(entry.args.join(' '));
                html += '</span></div>';
            }
        }

        contentEl.innerHTML = html; // noqa: SEC015

        // Attach filter listeners
        contentEl.querySelectorAll('._dev_console_filter').forEach(function (btn) {
            btn.addEventListener('click', function () {
                consoleFilter = btn.dataset.filter;
                renderConsoleTab();
            });
        });

        var clearBtn = document.getElementById('_dev_console_clear');
        if (clearBtn) {
            clearBtn.addEventListener('click', function () {
                consoleLogs.length = 0;
                renderConsoleTab();
                updateBadges();
            });
        }
    }

    // ── Keyboard shortcut: Ctrl+Shift+D ──
    document.addEventListener('keydown', function (e) {
        if (e.ctrlKey && e.altKey && (e.key === 'D' || e.key === 'd')) {
            e.preventDefault();
            toggleToolbar();
        }
    });

    // ── Init on DOMContentLoaded ──
    function init() {
        createToolbar();
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();
