// panes/websocket.js — generic WebSocket console with URL switcher, presets,
// JSON pretty-print, auto-reconnect, message history.
import { registerPane } from '../app.js';
import { escapeHtml } from '../lib/api.js';
import { ok, err, copyToClipboard, historyStore } from '../lib/ui.js';

const ENDPOINTS = [
  { label: 'echo',    url: '/ws/echo' },
];

const sendHistory = historyStore('cx_ws_send_history', 30);

const TPL = `
  <div class="pane-head">
    <div><div class="title">WebSocket</div><div class="sub">Connect to any WebSocket endpoint and inspect frames.</div></div>
    <div class="grow"></div>
    <span class="pill" id="ws-pill"><span class="dot"></span><span id="ws-state">idle</span></span>
  </div>

  <div class="card mb-3">
    <div class="flex gap-2 mb-2">
      <select class="input" id="ws-preset" style="max-width:160px"></select>
      <input class="input" id="ws-url" placeholder="ws:// or wss:// URL"/>
      <button class="btn btn-primary" id="ws-connect">Connect</button>
      <button class="btn btn-secondary" id="ws-disconnect">Disconnect</button>
    </div>
    <div class="flex gap-3 items-center" style="flex-wrap:wrap">
      <label class="check"><input type="checkbox" id="ws-autoreconnect"/> auto-reconnect</label>
      <label class="check"><input type="checkbox" id="ws-pretty" checked/> pretty-print JSON</label>
      <label class="check"><input type="checkbox" id="ws-ts" checked/> show timestamps</label>
      <span class="muted xsmall" id="ws-stats">0 in · 0 out · 0 b</span>
      <div class="grow"></div>
      <button class="btn btn-ghost" id="ws-clear">Clear log</button>
      <button class="btn btn-ghost" id="ws-copy">Copy log</button>
    </div>
  </div>

  <div class="grid" style="grid-template-columns: 1fr 280px; gap: 16px;">
    <div class="card">
      <div class="card-title"><h2>Frames</h2><span class="muted mono xsmall" id="ws-frame-count">0</span></div>
      <pre class="console" id="ws-log" style="height:48vh; max-height:60vh; overflow:auto"></pre>
      <form id="ws-form" class="flex gap-2 mt-3">
        <textarea class="input" id="ws-input" rows="2" placeholder="message — Enter to send, Shift+Enter for newline"></textarea>
        <button class="btn btn-primary" type="submit">Send</button>
      </form>
    </div>
    <div class="card">
      <div class="card-title"><h2>Send presets</h2></div>
      <div class="btn-row mb-3">
        <button class="btn btn-secondary" data-q='ping'>ping</button>
        <button class="btn btn-secondary" data-q='{"cmd":"hello"}'>hello (JSON)</button>
        <button class="btn btn-secondary" data-q='subscribe events'>subscribe</button>
      </div>
      <div class="card-title"><h2>History</h2><span class="muted xsmall" id="ws-hist-count">0</span></div>
      <div id="ws-history" style="max-height:30vh; overflow:auto"></div>
    </div>
  </div>
`;

let ws = null;
let stats = { in: 0, out: 0, bytes: 0 };
let reconnectTimer = null;

function setState(host, state, klass) {
  host.querySelector('#ws-state').textContent = state;
  const p = host.querySelector('#ws-pill');
  p.classList.remove('ok', 'err', 'warn', 'info', 'muted');
  p.classList.add(klass || 'muted');
}

function updateStats(host) {
  host.querySelector('#ws-stats').textContent = `${stats.in} in · ${stats.out} out · ${stats.bytes} b`;
  host.querySelector('#ws-frame-count').textContent = `${stats.in + stats.out}`;
}

function maybePretty(host, raw) {
  if (!host.querySelector('#ws-pretty').checked) return raw;
  try { return JSON.stringify(JSON.parse(raw), null, 2); } catch { return raw; }
}

function append(host, dir, raw) {
  const pre = host.querySelector('#ws-log');
  const showTs = host.querySelector('#ws-ts').checked;
  const text = maybePretty(host, raw);
  const ts = showTs ? `[${new Date().toISOString().slice(11, 23)}] ` : '';
  const arrow = dir === 'in' ? '←' : dir === 'out' ? '→' : '·';
  pre.textContent += `${ts}${arrow} ${text}\n`;
  pre.scrollTop = pre.scrollHeight;
}

