// Root app — single React tree owning patient state, calc state, PES, note,
// rail, about panel, flags panel, and the chart zones.

const { useState: useStateApp, useCallback: useCallbackApp, useMemo: useMemoApp } = React;

function RDApp() {
  const [state, setState] = useStateApp(window.RD.EMPTY_STATE);
  const [calc, setCalcState] = useStateApp(window.RD.EMPTY_CALC);
  const [pes, setPesState] = useStateApp(window.RD.EMPTY_PES);
  const [note, setNoteState] = useStateApp(window.RD.EMPTY_NOTE);
  const [aboutOpen, setAboutOpen] = useStateApp(false);
  const [aboutSection, setAboutSection] = useStateApp(null);
  const [settingsOpen, setSettingsOpen] = useStateApp(false);
  const [dismissedFlags, setDismissedFlags] = useStateApp([]);
  const [footerOpen, setFooterOpen] = useStateApp(false);
  const [msumOpen, setMsumOpen] = useStateApp(false);
  const [railOpen, setRailOpen] = useStateApp(false);

  const set = useCallbackApp(patch => setState(s => ({ ...s, ...patch })), []);
  const setCalc = useCallbackApp(patch => setCalcState(c => ({ ...c, ...patch })), []);
  const setPES = useCallbackApp(patch => setPesState(p => ({ ...p, ...patch })), []);
  const setNote = useCallbackApp(patch => setNoteState(n => ({ ...n, ...patch })), []);

  const hasData = (() => {
    const e = window.RD.EMPTY_STATE;
    return Object.keys(e).some(k => {
      const v = state[k], ev = e[k];
      if (Array.isArray(v)) return v.length > 0;
      if (typeof v === "boolean") return v !== ev;
      return (v ?? "") !== (ev ?? "");
    });
  })();

  const resetAll = useCallbackApp(() => {
    setState(window.RD.EMPTY_STATE);
    setCalcState(window.RD.EMPTY_CALC);
    setPesState(window.RD.EMPTY_PES);
    setNoteState(window.RD.EMPTY_NOTE);
    setDismissedFlags([]);
  }, []);

  // v1f — local case persistence (localStorage "rd:cases")
  const applyCase = useCallbackApp(({ state: s, calc: c, note: n, pes: p }) => {
    setState(s); setCalcState(c); setNoteState(n); setPesState(p); setDismissedFlags([]);
  }, []);
  const casesApi = useCases({ state, calc, note, pes, apply: applyCase, reset: resetAll });

  function onSeed() {
    setState(window.RD.pickSamplePatient());
    setCalcState(window.RD.EMPTY_CALC);
    setPesState(window.RD.EMPTY_PES);
    setNoteState(window.RD.EMPTY_NOTE);
    setDismissedFlags([]);
    casesApi.markBaseline();
  }
  function onReset() {
    resetAll();
    casesApi.markBaseline();
  }

  const openAbout = useCallbackApp((section) => {
    setAboutSection(section || null);
    setAboutOpen(true);
  }, []);
  const closeAbout = useCallbackApp(() => setAboutOpen(false), []);

  // Listen for global "open about" requests (e.g. from TierChip in controls.jsx)
  React.useEffect(() => {
    function onOpen(e) { openAbout(e.detail?.section); }
    window.addEventListener("rd:open-about", onOpen);
    return () => window.removeEventListener("rd:open-about", onOpen);
  }, [openAbout]);

  // Onboarding tooltip burst — show, fade, then hover-only.
  React.useEffect(() => {
    const showTimer = setTimeout(() => {
      const els = document.querySelectorAll('[data-tip][data-tip-onboard="1"]');
      if (!els.length) return;
      els.forEach(el => el.classList.add("tip-onboard"));
      const hideTimer = setTimeout(() => {
        els.forEach(el => el.classList.add("tip-onboard-hide"));
        const cleanupTimer = setTimeout(() => {
          els.forEach(el => {
            el.classList.remove("tip-onboard");
            el.classList.remove("tip-onboard-hide");
          });
        }, 450);
        cleanupRefs.push(cleanupTimer);
      }, 10000);
      cleanupRefs.push(hideTimer);
    }, 350);
    const cleanupRefs = [showTimer];
    return () => cleanupRefs.forEach(t => clearTimeout(t));
  }, []);

  const dismissFlag = useCallbackApp((id) => {
    setDismissedFlags(d => d.includes(id) ? d : [...d, id]);
  }, []);

  // Compute flags from current state
  const flags = useMemoApp(() => {
    const all = window.RD_Rules.evaluateFlags(state, calc) || [];
    return all.filter(f => !dismissedFlags.includes(f.id));
  }, [state, calc, dismissedFlags]);

  return (
    <div className={`app ${railOpen ? "rail-open" : "rail-closed"}`} data-screen-label="01 Workspace">
      <TopBar railOpen={railOpen} onToggleRail={() => setRailOpen(o => !o)} openAbout={openAbout} openSettings={() => setSettingsOpen(true)} casesApi={casesApi} hasData={hasData} />
      <aside className="rail" id="copilot-rail" aria-label="AI Copilot" aria-hidden={!railOpen}>
        <CopilotRail
          state={state}
          set={set}
          calc={calc}
          setCalc={setCalc}
          pes={pes}
          setPES={setPES}
          note={note}
          setNote={setNote}
          openAbout={openAbout}
          dismissFlag={dismissFlag}
        />
      </aside>

      <main className="chart" aria-label="Patient Chart">
        <div className="chart-inner">
          <Banner state={state} onSeed={onSeed} onReset={onReset} hasData={hasData} caseId={casesApi.currentId} />
          <FlagsPanel flags={flags} onDismiss={dismissFlag} onChartFocus={(target) => {
            const el = document.querySelector(`[data-field="${target}"]`);
            if (el) { el.focus(); el.scrollIntoView({ block: "center", behavior: "smooth" }); }
          }} onSetCalc={setCalc} onSet={set} onSetPES={setPES} />
          <div className="zones">
            <PatientInputs state={state} set={set} openAbout={openAbout} />
            <Calculations state={state} calc={calc} setCalc={setCalc} />
            <Documentation state={state} calc={calc} pes={pes} setPES={setPES} note={note} setNote={setNote} />
          </div>
        </div>
      </main>

      <SummaryColumn state={state} calc={calc}>
        <div className="summary-note">
          <FullNoteOutput noteText={window.computeNoteText(state, calc, pes, note)} />
        </div>
      </SummaryColumn>

      <MobileSummary state={state} calc={calc} noteText={window.computeNoteText(state, calc, pes, note)} open={msumOpen} setOpen={setMsumOpen} />

      <footer className="footer" role="contentinfo">
        <span className="badge">Basis · clinical nutrition workspace</span>
        <span className="sep">|</span>
        <span className={"body" + (footerOpen ? " expanded" : "")} onClick={() => setFooterOpen(o => !o)}>Outputs may contain errors. The registered dietitian is responsible for reviewing and finalizing all content before clinical use. Not a substitute for professional judgment.</span>
        <span className="sep">|</span>
        <span className="phi">Do not enter real PHI.</span>
      </footer>

      <AboutPanel open={aboutOpen} onClose={closeAbout} initialSection={aboutSection} />
      <SettingsPanel open={settingsOpen} onClose={() => setSettingsOpen(false)} />

      <button
        type="button"
        className="rail-fab"
        aria-label={railOpen ? "Hide copilot" : "Show copilot"}
        aria-expanded={railOpen}
        aria-controls="copilot-rail"
        title={railOpen ? "Hide copilot" : "Open copilot"}
        onClick={() => setRailOpen(o => !o)}
      >
        {railOpen ? (
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="M18 6 6 18"/><path d="m6 6 12 12"/></svg>
        ) : (
          <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"><path d="M21 12a8 8 0 0 1-8 8H8l-4 3v-6.5A8 8 0 0 1 12 4h1a8 8 0 0 1 8 8z"/></svg>
        )}
        {!railOpen && <span className="rt-dot" aria-hidden="true"></span>}
      </button>
    </div>
  );
}

