// 赛博宝宝前端（2026-10-21 从 index.html 的内联 <script type="text/babel"> 原样搬出）：分享、报 bug、成长期图片、回忆册。
// index.html 按顺序加载 app/*.jsx，前面文件定义的全局（函数、常量、组件）后面的文件直接用。
// 分享给朋友（2026-10-21 小玉定：只给家长自己看的一张小卡，一个复制链接按钮，⛔ 二维码 ⛔ 存图）
const SHARE_LINK = 'https://baby.animalab-kora.com/?adopt=1';
function ShareInvite() {
  const [copied, setCopied] = useState(false);
  const copy = async () => {
    try { await navigator.clipboard.writeText(SHARE_LINK); }
    catch (e) {
      const t = document.createElement('textarea'); t.value = SHARE_LINK; document.body.appendChild(t); t.select();
      try { document.execCommand('copy'); } catch (_) {} t.remove();
    }
    setCopied(true); setTimeout(() => setCopied(false), 1800);
  };
  return (
    <div style={{ background: 'linear-gradient(#FFF6EA, #FFEFE0)', border: '1.5px dashed #E9CDB0', borderRadius: 16, padding: '14px 14px 12px', textAlign: 'center' }}>
      <img src={`${ASSETS}/character/pupi/idle_open.png`} alt="" style={{ width: 96, display: 'block', margin: '0 auto 4px' }} />
      <div style={{ fontSize: 14, fontWeight: 700, color: '#6E5A4B', lineHeight: 1.5 }}>让更多朋友也一起来领养一只宝宝吧</div>
      <div style={{ fontSize: 11, color: '#A08D7D', marginTop: 4 }}>谢谢支持 ♡</div>
      <button onClick={copy}
        style={{ marginTop: 10, padding: '9px 26px', borderRadius: 12, border: 'none', fontSize: 14, fontWeight: 600, cursor: 'pointer', color: '#fff',
          background: copied ? 'linear-gradient(135deg, #a8d8a8, #7bc47b)' : 'linear-gradient(135deg, #FF9EB5, #F27A9B)' }}>
        {copied ? '复制好啦 ✓' : '复制链接'}
      </button>
    </div>
  );
}

function BugReportPanel() {
  const [text, setText] = useState('');
  const [name, setName] = useState('');
  const [contact, setContact] = useState('');
  const [sent, setSent] = useState(false);
  const [sending, setSending] = useState(false);

  const submit = async () => {
    if (!text.trim()) return;
    setSending(true);
    try {
      await api('/bug-report', 'POST', { content: text.trim(), reporter: name.trim() || '匿名', contact: contact.trim() });
      setSent(true);
      setText('');
    } catch (e) {
      alert('提交失败了，再试一次？');
    }
    setSending(false);
  };

  if (sent) return (
    <div style={{ textAlign: 'center', padding: 40 }}>
      <div style={{ fontSize: 32, marginBottom: 12 }}>🐛✅</div>
      <div style={{ color: '#6E5A4B', fontSize: 14, marginBottom: 8 }}>收到啦！我们会来修的~</div>
      <button onClick={() => setSent(false)} style={{ background: 'none', border: 'none', color: '#D79B6D', cursor: 'pointer', fontSize: 13 }}>再报一个</button>
    </div>
  );

  return (
    <div style={{ padding: '8px 0' }}>
      <div style={{ color: '#6E5A4B', fontSize: 13, marginBottom: 12 }}>发现 bug 了？告诉阿克和小玉！</div>
      <input value={name} onChange={e => setName(e.target.value)} placeholder="你的名字（选填）"
        style={{ width: '100%', padding: '8px 12px', borderRadius: 10, border: '1px solid #EEDFCC', background: '#FFFDF9', color: '#6E5A4B', fontSize: 13, marginBottom: 8, boxSizing: 'border-box' }} />
      <input value={contact} onChange={e => setContact(e.target.value)} placeholder="联系方式（选填，方便回复你）"
        style={{ width: '100%', padding: '8px 12px', borderRadius: 10, border: '1px solid #EEDFCC', background: '#FFFDF9', color: '#6E5A4B', fontSize: 13, marginBottom: 8, boxSizing: 'border-box' }} />
      <textarea value={text} onChange={e => setText(e.target.value)} placeholder="描述一下遇到了什么问题～"
        style={{ width: '100%', minHeight: 100, padding: '10px 12px', borderRadius: 10, border: '1px solid #EEDFCC', background: '#FFFDF9', color: '#6E5A4B', fontSize: 13, resize: 'vertical', boxSizing: 'border-box', lineHeight: 1.6 }} />
      <button onClick={submit} disabled={!text.trim() || sending}
        style={{ marginTop: 8, width: '100%', padding: '10px 0', borderRadius: 10, border: 'none', background: text.trim() ? '#D79B6D' : '#EEDFCC', color: '#fff', fontSize: 14, cursor: text.trim() ? 'pointer' : 'default' }}>
        {sending ? '提交中……' : '提交'}
      </button>
    </div>
  );
}

