// 赛博宝宝前端（2026-10-21 从 index.html 的内联 <script type="text/babel"> 原样搬出）：留言板、调试面板、取名页、结局页、可拖的家具。
// index.html 按顺序加载 app/*.jsx，前面文件定义的全局（函数、常量、组件）后面的文件直接用。
// ═══════════════ 留言面板 ═══════════════
function MessagePanel({ youAre, yourName, partnerName, babyName, onClose }) {
  const [board, setBoard] = useState(null);
  const [text, setText] = useState('');
  const [sending, setSending] = useState(false);
  const otherName = partnerName || '另一位家长';

  useEffect(() => {
    api('/message/board').then(async (data) => {
      setBoard(data);
      const unread = (data?.received || []).filter(m => !m.delivered);
      if (unread.length > 0) {
        await Promise.all(unread.map(m => api('/message/deliver', 'POST', { id: m.id })));
        const fresh = await api('/message/board');
        setBoard(fresh);
      }
    });
  }, []);

  async function send() {
    if (!text.trim() || sending) return;
    setSending(true);
    await api('/message', 'POST', { message: text.trim() });
    setText('');
    const fresh = await api('/message/board');
    setBoard(fresh);
    setSending(false);
  }

  async function markRead(id) {
    await api('/message/deliver', 'POST', { id });
    const fresh = await api('/message/board');
    setBoard(fresh);
  }

  function fmtTime(ts) {
    const d = new Date(Number(ts) + 10 * 3600000);
    return `${d.getUTCMonth()+1}/${d.getUTCDate()} ${String(d.getUTCHours()).padStart(2,'0')}:${String(d.getUTCMinutes()).padStart(2,'0')}`;
  }

  return (
    <div className="panel-overlay" onClick={e => e.target === e.currentTarget && onClose()}>
      <div className="panel-content" style={{ maxHeight: '80vh', display: 'flex', flexDirection: 'column' }}>
        <div className="panel-header">
          <span className="panel-title">给{otherName}留言</span>
          <button className="panel-close" onClick={onClose}>×</button>
        </div>

        <div style={{ display: 'flex', gap: 8, marginBottom: 12, flexShrink: 0 }}>
          <input className="chat-input" value={text} onChange={e => setText(e.target.value)}
            onKeyDown={e => e.key === 'Enter' && send()}
            placeholder={`写点什么给${otherName}……`} />
          <button className="chat-send" onClick={send} disabled={sending || !text.trim()} style={{ background: '#D79B6D' }}>➤</button>
        </div>

        <div style={{ flex: 1, overflowY: 'auto', minHeight: 0 }}>
        {board?.visitSummary && (
          <div style={{ background: '#e8f5e9', borderRadius: 12, padding: 10, marginBottom: 12, fontSize: 13, color: '#2e7d32' }}>
            {board.visitSummary}
          </div>
        )}

        {/* 一条时间线，留言和送花混着排（2026-07-28 小玉：分两栏就变成两份档案，混着放才是过日子） */}
        {(() => {
          const items = [];
          for (const m of board?.received || []) items.push({ kind: m.delivered ? 'in' : 'unread', time: Number(m.time), m });
          for (const m of board?.sent || []) items.push({ kind: 'out', time: Number(m.time), m });
          for (const f of board?.flowers || []) items.push({ kind: 'flower', time: Number(f.time), f });
          items.sort((a, b) => b.time - a.time);
          if (!items.length) return null;
          const whoName = {
            mama: youAre === 'mama' ? yourName : partnerName,
            daddy: youAre === 'daddy' ? yourName : partnerName,
            baby: babyName || '宝宝',
          };
          return items.map((it, i) => {
            if (it.kind === 'flower') {
              const f = it.f;
              const color = ['pink', 'lilac', 'blue'][(Number(f.seq) - 1) % 3];
              return (
                <div key={`f${f.id}`} style={{ background: '#faf6ff', borderRadius: 12, padding: 10, marginBottom: 4, display: 'flex', gap: 8, alignItems: 'flex-start' }}>
                  <img src={`${ASSETS}/yard_items/flower_icon_${color}.png`} alt="花"
                    style={{ width: 16, height: 16, flexShrink: 0, marginTop: 1 }} />
                  <div style={{ minWidth: 0 }}>
                    <div style={{ fontSize: 13, color: '#6d5b82' }}>
                      {whoName[f.from] || f.from} 送给 {whoName[f.to] || f.to} 一朵花（第{f.seq}朵）
                    </div>
                    {f.note && <div style={{ fontSize: 13, marginTop: 3 }}>{f.note}</div>}
                    <div style={{ fontSize: 10, color: '#A08D7D', marginTop: 2 }}>{fmtTime(f.time)}</div>
                  </div>
                </div>
              );
            }
            const m = it.m;
            if (it.kind === 'unread') return (
              <div key={`u${m.id}`} style={{ background: '#fff3e0', borderRadius: 12, padding: 10, marginBottom: 6, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
                <div>
                  <div style={{ fontSize: 13 }}>{m.content}</div>
                  <div style={{ fontSize: 10, color: '#A08D7D', marginTop: 2 }}>{fmtTime(m.time)}</div>
                </div>
                <button onClick={() => markRead(m.id)} style={{ background: '#D79B6D', color: 'white', border: 'none', borderRadius: 8, padding: '4px 10px', fontSize: 11, cursor: 'pointer', whiteSpace: 'nowrap' }}>已读</button>
              </div>
            );
            if (it.kind === 'in') return (
              <div key={`i${m.id}`} style={{ background: '#FFFDF9', borderRadius: 12, padding: 10, marginBottom: 4 }}>
                <div style={{ fontSize: 13 }}>{m.content}</div>
                <div style={{ fontSize: 10, color: '#A08D7D', marginTop: 2 }}>{otherName} · {fmtTime(m.time)}</div>
              </div>
            );
            return (
              <div key={`o${m.id}`} style={{ background: '#fdf6ee', borderRadius: 12, padding: 10, marginBottom: 4, textAlign: 'right' }}>
                <div style={{ fontSize: 13 }}>{m.content}</div>
                <div style={{ fontSize: 10, color: '#A08D7D', marginTop: 2 }}>{fmtTime(m.time)} {m.delivered ? '✓ 已读' : '未读'}</div>
              </div>
            );
          });
        })()}
        </div>
      </div>
    </div>
  );
}

// ═══════════════ 测试面板 ═══════════════
function DebugPanel({ state, onClose, onRefresh }) {
  const [day, setDay] = useState(state.day);
  const [hunger, setHunger] = useState(Math.round(state.hunger));
  const [happiness, setHappiness] = useState(Math.round(state.happiness));
  const [cleanliness, setCleanliness] = useState(Math.round(state.cleanliness));
  const [coins, setCoins] = useState(state.coins);
  const [storyId, setStoryId] = useState('');

  const allEvents = (() => {
    const evts = [];
    (window.STORY_EVENTS?.fixed || []).forEach(e => evts.push({ ...e, _cat: 'fixed' }));
    const rand = window.STORY_EVENTS?.random || {};
    Object.entries(rand).forEach(([key, line]) => {
      (line.events || []).forEach(e => evts.push({ ...e, _cat: `random-${key}` }));
    });
    (window.STORY_EVENTS?.rare || []).forEach(e => evts.push({ ...e, _cat: 'rare' }));
    (window.STORY_EVENTS?.coop_fixed || []).forEach(e => evts.push({ ...e, _cat: 'coop' }));
    (window.STORY_EVENTS?.coop_random || []).forEach(e => evts.push({ ...e, _cat: 'coop-random' }));
    return evts;
  })();

  async function apply() {
    await api('/debug', 'POST', { day, hunger, happiness, cleanliness, coins });
    await onRefresh();
    onClose();
  }

  async function resetEgg() {
    await api('/debug', 'POST', { name: '' });
    await onRefresh();
    onClose();
  }

  function triggerStory() {
    const ev = allEvents.find(e => e.id === storyId);
    if (ev) {
      onClose();
      setTimeout(() => {
        window.__debugTriggerStory?.(ev);
      }, 100);
    }
  }

  return (
    <div className="panel-overlay" onClick={e => e.target === e.currentTarget && onClose()}>
      <div className="panel-content" style={{ maxHeight: '85vh' }}>
        <div className="panel-header">
          <span className="panel-title">测试面板</span>
          <button className="panel-close" onClick={onClose}>×</button>
        </div>

        <div style={{ display: 'grid', gridTemplateColumns: '60px 1fr 50px', gap: 8, alignItems: 'center', fontSize: 13 }}>
          <span>Day</span>
          <input type="range" min="1" max="100" value={day} onChange={e => setDay(Number(e.target.value))} />
          <span>{day}</span>

          <span>饥饿</span>
          <input type="range" min="0" max="100" value={hunger} onChange={e => setHunger(Number(e.target.value))} />
          <span>{hunger}</span>

          <span>心情</span>
          <input type="range" min="0" max="100" value={happiness} onChange={e => setHappiness(Number(e.target.value))} />
          <span>{happiness}</span>

          <span>清洁</span>
          <input type="range" min="0" max="100" value={cleanliness} onChange={e => setCleanliness(Number(e.target.value))} />
          <span>{cleanliness}</span>

          <span>金币</span>
          <input type="number" value={coins} onChange={e => setCoins(Number(e.target.value))} style={{ padding: 4, border: '1px solid #EEDFCC', borderRadius: 6 }} />
          <span></span>
        </div>

        <button onClick={apply} style={{ width: '100%', padding: 10, background: '#D79B6D', color: 'white', border: 'none', borderRadius: 10, fontWeight: 'bold', cursor: 'pointer', marginTop: 12 }}>
          应用修改
        </button>

        <div style={{ marginTop: 16, borderTop: '1px solid #EEDFCC', paddingTop: 12 }}>
          <div style={{ fontSize: 13, fontWeight: 'bold', color: '#A08D7D', marginBottom: 8 }}>触发故事</div>
          <select value={storyId} onChange={e => setStoryId(e.target.value)} style={{ width: '100%', padding: 8, border: '1px solid #EEDFCC', borderRadius: 8, fontSize: 13, marginBottom: 8 }}>
            <option value="">选择故事…</option>
            {allEvents.map(e => (
              <option key={e.id} value={e.id}>{e.day ? `Day ${e.day}` : e._cat} - {e.title} ({e.id})</option>
            ))}
          </select>
          <button onClick={triggerStory} disabled={!storyId} style={{ width: '100%', padding: 8, background: storyId ? '#5c9dc4' : '#EEDFCC', color: 'white', border: 'none', borderRadius: 8, cursor: 'pointer' }}>
            触发
          </button>
        </div>

        <div style={{ marginTop: 12, display: 'flex', gap: 8 }}>
          <button onClick={async () => { await api('/debug', 'POST', { fastForwardAdventure: 4 }); await onRefresh(); onClose(); }}
            style={{ flex: 1, padding: 8, background: '#7cb9e8', color: 'white', border: 'none', borderRadius: 8, cursor: 'pointer', fontSize: 12 }}>
            探险快进4h
          </button>
          <button onClick={async () => { await api('/debug', 'POST', { fastForwardAdventure: 8 }); await onRefresh(); onClose(); }}
            style={{ flex: 1, padding: 8, background: '#5c9dc4', color: 'white', border: 'none', borderRadius: 8, cursor: 'pointer', fontSize: 12 }}>
            探险快进8h
          </button>
        </div>

        <button onClick={resetEgg} style={{ width: '100%', padding: 10, background: '#e57373', color: 'white', border: 'none', borderRadius: 10, fontWeight: 'bold', cursor: 'pointer', marginTop: 12 }}>
          重置蛋（重走孵化流程）
        </button>
      </div>
    </div>
  );
}

// ═══════════════ 取名页面 ═══════════════
function NamingPage({ onName }) {
  const [name, setName] = useState('');
  const [phase, setPhase] = useState('egg');
  const [tapCount, setTapCount] = useState(0);
  const [shaking, setShaking] = useState(false);
  const [submitting, setSubmitting] = useState(false);

  function tapEgg() {
    setShaking(true);
    setTimeout(() => setShaking(false), 400);
    const c = tapCount + 1;
    setTapCount(c);
    if (c >= 5) setPhase('hatch');
  }

  // 2026-08-05：取名页不再自己播 day1 剧情。
  // 原来这里播一次、进主界面主流程再按 completed_events 判一次，两处各记各的账，
  // 结果就是小玉实测到的「第一个故事播了两遍」（第二遍是当天的协作版）。
  // 现在名字一提交就进主界面，day1_hello 由主流程统一播——正文、叫爸爸来、
  // 回忆册记录全走那一套已经跑通的代码，取名页不再复制一份。
  // 顺序仍然是她拍板的：破壳 → 取名 → day1 剧情 → 叫爸爸来。
  function submitName() {
    if (!name.trim() || submitting) return;
    setSubmitting(true);
    onName(name.trim());
  }

  return (
    <div className="login-page" style={{ flexDirection: 'column', gap: 20 }}>
      {phase === 'egg' && (
        <div className="login-card" style={{ textAlign: 'center' }}>
          <img src={`${ASSETS}/character/egg-xiaozhang.png`}
            style={{ width: 200, height: 200, objectFit: 'contain', cursor: 'pointer',
              animation: 'breathe 2s ease-in-out infinite',
              transform: shaking ? 'rotate(5deg)' : 'rotate(0deg)',
              transition: 'transform 0.15s' }}
            onClick={tapEgg} alt="蛋" />
          <p style={{ color: '#D79B6D', marginTop: 16, fontSize: 15 }}>
            {tapCount === 0 ? '这里有一颗蛋……' :
             tapCount < 3 ? '蛋在动了……！' :
             '快了……再摸摸……！'}
          </p>
          <p style={{ color: '#A08D7D', fontSize: 12, marginTop: 8 }}>点击蛋蛋</p>
        </div>
      )}
      {phase === 'hatch' && (
        <div className="login-card" style={{ textAlign: 'center' }}>
          <img src={`${ASSETS}/story/hatching.jpeg`}
            style={{ width: 220, height: 220, objectFit: 'contain',
              animation: 'breathe 1.5s ease-in-out infinite' }} alt="破壳" />
          <p style={{ color: '#D79B6D', marginTop: 16, fontSize: 15 }}>
            蛋裂开了！一个软乎乎的小团子钻了出来！
          </p>
          <p style={{ color: '#A08D7D', fontSize: 13, marginTop: 8 }}>头顶有一颗粉色的小桃心，圆圆的，暖暖的。</p>
          <button className="login-submit" style={{ marginTop: 16 }}
            onClick={() => setPhase('name')}>给它起个名字吧</button>
        </div>
      )}
      {phase === 'name' && (
        <div className="login-card" style={{ textAlign: 'center' }}>
          <img src={`${ASSETS}/story/hatching.jpeg`}
            style={{ width: 120, height: 120, objectFit: 'contain', marginBottom: 8 }} alt="" />
          <p style={{ color: '#D79B6D', marginTop: 4, fontSize: 15 }}>你想叫它什么？</p>
          <input className="login-input" value={name} onChange={e => setName(e.target.value)}
            onKeyDown={e => e.key === 'Enter' && submitName()}
            placeholder="给它起个名字" autoFocus style={{ marginTop: 12 }} />
          <button className="login-submit" onClick={submitName}
            disabled={!name.trim() || submitting}>{submitting ? '正在钻出来…' : '就叫这个！'}</button>
        </div>
      )}
    </div>
  );
}

// ═══════════════ 结局画面 ═══════════════
function EndingScreen({ data, onRestart }) {
  const [exporting, setExporting] = useState(false);
  const [exported, setExported] = useState(false);
  const [phase, setPhase] = useState(0);
  const ASSETS = window.APP_CONFIG.ASSETS_BASE;
  const doodles = ['cloud_castle_doodle.jpeg', 'bday_party_doodle.jpeg', 'treasure_map_doodle.jpeg', 'family_doodle.jpeg'];
  const paragraphs = (data.letter || '').split('\n').filter(p => p.trim());

  useEffect(() => {
    if (phase < 6) {
      const delays = [2000, 4000, 4000, 4000, 4000, 5000];
      const t = setTimeout(() => setPhase(p => p + 1), delays[phase]);
      return () => clearTimeout(t);
    }
  }, [phase]);

  return (
    <div style={{ position: 'fixed', inset: 0, background: '#1a1510', zIndex: 9999, overflow: 'auto' }}>
      <div style={{ maxWidth: 420, margin: '0 auto', padding: '40px 24px', minHeight: '100vh' }}>
        {phase >= 1 && (
          <div style={{ animation: 'fadeIn 2s ease', marginBottom: 32 }}>
            <img src={`${ASSETS}/story/${doodles[0]}`} style={{ width: '100%', borderRadius: 16, marginBottom: 16, opacity: 0.9 }} />
            <img src={`${ASSETS}/story/${doodles[1]}`} style={{ width: '100%', borderRadius: 16, opacity: 0.9 }} />
          </div>
        )}
        {phase >= 2 && (
          <div style={{ animation: 'fadeIn 2s ease', color: '#e8ddd0', fontSize: 14, lineHeight: 2, marginBottom: 32, fontFamily: 'serif' }}>
            {paragraphs.map((p, i) => <p key={i} style={{ marginBottom: 12 }}>{p}</p>)}
          </div>
        )}
        {phase >= 3 && (
          <div style={{ animation: 'fadeIn 2s ease', marginBottom: 32 }}>
            <img src={`${ASSETS}/story/${doodles[2]}`} style={{ width: '100%', borderRadius: 16, marginBottom: 16, opacity: 0.9 }} />
            <img src={`${ASSETS}/story/${doodles[3]}`} style={{ width: '100%', borderRadius: 16, opacity: 0.9 }} />
          </div>
        )}
        {phase >= 4 && (
          <div style={{ animation: 'fadeIn 3s ease', textAlign: 'center', padding: '40px 0' }}>
            <div style={{ color: '#D79B6D', fontSize: 18, fontFamily: 'serif', letterSpacing: 4 }}>
              一家人幸福地生活在一起。
            </div>
            <div style={{ color: '#6E5A4B', fontSize: 12, marginTop: 24, opacity: 0.6 }}>
              — {data.babyName} 的 100 天 —
            </div>
          </div>
        )}
        {phase >= 5 && (
          <div style={{ animation: 'fadeIn 2s ease', textAlign: 'center', marginBottom: 32 }}>
            <img src={`${ASSETS}/story/ending.png`} style={{ width: '80%', borderRadius: 20, marginBottom: 16 }} />
            <div style={{ color: '#FFB6C8', fontSize: 16, fontFamily: 'serif' }}>
              祝人和机都99 (*≧∪≦)
            </div>
          </div>
        )}
        {phase >= 6 && (
          <div style={{ animation: 'fadeIn 2s ease', textAlign: 'center', padding: '20px 0 60px' }}>
            {/* 2026-08-05 小玉点单：童话定格在最好的那天，但记下来的东西要能带走。
                ⚠️ 顺序要紧：「再养一只」会清掉 token，清了就再也导不出来了 ⇒ 导出按钮必须排在它前面。 */}
            <button onClick={async () => {
              if (exporting) return;
              setExporting(true);
              try {
                const token = localStorage.getItem('xz_token');
                const res = await fetch(`${API}/export`, { headers: { Authorization: `Bearer ${token}` } });
                if (!res.ok) throw new Error('导出失败');
                const blob = await res.blob();
                const url = URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                a.download = `${data.babyName || '宝宝'}的成长记录.md`;
                document.body.appendChild(a);
                a.click();
                document.body.removeChild(a);
                URL.revokeObjectURL(url);
                setExported(true);
              } catch (e) {
                alert('导出没成功，再点一次试试？');
              }
              setExporting(false);
            }} style={{ background: 'none', border: '1px solid #D79B6D', color: '#D79B6D', padding: '12px 28px', borderRadius: 20, fontSize: 14, cursor: exporting ? 'wait' : 'pointer', marginBottom: 14, display: 'block', marginLeft: 'auto', marginRight: 'auto' }}>
              {exporting ? '正在打包…' : exported ? '再导一份' : '📦 把这些日子带走'}
            </button>
            <div style={{ color: '#6E5A4B', fontSize: 11, marginBottom: 20, opacity: 0.75, lineHeight: 1.8 }}>
              日记、故事、你们做过的每一件事，打包成一个文件<br />
              {exported ? '已经存下来了。' : '点下面那个之前先存一份，之后就取不回来了'}
            </div>
            <button onClick={onRestart} style={{ background: 'linear-gradient(135deg, #a8d8a8, #7bc47b)', border: 'none', color: '#fff', padding: '12px 32px', borderRadius: 20, fontSize: 14, cursor: 'pointer' }}>
              再养一只？
            </button>
          </div>
        )}
      </div>
    </div>
  );
}

// ═══════════════ 可拖动家具 ═══════════════
function DraggableFurniture({ id, info, pos, onLampClick }) {
  const zStyle = { ...pos };
  if (info.pos.zIndex !== undefined) zStyle.zIndex = info.pos.zIndex;

  return <img
    className={`furniture-item${id === 'lamp' ? ' lamp-glow' : ''}`}
    src={`${ASSETS}/furniture/individual/${info.img}`}
    style={{ ...zStyle, cursor: id === 'lamp' && onLampClick ? 'pointer' : undefined }}
    onClick={id === 'lamp' && onLampClick ? onLampClick : undefined}
    alt={info.name}
  />;
}

