/* ============================================================
   IRONLOG — App shell: nav, admin mode, /panel gate, footer
   ============================================================ */

const NAV = [
  ["snapshot", "Snapshot"], ["goal", "Goal"], ["prs", "PRs"],
  ["calendar", "Calendar"], ["supplements", "Supplements"],
];

/* ---------- Scroll progress + active section ---------- */
function useScrollProgress() {
  const [p, setP] = useState(0);
  const [active, setActive] = useState("snapshot");
  useEffect(() => {
    const onScroll = () => {
      const h = document.documentElement;
      const max = h.scrollHeight - h.clientHeight;
      setP(max > 0 ? h.scrollTop / max : 0);
      // active section
      let cur = "snapshot";
      for (const [id] of NAV) {
        const el = document.getElementById(id);
        if (el && el.getBoundingClientRect().top <= 120) cur = id;
      }
      setActive(cur);
    };
    window.addEventListener("scroll", onScroll, { passive: true });
    onScroll();
    return () => window.removeEventListener("scroll", onScroll);
  }, []);
  return { p, active };
}

function Nav({ admin, onExitAdmin }) {
  const { p, active } = useScrollProgress();
  const [solid, setSolid] = useState(false);
  useEffect(() => {
    const f = () => setSolid(window.scrollY > 40);
    window.addEventListener("scroll", f, { passive: true }); f();
    return () => window.removeEventListener("scroll", f);
  }, []);
  return (
    <nav className={"nav" + (solid ? " solid" : "")}>
      <div className="nav-inner container">
        <a href="#snapshot" className="brand" aria-label="IRONLOG home">
          <span className="brand-mark" aria-hidden="true"><i/><i/><i/></span>
          <span className="brand-word display">BrotherVader</span>
        </a>
        <div className="nav-links">
          {NAV.map(([id, label]) => (
            <a key={id} href={"#" + id} className={"nav-link head" + (active === id ? " on" : "")}>{label}</a>
          ))}
        </div>
        <div className="nav-right">
          {admin ? (
            <div className="admin-badge">
              <span className="admin-dot" /> Admin
              <button className="btn btn-ghost" style={{ marginLeft: 6 }} onClick={onExitAdmin} aria-label="Log out"><Icons.Logout size={15}/></button>
            </div>
          ) : (
            <a href="#/panel" className="btn btn-ghost nav-lock" aria-label="Admin panel"><Icons.Lock size={15}/></a>
          )}
        </div>
      </div>
      <div className="nav-progress" style={{ transform: `scaleX(${p})` }} />
    </nav>
  );
}

/* ---------- Footer w/ hidden mode toggle ---------- */
function Footer({ admin, data, onExport, onToggleAdmin }) {
  return (
    <footer className="footer">
      <div className="container footer-inner">
        <div className="footer-brand">
          <span className="brand-word display" style={{ fontSize: 30 }}>BrotherVader</span>
          <p className="dim" style={{ maxWidth: 320, marginTop: 10 }}>A personal record of the work. {data.profile.name} · {data.profile.federation}.</p>
        </div>
        <div className="footer-cols">
          <div className="footer-col">
            <span className="eyebrow">Sections</span>
            {NAV.map(([id, l]) => <a key={id} href={"#"+id} className="footer-link">{l}</a>)}
          </div>
          <div className="footer-col">
            <span className="eyebrow">Account</span>
            {admin
              ? <><button className="footer-link as-btn" onClick={onExport}><Icons.Download size={13}/> Export all sessions</button>
                  <a href="#snapshot" className="footer-link">Public view</a></>
              : <a href="#/panel" className="footer-link">Admin panel</a>}
          </div>
        </div>
      </div>
      <div className="container footer-base">
        <span className="dim tnum">© 2026 BrotherVader · Built, not born.</span>
        <button className="footer-toggle" onClick={onToggleAdmin}
          aria-label="Toggle admin mode (prototype)" title="Prototype: toggle admin mode">
          <Icons.Lock size={13} /> {admin ? "exit admin" : "demo admin"}
        </button>
      </div>
    </footer>
  );
}

