// DataKit — shared read-only loaders for RDB directory data and document
// indexes. The browser fetches from same-origin /api/sheet endpoints (a
// Cloudflare Pages Function proxies the real source server-side), so no source
// IDs or external URLs ever ship in this bundle. No API key, no write access.
//
// Architectural pattern (per RDB):
//   • frequently-updated data  → proxied source, rendered dynamically (headers
//     from row 1, data from row 2 down, auto-growing).
//   • files                    → indexed via a proxied source so the RDB team
//     can self-update without code.
//   • one-off static content   → hard-coded.

// Same-origin proxy endpoint. `src` is a whitelisted key resolved server-side
// (see functions/api/sheet.js); `opts.tab` selects a named tab where relevant.
function apiUrl(src, opts) {
  opts = opts || {};
  let u = "/api/sheet?src=" + encodeURIComponent(src);
  if (opts.tab) u += "&tab=" + encodeURIComponent(opts.tab);
  return u;
}

// --- RFC-4180-ish CSV parser: quoted fields, embedded commas, doubled quotes, newlines ---
function rdbParseCSV(text) {
  const rows = [];
  let row = [], field = "", i = 0, inQ = false;
  while (i < text.length) {
    const c = text[i];
    if (inQ) {
      if (c === '"') {
        if (text[i + 1] === '"') { field += '"'; i += 2; continue; }
        inQ = false; i++; continue;
      }
      field += c; i++; continue;
    }
    if (c === '"') { inQ = true; i++; continue; }
    if (c === ",") { row.push(field); field = ""; i++; continue; }
    if (c === "\r") { i++; continue; }
    if (c === "\n") { row.push(field); rows.push(row); row = []; field = ""; i++; continue; }
    field += c; i++;
  }
  if (field.length || row.length) { row.push(field); rows.push(row); }
  return rows;
}

// Parse into objects keyed by row-1 headers (auto-growing column support).
function rdbParseSheetObjects(text) {
  const cells = rdbParseCSV(text).filter(r => r.some(c => (c || "").trim() !== ""));
  if (!cells.length) return { headers: [], rows: [] };
  const headers = cells[0].map(h => (h || "").trim());
  const rows = cells.slice(1).map(r => {
    const o = {};
    headers.forEach((h, i) => { o[h] = (r[i] || "").trim(); });
    o._cells = r;
    return o;
  });
  return { headers, rows };
}

// Cached fetch by URL.
const _csvCache = {};
function rdbLoadCSV(url) {
  if (_csvCache[url]) return _csvCache[url];
  _csvCache[url] = (async () => {
    const ctrl = typeof AbortController !== "undefined" ? new AbortController() : null;
    const t = ctrl ? setTimeout(() => ctrl.abort(), 9000) : null;
    try {
      const res = await fetch(url, ctrl ? { signal: ctrl.signal } : undefined);
      if (!res.ok) throw new Error("HTTP " + res.status);
      return await res.text();
    } finally { if (t) clearTimeout(t); }
  })();
  return _csvCache[url];
}

// Generic hook: seed immediately, swap to live rows when they arrive.
// buildFn(text) -> array (throw / return [] to fall back to seed).
function useSheet(url, seed, buildFn) {
  const [s, setS] = React.useState(() => ({ rows: seed, status: "loading" }));
  React.useEffect(() => {
    let alive = true;
    rdbLoadCSV(url)
      .then(text => {
        const rows = buildFn(text);
        if (!rows || !rows.length) throw new Error("empty");
        if (alive) setS({ rows, status: "live" });
      })
      .catch(() => { if (alive) setS({ rows: seed, status: "sample" }); });
    return () => { alive = false; };
  }, [url]);
  return s;
}

// Shared sync indicator (matches the directory pages).
function SyncPill({ status, liveLabel, sampleLabel }) {
  const map = {
    live:    { color: "var(--success)", text: liveLabel || "Live · read-only" },
    sample:  { color: "var(--ink-6)",   text: sampleLabel || "Offline sample · live when hosted" },
    loading: { color: "var(--rdb-gold)", text: "Syncing…" },
  };
  const m = map[status] || map.loading;
  return (
    <span style={{ display: "inline-flex", alignItems: "center", gap: 9, fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--fg-2)", letterSpacing: "0.01em" }}>
      <span style={{ width: 7, height: 7, borderRadius: "50%", background: m.color, boxShadow: status === "live" ? "0 0 0 3px rgba(34,170,102,0.12)" : "none" }} />
      {m.text}
    </span>
  );
}

// File-type glyph for Drive document lists.
function fileIcon(type) {
  const t = (type || "").toLowerCase();
  if (t.includes("pdf")) return "file-text";
  if (t.includes("sheet") || t.includes("xls") || t.includes("csv")) return "table";
  if (t.includes("doc")) return "file-text";
  if (t.includes("slide") || t.includes("ppt")) return "presentation";
  if (t.includes("image") || t.includes("png") || t.includes("jpg")) return "image";
  return "file";
}

Object.assign(window, {
  apiUrl, rdbParseCSV, rdbParseSheetObjects,
  rdbLoadCSV, useSheet, SyncPill, fileIcon,
});
