/* ============================================================
   BridgeOS — app shell
   ============================================================ */
const PP = window.PP;

function useStore() {
  const [state, setState] = useState(() => {
    const loaded = PP.load();
    const s = loaded || PP.buildSample();
    PP.rebuildIndex((s.me && s.me.customTasks) || []);
    return s;
  });
  useEffect(() => { PP.save(state); }, [state]);
  return [state, setState];
}

function App() {
  const [state, setState] = useStore();
  const [tab, setTab] = useState("today");
  const [activeDate, setActiveDate] = useState(PP.todayISO());
  const [reviewWeek, setReviewWeek] = useState(null);
  const [msgTarget, setMsgTarget] = useState(null);
  const [levelTestOpen, setLevelTestOpen] = useState(false);
  const today = PP.todayISO();
  const me = state.me;
  const settings = state.settings;

  // ── Auth state ────────────────────────────────────────────────
  // undefined = still checking, null = not logged in, object = logged in
  const [authUser, setAuthUser] = useState(undefined);
  const [showAuthModal, setShowAuthModal] = useState(false);
  const [showNavDrawer, setShowNavDrawer] = useState(false);
  const [worldUnlocked, setWorldUnlocked] = useState(false);
  const [showThemeOnboard, setShowThemeOnboard] = useState(() => !localStorage.getItem('_themeSelected'));
  const syncTimerRef = useRef(null);
  const msgChannelRef = useRef(null);

  // Capture ?invite= param from URL immediately (before auth)
  useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const inviteId = params.get("invite");
    if (inviteId) {
      localStorage.setItem("_pendingInvite", inviteId);
      window.history.replaceState({}, "", window.location.pathname);
    }
  }, []);

  // Process pending invite once logged in
  useEffect(() => {
    if (!authUser) return;
    const pending = localStorage.getItem("_pendingInvite");
    if (!pending || pending === authUser.id) { localStorage.removeItem("_pendingInvite"); return; }
    localStorage.removeItem("_pendingInvite");
    Bridge.sendFriendRequest(authUser.id, pending).then(({ error }) => {
      if (error) {
        // 23505 = duplicate (already friends/requested) — not a real error
        if (error.code !== "23505") console.error("[Invite] sendFriendRequest failed:", error);
      } else {
        // Navigate to Messages so they see the confirmation and can wait for acceptance
        setTab("messages");
      }
    });
  }, [authUser]);

  useEffect(() => {
    const b = window.Bridge;
    if (!b) { setAuthUser(null); return; }
    b.getSession().then(session => {
      const user = session?.user ?? null;
      setAuthUser(user);
      // Show auth on first visit if not logged in
      if (!user) {
        const dismissed = localStorage.getItem("_bridgeAuthDismissed");
        if (!dismissed) setShowAuthModal(true);
      }
    });
    const { data: { subscription } } = b.onAuthChange((event, session) => {
      const user = session?.user ?? null;
      setAuthUser(user);
      if (event === "SIGNED_IN" && user) {
        b.ensureProfile(user).catch(() => {});
        setShowAuthModal(false);
      }
    });
    // expose for settings AccountPanel
    window._bridgeShowAuth = () => setShowAuthModal(true);
    return () => { subscription.unsubscribe(); delete window._bridgeShowAuth; };
  }, []);

  // Load cloud state once auth is known
  useEffect(() => {
    if (!authUser) return;
    const isSameUser = localStorage.getItem("_stateUserId") === authUser.id;
    Bridge.loadGameState(authUser.id).then(({ data }) => {
      if (!data || !data.me) {
        // Brand-new account — reset local state so old user's data doesn't linger
        if (!isSameUser) setState(PP.blankState());
        localStorage.setItem("_stateUserId", authUser.id);
        setLevelTestOpen(true);
        return;
      }
      const cloudDays = Object.keys(data.me.days || {}).length;
      const localDays = Object.keys(state.me.days || {}).length;
      // Different user or fresh device → always load cloud. Same user → prefer cloud if equal or more.
      if (!isSameUser || cloudDays >= localDays) {
        PP.rebuildIndex((data.me.customTasks) || []);
        setState(data);
      }
      localStorage.setItem("_stateUserId", authUser.id);
    });

    // Subscribe to incoming messages for badge updates
    msgChannelRef.current = Bridge.subscribeToMessages(authUser.id, (msg) => {
      setState(s => {
        const msgs = JSON.parse(JSON.stringify(s.messages || {}));
        const friendId = msg.from_id;
        msgs[friendId] = msgs[friendId] || [];
        if (!msgs[friendId].find(m => m.id === msg.id)) {
          msgs[friendId].push({ id: msg.id, from: friendId, text: msg.text, ts: new Date(msg.created_at).getTime(), read: false });
        }
        return { ...s, messages: msgs };
      });
    });

    // Subscribe to notifications
    const notifChannel = Bridge.subscribeToNotifications(authUser.id, (notif) => {
      setState(s => {
        const existing = (s.notifications || []);
        if (existing.find(n => n.id === notif.id)) return s;
        const newNotif = {
          id: notif.id,
          type: notif.type,
          friendId: notif.from_user_id,
          text: notif.text,
          icon: notif.icon,
          ts: new Date(notif.created_at).getTime(),
          read: false,
        };
        return { ...s, notifications: [newNotif, ...existing] };
      });
    });

    return () => {
      Bridge.unsubscribe(msgChannelRef.current);
      Bridge.unsubscribe(notifChannel);
    };
  }, [authUser]);

  // Refresh friends list whenever user navigates to messages or leaderboard
  // (catches cases where a request was accepted while the app was open)
  useEffect(() => {
    if (!authUser || !["messages", "leaderboard", "friends"].includes(tab)) return;
    Bridge.getFriends(authUser.id).then(({ data }) => {
      if (data) setState(s => ({ ...s, realFriends: data.map(Bridge.buildFriendProfile) }));
    });
  }, [tab, authUser]);

  // Load real friends from Supabase + strip demo friends on login
  useEffect(() => {
    if (!authUser) return;
    // Strip all demo data (short IDs = demo, UUIDs = real)
    setState(s => {
      const isDemo = id => id && id.length < 20;
      const cleanMsgs = {};
      Object.entries(s.messages || {}).forEach(([k, v]) => { if (!isDemo(k)) cleanMsgs[k] = v; });
      return {
        ...s,
        friends: (s.friends || []).filter(f => !isDemo(f.id)),
        notifications: (s.notifications || []).filter(n => !isDemo(n.friendId)),
        messages: cleanMsgs,
      };
    });
    Bridge.getFriends(authUser.id).then(({ data }) => {
      const realFriends = (data || []).map(Bridge.buildFriendProfile);
      setState(s => ({ ...s, realFriends }));
    });
  }, [authUser]);

  // Debounced cloud sync (4 s after last state change)
  useEffect(() => {
    if (!authUser) return;
    if (syncTimerRef.current) clearTimeout(syncTimerRef.current);
    syncTimerRef.current = setTimeout(async () => {
      // Never save demo friends to the cloud
      const saveState = (state.friends || []).some(f => f.id && f.id.length < 20)
        ? { ...state, friends: [] }
        : state;
      await Bridge.saveGameState(authUser.id, saveState);
      Bridge.updateLeaderboardSnapshot(authUser.id, {
        power: PP.power(state.me, PP.todayISO()),
        level: PP.profileLevel(state.me).level,
        streak: PP.streak(state.me, PP.todayISO()),
        scores: PP.decayedScores(state.me, PP.todayISO()),
      });
      if (state.me.meta) Bridge.updateProfile(authUser.id, {
        display_name: state.me.meta.name || null,
        avatar_url: state.me.meta.avatar || null,
      });
    }, 4000);
    return () => clearTimeout(syncTimerRef.current);
  }, [state, authUser]);

  // ── Theme ─────────────────────────────────────────────────────
  useEffect(() => {
    const id = PP.resolveThemeId(settings, today);
    const th = PP.THEMES[id] || PP.THEMES.quantum;
    const root = document.documentElement;
    Object.entries(th.vars).forEach(([k, v]) => root.style.setProperty(k, v));
    root.style.setProperty("--sans", th.sans);
    root.style.setProperty("--mono", th.mono);
    root.setAttribute("data-theme", id);
    root.setAttribute("data-pixel", th.pixel ? "1" : "0");
    root.setAttribute("data-density", (settings.organize || {}).density || "regular");
    if (PP.applyAmbient) PP.applyAmbient(id);
  }, [settings, today]);

  useEffect(() => { PP.rebuildIndex(me.customTasks || []); }, [me.customTasks]);
  useEffect(() => { if (!me.days[today]) updateMe((m) => { m.days[today] = PP.newDayRecord(today, null, m.presets); }); }, []);

  function updateMe(fn) { setState((s) => { const m = JSON.parse(JSON.stringify(s.me)); fn(m); return { ...s, me: m }; }); }

  const resolvedAnim = settings.completionAnim === "theme" ? (PP.THEMES[PP.resolveThemeId(settings, today)] || {}).anim : settings.completionAnim;
  const org = settings.organize || {};
  const needsCheckin = !me.seen[today] && !!me.days[today];

  const [toasts, setToasts] = useState([]);
  function onGain(task, values) {
    if (org.showXpToasts === false || !task || !task.fields) return;
    const tot = {};
    task.fields.forEach((f) => { const v = values && values[f.key] != null ? values[f.key] : f.default; Object.keys(f.stats || {}).forEach((s) => (tot[s] = (tot[s] || 0) + v * f.stats[s])); });
    Object.keys(tot).forEach((s) => {
      if (tot[s] < 1) return;
      const id = Date.now() + Math.random();
      setToasts((ts) => [...ts, { id, val: Math.round(tot[s]), stat: s, unit: PP.CATS[s].unit }]);
      setTimeout(() => setToasts((ts) => ts.filter((x) => x.id !== id)), 1600);
    });
  }

  function completeCheckin(data) { updateMe((m) => { Object.assign(m.days[today], data); m.seen[today] = true; }); }
  function skipCheckin() { updateMe((m) => (m.seen[today] = true)); }

  function newGame() {
    if (!confirm("Start a fresh run? This wipes your data on this device (rivals stay so you can compete). You'll begin with no presets — import a pack or build your own.")) return;
    const name = prompt("Name your character", "Player One");
    setState((s) => {
      const fresh = PP.blankState();
      if (name) fresh.me.meta.name = name;
      fresh.me.days[today] = PP.newDayRecord(today, null, fresh.me.presets);
      fresh.friends = s.friends; fresh.messages = s.messages; fresh.notifications = s.notifications;
      PP.rebuildIndex([]);
      return fresh;
    });
    setTab("today"); setActiveDate(today);
    setLevelTestOpen(true);
  }

  function wipeAccount() {
    if (!window.confirm("Permanently wipe all progress, friends, and cloud data? Cannot be undone.")) return;
    const blank = PP.blankState();
    Bridge.saveGameState(authUser.id, blank).catch(function() {});
    Bridge.clearAllFriendships(authUser.id).catch(function() {});
    localStorage.removeItem("_stateUserId");
    PP.rebuildIndex([]);
    setState(blank);
    setTab("today");
    setLevelTestOpen(true);
  }

  async function handleSignOut() {
    if (window.Bridge) await Bridge.signOut();
    localStorage.removeItem("_bridgeAuthDismissed");
    localStorage.removeItem("_stateUserId");
    setState(PP.buildSample()); // reset to demo data so previous user's info is gone
    setAuthUser(null);
    setShowAuthModal(true);
  }

  function handleAuthed(user, session) {
    setAuthUser(user);
    setShowAuthModal(false);
  }

  const lvl = PP.profileLevel(me);
  const pow = PP.power(me, today);
  const strk = PP.streak(me, today);

  // Merge real friends into the friends list shown on leaderboard/messages
  // null = explicit demo mode → show sample rivals; undefined (loading) or object (logged in) → real friends only
  const allFriends = authUser === null
    ? (state.friends || [])
    : (state.realFriends || []);

  let unreadMsgs = 0;
  Object.values(state.messages || {}).forEach((thread) => thread.forEach((m) => { if (m.from !== "me" && !m.read) unreadMsgs++; }));
  const unreadNotifs = (state.notifications || []).filter((n) => !n.read && !n.emoji).length;
  const totalUnread = unreadMsgs + unreadNotifs;

  const NAV = [
    { id: "today", label: "Today", icon: "◳" },
    { id: "calendar", label: "Calendar", icon: "▦" },
    { id: "friends", label: "Friends", icon: "⊹" },
    { id: "leaderboard", label: "Leaderboard", icon: "♛" },
    { id: "skills", label: "Skills", icon: "◈" },
    { id: "map", label: "World", icon: "⬡" },
    { id: "messages", label: "Messages", icon: "✉", badge: totalUnread },
    { id: "business", label: "Business", icon: "$" },
    { id: "goals", label: "Goals", icon: "◎" },
    { id: "character", label: "Profile", icon: "⬢" },
  ];
  const FOOT = [
    { id: "presets", label: "Presets", icon: "☰" },
    { id: "review", label: "Week Review", icon: "↻", action: () => setReviewWeek(PP.weekStart(today)) },
    { id: "settings", label: "Settings", icon: "⚙" },
  ];
  function nav(item) { if (item.action) item.action(); else setTab(item.id); }

  function NavBtn({ item, small }) {
    return (
      <button className={"nav-btn" + (small ? " small" : "") + (tab === item.id ? " on" : "")} onClick={() => nav(item)}>
        <span className="nav-ico">{item.icon}</span><span className="nav-lab">{item.label}</span>
        {item.badge > 0 && <span className="nav-badge">{item.badge}</span>}
      </button>
    );
  }

  // Auth loading splash
  if (authUser === undefined) {
    return <div className="auth-loading"><span className="brand-mark" style={{ fontSize: 40, color: "var(--accent)" }}>◆</span></div>;
  }

  // Theme onboarding — first visit only
  if (showThemeOnboard) {
    return <ThemeOnboarding
      onDone={() => setShowThemeOnboard(false)}
      setState={setState}
      settings={settings}
    />;
  }

  const stateWithFriends = { ...state, friends: allFriends };

  return (
    <div className="app">
      <aside className="sidebar">
        <div className="brand"><div className="brand-mark">◆</div><div className="brand-text">BRIDGE<span>OS</span></div></div>
        <button className="side-hero" onClick={() => setTab("character")}>
          <Avatar profile={me} size={46} />
          <div className="side-hero-meta">
            <div className="side-name">{me.meta.name}</div>
            <div className="side-strk mono">Lv {lvl.level} · ⚡{pow} · {strk > 0 ? `🔥${strk}d` : "0d"}</div>
          </div>
        </button>
        {authUser && <div className="side-sync-badge">● synced</div>}
        <nav className="nav">{NAV.map((n) => <NavBtn key={n.id} item={n} />)}</nav>
        <div className="side-foot">{FOOT.map((n) => <NavBtn key={n.id} item={n} small />)}</div>
      </aside>

      <main className="main">
        <div className="main-inner">
          {tab === "today" && <Checklist me={me} dateISO={activeDate} isToday={activeDate === today} updateMe={updateMe} today={today} onSetDate={setActiveDate} onGain={onGain} anim={resolvedAnim} organize={org} onOpenReview={() => setReviewWeek(PP.weekStart(today))} onEditPresets={() => setTab("presets")} readOnly={activeDate < PP.addDays(today, -2)} />}
          {tab === "calendar" && <Calendar me={me} updateMe={updateMe} today={today} onSetDate={setActiveDate} onGotoChecklist={() => setTab("today")} />}
          {tab === "friends" && <FriendsView state={stateWithFriends} today={today} authUser={authUser} onMessage={(fid) => { setMsgTarget(fid); setTab("messages"); }} onRefreshFriends={(removedId) => { if (removedId) setState(s => ({ ...s, realFriends: (s.realFriends||[]).filter(f => f.id !== removedId) })); if (authUser) Bridge.getFriends(authUser.id).then(({ data }) => { if (data) setState(s => ({ ...s, realFriends: data.map(Bridge.buildFriendProfile) })); }); }} />}
          {tab === "leaderboard" && <Leaderboard state={stateWithFriends} today={today} authUser={authUser} onMessage={(fid) => { setMsgTarget(fid); setTab("messages"); }} onRefreshFriends={(removedId) => { if (removedId) setState(s => ({ ...s, realFriends: (s.realFriends||[]).filter(f => f.id !== removedId) })); if (authUser) Bridge.getFriends(authUser.id).then(({ data }) => { if (data) setState(s => ({ ...s, realFriends: data.map(Bridge.buildFriendProfile) })); }); }} />}
          {tab === "map" && (worldUnlocked
            ? <WorldView state={stateWithFriends} today={today} setState={setState} onMessage={(fid) => { setMsgTarget(fid); setTab("messages"); }} />
            : <WorldLock onUnlock={() => setWorldUnlocked(true)} />
          )}
          {tab === "messages" && <Messages state={stateWithFriends} setState={setState} today={today} initialFriend={msgTarget} authUser={authUser} />}
          {tab === "skills" && <SkillsView me={me} updateMe={updateMe} today={today} />}
          {tab === "business" && <Business me={me} updateMe={updateMe} today={today} />}
          {tab === "goals" && <Goals me={me} updateMe={updateMe} today={today} />}
          {tab === "character" && <Character me={me} updateMe={updateMe} state={stateWithFriends} today={today} />}
          {tab === "presets" && <Presets me={me} updateMe={updateMe} friends={allFriends} />}
          {tab === "settings" && <SettingsView state={state} setState={setState} today={today} onNewGame={newGame} onCalibrate={() => setLevelTestOpen(true)} authUser={authUser} onSignOut={handleSignOut} onShowAuth={() => setShowAuthModal(true)} onWipeAccount={wipeAccount} />}
        </div>
      </main>

      <nav className="botnav">
        {NAV.filter(n => ["today","calendar","messages"].includes(n.id)).map((n) => (
          <button key={n.id} className={"botnav-btn" + (tab === n.id ? " on" : "")} onClick={() => { nav(n); setShowNavDrawer(false); }}>
            <span className="nav-ico">{n.icon}</span><span>{n.label}</span>
            {n.badge > 0 && <span className="nav-badge">{n.badge}</span>}
          </button>
        ))}
        {(() => {
          const allItems = [...NAV, ...FOOT];
          const onPrimary = ["today","calendar","messages"].includes(tab);
          const cur = !onPrimary && allItems.find(n => n.id === tab);
          return (
            <button className={"botnav-btn" + (!onPrimary || showNavDrawer ? " on" : "")} onClick={() => setShowNavDrawer(v => !v)}>
              <span className="nav-ico">{cur && !showNavDrawer ? cur.icon : "⊞"}</span>
              <span>{cur && !showNavDrawer ? cur.label : "Menu"}</span>
            </button>
          );
        })()}
      </nav>
      {showNavDrawer && <NavDrawer allItems={[...NAV, ...FOOT]} tab={tab} onNav={(item) => { nav(item); setShowNavDrawer(false); }} onClose={() => setShowNavDrawer(false)} />}

      {org.showXpToasts !== false && <XpToast toasts={toasts} />}

      {showAuthModal && (
        <AuthModal
          onAuthed={handleAuthed}
          onDemo={() => setShowAuthModal(false)}
        />
      )}

      {needsCheckin && !levelTestOpen && <CheckinModal today={today} record={me.days[today]} onComplete={completeCheckin} onSkip={skipCheckin} />}
      {levelTestOpen && <LevelTest name={me.meta.name} onDone={(seed) => { updateMe((m) => { m.meta.seed = seed; }); setLevelTestOpen(false); }} onSkip={() => setLevelTestOpen(false)} />}
      {reviewWeek && <WeekReviewModal me={me} mondayISO={reviewWeek} onClose={() => setReviewWeek(null)} onSaveNote={(note) => updateMe((m) => { m.weekReviews = m.weekReviews || {}; m.weekReviews[reviewWeek] = { note }; })} />}
    </div>
  );
}

