// ============================================================
// data.jsx — reads arete.* tables for the pages.
//
// Signed in: Supabase (schema arete; RLS lets members read).
// Local preview (?preview on localhost): local-data/<table>.json,
// built from the real CSVs by tools/build_local_data.py. That
// folder is git-ignored and never published.
// ============================================================
const PAGE_SIZE = 1000;

function isPreview() {
  return ["localhost", "127.0.0.1"].includes(window.location.hostname)
    && new URLSearchParams(window.location.search).has("preview");
}

// unique sort key per table, for stable paging (keyset: "after the last key")
const TABLE_KEYS = {
  reservations: "id", transactions: "txn_id", recurring_costs: "id", job_runs: "id",
  pacing_weeks: "week_date", market_daily: "date", cleanings: "booking_code",
  todos: "id", categories: "category", tax_deadlines: "id", tax_cpa_questions: "id",
  tax_time_entries: "id", tax_documents: "id", org: "id", routines: "id",
};

async function loadTable(name) {
  if (isPreview()) {
    const res = await fetch("local-data/" + encodeURIComponent(name) + ".json", { cache: "no-store" });
    if (res.status === 404) return [];          // no local file: show the empty state
    if (!res.ok) throw new Error("local-data/" + name + ".json: HTTP " + res.status);
    return res.json();
  }
  const key = TABLE_KEYS[name];
  const table = () => window.supaClient.schema("arete").from(name).select("*");
  if (!key) {
    // small tables (e.g. curves: 24 rows): one read, and refuse to guess if it is full
    const { data, error } = await table().limit(PAGE_SIZE);
    if (error) throw new Error(error.message);
    if (data.length >= PAGE_SIZE) throw new Error(name + ": too many rows for a single read");
    return data;
  }
  const rows = [];
  let last = null;
  for (;;) {
    let q = table().order(key, { ascending: true }).limit(PAGE_SIZE);
    if (last !== null) q = q.gt(key, last);
    const { data, error } = await q;
    if (error) throw new Error(error.message);
    rows.push(...data);
    if (data.length < PAGE_SIZE) return rows;
    last = data[data.length - 1][key];
  }
}

// useTables(["reservations", "curves"]) ->
//   { status: "loading" | "ready" | "error", data: {reservations: [...], ...}, error, reload }
function useTables(names) {
  const key = names.join(",");
  const [state, setState] = React.useState({ status: "loading", data: {} });
  const [tick, setTick] = React.useState(0);
  React.useEffect(() => {
    let alive = true;
    setState((s) => ({ status: "loading", data: s.data }));
    Promise.all(names.map((n) => loadTable(n))).then(
      (lists) => {
        if (!alive) return;
        const data = {};
        names.forEach((n, i) => { data[n] = lists[i]; });
        setState({ status: "ready", data });
      },
      (e) => { if (alive) setState({ status: "error", data: {}, error: String(e?.message || e) }); });
    return () => { alive = false; };
  }, [key, tick]);
  return { ...state, reload: () => setTick((t) => t + 1) };
}

// today's date where the house is ('YYYY-MM-DD', America/New_York)
function todayNY() {
  return new Intl.DateTimeFormat("en-CA", { timeZone: "America/New_York", year: "numeric", month: "2-digit", day: "2-digit" })
    .format(new Date());
}

const money = (v, d = 0) => (v < 0 ? "−" : "") + "$" + Math.abs(Number(v) || 0)
  .toLocaleString("en-US", { minimumFractionDigits: d, maximumFractionDigits: d });

// shared states for a data block
function Loading({ height = 320 }) {
  return <div className="skel" style={{ height }} aria-label="Loading" role="status" />;
}
function LoadError({ error, onRetry }) {
  return (
    <div className="notice" role="alert">
      <b>Could not load this.</b> {error} <button className="linkbtn" onClick={onRetry}>Try again</button>
    </div>
  );
}
function NoData({ children }) {
  return <div className="notice">{children}</div>;
}

Object.assign(window, { useTables, todayNY, money, Loading, LoadError, NoData, isPreview });
