// panes/dashboard.js — control center: KPIs, service health with latency
// sparklines, recent items, live MCP event ticker, quick actions.
import { registerPane } from '../app.js';
import { jget, services, probeService, formatUptime, escapeHtml, mcp } from '../lib/api.js';
import { sparkline, meterRow, fmtBytes, fmtNum, ok, err, copyToClipboard } from '../lib/ui.js';

const TPL = `
  <div class="pane-head">
    <div>
      <div class="title">Control center</div>
      <div class="sub">Live status across every CxWebApp service.</div>
    </div>
    <div class="grow"></div>
    <label class="check"><input type="checkbox" id="db-auto" checked/> auto (15s)</label>
    <button class="btn btn-secondary" id="db-refresh">Refresh</button>
  </div>

  <div class="grid cols-4 mb-3" id="db-kpis"></div>

  <div class="grid cols-2 mb-3">
    <div class="card">
      <div class="card-title"><h2>Services</h2><span class="muted mono xsmall" id="db-svc-meta">—</span></div>
      <div id="db-services"></div>
    </div>
    <div class="card">
      <div class="card-title"><h2>Resources</h2><span class="muted xsmall" id="db-res-ts">—</span></div>
      <div id="db-gauges"></div>
      <div class="muted xsmall mt-2 mb-1">Probe RTT (last 30 sweeps)</div>
      <div id="db-spark"></div>
    </div>
  </div>

  <div class="grid cols-2 mb-3">
    <div class="card">
      <div class="card-title"><h2>Recent items</h2><a href="#items" class="muted small">view all →</a></div>
      <div id="db-items"></div>
    </div>
    <div class="card">
      <div class="card-title"><h2>Quick actions</h2></div>
      <div class="btn-row" style="flex-wrap:wrap">
        <a class="btn btn-secondary" href="#api">API Explorer</a>
        <a class="btn btn-secondary" href="#tools">MCP Tools</a>
        <a class="btn btn-secondary" href="#inbox">Sweep Inbox</a>
        <a class="btn btn-secondary" href="#diffusion">Generate Image</a>
        <a class="btn btn-secondary" href="#demand">Queue Demand</a>
        <a class="btn btn-secondary" href="#lang">Run Pipeline</a>
        <button class="btn btn-ghost" id="db-copy">Copy snapshot</button>
      </div>
      <div class="divider"></div>
      <div class="card-title"><h2 style="font-size:14px">MCP event ticker</h2></div>
      <pre class="console" id="db-events" style="max-height:24vh">…</pre>
    </div>
  </div>

  <div class="card">
    <div class="card-title"><h2>Activity log</h2><span class="muted xsmall" id="db-log-meta">stream</span></div>
    <pre class="console" id="db-log" style="max-height:30vh">[boot] dashboard ready\n</pre>
  </div>
`;

function kpi(label, value, sub, accent = '★') {
  return `<div class="kpi">
    <div class="label">${escapeHtml(label)}</div>
    <div class="value">${escapeHtml(value)}</div>
    <div class="sub">${escapeHtml(sub || '')}</div>
    <div class="accent">${accent}</div>
  </div>`;
}

function svcRow(svc, res, history) {
  const cls = res.ok ? 'ok' : 'err';
  const text = res.ok ? `up · ${res.ms}ms` : `down · ${escapeHtml(String(res.error))}`;
  return `<div class="row">
    <span class="pill ${cls}"><span class="dot"></span>${escapeHtml(svc.label)}</span>
    <div class="grow muted small mono truncate" style="margin-left:8px">${escapeHtml(svc.health)}</div>
    <div style="width:120px">${sparkline(history, { w: 120, h: 22 })}</div>
    <span class="mono muted xsmall" style="width:110px;text-align:right">${text}</span>
  </div>`;
}

function log(host, msg) {
  const pre = host.querySelector('#db-log');
  if (!pre) return;
  const ts = new Date().toISOString().slice(11, 19);
  pre.textContent = `[${ts}] ${msg}\n` + pre.textContent;
  if (pre.textContent.length > 8000) pre.textContent = pre.textContent.slice(0, 8000);
}

const rttHist = {};   // service id -> [last 30 rtts]
let lastSnapshot = {};

function recordRtt(id, ms) {
  if (!rttHist[id]) rttHist[id] = [];
  rttHist[id].push(ms);
  while (rttHist[id].length > 30) rttHist[id].shift();
}

