// Cookie-backed API client. Replaces the old Basic-auth flow.
//
// Every authenticated route uses the `pulse_session` httpOnly cookie,
// which the browser attaches automatically on same-origin requests
// (including <img src="/api/screenshots/...">). All `apiFetch` needs to
// do is set `credentials: 'same-origin'` and surface 401s as a
// `pulse:session-lost` window event so `app.jsx` can drop to the
// login screen.
//
// Legacy note: `adminFetch` is kept as an alias so the many existing
// call-sites (admin.jsx, notifications.jsx, admin_editor.jsx,
// node_detail.jsx, app.jsx pause toggle) don't need an immediate edit.

// One-time migration: wipe any leftover sessionStorage token from the
// old Basic-auth implementation so it can't confuse later state.
try { sessionStorage.removeItem('pulse.adminAuth'); } catch (_) {}

async function apiFetch(path, init = {}) {
  const headers = new Headers(init.headers || {});
  if (init.body && !headers.has('Content-Type')) headers.set('Content-Type', 'application/json');
  const res = await fetch(path, { credentials: 'same-origin', ...init, headers });
  if (res.status === 401) {
    window.dispatchEvent(new CustomEvent('pulse:session-lost'));
    throw new Error('unauthorized');
  }
  if (!res.ok) {
    let msg = `http ${res.status}`;
    try { const j = await res.json(); if (j.detail) msg = j.detail; } catch (_) {}
    throw new Error(msg);
  }
  if (res.status === 204) return null;
  const ctype = res.headers.get('Content-Type') || '';
  if (!ctype.includes('json')) return null;
  return res.json();
}

async function signOut() {
  try { await apiFetch('/api/auth/logout', { method: 'POST' }); }
  catch (_) { /* even on failure we want the UI to drop out */ }
  window.dispatchEvent(new CustomEvent('pulse:session-lost'));
}

async function login(username, password) {
  const r = await fetch('/api/auth/login', {
    method: 'POST',
    credentials: 'same-origin',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ username, password }),
  });
  if (r.status === 401) throw new Error('invalid credentials');
  if (r.status === 429) throw new Error('too many attempts — wait a minute');
  if (!r.ok) {
    let msg = `http ${r.status}`;
    try { const j = await r.json(); if (j.detail) msg = j.detail; } catch (_) {}
    throw new Error(msg);
  }
  return r.json();
}

// Back-compat — do NOT remove; many components import under the old name.
// Since the app-level login gate runs before any admin view mounts, the
// `auth.authed` branches in admin.jsx / notifications.jsx are always
// true here. The stub prompt is a defence against any stale fallback.
const adminFetch = apiFetch;
const useAdminAuth = () => ({ authed: true, signIn: () => {}, signOut });
function AdminAuthPrompt() {
  // If something ever renders this, the login gate has been bypassed
  // in error. Bounce to the login screen immediately.
  React.useEffect(() => {
    window.dispatchEvent(new CustomEvent('pulse:session-lost'));
  }, []);
  return null;
}

Object.assign(window, { apiFetch, adminFetch, signOut, login, useAdminAuth, AdminAuthPrompt });