function defaultUrl() {
  const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
  return `${proto}//${location.host}/ws/echo`;
}

function connect(host) {
  if (ws && ws.readyState <= 1) return;
  let url = host.querySelector('#ws-url').value.trim();
  if (url.startsWith('/')) {
    const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
    url = `${proto}//${location.host}${url}`;
  }
  if (!url) url = defaultUrl();
  setState(host, 'connecting…', 'warn');
  append(host, '·', `connecting ${url}`);
  try { ws = new WebSocket(url); } catch (e) { setState(host, 'error', 'err'); append(host, '·', e.message); return; }
  ws.onopen    = () => { setState(host, 'connected', 'ok'); append(host, '·', 'connected'); };
  ws.onclose   = (e) => {
    setState(host, `closed (${e.code})`, 'err');
    append(host, '·', `closed code=${e.code} reason=${e.reason}`);
    if (host.querySelector('#ws-autoreconnect').checked) {
      reconnectTimer = setTimeout(() => connect(host), 2000);
    }
  };
  ws.onerror   = () => append(host, '·', 'error');
  ws.onmessage = (e) => { stats.in++; stats.bytes += (e.data?.length || 0); updateStats(host); append(host, 'in', e.data); };
}
function disconnect() {
  if (reconnectTimer) { clearTimeout(reconnectTimer); reconnectTimer = null; }
  try { ws?.close(); } catch {}
}

function renderHistory(host) {
  const items = sendHistory.list();
  host.querySelector('#ws-hist-count').textContent = String(items.length);
  host.querySelector('#ws-history').innerHTML = items.length
    ? items.map((h, i) => `<button class="nav-item" data-hi="${i}"><span class="truncate" style="font-size:12px">${escapeHtml(h.text)}</span></button>`).join('')
    : '<div class="muted xsmall p-2">empty</div>';
  host.querySelectorAll('#ws-history [data-hi]').forEach(b => {
    b.addEventListener('click', () => {
      host.querySelector('#ws-input').value = sendHistory.list()[+b.dataset.hi]?.text || '';
    });
  });
}

function send(host, text) {
  if (!ws || ws.readyState !== 1) return err('not connected');
  ws.send(text);
  stats.out++; stats.bytes += text.length; updateStats(host);
  append(host, 'out', text);
  sendHistory.push({ text });
  renderHistory(host);
}

registerPane('websocket', {
  label: 'WebSocket',
  init(host) {
    host.innerHTML = TPL;
    const sel = host.querySelector('#ws-preset');
    sel.innerHTML = ENDPOINTS.map((e, i) => `<option value="${i}">${escapeHtml(e.label)} (${escapeHtml(e.url)})</option>`).join('');
    host.querySelector('#ws-url').value = defaultUrl();
    sel.addEventListener('change', () => {
      const e = ENDPOINTS[+sel.value]; if (!e) return;
      const proto = location.protocol === 'https:' ? 'wss:' : 'ws:';
      host.querySelector('#ws-url').value = `${proto}//${location.host}${e.url}`;
    });

    host.querySelector('#ws-connect').addEventListener('click', () => connect(host));
    host.querySelector('#ws-disconnect').addEventListener('click', disconnect);
    host.querySelector('#ws-clear').addEventListener('click', () => {
      host.querySelector('#ws-log').textContent = '';
      stats = { in: 0, out: 0, bytes: 0 }; updateStats(host);
    });
    host.querySelector('#ws-copy').addEventListener('click', () => copyToClipboard(host.querySelector('#ws-log').textContent));

    host.querySelector('#ws-form').addEventListener('submit', (e) => {
      e.preventDefault();
      const v = host.querySelector('#ws-input').value;
      if (!v) return;
      send(host, v);
      host.querySelector('#ws-input').value = '';
    });
    host.querySelector('#ws-input').addEventListener('keydown', (e) => {
      if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); host.querySelector('#ws-form').requestSubmit(); }
    });
    host.querySelectorAll('[data-q]').forEach(b => b.addEventListener('click', () => send(host, b.dataset.q)));

    renderHistory(host);
    updateStats(host);
  },
});
