#!/usr/bin/env node
// redline-scan.mjs — 铁律 13 红线词扫描（CI 红线）。
// 扫描 UI 源面(app/ components/ lib/)中的红线词；命中即非零退出。
// 说明：「VPN」仅可出现在技术文档/代码注释,绝不进 UI 文案;域名占位 pangolin.vpn 为小写
// `.vpn`,与红线词大写 `VPN` 不冲突,故按大小写敏感匹配大写 VPN 以避开域名误报。
import { readdir, readFile } from 'node:fs/promises';
import { join, extname } from 'node:path';

const ROOTS = ['app', 'components', 'lib'];
const EXT = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs', '.css']);

// 红线词（doc CLAUDE §1 铁律 13）。中文短语 + 大写英文术语。
const REDLINES = [
  { re: /VPN/g, label: 'VPN' },
  { re: /翻墙/g, label: '翻墙' },
  { re: /科学上网/g, label: '科学上网' },
  { re: /突破封锁/g, label: '突破封锁' },
  { re: /自由穿越/g, label: '自由穿越' },
  { re: /梯子/g, label: '梯子' },
  { re: /机场/g, label: '机场' },
  { re: /Go anywhere/gi, label: 'Go anywhere' },
];

async function walk(dir) {
  let out = [];
  let entries;
  try {
    entries = await readdir(dir, { withFileTypes: true });
  } catch {
    return out;
  }
  for (const e of entries) {
    if (e.name === 'node_modules' || e.name === 'out' || e.name === '.next') continue;
    const p = join(dir, e.name);
    if (e.isDirectory()) out = out.concat(await walk(p));
    else if (EXT.has(extname(e.name))) out.push(p);
  }
  return out;
}

async function main() {
  let files = [];
  for (const r of ROOTS) files = files.concat(await walk(r));
  const hits = [];
  for (const f of files) {
    const text = await readFile(f, 'utf8');
    const lines = text.split('\n');
    lines.forEach((line, i) => {
      for (const rl of REDLINES) {
        rl.re.lastIndex = 0;
        if (rl.re.test(line)) hits.push({ f, n: i + 1, word: rl.label, line: line.trim().slice(0, 100) });
      }
    });
  }
  if (hits.length) {
    console.error(`✗ 红线词扫描命中 ${hits.length} 处：`);
    for (const h of hits) console.error(`  ${h.f}:${h.n}  [${h.word}]  ${h.line}`);
    process.exit(1);
  }
  console.log(`✓ 红线词扫描通过（扫描 ${files.length} 个文件，零命中）`);
}

main();
