// PRD §6.5 文本腐蚀：同音错字对照表（全 10 组，逐字实现）。
// 腐蚀只在对话渲染层实施（原文案保持干净），优先命中咪咪的台词。

import type { Stage } from '../types';

/** 同音错字对照表：幸福→辛福 / 开心→开欣 / 美好→美妤 / 大家→大伽 / 微笑→微效 /
 *  明天→名天 / 甜→恬 / 阳光→央光 / 朋友→棚友 / 永远→泳远 */
export const HOMOPHONES: [string, string][] = [
  ['幸福', '辛福'],
  ['开心', '开欣'],
  ['美好', '美妤'],
  ['大家', '大伽'],
  ['微笑', '微效'],
  ['明天', '名天'],
  ['甜', '恬'],
  ['阳光', '央光'],
  ['朋友', '棚友'],
  ['永远', '泳远'],
];

const END_PUNCT = ['！', '？', '。', '～'];

/** 句尾标点重复一次（S2 偶发） */
function repeatPunct(text: string): string {
  for (const p of END_PUNCT) {
    if (text.endsWith(p)) return text + p;
  }
  return text + '。';
}

/** 把命中的一组同音词替换一次；返回是否命中 */
function applyOneHomophone(text: string, used: Set<number>): { out: string; hit: boolean } {
  const order = HOMOPHONES.map((_, i) => i);
  // 随机顺序尝试，避免总是替换第一组
  for (let i = order.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [order[i], order[j]] = [order[j], order[i]];
  }
  for (const idx of order) {
    if (used.has(idx)) continue;
    const [from, to] = HOMOPHONES[idx];
    if (text.includes(from)) {
      used.add(idx);
      return { out: text.replace(from, to), hit: true };
    }
  }
  return { out: text, hit: false };
}

/**
 * 渲染层腐蚀。按 §6.4 矩阵密度：
 * - S2：偶发标点重复（每 80 句 1 次）
 * - S3：每 50 句 1 个同音错字
 * - S4：错字加密（每 10 句 1 次，最多 2 处）+ 咪咪句尾标点 ×3
 * 咪咪台词概率 ×3（S4 必中），即「优先命中」。
 */
export function corruptText(text: string, speakerId: string | undefined, stage: Stage): string {
  if (stage < 2 || !text) return text;
  const mimi = speakerId === 'mimi';
  const roll = Math.random();
  let out = text;

  if (stage === 2) {
    if (roll < (mimi ? 3 / 80 : 1 / 80)) out = repeatPunct(out);
    return out;
  }

  if (stage === 3) {
    if (roll < (mimi ? 3 / 50 : 1 / 50)) {
      out = applyOneHomophone(out, new Set()).out;
    }
    return out;
  }

  // stage === 4
  const p = mimi ? 1 : 1 / 10;
  if (roll < p) {
    const used = new Set<number>();
    const r1 = applyOneHomophone(out, used);
    out = r1.out;
    if (r1.hit && Math.random() < 0.6) {
      out = applyOneHomophone(out, used).out; // 加密：可能第二处
    }
  }
  if (mimi) {
    for (const pc of END_PUNCT) {
      if (out.endsWith(pc)) {
        out = out + pc + pc; // 句尾标点 ×3（原有 1 个 + 补 2 个）
        break;
      }
    }
  }
  return out;
}
