import fs from 'node:fs';
import path from 'node:path';

/**
 * 用词一致性守卫 —— 主管在**同一张确认单**上不该看到两个词指同一件事。
 *
 * 🔴 2026-08-17 审计发现：确认单段落里写「N 人无主」，而同一张卡片上的按钮写
 *   「换无专属客服的患者补上」。产品早就定过统一用「无专属」，`assignment-facts`
 *   （喂模型那份）也带着守卫注释，唯独界面文案没跟上。
 *
 * ⚠️ 这类漂移不会报错、测试也不会红 —— 只有主管一眼看出"这俩是不是两回事"。
 * ⇒ 用规则钉死：**会到达人或模型的字**里不许再出现废弃词。
 */

const ROOTS = ['src', '../pac-web/src', '../../packages/types/src'];
/** 废弃词 → 该用的词 */
const RETIRED: Array<[RegExp, string]> = [
  [/无主/, '无专属'],
  [/时效/, '时限'],
  /**
   * 🔴 「打不完」是**下判断**，而那条引导的卡片刻意只报数
   *   （「最忙的是王强：手上共 45 条，约 3 天的量」）。
   * ⚠️ 它曾经是 `GUIDANCE_KIND.daily_overload` 的值 —— 而那个值**会出现在模型嘴里**，
   *   等于让助手替主管说出卡片刻意不说的那句话；界面上一处「打不完」也没有。
   * ⛔ 同类禁词：几乎每批都会有人超，说成故障主管就会开始怀疑系统。
   */
  [/打不完/, '最忙的那位（只报数，⛔ 不下判断）'],
];
/** ⛔ 语义不同的例外（临床禁忌有效期 / 话术语气紧迫），⛔ 别连坐 */
const EXEMPT = /clinical-signals|persona-feature-specs|signal-extractor|contraindication|shared\/tone/;

function walk(dir: string, out: string[] = []): string[] {
  if (!fs.existsSync(dir)) return out;
  for (const e of fs.readdirSync(dir, { withFileTypes: true })) {
    const p = path.join(dir, e.name);
    if (e.isDirectory()) {
      if (!/node_modules|dist|\.next/.test(p)) walk(p, out);
    } else if (/\.tsx?$/.test(e.name)) out.push(p);
  }
  return out;
}

/**
 * 只看**会到达人或模型**的行 —— 注释里的沿革记录保留（那是历史，改了反而失真）。
 *
 * ⚠️ 必须**跟踪块注释状态**，⛔ 不能只看行首：`/* … *\/` 和 JSX 的 `{/* … *\/}`
 *   中间那些行不以 `*` 开头，只判行首会把历史记录当成活文案报出来（第一版就是这么假红的）。
 */
function liveLines(file: string): Array<{ n: number; text: string }> {
  const out: Array<{ n: number; text: string }> = [];
  let inBlock = false;
  fs.readFileSync(file, 'utf8')
    .split('\n')
    .forEach((text, i) => {
      const t = text.trim();
      const opens = (t.match(/\/\*/g) ?? []).length;
      const closes = (t.match(/\*\//g) ?? []).length;
      const wasInBlock = inBlock;
      if (opens > closes) inBlock = true;
      else if (closes > opens) inBlock = false;
      if (wasInBlock || inBlock || t.startsWith('*') || t.startsWith('//') || opens > 0) return;
      out.push({ n: i + 1, text });
    });
  return out;
}

describe('用词一致性 —— 废弃词不许出现在会被看见的字里', () => {
  const files = ROOTS.flatMap((r) => walk(path.join(__dirname, '..', r))).filter(
    (f) => !EXEMPT.test(f) && !f.includes('wording-consistency.spec'),
  );

  it('扫到了文件（否则这条测试是假绿）', () => {
    expect(files.length).toBeGreaterThan(200);
  });

  for (const [bad, good] of RETIRED) {
    it(`⛔ 不再出现「${bad.source}」—— 产品定的是「${good}」`, () => {
      const hits: string[] = [];
      for (const f of files) {
        for (const { n, text } of liveLines(f)) {
          if (bad.test(text)) hits.push(`${path.basename(f)}:${n}  ${text.trim().slice(0, 80)}`);
        }
      }
      expect(hits).toEqual([]);
    });
  }
});
