// panes/api.js — interactive HTTP explorer with presets, headers, history, share.
import { registerPane } from '../app.js';
import { jraw, escapeHtml } from '../lib/api.js';
import { fmtJSON, ok, err, copyToClipboard, historyStore, withCopy, bindCopyButtons } from '../lib/ui.js';

const PRESETS = [
  { label: 'health',         method: 'GET',  path: '/api/health' },
  { label: 'version',        method: 'GET',  path: '/api/version' },
  { label: 'system',         method: 'GET',  path: '/api/system' },
  { label: 'services',       method: 'GET',  path: '/api/services' },
  { label: 'items',          method: 'GET',  path: '/api/items' },
  { label: 'create item',    method: 'POST', path: '/api/items',
    body: '{\n  "name": "demo",\n  "description": "from API explorer"\n}' },
  { label: 'mac info',       method: 'GET',  path: '/api/mac/info' },
  { label: 'echo',           method: 'POST', path: '/api/echo', body: '{"hello":"world"}' },
  { label: 'diffusion health', method: 'GET', path: '/api/diffusion/healthz' },
  { label: 'demand reports',   method: 'GET', path: '/api/demand/reports' },
  { label: 'lang pipelines',   method: 'GET', path: '/api/lang/pipelines' },
  { label: 'slack info',       method: 'GET', path: '/api/slack/info' },
  { label: 'files providers',  method: 'GET', path: '/api/files/mcp_providers?select=*' },
];

const history = historyStore('cx_api_history', 30);

const TPL = `
  <div class="pane-head">
    <div><div class="title">API Explorer</div><div class="sub">Hit any backend route — auto-saves history, supports custom headers and presets.</div></div>
    <div class="grow"></div>
    <button class="btn btn-ghost" id="ax-clear-hist">Clear history</button>
    <button class="btn btn-secondary" id="ax-share">Share link</button>
  </div>

  <div class="grid" style="grid-template-columns: 240px 1fr; gap: 16px;">
    <div class="card no-pad" style="padding: 8px;">
      <div class="card-title" style="padding: 8px 10px;"><h2 style="font-size:13px">Presets</h2></div>
      <div id="ax-presets" style="display:flex; flex-direction:column; gap:2px"></div>
      <div class="divider"></div>
      <div class="card-title" style="padding: 4px 10px;"><h2 style="font-size:13px">History</h2><span class="muted xsmall" id="ax-hist-count">0</span></div>
      <div id="ax-history" style="display:flex; flex-direction:column; gap:2px; max-height:40vh; overflow:auto"></div>
    </div>

    <div>
      <div class="card mb-3">
        <div class="flex gap-2 mb-3">
          <select class="input" id="ax-method" style="max-width:120px">
            <option>GET</option><option>POST</option><option>PUT</option><option>PATCH</option><option>DELETE</option><option>HEAD</option>
          </select>
          <input class="input" id="ax-path" value="/api/health" placeholder="/api/…"/>
          <button class="btn btn-primary" id="ax-send">Send</button>
        </div>

        <div class="seg mb-3" id="ax-tabs">
          <button class="active" data-tab="body">Body</button>
          <button data-tab="headers">Headers</button>
          <button data-tab="params">Query</button>
        </div>

        <div data-pane-tab="body">
          <label class="check mb-2"><input type="checkbox" id="ax-pretty" checked/> pretty-print outgoing JSON on Format</label>
          <textarea class="input" id="ax-body" rows="8" placeholder='{"key": "value"}'></textarea>
          <div class="btn-row mt-2">
            <button class="btn btn-ghost" id="ax-fmt">Format JSON</button>
            <button class="btn btn-ghost" id="ax-copy-body">Copy</button>
            <button class="btn btn-ghost" id="ax-as-curl">Copy as cURL</button>
          </div>
        </div>

        <div data-pane-tab="headers" style="display:none">
          <div class="muted xsmall mb-2">One per line, format <code>Header: value</code>. Defaults <code>Content-Type: application/json</code> + <code>Accept: application/json</code> are added automatically.</div>
          <textarea class="input" id="ax-headers" rows="6" placeholder="X-Trace-Id: abc-123"></textarea>
        </div>

        <div data-pane-tab="params" style="display:none">
          <div class="muted xsmall mb-2">Auto-appended to the URL. <code>key=value</code>, one per line.</div>
          <textarea class="input" id="ax-params" rows="6" placeholder="limit=10"></textarea>
        </div>
      </div>

      <div class="card">
        <div class="card-title">
          <h2>Response</h2>
          <span class="flex gap-2 items-center">
            <span class="pill muted" id="ax-status">—</span>
            <button class="btn btn-ghost" id="ax-copy-resp">Copy</button>
            <button class="btn btn-ghost" id="ax-resp-pretty">Toggle pretty</button>
          </span>
        </div>
        <div id="ax-resp-meta" class="muted xsmall mb-2">no request yet</div>
        <pre id="ax-out" class="code" style="max-height:55vh">no request yet</pre>
      </div>
    </div>
  </div>
`;

