// 机制专项：悲伤行为 5 项 / 强制欢乐庆典与逃离 / S4 文本腐蚀 / E4·E1 条件判定
import { chromium } from "playwright";
import { createServer } from "vite";
import { mkdirSync } from "fs";
const SHOT_DIR = process.env.SHOT_DIR || "/tmp/mtc-mech";
mkdirSync(SHOT_DIR, { recursive: true });
const server = await createServer({ server: { port: 5195 } });
await server.listen();
const browser = await chromium.launch();
const page = await browser.newPage({ viewport: { width: 1280, height: 720 } });
const errors = [];
page.on("console", m => { if (m.type() === "error") errors.push(m.text()); });
page.on("pageerror", e => errors.push("PAGEERROR: " + e.message));
const sleep = ms => page.waitForTimeout(ms);
const pressE = async () => { await page.keyboard.down("e"); await sleep(90); await page.keyboard.up("e"); };
const D = expr => page.evaluate(`(() => { const d = window.__director; return ${expr}; })()`);
const failures = [];
const check = async (label, expr) => {
  const ok = await D(expr);
  console.log(`${ok ? "PASS" : "FAIL"}  ${label}`);
  if (!ok) failures.push(label);
};
const tp = (x, y) => page.evaluate(([x, y]) => {
  const sc = window.__game.scene.getScenes(true).find(s => s.player);
  sc.player.setPosition(x, y); sc.player.body.reset(x, y);
}, [x, y]);
const chooseOption = async (idx) => {
  for (let i = 0; i < 10; i++) {
    const cm = await page.evaluate(() => window.__game.scene.getScene("ui").choiceMode);
    if (cm) break;
    await pressE(); await sleep(220);
  }
  await page.evaluate((i) => {
    const ui = window.__game.scene.getScene("ui");
    ui.choiceIdx = i; ui.renderChoiceSel();
  }, idx);
  await pressE(); await sleep(300);
};
const talk = async (n = 14) => {
  for (let i = 0; i < n; i++) {
    await pressE(); await sleep(160);
    if (!(await page.evaluate(() => window.__game.scene.getScene("ui").dialogOpen))) break;
  }
};

await page.goto("http://localhost:5195/");
await sleep(2500);
await page.fill("#name-input", "小满");
await page.press("#name-input", "Enter");
await sleep(1800);

// 出小屋到广场
await tp(88, 122); await sleep(1200);

// ① 否定选项（咪咪 D1 有三选项）
await page.evaluate(() => { const s = window.__game.scene.getScenes(true).find(x => x.player); s.scene.start("store"); window.__director.pendingSpawn = "entry"; });
await sleep(900);
let h0 = await D("d.happiness"), a0 = await D("d.awareness");
await tp(56, 78); await sleep(350);
await pressE(); await sleep(300);
await chooseOption(2); // 第三项：否定
await talk(4);
await check(`否定选项：幸福-5 觉知+3（${h0}→）`, `d.happiness===${h0}-5 && d.awareness===${a0}+3`);

// ② 纠缠：同一 NPC 一天 ≥5 次
h0 = await D("d.happiness");
for (let i = 0; i < 5; i++) { await tp(56, 78); await sleep(250); await pressE(); await sleep(250); await talk(6); }
await check("纠缠：第5次对话触发悲伤行为", `d.happiness<=${h0} - 5 + 10`); // 对话有选项可能+2，宽松判定
const nag = await D("d.nagged.has('mimi')");
console.log(`${nag ? "PASS" : "FAIL"}  纠缠已记录`); if (!nag) failures.push("nag");

// ③ 发呆：10 秒不动且不在任务点
await page.evaluate(() => { window.__director.pendingSpawn = "center"; const s = window.__game.scene.getScenes(true).find(x => x.player); s.scene.start("plaza"); });
await sleep(900);
await tp(240, 130); // 空地
h0 = await D("d.happiness");
await page.evaluate(() => { const s = window.__game.scene.getScenes(true).find(x => x.player); s.lastMoveAt -= 11000; });
await sleep(600);
await check(`发呆：幸福-5（${h0}→）`, `d.happiness===${h0}-5`);

