// ═══════════════════════════════════════════════════════
//  CVC Dashboard — Main Application
//  (Compiled from ts/app.ts — edit TypeScript source)
// ═══════════════════════════════════════════════════════
"use strict";

/** @type {DashboardWS} */
let ws;
/** @type {string} */
let currentTab = 'chat';
/** @type {ReturnType<typeof setInterval>|null} */
let pollTimer = null;
/** @type {'dark'|'light'} */
let activeTheme = 'dark';
const THEME_STORAGE_KEY = 'cvc-dashboard-theme';

// ── Toast system ────────────────────────────────────
function toast(message, type = 'info') {
  let container = document.querySelector('.toast-container');
  if (!container) {
    container = document.createElement('div');
    container.className = 'toast-container';
    document.body.appendChild(container);
  }
  const el = document.createElement('div');
  el.className = `toast ${type}`;
  el.textContent = message;
  container.appendChild(el);
  setTimeout(() => el.remove(), 4000);
}

// ── HTML escape ─────────────────────────────────────
function esc(s) {
  const d = document.createElement('div');
  d.textContent = String(s ?? '');
  return d.innerHTML;
}

function formatDate(ts) {
  try {
    if (!ts || ts === 0 || ts === '0') return '—';
    var n = typeof ts === 'string' ? parseFloat(ts) : ts;
    if (isNaN(n) || n === 0) return '—';
    var ms = n < 1e12 ? n * 1000 : n;
    return new Date(ms).toLocaleString();
  } catch { return String(ts); }
}

function getStoredTheme() {
  try {
    const stored = localStorage.getItem(THEME_STORAGE_KEY);
    return stored === 'light' || stored === 'dark' ? stored : 'dark';
  } catch {
    return 'dark';
  }
}

function updateThemeToggle(theme) {
  const btn = document.getElementById('theme-toggle');
  const icon = document.getElementById('theme-toggle-icon');
  const label = document.getElementById('theme-toggle-label');
  if (!btn || !icon || !label) return;

  const nextTheme = theme === 'dark' ? 'light' : 'dark';
  icon.textContent = theme === 'dark' ? '🌙' : '☀️';
  label.textContent = theme === 'dark' ? 'Dark' : 'Light';
  btn.title = `Switch to ${nextTheme} mode`;
  btn.setAttribute('aria-label', btn.title);
}

function applyTheme(theme, persist = true) {
  activeTheme = theme === 'light' ? 'light' : 'dark';
  document.documentElement.dataset.theme = activeTheme;
  document.documentElement.style.colorScheme = activeTheme;
  updateThemeToggle(activeTheme);
  if (!persist) return;
  try {
    localStorage.setItem(THEME_STORAGE_KEY, activeTheme);
  } catch {
    // ignore storage failures
  }
}

function toggleTheme() {
  applyTheme(activeTheme === 'dark' ? 'light' : 'dark');
}

function initTheme() {
  applyTheme(getStoredTheme(), false);
  document.getElementById('theme-toggle')?.addEventListener('click', toggleTheme);
}

/** @param {string} id @param {string} status */
function setDot(id, status) {
  const el = document.getElementById(id);
  if (!el) return;
  el.className = 'status-dot';
  el.classList.add(status === 'running' ? 'running' : status === 'stopped' ? 'stopped' : 'unknown');
}

/** @param {string} id @param {string|number} text */
function setText(id, text) {
  const el = document.getElementById(id);
  if (el) el.textContent = String(text);
}

// ── Globally accessible actions ─────────────────────
async function serviceAction(service, action) {
  try {
    const r = await CvcApi.serviceAction(service, action);
    toast(r.message || `${service} ${action}: OK`, 'success');
    setTimeout(() => refreshCurrentTab(), 1000);
  } catch (e) { toast(e.message, 'error'); }
}

function showRegisterAgentDialog() {
  document.getElementById('register-dialog')?.classList.remove('hidden');
}

function hideRegisterAgentDialog() {
  document.getElementById('register-dialog')?.classList.add('hidden');
}