function NavDrawer({ allItems, tab, onNav, onClose }) {
  return (
    <>
      <div className="nav-drawer-backdrop" onClick={onClose} />
      <div className="nav-drawer">
        <div className="nav-drawer-handle" onClick={onClose} />
        <div className="nav-drawer-grid">
          {allItems.map(item => (
            <button key={item.id} className={"nav-drawer-item" + (tab === item.id ? " on" : "")} onClick={() => onNav(item)}>
              <span className="nav-drawer-icon">{item.icon}</span>
              <span className="nav-drawer-label">{item.label}</span>
              {item.badge > 0 && <span className="nav-badge">{item.badge}</span>}
            </button>
          ))}
        </div>
      </div>
    </>
  );
}

function WorldLock({ onUnlock }) {
  const [pin, setPin] = useState("");
  const [shake, setShake] = useState(false);
  function tryPin() {
    if (pin === "11223") { onUnlock(); return; }
    setShake(true); setTimeout(() => setShake(false), 600);
    setPin("");
  }
  return (
    <div className="world-lock">
      <div className="world-lock-box" style={shake ? { animation: "lockShake .5s ease" } : {}}>
        <div className="world-lock-icon">⬡</div>
        <div className="world-lock-title">COMING SOON</div>
        <div className="world-lock-sub">The overworld is under construction. Check back soon.</div>
        <div className="world-lock-pin">
          <div className="field-label">Early Access PIN</div>
          <div className="row-inline">
            <input className="inp" type="password" value={pin} maxLength={8} placeholder="• • • • •"
              style={{ letterSpacing: 6, textAlign: "center", fontFamily: "var(--mono)" }}
              onChange={e => setPin(e.target.value)}
              onKeyDown={e => e.key === "Enter" && tryPin()} autoComplete="off" />
            <button className="btn btn-accent" onClick={tryPin}>→</button>
          </div>
        </div>
      </div>
    </div>
  );
}