/* ---------- Password gate (/panel) — real server-side check ---------- */
function PanelGate({ onSuccess }) {
  const [pw, setPw] = useState("");
  const [shake, setShake] = useState(false);
  const [busy, setBusy] = useState(false);
  const [err, setErr] = useState("");
  const ref = useRef(null);
  useEffect(() => { ref.current && ref.current.focus(); }, []);
  function fail(msg) { setErr(msg); setShake(true); setTimeout(() => setShake(false), 500); }
  async function submit(e) {
    e.preventDefault();
    if (!pw.trim()) return fail("Enter the passcode.");
    setBusy(true); setErr("");
    try {
      // The password is sent to the server and compared there — it is
      // never checked in the browser and never lives in the bundle.
      await API.login(pw);
      onSuccess();
    } catch (ex) {
      fail(ex.message || "Incorrect passcode");
    } finally {
      setBusy(false);
    }
  }
  return (
    <div className="gate">
      <div className="gate-bg" aria-hidden="true" />
      <form className={"gate-card" + (shake ? " shake" : "")} onSubmit={submit}>
        <a href="#snapshot" className="brand gate-brand">
          <span className="brand-mark" aria-hidden="true"><i/><i/><i/></span>
          <span className="brand-word display">BrotherVader</span>
        </a>
        <div className="gate-lock"><Icons.Lock size={22} /></div>
        <h1 className="display gate-h">RESTRICTED</h1>
        <p className="muted gate-sub">Enter the passcode to manage the ledger.</p>
        <input ref={ref} className="input gate-input tnum" type="password" value={pw}
          onChange={(e) => { setPw(e.target.value); setErr(""); }} placeholder="• • • • • • • •" aria-label="Passcode" disabled={busy} />
        <button type="submit" className="btn btn-primary gate-btn" disabled={busy}>{busy ? "Checking…" : <>Enter <Icons.Arrow size={15}/></>}</button>
        <a href="#snapshot" className="gate-back muted">← Back to the page</a>
        <p className={"gate-hint " + (err ? "gate-err" : "dim")}>{err || "Authorized access only."}</p>
      </form>
    </div>
  );
}

/* ---------- Export (real .txt download from the server) ---------- */
async function exportSessions() {
  try {
    await API.exportSessions();
  } catch (ex) {
    alert(ex.message || "Could not export sessions.");
  }
}

/* ============================================================
   APP
   ============================================================ */
