// api-client.jsx — thin client that talks to the Cloudflare Worker backend.
// Falls back to localStorage when the API isn't reachable (offline / dev / not yet deployed),
// so the app keeps working as a single-browser-only experience until the backend is live.

const API_BASE = (() => {
  // In production we'll be served from årshjulet.husfar.org; the Worker lives at /api
  // (path-routed by Cloudflare Pages Functions or by the Worker on the same domain).
  // Override via ?api=https://other-host for local backend testing.
  const params = new URLSearchParams(window.location.search);
  const override = params.get('api');
  if (override) return override.replace(/\/$/, '');
  return ''; // same origin, /api/...
})();

const LS = {
  token:   'aarshjul.token',
  email:   'aarshjul.email',
  who:     'aarshjul.who',
  state:   'aarshjul.state',   // mirror of server state for offline
  members: 'aarshjul.members', // for the invite-flow added drengs before backend
};

async function apiFetch(path, opts = {}) {
  const token = localStorage.getItem(LS.token);
  const headers = { 'Content-Type': 'application/json', ...(opts.headers || {}) };
  if (token) headers['Authorization'] = `Bearer ${token}`;
  const res = await fetch(`${API_BASE}${path}`, { ...opts, headers });
  if (!res.ok) throw new Error(`API ${res.status}: ${await res.text().catch(() => '')}`);
  return res.json();
}

// ── Auth ──────────────────────────────────────────────────────────────────────

async function requestMagicLink(email) {
  // POST /api/auth/request-link { email }
  // Worker checks email is whitelisted, generates a one-time code, emails it.
  // Returns { ok: true, demo_code?: string } — in dev the worker echoes the code.
  try {
    return await apiFetch('/api/auth/request-link', {
      method: 'POST',
      body: JSON.stringify({ email: email.toLowerCase().trim() }),
    });
  } catch (err) {
    // Backend not deployed yet → fake the flow so the UI is testable
    console.warn('[api] backend offline, faking magic-link request', err);
    return { ok: true, demo: true };
  }
}

async function verifyMagicLink(email, code) {
  // POST /api/auth/verify { email, code } → { token, member: {...} }
  try {
    const data = await apiFetch('/api/auth/verify', {
      method: 'POST',
      body: JSON.stringify({ email: email.toLowerCase().trim(), code: code.trim() }),
    });
    if (data.token) localStorage.setItem(LS.token, data.token);
    if (data.email) localStorage.setItem(LS.email, data.email);
    if (data.member?.id) localStorage.setItem(LS.who, data.member.id);
    return data;
  } catch (err) {
    // Offline fallback: check the email locally against MEMBERS list
    console.warn('[api] backend offline, falling back to local whitelist match', err);
    const all = getAllMembers();
    const member = all.find((m) => m.email?.toLowerCase() === email.toLowerCase().trim());
    if (!member) throw new Error('Email ikke fundet i drenge-listen');
    localStorage.setItem(LS.email, email.toLowerCase().trim());
    localStorage.setItem(LS.who, member.id);
    return { token: 'offline', member, offline: true };
  }
}

function logout() {
  localStorage.removeItem(LS.token);
  localStorage.removeItem(LS.email);
  localStorage.removeItem(LS.who);
}

function getCurrentUserId() {
  return localStorage.getItem(LS.who) || null;
}

async function loginPassword(email, password) {
  const data = await apiFetch('/api/auth/login', {
    method: 'POST',
    body: JSON.stringify({ email: email.toLowerCase().trim(), password }),
  });
  if (data.token) localStorage.setItem(LS.token, data.token);
  if (data.email) localStorage.setItem(LS.email, data.email);
  if (data.member?.id) localStorage.setItem(LS.who, data.member.id);
  return data;
}

async function registerPassword(email, password) {
  const data = await apiFetch('/api/auth/register', {
    method: 'POST',
    body: JSON.stringify({ email: email.toLowerCase().trim(), password }),
  });
  if (data.token) localStorage.setItem(LS.token, data.token);
  if (data.email) localStorage.setItem(LS.email, data.email);
  if (data.member?.id) localStorage.setItem(LS.who, data.member.id);
  return data;
}

// ── Members (whitelist) ───────────────────────────────────────────────────────
// Backend SHOULD own this. Frontend keeps a local-only "added drenge" list so
// we can iterate before the worker exists.

function getAllMembers() {
  const baseline = window.MEMBERS || [];
  let extra = [];
  try {
    extra = JSON.parse(localStorage.getItem(LS.members) || '[]');
  } catch {}
  // Merge: extra members can override email for an existing id
  const byId = new Map(baseline.map((m) => [m.id, { ...m }]));
  extra.forEach((m) => {
    if (byId.has(m.id)) byId.set(m.id, { ...byId.get(m.id), ...m });
    else byId.set(m.id, m);
  });
  return [...byId.values()];
}

async function inviteMember({ id, name, email, initials }) {
  // POST /api/members { id, name, email } when backend exists.
  // Until then, persist locally so the UI works.
  const payload = { id, name, email: email.toLowerCase().trim(), initials };
  try {
    await apiFetch('/api/members', { method: 'POST', body: JSON.stringify(payload) });
  } catch (err) {
    console.warn('[api] backend offline, storing invite locally', err);
    const all = JSON.parse(localStorage.getItem(LS.members) || '[]');
    const without = all.filter((m) => m.id !== id);
    without.push(payload);
    localStorage.setItem(LS.members, JSON.stringify(without));
  }
  return payload;
}

// ── Shared state (RSVP/chores/expenses) ──────────────────────────────────────

async function fetchState() {
  try {
    return await apiFetch('/api/state');
  } catch (err) {
    console.warn('[api] backend offline, using localStorage mirror', err);
    try {
      return JSON.parse(localStorage.getItem(LS.state) || 'null');
    } catch { return null; }
  }
}

async function pushState(patch) {
  // PATCH /api/state — Worker merges and broadcasts via SSE.
  try {
    return await apiFetch('/api/state', {
      method: 'PATCH',
      body: JSON.stringify(patch),
    });
  } catch (err) {
    // Offline: just mirror locally
    let cur = {};
    try { cur = JSON.parse(localStorage.getItem(LS.state) || '{}'); } catch {}
    const merged = deepMerge(cur, patch);
    localStorage.setItem(LS.state, JSON.stringify(merged));
    return merged;
  }
}

function deepMerge(a, b) {
  if (!a) return b;
  if (!b) return a;
  if (Array.isArray(b)) return b; // arrays replace, not merge
  if (typeof b !== 'object') return b;
  const out = { ...a };
  Object.keys(b).forEach((k) => { out[k] = deepMerge(a[k], b[k]); });
  return out;
}

window.AarshjulAPI = {
  loginPassword, registerPassword,
  requestMagicLink, verifyMagicLink, logout, getCurrentUserId,
  getAllMembers, inviteMember,
  fetchState, pushState,
  LS,
};
