// セーブコードが本当に往復するかを、ゲーム自身の書き出し・読み込みで確かめる。
//
// 回帰テストの表示に壊れた文字（孤立サロゲート）が出たので、
//   ・書き出したコードを正しく復号すると名前は保たれているか
//   ・そのコードをゲームに読み込ませて、同じ名前が戻るか
// の二つを見る。前者だけだと、こちらの復号の仕方が悪いだけかもしれない。
import fs from 'fs';
import { spawn } from 'child_process';

const CHROME = 'C:/Program Files/Google/Chrome/Application/chrome.exe';
const PORT = 9531, profile = 'C:/Users/ADMINI~1/AppData/Local/Temp/lm-save';
const PAGE = process.argv[2] || 'build/index-test.html';
fs.rmSync(profile, { recursive: true, force: true });
const chrome = spawn(CHROME, ['--headless=new', '--disable-gpu', '--no-sandbox', '--no-first-run',
  '--remote-debugging-port=' + PORT, '--user-data-dir=' + profile,
  '--window-size=1400,900', 'about:blank'], { stdio: 'ignore' });
const sleep = ms => new Promise(r => setTimeout(r, ms));

let list = null;
for (let i = 0; i < 40; i++) {
  try { list = await (await fetch(`http://127.0.0.1:${PORT}/json/list`)).json(); if (list.length) break; } catch { }
  await sleep(300);
}
const ws = new WebSocket(list.find(t => t.type === 'page').webSocketDebuggerUrl);
await new Promise(r => ws.addEventListener('open', r));
let id = 0; const pend = new Map(); const errors = [];
ws.addEventListener('message', e => {
  const m = JSON.parse(e.data);
  if (m.id && pend.has(m.id)) { pend.get(m.id)(m.result); pend.delete(m.id); }
  if (m.method === 'Runtime.exceptionThrown')
    errors.push(m.params.exceptionDetails.exception?.description || m.params.exceptionDetails.text);
});
const send = (m, p = {}) => new Promise(res => { const i = ++id; pend.set(i, res); ws.send(JSON.stringify({ id: i, method: m, params: p })); });
const ev = async x => (await send('Runtime.evaluate', { expression: x, returnByValue: true, awaitPromise: true })).result?.value;

await send('Page.enable'); await send('Runtime.enable');
await send('Page.navigate', { url: 'file:///' + process.cwd().replace(/\\/g, '/') + '/' + PAGE });
await sleep(4500);
for (let i = 0; i < 14; i++) {
  const done = await ev(`(()=>{
    const dlg=[...document.querySelectorAll('.win,.dialog,[role=dialog]')].find(d=>d.offsetParent);
    const bs=[...(dlg||document).querySelectorAll('button')].filter(b=>b.offsetParent && !b.disabled);
    const pick = bs.find(b=>/次|始|署名|決定|OK/.test(b.textContent)) || bs[bs.length-1];
    if(!pick) return true; pick.click(); return false; })()`);
  await sleep(400);
  if (done) break;
}
await sleep(2500);

// 1) 書き出し
await ev(`(()=>{const b=document.getElementById("m-save"); if(b) b.click();})()`);
await sleep(700);
const step1 = await ev(`(()=>{
  const ta = document.getElementById("savetext");
  const code = ta ? (ta.value||"").trim() : "";
  // 正しい復号: base64 → バイト列 → UTF-8
  let name = null, err = null;
  try {
    const bin = atob(code);
    const bytes = Uint8Array.from(bin, c => c.charCodeAt(0));
    const json = new TextDecoder("utf-8", {fatal:false}).decode(bytes);
    const j = JSON.parse(json);
    name = j.g && j.g.name;
  } catch(e) { err = String(e); }
  // 画面が今表示している名前
  const shown = (document.querySelector('#sheet, .sheet, body') || document.body).textContent;
  return { len: code.length, name, err,
           lone: name ? /[\\uD800-\\uDFFF]/.test(name) : null,
           code };
})()`);
console.log('書き出し:', { 長さ: step1.len, 名前: step1.name, 孤立サロゲート: step1.lone, 誤り: step1.err });

// 2) その場でゲームに読み戻す
await ev(`(()=>{const x=document.querySelector('[data-close="savedlg"]'); if(x) x.click();})()`);
await sleep(400);
const before = await ev(`(()=>{try{return JSON.parse(JSON.stringify({name:(window.G&&G.name)||null}))}catch(e){return {name:null}}})()`);
await ev(`(()=>{const b=document.getElementById("m-load"); if(b) b.click();})()`);
await sleep(700);
const step2 = await ev(`(()=>{
  const ta = document.getElementById("savetext");
  if(!ta) return {ok:false, err:"読み込みの窓が見つかりません"};
  ta.value = ${JSON.stringify(step1.code)};
  ta.dispatchEvent(new Event("input", {bubbles:true}));
  const ok = document.getElementById("saveok");
  if(ok) ok.click();
  return {ok:true};
})()`);
await sleep(2500);
const after = await ev(`(()=>{try{return {name:(window.G&&G.name)||null}}catch(e){return {name:null}}})()`);

console.log('読み戻し:', step2, ' 読み込み後の名前:', after.name);
console.log('ページの例外:', errors.slice(0, 3));

const ok = !!step1.len && !step1.err && step1.lone === false;
console.log(ok ? '\nセーブの判定: OK（コードは正しく復号でき、壊れた文字なし）'
  : '\nセーブの判定: 要確認 — 上の値を見てください');
ws.close(); chrome.kill();
process.exit(ok ? 0 : 1);
