// Local case persistence — localStorage["rd:cases"] = [{ id, label, savedAt, state, calcOverrides, docEdits }]
// v1f. No backend. Session strip moved here from copilot.jsx.

const { useState: useStateCase, useEffect: useEffectCase, useRef: useRefCase, useCallback: useCallbackCase, useMemo: useMemoCase } = React;

const CASES_KEY = "rd:cases";

function readCases() {
  try { const v = JSON.parse(localStorage.getItem(CASES_KEY) || "[]"); return Array.isArray(v) ? v : []; }
  catch { return []; }
}
function writeCases(list) {
  try { localStorage.setItem(CASES_KEY, JSON.stringify(list)); } catch { /* quota / privacy mode — ignore */ }
}
function newId() {
  return (window.crypto?.randomUUID?.() || (Math.random().toString(36).slice(2) + Date.now().toString(36)));
}

// Stable snapshot used for both saving and dirty comparison.
function snapshot(state, calc, note, pes) {
  return { state, calcOverrides: calc, docEdits: { note, pes } };
}
function snapKey(s) { return JSON.stringify(s); }

function shortDx(state) {
  return window.RD.dxShort(state);
}
function autoLabel(state, when = new Date()) {
  const ageSex = `${state.age || "—"}${state.sex ? state.sex.charAt(0) : ""}`;
  const dx = shortDx(state) || "No dx";
  const date = when.toLocaleDateString("en-US", { month: "short", day: "numeric" });
  return `${ageSex} · ${dx} · ${date}`;
}
function relTime(ts) {
  if (!ts) return "";
  const s = Math.floor((Date.now() - ts) / 1000);
  if (s < 10) return "just now";
  if (s < 60) return `${s}s ago`;
  const m = Math.floor(s / 60);
  if (m < 60) return `${m} min ago`;
  const h = Math.floor(m / 60);
  if (h < 24) return `${h}h ago`;
  return new Date(ts).toLocaleDateString("en-US", { month: "short", day: "numeric" });
}

// ---------------------------------------------------------------------------
// Hook: autosave (1s debounce) to the current case. Sequential #0001 numbers
// assigned on first write from localStorage["rd:caseSeq"], never reused.
// Stored entry: { id, num, label, labelTouched, savedAt, state, calcOverrides, docEdits }
//   label = descriptive part only ("52F · T2DM · Sep 6"); display composes "#0003 · " + label.
// ---------------------------------------------------------------------------
const SEQ_KEY = "rd:caseSeq";
function nextSeq() {
  let n = 0;
  try { n = parseInt(localStorage.getItem(SEQ_KEY) || "0", 10) || 0; } catch {}
  // Never reuse: also bump past any number already present in the list.
  const maxExisting = readCases().reduce((m, c) => Math.max(m, c.num || 0), 0);
  n = Math.max(n, maxExisting) + 1;
  try { localStorage.setItem(SEQ_KEY, String(n)); } catch {}
  return n;
}
const padNum = n => "#" + String(n).padStart(4, "0");
function displayLabel(num, label) { return num ? `${padNum(num)} · ${label}` : label; }

function isEmptySnapshot(state, calc, note, pes) {
  const RD = window.RD;
  return snapKey(snapshot(state, calc, note, pes)) === snapKey(snapshot(RD.EMPTY_STATE, RD.EMPTY_CALC, RD.EMPTY_NOTE, RD.EMPTY_PES));
}

function useCases({ state, calc, note, pes, apply, reset }) {
  const [cases, setCases] = useStateCase(readCases);
  const [currentId, setCurrentId] = useStateCase(() => newId());
  const [num, setNum] = useStateCase(null);
  const [label, setLabel] = useStateCase("");
  const [labelTouched, setLabelTouched] = useStateCase(false);
  const [lastSavedAt, setLastSavedAt] = useStateCase(null);
  const [, tick] = useStateCase(0);
  const timerRef = useRefCase(null);
  const skipNextRef = useRefCase(false);   // suppress autosave right after open/new (state is already persisted)

  useEffectCase(() => { const t = setInterval(() => tick(x => x + 1), 30000); return () => clearInterval(t); }, []);
  useEffectCase(() => {
    function onStorage(e) { if (e.key === CASES_KEY) setCases(readCases()); }
    window.addEventListener("storage", onStorage);
    return () => window.removeEventListener("storage", onStorage);
  }, []);

  // Descriptive label regenerates from state until manually edited.
  useEffectCase(() => { if (!labelTouched) setLabel(autoLabel(state)); }, [state.age, state.sex, state.diagnosis, state.diagnosisOther, labelTouched]);

  const currentKey = useMemoCase(() => snapKey(snapshot(state, calc, note, pes)), [state, calc, note, pes]);

  // Autosave — debounced 1s. Empty brand-new case is never written.
  useEffectCase(() => {
    if (skipNextRef.current) { skipNextRef.current = false; return; }
    if (isEmptySnapshot(state, calc, note, pes) && num == null) return;
    clearTimeout(timerRef.current);
    timerRef.current = setTimeout(() => {
      const n = num ?? nextSeq();
      const savedAt = Date.now();
      const lbl = labelTouched ? label : autoLabel(state, new Date(savedAt));
      const entry = { id: currentId, num: n, label: lbl, labelTouched, savedAt, ...snapshot(state, calc, note, pes) };
      const next = [entry, ...readCases().filter(c => c.id !== currentId)];
      writeCases(next);
      setCases(next);
      if (num == null) setNum(n);
      if (!labelTouched) setLabel(lbl);
      setLastSavedAt(savedAt);
    }, 1000);
    return () => clearTimeout(timerRef.current);
  }, [currentKey, label, labelTouched]);

  const newCase = useCallbackCase(() => {
    clearTimeout(timerRef.current);
    skipNextRef.current = true;
    reset();
    setCurrentId(newId());
    setNum(null);
    setLabelTouched(false);
    setLastSavedAt(null);
    return true;
  }, [reset]);

  const open = useCallbackCase((id) => {
    const c = readCases().find(x => x.id === id);
    if (!c) return false;
    clearTimeout(timerRef.current);
    skipNextRef.current = true;
    const RD = window.RD;
    apply({
      state: { ...RD.EMPTY_STATE, ...(c.state || {}) },
      calc:  { ...RD.EMPTY_CALC, ...(c.calcOverrides || {}) },
      note:  { ...RD.EMPTY_NOTE, ...(c.docEdits?.note || {}) },
      pes:   { ...RD.EMPTY_PES, ...(c.docEdits?.pes || {}) },
    });
    setCurrentId(c.id);
    setNum(c.num || null);
    setLabel(c.label);
    setLabelTouched(!!c.labelTouched);
    setLastSavedAt(c.savedAt);
    return true;
  }, [apply]);

  const remove = useCallbackCase((id) => {
    const next = readCases().filter(c => c.id !== id);
    writeCases(next);
    setCases(next);
    if (id === currentId) { clearTimeout(timerRef.current); skipNextRef.current = true; setCurrentId(newId()); setNum(null); setLastSavedAt(null); setLabelTouched(false); }
  }, [currentId]);

  const renameLabel = useCallbackCase((v) => { setLabel(v); setLabelTouched(true); }, []);

  // Sample patient / Clear chart: start a fresh case id (autosave picks it up if non-empty).
  const markBaseline = useCallbackCase(() => {
    clearTimeout(timerRef.current);
    setCurrentId(newId());
    setNum(null);
    setLabelTouched(false);
    setLastSavedAt(null);
  }, []);

  return { cases, currentId, num, label, lastSavedAt, newCase, open, remove, renameLabel, markBaseline, displayLabel };
}

