#!/usr/bin/env node
/** validate_html.js — 校验同步关键点 + 八股风险 */
const fs=require("fs");
const file=process.argv[2];
if(!file){console.error("用法: node validate_html.js <html>");process.exit(1);}
const h=fs.readFileSync(file,"utf8");

const hard=[
  ["有 voice 音频标签", /<audio[^>]*id="voice"/.test(h)],
  ["主时钟用 currentTime", /voice\.currentTime/.test(h)],
  ["无独立 setInterval/setTimeout 驱动画面", !/set(Interval|Timeout)\s*\(/.test(h)],
  ["无可见播放控件(录屏干净)", !/id="controls"/.test(h)],
  ["无校准/调试UI残留", !/calMode|calPanel/.test(h)],
  ["占位符已全部替换", !/\{\{[A-Z_]+\}\}/.test(h)],
  ["cues 已注入", /const cues\s*=\s*\[/.test(h)],
  ["subtitles 已注入", /const subtitles\s*=\s*\[/.test(h)],
];

let ok=true;
console.log("== 硬约束 ==");
hard.forEach(([l,p])=>{console.log(`${p?"✓":"✗"} ${l}`);if(!p)ok=false;});

// 软提醒：八股风险——tpl里出现长中文串(>=8字)直接焊死，说明文字没走cue字段
console.log("\n== 八股风险提醒(非阻断) ==");
const tplBlock=(h.match(/function tpl\(c\)\{[\s\S]*?\n\}/)||[""])[0];
const hardcoded=[...tplBlock.matchAll(/[\u4e00-\u9fa5]{8,}/g)].map(m=>m[0]);
if(hardcoded.length){
  console.log("⚠ tpl() 里出现疑似写死的长句，镜头可能绑死了内容（应改为读 c.t/c.s 字段）:");
  hardcoded.slice(0,5).forEach(s=>console.log("   ·",s));
}else{
  console.log("✓ tpl() 未发现写死的长中文句");
}
// scene 引用检查
const names=[...new Set([...h.matchAll(/"f":"(\w+)"/g)].map(m=>m[1]))];
names.forEach(n=>{const def=new RegExp(`c\\.f==="${n}"`).test(h);
  console.log(`${def?"✓":"✗"} 镜头 ${n} 在 tpl() 中已定义`);if(!def)ok=false;});

console.log(ok?"\n硬约束全部通过 ✅":"\n存在硬约束问题 ❌");
process.exit(ok?0:1);