async function doRegisterAgent() {
  const agentId = document.getElementById('reg-agent-id').value.trim();
  if (!agentId) { toast('Agent ID required', 'error'); return; }
  try {
    await CvcApi.registerHiveMindAgent({
      agent_id: agentId,
      name: document.getElementById('reg-name').value.trim() || undefined,
      role: document.getElementById('reg-role').value.trim() || 'worker',
      rank: document.getElementById('reg-rank').value.trim() || 'private',
      squad: document.getElementById('reg-squad').value.trim() || 'alpha',
    });
    toast('Agent registered', 'success');
    hideRegisterAgentDialog();
    if (currentTab === 'sdk') loadSDK();
  } catch (e) { toast(e.message, 'error'); }
}

// ── Operations ──────────────────────────────────────
async function doCommit() {
  const msg = document.getElementById('op-commit-msg').value.trim();
  if (!msg) { toast('Commit message required', 'error'); return; }
  try {
    const r = await CvcApi.opsCommit({
      message: msg,
      content: document.getElementById('op-commit-content').value || undefined,
      commit_type: document.getElementById('op-commit-type').value,
    });
    toast(`Committed: ${(r.commit_hash || '').slice(0, 8) || 'OK'}`, 'success');
    document.getElementById('op-commit-msg').value = '';
    document.getElementById('op-commit-content').value = '';
  } catch (e) { toast(e.message, 'error'); }
}

async function doBranch() {
  const name = document.getElementById('op-branch-name').value.trim();
  if (!name) { toast('Branch name required', 'error'); return; }
  try {
    await CvcApi.opsBranch({ name });
    toast(`Branch "${name}" created`, 'success');
    document.getElementById('op-branch-name').value = '';
    loadBranchesList();
  } catch (e) { toast(e.message, 'error'); }
}

async function doMerge() {
  const source = document.getElementById('op-merge-source').value.trim();
  if (!source) { toast('Source branch required', 'error'); return; }
  try {
    const strategy = document.getElementById('op-merge-strategy').value;
    await CvcApi.opsMerge({ source_branch: source, strategy });
    toast(`Merged "${source}"`, 'success');
  } catch (e) { toast(e.message, 'error'); }
}

async function doRestore() {
  const hash = document.getElementById('op-restore-hash').value.trim();
  if (!hash) { toast('Commit hash required', 'error'); return; }
  try {
    await CvcApi.opsRestore({ commit_hash: hash });
    toast(`Restored to ${hash.slice(0, 8)}`, 'success');
  } catch (e) { toast(e.message, 'error'); }
}

async function doRecall() {
  const query = document.getElementById('op-recall-query').value.trim();
  if (!query) return;
  const el = document.getElementById('op-recall-results');
  el.textContent = 'Searching…';
  try {
    const r = await CvcApi.opsRecall(query);
    if (!r.results?.length) { el.textContent = 'No results.'; return; }
    el.innerHTML = r.results.map(item =>
      `<div style="margin-bottom:8px"><strong>${esc(item.message || '')}</strong> <span class="text-muted">${item.distance != null ? `(${(100 - item.distance * 100).toFixed(0)}%)` : ''}</span><br><code>${(item.short_hash || item.commit_hash || '').slice(0, 8)}</code> <span class="tag tag-blue">${esc(item.relevance_source || '')}</span></div>`
    ).join('');
  } catch (e) { el.textContent = e.message; }
}

async function doDiff() {
  const h1 = document.getElementById('op-diff-hash1').value.trim();
  const h2 = document.getElementById('op-diff-hash2').value.trim();
  if (!h1 || !h2) { toast('Both hashes required', 'error'); return; }
  const el = document.getElementById('op-diff-result');
  el.textContent = 'Comparing…';
  try {
    const r = await CvcApi.opsDiff(h1, h2);
    el.innerHTML = `<pre>${esc(typeof r.diff === 'string' ? r.diff : JSON.stringify(r.diff, null, 2))}</pre>`;
  } catch (e) { el.textContent = e.message; }
}

async function switchModelTo(provider, model) {
  try {
    await CvcApi.switchModel(provider, model);
    toast(`Switched to ${model}`, 'success');
    loadModels();
  } catch (e) { toast(e.message, 'error'); }
}

