// Live API client + WebSocket subscriber.
//
// When the page is served by the bridge, every request goes same-origin —
// no CORS hassle. When the page is opened from the local file system (i.e.
// the in-project prototype), these fetches fail and the app falls back to
// mock data (see app.jsx for the probe).

const _base = "";   // same-origin

async function _fetch(path, opts = {}) {
  const res = await fetch(_base + path, {
    credentials: "same-origin",
    headers: { "Content-Type": "application/json", ...(opts.headers || {}) },
    ...opts,
    body: opts.body ? JSON.stringify(opts.body) : undefined
  });
  if (!res.ok) {
    const err = await res.json().catch(() => ({ error: res.statusText }));
    const e = new Error(err.error || res.statusText);
    e.status = res.status;
    throw e;
  }
  if (res.status === 204) return null;
  return res.json();
}

const API = {
  // ---- auth ----
  me: () => _fetch("/api/auth/me"),
  login: (handle, password) => _fetch("/api/auth/login", { method: "POST", body: { handle, password } }),
  logout: () => _fetch("/api/auth/logout", { method: "POST" }),
  changePassword: (current, next) => _fetch("/api/auth/password", { method: "POST", body: { current, next } }),

  // ---- staff (owner only) ----
  staff: () => _fetch("/api/staff"),
  addStaff: (body) => _fetch("/api/staff", { method: "POST", body }),
  setStaffRole: (id, role) => _fetch(`/api/staff/${id}/role`, { method: "POST", body: { role } }),
  resetStaffPassword: (id, password) => _fetch(`/api/staff/${id}/password`, { method: "POST", body: { password } }),
  removeStaff: (id) => _fetch(`/api/staff/${id}`, { method: "DELETE" }),

  // ---- servers ----
  servers: () => _fetch("/api/servers"),
  server: (id) => _fetch(`/api/servers/${id}`),
  kick: (id, target, reason) => _fetch(`/api/servers/${id}/kick`, { method: "POST", body: { target, reason } }),
  ban: (id, body) => _fetch(`/api/servers/${id}/ban`, { method: "POST", body }),
  pm: (id, target, message) => _fetch(`/api/servers/${id}/pm`, { method: "POST", body: { target, message } }),
  broadcast: (id, message, scope = "server") => _fetch(`/api/servers/${id}/broadcast`, { method: "POST", body: { message, scope } }),
  power: (id, action, reason) => _fetch(`/api/servers/${id}/power`, { method: "POST", body: { action, reason } }),

  // ---- notes / audit / bans ----
  notes: (guid) => _fetch(`/api/notes/${encodeURIComponent(guid)}`),
  addNote: (guid, body) => _fetch(`/api/notes/${encodeURIComponent(guid)}`, { method: "POST", body: { body } }),
  audit: () => _fetch("/api/audit"),
  bans: () => _fetch("/api/bans"),
  revoke: (id) => _fetch(`/api/bans/${id}`, { method: "DELETE" }),

  // ---- live updates ----
  subscribe(onMessage) {
    const proto = location.protocol === "https:" ? "wss" : "ws";
    const ws = new WebSocket(`${proto}://${location.host}/ws`);
    ws.onmessage = (e) => {
      try { onMessage(JSON.parse(e.data)); } catch {}
    };
    ws.onclose = () => {
      // reconnect with backoff
      setTimeout(() => API.subscribe(onMessage), 2000);
    };
    return ws;
  }
};

// Probe to decide live vs mock at boot.
async function probe() {
  try {
    const r = await fetch("/api/auth/me", { credentials: "same-origin" });
    if (r.status === 200) return { mode: "live", user: (await r.json()).user };
    if (r.status === 401) return { mode: "live", user: null };
    return { mode: "mock" };
  } catch {
    return { mode: "mock" };
  }
}

Object.assign(window, { API, probe });