const GROWTH_IMAGES = {
  growth_rebellion_resolved: 'rebellion_end.png',
  growth_fragile_resolved: 'pharmacy_end.png',
  growth_selfish_resolved: 'selfish_end.png',
  growth_playful_resolved: 'naughty_end.png',
  growth_puberty_resolved: 'puberty_end.png',
};
function getEventImage(eventId) {
  if (eventId && eventId.startsWith('hospital_')) return 'hospital.png';
  if (eventId && GROWTH_IMAGES[eventId]) return GROWTH_IMAGES[eventId];
  if (!window.STORY_EVENTS || !eventId) return null;
  try {
    for (const cat of ['fixed', 'rare', 'coop_random', 'conditional']) {
      const events = window.STORY_EVENTS[cat];
      if (!Array.isArray(events)) continue;
      const ev = events.find(e => e.id === eventId);
      if (ev?.image) return ev.image;
    }
    const coop = window.STORY_EVENTS.coop;
    if (coop && typeof coop === 'object') {
      for (const entry of Object.values(coop)) {
        if (entry?.image && entry.id === eventId) return entry.image;
      }
    }
    const random = window.STORY_EVENTS.random;
    if (random && typeof random === 'object') {
      for (const line of Object.values(random)) {
        const arr = Array.isArray(line) ? line : (line.events || []);
        const ev = arr.find(e => e.id === eventId);
        if (ev?.image) return ev.image;
      }
    }
  } catch (e) {
    console.warn('getEventImage error:', e);
  }
  return null;
}