let prettyMode = true;
let lastResponse = null;
let lastRequest = null;

function parseKVLines(s, sep) {
  return (s || '').split('\n').map(l => l.trim()).filter(Boolean).map(l => {
    const i = l.indexOf(sep);
    return i < 0 ? null : [l.slice(0, i).trim(), l.slice(i + sep.length).trim()];
  }).filter(Boolean);
}

function buildUrl(host, path, paramsText) {
  const params = parseKVLines(paramsText, '=');
  if (!params.length) return path;
  const usp = new URLSearchParams();
  for (const [k, v] of params) usp.append(k, v);
  return path + (path.includes('?') ? '&' : '?') + usp.toString();
}

function asCurl(req) {
  const parts = ['curl', '-X', req.method, JSON.stringify(req.url)];
  for (const [k, v] of (req.headers || [])) parts.push('-H', JSON.stringify(`${k}: ${v}`));
  if (req.body) parts.push('--data-raw', JSON.stringify(req.body));
  return parts.join(' ');
}

function renderResponse(host) {
  const out = host.querySelector('#ax-out');
  if (!lastResponse) { out.textContent = 'no response yet'; return; }
  const { body, raw } = lastResponse;
  if (prettyMode) out.textContent = typeof body === 'string' ? body : fmtJSON(body);
  else            out.textContent = raw;
}

function renderHistory(host) {
  const items = history.list();
  host.querySelector('#ax-hist-count').textContent = String(items.length);
  host.querySelector('#ax-history').innerHTML = items.length
    ? items.map((h, i) => `<button class="nav-item" data-hi="${i}" title="${escapeHtml(h.path)}">
        <span class="pill ${h.status >= 200 && h.status < 300 ? 'ok' : h.status >= 400 ? 'err' : 'warn'}" style="font-size:10px">${h.method}</span>
        <span class="truncate" style="font-size:12px">${escapeHtml(h.path)}</span>
      </button>`).join('')
    : '<div class="muted xsmall p-2">empty</div>';
  host.querySelectorAll('#ax-history [data-hi]').forEach(b => {
    b.addEventListener('click', () => {
      const it = history.list()[+b.dataset.hi];
      if (!it) return;
      host.querySelector('#ax-method').value = it.method;
      host.querySelector('#ax-path').value   = it.path;
      host.querySelector('#ax-body').value   = it.body || '';
    });
  });
}

function renderPresets(host) {
  host.querySelector('#ax-presets').innerHTML = PRESETS.map((p, i) =>
    `<button class="nav-item" data-pi="${i}"><span class="pill info" style="font-size:10px">${p.method}</span><span class="truncate" style="font-size:12px">${escapeHtml(p.label)}</span></button>`
  ).join('');
  host.querySelectorAll('#ax-presets [data-pi]').forEach(b => {
    b.addEventListener('click', () => {
      const p = PRESETS[+b.dataset.pi];
      if (!p) return;
      host.querySelector('#ax-method').value = p.method;
      host.querySelector('#ax-path').value   = p.path;
      host.querySelector('#ax-body').value   = p.body || '';
    });
  });
}

