/* =========================================================================
   LIFE OS — app.jsx
   The shell: topbar, HQ stage + Processing Core, Gus bar, panel tabs,
   game loop, particles, save/load, overlays. Mounts the whole thing.
   ========================================================================= */
(function () {
  const { useReducer, useEffect, useRef, useState } = React;
  const D = window.LifeData, EN = window.LifeEngine, Art = window.LifeArt;
  const P = window.LifePanels, OV = window.LifeOverlays, V2U = window.LifeV2UI;
  const { CreatureSprite, Coin, moodFromCare } = Art;
  const fmt = P.fmt;

  function App() {
    const [state, dispatch] = useReducer(EN.reducer, null, () => EN.load() || EN.initialState());
    const stateRef = useRef(state); stateRef.current = state;
    const [floats, setFloats] = useState([]);
    const [bits, setBits] = useState([]);
    const [tab, setTab] = useState('team');
    const [mview, setMview] = useState('hq'); // mobile: 'hq' | panel tab
    const goMobile = (k) => { SFX.play('ui'); if (k === 'hq') setMview('hq'); else { setMview(k); setTab(k); } };
    const [dexKey, setDexKey] = useState(null);
    const ptr = useRef({ x: 0, y: 0 });
    const stageRef = useRef(null);

    // remove boot splash once mounted
    useEffect(() => { const b = document.getElementById('boot'); if (b) b.remove(); }, []);

    // ---- game loop ----
    useEffect(() => {
      const tick = setInterval(() => {
        const s = stateRef.current;
        if (!s.started || s.event) return;
        dispatch({ type: 'TICK', dt: 0.25 });
      }, 250);
      const save = setInterval(() => EN.save(stateRef.current), 4000);
      const ambient = setInterval(() => {
        const s = stateRef.current;
        if (s.started && !s.event && Math.random() < 0.6) dispatch({ type: 'AMBIENT' });
      }, 38000);
      const events = setInterval(() => {
        const s = stateRef.current;
        if (s.started && !s.event && s.act >= 2 && s.mode !== 'soul' && !s._btOpen && Math.random() < 0.4) {
          const orbs = 4 + Math.min(4, Math.floor(s.btCount + s.act / 2));
          dispatch({ type: 'START_BREACH', orbs });
        }
      }, 75000);
      const onHide = () => EN.save(stateRef.current);
      document.addEventListener('visibilitychange', onHide);
      window.addEventListener('beforeunload', onHide);
      return () => { clearInterval(tick); clearInterval(save); clearInterval(ambient); clearInterval(events); document.removeEventListener('visibilitychange', onHide); window.removeEventListener('beforeunload', onHide); };
    }, []);

    // ---- click fx ----
    useEffect(() => {
      const fx = state._fx; if (!fx) return;
      if (fx.type === 'click') {
        const id = Math.random();
        const f = { id, x: ptr.current.x, y: ptr.current.y, text: '+' + fmt(fx.gain), color: 'var(--sun)' };
        setFloats(v => [...v, f]);
        if (fx.clarityGain) setFloats(v => [...v, { id: id + 0.1, x: ptr.current.x + 24, y: ptr.current.y - 8, text: '+' + fx.clarityGain.toFixed(1) + '◆', color: 'var(--lav-deep)' }]);
        if (fx.wonderGain) setFloats(v => [...v, { id: id + 0.2, x: ptr.current.x - 22, y: ptr.current.y - 10, text: '+' + fx.wonderGain.toFixed(1) + '◇', color: 'var(--wonder-deep)' }]);
        // burst
        const nb = [];
        for (let i = 0; i < 7; i++) nb.push({ id: id + '_' + i, x: ptr.current.x, y: ptr.current.y, dx: (Math.random() * 80 - 40) + 'px', dy: (Math.random() * -70 - 10) + 'px', c: ['#FFD45E', '#FF9EC0', '#B6A4EE'][i % 3] });
        setBits(v => [...v, ...nb]);
        setTimeout(() => { setFloats(v => v.filter(x => x.id !== id && x.id !== id + 0.1 && x.id !== id + 0.2)); setBits(v => v.filter(x => !String(x.id).startsWith(String(id)))); }, 1000);
      }
      if (fx.type === 'merge' && (fx.isNew || D.E[fx.key].tier >= 2)) {
        // soul flash handled by mode; auto-return
        if (!D.E[fx.key].capstone) setTimeout(() => { if (stateRef.current.mode === 'soul' && !stateRef.current._btOpen) dispatch({ type: 'CLOSE_SOUL' }); }, 2200);
      }
      dispatch({ type: 'CLEAR_FX' });
    }, [state._fx]);

    // ---- toast prune ----
    useEffect(() => {
      if (!state.toasts.length) return;
      const t = setTimeout(() => dispatch({ type: 'PRUNE_TOAST', id: state.toasts[0].id }), 2600);
      return () => clearTimeout(t);
    }, [state.toasts]);

    const clickCore = (e) => {
      const r = stageRef.current.getBoundingClientRect();
      const cx = (e.touches ? e.touches[0].clientX : e.clientX) - r.left;
      const cy = (e.touches ? e.touches[0].clientY : e.clientY) - r.top;
      ptr.current = { x: cx, y: cy };
      dispatch({ type: 'CLICK' });
    };

    if (!state.started) return <OV.Onboarding dispatch={dispatch} />;

    const act = EN.sel.active(state);
    const rates = EN.sel.totals(state);
    const day = 1 + Math.floor(state.sec / 240);
    const btReady = state.res.gpp >= EN.C.btThreshold(state.btCount);
    const lowF = state.res.fucks < 22;
    const mergeAvail = (() => {
      const pool = state.creatures.filter(c => !c.shadow);
      for (let i = 0; i < pool.length; i++) for (let j = i + 1; j < pool.length; j++) {
        const r = D.mergeResult(pool[i].key, pool[j].key);
        if (r && !state.dex[r]) return true;
      } return false;
    })();
    const questReady = EN.actGoalsDone(state, state.act) && !state.actDone[state.act];
    const hasCuri = state.creatures.some(c => c.key === 'curiosity');
    const curioBadge = hasCuri && (
      D.V2.QUESTIONS.some(q => {
        if (state.questions.answered.includes(q.id)) return false;
        const asked = !q.deep || state.questions.asked.includes(q.id);
        if (!asked) return false;
        const [c, m] = q.p(state); return c >= m;
      })
      || D.V2.ROOMS.some(r => !state.rooms[r.id] && (state.res.wonder || 0) >= r.cost)
    );
    const tabDefs = [['team', '🧸', 'Team'], ['lab', '🧬', 'Lab'], ['dex', '📖', 'Dex'], ['quest', '🎬', 'Story']];
    if (hasCuri) tabDefs.splice(2, 0, ['curio', '🛰', 'Curiosity']);
    const rootCls = 'mode-' + state.mode;
    // emotional wellbeing -> world saturation (M+ mood DNA)
    const wellbeing = (() => {
      const live = state.creatures.filter(c => !c.shadow);
      const careA = live.length ? live.reduce((s, c) => s + EN.sel.careAvg(c), 0) / live.length / 100 : 0.6;
      const shadowPen = state.creatures.some(c => c.shadow) ? 0.8 : 1;
      return Math.max(0, Math.min(1, (careA * 0.72 + (state.res.fucks / 100) * 0.28) * shadowPen));
    })();

    const depts = D.DEPTS.map(d => ({ ...d, list: state.creatures.filter(c => D.E[c.key].home === d.id) }));

    return (
      <div id="root" className={rootCls} style={{ '--mood': wellbeing }}>
        {state.mode === 'chaos' && <div className="glitch-bars" />}
        {state.mode === 'soul' && <div className="soul-glow" />}

        <div className="shell">
          {/* ---------- TOPBAR ---------- */}
          <div className="topbar">
            <div className="brand"><span className="logo">Life OS</span><span className="tag">You,&nbsp;Inc.</span></div>

            <div className="res-row">
              <div className="pill"><Coin kind="energy" /><div className="col"><span className="v">{fmt(state.res.energy)}</span><span className="k">+{rates.energy.toFixed(1)}/s</span></div></div>
              <div className="pill"><Coin kind="clarity" /><div className="col"><span className="v">{fmt(state.res.clarity)}</span><span className="k">+{rates.clarity.toFixed(2)}/s</span></div></div>
              <div className={'pill fucks-pill' + (lowF ? ' low' : '')}>
                <div className="fucks-bar" style={{ width: state.res.fucks + '%' }} />
                <Coin kind="fucks" /><div className="col"><span className="v">{Math.floor(state.res.fucks)}</span><span className="k">fucks to give</span></div>
              </div>
              <div className="pill"><Coin kind="gpp" /><div className="col"><span className="v">{fmt(state.res.gpp)}</span><span className="k">good-place pts</span></div></div>
              {hasCuri && <div className="pill wonder-pill"><Coin kind="wonder" /><div className="col"><span className="v">{fmt(state.res.wonder || 0)}</span><span className="k">+{rates.wonder.toFixed(2)}/s</span></div></div>}
            </div>

            <div className="spacer" />
            <div className="daychip">DAY {day} · ACT {state.act}</div>
            <button className="btn ghost sm" onClick={() => dispatch({ type: 'REST' })} title="Refill your Fucks to Give">😮‍💨 Rest</button>
            {btReady && <button className="btn lav sm" style={{ animation: 'corepulse 1.4s infinite' }} onClick={() => dispatch({ type: 'OPEN_BT' })}>✦ Breakthrough</button>}
            <button className={'modebtn' + (state.maskOn ? ' on' : '')} onClick={() => dispatch({ type: 'TOGGLE_MASK' })} title="Toggle Corporate 'Mask On' mode">
              {state.maskOn ? '🙂 Mask On' : '😌 Mask Off'}
            </button>
            <button className="btn ghost sm" onClick={() => dispatch({ type: 'TOGGLE_MUTE' })} title="Sound">{state.muted ? '🔇' : '🔊'}</button>
          </div>

          {/* ---------- HQ STAGE ---------- */}
          <div className={'stage' + (mview === 'hq' ? ' m-on' : ' m-off')} ref={stageRef}>
            <div className="stage-head">
              <div><div className="stage-title">Emotional Headquarters</div>
                <div className="stage-sub">{state.maskOn ? 'corporate mode · functioning · do not feel' : 'six departments · all feelings welcome'}</div></div>
              <div className="stage-sub">{state.creatures.length} on staff</div>
            </div>

            <div className="hq">
              <div className="depts">
                {depts.map(d => (
                  <div className="dept" key={d.id} style={{ borderColor: d.list.length ? d.accent + '99' : '' }}>
                    <h4><span style={{ fontSize: 14 }}>{d.icon}</span> {d.name}</h4>
                    <div className="dept-sub">{d.sub}</div>
                    <div className="slots">
                      {d.list.map(c => (
                        <span key={c.uid} title={D.E[c.key].name} style={{ cursor: 'pointer' }}
                          onClick={() => { dispatch({ type: 'SET_ACTIVE', uid: c.uid }); setTab('team'); SFX.play('ui'); }}>
                          <CreatureSprite emo={D.E[c.key]} mood={moodFromCare(c.care, c.shadow)} size={34} />
                        </span>
                      ))}
                      {!d.list.length && <span className="hand muted" style={{ fontSize: 13 }}>empty</span>}
                    </div>
                  </div>
                ))}
              </div>

              {/* processing core */}
              <div className="core-wrap">
                <div className={'core'} onPointerDown={clickCore}>
                  <div className="ring" /><div className="ring2" />
                  {act ? <CreatureSprite emo={D.E[act.key]} mood={moodFromCare(act.care, act.shadow)} size={120} /> : <span className="qmark">?</span>}
                  <div className="core-label">PROCESSING CORE</div>
                </div>
                <div className="core-hint">{act ? `tap to process ${D.E[act.key].raw}` : 'hatch a feeling to begin'}{lowF ? ' · (you\u2019re low on fucks — Rest)' : ''}</div>
              </div>

              {/* floats + particles */}
              {floats.map(f => <div key={f.id} className="float" style={{ left: f.x, top: f.y, color: f.color }}>{f.text}</div>)}
              {bits.map(b => <span key={b.id} className="spark-bit" style={{ left: b.x, top: b.y, width: 9, height: 9, background: b.c, '--dx': b.dx, '--dy': b.dy }} />)}

              {/* V2 — roaming Curiosity comet */}
              {hasCuri && (() => {
                const cur = state.creatures.find(c => c.key === 'curiosity' && !c.shadow);
                return cur ? <V2U.CuriosityRoamer creature={cur} active={state.activeUid === cur.uid} dispatch={dispatch} /> : null;
              })()}
            </div>
          </div>

          {/* ---------- PANEL ---------- */}
          <div className={'panel' + (mview !== 'hq' ? ' m-on' : ' m-off')}>
            <div className="tabs">
              {tabDefs.map(([k, i, l]) => (
                <button key={k} className={'tab' + (tab === k ? ' on' : '')} onClick={() => { setTab(k); SFX.play('ui'); }}>
                  <span className="ti">{i}</span>{l}
                  {k === 'lab' && mergeAvail && <span className="badge">!</span>}
                  {k === 'curio' && curioBadge && <span className="badge">!</span>}
                  {k === 'quest' && questReady && <span className="badge">✓</span>}
                </button>
              ))}
            </div>
            <div className="panel-body">
              {tab === 'team' && <P.TeamPanel state={state} dispatch={dispatch} />}
              {tab === 'lab' && <P.MergeLab state={state} dispatch={dispatch} />}
              {tab === 'curio' && <V2U.CuriosityPanel state={state} dispatch={dispatch} />}
              {tab === 'dex' && <P.DexPanel state={state} onOpen={setDexKey} />}
              {tab === 'quest' && <P.QuestPanel state={state} dispatch={dispatch} />}
            </div>
          </div>

          {/* ---------- GUS BAR ---------- */}
          <GusBar gus={state.gus} muted={state.muted} />

          {/* ---------- MOBILE BOTTOM NAV ---------- */}
          <nav className="botnav">
            <button className={'bn' + (mview === 'hq' ? ' on' : '')} onClick={() => goMobile('hq')}><span className="bni">🏢</span>HQ</button>
            {tabDefs.map(([k, i, l]) => (
              <button key={k} className={'bn' + (mview === k ? ' on' : '')} onClick={() => goMobile(k)}>
                <span className="bni">{i}</span>{l}
                {k === 'lab' && mergeAvail && <span className="badge">!</span>}
                {k === 'curio' && curioBadge && <span className="badge">!</span>}
                {k === 'quest' && questReady && <span className="badge">✓</span>}
              </button>
            ))}
          </nav>
        </div>

        {/* ---------- OVERLAYS ---------- */}
        {state.welcome && <OV.WelcomeBack state={state} dispatch={dispatch} />}
        {state._btOpen && <OV.SoulOverlay state={state} dispatch={dispatch} />}
        {state._capstone && <OV.CapstoneOverlay dispatch={dispatch} />}
        {state.event && state.event.type === 'breach' && <OV.BreachOverlay state={state} dispatch={dispatch} />}
        {state._curiosityIntro && <V2U.CuriosityIntro state={state} dispatch={dispatch} />}
        {state._roomOpened && <V2U.RoomOpened state={state} dispatch={dispatch} />}
        {state._hunch && <V2U.HunchResult state={state} dispatch={dispatch} />}
        {dexKey && <OV.CreatureModal k={dexKey} state={state} dispatch={dispatch} onClose={() => setDexKey(null)} />}
        <OV.Toasts toasts={state.toasts} />
      </div>
    );
  }

  // Gus typewriter bar
  function GusBar({ gus }) {
    const [n, setN] = useState(0);
    useEffect(() => { setN(0); const t = setInterval(() => setN(v => { if (v >= gus.text.length) { clearInterval(t); return v; } return v + 2; }), 16); return () => clearInterval(t); }, [gus.id]);
    const shown = gus.text.slice(0, n);
    return (
      <div className="gusbar">
        <div className="gus-av"><CreatureSprite emo={{ c: ['#C9BBE0', '#8C77D6'], body: 'cloud' }} mood="content" size={46} /></div>
        <div className="gus-speech"><span className="who">GUS · asst. mgr, feelings div.</span>
          <span className="txt">{shown}{n < gus.text.length && <span className="gus-cursor" />}</span></div>
        <div className="gus-meta">used to be<br />“Gusto”</div>
      </div>
    );
  }

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