/* =====================================================
   Betting Tracker — app shell
   Store (reducer) · site nav + in-app tab bar · PT/EN ·
   save/export flow (File System Access-style, simulated) ·
   local autosave · mount.
   ===================================================== */

const { useReducer } = React;

const LS_STATE = "doinp.tracker.state";
const LS_VIEW = "doinp.tracker.view";
const LS_LANG = "doinp.lang";

// the localStorage safety-net copy, but ONLY if it holds real bets — used to
// offer “resume last session” instead of silently resurrecting stale data.
function readLocalState() {
  try { const s = localStorage.getItem(LS_STATE); if (s) { const p = JSON.parse(s); if (p && p.bets && p.bets.length) return p; } } catch (_) {}
  return null;
}

function reducer(state, a) {
  switch (a.type) {
    case "load": return a.state;
    case "add-bet": return { ...state, bets: [...state.bets, a.bet] };
    case "update-bet": return { ...state, bets: state.bets.map((b) => (b.id === a.bet.id ? a.bet : b)) };
    case "delete-bet": return { ...state, bets: state.bets.filter((b) => b.id !== a.id) };
    case "set-setting": return { ...state, settings: { ...state.settings, [a.key]: a.value } };
    case "add-txn": return { ...state, transactions: [...state.transactions, a.txn] };
    case "update-txn": return { ...state, transactions: state.transactions.map((x) => (x.id === a.txn.id ? a.txn : x)) };
    case "delete-txn": return { ...state, transactions: state.transactions.filter((t) => t.id !== a.id) };
    case "add-wallet":
      return {
        ...state,
        bankrolls: [...state.bankrolls, a.wallet],
        transactions: [...state.transactions, { id: "t" + Math.random().toString(36).slice(2, 7), bankrollId: a.wallet.id, type: "deposit", date: TRK.today(), amount: a.wallet.initial, note: "Opening balance" }]
      };
    case "update-wallet":
      return { ...state, bankrolls: state.bankrolls.map((w) => (w.id === a.wallet.id ? a.wallet : w)) };
    case "delete-wallet": {
      if (state.bankrolls.length <= 1) return state;
      var fallback = state.bankrolls.find((w) => w.id !== a.id);
      return {
        ...state,
        bankrolls: state.bankrolls.filter((w) => w.id !== a.id),
        transactions: state.transactions.filter((t) => t.bankrollId !== a.id),
        bets: state.bets.map((b) => (b.bankrollId === a.id ? { ...b, bankrollId: fallback.id } : b))
      };
    }
    case "mark-saved": return { ...state, meta: { ...state.meta, lastSaved: a.when } };
    default: return state;
  }
}

/* ---- site nav (matches Doinp shell) ---- */
function SiteNav({ lang, setLang, t }) {
  return (
    <header className="nav" data-screen-label="Nav">
      <a className="nav__brand" href="../index">
        <span className="nav__pfp" aria-hidden="true">
          <span className="pfp-ring__glow"></span>
          <span className="pfp-ring__band"></span>
          <img src="../assets/me.jpg" alt="" width="44" height="44" />
        </span>
        <span className="nav__name">Doinp</span>
      </a>
      <nav aria-label="Primary">
        <ul className="nav__links">
          <li><a href="../index">{t("nav.home")}</a></li>
          <li><a href="../prophet">{t("nav.models")}</a></li>
          <li><a href="../tools" className="is-active">{t("nav.tools")}</a></li>
          <li><a href="../lol">{t("nav.lol")}</a></li>
        </ul>
      </nav>
      <div className="nav__lang" data-lang={lang} role="group" aria-label="Language">
        <button type="button" aria-pressed={String(lang === "pt")} onClick={() => setLang("pt")}>PT</button>
        <button type="button" aria-pressed={String(lang === "en")} onClick={() => setLang("en")}>EN</button>
      </div>
    </header>
  );
}