// ---------------------------------------------------------------------------
// CaseBar — status · #num · label · New case · Open ▾
// ---------------------------------------------------------------------------
function CaseBar({ cases: api }) {
  const { cases, currentId, num, label, lastSavedAt, newCase, open, remove, renameLabel } = api;
  const [menuOpen, setMenuOpen] = useStateCase(false);
  const [editing, setEditing] = useStateCase(false);
  const wrapRef = useRefCase(null);
  const inputRef = useRefCase(null);

  useEffectCase(() => {
    function onDoc(e) { if (wrapRef.current && !wrapRef.current.contains(e.target)) setMenuOpen(false); }
    document.addEventListener("mousedown", onDoc);
    return () => document.removeEventListener("mousedown", onDoc);
  }, []);
  useEffectCase(() => { if (editing && inputRef.current) { inputRef.current.focus(); inputRef.current.select(); } }, [editing]);

  const status = lastSavedAt
    ? { cls: "saved", text: `Saved ${relTime(lastSavedAt)}` }
    : { cls: "", text: "Not saved yet" };

  return (
    <div className="casebar" ref={wrapRef}>
      <span className={`case-status ${status.cls}`} role="status">
        <span className="case-dot" aria-hidden="true"></span>
        {status.text}
      </span>

      <span className="case-id">
        {num != null && <span className="case-num">{padNum(num)}</span>}
        {editing ? (
          <input
            ref={inputRef}
            className="case-label-input"
            value={label}
            onChange={e => renameLabel(e.target.value)}
            onBlur={() => setEditing(false)}
            onKeyDown={e => { if (e.key === "Enter" || e.key === "Escape") setEditing(false); }}
            aria-label="Case label"
          />
        ) : (
          <button type="button" className="case-label" onClick={() => setEditing(true)} title="Click to rename">
            {label || "Untitled case"}
          </button>
        )}
      </span>

      <button type="button" className="btn-ghost" onClick={newCase} data-tip="Start a blank case">New case</button>

      <div className="case-menu-wrap">
        <button
          type="button"
          className="btn-ghost case-open"
          aria-haspopup="listbox"
          aria-expanded={menuOpen}
          onClick={() => setMenuOpen(o => !o)}
          data-tip="Open a saved case"
        >
          Open{cases.length ? ` (${cases.length})` : ""}
          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><polyline points="6 9 12 15 18 9"/></svg>
        </button>
        {menuOpen && (
          <div className="case-menu" role="listbox">
            {cases.length === 0 ? (
              <div className="case-menu-empty">No saved cases yet. Cases save automatically as you work.</div>
            ) : cases.map(c => (
              <div key={c.id} className={`case-item ${c.id === currentId ? "is-current" : ""}`} role="option" aria-selected={c.id === currentId}>
                <button type="button" className="case-item-main" onClick={() => { if (open(c.id)) setMenuOpen(false); }}>
                  <span className="case-item-label">{displayLabel(c.num, c.label)}</span>
                  <span className="case-item-date">{new Date(c.savedAt).toLocaleString("en-US", { month: "short", day: "numeric", hour: "numeric", minute: "2-digit" })}</span>
                </button>
                <button type="button" className="case-item-x" aria-label={`Delete ${displayLabel(c.num, c.label)}`} onClick={() => { if (window.confirm(`Delete "${displayLabel(c.num, c.label)}"?`)) remove(c.id); }}>×</button>
              </div>
            ))}
            <div className="case-menu-note">Cases are stored in this browser only.</div>
          </div>
        )}
      </div>
    </div>
  );
}

Object.assign(window, { useCases, CaseBar });
