// lib/api.js — fetch helpers, MCP base resolver, service catalogue.

export const MCP_BASE = (() => {
  const fromAttr = document.body?.dataset?.mcpBase;
  return (fromAttr || 'http://127.0.0.1:8082').replace(/\/+$/, '');
})();

class HttpError extends Error {
  constructor(status, body) {
    super(`HTTP ${status}`);
    this.status = status;
    this.body = body;
  }
}

async function parseResp(res) {
  const text = await res.text();
  let body;
  try { body = text ? JSON.parse(text) : null; } catch { body = text; }
  if (!res.ok) throw new HttpError(res.status, body);
  return body;
}

export async function jget(url, opts = {}) {
  const res = await fetch(url, { ...opts, headers: { Accept: 'application/json', ...(opts.headers || {}) } });
  return parseResp(res);
}

export async function jpost(url, body, opts = {}) {
  const res = await fetch(url, {
    method: 'POST',
    ...opts,
    headers: { 'Content-Type': 'application/json', Accept: 'application/json', ...(opts.headers || {}) },
    body: body == null ? undefined : (typeof body === 'string' ? body : JSON.stringify(body)),
  });
  return parseResp(res);
}

export async function jdelete(url, opts = {}) {
  const res = await fetch(url, { method: 'DELETE', ...opts });
  return parseResp(res);
}

export async function jraw(method, url, body) {
  const init = { method };
  if (body && body !== '') {
    init.headers = { 'Content-Type': 'application/json' };
    init.body = body;
  }
  const res = await fetch(url, init);
  const text = await res.text();
  let parsed;
  try { parsed = text ? JSON.parse(text) : null; } catch { parsed = text; }
  return { status: res.status, ok: res.ok, body: parsed, raw: text };
}

// ---------- MCP shortcuts (used by agent/inbox/tools panes) ----------
export const mcp = {
  status:   () => jget(`${MCP_BASE}/status`),
  events:   (limit = 150) => jget(`${MCP_BASE}/events?limit=${limit}`),
  tools:    () => jget(`${MCP_BASE}/tools`),
  call:     (name, args = {}) => jpost(`${MCP_BASE}/tools/${name}`, args),
};

// ---------- Service registry (used by dashboard health pills) ----------
export const services = [
  { id: 'crow',      label: 'Crow API',  health: '/api/health'              },
  { id: 'mcp',       label: 'MCP',       health: `${MCP_BASE}/status`       },
  { id: 'diffusion', label: 'Diffusion', health: '/api/diffusion/v1/health' },
  { id: 'demand',    label: 'Demand',    health: '/api/demand/healthz'      },
  { id: 'lang',      label: 'Lang',      health: '/api/lang/healthz'        },
  { id: 'slack',     label: 'Slack',     health: '/api/slack/healthz'       },
];

export async function probeService(svc) {
  const t0 = performance.now();
  try {
    await jget(svc.health);
    return { ok: true, ms: Math.round(performance.now() - t0) };
  } catch (err) {
    return { ok: false, ms: Math.round(performance.now() - t0), error: err.status || err.message };
  }
}

export function escapeHtml(s) {
  return String(s ?? '').replace(/[&<>"']/g, c => ({
    '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;'
  }[c]));
}

export function formatUptime(seconds) {
  if (seconds == null || isNaN(seconds)) return '—';
  seconds = Math.max(0, Math.floor(seconds));
  const d = Math.floor(seconds / 86400); seconds %= 86400;
  const h = Math.floor(seconds / 3600);  seconds %= 3600;
  const m = Math.floor(seconds / 60);    const s = seconds % 60;
  if (d) return `${d}d ${h}h ${m}m`;
  if (h) return `${h}h ${m}m ${s}s`;
  if (m) return `${m}m ${s}s`;
  return `${s}s`;
}

export function statusClass(status) {
  if (!status) return 'muted';
  const s = String(status).toLowerCase();
  if (s.includes('success') || s === 'ok' || s === 'ready' || s === 'healthy') return 'ok';
  if (s.includes('error') || s.includes('fail')) return 'err';
  if (s.includes('warn') || s.includes('pending') || s.includes('queued') || s.includes('running')) return 'warn';
  return 'info';
}

export { HttpError };