async function send(host) {
  const method = host.querySelector('#ax-method').value;
  const path   = host.querySelector('#ax-path').value.trim();
  const url    = buildUrl(host, path, host.querySelector('#ax-params').value);
  const bodyTxt = host.querySelector('#ax-body').value.trim();
  const headers = parseKVLines(host.querySelector('#ax-headers').value, ':');

  if (!path) return err('path required');

  host.querySelector('#ax-status').textContent = '…';
  host.querySelector('#ax-resp-meta').textContent = 'sending…';
  const t0 = performance.now();

  // jraw uses fetch but lib helper doesn't accept custom headers; do it directly for full control.
  const init = { method };
  const hdr = { Accept: 'application/json' };
  if (bodyTxt && method !== 'GET' && method !== 'HEAD') {
    hdr['Content-Type'] = 'application/json';
    init.body = bodyTxt;
  }
  for (const [k, v] of headers) hdr[k] = v;
  init.headers = hdr;

  try {
    const r = await fetch(url, init);
    const text = await r.text();
    let parsed; try { parsed = text ? JSON.parse(text) : null; } catch { parsed = text; }
    const dt = Math.round(performance.now() - t0);
    lastResponse = { status: r.status, body: parsed, raw: text };
    lastRequest  = { method, url, headers: Object.entries(hdr), body: bodyTxt };

    const pill = host.querySelector('#ax-status');
    pill.classList.remove('ok', 'warn', 'err', 'muted');
    pill.classList.add(r.ok ? 'ok' : (r.status >= 500 ? 'err' : 'warn'));
    pill.textContent = `${r.status}`;

    host.querySelector('#ax-resp-meta').textContent =
      `${method} ${url} · ${dt}ms · ${text.length}b · ${r.headers.get('content-type') || ''}`;
    renderResponse(host);

    history.push({ method, path, body: bodyTxt, status: r.status });
    renderHistory(host);
    ok(`${method} ${path} → ${r.status}`);
  } catch (e) {
    host.querySelector('#ax-status').textContent = 'ERR';
    host.querySelector('#ax-resp-meta').textContent = 'network: ' + e.message;
    host.querySelector('#ax-out').textContent = e.message;
    err(e.message);
  }
}

function applyShareHash(host) {
  const m = location.hash.match(/api\?(.*)$/);
  if (!m) return;
  const p = new URLSearchParams(m[1]);
  if (p.get('m')) host.querySelector('#ax-method').value = p.get('m');
  if (p.get('p')) host.querySelector('#ax-path').value = p.get('p');
  if (p.get('b')) try { host.querySelector('#ax-body').value = atob(p.get('b')); } catch {}
}

registerPane('api', {
  label: 'API Explorer',
  init(host) {
    host.innerHTML = TPL;
    renderPresets(host);
    renderHistory(host);
    applyShareHash(host);

    host.querySelector('#ax-send').addEventListener('click', () => send(host));
    host.querySelector('#ax-path').addEventListener('keydown', e => { if (e.key === 'Enter') send(host); });

    // tab switching
    host.querySelectorAll('#ax-tabs button').forEach(b => {
      b.addEventListener('click', () => {
        host.querySelectorAll('#ax-tabs button').forEach(x => x.classList.toggle('active', x === b));
        host.querySelectorAll('[data-pane-tab]').forEach(el => el.style.display = el.dataset.paneTab === b.dataset.tab ? '' : 'none');
      });
    });

    host.querySelector('#ax-fmt').addEventListener('click', () => {
      const ta = host.querySelector('#ax-body');
      try { ta.value = JSON.stringify(JSON.parse(ta.value), null, 2); } catch (e) { err('bad JSON: ' + e.message); }
    });
    host.querySelector('#ax-copy-body').addEventListener('click', () => copyToClipboard(host.querySelector('#ax-body').value));
    host.querySelector('#ax-copy-resp').addEventListener('click', () => copyToClipboard(host.querySelector('#ax-out').textContent));
    host.querySelector('#ax-as-curl').addEventListener('click', () => {
      const url    = buildUrl(host, host.querySelector('#ax-path').value, host.querySelector('#ax-params').value);
      const method = host.querySelector('#ax-method').value;
      const body   = host.querySelector('#ax-body').value;
      const headers = parseKVLines(host.querySelector('#ax-headers').value, ':');
      copyToClipboard(asCurl({ method, url, body, headers }), 'curl copied');
    });
    host.querySelector('#ax-resp-pretty').addEventListener('click', () => { prettyMode = !prettyMode; renderResponse(host); });
    host.querySelector('#ax-clear-hist').addEventListener('click', () => { history.clear(); renderHistory(host); ok('history cleared'); });
    host.querySelector('#ax-share').addEventListener('click', () => {
      const m = host.querySelector('#ax-method').value;
      const p = host.querySelector('#ax-path').value;
      const b = host.querySelector('#ax-body').value;
      const usp = new URLSearchParams({ m, p });
      if (b) usp.set('b', btoa(b));
      const url = `${location.origin}/#api?${usp.toString()}`;
      copyToClipboard(url, 'share link copied');
    });
  },
});