function ThemeOnboarding({ onDone, setState, settings }) {
  const P = window.PP;
  const [selected, setSelected] = useState(settings.theme || "quantum");
  const [mode, setMode] = useState(settings.themeMode || "fixed");

  useEffect(() => {
    const th = P.THEMES[selected] || P.THEMES.quantum;
    const root = document.documentElement;
    Object.entries(th.vars).forEach(([k, v]) => root.style.setProperty(k, v));
    root.style.setProperty("--sans", th.sans);
    root.style.setProperty("--mono", th.mono);
  }, [selected]);

  function confirm() {
    setState(s => ({
      ...s,
      settings: {
        ...s.settings,
        theme: selected,
        themeMode: mode,
        rotation: mode !== "fixed" ? P.THEME_LIST.slice() : (s.settings.rotation || P.THEME_LIST.slice(0, 4)),
      },
    }));
    localStorage.setItem("_themeSelected", "1");
    onDone();
  }

  return (
    <div className="theme-onboard">
      <div className="theme-onboard-inner">
        <div className="theme-onboard-header">
          <div className="brand-mark" style={{ fontSize: 32, marginBottom: 8 }}>◆</div>
          <div className="theme-onboard-title">Welcome to BridgeOS</div>
          <div className="theme-onboard-sub">Pick your visual style — you can change it anytime in Settings.</div>
        </div>

        <div className="theme-onboard-grid">
          {P.THEME_LIST.map(id => {
            const th = P.THEMES[id];
            return (
              <button key={id}
                className={"theme-onboard-swatch" + (selected === id ? " on" : "")}
                style={{ background: th.vars["--bg"], borderColor: selected === id ? th.vars["--accent"] : th.vars["--line"] }}
                onClick={() => { setSelected(id); setMode("fixed"); }}>
                <div className="ts-bars" style={{ marginBottom: 8 }}>
                  <span style={{ background: th.vars["--accent"], height: 20, flex: 1, borderRadius: 3 }} />
                  <span style={{ background: th.vars["--panel2"], height: 20, flex: 1, borderRadius: 3 }} />
                  <span style={{ background: th.vars["--txt"], height: 20, flex: 1, borderRadius: 3 }} />
                </div>
                <div style={{ color: th.vars["--txt"], fontWeight: 700, fontSize: 12, lineHeight: 1.2 }}>{th.name}</div>
                <div style={{ color: th.vars["--dim"], fontSize: 9, textTransform: "uppercase", letterSpacing: ".08em", marginTop: 2 }}>{th.group}</div>
              </button>
            );
          })}
        </div>

        <div>
          <div className="field-label" style={{ marginBottom: 8 }}>Theme rotation</div>
          <div className="theme-onboard-modes">
            {[
              { id: "fixed", label: "Keep this theme" },
              { id: "daily", label: "Rotate daily" },
              { id: "random", label: "Random each day" },
            ].map(m => (
              <button key={m.id} className={"theme-onboard-mode" + (mode === m.id ? " on" : "")} onClick={() => setMode(m.id)}>{m.label}</button>
            ))}
          </div>
        </div>

        <button className="btn btn-accent" style={{ width: "100%", fontSize: 15, padding: "13px 0", fontWeight: 700 }} onClick={confirm}>
          Start →
        </button>
      </div>
    </div>
  );
}