function App() {
  const [admin, setAdmin] = useState(false);
  const [route, setRoute] = useState(() => (location.hash === "#/panel" ? "panel" : "home"));

  // data state — seeded with the design's mock shape, then hydrated from the API.
  const [transformation, setTransformation] = useState(IRONLOG.transformation);
  const [goal, setGoal] = useState(IRONLOG.goal);
  const [prsMain, setPrsMain] = useState(IRONLOG.prsMain);
  const [prsOther, setPrsOther] = useState(IRONLOG.prsOther);
  const [philosophy, setPhilosophy] = useState(IRONLOG.philosophy);
  const [supplements, setSupplements] = useState(IRONLOG.supplements);
  const [log, setLog] = useState(IRONLOG.log);
  const [blocks, setBlocks] = useState([]);
  const [foods, setFoods] = useState({});
  const [posts, setPosts] = useState([]);
  const [video, setVideo] = useState(null);
  const [profileMeta, setProfileMeta] = useState(IRONLOG.profile);
  const [hydrated, setHydrated] = useState(false);

  useEffect(() => {
    const onHash = () => setRoute(location.hash === "#/panel" ? "panel" : "home");
    window.addEventListener("hashchange", onHash);
    return () => window.removeEventListener("hashchange", onHash);
  }, []);

  // ---- Hydrate from the backend (and restore admin session) ----
  const hydrate = useCallback(async () => {
    try {
      const [me, profile, prs, sessions, supps, blks, fds, psts, vid] = await Promise.all([
        API.me(), API.getProfile(), API.getPRs(), API.getSessions(), API.getSupplements(), API.getBlocks(),
        API.getFoods(), API.getPosts(), API.getVideo(),
      ]);
      setAdmin(!!me.admin);
      if (profile) {
        if (profile.transformation) setTransformation(profile.transformation);
        if (profile.goal) setGoal(profile.goal);
        if (profile.philosophy) setPhilosophy(profile.philosophy);
        const meta = { name: profile.name, handle: profile.handle, discipline: profile.discipline, federation: profile.federation, age: profile.age, heightCm: profile.heightCm, status: profile.status };
        setProfileMeta(meta);
        Object.assign(IRONLOG.profile, meta); // keep inline global reads (Hero/Footer) live
      }
      if (prs) { setPrsMain(prs.main || []); setPrsOther(prs.other || []); }
      if (sessions && Object.keys(sessions).length) setLog(sessions);
      if (supps) setSupplements(supps);
      if (blks) setBlocks(blks);
      if (fds) setFoods(fds);
      if (psts) setPosts(psts);
      if (vid) setVideo(vid);
    } catch (ex) {
      console.warn("[ironlog] hydrate failed, showing seed data:", ex.message);
    } finally {
      setHydrated(true);
    }
  }, []);
  useEffect(() => { hydrate(); }, [hydrate]);

  // Fire-and-forget visit beacon — the server resolves city/device/source and
  // notifies the owner over Telegram. Deduped per browser session.
  useEffect(() => {
    try {
      if (!sessionStorage.getItem("bv_visited")) {
        sessionStorage.setItem("bv_visited", "1");
        API.visit(document.referrer || "");
      }
    } catch (e) { API.visit(document.referrer || ""); }
  }, []);

  const oops = (ex) => alert((ex && ex.message) || "Something went wrong. Please try again.");

  // ---- Content handlers (optimistic UI + server persistence) ----
  const onUpdateTransformation = async (which, rec) => {
    const prev = transformation;
    const next = { ...transformation, [which]: rec };
    setTransformation(next);
    try { await API.saveProfile({ transformation: { ...next, [which]: { ...rec, imageId: rec.imageId } } }); }
    catch (ex) { setTransformation(prev); oops(ex); }
  };
  const onUpdateGoal = async (g) => { const prev = goal; setGoal(g); try { await API.saveProfile({ goal: g }); } catch (ex) { setGoal(prev); oops(ex); } };
  const onUpdatePhilosophy = async (p) => { const prev = philosophy; setPhilosophy(p); try { await API.saveProfile({ philosophy: p }); } catch (ex) { setPhilosophy(prev); oops(ex); } };

  const onUploadImage = async (which) => {
    const picked = await pickImageFile();
    if (!picked) return;
    try {
      const r = await API.upload(picked.dataUrl, { target: "profile", which });
      setTransformation(t => ({ ...t, [which]: { ...t[which], image: (r.images && r.images[0]) || r.url, images: r.images || [r.url], imageId: (r.imageIds && r.imageIds[0]) || r.id, imageIds: r.imageIds || [r.id] } }));
    } catch (ex) { oops(ex); }
  };
  const onRemoveImage = async (which, imageId) => {
    try {
      const r = await API.deleteProfilePhoto(which, imageId);
      setTransformation(t => ({ ...t, [which]: { ...t[which], images: r.images || [], imageIds: r.imageIds || [], image: (r.images && r.images[0]) || null, imageId: (r.imageIds && r.imageIds[0]) || null } }));
    } catch (ex) { oops(ex); }
  };

  const onSavePR = async (p) => {
    const upd = (arr) => arr.map(x => x.id === p.id ? { ...x, ...p } : x);
    setPrsMain(m => upd(m)); setPrsOther(o => upd(o));
    try {
      await API.savePR(p.id, { lift: p.lift, value: p.value, unit: p.unit });
      const fresh = await API.getPRs(); setPrsMain(fresh.main || []); setPrsOther(fresh.other || []);
    } catch (ex) { const fresh = await API.getPRs(); setPrsMain(fresh.main || []); setPrsOther(fresh.other || []); oops(ex); }
  };
  const onAddPR = async (p) => {
    try {
      const created = await API.addPR({ lift: p.lift, value: p.value, unit: p.unit, category: p.category || "other" });
      if ((p.category || "other") === "main") setPrsMain(m => [...m, created]);
      else setPrsOther(o => [...o, created]);
    } catch (ex) { oops(ex); }
  };
  const onDeletePR = async (id) => {
    const prevM = prsMain, prevO = prsOther;
    setPrsMain(m => m.filter(x => x.id !== id)); setPrsOther(o => o.filter(x => x.id !== id));
    try { await API.deletePR(id); } catch (ex) { setPrsMain(prevM); setPrsOther(prevO); oops(ex); }
  };

  const onSaveLog = async (k, e) => {
    const prev = log;
    setLog(l => ({ ...l, [k]: e }));
    try { const saved = await API.saveSession(k, e); setLog(l => ({ ...l, [k]: saved })); }
    catch (ex) { setLog(prev); oops(ex); }
  };

  const onSaveSupp = async (it) => {
    const prev = supplements;
    setSupplements(s => s.map(x => x.id === it.id ? it : x));
    try { await API.saveSupplement(it.id, it); } catch (ex) { setSupplements(prev); oops(ex); }
  };
  const onAddSupp = async (it) => {
    try { const created = await API.addSupplement(it); setSupplements(s => [...s, created]); } catch (ex) { oops(ex); }
  };
  const onRemoveSupp = async (id) => {
    const prev = supplements;
    setSupplements(s => s.filter(x => x.id !== id));
    try { await API.deleteSupplement(id); } catch (ex) { setSupplements(prev); oops(ex); }
  };

  // ---- Journey-wall blocks (extra DB-stored divs) ----
  const onAddBlock = async (b) => { try { const created = await API.addBlock(b); setBlocks(bl => [...bl, created]); } catch (ex) { oops(ex); } };
  const onSaveBlock = async (b) => {
    const prev = blocks;
    setBlocks(bl => bl.map(x => x.id === b.id ? b : x));
    try { const saved = await API.saveBlock(b.id, b); setBlocks(bl => bl.map(x => x.id === b.id ? saved : x)); }
    catch (ex) { setBlocks(prev); oops(ex); }
  };
  const onRemoveBlock = async (id) => {
    const prev = blocks;
    setBlocks(bl => bl.filter(x => x.id !== id));
    try { await API.deleteBlock(id); } catch (ex) { setBlocks(prev); oops(ex); }
  };

  // ---- Food calendar ----
  const onSaveFood = async (k, e) => {
    const prev = foods;
    setFoods(f => ({ ...f, [k]: { ...e, date: e.date || k, dateKey: k } }));
    try { const saved = await API.saveFood(k, e); setFoods(f => ({ ...f, [k]: saved })); }
    catch (ex) { setFoods(prev); oops(ex); }
  };
  const onDeleteFood = async (k) => {
    const prev = foods;
    setFoods(f => { const n = { ...f }; delete n[k]; return n; });
    try { await API.deleteFood(k); } catch (ex) { setFoods(prev); oops(ex); }
  };

  // ---- Daily posts + comments ----
  const onAddPost = async (p) => { try { const created = await API.addPost(p); setPosts(ps => [created, ...ps]); } catch (ex) { oops(ex); } };
  const onSavePost = async (p) => {
    const prev = posts;
    setPosts(ps => ps.map(x => x.id === p.id ? { ...x, ...p } : x));
    try { const saved = await API.savePost(p.id, p); setPosts(ps => ps.map(x => x.id === p.id ? { ...saved, comments: x.comments } : x)); }
    catch (ex) { setPosts(prev); oops(ex); }
  };
  const onDeletePost = async (id) => {
    const prev = posts;
    setPosts(ps => ps.filter(x => x.id !== id));
    try { await API.deletePost(id); } catch (ex) { setPosts(prev); oops(ex); }
  };
  const onComment = (scope, text, name) => API.addComment(scope, text, name); // returns the saved comment

  // ---- Video ----
  const onSaveVideo = async (v) => { const prev = video; setVideo(v); try { const saved = await API.saveVideo(v); setVideo(saved); } catch (ex) { setVideo(prev); oops(ex); } };

  // ---- Session / admin lifecycle ----
  const logout = async () => { try { await API.logout(); } catch (e) {} setAdmin(false); };
  // Footer toggle stays, but routes through the secure flow (no client-side bypass).
  const onToggleAdmin = () => { if (admin) logout(); else { location.hash = "#/panel"; } };

  if (route === "panel") {
    return <PanelGate onSuccess={() => { setAdmin(true); location.hash = "#snapshot"; }} />;
  }

  return (
    <div className={(admin ? "admin" : "") + (hydrated ? " hydrated" : " hydrating")}>
      <Nav admin={admin} onExitAdmin={logout} />
      <main>
        <Hero data={transformation} admin={admin}
          onUpdate={onUpdateTransformation}
          onUploadImage={onUploadImage} onRemoveImage={onRemoveImage} />
        <Goal data={goal} admin={admin} onUpdate={onUpdateGoal} />
        <PRs main={prsMain} other={prsOther} admin={admin} onSavePR={onSavePR} onAddPR={onAddPR} onDeletePR={onDeletePR} />
        <Philosophy data={philosophy} admin={admin} onUpdate={onUpdatePhilosophy} />
        <Calendar log={log} admin={admin} onSaveLog={onSaveLog} />
        <FoodCalendar foods={foods} admin={admin} onSaveFood={onSaveFood} onDeleteFood={onDeleteFood} />
        <VideoBlock video={video} admin={admin} onSave={onSaveVideo} />
        <Gallery blocks={blocks} admin={admin} onAdd={onAddBlock} onSave={onSaveBlock} onRemove={onRemoveBlock} />
        <DailyLog posts={posts} admin={admin} onAddPost={onAddPost} onSavePost={onSavePost} onDeletePost={onDeletePost} onComment={onComment} />
        <Supplements items={supplements} admin={admin} onSave={onSaveSupp} onAdd={onAddSupp} onRemove={onRemoveSupp} />
      </main>
      <Footer admin={admin} data={{ profile: profileMeta }} onExport={exportSessions}
        onToggleAdmin={onToggleAdmin} />
      <ChatDock />
    </div>
  );
}

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