/* App — 社畜小猫桌宠 主编排
 * 亲密度 / 状态衰减 / 碎碎念 / 等待与惊喜 / 投喂 / 聊天 / 升级 / Tweaks
 */
const { useState: uS, useEffect: uE, useRef: uR, useCallback: uC } = React;

const STATE_KEY = "shechu-cat-state-v2";
const AWAY_MIN_MS = 45 * 1000; // 演示:离开 ≥45s 回来就有明信片
const EXPLORATION_CHECK_MS = 6 * 60 * 1000;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/ {
  paperTone: "适中",
  catScale: 1,
  chatterFreq: "适中",
  decaySpeed: "正常",
}; /*EDITMODE-END*/

const PAPER_TONES = {
  浅: { kraft: "#d3b889", deep: "#c2a36a" },
  适中: { kraft: "#c7a574", deep: "#b08d57" },
  深: { kraft: "#b8945c", deep: "#a07d45" },
};
const CHATTER_MS = { 安静: 22000, 适中: 12000, 话痨: 6500 };
const DECAY = {
  关闭: null,
  正常: { ms: 17000, e: 1.6, f: 2.2, m: 1.1 },
  演示快: { ms: 5200, e: 3, f: 4, m: 2 },
};

function clamp(v) {
  return Math.max(0, Math.min(100, v));
}
function uid() {
  return Date.now() + "-" + Math.random().toString(16).slice(2, 7);
}
function idleAction(scene) {
  return scene === "office" ? "talk" : "cute";
}
function stampFor(kind, action) {
  if (kind === "feed") return "eat";
  if (kind === "pet") return "cute";
  if (kind === "celebrate") return "cute";
  if (kind === "comfort") return "sleep";
  if (kind === "memory") return "received";
  if (kind === "work") return "work";
  return null;
}

function formatBeijingTime() {
  return new Intl.DateTimeFormat("zh-CN", {
    timeZone: "Asia/Shanghai",
    hour: "2-digit",
    minute: "2-digit",
    hour12: false,
  }).format(new Date());
}

function loadState() {
  try {
    return JSON.parse(window.localStorage.getItem(STATE_KEY) || "null");
  } catch {
    return null;
  }
}

function pickPostcards(scene, hours) {
  const PET = window.PET;
  const pool = PET.POSTCARDS.slice();
  // 优先当前场景,再随机补一张
  pool.sort(
    (a, b) => (b.scene === scene) - (a.scene === scene) || Math.random() - 0.5,
  );
  const n = hours >= 6 ? 2 : 1;
  return pool.slice(0, n);
}

function letterToCard(letter) {
  return {
    id: `letter-${letter.id || Date.now()}`,
    at: letter.created_at ? Date.parse(letter.created_at) : Date.now(),
    stamp: letter.stamp || "talk",
    scene: "online",
    title: letter.title,
    body: letter.body,
    effect: letter.effect || {},
    tag: "小猫探索",
    kind: "exploration",
    section: letter.section || "exploration",
    topic: letter.topic,
    sourceTitle: letter.source_title,
    sourceUrl: letter.source_url,
    nextAfter: letter.next_after,
    toneLevel: letter.tone_level,
  };
}