/* ---- in-app tab bar ---- */
function AppBar({ tab, setTab, t, state, dirty, onSave, onExport, onClose, saving }) {
  const tabs = [
    { id: "dashboard", icon: "dashboard", label: t("tab.dashboard") },
    { id: "bets", icon: "bets", label: t("tab.bets") },
    { id: "analytics", icon: "analytics", label: t("tab.analytics") },
    { id: "bankroll", icon: "bankroll", label: t("tab.bankroll") }
  ];
  const saved = state.meta.lastSaved;
  return (
    <div className="trk-appbar">
      <div className="trk-appbar__file">
        <span className="trk-appbar__fileic"><Icon name="file" size={16} /></span>
        <div className="trk-appbar__filemeta">
          <span className="trk-appbar__filename">{state.meta.name}</span>
          <span className={"trk-appbar__status" + (dirty ? " is-dirty" : "")}>
            <span className="trk-appbar__dot"></span>
            {dirty ? t("app.unsaved") : (saved ? t("app.lastsaved") + " " + new Date(saved).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }) : t("app.autosaved"))}
          </span>
        </div>
      </div>
      <nav className="trk-tabs" aria-label="Tracker sections">
        {tabs.map((tb) => (
          <button key={tb.id} className={"trk-tab" + (tab === tb.id ? " is-active" : "")} onClick={() => setTab(tb.id)}>
            <Icon name={tb.icon} size={16} /><span>{tb.label}</span>
          </button>
        ))}
      </nav>
      <div className="trk-appbar__actions">
        <button className={"trk-iconbtn trk-iconbtn--lg" + (tab === "settings" ? " is-active" : "")}
          onClick={() => setTab(tab === "settings" ? "dashboard" : "settings")}
          aria-label={t("set.title")} title={t("set.title")} aria-pressed={String(tab === "settings")}><Icon name="gear" size={18} /></button>
        <Btn variant="primary" icon="save" size="sm" onClick={onSave} disabled={saving}>
          {saving ? t("app.saving") : t("app.save")}
        </Btn>
        <button className="trk-iconbtn trk-iconbtn--lg" onClick={onClose} aria-label={t("app.close")} title={t("app.close")}><Icon name="close" size={17} /></button>
      </div>
    </div>
  );
}

/* ---- save flow modal (File System Access-style, simulated) ---- */
function SaveModal({ open, onClose, onConfirm, onExport, t, state }) {
  const sheets = [
    { n: "Bets", c: state.bets.length }, { n: "Bankrolls", c: state.bankrolls.length },
    { n: "Transactions", c: state.transactions.length }, { n: "Models", c: state.models.length },
    { n: "Settings", c: 1 }, { n: "Read me", c: "\u2713" }
  ];
  return (
    <Modal open={open} onClose={onClose} title={t("app.save")} width={480}
      footer={<>
        <Btn variant="ghost" icon="download" onClick={onExport}>{t("app.export")}</Btn>
        <Btn variant="primary" icon="save" onClick={onConfirm}>{t("app.save")}</Btn>
      </>}>
      <p className="trk-savenote">
        <Icon name="lock" size={14} /> BettingTracker.xlsx is written straight back to your device — nothing is uploaded.
      </p>
      <div className="trk-sheetlist">
        {sheets.map((s) => (
          <div className="trk-sheet" key={s.n}>
            <span className="trk-sheet__ic"><Icon name="layers" size={14} /></span>
            <span className="trk-sheet__n">{s.n}</span>
            <span className="trk-sheet__c">{s.c}</span>
          </div>
        ))}
      </div>
      <div className="trk-savehint">On Chrome & Edge this saves back to the same file you opened. Elsewhere it downloads a fresh copy to replace.</div>
    </Modal>
  );
}

function Footer({ t }) {
  return (
    <footer>
      <p className="footer__left">{t("footer.disclaimer")}</p>
      <p className="footer__mid">{"\u00a9 " + new Date().getFullYear() + " doinp.com.br \u00b7 "}{t("footer.rights")}</p>
      <a className="footer__right" href="../terms">{t("footer.terms")}</a>
    </footer>
  );
}

