// panes/system.js — process & host introspection with live sparklines.
import { registerPane } from '../app.js';
import { jget, formatUptime, escapeHtml } from '../lib/api.js';
import { fmtJSON, fmtBytes, meterRow, sparkline, copyToClipboard, ok } from '../lib/ui.js';

const TPL = `
  <div class="pane-head">
    <div><div class="title">System</div><div class="sub">Process, host and runtime introspection.</div></div>
    <div class="grow"></div>
    <label class="check"><input type="checkbox" id="sys-live" checked/> live (5s)</label>
    <button class="btn btn-secondary" id="sys-refresh">Refresh</button>
  </div>

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

  <div class="grid cols-2 mb-3">
    <div class="card">
      <div class="card-title"><h2>Resources</h2><span class="muted xsmall" id="sys-cpu-ts">—</span></div>
      <div id="sys-gauges"></div>
      <div class="divider"></div>
      <div class="muted xsmall mb-1">Resident memory (last 60 samples)</div>
      <div id="sys-spark-rss"></div>
      <div class="muted xsmall mt-2 mb-1">CPU load (1m)</div>
      <div id="sys-spark-cpu"></div>
    </div>

    <div class="card">
      <div class="card-title"><h2>Build &amp; identity</h2></div>
      <dl class="dl" id="sys-version"></dl>
      <div class="divider"></div>
      <div class="card-title"><h2 style="font-size:14px">Configured services</h2></div>
      <div id="sys-services"></div>
    </div>
  </div>

  <div class="grid cols-2">
    <div class="card">
      <div class="card-title"><h2>Raw /api/system</h2><button class="btn btn-ghost" data-copy="sys-s">Copy</button></div>
      <pre id="sys-s" class="code" style="max-height:36vh">…</pre>
    </div>
    <div class="card">
      <div class="card-title"><h2>Raw /api/version</h2><button class="btn btn-ghost" data-copy="sys-v">Copy</button></div>
      <pre id="sys-v" class="code" style="max-height:36vh">…</pre>
    </div>
  </div>
`;

const rssHistory = [];
const cpuHistory = [];
let timer = null;

function kpi(label, value, sub) {
  return `<div class="kpi"><div class="label">${escapeHtml(label)}</div><div class="value">${escapeHtml(value)}</div><div class="sub">${escapeHtml(sub || '')}</div></div>`;
}
function row(k, v) { return `<dt>${escapeHtml(k)}</dt><dd>${escapeHtml(v ?? '—')}</dd>`; }
function memState(pct) { return pct > 85 ? 'err' : pct > 60 ? 'warn' : 'ok'; }

async function load(host) {
  let v = {}, s = {}, svc = { services: [] };
  try { [v, s, svc] = await Promise.all([jget('/api/version'), jget('/api/system'), jget('/api/services').catch(() => ({ services: [] }))]); }
  catch (e) { host.querySelector('#sys-s').textContent = 'error: ' + e.message; return; }

  host.querySelector('#sys-v').textContent = fmtJSON(v);
  host.querySelector('#sys-s').textContent = fmtJSON(s);

  // KPIs
  host.querySelector('#sys-kpis').innerHTML = [
    kpi('Uptime',  formatUptime(s.uptime_seconds), `pid ${s.pid ?? '—'}`),
    kpi('Memory',  fmtBytes(s.rss_bytes), s.mem_used_pct != null ? `${s.mem_used_pct.toFixed(1)}% of host` : 'resident set'),
    kpi('CPUs',    String(s.cpu_count || '—'), `load ${(s.loadavg?.[0] ?? 0).toFixed(2)} / ${(s.loadavg?.[1] ?? 0).toFixed(2)} / ${(s.loadavg?.[2] ?? 0).toFixed(2)}`),
    kpi('Host',    s.hostname || '—', `${s.sysname || ''} ${s.release || ''}`.trim()),
  ].join('');

  // sparkline history
  rssHistory.push(+s.rss_bytes || 0); while (rssHistory.length > 60) rssHistory.shift();
  cpuHistory.push(+s.loadavg?.[0] || 0); while (cpuHistory.length > 60) cpuHistory.shift();
  host.querySelector('#sys-spark-rss').innerHTML = sparkline(rssHistory);
  host.querySelector('#sys-spark-cpu').innerHTML = sparkline(cpuHistory);

  // gauges
  const cpus = s.cpu_count || 1;
  const loadPct = Math.min(100, (+s.loadavg?.[0] || 0) / cpus * 100);
  const memPct = +s.mem_used_pct || 0;
  host.querySelector('#sys-gauges').innerHTML = [
    meterRow('Memory',  memPct,  `${memPct.toFixed(1)}%`, memState(memPct)),
    meterRow('Load 1m', loadPct, (+s.loadavg?.[0] || 0).toFixed(2), loadPct > 80 ? 'err' : loadPct > 50 ? 'warn' : 'ok'),
    meterRow('User CPU',  Math.min(100, (s.user_cpu_s   || 0) / Math.max(1, s.uptime_seconds) * 100), `${(s.user_cpu_s   || 0).toFixed(1)}s`),
    meterRow('Sys CPU',   Math.min(100, (s.system_cpu_s || 0) / Math.max(1, s.uptime_seconds) * 100), `${(s.system_cpu_s || 0).toFixed(1)}s`),
  ].join('');
  host.querySelector('#sys-cpu-ts').textContent = new Date().toLocaleTimeString();

  // version dl
  host.querySelector('#sys-version').innerHTML =
    row('Name', v.name) +
    row('Version', v.version) +
    row('Git SHA', v.git_sha) +
    row('Build time', v.build_time) +
    row('C++ standard', s.cxx_standard) +
    row('Architecture', s.machine) +
    row('Total memory', fmtBytes(s.mem_total));

  // configured services
  host.querySelector('#sys-services').innerHTML = (svc.services || []).map(x => `
    <div class="row">
      <span class="pill ${x.configured ? 'ok' : 'muted'}"><span class="dot"></span>${escapeHtml(x.service)}</span>
      <div class="grow muted small">${escapeHtml(x.prefix)}</div>
      <span class="muted mono small">${x.configured ? `${escapeHtml(x.upstream_scheme || '')}://${escapeHtml(x.upstream_host || '')}:${x.upstream_port || ''}` : 'not configured'}</span>
    </div>
  `).join('') || '<div class="muted xsmall p-2">no services exposed</div>';
}

registerPane('system', {
  label: 'System',
  init(host) {
    host.innerHTML = TPL;
    host.querySelector('#sys-refresh').addEventListener('click', () => load(host));
    host.querySelectorAll('[data-copy]').forEach(b =>
      b.addEventListener('click', () => copyToClipboard(host.querySelector('#' + b.dataset.copy)?.textContent || ''))
    );
    load(host);
    timer = setInterval(() => {
      if (!host.classList.contains('active')) return;
      if (!host.querySelector('#sys-live')?.checked) return;
      load(host);
    }, 5000);
  },
  refresh: load,
});