// v1f — full-width top bar: toggle + brand on the left; session strip + About/Settings on the right.
function TopBar({ railOpen, onToggleRail, openAbout, openSettings, casesApi, hasData }) {
  const startRef = React.useRef(Date.now());
  const [, tick] = useStateApp(0);
  React.useEffect(() => { const t = setInterval(() => tick(x => x + 1), 30000); return () => clearInterval(t); }, []);
  function started() {
    const s = Math.floor((Date.now() - startRef.current) / 1000);
    if (s < 60) return "just now";
    const m = Math.floor(s / 60);
    if (m < 60) return m + " min ago";
    return Math.floor(m / 60) + "h ago";
  }
  return (
    <header className="topbar" role="banner">
      <div className="topbar-l">
        <div className="brand">
          <svg className="brand-mark" aria-hidden="true" viewBox="0 0 24 24"><rect width="24" height="24" rx="7" fill="var(--rd-accent)"/><path d="M6 16.5h12" stroke="#F7F5EE" strokeWidth="2" strokeLinecap="round"/><path d="M12 16.5V9.5" stroke="#F7F5EE" strokeWidth="2" strokeLinecap="round"/></svg>
          <span className="brand-word">Basis</span>
          <span className="brand-greet">Hi, Joyce</span>
        </div>
      </div>
      <div className="topbar-m">
        <div className="session-strip" role="status">
          <span className="pulse" aria-hidden="true"></span>
          <span className="session-started">Session started {started()}</span>
          <span className="sep session-started">·</span>
          <CaseBar cases={casesApi} />
        </div>
      </div>
      <div className="topbar-r">
        <OverflowMenu casesApi={casesApi} openAbout={openAbout} openSettings={openSettings} />
        <div className="topbar-case-actions">
          <button type="button" className="btn-ghost" onClick={casesApi.newCase} data-tip="Start a blank case">New case</button>
          <CaseOpenMenu casesApi={casesApi} />
        </div>
        <div className="head-actions">
          <button className="icon-btn" aria-label="About this prototype" title="About" onClick={() => openAbout()} data-tip="About this prototype" data-tip-placement="bottom">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="9"/><path d="M12 8h.01"/><path d="M11 12h1v4h1"/></svg>
          </button>
          <button className="icon-btn" aria-label="Settings" title="Settings" onClick={openSettings} data-tip="Settings" data-tip-placement="bottom">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="2.6"/><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 0 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1a2 2 0 1 1-2.8-2.8l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 0 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 0 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9a1.7 1.7 0 0 0 1.5 1H21a2 2 0 0 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z"/></svg>
          </button>
        </div>
      </div>
    </header>
  );
}

