// 赛博宝宝前端（2026-10-21 从 index.html 的内联 <script type="text/babel"> 原样搬出）：Toast、故事弹窗、厨房/做饭。
// index.html 按顺序加载 app/*.jsx，前面文件定义的全局（函数、常量、组件）后面的文件直接用。
// ═══════════════ Toast 组件 ═══════════════
function Toast({ message, onDone }) {
  useEffect(() => {
    const t = setTimeout(onDone, 2500);
    return () => clearTimeout(t);
  }, []);
  return <div className="toast">{message}</div>;
}

// ═══════════════ 故事弹窗 ═══════════════
function StoryDialog({ event, onClose, parentName, partnerName, babyName, youAre, onRefresh }) {
  const [lineIdx, setLineIdx] = useState(0);
  const [phase, setPhase] = useState('reading');
  const [replyText, setReplyText] = useState('');
  const [aiResult, setAiResult] = useState(null);
  const [sending, setSending] = useState(false);
  if (!event || !event.lines) return null;

  const bName = babyName || '宝宝';
  // 另一位家长怎么称呼。家里改过称呼就用改过的，没传就按爸爸/妈妈对倒
  const otherName = partnerName || '另一位家长';
  const visibleLines = event.lines.slice(0, lineIdx + 1);
  const allShown = lineIdx + 1 >= event.lines.length;

  useEffect(() => {
    if (phase === 'reading' && allShown) {
      const t = setTimeout(() => setPhase('respond'), 1500);
      return () => clearTimeout(t);
    }
  }, [phase, allShown]);

  function advance() {
    if (phase === 'reading' && allShown) {
      setPhase('respond');
    } else if (phase === 'reading') {
      setLineIdx(prev => prev + 1);
    }
  }

  async function sendReply() {
    if (!replyText.trim() || sending) return;
    setSending(true);
    try {
      if (event._isCoopChoice) {
        const res = await api('/coop/choice', 'POST', { eventId: event._choiceEventId || event.id, choice: replyText });
        const msg = res.bothChosen ? `${bName}的信写好啦！` : `${bName}记住了，等另一个人也写完就能拼信了~`;
        setAiResult({ reply: msg, coinReward: 10 });
      } else if (event._isCoopPending) {
        // 后到的那位家长在网页上回应合作剧情。2026-08-05 之前这条路只有 MCP 走得通，
        // 网页上压根没有入口——妈妈发起完就永远停在「等爸爸来帮忙」那行字上。
        const res = await api('/coop/respond', 'POST', { eventId: event._coopTaskId, message: replyText });
        // 这条路给的是礼物额度不是金币（服务端 /coop/respond），所以金币栏留空，另外提示
        setAiResult({ reply: res.babyReply || `${bName}开心地笑了~`, coinReward: 0, giftCredit: !!res.giftCreditEarned });
      } else if (event._daddyChoices) {
        const res = await api('/coop/respond', 'POST', { eventId: event.id.replace(/_daddy$/, ''), message: replyText });
        setAiResult({ reply: res.babyReply || `${bName}开心地笑了~`, coinReward: 10 });
      } else {
        const context = event.lines.map(l => l.text).join(' ').slice(0, 200);
        const res = await api('/story/react', 'POST', { message: replyText, storyTitle: event.title, storyContext: context });
        setAiResult(res);
      }
      setPhase('result');
      if (onRefresh) onRefresh();
    } catch (e) {
      setPhase('result');
      setAiResult({ reply: `${bName}开心地笑了~`, coinReward: 0 });
    }
    setSending(false);
  }

  function replaceNames(t) {
    const other = otherName;
    // 剧本里的「他/她」指另一位家长：用 ta 的称呼（⛔ 按角色猜性别）
    return (t || '').replace(/\{name\}/g, bName)
      .replace(/妈妈（\/爸爸）/g, parentName).replace(/爸爸（\/妈妈）/g, other)
      .replace(/（\/爸爸）/g, '').replace(/（\/妈妈）/g, '')
      .replace(/他\/她/g, other || '另一位家长').replace(/\{parent2\}/g, (youAre === 'mama' ? parentName : other) || '另一位家长');   // {parent2}＝第二位家长（10/21）
  }

  return (
    <div className="story-overlay" onClick={phase === 'reading' ? advance : undefined}>
      <div className="story-box" onClick={e => e.stopPropagation()}>
        {event.image && <img className="story-image" src={`assets/story/${event.image}?v=${window.STORY_V}`} alt="" style={event.imagePos ? { objectPosition: event.imagePos } : undefined} />}{/* imagePos：竖图在 16:10 框里裁哪一段（day55 自画像皮皮在画底下） */}
        <div className="story-body">
          <div className="story-title">{replaceNames(event.title)}</div>
          {/* 2026-08-05：合作剧情一开场就说清楚这是要两个人的。
              在这之前它跟普通剧情长得一模一样，只有喂饭按钮上头一行小字提过一嘴——
              小玉玩了 73 天都没认出来那是合作剧情。 */}
          {(event.coopRequired || event._isCoopPending) && (
            <div style={{ textAlign: 'center', fontSize: 12, color: '#D79B6D', background: 'rgba(215,155,109,0.12)', borderRadius: 10, padding: '6px 10px', margin: '0 0 10px' }}>
              👨‍👩‍👧 这是一段要两个人一起走的剧情
              <div style={{ fontSize: 11, color: '#A08D7D', marginTop: 2 }}>
                {event._isCoopPending ? `${bName}在等你回一句` : `说完这段，再叫${otherName}来接着往下走`}
              </div>
            </div>
          )}
          {visibleLines.map((line, i) => {
            const text = replaceNames(line.text);
            return (
              <div key={i} className={`story-line ${line.type || 'narration'}`}>
                {line.image && <img className="story-line-image" src={`assets/story/${line.image}?v=${window.STORY_V}`} alt="" />}
                {line.type === 'baby' ? `「${text}」` : text}
              </div>
            );
          })}

          {phase === 'reading' && !allShown && (
            <button className="story-next" onClick={advance}>▼</button>
          )}

          {allShown && event.effect && (
            <div style={{ textAlign: 'center', fontSize: 13, color: '#D79B6D', margin: '8px 0', padding: '6px 12px', background: 'rgba(215,155,109,0.1)', borderRadius: 8 }}>✨ {replaceNames(event.effect)}</div>
          )}

          {phase === 'respond' && event._daddyChoices ? (
            <div style={{ marginTop: 12 }}>
              <div style={{ fontSize: 12, color: '#A08D7D', marginBottom: 6, textAlign: 'center' }}>选一个开头吧~</div>
              {/* 2026-08-28 小玉报：全家信的三个选项直接把原文贴上来，{name} 没替换就摆到她面前，
                  点完存进库的也是带占位符的那一串。选项跟别处一样要过 renderFamilyText，
                  而且存的必须是渲染后的文本——这封信以后要拼进合并信里给孩子看的。 */}
              {event._daddyChoices.map((choice, i) => {
                const shown = renderFamilyText(choice, bName, parentName, otherName);
                return (
                  <button key={i} onClick={() => { setReplyText(shown); }} disabled={sending}
                    style={{ display: 'block', width: '100%', margin: '6px 0', padding: '10px 14px', background: replyText === shown ? '#FFF3E0' : '#FAFAF5', border: replyText === shown ? '2px solid #D79B6D' : '1px solid #EEDFCC', borderRadius: 10, fontSize: 14, textAlign: 'left', cursor: 'pointer', color: '#5C4A3A' }}>
                    {shown}
                  </button>
                );
              })}
              {replyText && (
                <button className="chat-send" onClick={sendReply} disabled={sending}
                  style={{ width: '100%', marginTop: 8, padding: '10px 14px', background: '#D79B6D', color: '#fff', border: 'none', borderRadius: 10, fontSize: 14, cursor: 'pointer', opacity: sending ? 0.5 : 1 }}>
                  {sending ? '选好了…' : '就这个！'}
                </button>
              )}
              <button onClick={() => onClose(event)} style={{ width: '100%', marginTop: 8, padding: 8, background: 'none', border: 'none', color: '#A08D7D', fontSize: 12, cursor: 'pointer' }}>跳过</button>
            </div>
          ) : phase === 'respond' && (
            <div style={{ marginTop: 12 }}>
              <div style={{ fontSize: 12, color: '#A08D7D', marginBottom: 6, textAlign: 'center' }}>跟{bName}说点什么吧~</div>
              <div style={{ display: 'flex', gap: 8 }}>
                <input
                  value={replyText}
                  onChange={e => setReplyText(e.target.value)}
                  onKeyDown={e => e.key === 'Enter' && sendReply()}
                  placeholder="..."
                  autoFocus
                  style={{ flex: 1, padding: '8px 12px', border: '1px solid #EEDFCC', borderRadius: 10, fontSize: 14 }}
                />
                <button className="chat-send" onClick={sendReply} disabled={sending || !replyText.trim()}
                  style={{ opacity: sending ? 0.5 : 1 }}>
                  {sending ? '…' : '➤'}
                </button>
              </div>
              <button onClick={() => {
                const enriched = event.coopRequired ? { ...event, _coopMamaResponse: null } : event;
                onClose(enriched);
              }} style={{ width: '100%', marginTop: 8, padding: 8, background: 'none', border: 'none', color: '#A08D7D', fontSize: 12, cursor: 'pointer' }}>跳过</button>
            </div>
          )}

          {phase === 'result' && aiResult && (
            <div style={{ marginTop: 12 }}>
              <div className="story-line narration" style={{ color: '#8B7355', fontStyle: 'italic', textAlign: 'right' }}>「{replyText}」</div>
              <div className="story-line baby">「{aiResult.reply}」</div>
              {aiResult.coinReward > 0 && (
                <div style={{ textAlign: 'center', fontSize: 13, color: '#D79B6D', marginTop: 8 }}>
                  +{aiResult.coinReward} 金币 ✨
                </div>
              )}
              {aiResult.giftCredit && (
                <div style={{ textAlign: 'center', fontSize: 13, color: '#D79B6D', marginTop: 8 }}>🎁 两个人都到齐了，多了一个礼物额度！</div>
              )}
              {event.coopRequired ? (
                <div style={{ marginTop: 16, textAlign: 'center' }}>
                  {/* 2026-08-05 改：原来这里写「等待爸爸回复…」＋一个叫「先关掉」的按钮，
                      通知其实是按下「先关掉」那一刻才发出去的——她一直在找「叫爸爸来」在哪，
                      找不到，因为那个按钮叫「先关掉」。现在按钮就是那个动作。 */}
                  <div style={{ padding: '12px 16px', background: 'rgba(215,155,109,0.12)', borderRadius: 12, marginBottom: 10 }}>
                    <div style={{ fontSize: 14, color: '#D79B6D', fontWeight: 'bold', marginBottom: 4 }}>这段还没完，{bName}在等{otherName}</div>
                  </div>
                  <button className="story-next" onClick={() => {
                    const enriched = { ...event, _coopMamaResponse: replyText, lines: [...event.lines, { type: 'parent', text: replyText }, { type: 'baby', text: aiResult.reply }] };
                    onClose(enriched);
                  }}>📨 叫{otherName}来</button>
                </div>
              ) : (
                <button className="story-next" onClick={() => {
                  const enriched = { ...event, lines: [...event.lines, { type: 'parent', text: replyText }, { type: 'baby', text: aiResult.reply }] };
                  onClose(enriched);
                }}>关闭</button>
              )}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ═══════════════ 做饭面板 ═══════════════
// ═══════════════ 厨房面板（做饭+建造） ═══════════════
function KitchenPanel({ inventory, coins, day, youAre, yourName, partnerName, onClose, onCook, onBuild }) {
  const [tab, setTab] = useState('cook');
  const ASSETS = window.APP_CONFIG.ASSETS_BASE;
  return (
    <div className="panel slide-left" style={{ background: '#FDF6EE', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
      <div style={{ position: 'relative', width: '100%', maxWidth: 420, aspectRatio: '941 / 1672', maxHeight: '100vh' }}>
        <div style={{ position: 'absolute', inset: 0, backgroundImage: `url(${ASSETS}/UI/kitchen_frame_clear.png)`, backgroundSize: '100% 100%', backgroundRepeat: 'no-repeat', zIndex: 2, pointerEvents: 'none' }} />
        <button className="back-btn-wiggle" onClick={onClose} style={{ position: 'absolute', bottom: '3%', left: '50%', background: 'transparent', border: 'none', cursor: 'pointer', zIndex: 5, padding: 0 }}>
          <img src={`${ASSETS}/UI/back_button.png`} style={{ width: 200, height: 'auto' }} alt="返回" />
        </button>
        <div style={{ position: 'absolute', top: '10%', left: '7%', right: '7%', bottom: '15%', overflowY: 'auto', padding: '0 2px', zIndex: 1 }}>
          <div style={{ display: 'flex', gap: 6, marginBottom: 12, background: '#EEDFCC', borderRadius: 14, padding: 3 }}>
            <button onClick={() => setTab('cook')}
              style={{ flex: 1, padding: '8px 12px', border: 'none', borderRadius: 12, cursor: 'pointer', fontSize: 13, transition: 'all 0.2s',
                background: tab === 'cook' ? '#FFFDF9' : 'transparent', fontWeight: tab === 'cook' ? 'bold' : 'normal',
                color: tab === 'cook' ? '#6E5A4B' : '#A08D7D', boxShadow: tab === 'cook' ? '0 2px 8px rgba(110,90,75,0.1)' : 'none' }}>🍳 做饭</button>
            <button onClick={() => setTab('build')}
              style={{ flex: 1, padding: '8px 12px', border: 'none', borderRadius: 12, cursor: 'pointer', fontSize: 13, transition: 'all 0.2s',
                background: tab === 'build' ? '#FFFDF9' : 'transparent', fontWeight: tab === 'build' ? 'bold' : 'normal',
                color: tab === 'build' ? '#6E5A4B' : '#A08D7D', boxShadow: tab === 'build' ? '0 2px 8px rgba(110,90,75,0.1)' : 'none' }}>🔨 建造</button>
          </div>
          {tab === 'cook' ? <CookPanelInner inventory={inventory} partnerName={partnerName} onCook={onCook} /> :
           <BuildPanelInner inventory={inventory} coins={coins} day={day} youAre={youAre} yourName={yourName} partnerName={partnerName} onBuild={onBuild} />}
        </div>
      </div>
    </div>
  );
}

function CookPanelInner({ inventory, partnerName, onCook }) {
  const ASSETS = window.APP_CONFIG.ASSETS_BASE;
  const [slots, setSlots] = useState([null, null, null, null]);
  const [cooking, setCooking] = useState(false);
  const [result, setResult] = useState(null);

  const invMap = {};
  (inventory || []).forEach(i => {
    if (i.item_type === 'ingredient') invMap[i.item_id] = i.quantity;
  });

  const usedCounts = {};
  slots.forEach(s => { if (s) usedCounts[s] = (usedCounts[s] || 0) + 1; });

  function addToSlot(ingId) {
    const available = (invMap[ingId] || 0) - (usedCounts[ingId] || 0);
    if (available <= 0) return;
    const idx = slots.indexOf(null);
    if (idx === -1) return;
    const next = [...slots];
    next[idx] = ingId;
    setSlots(next);
  }

  function removeSlot(idx) {
    if (cooking) return;
    const next = [...slots];
    next[idx] = null;
    setSlots(next);
  }

  function findMatchingRecipe() {
    const placed = slots.filter(Boolean);
    if (placed.length === 0) return null;
    for (const [id, r] of Object.entries(RECIPES)) {
      if (r.ingredients.length !== placed.length) continue;
      if (recipeMatchesSlots(r.ingredients, placed)) return id;
    }
    return null;
  }

  // 判断玩家放置的食材列表是否满足配方（贪心：先固定槽后数组槽）
  function recipeMatchesSlots(ingredients, placed) {
    // 统计 placed 里各食材数量
    const placedCounts = {};
    for (const p of placed) placedCounts[p] = (placedCounts[p] || 0) + 1;

    // 先扣固定槽
    const remaining = Object.assign({}, placedCounts);
    for (const slot of ingredients) {
      if (typeof slot === 'string') {
        if (!remaining[slot] || remaining[slot] <= 0) return false;
        remaining[slot]--;
      }
    }
    // 再满足数组槽（贪心：用 remaining 里第一个可用的）
    for (const slot of ingredients) {
      if (!Array.isArray(slot)) continue;
      const chosen = slot.find(opt => (remaining[opt] || 0) >= 1);
      if (!chosen) return false;
      remaining[chosen]--;
    }
    return true;
  }

  async function handleCook() {
    const recipeId = findMatchingRecipe();
    if (!recipeId) {
      setResult({ success: false, text: '这些食材组合不出任何菜谱……' });
      return;
    }
    setCooking(true);
    // 2026-08-28 小玉报 bug：锅里摆了什么以前根本没传给后端，二选一的槽（鸡蛋羹的
    // ['egg','milk']）后端自己按配方顺序挑，永远先吃蛋。把实际摆的食材一起传过去。
    const placedNow = slots.filter(Boolean);
    setTimeout(async () => {
      await onCook(recipeId, placedNow);
      setResult({ success: true, text: `${RECIPES[recipeId].name}做好了！` });
      setCooking(false);
    }, 2000);
  }

  if (result) {
    return (
      <div style={{ textAlign: 'center', padding: '30px 10px' }}>
        {result.success ? <div style={{ fontSize: 48, marginBottom: 12 }}>🎉</div> : <img src={`${ASSETS}/items/cross.png`} style={{ width: 64, height: 64, objectFit: 'contain', marginBottom: 12 }} alt="" />}
        <div style={{ fontSize: 16, fontWeight: 'bold', color: '#6E5A4B', marginBottom: 20, whiteSpace: 'pre-line' }}>{result.text}</div>
        <button onClick={() => { setResult(null); setSlots([null, null, null, null]); }}
          style={{ padding: '10px 32px', background: 'linear-gradient(135deg, #D79B6D, #e0ac80)', color: 'white', border: 'none', borderRadius: 14, fontSize: 14, fontWeight: 'bold', cursor: 'pointer' }}>
          继续做饭
        </button>
      </div>
    );
  }

  const hasIngredients = slots.some(Boolean);

  return (
    <>
      <div className="stove-area">
        <img className="stove-img" src={`${ASSETS}/UI/kitchen_stove.png`} alt="灶台" />
        <img className={`pan-img${cooking ? ' pan-cooking' : ''}`}
          src={`${ASSETS}/UI/${hasIngredients || cooking ? 'pan_full' : 'pan_empty'}.png`} alt="锅" />
        {cooking && <div className="cooking-steam" style={{ position: 'absolute', top: '18%', left: '50%', transform: 'translateX(-50%)' }}>
          <span className="steam s1">~</span><span className="steam s2">~</span><span className="steam s3">~</span>
        </div>}
      </div>

      <div className="cook-slots">
        {slots.map((s, i) => (
          <div key={i} className={`cook-slot${s ? ' filled' : ''}`} onClick={() => s && removeSlot(i)}>
            {s ? <>
              <img src={`${ASSETS}/food/ingredients_sprites/${s}.png`} style={{ width: 36, height: 36, objectFit: 'contain' }} onError={e => e.target.style.display='none'} />
              <div style={{ fontSize: 9, color: '#6E5A4B', marginTop: 2 }}>{INGREDIENTS[s]}</div>
            </> : <div style={{ fontSize: 20, color: '#D9C8B4' }}>+</div>}
          </div>
        ))}
      </div>

      <div className="cook-hint">配方可以问问{partnerName || '另一位家长'} 🐾{isNew() && <span className="new-bubble inline">New</span>}</div>
      <button onClick={handleCook} disabled={!hasIngredients || cooking}
        style={{ display: 'block', margin: '8px auto', padding: '10px 40px', background: hasIngredients ? 'linear-gradient(135deg, #D79B6D, #e0ac80)' : '#E0D5C8', color: 'white', border: 'none', borderRadius: 14, fontSize: 15, fontWeight: 'bold', cursor: hasIngredients ? 'pointer' : 'default', opacity: hasIngredients ? 1 : 0.5 }}>
        {cooking ? '做饭中……' : 'Cook!'}
      </button>

      <div style={{ marginTop: 10 }}>
        <div className="inv-category">🌿 食材库</div>
        <div className="inventory-grid">
          {Object.entries(invMap).map(([id, qty]) => {
            const available = qty - (usedCounts[id] || 0);
            return (
              <div key={id} className={`inv-slot${available <= 0 ? ' used-up' : ''}`} onClick={() => available > 0 && addToSlot(id)}>
                <img className="inv-img" src={`${ASSETS}/food/ingredients_sprites/${id}.png`}
                  onError={e => e.target.style.display='none'} />
                <div className="inv-name">{INGREDIENTS[id] || id}</div>
                {available > 0 && <span className="inv-qty">×{available}</span>}
              </div>
            );
          })}
        </div>
      </div>
    </>
  );
}