async function removeAgent(id) {
  try {
    await CvcApi.removeHiveMindAgent(id);
    toast(`Agent "${id}" removed`, 'success');
    loadSDK();
  } catch (e) { toast(e.message, 'error'); }
}

// ── Tab Navigation ──────────────────────────────────
function setupNav() {
  document.querySelectorAll('.nav-link[data-tab]').forEach(link => {
    link.addEventListener('click', () => {
      switchTab(link.dataset.tab);
    });
  });
}

function switchTab(tab) {
  currentTab = tab;

  // Update nav
  document.querySelectorAll('.nav-link').forEach(l => l.classList.remove('active'));
  document.querySelector(`.nav-link[data-tab="${tab}"]`)?.classList.add('active');

  // Show content
  document.querySelectorAll('.tab-content').forEach(el => el.classList.add('hidden'));
  document.getElementById(`tab-${tab}`)?.classList.remove('hidden');

  // Update title
  const titles = {
    chat: 'Chat', overview: 'Overview', proxy: 'Proxy', agent: 'Agent',
    mcp: 'MCP Server', sdk: 'Hive Mind', operations: 'Operations',
    memory: 'Memory & Context', timeline: 'Timeline', models: 'Models',
    connections: 'Connections', settings: 'Settings',
  };
  setText('page-title', titles[tab] || tab);
  refreshCurrentTab();
}

// ── Data Loaders ────────────────────────────────────
async function loadServices() {
  try {
    const data = await CvcApi.getServices();
    // data is a dict of {proxy: {...}, agent: {...}, ...} directly
    for (const [name, info] of Object.entries(data)) {
      if (!info || typeof info !== 'object') continue;
      setDot(`status-${name}`, info.status);
      setDot(`ov-status-${name}`, info.status);
    }
    const px = data.proxy;
    if (px) {
      setText('ov-proxy-uptime', px.uptime || '—');
      setText('ov-proxy-pid', px.pid != null ? String(px.pid) : '—');
      setText('ov-proxy-port', px.port != null ? String(px.port) : '19333');
      setDot('px-status', px.status);
      setText('px-status-text', px.status);
      setText('px-pid', px.pid != null ? String(px.pid) : '—');
      setText('px-uptime', px.uptime || '—');
      setText('px-port', px.port != null ? String(px.port) : '19333');
    }
    const ag = data.agent;
    if (ag) setText('ov-agent-status', ag.status);
    const mcp = data.mcp;
    if (mcp?.tool_count != null) setText('ov-mcp-tools', mcp.tool_count);
  } catch { /* ignore */ }
}

async function loadAnalytics() {
  try {
    const a = await CvcApi.getAnalytics();
    const totalCommits = a.commits?.total ?? 0;
    const totalTokens = a.tokens?.total ?? 0;
    const estCost = a.tokens?.estimated_cost_usd ?? 0;
    setText('stat-commits', totalCommits);
    setText('stat-tokens', totalTokens.toLocaleString());
    setText('stat-cost', `$${estCost.toFixed(2)}`);
    setText('hive-commits-count', totalCommits);
  } catch { /* ignore */ }
}

