// MA-Trip — API client for Cloudflare Worker backend.
// Session cookie (mt_session) carried by browser via credentials:"include".

// Defaults to the production custom domain. Override per-environment by
// injecting `<script>window.MA_API_BASE = "https://..."</script>` BEFORE
// api.jsx loads in index.html — e.g. set to a *.workers.dev URL during
// active debugging. workers_dev=false on the prod worker means the
// fallback only resolves through the bound custom domain.
const API_BASE = (window.MA_API_BASE || "https://api.matrip.io").replace(/\/$/, "");

// Turnstile site key — REQUIRED in production. Inject the real key via an inline
// <script>window.MA_TURNSTILE_SITE_KEY = "0x...";</script> in index.html BEFORE
// api.jsx loads. Without a real key on a non-test host, the widget is disabled
// and signup/login will not include a turnstile token — the worker MUST then
// reject (its TURNSTILE_SECRET must be set so verifyTurnstile fails closed).
const TURNSTILE_TEST_KEY = "1x00000000000000000000AA"; // always-pass — DEV ONLY
const IS_TEST_HOST = /^(localhost|127\.0\.0\.1|10\.|192\.168\.|.*\.local)$/.test(location.hostname);
const TURNSTILE_SITE_KEY = window.MA_TURNSTILE_SITE_KEY || (IS_TEST_HOST ? TURNSTILE_TEST_KEY : null);
if (!TURNSTILE_SITE_KEY) {
  console.warn(
    "[MaApi] No Turnstile site key for host '" + location.hostname + "'. " +
    "Set window.MA_TURNSTILE_SITE_KEY before api.jsx loads."
  );
}

async function apiFetch(path, opts = {}) {
  const r = await fetch(API_BASE + path, {
    credentials: "include",
    method: opts.method || "GET",
    headers: {
      "Content-Type": "application/json",
      ...(opts.headers || {}),
    },
    body: opts.body,
  });
  let body = null;
  let bodyText = null;
  try {
    bodyText = await r.text();
    body = bodyText ? JSON.parse(bodyText) : null;
  } catch (e) {
    // Non-JSON response (e.g. CF HTML error page) — keep raw text for diagnosis.
    console.warn("[MaApi] non-JSON response", r.status, path, bodyText && bodyText.slice(0, 200));
  }
  if (!r.ok) {
    const err = new Error((body && body.error) || ("http-" + r.status));
    err.status = r.status;
    err.code = body && body.error;
    err.meta = body && body.meta;
    err.bodyText = bodyText;
    throw err;
  }
  return body;
}

const MaApi = {
  API_BASE,
  TURNSTILE_SITE_KEY,
  signup: (email, name, password, turnstile) =>
    apiFetch("/api/auth/signup", { method: "POST", body: JSON.stringify({ email, name, password, turnstile }) }),
  login: (email, password, turnstile) =>
    apiFetch("/api/auth/login", { method: "POST", body: JSON.stringify({ email, password, turnstile }) }),
  confirmEmail: (token) =>
    apiFetch("/api/auth/confirm", { method: "POST", body: JSON.stringify({ token }) }),
  me: () => apiFetch("/api/me"),
  logout: () => apiFetch("/api/auth/logout", { method: "POST" }),

  listTrips: () => apiFetch("/api/trips"),
  createTrip: (data) =>
    apiFetch("/api/trips", { method: "POST", body: JSON.stringify({ data }) }),
  getTrip: (id) => apiFetch("/api/trips/" + id),
  updateTrip: (id, data, expectedVersion) =>
    apiFetch("/api/trips/" + id, { method: "PUT", body: JSON.stringify({ data, expectedVersion }) }),
  tripFeed: (id, since) =>
    apiFetch("/api/trips/" + id + "/feed?since=" + (since || 0)),

  createShare: (id, role, ttlSec) =>
    apiFetch("/api/trips/" + id + "/share", { method: "POST", body: JSON.stringify({ role: role || "editor", ttlSec: ttlSec || 7 * 24 * 3600 }) }),
  joinShare: (token) =>
    apiFetch("/api/share/join", { method: "POST", body: JSON.stringify({ token }) }),

  // ---- R2 blobs (photos / voice / documents) ----
  // body is base64url of raw plaintext bytes — worker re-encrypts with
  // ENCRYPTION_KEY (AES-GCM) before R2 put. HTTPS protects transit.
  listBlobs: (tripId) => apiFetch("/api/trips/" + tripId + "/blobs"),
  uploadBlob: (tripId, { kind, mime, body }) =>
    apiFetch("/api/trips/" + tripId + "/blobs", {
      method: "POST",
      body: JSON.stringify({ kind, mime, body }),
    }),
  downloadBlob: async (id) => {
    // Returns a Blob — not JSON. Worker emits the decrypted bytes inline.
    const r = await fetch(API_BASE + "/api/blobs/" + id, {
      credentials: "include",
      method: "GET",
    });
    if (!r.ok) {
      const err = new Error("blob-http-" + r.status);
      err.status = r.status;
      throw err;
    }
    return await r.blob();
  },
  deleteBlob: (id) => apiFetch("/api/blobs/" + id, { method: "DELETE" }),

  health: (deep) => apiFetch("/api/health" + (deep ? "?deep=1" : "")),
};

let _turnstileLoaded = false;
function loadTurnstile() {
  if (_turnstileLoaded) return Promise.resolve();
  return new Promise((resolve, reject) => {
    const s = document.createElement("script");
    s.src = "https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit";
    s.async = true;
    s.defer = true;
    s.onload = () => { _turnstileLoaded = true; resolve(); };
    s.onerror = reject;
    document.head.appendChild(s);
  });
}

function renderTurnstile(container, onToken) {
  if (!window.turnstile || !TURNSTILE_SITE_KEY) return null;
  return window.turnstile.render(container, {
    sitekey: TURNSTILE_SITE_KEY,
    callback: onToken,
    "expired-callback": () => onToken(null),
    "error-callback": () => onToken(null),
    theme: "light",
    size: "normal",
  });
}

window.MaApi = MaApi;
window.loadTurnstile = loadTurnstile;
window.renderTurnstile = renderTurnstile;
