/* global React */
/* ==========================================================
   api.jsx — thin client for the Stage 2 back end.

   All fetches go through `request()`, which:
     - prefixes the path with /api
     - always sends credentials (so the session cookie is included)
     - JSON-encodes the body and sets Content-Type
     - parses the response as JSON when there is one
     - throws an ApiError with the server's {error, message} on
       any non-2xx response

   Components should `try { … } catch (e) { if (e instanceof window.ApiError) … }`.
   ========================================================== */

class ApiError extends Error {
  constructor(status, code, message) {
    super(message || code || 'Request failed');
    this.status = status;
    this.code = code || 'unknown';
  }
}
window.ApiError = ApiError;

async function request(path, options = {}) {
  const opts = {
    credentials: 'same-origin',
    headers: { 'Accept': 'application/json' },
    ...options,
  };
  if (options.body !== undefined && !(options.body instanceof FormData)) {
    opts.headers['Content-Type'] = 'application/json';
    opts.body = JSON.stringify(options.body);
  }
  const res = await fetch('/api' + path, opts);
  if (res.status === 204) return null;
  let payload = null;
  const ct = res.headers.get('content-type') || '';
  if (ct.includes('application/json')) {
    try { payload = await res.json(); } catch (e) { /* ignore */ }
  } else {
    try { payload = await res.text(); } catch (e) { /* ignore */ }
  }
  if (!res.ok) {
    const err = (payload && typeof payload === 'object') ? payload : {};
    throw new ApiError(res.status, err.error, err.message);
  }
  return payload;
}

const api = {
  /* ---------- auth ---------- */
  // Always resolves (server returns 204 even on bad input — never leaks).
  requestLink: (email) => request('/auth/request', { method: 'POST', body: { email } }),
  // Returns the current user, or null if not signed in.
  me: async () => {
    try { return await request('/me'); }
    catch (e) {
      if (e instanceof ApiError && e.status === 401) return null;
      throw e;
    }
  },
  signOut: () => request('/auth/signout', { method: 'POST' }),

  /* ---------- members ---------- */
  listMembers: () => request('/members'),
  invite: (payload) => request('/members/invite', { method: 'POST', body: payload }),

  /* ---------- ideas ---------- */
  listIdeas: () => request('/ideas'),
  getIdea: (id) => request('/ideas/' + encodeURIComponent(id)),
  createIdea: (payload) => request('/ideas', { method: 'POST', body: payload }),
  editIdea: (id, patch) =>
    request('/ideas/' + encodeURIComponent(id), { method: 'PATCH', body: patch }),
  postComment: (id, text) =>
    request('/ideas/' + encodeURIComponent(id) + '/comments', {
      method: 'POST', body: { text },
    }),
  rankAxis: (id, axis, value) =>
    request('/ideas/' + encodeURIComponent(id) + '/ranking/' + axis, {
      method: 'PUT', body: { value },
    }),

  /* ---------- claims / ownership ---------- */
  requestClaim: (id, message) =>
    request('/ideas/' + encodeURIComponent(id) + '/claim', {
      method: 'POST', body: { message: message || '' },
    }),
  withdrawClaim: (id) =>
    request('/ideas/' + encodeURIComponent(id) + '/claim', { method: 'DELETE' }),
  stepBack: (id) =>
    request('/ideas/' + encodeURIComponent(id) + '/step-back', { method: 'POST' }),

  /* ---------- LEAF moderation ---------- */
  approveClaim: (id, memberId) =>
    request('/ideas/' + encodeURIComponent(id) + '/claim/' + encodeURIComponent(memberId) + '/approve', { method: 'POST' }),
  declineClaim: (id, memberId) =>
    request('/ideas/' + encodeURIComponent(id) + '/claim/' + encodeURIComponent(memberId) + '/decline', { method: 'POST' }),
  setStage: (id, stage) =>
    request('/ideas/' + encodeURIComponent(id) + '/stage', {
      method: 'PUT', body: { stage },
    }),
  setLeafResponse: (id, text) =>
    request('/ideas/' + encodeURIComponent(id) + '/leaf-response', {
      method: 'PUT', body: { text },
    }),
  bulkImport: (ideas) =>
    request('/ideas/bulk-import', { method: 'POST', body: { ideas } }),
  pendingClaims: () => request('/admin/pending-claims'),
  insights: () => request('/admin/insights'),
};

window.api = api;