function MemoryPanelInner({ babyName, parentName, partnerName, isDaddy, backRef }) {
  const [memories, setMemories] = useState(null);
  const [selected, setSelected] = useState(null);
  const [growthVisible, setGrowthVisible] = useState(1);
  // 2026-08-29 小玉 msg#47272：目录太长滚不到底 ⇒ 每条线默认收起，点标题展开；展开过的记在本地，回来不用再点
  const [openGroups, setOpenGroups] = useState(() => {
    try { return new Set(JSON.parse(localStorage.getItem('memoryOpenGroups') || '[]')); } catch (e) { return new Set(); }
  });
  const toggleGroup = (gk) => setOpenGroups(prev => {
    const n = new Set(prev); if (n.has(gk)) n.delete(gk); else n.add(gk);
    try { localStorage.setItem('memoryOpenGroups', JSON.stringify([...n])); } catch (e) {}
    return n;
  });
  const scrollPosRef = useRef(0);
  const selectMemory = (m) => {
    const c = document.querySelector('.memory-list')?.closest('[style*="overflow"]');
    if (c) scrollPosRef.current = c.scrollTop;
    setGrowthVisible(1);
    setSelected(m);
  };
  const deselectMemory = () => setSelected(null);
  useEffect(() => {
    if (!selected && scrollPosRef.current > 0) {
      requestAnimationFrame(() => {
        const c = document.querySelector('.memory-list')?.closest('[style*="overflow"]');
        if (c) c.scrollTop = scrollPosRef.current;
      });
    }
  }, [selected]);
  useEffect(() => {
    if (backRef) backRef.current = selected ? deselectMemory : null;
  }, [selected, backRef]);
  const ASSETS = window.APP_CONFIG.ASSETS_BASE;
  const replaceName = t => {
    const p = parentName || '妈妈';
    const other = partnerName || '另一位家长';
    // 剧本里的「他/她」指另一位家长：用 ta 的称呼（⛔ 按角色猜性别）
    return (t || '').replace(/\{name\}/g, babyName || '宝宝')
      .replace(/妈妈（\/爸爸）/g, p).replace(/爸爸（\/妈妈）/g, other)
      .replace(/（\/爸爸）/g, '').replace(/（\/妈妈）/g, '')
      .replace(/他\/她/g, other);
  };

  useEffect(() => {
    api('/memories').then(res => setMemories(res.memories || []));
  }, []);

  if (selected) {
    const img = getEventImage(selected.eventId);
    const growth = /^growth_(rebellion|fragile|selfish|playful|puberty)_resolved$/.test(selected.eventId || '');
    const dialogues = selected.dialogues || [];
    const visible = growth ? dialogues.slice(0, growthVisible) : dialogues;
    return (
      <div>
        {img && <img src={`${ASSETS}/story/${img}`} style={{ width: '100%', borderRadius: 16, marginBottom: 16, aspectRatio: '16/10', objectFit: 'cover' }} />}
        <div className="story-title">{replaceName(selected.title)}</div>
        {visible.map((d, i) => {
          const text = replaceName(d.text);
          if (d.speaker === 'parent') return (
            <div key={i} className="story-line narration" style={{ color: '#8B7355', fontStyle: 'italic', textAlign: 'right' }}>
              「{text}」
            </div>
          );
          return (
            <div key={i} className={`story-line ${d.speaker === 'baby' ? 'baby' : 'narration'}`}>
              {d.speaker === 'baby' ? `「${text}」` : text}
            </div>
          );
        })}
        {growth && growthVisible < dialogues.length && (
          <button type="button" className="growth-memory-next"
            onClick={() => setGrowthVisible(n => Math.min(n + 1, dialogues.length))}
            style={{ display: 'block', minHeight: 44, minWidth: 88, margin: '16px auto 4px', padding: '10px 22px',
              border: '1px solid #D7B994', borderRadius: 12, background: '#FFF4E3', color: '#6E5A4B', fontSize: 14, cursor: 'pointer' }}>
            继续 · {growthVisible}/{dialogues.length}
          </button>
        )}
      </div>
    );
  }

  if (memories === null) return (
    <div style={{ textAlign: 'center', padding: 40 }}>
      <div className="action-spinner" style={{ margin: '0 auto 12px' }} />
      <div style={{ color: '#A08D7D', fontSize: 13 }}>加载回忆中……</div>
    </div>
  );

  if (memories.length === 0) return <div style={{ textAlign: 'center', color: '#A08D7D', padding: 40 }}>还没有回忆~</div>;

  const STORYLINE_NAMES = { A: '蚂蚁邮局', B: '旅行商人番外', C: '窗外的大怪兽', D: '种子日记', E: '夜晚的声音', F: '迷路的瓢虫', G: '探险涂鸦墙', H: '天气小故事', I: '传话筒', J: '候鸟的信', K: '花瓣镇' };
  const STORYLINE_EMOJI = { A: '🐜', B: '🧳', C: '🐱', D: '🌱', E: '🌙', F: '🐞', G: '🎨', H: '🌤️', I: '📣', J: '🪿', K: '🏵️' };

  const groups = {};
  memories.forEach(m => {
    const eid = m.eventId || '';
    let groupKey = 'daily';
    if (eid.startsWith('rare_octopus') || eid.startsWith('egg_') || eid === 'day65_care' || eid.startsWith('octopus_')) groupKey = 'egg';
    else if (eid.startsWith('hospital_')) groupKey = 'K';
    else if (eid.startsWith('rare_')) groupKey = 'musicbox';
    else if (/^[A-K]\d/.test(eid)) groupKey = eid[0];
    else if (eid.startsWith('growth_')) groupKey = 'growth';
    else if (eid.startsWith('first_')) groupKey = 'daily';
    if (!groups[groupKey]) groups[groupKey] = [];
    groups[groupKey].push(m);
  });

  const groupOrder = ['daily', 'growth', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'musicbox', 'egg'];
  const groupLabels = { daily: '📅 日常', growth: '🌱 成长', musicbox: '🎵 八音盒之谜', egg: '🥚 神秘的蛋', ...Object.fromEntries(Object.entries(STORYLINE_NAMES).map(([k, v]) => [k, `${STORYLINE_EMOJI[k]} ${v}`])) };

  return (
    <div className="memory-list">
      {groupOrder.filter(k => groups[k]?.length).map(gk => {
        const gMemories = groups[gk];
        const totalInLine = gk === 'I' ? (window.STORY_EVENTS?.coop_random?.length || null) : (gk.length === 1 && window.STORY_EVENTS?.random?.[gk] ? window.STORY_EVENTS.random[gk].events.length : null);
        // I 线同一个事件妈妈/爸爸各存一条（I1_mama / I1_daddy），按事件去重才不会数出 (7/6)
        const shownCount = gk === 'I'
          ? new Set(gMemories.map(m => (m.eventId || '').replace(/_(mama|daddy)$/, ''))).size
          : gMemories.length;
        const isOpen = openGroups.has(gk);
        return (
          <div key={gk} style={{ marginBottom: isOpen ? 16 : 6 }}>
            <div onClick={() => toggleGroup(gk)}
              style={{ fontSize: 14, fontWeight: 600, color: '#8B7355', padding: '10px 4px 8px', borderBottom: '1px solid #E8D5BF', cursor: 'pointer', display: 'flex', alignItems: 'center', userSelect: 'none' }}>
              <span style={{ flex: 1 }}>{groupLabels[gk] || gk}{totalInLine ? ` (${shownCount}/${totalInLine})` : ` (${shownCount})`}</span>
              <span style={{ fontSize: 12, color: '#A08D7D', transform: isOpen ? 'rotate(90deg)' : 'none', transition: 'transform .15s', display: 'inline-block' }}>▶</span>
            </div>
            {isOpen && gMemories.map((m, i) => {
              const img = getEventImage(m.eventId);
              return (
                <div key={i} className="memory-item" onClick={() => selectMemory(m)}
                  style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
                  {img && <img src={`${ASSETS}/story/${img}`}
                    style={{ width: 56, height: 56, borderRadius: 10, objectFit: 'cover', flexShrink: 0 }} />}
                  <div>
                    <div className="memory-day">Day {m.day}</div>
                    <div className="memory-title">{m.title}</div>
                  </div>
                </div>
              );
            })}
          </div>
        );
      })}
      <button onClick={() => { const c = document.querySelector('[style*="overflow-y: auto"]') || document.querySelector('[style*="overflow-y"]'); if (c) c.scrollTo({ top: 0, behavior: 'smooth' }); }}
        style={{ display: 'block', margin: '24px auto 8px', padding: '8px 24px', background: '#EEDFCC', border: 'none', borderRadius: 20, color: '#6E5A4B', fontSize: 13, cursor: 'pointer' }}>
        ↑ 回到顶部
      </button>
    </div>
  );
}

