/* app.jsx — Persom 2.1 产品设计探索：入口
   结构：可点击的 Mac 窗口 + 右上角 Tweaks 浮动面板（评审时一键切到某个状态，不属于产品界面）
   全部状态集中在这里，通过 P21.Ctx 下发给各页面 */
/* global React */
(() => {
  const P = window.P21;
  const { C, D, Ctx, Sidebar, AskFab, AskPanel } = P;

  const INIT = {
    /* 导航与浮层 */
    page: 'Home', acct: false, modal: null, toast: null,
    /* 侧边栏与账号 */
    paused: false, pauseMenu: false, restartHint: false, avatar: false, greet: 'auto', signedOut: false,
    /* Ask */
    ask: false, msgs: [], chip: null, card: null, askMode: null,
    /* You */
    youTab: 'Topics', graph: false, gx: {}, scrollTo: null, hiddenTraits: [],
    /* Timeline */
    day: D.today, tlFilter: 'All', cal: false, highlight: null, replay: null,
    /* Playbook */
    pbView: 'main', mineTab: 'Installed', mineFilter: 'All', mineSort: 'Recent', mkCat: 'All',
    skills: D.skills, published: D.published, sugg: D.sugg, imported: [], emails: [], rec: null,
    /* Check-ins */
    checkins: D.checkins,
  };

  /* 预置的 Ask 对话（Tweaks 面板用） */
  const DEMO = {
    askDemo: { ask: true, msgs: [{ role: 'you', text: D.askPromises.q }, { role: 'persom', text: D.askPromises.a, sources: D.askPromises.sources }], card: null, chip: null },
    askNew: { ask: true, msgs: [{ role: 'you', text: '每周一早上把还没兑现的承诺发给我' }], card: { type: 'new', what: 'Open promises', when: 'Every Mon 09:00' }, chip: null },
    askChip: { ask: true, msgs: [], card: null, chip: 'c2' },
    recDemo: { rec: { phase: 'rec', sec: 204, name: '' } },
    recName: { rec: { phase: 'name', sec: 204, name: '' } },
    askEdit: { ask: true, msgs: [{ role: 'you', text: '改成每天早上 8 点', chip: 'Open promises · Mon 09:00' }], card: { type: 'edit', id: 'c2', what: 'Open promises', when: 'Every day 08:00', was: 'Mon 09:00', on: true, wasOn: true }, chip: null },
  };

  /* 网址参数直达某个状态，便于评审分享与截图验证
     例：?s=acct,restartHint,avatar   ?s=page:Timeline,day:8,tlFilter:AI   ?s=page:Playbook,modal:review:s2
     值是纯数字时转成数字；modal:类型:id 会展开成 { type, id }；不带冒号为 true；askDemo / askNew / askChip / askEdit 为预置对话 */
  function initFromUrl() {
    const raw = new URLSearchParams(location.search).get('s');
    let st = { ...INIT };
    if (!raw) return st;
    raw.split(',').filter(Boolean).forEach(kv => {
      const [k, v, id] = kv.split(':');
      if (DEMO[k]) st = { ...st, ...DEMO[k] };
      else if (v === undefined) st[k] = true;
      else if (k === 'modal') st.modal = { type: v, id, mode: v === 'skill' ? (String(id).startsWith('mk') ? 'market' : 'installed') : undefined, skill: v === 'publish' ? id : undefined };
      else st[k] = /^\d+$/.test(v) ? Number(v) : v;
    });
    if (st.replay === true) st.replay = { day: st.day, t: 9.4 };
    return st;
  }

  /* 弹窗注册表：modal.type → 组件 */
  const MODALS = {
    connections: 'ConnectionsModal', settings: 'SettingsModal', account: 'AccountModal',
    sources: 'SourcesModal', entity: 'EntityModal', portrait: 'PortraitModal', skill: 'SkillDetailModal',
    delChk: 'DelChkModal',
    invite: 'InviteModal', signin: 'SignInModal', publish: 'PublishModal', recConfirm: 'RecConfirmModal',
    import: 'ImportModal', review: 'ReviewModal', update: 'UpdateModal', emails: 'EmailsModal',
  };

  /* ───────── Tweaks 浮动面板（评审用，不属于产品界面） ─────────
     面包屑右边的「Tweaks」按钮开合，面板在按钮下方展开；按当前页面显示对应的状态开关；开合状态存 localStorage */
  const TW_KEY = 'p21-tweaks-open';
  const TW_FONT = '-apple-system, "PingFang SC", sans-serif';

  const DOC_URL = 'https://note.houjoe.me/b3dw37eg#nSihmfTDMl6MuszyIy6c4A'; // Persom 2.1 产品设计文档
  function TweaksPanel({ s, set, reset }) {
    const [open, setOpen] = React.useState(() => { try { return localStorage.getItem(TW_KEY) === '1'; } catch (e) { return false; } });
    const toggle = (v) => { setOpen(v); try { localStorage.setItem(TW_KEY, v ? '1' : '0'); } catch (e) {} };

    const reviewing = s.published.find(p => p.status === 'review' || p.updating);
    const decide = (ok) => {
      if (!reviewing) return set({ toast: '没有审核中的 Skill，先去 Mine 发布一个' });
      const up = !!reviewing.updating;
      const published = s.published.map(p => p.id !== reviewing.id ? p
        : ok ? { ...p, status: 'live', updating: false, changed: false, installs: p.installs || 0, since: p.since || 'Oct 1' }
          : up ? { ...p, updating: false } : { ...p, status: 'rejected', reason: 'the steps still mention a private project name.', reviewed: 'Oct 1' });
      set({ published, emails: [...s.emails, { kind: ok ? 'live' : 'rejected', name: reviewing.name, update: up }], page: 'Playbook', pbView: 'mine', mineTab: 'Published', toast: ok ? `"${reviewing.name}" is live · We emailed you` : `"${reviewing.name}" wasn't approved · We emailed you` });
    };

    /* 面板里的几种控件：分组、开关行、分段行、动作按钮 */
    const Sec = ({ title, children }) => (
      <div style={{ padding: '12px 16px', borderTop: `1px solid ${C.line}`, display: 'flex', flexDirection: 'column', gap: 10 }}>
        <div style={{ fontSize: 11, fontWeight: 600, color: C.faint, letterSpacing: '.04em' }}>{title}</div>
        {children}
      </div>
    );
    const Sw = ({ label, on, onClick }) => (
      <div onClick={onClick} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 12, cursor: 'pointer', fontSize: 13 }}>
        <span>{label}</span><P.Toggle on={on} />
      </div>
    );
    const SegRow = ({ label, items, value, onChange }) => (
      <div style={{ display: 'flex', flexDirection: 'column', gap: 6, fontSize: 13 }}>
        {label && <span>{label}</span>}
        <P.Seg small items={items} value={value} onChange={onChange} />
      </div>
    );
    const Act = ({ on, onClick, children }) => (
      <span onClick={onClick} className={on ? '' : 'p21-h-soft'} style={{ padding: '5px 10px', borderRadius: 6, fontSize: 12.5, cursor: 'pointer', whiteSpace: 'nowrap', background: on ? C.ink : '#fff', color: on ? '#fff' : C.ink, border: `1px solid ${on ? C.ink : C.border}` }}>{children}</span>
    );
    const Acts = ({ children }) => <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6 }}>{children}</div>;

    return (
      <div style={{ position: 'absolute', top: 18, left: 220, zIndex: 300, fontFamily: TW_FONT, color: C.ink }}>
        <span onClick={() => toggle(!open)} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, padding: '5px 12px', borderRadius: 999, background: open ? '#fff' : C.ink, color: open ? C.ink : '#fff', border: `1px solid ${open ? C.border : C.ink}`, fontSize: 12.5, fontWeight: 500, cursor: 'pointer', boxShadow: '0 4px 12px rgba(15,15,15,.15)' }}>
          <span style={{ fontSize: 13 }}>⚙</span>Tweaks
        </span>
        {/* 文档入口：新标签页打开产品设计文档 */}
        <a href={DOC_URL} target="_blank" rel="noopener noreferrer" className="p21-h-soft"
          style={{ marginLeft: 8, display: 'inline-flex', alignItems: 'center', gap: 5, padding: '5px 12px', borderRadius: 999, background: '#fff', color: C.ink, border: `1px solid ${C.border}`, fontSize: 12.5, fontWeight: 500, textDecoration: 'none', boxShadow: '0 4px 12px rgba(15,15,15,.08)' }}>
          <span style={{ fontSize: 12 }}>↗</span>文档
        </a>
        {open && (
          <div className="p21-scroll" style={{ position: 'absolute', top: 36, left: 0, width: 300, maxHeight: 'calc(100vh - 80px)', overflowY: 'auto', background: '#fff', borderRadius: 12, border: `1px solid ${C.border}`, boxShadow: '0 20px 50px rgba(15,15,15,.22)', animation: 'p21pop .15s ease-out' }}>
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '12px 16px' }}>
              <span style={{ fontSize: 14, fontWeight: 600 }}>Tweaks</span>
              <P.X size={18} onClick={() => toggle(false)} />
            </div>

            <Sec title={`当前页面 · ${s.page}`}>
              {s.page === 'Home' && (
                <SegRow label="问候语时段" items={[['auto', '当前'], ['morning', '早'], ['afternoon', '午'], ['evening', '晚']]} value={s.greet} onChange={(k) => set({ greet: k })} />
              )}
              {s.page === 'You' && <Sw label="展开 Graph" on={s.graph} onClick={() => set({ graph: !s.graph })} />}
              {s.page === 'Timeline' && (<>
                <SegRow label="日期" items={[[30, '今天'], [29, '昨天'], [8, 'Sep 8'], [10, '无记录']]} value={s.day} onChange={(d) => set({ day: d, replay: null })} />
                {s.day === 29 && <div style={{ fontSize: 12, color: C.muted, marginTop: -4 }}>昨天有记录，但没有 AI 工作</div>}
                <Sw label="只看 Done by AI" on={s.tlFilter === 'AI'} onClick={() => set({ tlFilter: s.tlFilter === 'AI' ? 'All' : 'AI' })} />
                <Sw label="日历" on={s.cal} onClick={() => set({ cal: !s.cal })} />
                <Sw label="回放" on={!!s.replay} onClick={() => set({ replay: s.replay ? null : { day: s.day, t: 9.4 } })} />
              </>)}
              {s.page === 'Playbook' && (<>
                <Sw label="未登录" on={s.signedOut} onClick={() => set({ signedOut: !s.signedOut })} />
                <div style={{ fontSize: 13 }}>审核结果</div>
                <Acts>
                  <Act onClick={() => decide(true)}>审核通过</Act>
                  <Act onClick={() => decide(false)}>审核不通过</Act>
                  <Act on={s.modal && s.modal.type === 'emails'} onClick={() => set({ modal: { type: 'emails' } })}>邮件</Act>
                </Acts>
              </>)}
              {s.page === 'Check-ins' && (
                <Acts>
                  <Act onClick={() => set({ ask: true, askMode: 'new', chip: null, card: null, msgs: [] })}>新建</Act>
                  <Act onClick={() => set(DEMO.askChip)}>编辑</Act>
                  <Act onClick={() => set(DEMO.askEdit)}>编辑卡片</Act>
                </Acts>
              )}
            </Sec>

            <Sec title="Ask 演示">
              <Acts>
                <Act onClick={() => set(DEMO.askDemo)}>问记忆</Act>
                <Act onClick={() => set(DEMO.askNew)}>建 Check-in</Act>
              </Acts>
            </Sec>

            <Sec title="通用">
              <Sw label="侧边栏：待重启更新" on={s.restartHint} onClick={() => set({ restartHint: !s.restartHint })} />
              <Sw label="侧边栏：账号菜单" on={s.acct} onClick={() => set({ acct: !s.acct, pauseMenu: false, modal: null })} />
              <Sw label="头像：已上传" on={s.avatar} onClick={() => set({ avatar: !s.avatar })} />
            </Sec>

            <div onClick={reset} className="p21-h-soft" style={{ padding: '11px 16px', borderTop: `1px solid ${C.line}`, fontSize: 12.5, color: C.muted, cursor: 'pointer' }}>↺ 重置到初始状态</div>
          </div>
        )}
      </div>
    );
  }

  function Persom21App() {
    const [s, setS] = React.useState(initFromUrl);
    const set = React.useCallback((patch) => setS(prev => ({ ...prev, ...(typeof patch === 'function' ? patch(prev) : patch) })), []);
    const mainRef = React.useRef(null);

    /* 切页：默认回到各页的起点（Playbook 主页、You 非 Graph），extra 可覆盖 */
    const go = React.useCallback((page, extra = {}) => {
      setS(prev => ({ ...prev, page, acct: false, pauseMenu: false, modal: null, cal: false, graph: false, pbView: 'main', ...extra }));
      if (mainRef.current) mainRef.current.scrollTop = 0;
    }, []);
    const openModal = React.useCallback((modal) => set({ modal, acct: false, pauseMenu: false }), [set]);
    const closeModal = () => set({ modal: null });

    /* 键盘：Esc 关闭浮层，⌘, 打开 Settings */
    React.useEffect(() => {
      const onKey = (e) => {
        if (e.key === 'Escape') setS(prev => ({ ...prev, modal: null, acct: false, pauseMenu: false, cal: false, replay: null }));
        if ((e.metaKey || e.ctrlKey) && e.key === ',') { e.preventDefault(); setS(prev => ({ ...prev, modal: { type: 'settings' }, acct: false })); }
      };
      window.addEventListener('keydown', onKey);
      return () => window.removeEventListener('keydown', onKey);
    }, []);

    /* 录制计时 */
    const recording = s.rec && s.rec.phase === 'rec';
    React.useEffect(() => {
      if (!recording) return;
      const iv = setInterval(() => setS(prev => prev.rec && prev.rec.phase === 'rec' ? { ...prev, rec: { ...prev.rec, sec: prev.rec.sec + 1 } } : prev), 1000);
      return () => clearInterval(iv);
    }, [recording]);

    /* 轻提示 2.4 秒后消失 */
    React.useEffect(() => {
      if (!s.toast) return;
      const t = setTimeout(() => set({ toast: null }), 2400);
      return () => clearTimeout(t);
    }, [s.toast, set]);

    const ctx = { s, set, go, openModal };
    const m = s.modal;
    const ModalC = m && P[MODALS[m.type]];

    let page;
    if (s.page === 'Home') page = <P.HomePage />;
    else if (s.page === 'You') page = <P.YouPage mainRef={mainRef} />;
    else if (s.page === 'Timeline') page = <P.TimelinePage mainRef={mainRef} />;
    else if (s.page === 'Playbook') page = <P.PlaybookPage />;
    else page = <P.CheckinsPage />;

    const showTV = s.page === 'Timeline' && !s.replay && P.tlDayData(s.day);

    return (
      <Ctx.Provider value={ctx}>
        <div style={{ position: 'fixed', inset: 0, background: '#E7E5DF', minWidth: 1180 }}>
          <TweaksPanel s={s} set={set} reset={() => { setS(INIT); if (mainRef.current) mainRef.current.scrollTop = 0; }} />
          {/* Mac 窗口 */}
          <div className="p21" onClick={() => (s.acct || s.pauseMenu) && set({ acct: false, pauseMenu: false })}
            style={{ position: 'absolute', top: 60, left: 24, right: 24, bottom: 24, background: '#fff', borderRadius: 12, overflow: 'hidden', display: 'flex', boxShadow: '0 30px 80px rgba(15,15,15,.18), 0 0 0 1px rgba(15,15,15,.06)' }}>
            <Sidebar />
            <main ref={mainRef} className="p21-scroll" style={{ flex: 1, minWidth: 0, overflowY: 'auto', position: 'relative' }}>
              {page}
            </main>
            {showTV && <P.ReplayTV />}
            {s.ask ? <AskPanel /> : <AskFab />}
            {s.rec && s.rec.phase !== 'confirm' && <P.RecordBar />}
            {ModalC && <ModalC key={[m.type, m.id, m.skill, m.mode].join('|')} m={m} onClose={closeModal} />}
            {s.replay && <P.ReplayOverlay />}
            {s.toast && (
              <div style={{ position: 'absolute', left: '50%', bottom: 28, transform: 'translateX(-50%)', zIndex: 60, background: C.ink, color: '#F3F4F1', padding: '9px 16px', borderRadius: 8, fontSize: 13, whiteSpace: 'nowrap', boxShadow: '0 12px 30px rgba(15,15,15,.25)', animation: 'p21pop .18s ease-out' }}>{s.toast}</div>
            )}
          </div>
        </div>
      </Ctx.Provider>
    );
  }

  window.Persom21App = Persom21App;
})();