// Mobile (≤640px) overflow: New case · Open… · About · Settings. Hidden above the breakpoint via CSS.
function OverflowMenu({ casesApi, openAbout, openSettings }) {
  const [open, setOpen] = useStateApp(false);
  const [sub, setSub] = useStateApp(false); // "Open case" submenu
  const ref = React.useRef(null);
  React.useEffect(() => {
    function onDoc(e) { if (ref.current && !ref.current.contains(e.target)) { setOpen(false); setSub(false); } }
    document.addEventListener("mousedown", onDoc);
    return () => document.removeEventListener("mousedown", onDoc);
  }, []);
  const close = () => { setOpen(false); setSub(false); };
  return (
    <div className="overflow-wrap" ref={ref}>
      <button type="button" className="icon-btn overflow-btn" aria-label="More" aria-haspopup="menu" aria-expanded={open} onClick={() => setOpen(o => !o)}>
        <svg viewBox="0 0 24 24" fill="currentColor"><circle cx="12" cy="5" r="1.8"/><circle cx="12" cy="12" r="1.8"/><circle cx="12" cy="19" r="1.8"/></svg>
      </button>
      {open && (
        <div className="case-menu overflow-menu" role="menu">
          {!sub ? (
            <>
              <button type="button" role="menuitem" className="overflow-item" onClick={() => { casesApi.newCase(); close(); }}>New case</button>
              <button type="button" role="menuitem" className="overflow-item" onClick={() => setSub(true)}>Open case{casesApi.cases.length ? ` (${casesApi.cases.length})` : ""}…</button>
              <div className="overflow-sep"></div>
              <button type="button" role="menuitem" className="overflow-item" onClick={() => { openAbout(); close(); }}>About this prototype</button>
              <button type="button" role="menuitem" className="overflow-item" onClick={() => { openSettings(); close(); }}>Settings</button>
            </>
          ) : (
            <>
              <button type="button" className="overflow-item overflow-back" onClick={() => setSub(false)}>← Back</button>
              {casesApi.cases.length === 0 ? (
                <div className="case-menu-empty">No saved cases yet.</div>
              ) : casesApi.cases.map(c => (
                <div key={c.id} className={`case-item ${c.id === casesApi.currentId ? "is-current" : ""}`}>
                  <button type="button" className="case-item-main" onClick={() => { if (casesApi.open(c.id)) close(); }}>
                    <span className="case-item-label">{casesApi.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" onClick={() => { if (window.confirm(`Delete "${casesApi.displayLabel(c.num, c.label)}"?`)) casesApi.remove(c.id); }}>×</button>
                </div>
              ))}
              <div className="case-menu-note">Cases are stored in this browser only.</div>
            </>
          )}
        </div>
      )}
    </div>
  );
}

// Top-bar "Open" dropdown (desktop). Mirrors the list in CaseBar/OverflowMenu.
function CaseOpenMenu({ casesApi }) {
  const [open, setOpen] = useStateApp(false);
  const ref = React.useRef(null);
  React.useEffect(() => {
    function onDoc(e) { if (ref.current && !ref.current.contains(e.target)) setOpen(false); }
    document.addEventListener("mousedown", onDoc);
    return () => document.removeEventListener("mousedown", onDoc);
  }, []);
  const { cases, currentId, displayLabel } = casesApi;
  return (
    <div className="case-menu-wrap" ref={ref}>
      <button type="button" className="btn-ghost case-open" aria-haspopup="listbox" aria-expanded={open} onClick={() => setOpen(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>
      {open && (
        <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 (casesApi.open(c.id)) setOpen(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" onClick={() => { if (window.confirm(`Delete "${displayLabel(c.num, c.label)}"?`)) casesApi.remove(c.id); }}>×</button>
            </div>
          ))}
          <div className="case-menu-note">Cases are stored in this browser only.</div>
        </div>
      )}
    </div>
  );
}

const root = ReactDOM.createRoot(document.getElementById("app-root"));
root.render(<RDApp />);
