// Source: index.html script block (extracted). Loaded via <script type="text/babel"> in index.html.
//
// Manager-only "Database" blade. Browses and edits the per-cohort clinical DB
// (patients / drugs / diseases / labs / imaging / guidelines) on top of the
// /api/db CRUD surface exposed by window.tasksApi.db.{list,get,put,remove,seed}.

const DB_NAMESPACES = [
  "patients",
  "drugs",
  "diseases",
  "lab_catalog",
  "patient_labs",
  "imaging_catalog",
  "patient_imaging",
  "treatment_guidelines",
];

const SHARED_COHORT = "__shared";
const PAGE_SIZE = 50;

function dbFirstStringValue(obj) {
  if (!obj || typeof obj !== "object") return "";
  for (const k of Object.keys(obj)) {
    const v = obj[k];
    if (typeof v === "string" && v.trim()) return v;
    if (typeof v === "number") return String(v);
  }
  return "";
}

function dbSafeStringify(v) {
  try { return JSON.stringify(v, null, 2); } catch { return "{}"; }
}

function DatabasePage({ user }) {
  const { t: tr } = useLang();

  if (!user || user.kind !== "manager") {
    return (
      <div className="db" style={{padding:"22px 32px", color:"var(--ink-dim)"}}>
        Managers only
      </div>
    );
  }

  const api = (typeof window !== "undefined" && window.tasksApi) || null;
  const dbApi = api && api.db ? api.db : null;

  // ---- cohort list -------------------------------------------------------
  const [cohorts, setCohorts] = React.useState([]);
  const [cohort, setCohort] = React.useState(SHARED_COHORT);

  React.useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        if (!api || !api.listCohorts) return;
        const res = await api.listCohorts();
        const items = Array.isArray(res && res.items) ? res.items : (Array.isArray(res) ? res : []);
        if (cancelled) return;
        setCohorts(items);
        setCohort(prev => {
          if (prev && prev !== SHARED_COHORT) return prev;
          return (items[0] && items[0].id) || SHARED_COHORT;
        });
      } catch { /* non-fatal */ }
    })();
    return () => { cancelled = true; };
  }, [api]);

  // ---- namespace state ---------------------------------------------------
  const [namespace, setNamespace] = React.useState(DB_NAMESPACES[0]);
  const [customNs, setCustomNs] = React.useState("");
  const [extraNs, setExtraNs] = React.useState([]); // user-added custom namespaces

  // ---- search (debounced) ------------------------------------------------
  const [searchInput, setSearchInput] = React.useState("");
  const [q, setQ] = React.useState("");
  React.useEffect(() => {
    const id = window.setTimeout(() => setQ(searchInput.trim()), 250);
    return () => window.clearTimeout(id);
  }, [searchInput]);

  // ---- rows --------------------------------------------------------------
  const [items, setItems] = React.useState([]);
  const [cursor, setCursor] = React.useState(null);
  const [total, setTotal] = React.useState(0);
  const [loading, setLoading] = React.useState(false);
  const [loadErr, setLoadErr] = React.useState(null);

  const [selectedId, setSelectedId] = React.useState(null);
  const [draft, setDraft] = React.useState(null); // {id, data, isNew, namespace, cohort_id, updated_at, updated_by}
  const [draftText, setDraftText] = React.useState("{}");
  const [draftErr, setDraftErr] = React.useState(null);
  const [draftBusy, setDraftBusy] = React.useState(false);

  const [notice, setNotice] = React.useState(null);
  const showNotice = React.useCallback((msg) => {
    setNotice(msg);
    window.setTimeout(() => setNotice(n => (n === msg ? null : n)), 3500);
  }, []);

  const cohortQs = React.useMemo(() => ({ cohort }), [cohort]);

  const loadFirstPage = React.useCallback(async () => {
    if (!dbApi || !dbApi.list) {
      setItems([]); setCursor(null); setTotal(0);
      setLoadErr("Database API not available yet — the backend shim (window.tasksApi.db) isn't loaded.");
      return;
    }
    setLoading(true);
    setLoadErr(null);
    try {
      const res = await dbApi.list(namespace, { cohort, limit: PAGE_SIZE, q: q || undefined });
      const rows = Array.isArray(res && res.items) ? res.items : [];
      setItems(rows);
      setCursor((res && res.next_cursor) || null);
      setTotal((res && typeof res.total === "number") ? res.total : rows.length);
    } catch (err) {
      setItems([]); setCursor(null); setTotal(0);
      setLoadErr((err && err.message) || "Failed to load rows.");
    } finally {
      setLoading(false);
    }
  }, [dbApi, namespace, cohort, q]);

  React.useEffect(() => { loadFirstPage(); }, [loadFirstPage]);

  const loadMore = async () => {
    if (!dbApi || !dbApi.list || !cursor) return;
    setLoading(true);
    try {
      const res = await dbApi.list(namespace, { cohort, limit: PAGE_SIZE, q: q || undefined, cursor });
      const rows = Array.isArray(res && res.items) ? res.items : [];
      setItems(prev => prev.concat(rows));
      setCursor((res && res.next_cursor) || null);
      if (res && typeof res.total === "number") setTotal(res.total);
    } catch (err) {
      showNotice((err && err.message) || "Failed to load more.");
    } finally {
      setLoading(false);
    }
  };

  // ---- selection & draft -------------------------------------------------
  const selectRow = async (row) => {
    if (!row || !row.id) return;
    setSelectedId(row.id);
    setDraftErr(null);
    // Use inline data if present, otherwise fetch the full record.
    if (row.data && typeof row.data === "object") {
      const d = { id: row.id, data: row.data, namespace, cohort_id: row.cohort_id || cohort, updated_at: row.updated_at, updated_by: row.updated_by, isNew: false };
      setDraft(d);
      setDraftText(dbSafeStringify(row.data));
      return;
    }
    if (!dbApi || !dbApi.get) return;
    try {
      const full = await dbApi.get(namespace, row.id, { cohort });
      const data = (full && full.data) || {};
      const d = { id: full.id || row.id, data, namespace, cohort_id: full.cohort_id || cohort, updated_at: full.updated_at, updated_by: full.updated_by, isNew: false };
      setDraft(d);
      setDraftText(dbSafeStringify(data));
    } catch (err) {
      showNotice((err && err.message) || "Failed to load row.");
    }
  };

  const beginNewRow = () => {
    setSelectedId(null);
    const d = { id: "", data: {}, namespace, cohort_id: cohort, updated_at: null, updated_by: null, isNew: true };
    setDraft(d);
    setDraftText("{\n  \n}");
    setDraftErr(null);
  };

  const cancelDraft = () => {
    setDraft(null);
    setSelectedId(null);
    setDraftErr(null);
    setDraftText("{}");
  };

  const onDraftTextChange = (txt) => {
    setDraftText(txt);
    try {
      JSON.parse(txt);
      setDraftErr(null);
    } catch (err) {
      setDraftErr((err && err.message) || tr("db_invalid_json") || "Invalid JSON");
    }
  };

  const onDraftIdChange = (newId) => {
    setDraft(prev => prev ? { ...prev, id: newId } : prev);
  };

  const applyDraft = async () => {
    if (!draft) return;
    if (!draft.id || !draft.id.trim()) {
      setDraftErr("id required");
      return;
    }
    let parsed;
    try { parsed = JSON.parse(draftText); }
    catch (err) { setDraftErr((err && err.message) || "Invalid JSON"); return; }
    if (!dbApi || !dbApi.put) { showNotice("Database API not available."); return; }
    setDraftBusy(true);
    try {
      await dbApi.put(namespace, draft.id, parsed, { cohort });
      showNotice(`Saved ${namespace}/${draft.id}`);
      // Refresh the list so the edited/new row surfaces.
      await loadFirstPage();
      setDraft(prev => prev ? { ...prev, data: parsed, isNew: false, updated_at: new Date().toISOString(), updated_by: user.email || user.name } : prev);
      setSelectedId(draft.id);
    } catch (err) {
      setDraftErr((err && err.message) || "Save failed.");
    } finally {
      setDraftBusy(false);
    }
  };

  const deleteRow = async (id) => {
    if (!id) return;
    if (!dbApi || !dbApi.remove) { showNotice("Database API not available."); return; }
    if (!window.confirm(`Delete ${namespace}/${id}? This cannot be undone.`)) return;
    try {
      await dbApi.remove(namespace, id, { cohort });
      showNotice(`Deleted ${namespace}/${id}`);
      if (selectedId === id) cancelDraft();
      await loadFirstPage();
    } catch (err) {
      showNotice((err && err.message) || "Delete failed.");
    }
  };

  // ---- bulk seed ---------------------------------------------------------
  const [seedOpen, setSeedOpen] = React.useState(false);
  const [seedBusy, setSeedBusy] = React.useState(false);
  const [seedResult, setSeedResult] = React.useState(null);

  const onSeedFile = async (file) => {
    if (!file) return;
    if (!dbApi || !dbApi.seed) { showNotice("Database API not available."); return; }
    setSeedBusy(true);
    setSeedResult(null);
    try {
      const txt = await file.text();
      const parsed = JSON.parse(txt);
      const namespaces = (parsed && typeof parsed === "object" && parsed.namespaces && typeof parsed.namespaces === "object")
        ? parsed.namespaces
        : parsed;
      const res = await dbApi.seed({ namespaces }, { cohort });
      setSeedResult(res);
      showNotice(`Seeded ${JSON.stringify((res && res.counts) || {})}`);
      await loadFirstPage();
    } catch (err) {
      setSeedResult({ error: (err && err.message) || String(err) });
      showNotice(`Seed failed: ${(err && err.message) || err}`);
    } finally {
      setSeedBusy(false);
    }
  };

  // ---- namespace tabs (built-ins + extras) -------------------------------
  const nsTabs = React.useMemo(() => {
    const seen = new Set(DB_NAMESPACES);
    const extras = extraNs.filter(n => {
      if (seen.has(n)) return false;
      seen.add(n);
      return true;
    });
    return DB_NAMESPACES.concat(extras);
  }, [extraNs]);

  const addCustomNs = () => {
    const name = customNs.trim();
    if (!name) return;
    if (!DB_NAMESPACES.includes(name) && !extraNs.includes(name)) {
      setExtraNs(prev => prev.concat(name));
    }
    setNamespace(name);
    setCustomNs("");
  };

  // ---- render ------------------------------------------------------------
  const updatedFooter = draft && !draft.isNew ? (
    <div style={{marginTop:10, fontSize:11.5, color:"var(--ink-mute)", fontFamily:"var(--font-mono)"}}>
      {tr("db_updated") || "Last updated"}: {draft.updated_at || "—"}{draft.updated_by ? ` · ${draft.updated_by}` : ""}
    </div>
  ) : null;

  return (
    <div className="db">
      <div className="db-head">
        <div>
          <div style={{fontSize:11.5, color:"var(--ink-mute)", fontFamily:"var(--font-mono)", textTransform:"uppercase", letterSpacing:"0.06em"}}>
            Admin · manager
          </div>
          <h1>{tr("db_title") || "Clinical database"}</h1>
          <p>{tr("db_subtitle")}</p>
        </div>
        <div style={{marginLeft:"auto", display:"flex", gap:8, alignItems:"center"}}>
          <button className="btn" onClick={loadFirstPage} disabled={loading}>
            {Ico.reset ? Ico.reset() : null} Refresh
          </button>
          <button className="btn" onClick={()=>setSeedOpen(o => !o)} title="Bulk seed from JSON file">
            {Ico.upload()} {tr("db_seed") || "Bulk seed"}
          </button>
          <button className="btn primary" onClick={beginNewRow}>
            {Ico.plus()} {tr("db_new_row") || "New row"}
          </button>
        </div>
      </div>

      {notice && (
        <div style={{margin:"0 0 14px", padding:"8px 12px", fontSize:12.5, border:"1px solid var(--line)", background:"var(--bg-sunken)", borderRadius:"var(--r-md)", color:"var(--ink-dim)"}}>
          {notice}
        </div>
      )}

      {seedOpen && (
        <div style={{margin:"0 0 16px", padding:"12px 14px", border:"1px solid var(--line)", background:"var(--bg-sunken)", borderRadius:"var(--r-md)"}}>
          <div style={{display:"flex", alignItems:"center", gap:10, marginBottom:8}}>
            <b style={{fontSize:13}}>{tr("db_seed") || "Bulk seed from file"}</b>
            <span style={{fontSize:11.5, color:"var(--ink-mute)", fontFamily:"var(--font-mono)"}}>
              JSON of shape <code>{"{ namespaces: { patients: [...], drugs: [...] } }"}</code> (or <code>{"{ patients: [...], drugs: [...] }"}</code>)
            </span>
            <button className="btn ghost" style={{marginLeft:"auto"}} onClick={()=>setSeedOpen(false)}>{Ico.x()}</button>
          </div>
          <input
            type="file"
            accept=".json,application/json"
            disabled={seedBusy}
            onChange={e => { const f = e.target.files && e.target.files[0]; if (f) onSeedFile(f); e.target.value = ""; }}
          />
          {seedBusy && <div style={{marginTop:6, fontSize:12, color:"var(--ink-dim)"}}>Seeding…</div>}
          {seedResult && (
            <pre style={{marginTop:8, padding:10, background:"var(--bg)", border:"1px solid var(--line)", borderRadius:6, fontSize:11, color: seedResult.error ? "var(--red, #b94a48)" : "var(--ink-dim)", maxHeight:140, overflow:"auto"}}>
              {JSON.stringify(seedResult, null, 2)}
            </pre>
          )}
        </div>
      )}

      <div className="db-layout">
        {/* ---- LEFT: picker + rows ---- */}
        <div>
          <div style={{display:"grid", gridTemplateColumns:"auto 1fr", gap:10, alignItems:"center", marginBottom:10}}>
            <label style={{fontSize:11.5, color:"var(--ink-mute)", fontFamily:"var(--font-mono)", textTransform:"uppercase", letterSpacing:"0.05em"}}>
              {tr("db_cohort") || "Cohort"}
            </label>
            <select
              className="inp"
              value={cohort}
              onChange={e => setCohort(e.target.value)}
              style={{maxWidth:360}}
            >
              <option value={SHARED_COHORT}>{tr("db_shared") || "Shared (no cohort)"}</option>
              {cohorts.map(c => (
                <option key={c.id} value={c.id}>{c.name || c.id} ({(c.total || c.task_count || 0).toLocaleString()})</option>
              ))}
            </select>
          </div>

          <div style={{fontSize:11.5, color:"var(--ink-mute)", fontFamily:"var(--font-mono)", textTransform:"uppercase", letterSpacing:"0.05em", marginTop:8}}>
            {tr("db_namespace") || "Namespace"}
          </div>
          <div className="db-ns-tabs">
            {nsTabs.map(ns => (
              <button
                key={ns}
                className="filter-pill"
                data-active={ns === namespace}
                onClick={() => setNamespace(ns)}
                title={ns}
              >
                {ns}
              </button>
            ))}
            <span style={{display:"inline-flex", gap:4, alignItems:"center", marginLeft:6}}>
              <input
                className="inp"
                placeholder="+ add namespace"
                value={customNs}
                onChange={e => setCustomNs(e.target.value)}
                onKeyDown={e => { if (e.key === "Enter") { e.preventDefault(); addCustomNs(); } }}
                style={{width:160, fontSize:12, padding:"3px 8px"}}
              />
              <button className="btn ghost" onClick={addCustomNs} disabled={!customNs.trim()}>{Ico.plus()}</button>
            </span>
          </div>

          <div style={{margin:"10px 0", display:"flex", alignItems:"center", gap:8}}>
            <div style={{position:"relative", flex:1}}>
              <span style={{position:"absolute", top:8, left:10, color:"var(--ink-mute)"}}>{Ico.search ? Ico.search() : null}</span>
              <input
                className="inp"
                placeholder={tr("db_search_ph") || "Search by id…"}
                value={searchInput}
                onChange={e => setSearchInput(e.target.value)}
                style={{paddingLeft:30, width:"100%"}}
              />
            </div>
            <span style={{fontSize:11.5, color:"var(--ink-mute)", fontFamily:"var(--font-mono)"}}>
              {items.length.toLocaleString()} / {total.toLocaleString()}
            </span>
          </div>

          {loadErr && (
            <div style={{margin:"0 0 10px", padding:"8px 12px", fontSize:12.5, border:"1px solid var(--line)", background:"var(--bg-sunken)", borderRadius:"var(--r-md)", color:"var(--red, #b94a48)"}}>
              {loadErr}
            </div>
          )}

          <div className="db-rows">
            {items.length === 0 && !loading && (
              <div style={{padding:"20px 14px", color:"var(--ink-mute)", fontSize:13}}>
                {tr("db_no_rows") || "No rows yet."}
              </div>
            )}
            {items.map(row => {
              const summary = dbFirstStringValue(row.data || {});
              return (
                <div
                  key={row.id}
                  className="db-row"
                  data-active={row.id === selectedId}
                  onClick={() => selectRow(row)}
                >
                  <code>{row.id}</code>
                  <span style={{fontSize:12.5, color:"var(--ink-dim)", overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap"}}>
                    {summary || <span style={{color:"var(--ink-mute)"}}>—</span>}
                  </span>
                  <button
                    className="btn ghost"
                    title="Delete row"
                    onClick={e => { e.stopPropagation(); deleteRow(row.id); }}
                  >
                    {Ico.x()}
                  </button>
                </div>
              );
            })}
            {loading && (
              <div style={{padding:"12px 14px", color:"var(--ink-mute)", fontSize:12}}>Loading…</div>
            )}
          </div>
          {cursor && (
            <div style={{marginTop:10, display:"flex"}}>
              <button className="btn" onClick={loadMore} disabled={loading} style={{margin:"0 auto"}}>
                Load more
              </button>
            </div>
          )}
        </div>

        {/* ---- RIGHT: edit drawer ---- */}
        <div className="db-drawer">
          {!draft ? (
            <div style={{color:"var(--ink-mute)", fontSize:13, padding:"40px 10px", textAlign:"center"}}>
              Select a row on the left, or click <b>{tr("db_new_row") || "New row"}</b> to create one.
            </div>
          ) : (
            <>
              <div style={{display:"grid", gridTemplateColumns:"auto 1fr", gap:10, alignItems:"center", marginBottom:10}}>
                <label style={{fontSize:11.5, color:"var(--ink-mute)", fontFamily:"var(--font-mono)", textTransform:"uppercase", letterSpacing:"0.05em"}}>id</label>
                {draft.isNew ? (
                  <input
                    className="inp"
                    value={draft.id}
                    onChange={e => onDraftIdChange(e.target.value)}
                    placeholder="e.g. P001"
                    autoFocus
                  />
                ) : (
                  <code style={{fontFamily:"var(--font-mono)", fontSize:12, color:"var(--ink)"}}>{draft.id}</code>
                )}
              </div>
              <div style={{display:"flex", gap:6, alignItems:"center", marginBottom:6}}>
                <span className="tag">{namespace}</span>
                <span className="tag">{cohort === SHARED_COHORT ? "shared" : cohort}</span>
                {draft.isNew && <span className="tag accent">new</span>}
              </div>
              <textarea
                spellCheck={false}
                value={draftText}
                onChange={e => onDraftTextChange(e.target.value)}
                style={draftErr ? { borderColor: "var(--red, #b94a48)" } : undefined}
              />
              {draftErr && (
                <div className="err">{draftErr}</div>
              )}
              <div style={{marginTop:10, display:"flex", gap:8, alignItems:"center"}}>
                <button className="btn primary" onClick={applyDraft} disabled={draftBusy || !!draftErr}>
                  {Ico.check()} {tr("db_apply") || "Apply"}
                </button>
                <button className="btn ghost" onClick={cancelDraft} disabled={draftBusy}>Cancel</button>
                {!draft.isNew && (
                  <button className="btn danger" onClick={() => deleteRow(draft.id)} disabled={draftBusy} style={{marginLeft:"auto"}}>
                    {Ico.x()} {tr("db_delete") || "Delete row"}
                  </button>
                )}
              </div>
              {updatedFooter}
            </>
          )}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { DatabasePage });