/* ---- toast ---- */
function Toast({ msg }) {
  if (!msg) return null;
  return <Portal><div className="trk-toast"><Icon name="check" size={16} /> {msg}</div></Portal>;
}

function App() {
  const [lang, setLangState] = useState(() => { try { return localStorage.getItem(LS_LANG) || "en"; } catch (_) { return "en"; } });
  const t = useCallback((k) => window.TRK_T(k, lang), [lang]);
  const setLang = (l) => { setLangState(l); try { localStorage.setItem(LS_LANG, l); } catch (_) {} };

  const [view, setView] = useState(() => { try { return localStorage.getItem(LS_VIEW) || "entry"; } catch (_) { return "entry"; } });
  const [state, dispatch] = useReducer(reducer, null, () => {
    try { const s = localStorage.getItem(LS_STATE); if (s) return JSON.parse(s); } catch (_) {}
    return TRK.emptyState();
  });
  const [tab, setTab] = useState("dashboard");
  const [dirty, setDirty] = useState(false);
  const [saving, setSaving] = useState(false);
  const [saveOpen, setSaveOpen] = useState(false);
  const [toast, setToast] = useState("");
  const [focusBet, setFocusBet] = useState(null);
  const [quickAdd, setQuickAdd] = useState(false); // global floating Add-bet drawer
  const [resume, setResume] = useState(() => readLocalState()); // offer to restore an unsaved local session
  const [fileHandle, setFileHandle] = useState(null); // File System Access handle for in-place save
  const firstRun = useRef(true);
  const skipDirty = useRef(false); // true when a programmatic load/save-stamp shouldn't flag “unsaved”

  // autosave to localStorage whenever data changes (the local safety-net pitch)
  useEffect(() => {
    try { localStorage.setItem(LS_STATE, JSON.stringify(state)); } catch (_) {}
    if (firstRun.current) { firstRun.current = false; return; }
    if (skipDirty.current) { skipDirty.current = false; return; }
    setDirty(true);
  }, [state]);
  useEffect(() => { try { localStorage.setItem(LS_VIEW, view); } catch (_) {} }, [view]);

  useEffect(() => { document.documentElement.lang = lang === "pt" ? "pt-BR" : "en"; }, [lang]);
  // keep the Units formatter in sync with the configured unit size
  useEffect(() => { TRK.setUnitSize(state.settings.unitSize); }, [state.settings.unitSize]);
  // keep the odds formatter (decimal / American / fractional) in sync
  useEffect(() => { TRK.setOddsFormat(state.settings.oddsFormat); }, [state.settings.oddsFormat]);
  // warn before leaving with unsaved changes (the file is the only durable copy)
  useEffect(() => {
    if (!dirty) return;
    const h = (e) => { e.preventDefault(); e.returnValue = ""; };
    window.addEventListener("beforeunload", h);
    return () => window.removeEventListener("beforeunload", h);
  }, [dirty]);

  const wrappedDispatch = useCallback((a) => dispatch(a), []);

  const onLoad = (newState, mode, handle) => {
    if (mode === "open") newState.meta.lastSaved = Date.now();
    skipDirty.current = true;
    dispatch({ type: "load", state: newState });
    setFileHandle(handle || null);
    setResume(null);
    // a brand-new tracker has no file on disk yet — flag it dirty so Save writes it
    setView("app"); setTab(mode === "new" ? "bets" : "dashboard"); setDirty(mode === "new");
    window.scrollTo(0, 0);
  };

  // resume the unsaved local session already sitting in state
  const doResume = () => {
    skipDirty.current = true;
    setResume(null); setView("app"); setTab("dashboard"); setDirty(false);
    window.scrollTo(0, 0);
  };
  const doDiscard = () => {
    if (!window.confirm(t("app.confirmDiscard"))) return;
    skipDirty.current = true;
    dispatch({ type: "load", state: TRK.emptyState() });
    setResume(null);
  };

  const toastFor = (m) => { setToast(m); setTimeout(() => setToast(""), 2400); };

  // real XLSX write-back: to the same file (FSA handle), else prompt a location, else download.
  // Any picker/permission failure (e.g. cross-origin iframe) falls back to a plain download.
  const doSave = async () => {
    setSaveOpen(false); setSaving(true);
    const io = window.TRK_XLSX;
    const name = state.meta.name || "BettingTracker.xlsx";
    let ok = false;
    try {
      if (fileHandle) {
        await io.writeHandle(fileHandle, state); ok = true;
      } else if (io.fsaSupported) {
        try { const h = await io.pickSaveNew(state, name); setFileHandle(h); ok = true; }
        catch (e) { if (e && e.name === "AbortError") { setSaving(false); return; } io.download(state, name); ok = true; }
      } else {
        io.download(state, name); ok = true;
      }
    } catch (e) {
      try { io.download(state, name); ok = true; } catch (_) {}
    }
    if (ok) {
      skipDirty.current = true;
      dispatch({ type: "mark-saved", when: Date.now() });
      setDirty(false);
      toastFor("Saved to " + name);
    }
    setSaving(false);
  };
  const doExport = () => {
    setSaveOpen(false);
    try { window.TRK_XLSX.download(state, "BettingTracker.xlsx"); toastFor("Copy downloaded"); } catch (_) {}
  };
  const closeTracker = () => {
    if (dirty && !window.confirm(t("app.confirmClose"))) return;
    setView("entry"); window.scrollTo(0, 0);
  };

  const go = useCallback((t, betId) => { setTab(t); if (betId) setFocusBet(betId); window.scrollTo(0, 0); }, []);

  const ctx = {
    state, dispatch: wrappedDispatch, lang, setLang, t, go,
    focusBet, clearFocus: () => setFocusBet(null)
  };

  return (
    <TrackerCtx.Provider value={ctx}>
      <div className="trk-shell">
        <SiteNav lang={lang} setLang={setLang} t={t} />
        {view === "entry" ? (
          <Entry onLoad={onLoad} resume={resume} onResume={doResume} onDiscard={doDiscard} />
        ) : (
          <main className="trk-main">
            <div className="trk-crumb">
              <a href="../tools">{t("crumb.tools")}</a> · <span>{t("crumb.type")}</span>
            </div>
            <AppBar tab={tab} setTab={setTab} t={t} state={state} dirty={dirty}
              onSave={() => setSaveOpen(true)} onExport={doExport} onClose={closeTracker} saving={saving} />
            <div className="trk-view">
              {tab === "dashboard" && <Dashboard />}
              {tab === "bets" && <Bets />}
              {tab === "bankroll" && <Bankroll />}
              {tab === "analytics" && <Analytics />}
              {tab === "settings" && <Settings />}
            </div>
          </main>
        )}
        <Footer t={t} />
        <SaveModal open={saveOpen} onClose={() => setSaveOpen(false)} onConfirm={doSave} onExport={doExport} t={t} state={state} />
        {view === "app" && (
          <Portal>
            <button className="trk-fab" onClick={() => setQuickAdd(true)} aria-label={t("bets.add")}>
              <Icon name="plus" size={19} /><span>{t("bets.add")}</span>
            </button>
          </Portal>
        )}
        <Drawer open={quickAdd} onClose={() => setQuickAdd(false)} title={t("form.new")}>
          {quickAdd && <BetForm initial={blankBet(state)} isNew
            onSave={(bet) => { dispatch({ type: "add-bet", bet }); setQuickAdd(false); toastFor(t("app.saved")); }}
            onDelete={() => {}}
            onCancel={() => setQuickAdd(false)} />}
        </Drawer>
        <Toast msg={toast} />
      </div>
    </TrackerCtx.Provider>
  );
}

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