function formatBytes(b) {
  if (!b || b === 0) return '0 B';
  const k = 1024;
  const sizes = ['B', 'KB', 'MB', 'GB'];
  const i = Math.floor(Math.log(b) / Math.log(k));
  return parseFloat((b / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
}

async function loadStats() {
  try {
    const s = await CvcApi.getStats();
    setText('stat-db-size', formatBytes(s.database_size_bytes));
    setText('stat-blobs', s.blob_count);
    setText('stat-branches', s.total_branches);
    setText('mem-db-size', formatBytes(s.database_size_bytes));
    setText('mem-blob-count', s.blob_count);
  } catch { /* ignore */ }
}

async function loadCommits() {
  try {
    const result = await CvcApi.getCommits(20);
    const commits = result?.commits || [];
    const el = document.getElementById('activity-feed');
    if (!commits.length) { el.innerHTML = '<p class="text-muted">No commits yet.</p>'; return; }
    const html = `<table class="data-table"><thead><tr><th>Hash</th><th>Message</th><th>Type</th><th>Time</th></tr></thead><tbody>` +
      commits.map(c =>
        `<tr><td><code>${esc((c.hash || '').slice(0, 8))}</code></td><td>${esc(c.message || '')}</td><td><span class="tag tag-blue">${esc(c.type || 'checkpoint')}</span></td><td>${formatDate(c.created_at)}</td></tr>`
      ).join('') + '</tbody></table>';
    el.innerHTML = html;
    const agEl = document.getElementById('agent-commits');
    if (agEl) agEl.innerHTML = html;
  } catch (e) {
    const el = document.getElementById('activity-feed');
    if (el) el.innerHTML = `<p class="text-muted">${esc(e.message)}</p>`;
  }
}

async function loadBranchesList() {
  try {
    const result = await CvcApi.opsBranches();
    const bs = result?.branches || [];
    const el = document.getElementById('branches-list');
    if (!bs.length) { el.innerHTML = '<p class="text-muted">No branches yet.</p>'; return; }
    const html = bs.map(b => `<span class="tag tag-green" style="margin:2px">${esc(b.name || b)}</span>`).join(' ');
    el.innerHTML = html;
    const opEl = document.getElementById('op-branches-list');
    if (opEl) opEl.innerHTML = html;
  } catch { /* ignore */ }
}

async function loadTimeline() {
  try {
    const result = await CvcApi.opsTimeline(50);
    const entries = result?.timeline || [];
    const el = document.getElementById('timeline-view');
    if (!entries.length) { el.innerHTML = '<p class="text-muted">No timeline entries.</p>'; return; }
    el.innerHTML = entries.map(e => `
      <div class="timeline-entry">
        <div class="tl-time">${formatDate(e.created_at)}</div>
        <div class="tl-body">
          <div class="tl-msg">${esc(e.message)}</div>
          <div class="tl-hash">${esc((e.hash || '').slice(0, 12))}</div>
          ${e.type ? `<span class="tl-type">${esc(e.type)}</span>` : ''}
        </div>
      </div>
    `).join('');
  } catch (e) {
    const el = document.getElementById('timeline-view');
    if (el) el.innerHTML = `<p class="text-muted">${esc(e.message)}</p>`;
  }
}

async function loadOpsStatus() {
  try {
    const s = await CvcApi.opsStatus();
    const el = document.getElementById('ops-status');
    el.innerHTML = `
      <div class="detail-grid">
        <div><span class="label">DB Size</span><span class="value">${formatBytes(s.database_size_bytes)}</span></div>
        <div><span class="label">Commits</span><span class="value">${s.total_commits}</span></div>
        <div><span class="label">Branches</span><span class="value">${s.total_branches}</span></div>
        <div><span class="label">HEAD</span><span class="value code">${esc((s.head || 'none').slice(0, 12))}</span></div>
        <div><span class="label">Blobs</span><span class="value">${s.blob_count}</span></div>
      </div>
    `;
  } catch { /* ignore */ }
}

async function loadMemory() {
  try {
    const [ctx, blobs, stats] = await Promise.all([
      CvcApi.getMemoryContext().catch(() => ({ messages: [], token_count: 0 })),
      CvcApi.getMemoryBlobs().catch(() => ({ blobs: [], total: 0 })),
      CvcApi.getMemoryStats().catch(() => ({ database_size_bytes: 0, blob_count: 0 })),
    ]);

    setText('mem-db-size', formatBytes(stats.database_size_bytes));
    setText('mem-blob-count', stats.blob_count || 0);

    const ctxEl = document.getElementById('memory-context');
    const ctxData = ctx.messages || [];
    if (ctxData.length === 0) {
      ctxEl.innerHTML = '<p class="text-muted">No context entries.</p>';
    } else {
      ctxEl.innerHTML = `<p style="margin-bottom:8px">Token count: <strong>${ctx.token_count || '—'}</strong> | Messages: <strong>${ctx.total || ctxData.length}</strong></p>` +
        ctxData.slice(0, 20).map(e =>
          `<div style="padding:6px 0;border-bottom:1px solid var(--border)"><span class="tag tag-blue">${esc(e.role)}</span> ${esc((e.content || '').slice(0, 200))}</div>`
        ).join('');
    }

    const blobEl = document.getElementById('memory-blobs');
    const blobData = blobs.blobs || [];
    if (blobData.length === 0) {
      blobEl.innerHTML = '<p class="text-muted">No blobs stored.</p>';
    } else {
      blobEl.innerHTML = `<table class="data-table"><thead><tr><th>Hash</th><th>Size</th></tr></thead><tbody>` +
        blobData.slice(0, 50).map(b =>
          `<tr><td><code>${esc((b.hash || '').slice(0, 12))}</code></td><td>${formatBytes(b.size_bytes) || '—'}</td></tr>`
        ).join('') + '</tbody></table>';
    }
  } catch { /* ignore */ }
}

async function loadModels() {
  try {
    const [catalog, current] = await Promise.all([
      CvcApi.getModelCatalog().catch(() => ({ providers: {} })),
      CvcApi.getCurrentModel().catch(() => ({ provider: '—', model: '—', api_key_set: false })),
    ]);

    const curEl = document.getElementById('current-model-info');
    curEl.innerHTML = `
      <div class="detail-grid">
        <div><span class="label">Provider</span><span class="value">${esc(current.provider)}</span></div>
        <div><span class="label">Model</span><span class="value code">${esc(current.model)}</span></div>
        <div><span class="label">API Key</span><span class="value">${current.api_key_set ? '<span class="tag tag-green">Set</span>' : '<span class="tag tag-red">Not set</span>'}</span></div>
      </div>
    `;
    setText('ag-provider', current.provider);
    setText('ag-model', current.model);
    setText('ov-agent-provider', current.provider);
    setText('ov-agent-model', current.model);

    const catEl = document.getElementById('model-catalog');
    const providers = catalog.providers || {};
    const providerNames = Object.keys(providers);
    if (!providerNames.length) {
      catEl.innerHTML = '<p class="text-muted">No model catalog available.</p>';
      return;
    }

    let html = '<div class="model-grid">';
    for (const prov of providerNames) {
      for (const m of (providers[prov] || [])) {
        const modelId = m.id || m.name || '';
        const isActive = modelId === current.model && prov === current.provider;
        html += `<div class="model-card ${isActive ? 'active-model' : ''}" onclick="switchModelTo('${esc(prov)}','${esc(modelId)}')">
          <div class="model-name">${esc(m.name || m.id || '?')}</div>
          <div class="model-provider">${esc(prov)}</div>
          ${m.context_window ? `<div class="model-ctx">${(m.context_window / 1000).toFixed(0)}k context</div>` : ''}
        </div>`;
      }
    }
    html += '</div>';
    catEl.innerHTML = html;
  } catch { /* ignore */ }
}

async function loadMCP() {
  try {
    const [toolsResp, status] = await Promise.all([
      CvcApi.getMCPTools().catch(() => ({ tools: [] })),
      CvcApi.getMCPStatus().catch(() => ({ status: 'unavailable', transport: 'stdio', tools_count: 0 })),
    ]);

    setText('mcp-transport', status.transport || 'stdio');
    setText('mcp-status-val', status.status || 'available');
    setText('mcp-tool-count', status.tools_count || 0);

    const el = document.getElementById('mcp-tools-list');
    const toolList = toolsResp.tools || (Array.isArray(toolsResp) ? toolsResp : []);
    if (!toolList.length) {
      el.innerHTML = '<p class="text-muted">No tools loaded.</p>';
      return;
    }
    el.innerHTML = `<table class="data-table"><thead><tr><th>Tool</th><th>Description</th></tr></thead><tbody>` +
      toolList.map(t =>
        `<tr><td><code>${esc(t.name)}</code></td><td>${esc(t.description || '')}</td></tr>`
      ).join('') + '</tbody></table>';
  } catch { /* ignore */ }
}

async function loadSDK() {
  try {
    const result = await CvcApi.getHiveMindAgents().catch(() => ({ agents: [] }));
    const list = result.agents || [];
    setText('hive-agents-count', list.length);

    const el = document.getElementById('sdk-agents-table');
    if (!list.length) {
      el.innerHTML = '<p class="text-muted">No agents registered.</p>';
    } else {
      el.innerHTML = `<table class="data-table"><thead><tr><th>ID</th><th>Name</th><th>Role</th><th>Squad</th><th>Actions</th></tr></thead><tbody>` +
        list.map(a =>
          `<tr><td><code>${esc(a.agent_id)}</code></td><td>${esc(a.name || '—')}</td><td>${esc(a.role || '—')}</td><td>${esc(a.squad || '—')}</td><td><button class="btn btn-sm btn-red" onclick="removeAgent('${esc(a.agent_id)}')">Remove</button></td></tr>`
        ).join('') + '</tbody></table>';
    }
  } catch { /* ignore */ }
  // Load new sub-modules
  if (typeof loadAgentsData === 'function') loadAgentsData();
  if (typeof loadHiveData === 'function') loadHiveData();
}

async function loadSessions() {
  try {
    const result = await CvcApi.getSessions();
    const el = document.getElementById('proxy-sessions');
    const list = result?.sessions || (Array.isArray(result) ? result : []);
    if (!list.length) {
      el.innerHTML = '<p class="text-muted">No active sessions.</p>';
      return;
    }
    el.innerHTML = `<table class="data-table"><thead><tr><th>Session</th><th>Details</th></tr></thead><tbody>` +
      list.map((s, i) =>
        `<tr><td>#${i + 1}</td><td><pre style="margin:0">${esc(JSON.stringify(s, null, 2))}</pre></td></tr>`
      ).join('') + '</tbody></table>';
  } catch { /* ignore */ }
}

async function loadConnections() {
  try {
    await CvcApi.getConnections();
    setText('conn-url', 'http://127.0.0.1:19333/v1');

    const tools = [
      { icon: '🔷', name: 'Cursor', desc: 'Add CVC as an OpenAI-compatible provider' },
      { icon: '🟢', name: 'Windsurf', desc: 'Configure as custom API endpoint' },
      { icon: '🟣', name: 'Continue.dev', desc: 'Set as OpenAI proxy in config.json' },
      { icon: '⬛', name: 'Cline', desc: 'Use CVC proxy URL in Cline settings' },
      { icon: '🔵', name: 'VS Code Copilot', desc: 'Route through CVC proxy' },
      { icon: '🌐', name: 'Open WebUI', desc: 'Add as OpenAI-compatible connection' },
      { icon: '📟', name: 'CLI (curl)', desc: 'Direct API access via curl/httpie' },
    ];

    const connEl = document.getElementById('connections-list');
    connEl.innerHTML = tools.map(t => `
      <div class="connection-card">
        <div class="conn-icon">${t.icon}</div>
        <div>
          <div class="conn-name">${esc(t.name)}</div>
          <div class="conn-desc">${esc(t.desc)}</div>
        </div>
      </div>
    `).join('');
  } catch { /* ignore */ }
}

async function loadSettings() {
  try {
    const [config, vcs, auditResp, apState] = await Promise.all([
      CvcApi.getConfig().catch(() => ({})),
      CvcApi.getVCSStatus().catch(() => ({ provider: '—', has_repo: false })),
      CvcApi.getAudit().catch(() => ({ entries: [] })),
      CvcApi.getAutopilot().catch(() => ({ autopilot: false })),
    ]);

    // ── Auto-pilot toggle ────────────────────────────
    const apToggle = document.getElementById('autopilot-toggle');
    const apStatus = document.getElementById('autopilot-status');
    if (apToggle) {
      apToggle.checked = apState.autopilot;
      apStatus.textContent = apState.autopilot ? 'ON' : 'OFF';
      apStatus.className = 'tag ' + (apState.autopilot ? 'tag-green' : 'tag-red');
      // Remove old listener before adding new one
      apToggle.onchange = async () => {
        const resp = await CvcApi.setAutopilot(apToggle.checked);
        apStatus.textContent = resp.autopilot ? 'ON' : 'OFF';
        apStatus.className = 'tag ' + (resp.autopilot ? 'tag-green' : 'tag-red');
      };
    }

    const cfgEl = document.getElementById('settings-config');
    if (cfgEl) cfgEl.innerHTML = `<div class="code-block"><div class="code-title">Configuration</div><pre>${esc(JSON.stringify(config, null, 2))}</pre></div>`;

    const vcsEl = document.getElementById('settings-vcs');
    vcsEl.innerHTML = `<div class="detail-grid">
      <div><span class="label">Provider</span><span class="value">${esc(vcs.provider || '—')}</span></div>
      <div><span class="label">Has Repo</span><span class="value">${vcs.has_repo ? '<span class="tag tag-green">Yes</span>' : '<span class="tag tag-red">No</span>'}</span></div>
      <div><span class="label">Repo Root</span><span class="value code">${esc(vcs.repo_root || '—')}</span></div>
      <div><span class="label">Branch</span><span class="value">${esc(vcs.current_branch || '—')}</span></div>
    </div>`;

    const auditEl = document.getElementById('settings-audit');
    const auditList = auditResp.entries || (Array.isArray(auditResp) ? auditResp : []);
    if (!auditList.length) {
      auditEl.innerHTML = '<p class="text-muted">No audit entries.</p>';
    } else {
      auditEl.innerHTML = `<table class="data-table"><thead><tr><th>Time</th><th>Action</th><th>Details</th></tr></thead><tbody>` +
        auditList.slice(0, 50).map(e =>
          `<tr><td>${formatDate(e.created_at || e.timestamp)}</td><td><code>${esc(e.event_type || e.action)}</code></td><td>${esc(e.action_detail ? JSON.stringify(e.action_detail) : (e.details || ''))}</td></tr>`
        ).join('') + '</tbody></table>';
    }
  } catch { /* ignore */ }
  // Load new settings sub-module
  if (typeof loadSettingsData === 'function') loadSettingsData();
}

async function loadProxy() {
  await loadServices();
  await loadSessions();
  try {
    const config = await CvcApi.getConfig();
    const el = document.getElementById('proxy-config');
    el.innerHTML = `<div class="code-block"><div class="code-title">Proxy Configuration</div><pre>${esc(JSON.stringify(config, null, 2))}</pre></div>`;
  } catch { /* ignore */ }
}

// ── Refresh Logic ───────────────────────────────────
function refreshCurrentTab() {
  switch (currentTab) {
    case 'overview':
      loadServices(); loadAnalytics(); loadStats(); loadCommits(); loadBranchesList();
      break;
    case 'proxy': loadProxy(); break;
    case 'agent': loadModels(); loadCommits(); break;
    case 'mcp': loadMCP(); break;
    case 'sdk': loadSDK(); loadAnalytics(); break;
    case 'operations': loadOpsStatus(); loadBranchesList(); break;
    case 'memory': loadMemory(); break;
    case 'timeline': loadTimeline(); break;
    case 'models': loadModels(); break;
    case 'connections': loadConnections(); break;
    case 'settings': loadSettings(); break;
  }
}

// ── Clock ───────────────────────────────────────────
function startClock() {
  const tick = () => setText('clock', new Date().toLocaleTimeString());
  tick();
  setInterval(tick, 1000);
}

// ── Init ────────────────────────────────────────────
document.addEventListener('DOMContentLoaded', () => {
  initTheme();
  setupNav();
  initChat();
  startClock();
  if (typeof initSettings === 'function') initSettings();
  if (typeof initAgents === 'function') initAgents();
  if (typeof initHive === 'function') initHive();

  // WebSocket
  ws = new DashboardWS();
  ws.connect();
  ws.on(msg => {
    if (msg.type === 'status_update') {
      loadServices();
    } else if (msg.type === 'commit') {
      toast(`New commit: ${msg.data?.message || ''}`, 'info');
      if (currentTab === 'overview' || currentTab === 'timeline') refreshCurrentTab();
    }
  });

  // Initial data load
  loadServices();

  // Periodic refresh — every 15s
  pollTimer = setInterval(() => {
    loadServices();
    if (currentTab === 'overview') {
      loadAnalytics();
      loadStats();
    }
  }, 15000);
});