function CheckinModal({ today, record, onComplete, onSkip }) {
  const [sleepHrs, setSleep] = useState(record.sleepHrs != null ? record.sleepHrs : 7.5);
  const [wokeTime, setWoke] = useState(record.wokeTime || "5:30");
  const [bedTimePrev, setBed] = useState(record.bedTimePrev || "8:30 PM");
  const [morningMood, setMood] = useState(record.morningMood || 4);
  const [morningEnergy, setEnergy] = useState(record.morningEnergy || 4);
  return (
    <Modal open dismissable={false} onClose={() => {}} title={"Morning check-in · " + PP.prettyDate(today)}
      footer={<><button className="btn" onClick={onSkip}>Skip</button><button className="btn btn-accent" onClick={() => onComplete({ sleepHrs: Number(sleepHrs), wokeTime, bedTimePrev, morningMood, morningEnergy })}>Begin the day →</button></>}>
      <div className="stack">
        <div className="checkin-hello">A new day. Log how you arrived, then go earn it. <span className="dim">(Editable anytime from "Day Vitals".)</span></div>
        <Field label={`Hours of sleep — ${sleepHrs}h`}><input className="range" type="range" min="3" max="11" step="0.5" value={sleepHrs} onChange={(e) => setSleep(e.target.value)} /></Field>
        <div className="row2">
          <Field label="Woke up at"><input className="inp" value={wokeTime} onChange={(e) => setWoke(e.target.value)} /></Field>
          <Field label="Went to bed (last night)"><input className="inp" value={bedTimePrev} onChange={(e) => setBed(e.target.value)} /></Field>
        </div>
        <Field label="Morning mood"><Rating value={morningMood} onChange={setMood} /></Field>
        <Field label="Energy"><Rating value={morningEnergy} onChange={setEnergy} icon="▮" /></Field>
      </div>
    </Modal>
  );
}

