/**
 * 诊断码 → 8 类业务标签(初选矩阵 X 轴)的**声明式**映射。
 *
 * ═══ 为什么要有这张表 ═══════════════════════════════════════════════
 * 真理源本来是 `potential-treatment.feature.ts` 的 `classifyGapToLabel`(一个 switch)。
 * 初选矩阵改成 **reason 驱动**之后,同一套判定必须在 **SQL 里**再表达一次
 * (矩阵/确认单/列表三处都是原生 SQL,拿不到那个 TS 函数)。
 *
 * ⛔ 手在 SQL 里抄一遍 = 两份真理源。年龄闸一改、K03 关键词一加,两边就悄悄分叉,
 *   表现是「矩阵里有这个人、点进去列表没有」——T6a 明令要防的那件事,而且不报错。
 * ✅ 改成:**一张声明式表** → TS 侧和 SQL 侧都从它生成。
 *   `tests/potential-label-rules.spec.ts` 锁住本表与 `classifyGapToLabel` 在全量组合上等价。
 *
 * ═══ 顺序敏感,第一条命中即返回(与 switch 语义一致)═══════════════════
 * K03 必须「含关键词 → 拔牙」在前、「其余 → 修复」在后;调换顺序会让所有 K03 都归修复。
 */

/** K03「该拔不是该补」的诊断名关键词 —— ⛔ 单一定义,SQL 与 TS 同源 */
export const EXTRACTION_NAME_KEYWORDS: readonly string[] = ['残根', '残冠', '无法保留', '不能保留'];

export interface PotentialLabelRule {
  /** 诊断/建议的**组主码**(GAP_PRIMARY_GROUPS 的 key) */
  code: string;
  /** 命中后归入的业务标签 */
  label: string;
  /** 年龄**严格大于**(种植 K08 要求 >18) */
  ageGt?: number;
  /** 年龄闭区间(正畸 13-40 / 早矫 3-12) */
  ageMin?: number;
  ageMax?: number;
  /** 诊断名需含其一(K03 → 拔牙) */
  nameAny?: readonly string[];
}

/**
 * ⚠️ 年龄为 null(无生日)时:带年龄条件的规则**一律不命中** —— 与 `classifyGapToLabel`
 *   的 `age !== null && age > 18` / `if (age === null) return null` 一致。
 *   SQL 侧靠"与 NULL 比较得 NULL(非真)"天然获得同样语义,⛔ 别加 COALESCE 兜底。
 * ⚠️ K00 / K09 / EXTRACTION_RECOMMENDED **刻意不在表里** —— 它们不属于 8 类业务标签,
 *   矩阵没有这些行。落到 NULL 是正确结果,不是遗漏。
 */
export const POTENTIAL_LABEL_RULES: readonly PotentialLabelRule[] = [
  { code: 'K08', label: 'implant', ageGt: 18 },
  { code: 'K02', label: 'filling' },
  { code: 'K04', label: 'endo' },
  { code: 'K05', label: 'perio' },
  { code: 'K06', label: 'perio' },
  { code: 'K01', label: 'extraction' },
  { code: 'K03', label: 'extraction', nameAny: EXTRACTION_NAME_KEYWORDS },
  { code: 'K03', label: 'restoration' },
  { code: 'K07', label: 'early_ortho', ageMin: 3, ageMax: 12 },
  { code: 'K07', label: 'ortho', ageMin: 13, ageMax: 40 },
];

/**
 * TS 侧判定(与 SQL 侧同源)。
 * @param age 周岁;无生日传 null —— 带年龄条件的规则一律不命中
 */
export function classifyCodeToLabel(
  code: string | null | undefined,
  nameZh: string | null | undefined,
  age: number | null,
): string | null {
  if (!code) return null;
  const nm = nameZh ?? '';
  for (const r of POTENTIAL_LABEL_RULES) {
    if (r.code !== code) continue;
    if (r.ageGt !== undefined && !(age !== null && age > r.ageGt)) continue;
    if (r.ageMin !== undefined && !(age !== null && age >= r.ageMin)) continue;
    if (r.ageMax !== undefined && !(age !== null && age <= r.ageMax)) continue;
    if (r.nameAny && !r.nameAny.some((k) => nm.includes(k))) continue;
    return r.label;
  }
  return null;
}