async function refresh(host) {
  const results = await Promise.all(services.map(s => probeService(s).then(r => ({ s, r }))));
  results.forEach(({ s, r }) => recordRtt(s.id, r.ms));
  host.querySelector('#db-services').innerHTML = results.map(({ s, r }) => svcRow(s, r, rttHist[s.id])).join('');
  const up = results.filter(x => x.r.ok).length;
  host.querySelector('#db-svc-meta').textContent = `${up}/${results.length} up`;

  let sys = {}, ver = {}, itemsResp = { items: [] };
  try { [sys, ver, itemsResp] = await Promise.all([
    jget('/api/system'), jget('/api/version'), jget('/api/items'),
  ]); } catch (_) {}

  host.querySelector('#db-kpis').innerHTML = [
    kpi('Health', up === results.length ? 'All up' : `${up}/${results.length}`,
        up === results.length ? 'all systems normal' : 'some degraded', up === results.length ? '✓' : '!'),
    kpi('Uptime', formatUptime(sys.uptime_seconds), 'since process boot', '⏱'),
    kpi('Version', ver.version || '—', (ver.git_sha || '').slice(0, 7) || '—', '⌥'),
    kpi('Items',   fmtNum(itemsResp.count ?? (itemsResp.items || []).length), 'stored in API', '☷'),
  ].join('');

  // resource gauges
  const cpus  = sys.cpu_count || 1;
  const load  = +sys.loadavg?.[0] || 0;
  const memPct = +sys.mem_used_pct || 0;
  const loadPct = Math.min(100, load / cpus * 100);
  host.querySelector('#db-gauges').innerHTML = [
    meterRow('Memory',  memPct,  `${fmtBytes(sys.rss_bytes)} / ${fmtBytes(sys.mem_total)}`, memPct > 85 ? 'err' : memPct > 60 ? 'warn' : 'ok'),
    meterRow('Load 1m', loadPct, load.toFixed(2),                                            loadPct > 80 ? 'err' : loadPct > 50 ? 'warn' : 'ok'),
  ].join('');

  // combined RTT sparkline (averaged across services)
  const len = Math.max(0, ...Object.values(rttHist).map(a => a.length));
  const avg = Array.from({ length: len }, (_, i) => {
    const vals = Object.values(rttHist).map(a => a[i]).filter(v => Number.isFinite(v));
    return vals.length ? vals.reduce((a, b) => a + b, 0) / vals.length : 0;
  });
  host.querySelector('#db-spark').innerHTML = sparkline(avg, { w: 480, h: 36 });
  host.querySelector('#db-res-ts').textContent = new Date().toLocaleTimeString();

  // recent items
  const items = (itemsResp.items || []).slice(0, 6);
  host.querySelector('#db-items').innerHTML = items.length
    ? items.map(it => `<div class="row">
        <span class="mono muted">#${it.id}</span>
        <div class="grow"><div class="ttl">${escapeHtml(it.name)}</div><div class="desc">${escapeHtml(it.description || '')}</div></div>
      </div>`).join('')
    : '<div class="muted" style="padding:12px">No items yet.</div>';

  // MCP events ticker
  try {
    const arr = await mcp.events(20);
    host.querySelector('#db-events').textContent = (arr || []).slice(-20).reverse().map(ev => {
      const t = ev.ts ?? ev.timestamp ?? ev.time;
      const time = t ? new Date(typeof t === 'number' ? (t < 1e12 ? t * 1000 : t) : t).toLocaleTimeString() : '—';
      return `${time}  ${ev.kind || ev.type || 'event'}  ${typeof ev.detail === 'string' ? ev.detail : JSON.stringify(ev.detail ?? ev.data ?? '')}`;
    }).join('\n') || '(no events)';
  } catch (e) {
    host.querySelector('#db-events').textContent = `(events unavailable: ${e.message})`;
  }

  lastSnapshot = { sys, ver, services: results.map(({ s, r }) => ({ id: s.id, ok: r.ok, ms: r.ms })) };
  log(host, `refreshed · ${up}/${results.length} services up`);
}

let timer = null;
registerPane('dashboard', {
  label: 'Dashboard',
  init(host) {
    host.innerHTML = TPL;
    host.querySelector('#db-refresh').addEventListener('click', () => refresh(host));
    host.querySelector('#db-copy').addEventListener('click', () => copyToClipboard(JSON.stringify(lastSnapshot, null, 2), 'snapshot copied'));
    refresh(host);
    timer = setInterval(() => {
      if (!host.classList.contains('active')) return;
      if (!host.querySelector('#db-auto')?.checked) return;
      refresh(host);
    }, 15_000);
  },
  refresh,
});