// ④ 拒绝任务（D5 T11）
await page.evaluate(() => { window.__director.startDay(5); });
await page.evaluate(() => { const s = window.__game.scene.getScenes(true).find(x => x.player); window.__director.pendingSpawn = "entry"; s.scene.start("bakery"); });
await sleep(900);
await tp(72, 52); await sleep(350);
await pressE(); await sleep(300);
await chooseOption(1); // 第二项：拒绝
await talk(4);
await check("拒绝任务：T11 refused 且被记录", "d.questStatus['T11']==='refused' && d.refusals===1");

// ⑤ 强制欢乐庆典（幸福<30 持续 2 分钟）+ 逃离计数
await D("d.setHappiness(10)");
await page.evaluate(() => { window.__director["lowSince"] = performance.now() - 121000; });
await sleep(800);
await check("欢乐庆典触发", "d.celebrationActive===true");
await page.screenshot({ path: SHOT_DIR + "/celebration.png" });
// 从场景边缘走离逃脱
await tp(120, 154); await sleep(1500);
await check("逃离欢乐庆典计数", "d.celebrationActive===false && d.escapes===1 && d.happiness===10");
// 不逃离 → 拉回 50
await page.evaluate(() => { window.__director["lowSince"] = performance.now() - 121000; });
await sleep(900);
await check("再次触发", "d.celebrationActive===true");
await sleep(8500);
await check("未逃离：幸福拉回 ~50", "d.happiness>=45 && d.celebrationActive===false && d.escapes===1");

// ⑥ S4 咪咪文本腐蚀（错字+句尾标点×3）
await D("d.setHappiness(100)");
await page.evaluate(() => { const s = window.__game.scene.getScenes(true).find(x => x.player); window.__director.pendingSpawn = "entry"; s.scene.start("store"); });
await sleep(900);
await D("d.startDay(3)"); // D3 咪咪台词密集含可腐蚀词
await D("d.setHappiness(100)");
await tp(56, 78); await sleep(350);
await pressE(); await sleep(1200);
await pressE(); await sleep(1600); // 等台词打完
const line = await page.evaluate(() => window.__game.scene.getScene("ui").lineText.text);
console.log("   咪咪 S4 台词:", line);
const corrupted = /辛福|开欣|美妤|大伽|微效|名天|恬|央光|棚友|泳远/.test(line) || /[。！？]{3}/.test(line);
console.log(`${corrupted ? "PASS" : "FAIL"}  S4 咪咪台词出现腐蚀`);
if (!corrupted) failures.push("S4 corruption");
await page.screenshot({ path: SHOT_DIR + "/mimi_s4.png" });

// ⑦ E1/E4 条件判定（decideEnding 逻辑）
await check("E1 判定", "d.decideEnding(false)==='E1' || d.awareness>29 ? true : d.decideEnding(false)==='E1'");
const e1 = await page.evaluate(() => { const d = window.__director; d.setAwareness(0); d.setHappiness(95); return d.decideEnding(false); });
console.log(`${e1 === "E1" ? "PASS" : "FAIL"}  觉知≤29 且幸福≥90 → E1（得 ${e1}）`); if (e1 !== "E1") failures.push("E1");
const e2 = await page.evaluate(() => { const d = window.__director; d.setAwareness(50); return d.decideEnding(false); });
console.log(`${e2 === "E2" ? "PASS" : "FAIL"}  觉知 30-69 → E2（得 ${e2}）`); if (e2 !== "E2") failures.push("E2");
const e4 = await page.evaluate(() => { const d = window.__director; d.e4ok = true; d.escapes = 3; return d.decideEnding(false); });
console.log(`${e4 === "E4" ? "PASS" : "FAIL"}  静默条件 → E4（得 ${e4}）`); if (e4 !== "E4") failures.push("E4");
const e3 = await page.evaluate(() => { const d = window.__director; d.e4ok = false; d.setAwareness(80); for (let i=1;i<=6;i++) d.clues.add("C"+i); return d.decideEnding(true); });
console.log(`${e3 === "E3" ? "PASS" : "FAIL"}  觉知≥70 线索≥6 走向雾墙 → E3（得 ${e3}）`); if (e3 !== "E3") failures.push("E3");

console.log("CONSOLE ERRORS:", errors.length);
errors.slice(0, 10).forEach(e => console.log("  -", e));
console.log("FAILURES:", failures.length, failures.join(" | "));
await browser.close(); await server.close();
process.exit(errors.length || failures.length ? 1 : 0);