function WeekReviewModal({ me, mondayISO, onClose, onSaveNote }) {
  const days = []; for (let i = 0; i < 7; i++) days.push(PP.addDays(mondayISO, i));
  const recs = days.map((iso) => me.days[iso]).filter(Boolean);
  let sleepSum = 0, sleepN = 0, completed = 0, workouts = 0, skillMin = 0, moodSum = 0, moodN = 0;
  recs.forEach((r) => {
    if (r.completed) completed++;
    if (typeof r.sleepHrs === "number") { sleepSum += r.sleepHrs; sleepN++; }
    skillMin += PP.dayContribs(r).SKL;
    r.items.forEach((it) => { const tk = PP.taskById(it.taskId); if (tk && (tk.group === "Strength" || tk.group === "Endurance") && r.logs[it.iid] && r.logs[it.iid].done) workouts++; });
    if (r.reflection && typeof r.reflection.feel === "number") { moodSum += r.reflection.feel; moodN++; }
  });
  const skillHrs = skillMin / 60;
  const existing = (me.weekReviews || {})[mondayISO];
  const [note, setNote] = useState(existing ? existing.note : "");
  const skillPct = Math.min(1, skillHrs / 10);
  return (
    <Modal open wide onClose={onClose} title={`Week Review · ${PP.MONTHS[PP.parseISO(mondayISO).getMonth()]} ${PP.parseISO(mondayISO).getDate()}–${PP.parseISO(PP.addDays(mondayISO, 6)).getDate()}`}
      footer={<button className="btn btn-accent" onClick={() => { onSaveNote(note); onClose(); }}>Save review</button>}>
      <div className="wr-stats">
        <WrStat n={completed + "/7"} l="days completed" />
        <WrStat n={sleepN ? (sleepSum / sleepN).toFixed(1) + "h" : "—"} l="avg sleep" />
        <WrStat n={workouts} l="workouts" />
        <WrStat n={skillHrs.toFixed(1) + "h"} l="skill work" />
        <WrStat n={moodN ? (moodSum / moodN).toFixed(1) + "/5" : "—"} l="avg mood" />
      </div>
      <div className="wr-skill"><Bar pct={skillPct} label={`Skill goal — ${skillHrs.toFixed(1)} / 10h`} value={Math.round(skillPct * 100) + "%"} mono height={10} /></div>
      <div className="wr-days">{days.map((iso) => { const r = me.days[iso]; const pct = r ? PP.dayCompletion(r) : 0; return <div className="wr-day" key={iso}><div className="wr-day-name">{PP.WEEKDAY_NAMES[PP.weekday(iso)]}</div><Ring pct={pct} size={42} stroke={4}><span className="mono wr-day-pct">{r && r.completed ? "✓" : Math.round(pct * 100)}</span></Ring></div>; })}</div>
      <Field label="How did the week feel? What will you change?"><textarea className="ta" rows={3} value={note} placeholder="Weekly reflection…" onChange={(e) => setNote(e.target.value)} /></Field>
    </Modal>
  );
}
function WrStat({ n, l }) { return <div className="wr-stat"><div className="wr-stat-n mono">{n}</div><div className="wr-stat-l">{l}</div></div>; }

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