function App() {
  const PET = window.PET;
  const Brain = window.PetBrain;
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);

  const saved = uR(loadState()).current;
  const [intimacy, setIntimacy] = uS(saved?.intimacy ?? 10);
  const [energy, setEnergy] = uS(saved?.energy ?? 76);
  const [fullness, setFullness] = uS(saved?.fullness ?? 68);
  const [mood, setMood] = uS(saved?.mood ?? 82);
  const [scene, setScene] = uS(saved?.scene ?? "office");
  const [action, setActionRaw] = uS(saved?.scene === "home" ? "cute" : "talk");
  const [messages, setMessages] = uS(
    saved?.messages?.length
      ? saved.messages
      : [
          {
            id: uid(),
            role: "pet",
            text: "打卡到岗!点点我会有反应,投喂能补能量,聊得越多我们越熟喵~",
          },
        ],
  );
  const [mailbox, setMailbox] = uS(saved?.mailbox ?? []);
  const [bubble, setBubble] = uS(null);
  const [stamp, setStamp] = uS(null);
  const [busy, setBusy] = uS(false);
  const [categoryId, setCategoryId] = uS(saved?.categoryId ?? "daily");
  const [beijingTime, setBeijingTime] = uS(formatBeijingTime);
  const [showMailbox, setShowMailbox] = uS(false);
  const [surprise, setSurprise] = uS(null);
  const [cooldown, setCooldown] = uS({});

  const intimacyLv = PET.intimacyState(intimacy).lv;
  const revertRef = uR(0);
  const bubbleRef = uR(0);

  // ---- 派生:把当前状态打包给 Brain ----
  const stateRef = uR({});
  stateRef.current = {
    scene,
    action,
    energy,
    fullness,
    mood,
    intimacy,
    intimacyLv,
    beijingTime,
    history: messages,
  };

  // ---- 小猫可见的北京时间 ----
  uE(() => {
    const tick = () => setBeijingTime(formatBeijingTime());
    tick();
    const id = setInterval(tick, 30000);
    return () => clearInterval(id);
  }, []);

  // ---- 应用 paperTone ----
  uE(() => {
    const tone = PAPER_TONES[t.paperTone] || PAPER_TONES["适中"];
    document.documentElement.style.setProperty("--kraft", tone.kraft);
    document.documentElement.style.setProperty("--kraft-deep", tone.deep);
  }, [t.paperTone]);

  // ---- 持久化 ----
  uE(() => {
    const data = {
      intimacy,
      energy,
      fullness,
      mood,
      scene,
      categoryId,
      mailbox,
      messages: messages.slice(-40),
      lastSeen: Date.now(),
    };
    window.localStorage.setItem(STATE_KEY, JSON.stringify(data));
  }, [intimacy, energy, fullness, mood, scene, categoryId, mailbox, messages]);

  uE(() => {
    const save = () => {
      const prev = loadState() || {};
      window.localStorage.setItem(
        STATE_KEY,
        JSON.stringify({ ...prev, lastSeen: Date.now() }),
      );
    };
    window.addEventListener("pagehide", save);
    window.addEventListener("beforeunload", save);
    return () => {
      window.removeEventListener("pagehide", save);
      window.removeEventListener("beforeunload", save);
    };
  }, []);

  // ---- 工具 ----
  const pushMsg = uC(
    (m) => setMessages((arr) => [...arr, { id: uid(), ...m }]),
    [],
  );
  const popBubble = uC((text, ms = 3800) => {
    setBubble({ text, nonce: uid() });
    clearTimeout(bubbleRef.current);
    bubbleRef.current = setTimeout(() => setBubble(null), ms);
  }, []);
  const popStamp = uC((key) => key && setStamp({ key, nonce: uid() }), []);

  const setAction = uC((a, opts = {}) => {
    setActionRaw(a);
    clearTimeout(revertRef.current);
    // 瞬时动作:吃饭/打滚 播一会儿后回到待机
    if ((a === "eat" || a === "roll") && !opts.hold) {
      revertRef.current = setTimeout(
        () => {
          setActionRaw(idleAction(stateRef.current.scene));
        },
        a === "roll" ? 3200 : 4200,
      );
    }
  }, []);

  const addIntimacy = uC((delta) => {
    setIntimacy((p) => {
      const before = PET.intimacyState(p).lv;
      const next = p + delta;
      const after = PET.intimacyState(next).lv;
      if (after > before) {
        const st = PET.intimacyState(next);
        setTimeout(() => {
          pushMsg({ type: "toast", text: `亲密度提升!现在是「${st.title}」` });
          setActionRaw("roll");
          popStamp("cute");
          clearTimeout(revertRef.current);
          revertRef.current = setTimeout(
            () => setActionRaw(idleAction(stateRef.current.scene)),
            3200,
          );
        }, 280);
      }
      return next;
    });
  }, []);

  // ---- 等待与惊喜:首屏判定 ----
  uE(() => {
    const s = saved;
    const now = Date.now();
    if (s && s.lastSeen) {
      const away = now - s.lastSeen;
      if (away >= AWAY_MIN_MS) {
        const hours = away / 3600000;
        // 离开期间状态自然衰减
        setEnergy((e) => clamp(e - Math.min(45, hours * 9 + 6)));
        setFullness((f) => clamp(f - Math.min(50, hours * 11 + 8)));
        setMood((m) => clamp(m - Math.min(30, hours * 5)));
        const cards = pickPostcards(s.scene || "office", hours);
        // 应用明信片正向效果 + 收进信箱
        const stamped = cards.map((c) => ({ ...c, at: now + Math.random() }));
        stamped.forEach((c) => {
          if (c.effect.energy) setEnergy((e) => clamp(e + c.effect.energy));
          if (c.effect.fullness)
            setFullness((f) => clamp(f + c.effect.fullness));
          if (c.effect.mood) setMood((m) => clamp(m + c.effect.mood));
          if (c.effect.intimacy) addIntimacy(c.effect.intimacy);
        });
        setMailbox((mb) => [...mb, ...stamped]);
        setSurprise(stamped);
      }
    }
  }, []);

  // ---- 状态衰减 + 睡眠回血 + 低电量自动趴下 ----
  uE(() => {
    const cfg = DECAY[t.decaySpeed];
    if (!cfg) return;
    const id = setInterval(() => {
      const a = stateRef.current.action;
      if (a === "sleep") {
        setEnergy((e) => clamp(e + 7));
        setFullness((f) => clamp(f - cfg.f * 0.4));
      } else {
        setEnergy((e) => clamp(e - cfg.e));
        setFullness((f) => clamp(f - cfg.f));
        setMood((m) => clamp(m - cfg.m));
      }
    }, cfg.ms);
    return () => clearInterval(id);
  }, [t.decaySpeed]);

  // 睡醒 / 低电量趴下(独立轻量轮询)
  uE(() => {
    const id = setInterval(() => {
      const a = stateRef.current.action;
      const e = stateRef.current.energy;
      if (a === "sleep" && e >= 64) {
        setActionRaw(idleAction(stateRef.current.scene));
        popBubble("睡饱啦,电量回来了,继续陪你冲!");
        popStamp(null);
      } else if (a !== "sleep" && e < 15 && !busy) {
        setActionRaw("sleep");
        popStamp("sleep");
        popBubble("电量见底…让我趴一小会儿喵。");
      }
    }, 3500);
    return () => clearInterval(id);
  }, [busy, popBubble, popStamp]);

  // ---- 碎碎念 ----
  uE(() => {
    const ms = CHATTER_MS[t.chatterFreq] || 12000;
    const id = setInterval(() => {
      if (busy || surprise || showMailbox) return;
      const s = stateRef.current;
      let pool;
      if (s.energy < 24) pool = PET.CHATTER.tired;
      else if (s.fullness < 24) pool = PET.CHATTER.hungry;
      else if (s.intimacyLv >= 4 && Math.random() < 0.4)
        pool = PET.CHATTER.intimate;
      else pool = PET.CHATTER[s.scene] || PET.CHATTER.office;
      popBubble(pool[Math.floor(Math.random() * pool.length)], 4200);
    }, ms);
    return () => clearInterval(id);
  }, [t.chatterFreq, busy, surprise, showMailbox, popBubble]);

  // ---- 摸摸 ----
  const onPet = uC(() => {
    addIntimacy(PET.GAIN.pet);
    setMood((m) => clamp(m + 4));
    popStamp("cute");
    if (Math.random() < 0.5) {
      const lines =
        intimacyLv >= 4
          ? PET.CHATTER.intimate
          : ["喵~好舒服", "再摸摸嘛", "蹭蹭你"];
      popBubble(lines[Math.floor(Math.random() * lines.length)], 2600);
    }
  }, [intimacyLv, addIntimacy]);

  // ---- 投喂 ----
  const onFeed = uC(
    (food) => {
      if (cooldown[food.id]) return;
      setAction("eat");
      popStamp("eat");
      popBubble(food.line, 3600);
      setFullness((f) => clamp(f + food.full));
      setEnergy((e) => clamp(e + food.energy));
      setMood((m) => clamp(m + food.mood));
      addIntimacy(PET.GAIN.feed);
      setCooldown((c) => ({ ...c, [food.id]: true }));
      setTimeout(() => setCooldown((c) => ({ ...c, [food.id]: false })), 4500);
    },
    [cooldown, setAction, addIntimacy, popBubble, popStamp],
  );

  // ---- 发图 ----
  const onPhoto = uC(
    async (dataUrl, pickedCategoryId = categoryId, text = "") => {
      const category = PET.categoryById(pickedCategoryId);
      const caption = text || `${category.label}: 给小猫看一张图`;
      pushMsg({
        role: "user",
        photo: dataUrl,
        text: caption,
        category: category.id,
        categoryLabel: category.label,
      });
      addIntimacy((category.gain || PET.GAIN.photo) + PET.GAIN.photo);
      if (category.energy) setEnergy((e) => clamp(e + category.energy));
      if (category.fullness) setFullness((f) => clamp(f + category.fullness));
      setMood((m) => clamp(m + (category.mood || 3)));
      setAction(category.action || "cute", {
        hold: ["work", "talk", "sleep", "cute"].includes(category.action),
      });
      popStamp(
        stampFor(
          category.memory === "highlight" ? "celebrate" : "memory",
          category.action,
        ),
      );
      setBusy(true);

      const typing = { id: uid(), role: "pet", typing: true };
      setMessages((arr) => [...arr, typing]);

      let reply;
      try {
        reply = await Brain.reply(caption, stateRef.current, category, dataUrl);
      } catch {
        reply = {
          action: category.action || "cute",
          text: `哇,我认真看啦!这张图我按「${category.label}」收进小本本了喵。`,
          fallback: true,
        };
      }

      setMessages((arr) =>
        arr.map((m) =>
          m.id === typing.id
            ? {
                ...m,
                typing: false,
                text:
                  reply.text ||
                  `哇,我认真看啦!这张图我按「${category.label}」收进小本本了喵。`,
                fallback: reply.fallback,
              }
            : m,
        ),
      );

      if (reply.memoryHits && reply.memoryHits.length) {
        pushMsg({
          type: "mem",
          text: `命中 ${reply.memoryHits.length} 条记忆`,
        });
      }

      setAction(reply.action || category.action || "cute", {
        hold: ["work", "talk", "sleep", "cute"].includes(
          reply.action || category.action,
        ),
      });

      if (category.memory !== "none") {
        Brain.remember(caption, "photo", {
          category: category.id,
          categoryLabel: category.label,
          memoryPolicy: category.memory,
          hasImage: true,
        });
      }
      setBusy(false);
    },
    [categoryId, addIntimacy, setAction, popStamp],
  );

  // ---- 聊天 ----
  const send = uC(
    async (text, pickedCategoryId = categoryId) => {
      const category = PET.categoryById(pickedCategoryId);
      pushMsg({
        role: "user",
        text,
        category: category.id,
        categoryLabel: category.label,
      });
      setBusy(true);
      setAction(category.action || "work", {
        hold: ["work", "talk", "sleep", "cute"].includes(category.action),
      });
      setBubble(null);

      const typing = { id: uid(), role: "pet", typing: true };
      setMessages((arr) => [...arr, typing]);

      let reply;
      try {
        reply = await Brain.reply(text, stateRef.current, category);
      } catch {
        reply = {
          action: category.action || "talk",
          status: category.status || "陪聊中",
          text: "我在的,继续说喵。",
          kind: "chat",
        };
      }

      setMessages((arr) =>
        arr.map((m) =>
          m.id === typing.id
            ? {
                ...m,
                typing: false,
                text: reply.text,
                fallback: reply.fallback,
              }
            : m,
        ),
      );

      if (reply.memoryHits && reply.memoryHits.length) {
        pushMsg({
          type: "mem",
          text: `命中 ${reply.memoryHits.length} 条记忆`,
        });
      }

      setAction(reply.action || "talk", {
        hold: ["work", "talk", "sleep", "cute"].includes(reply.action),
      });
      popStamp(stampFor(reply.kind, reply.action));

      // 互动加成
      let gain = category.gain || PET.GAIN.chat;
      if (reply.rememberThis || category.memory === "highlight")
        gain += PET.GAIN.share;
      const eff = reply.effect || {};
      if (category.fullness) setFullness((f) => clamp(f + category.fullness));
      if (category.energy) setEnergy((e) => clamp(e + category.energy));
      if (category.mood) setMood((m) => clamp(m + category.mood));
      if (eff.fullness) setFullness((f) => clamp(f + eff.fullness));
      if (eff.energy) setEnergy((e) => clamp(e + eff.energy));
      if (eff.mood) setMood((m) => clamp(m + eff.mood));
      addIntimacy(gain + (eff.intimacyBonus || 0));

      if (reply.rememberThis || category.memory === "highlight") {
        Brain.remember(text, "achievement", {
          category: category.id,
          categoryLabel: category.label,
          memoryPolicy: "highlight",
        });
      } else if (category.memory && category.memory !== "none") {
        Brain.remember(text, category.memory, {
          category: category.id,
          categoryLabel: category.label,
          memoryPolicy: category.memory,
        });
      } else if (Brain.shouldRememberImplicit(text)) {
        Brain.remember(text, "implicit", {
          category: category.id,
          categoryLabel: category.label,
          memoryPolicy: "implicit",
        });
      }

      setBusy(false);
    },
    [categoryId, setAction, addIntimacy, popStamp],
  );

  const onQuick = uC(
    (q) => {
      if (q.kind === "share") {
        // 分享成就:更高加成
        pushMsg({ role: "user", text: q.text });
        setBusy(true);
        setAction("roll");
        popStamp("cute");
        setTimeout(() => {
          pushMsg({
            role: "pet",
            text: "好耶!这件事我帮你存进高光时刻,为你打滚庆祝!",
          });
          pushMsg({ type: "mem", text: "已记入「高光时刻」" });
          Brain.remember(q.text, "achievement");
          addIntimacy(PET.GAIN.share);
          setMood((m) => clamp(m + 10));
          setBusy(false);
        }, 820);
      } else {
        send(q.text);
      }
    },
    [send, setAction, addIntimacy, popStamp],
  );

  // ---- 惊喜领取 ----
  const claimSurprise = uC(() => {
    setSurprise(null);
    pushMsg({
      role: "pet",
      text: "我回来啦!刚才的小事都记在信箱里咯,我们继续一起冲~",
    });
  }, []);

  const addExplorationLetter = uC(
    (letter, announce = true) => {
      if (!letter || !letter.due) return false;
      const card = letterToCard(letter);
      setMailbox((mb) => {
        if (mb.some((it) => it.id === card.id)) return mb;
        return [...mb, card];
      });
      if (announce) setSurprise([card]);
      if (card.effect?.mood) setMood((m) => clamp(m + card.effect.mood));
      if (card.effect?.intimacy) addIntimacy(card.effect.intimacy);
      if (announce) {
        popBubble("我出门逛互联网回来啦,给你写了封信!");
        popStamp(card.stamp);
      }
      return true;
    },
    [addIntimacy, popBubble, popStamp],
  );

  const checkExploration = uC(
    async (force = false) => {
      if (!Brain.hasBackend()) return;
      try {
        const letter = await Brain.explore(stateRef.current, force);
        addExplorationLetter(letter, force);
      } catch (err) {
        if (force) {
          pushMsg({
            type: "toast",
            text: "小猫出门信号不太稳,等会儿再试一次。",
          });
        }
      }
    },
    [addExplorationLetter, pushMsg],
  );

  // ---- 小猫后台探索:按亲密度由后端控制频率 ----
  uE(() => {
    const first = setTimeout(() => checkExploration(false), 45000);
    const id = setInterval(() => checkExploration(false), EXPLORATION_CHECK_MS);
    return () => {
      clearTimeout(first);
      clearInterval(id);
    };
  }, [checkExploration]);

  // ---- Tweaks 工具:模拟离开 / 重置 ----
  const simulateAway = uC(() => {
    const hours = 3;
    setEnergy((e) => clamp(e - 30));
    setFullness((f) => clamp(f - 38));
    setMood((m) => clamp(m - 16));
    const cards = pickPostcards(scene, hours).map((c) => ({
      ...c,
      at: Date.now() + Math.random(),
    }));
    cards.forEach((c) => {
      if (c.effect.energy) setEnergy((e) => clamp(e + c.effect.energy));
      if (c.effect.fullness) setFullness((f) => clamp(f + c.effect.fullness));
      if (c.effect.mood) setMood((m) => clamp(m + c.effect.mood));
      if (c.effect.intimacy) addIntimacy(c.effect.intimacy);
    });
    setMailbox((mb) => [...mb, ...cards]);
    setSurprise(cards);
  }, [scene, addIntimacy]);

  const resetAll = uC(() => {
    window.PetBrain.clearMemory();
    window.localStorage.removeItem(STATE_KEY);
    setIntimacy(10);
    setEnergy(76);
    setFullness(68);
    setMood(82);
    setCategoryId("daily");
    setMailbox([]);
    setMessages([
      { id: uid(), role: "pet", text: "重新认识一下~我是社畜小猫,请多关照喵!" },
    ]);
    setActionRaw("talk");
    setScene("office");
  }, []);

  const unread = surprise ? 0 : 0; // 信箱角标用累计数
  const mailUnread = mailbox.length;

  return (
    <div className="phone app-enter">
      <TopBar
        intimacy={intimacy}
        beijingTime={beijingTime}
        unread={mailUnread}
        onMailbox={() => setShowMailbox(true)}
      />

      <div className="stage" data-scene={scene}>
        <SceneSets />
        <SceneToggle scene={scene} onScene={setScene} />
        <Meters energy={energy} fullness={fullness} mood={mood} />
        <FeedDock busy={busy} onFeed={onFeed} cooldown={cooldown} />
        <CatStage
          action={action}
          bubble={bubble}
          stamp={stamp}
          onPet={onPet}
          scale={t.catScale}
        />
      </div>

      <ChatLog messages={messages} />

      <Composer
        busy={busy}
        onSend={send}
        onPhoto={onPhoto}
        categoryId={categoryId}
        onCategory={setCategoryId}
      />

      {surprise ? (
        <SurpriseModal cards={surprise} onClaim={claimSurprise} />
      ) : null}
      {showMailbox ? (
        <Mailbox items={mailbox} onClose={() => setShowMailbox(false)} />
      ) : null}

      <TweaksPanel title="Tweaks">
        <TweakSection label="外观" />
        <TweakRadio
          label="牛皮纸"
          value={t.paperTone}
          options={["浅", "适中", "深"]}
          onChange={(v) => setTweak("paperTone", v)}
        />
        <TweakSlider
          label="小猫大小"
          value={t.catScale}
          min={0.8}
          max={1.25}
          step={0.05}
          onChange={(v) => setTweak("catScale", v)}
        />
        <TweakSection label="陪伴节奏" />
        <TweakRadio
          label="碎碎念"
          value={t.chatterFreq}
          options={["安静", "适中", "话痨"]}
          onChange={(v) => setTweak("chatterFreq", v)}
        />
        <TweakRadio
          label="状态衰减"
          value={t.decaySpeed}
          options={["关闭", "正常", "演示快"]}
          onChange={(v) => setTweak("decaySpeed", v)}
        />
        <TweakSection label="演示" />
        <TweakButton
          label="让小猫出门探索一次"
          onClick={() => checkExploration(true)}
        />
        <TweakButton label="模拟离开回来(惊喜)" onClick={simulateAway} />
        <TweakButton label="重置亲密度 & 记忆" onClick={resetAll} />
      </TweaksPanel>
    </div>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
