/* ============================================================
   App root: state, persistence, top bar.
   ============================================================ */
;(function(){
const { CALLS, parseAnalysis, loadDemo, fileStem } = window.AIData;
const { Icon, useIsMobile } = window.UI;
const { Table } = window.AITable;
const { CallModal } = window.AIModal;
const { LoaderModal } = window.AILoader;

const OPEN_KEY = "aic_pca_open";
const PH_KEY = "aic_pca_playhead"; // mirror of modal.jsx, cleared on reset
const AIStore = window.AIStore;

function TopBar() {
  const isMobile = useIsMobile();
  return (
    <header style={{
      position: "sticky", top: 0, zIndex: 40, display: "flex", alignItems: "center", gap: 14,
      padding: isMobile ? "0 16px" : "0 32px", height: 58, borderBottom: "1px solid var(--border-subtle)",
      background: "rgba(15,15,15,0.72)", backdropFilter: "blur(10px)", WebkitBackdropFilter: "blur(10px)",
    }}>
      <img src="assets/logo/aic-mark-on-black.png" alt="" style={{ width: 24, height: 24, borderRadius: 5 }} />
      <span style={{ fontFamily: "var(--font-display)", fontSize: 18, letterSpacing: "-0.01em", color: "var(--text-primary)" }}>ai-coustics</span>
      <span style={{ fontFamily: "var(--font-mono)", fontSize: 12, color: "var(--text-tertiary)", letterSpacing: "0.04em" }}>/ SDK</span>
      <div style={{ flex: 1 }} />
      <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "6px 12px", border: "1px solid var(--border-subtle)", borderRadius: "var(--radius-md)", color: "var(--text-secondary)", fontSize: 12.5, fontFamily: "var(--font-mono)" }}>
        <span style={{ width: 7, height: 7, borderRadius: "50%", background: "var(--teal)" }} /> Production
      </div>
    </header>
  );
}

function App() {
  const [calls, setCalls] = React.useState(CALLS);
  const [isCustom, setIsCustom] = React.useState(false);
  const [restored, setRestored] = React.useState(false); // came from local storage vs fresh upload
  const [restoring, setRestoring] = React.useState(true);
  const [openId, setOpenId] = React.useState(() => {
    try { return JSON.parse(localStorage.getItem(OPEN_KEY)); } catch (e) { return null; }
  });
  const [loaderOpen, setLoaderOpen] = React.useState(false);
  const [riskMetric, setRiskMetric] = React.useState("p95"); // shared by the table + modal
  const urlsRef = React.useRef([]); // object URLs we created, so we can revoke them

  // swap in a custom dataset, revoking any object URLs from the previous one
  const setCustomCalls = (rows, fromRestore) => {
    urlsRef.current.forEach((u) => { try { URL.revokeObjectURL(u); } catch (e) {} });
    urlsRef.current = rows.map((c) => c.audioUrl).filter((u) => u && u.indexOf("blob:") === 0);
    setCalls(rows); setIsCustom(true); setRestored(!!fromRestore);
  };

  // load the bundled demo (real Tyto data + hosted audio); falls back to the built-in set
  const loadDefault = async () => {
    try { const demo = await loadDemo(); if (demo && demo.length) { setCalls(demo); return; } }
    catch (e) { console.warn("Tyto: using built-in fallback data", e); }
    setCalls(CALLS);
  };

  // on mount: restore a saved session from IndexedDB, else show the bundled demo
  React.useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        if (AIStore && AIStore.available()) {
          const text = await AIStore.loadDataset();
          if (!cancelled && text) {
            const rows = parseAnalysis(text);
            const blobs = await AIStore.loadAllAudio();
            const byStem = {};
            Object.keys(blobs).forEach((n) => { byStem[fileStem(n)] = blobs[n]; });
            rows.forEach((c) => { const b = byStem[fileStem(c.file)]; if (b) c.audioUrl = URL.createObjectURL(b); });
            if (!cancelled) setCustomCalls(rows, true);
            return; // returning visitor, done
          }
        }
        if (!cancelled) await loadDefault(); // first visit, bundled demo
      } catch (e) { console.warn("Tyto: could not restore session", e); }
      finally { if (!cancelled) setRestoring(false); }
    })();
    return () => { cancelled = true; };
  }, []);

  const openCall = calls.find((c) => String(c.id) === String(openId)) || null;
  const open = (c) => { setOpenId(c.id); try { localStorage.setItem(OPEN_KEY, JSON.stringify(c.id)); } catch (e) {} };
  const close = () => { setOpenId(null); try { localStorage.removeItem(OPEN_KEY); } catch (e) {} };

  // clear stored data and return to the bundled demo set
  const reset = () => {
    urlsRef.current.forEach((u) => { try { URL.revokeObjectURL(u); } catch (e) {} });
    urlsRef.current = [];
    if (AIStore && AIStore.available()) AIStore.clearAll();
    try { localStorage.removeItem(OPEN_KEY); localStorage.removeItem(PH_KEY); } catch (e) {}
    setIsCustom(false); setRestored(false); setOpenId(null);
    loadDefault();
  };

  if (restoring) {
    return (
      <React.Fragment>
        <TopBar />
        <div style={{ display: "flex", alignItems: "center", justifyContent: "center", gap: 9, height: "60vh", color: "var(--text-tertiary)", fontFamily: "var(--font-mono)", fontSize: 13 }}>
          <Icon name="arrow.triangle.2.circlepath" size={15} color="var(--text-tertiary)" /> Loading…
        </div>
      </React.Fragment>
    );
  }

  return (
    <React.Fragment>
      <TopBar />
      <Table calls={calls} onOpen={open} onLoadClick={() => setLoaderOpen(true)} isCustom={isCustom} restored={restored} onReset={reset} riskMetric={riskMetric} setRiskMetric={setRiskMetric} />
      {openCall && <CallModal call={openCall} onClose={close} riskMetric={riskMetric} />}
      {loaderOpen && <LoaderModal onClose={() => setLoaderOpen(false)} onLoad={(c) => { setCustomCalls(c, false); setOpenId(null); }} />}
    </React.Fragment>
  );
}

ReactDOM.createRoot(document.getElementById("app")).render(<App />);
})();
