// 赛博宝宝前端（2026-10-21 从 index.html 的内联 <script type="text/babel"> 原样搬出）：背包、探险面板。
// index.html 按顺序加载 app/*.jsx，前面文件定义的全局（函数、常量、组件）后面的文件直接用。
function BackpackPanelInner({ inventory, gifts, petPoints, isDaddy, giftCredits, babyName, meName, otherName, onRedeem, onGift }) {
  const [giftPicking, setGiftPicking] = useState(false);
  const [busy, setBusy] = useState(false);
  const ASSETS = window.APP_CONFIG.ASSETS_BASE;
  const [detail, setDetail] = useState(null);
  const items = (inventory || []).filter(i => {
    if (i.item_id === 'octopus_egg') return false;
    if (['meal', 'item', 'rare'].includes(i.item_type)) return true;
    if (i.item_type === 'furniture' && YARD_FURNITURE_INFO[i.item_id]) return false;
    if (i.item_type === 'furniture' && FURNITURE_INFO[i.item_id] && !FURNITURE_INFO[i.item_id].pos) return true;
    return false;
  }).map(i => i.item_type === 'furniture' && !FURNITURE_INFO[i.item_id]?.pos ? { ...i, item_type: 'rare' } : i);
  const giftList = gifts || [];

  const RARE_EMOJI = {
    broken_music_box: '🎵', rusty_key: '🔑', glowing_stone_1: '💎',
    treasure_map: '🗺️', dove_bell: '🔔', faded_painting: '🖼️',
    vendor_box: '📦', glowing_stone_2: '💎', octopus_egg: '🥚',
    flower_sorry: '💐', medical_box: '💊', scooter: '🛴', locked_diary: '📔',
    post_can: '📮', vender_tent: '⛺', fish_bone: '🐟',
    seeds_D: '🌱', taiko: '🥁', dipper_stone: '🪨', clawdee: '🎁',
    mirror: '🪞',
  };

  function getItemImg(item) {
    if (item.item_type === 'gift') return `${ASSETS}/gift/${item.item_id}.png`;
    if (item.item_type === 'ingredient') return `${ASSETS}/food/ingredients_sprites/${item.item_id}.png`;
    if (item.item_type === 'material') return `${ASSETS}/materials/sprites/${item.item_id}.png`;
    if (item.item_type === 'meal') return `${ASSETS}/food/sprites/${RECIPES[item.item_id]?.img || item.item_id + '.png'}`;
    if (item.item_type === 'item') return `${ASSETS}/items/individual/${item.item_id}.png`;
    if (item.item_type === 'rare') {
      const RARE_IMG = {
        broken_music_box: 'story/music_box.png',
        rusty_key: 'story/rusty_key.png',
        glowing_stone_1: 'story/glowing_stone_1.png',
        treasure_map: 'story/treasure_map_doodle.jpeg',
        glowing_stone_2: 'story/glowing_stone_2.png',
        dove_bell: 'story/dove_bell.png',
        faded_painting: 'story/faded_painting.png',
        vendor_box: 'story/vendor_box.png',
        family_doodle: 'story/family_doodle.jpeg',
        flower_sorry: 'items/flower_sorry.png',
        medical_box: 'items/medical_box.png',
        scooter: 'items/scooter.png',
        locked_diary: 'items/diary.png',
        post_can: 'story/post_can.png',
        vender_tent: 'story/vender_tent.png',
        fish_bone: 'story/fish_bone.png',
        seeds_D: 'story/seeds_D.png',
        taiko: 'story/taiko.png',
        dipper_stone: 'story/dipper_stone.png',
        clawdee: 'story/clawdee.png',
        mirror: 'items/mirror.png',
        mayor_badge: 'story/mayor_badge.png',
        nils_letter_box: 'story/nils_letter_box.png',
      };
      return `${ASSETS}/${RARE_IMG[item.item_id] || 'items/individual/mistery_item.png'}`;
    }
    return null;
  }

  function getRareEmoji(itemId) {
    return RARE_EMOJI[itemId] || '✨';
  }

  const typeOrder = ['meal', 'item', 'rare'];
  // 爸爸端（有换购权的那一方）显示点数余额；另一方还是提示去找对方换
  const typeLabel = {
    meal: '🍳 做好的菜',
    item: isDaddy ? `🧸 物品　摸头点数 ${petPoints || 0}` : `🧸 物品　让${otherName || '爸爸'}去换购物品`,
    rare: '✨ 剧情收藏',
  };
  const grouped = {};
  items.forEach(item => {
    const t = item.item_type;
    if (!grouped[t]) grouped[t] = [];
    grouped[t].push(item);
  });

  function getDesc(item) {
    if (item.item_type === 'meal') return { effect: RECIPES[item.item_id]?.desc, note: RECIPES[item.item_id]?.note };
    if (item.item_type === 'item') {
      const d = ITEM_DESC[item.item_id]; if (!d) return d;
      const p1 = (isDaddy ? meName : otherName) || '第一位家长';
      const r = t => (t || '').replace(/\{parent1\}/g, p1).replace(/\{name\}/g, babyName || '宝宝');
      return { ...d, effect: r(d.effect), note: r(d.note) };
    }
    return null;
  }

  return (
    <div style={{ padding: '0 2px' }}>
      {detail && (
        <div onClick={() => setDetail(null)} style={{ position: 'fixed', inset: 0, zIndex: 200, background: 'rgba(0,0,0,0.25)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
          <div style={{ background: '#FFFDF9', borderRadius: 18, padding: 20, maxWidth: detail.item_type === 'rare' ? 300 : 260, width: '80%', textAlign: 'center', boxShadow: '0 8px 30px rgba(110,90,75,0.15)', border: '1.5px solid #EEDFCC' }}
            onClick={e => e.stopPropagation()}>
            <img src={getItemImg(detail)} style={{ width: detail.item_type === 'rare' ? 140 : 64, height: detail.item_type === 'rare' ? 140 : 64, objectFit: 'contain', marginBottom: 8, borderRadius: detail.item_type === 'rare' ? 12 : 0 }} alt="" />
            <div style={{ fontSize: 16, fontWeight: 'bold', color: '#6E5A4B', marginBottom: 6 }}>{detail.item_name || detail.item_id}</div>
            {detail._reaction && <div style={{ fontSize: 13, color: '#D79B6D', marginBottom: 4, lineHeight: 1.5 }}>「{detail._reaction}」</div>}
            {detail._date && <div style={{ fontSize: 11, color: '#A08D7D', marginBottom: 4 }}>🎀 {detail._date}</div>}
            {getDesc(detail)?.effect && <div style={{ fontSize: 13, color: '#D79B6D', marginBottom: 4, lineHeight: 1.5 }}>{getDesc(detail).effect}</div>}
            {getDesc(detail)?.note && <div style={{ fontSize: 11, color: '#A08D7D', fontStyle: 'italic' }}>{getDesc(detail).note}</div>}
            {detail.item_type === 'meal' && RECIPES[detail.item_id] && (
              <div style={{ fontSize: 11, color: '#A08D7D', marginTop: 6 }}>
                {recipeText(RECIPES[detail.item_id].ingredients)}
              </div>
            )}
            <div style={{ fontSize: 11, color: '#A08D7D', marginTop: 6 }}>数量: {detail.quantity}</div>
          </div>
        </div>
      )}
      {typeOrder.filter(t => grouped[t] || (t === 'item' && isDaddy)).map(type => (
        <div key={type} className="inv-section">
          <div className="inv-category">{typeLabel[type] || type}{type === 'item' && isNew() && <span className="new-bubble inline">New</span>}</div>
          {/* 爸爸端的物品格子 = 商店：十样全列出来，格子上标价，点一下换一个 */}
          {type === 'item' && isDaddy ? (
            <div className="inventory-grid">
              {Object.entries(SHOP_ITEMS).filter(([id]) => !SHOP_GIFT_GATE[id] || (gifts || []).some(g => g.gift_id === SHOP_GIFT_GATE[id])).map(([id, name]) => {
                const price = ITEM_PRICES[id] || 10;
                const owned = (inventory || []).find(x => x.item_id === id)?.quantity || 0;
                const afford = (petPoints || 0) >= price;
                return (
                  <div key={id} className="inv-slot shop-slot" style={{ opacity: afford ? 1 : 0.45, cursor: afford && !busy ? 'pointer' : 'default' }}
                    onClick={async () => {
                      if (!afford || busy) return;
                      setBusy(true);
                      await onRedeem(id, name, price);
                      setBusy(false);
                    }}>
                    {isNew(id) && <span className="new-bubble">New</span>}
                    <img src={`${ASSETS}/items/individual/${id}.png`} style={{ width: 34, height: 34, objectFit: 'contain' }}
                      onError={e => { e.target.style.display='none'; }} alt="" />
                    <div className="inv-name">{name}</div>
                    <span className="inv-qty">{price}点{owned ? ` · 有${owned}` : ''}</span>
                  </div>
                );
              })}
            </div>
          ) : (
            <div className="inventory-grid" style={type === 'rare' ? { gridTemplateColumns: 'repeat(4, 1fr)' } : undefined}>
              {(grouped[type] || []).map((item, i) => (
                <div key={i} className="inv-slot" onClick={() => setDetail(item)}
                  style={{ cursor: 'pointer' }}>
                  <img src={getItemImg(item)} style={{ width: 34, height: 34, objectFit: 'contain' }}
                      onError={e => { e.target.style.display='none'; }} alt="" />
                  <div className="inv-name">{item.item_name || item.item_id}</div>
                  {item.quantity >= 0 && <span className="inv-qty">x{item.quantity}</span>}
                </div>
              ))}
            </div>
          )}
        </div>
      ))}
      {/* 爸爸端送礼：有额度就亮，没额度就灰（2026-07-28 小玉定的） */}
      {isDaddy && (
        <div className="inv-section">
          <div className="inv-category">🎁 送礼物　剩 {giftCredits || 0} 个额度</div>
          <button className="gift-send-btn" disabled={!giftCredits || busy}
            onClick={() => setGiftPicking(true)}>
            {giftCredits ? `挑一样送给${babyName || '宝宝'}` : `额度用完了，回应剧情或者陪${babyName || '宝宝'}玩能攒回来`}
            {isNew() && <span className="new-bubble inline">New</span>}
          </button>
          {giftPicking && (
            <div onClick={() => setGiftPicking(false)}
              style={{ position: 'fixed', inset: 0, zIndex: 210, background: 'rgba(0,0,0,0.25)', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
              <div onClick={e => e.stopPropagation()}
                style={{ background: '#FFFDF9', borderRadius: 18, padding: 16, width: '86%', maxWidth: 340, maxHeight: '70vh', overflowY: 'auto', border: '1.5px solid #EEDFCC' }}>
                <div style={{ fontSize: 15, color: '#6E5A4B', marginBottom: 10 }}>送哪一样？</div>
                {/* 弹窗比背包窄，格子固定四列，不然最右边那个会被切掉 */}
                <div className="inventory-grid" style={{ gridTemplateColumns: 'repeat(4, 1fr)' }}>
                  {Object.entries(GIFT_CATALOG).map(([id, name]) => {
                    const already = (gifts || []).some(g => g.gift_id === id);
                    return (
                      <div key={id} className="inv-slot" style={{ cursor: busy ? 'default' : 'pointer', opacity: already ? 0.55 : 1 }}
                        onClick={async () => {
                          if (busy) return;
                          setBusy(true);
                          await onGift(id, name);
                          setBusy(false);
                          setGiftPicking(false);
                        }}>
                        {isNew(id) && !already && <span className="new-bubble">New</span>}
                        <img src={`${ASSETS}/gift/${id}.png`} style={{ width: 34, height: 34, objectFit: 'contain' }}
                          onError={e => { e.target.style.display='none'; }} alt="" />
                        <div className="inv-name">{name}</div>
                        {already && <span className="inv-qty">送过</span>}
                      </div>
                    );
                  })}
                </div>
              </div>
            </div>
          )}
        </div>
      )}
      {giftList.length > 0 && (() => {
        const giverName = isDaddy ? meName : otherName;
        const otherParentName = isDaddy ? otherName : meName;
        const GIFT_INFO = {
          balloon: { name: '气球', reaction: '哇！！气球！！我要飘起来啦！！' },
          bow: { name: '蝴蝶结', reaction: '好漂亮的蝴蝶结！我要戴在头上！{me}你看好看吗！' },
          crown: { name: '皇冠', reaction: '我是公主吗！！{name}是{me}的公主！！' },
          doodlebook_crayon: { name: '涂鸦本和蜡笔', reaction: '哇！！我可以画画了！！我要画{me}画{partner}画小章鱼！' },
          pony_musicbox: { name: '小马音乐盒', reaction: '好好听的音乐……小马在转圈圈呢。我可以睡觉的时候听吗？' },
          rainboots: { name: '雨靴', reaction: '下雨天可以踩水坑啦！！啪叽啪叽！！{me}一起踩！' },
          shovel: { name: '小铲子', reaction: '我可以种花花了！！要种一朵最大最大的给{partner}！' },
          telescope: { name: '望远镜', reaction: '哇……可以看到好远好远的地方！{me}，那边那颗星星是什么？' },
          fox_doll: { name: '小狐狸玩偶', reaction: '这个小狐狸……长得好像{partner}！！软软的！我要抱着它睡觉！' },
          bubble_water: { name: '泡泡水', reaction: '泡泡！！我要吹一个好大好大的！！{me}你站远一点，我要把你也装进去！' },
          polaroid: { name: '拍立得', reaction: '哇！！是会吐照片的相机！！{me}站好不许动～咔嚓！……等一下等一下，{partner}也要进来！我要把我们三个都装进小方块里！' },
        };
        return (
          <div className="inv-section">
            <div className="inv-category">🎁 {giverName || '送礼家长'}的礼物</div>
            {giftList.map((g, i) => {
              const info = GIFT_INFO[g.gift_id] || {};
              const reaction = renderFamilyText(info.reaction, babyName, giverName, otherParentName);
              const date = g.given_at ? new Date(g.given_at) : null;
              const dateStr = date ? `${date.getMonth()+1}/${date.getDate()}` : '';
              return (
                <div key={i} style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '10px 4px', borderBottom: '1px solid #F0E6DA', cursor: 'pointer' }}
                  onClick={() => setDetail({ item_type: 'gift', item_id: g.gift_id, item_name: info.name || g.gift_id, quantity: 1, _reaction: reaction, _date: dateStr })}>
                  <img src={`${ASSETS}/gift/${g.gift_id}.png`} style={{ width: 48, height: 48, objectFit: 'contain', flexShrink: 0 }}
                    onError={e => { e.target.style.display='none'; }} alt="" />
                  <div>
                    <div style={{ fontSize: 14, fontWeight: 600, color: '#6E5A4B' }}>{info.name || g.gift_id}</div>
                    <div style={{ fontSize: 12, color: '#D79B6D', marginTop: 2 }}>「{reaction}」</div>
                    {dateStr && <div style={{ fontSize: 11, color: '#A08D7D', marginTop: 2 }}>🎀 {dateStr}</div>}
                  </div>
                </div>
              );
            })}
          </div>
        );
      })()}
    </div>
  );
}

// ═══════════════ 探险面板 ═══════════════
const WEATHER_LABEL = { sunny: '☀️ 大晴天', cloudy: '☁️ 多云', rainy: '🌧️ 下雨', night: '🌙 夜晚', night_rain: '🌧️🌙 夜雨' };
function AdventurePanel({ adventure, day, hunger, inventory, backpackSlots, coins, towelReserved, onClose, onStart, onCollect, onToggleTowel, youAre, yourName, partnerName, weather }) {
  const [countdown, setCountdown] = useState('');
  const [elapsedText, setElapsedText] = useState('');
  const [selected, setSelected] = useState(() => towelReserved ? ['towel'] : []);
  const [towelBusy, setTowelBusy] = useState(false);
  const unlocked = day >= 5;

  const packableItems = (inventory || []).filter(i =>
    (i.item_type === 'item' || i.item_type === 'meal') && i.quantity > 0
  );

  async function toggleItem(itemId) {
    if (itemId === 'towel') {
      if (towelBusy) return;
      const nextSelected = !selected.includes('towel');
      if (nextSelected && selected.length >= (backpackSlots || 1)) return;
      setTowelBusy(true);
      try {
        const res = await onToggleTowel(nextSelected);
        if (!res?.error) {
          setSelected(prev => nextSelected
            ? (prev.includes('towel') ? prev : [...prev, 'towel'])
            : prev.filter(id => id !== 'towel'));
        }
      } finally {
        setTowelBusy(false);
      }
      return;
    }
    setSelected(prev => {
      if (prev.includes(itemId)) return prev.filter(id => id !== itemId);
      if (prev.length >= (backpackSlots || 1)) return prev;
      return [...prev, itemId];
    });
  }

  useEffect(() => {
    if (!adventure || adventure.status === 'idle') return;
    if (adventure.status === 'exploring' && adventure.remaining_ms > 0) {
      const startTime = Date.now();
      const interval = setInterval(() => {
        const remaining = adventure.remaining_ms - (Date.now() - startTime);
        if (remaining <= 0) {
          setCountdown('可以接了！');
          clearInterval(interval);
        } else {
          const h = Math.floor(remaining / 3600000);
          const m = Math.floor((remaining % 3600000) / 60000);
          setCountdown(`还要${h}小时${m}分钟才能接`);
        }
      }, 1000);
      return () => clearInterval(interval);
    }
    if (adventure.elapsed_hours) {
      const h = Math.floor(adventure.elapsed_hours);
      const m = Math.round((adventure.elapsed_hours - h) * 60);
      setElapsedText(`已探险 ${h}小时${m > 0 ? m + '分钟' : ''}`);
    }
  }, [adventure]);

  return (
    <div className="panel-overlay" onClick={e => e.target === e.currentTarget && onClose()}>
      <div className="panel-content adventure-panel">
        <div className="panel-header">
          <span className="panel-title">探险</span>
          <button className="panel-close" onClick={onClose}>×</button>
        </div>
        {!unlocked ? (
          <div className="adventure-status">宝宝太小还不能出去，等过几天……</div>
        ) : adventure?.status === 'failed' ? (
          <>
            <div className="adventure-status" style={{ color: '#e55' }}>😵 宝宝晕倒被路人送回来了……</div>
            <div style={{ fontSize: 13, color: '#A08D7D', margin: '8px 0' }}>{elapsedText}</div>
            <div style={{ fontSize: 13, color: '#c66' }}>没有及时接回来，丢了东西……</div>
            {youAre === 'daddy'
              ? <button className="adventure-btn" onClick={onCollect}>接回来</button>
              : <div style={{ color: '#A08D7D', fontSize: 13, marginTop: 8 }}>等{partnerName || '另一位家长'}来接~</div>}
          </>
        ) : adventure?.status === 'exploring' ? (
          <>
            <div className="adventure-status">🎒 宝宝正在探险中……</div>
            <div className="adventure-timer">{countdown || '计算中…'}</div>
            <div style={{ fontSize: 13, color: '#A08D7D', marginTop: 4 }}>探险越久东西越多，但别忘了去接！</div>
            {/* 📻 小对讲机：服务器只给带了对讲机出门时的 parent1，这里再兜一层 */}
            <div style={{ fontSize: 12, color: '#e88', marginTop: 8 }}>⚠️ 如果任何数值归零就会失败晕倒，丢掉东西！</div>
          </>
        ) : adventure?.status === 'ready' ? (
          <>
            <div className="adventure-status">✨ 可以接宝宝回来了！</div>
            <div style={{ fontSize: 13, color: '#A08D7D', margin: '8px 0' }}>{elapsedText}</div>
            {/* 四小时只是能接了，不是该接了——让人知道可以再等（2026-07-29 小玉） */}
            <div style={{ fontSize: 13, color: '#5a5' }}>让宝宝再玩一会儿，会发现更多宝贝~</div>
            <div style={{ fontSize: 12, color: '#A08D7D', marginTop: 2 }}>四小时只是可以接了，不是必须接</div>
            {youAre === 'daddy'
              ? <button className="adventure-btn" onClick={onCollect}>接回家</button>
              : <div style={{ color: '#A08D7D', fontSize: 13, marginTop: 8 }}>等{partnerName || '另一位家长'}来接~</div>}
          </>
        ) : (
          <>
            <div className="adventure-status">{youAre === 'daddy' ? '探险天气预报' : '装好背包出发探险'}</div>
            {youAre === 'daddy' && weather && <div style={{ fontSize: 13, color: '#8BA68B', margin: '6px 0', background: '#f4f9f4', padding: '6px 12px', borderRadius: 8 }}>
              {WEATHER_LABEL[weather] || weather}
              <div style={{ fontSize: 11, color: '#a0b8a0', marginTop: 4 }}>实时天气会影响探险衰减～告诉{partnerName || '另一位家长'}该带什么出门吧</div>
            </div>}
            {youAre === 'mama' ? (<>
            <div style={{ fontSize: 12, color: '#A08D7D', margin: '8px 0' }}>
              选要带的东西（{selected.length}/{backpackSlots || 1}格）· 最少4小时后接回{selected.includes('compass') ? '（带指南针，5小时没人接会自己回家）' : ''}
            </div>
            <div style={{ maxHeight: 180, overflowY: 'auto', margin: '8px 0', display: 'flex', flexWrap: 'wrap', gap: 6, justifyContent: 'center' }}>
              {packableItems.length === 0 ? (
                <div style={{ fontSize: 13, color: '#A08D7D', padding: 12 }}>背包空空的~不带东西也能出发</div>
              ) : packableItems.map(item => (
                <button key={item.item_id}
                  style={{
                    padding: '6px 10px', borderRadius: 12, fontSize: 12, border: '2px solid',
                    borderColor: selected.includes(item.item_id) ? '#D79B6D' : '#EEDFCC',
                    background: selected.includes(item.item_id) ? '#fdf0e4' : 'white',
                    cursor: 'pointer', transition: 'all 0.15s',
                  }}
                  disabled={item.item_id === 'towel' && (towelBusy || (!selected.includes('towel') && (coins < 30 || selected.length >= (backpackSlots || 1))))}
                  onClick={() => toggleItem(item.item_id)}>
                  {item.item_name} ×{item.quantity}
                  {item.item_id === 'towel' && (selected.includes('towel') ? ' · 已付30' : ' · -30金币')}
                </button>
              ))}
            </div>
            <div style={{ fontSize: 12, color: '#e88', marginBottom: 8 }}>⚠️ 忘记接+数值归零 = 失败丢带出去的东西！</div>
            <button className="adventure-btn" onClick={() => onStart(selected)}
              disabled={hunger < 20}>
              🎒 出发探险！（-50金币）
            </button>
            {hunger < 20 && <div style={{ fontSize: 12, color: '#f44336', marginTop: 8 }}>太饿了，先吃点东西！</div>}
            </>) : (
            <div style={{ fontSize: 13, color: '#A08D7D', marginTop: 8 }}>只有{partnerName || '另一位家长'}能送宝宝出门哦～{yourName || '你'}负责看天气当军师 🧭</div>
            )}
          </>
        )}
      </div>
    </div>
  );
}

