// TextCorruptor.ts: applies §6.5 text corruption at render time.
// Original text stays clean in data; corruption applied based on stage and NPC bias.
import { CORRUPTION_MAP, NPCS } from '../data/gamedata';

export type Stage = 'S0' | 'S1' | 'S2' | 'S3' | 'S4';

// Count NPC talk turns for periodic corruption (S2: 1/80, S3: 1/50)
let globalTurnCount = 0;
export function tickTurn() { globalTurnCount++; }
export function resetTurns() { globalTurnCount = 0; }

// Deterministic-ish RNG to decide whether to corrupt a given target word.
function chance(p: number): boolean { return Math.random() < p; }

/**
 * Corrupt a line of text.
 * @param text original clean text
 * @param stage current erosion stage
 * @param npcKey the speaking NPC key (mimi gets priority)
 * @param forceStrong if true (S4 / off), apply aggressive corruption
 */
export function corrupt(text: string, stage: Stage, npcKey?: string, forceStrong = false): string {
  let out = text;
  const npc = npcKey ? NPCS[npcKey] : undefined;
  const isMimi = npcKey === 'mimi';

  // Determine effective stage with NPC threshold shift applied to "when corruption starts".
  // mimi (-10): corruption starts ~1 stage earlier; yuanyuan (+15): ~1 stage later.
  let eff: Stage = stage;
  if (npc) {
    const shift = npc.thresholdShift;
    // shift<0 -> earlier; map by nudging stage index
    const order: Stage[] = ['S0','S1','S2','S3','S4'];
    let idx = order.indexOf(stage);
    // negative shift reduces the "needed" happiness -> treat as if one stage higher (more corrupt) for mimi
    if (shift < 0) idx = Math.min(4, idx + 1);
    else if (shift > 0) idx = Math.max(0, idx - 1);
    eff = order[idx];
  }
  // dr_fu & grandpa never corrupt (not participants)
  if (npcKey === 'dr_fu' || npcKey === 'grandpa') return out;

  // Apply per-stage rules
  // Build list of [target, replacement]
  const pairs: [string, string][] = Object.entries(CORRUPTION_MAP);

  // prioritize mimi: her words get corrupted first
  const ordered = isMimi ? pairs : (pairs.slice().sort(() => Math.random() - 0.5));

  if (eff === 'S0' || eff === 'S1') {
    // normal: no corruption
    return out;
  }

  if (eff === 'S2') {
    // occasional repeated punctuation (1 per 80 sentences)
    if (forceStrong || isMimi || globalTurnCount % 80 === 0) {
      // corrupt at most one word, low chance
      for (const [from, to] of ordered) {
        if (out.includes(from) && chance(isMimi ? 0.5 : 0.25)) { out = out.replace(from, to); break; }
      }
      // repeated punctuation
      if (chance(isMimi ? 0.5 : 0.2)) {
        out = out.replace(/([。！？，])/g, (m) => chance(0.3) ? m + m : m);
      }
    }
    return out;
  }

  if (eff === 'S3') {
    // homophone typos: 1 per 50 sentences, mimi higher
    const baseProb = isMimi ? 0.7 : (forceStrong ? 0.6 : 0.4);
    let didOne = false;
    for (const [from, to] of ordered) {
      if (out.includes(from) && (!didOne || isMimi) && chance(baseProb)) {
        out = out.replace(from, to); didOne = true;
      }
    }
    if (isMimi) {
      // mimi句尾标点 ×3 (S4-like, but begins appearing)
      out = out.replace(/([。！？])$/, (m) => m + m);
    }
    return out;
  }

  // eff === 'S4'
  {
    // 错字加密 + 咪咪句尾标点 ×3
    for (const [from, to] of ordered) {
      if (out.includes(from)) {
        out = out.split(from).join(to);
      }
    }
    if (isMimi) {
      out = out.replace(/([。！？])$/, (m) => m + m + m);
    }
    return out;
  }
}

// Glitch signature for C4 (the PT-0▓ garbled signature flash)
export const C4_GARBLE = 'PT-0▓';
