/** §6.4 stage matrix + §6.5 homophone pairs */

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

export interface StageParams {
  stage: Stage;
  saturation: number;
  vignette: number;
  playbackRate: number;
  /** null = no lowpass */
  lowpassHz: number | null;
  birdMute: boolean;
  /** punctuation repeat every N sentences; null = never */
  punctRepeatEvery: number | null;
  /** homophone every N sentences; null = never */
  homophoneEvery: number | null;
  /** mimi sentence-end punct ×3 */
  mimiTriplePunct: boolean;
  /** denser typos in S4 */
  denseTypos: boolean;
  negativeFlashPerMin: number;
  offFacePerMin: number;
  showBlackSeeds: boolean;
}

export function stageFromHappiness(happiness: number): Stage {
  if (happiness >= 100) return 'S4';
  if (happiness >= 81) return 'S3';
  if (happiness >= 51) return 'S2';
  if (happiness >= 21) return 'S1';
  return 'S0';
}

export function stageParams(stage: Stage): StageParams {
  switch (stage) {
    case 'S0':
      return {
        stage,
        saturation: 0.9,
        vignette: 0,
        playbackRate: 1,
        lowpassHz: null,
        birdMute: false,
        punctRepeatEvery: null,
        homophoneEvery: null,
        mimiTriplePunct: false,
        denseTypos: false,
        negativeFlashPerMin: 0,
        offFacePerMin: 0,
        showBlackSeeds: false,
      };
    case 'S1':
      return {
        stage,
        saturation: 1.0,
        vignette: 0,
        playbackRate: 1,
        lowpassHz: null,
        birdMute: false,
        punctRepeatEvery: null,
        homophoneEvery: null,
        mimiTriplePunct: false,
        denseTypos: false,
        negativeFlashPerMin: 0,
        offFacePerMin: 0,
        showBlackSeeds: false,
      };
    case 'S2':
      return {
        stage,
        saturation: 1.15,
        vignette: 0,
        playbackRate: 0.97,
        lowpassHz: null,
        birdMute: false,
        punctRepeatEvery: 80,
        homophoneEvery: null,
        mimiTriplePunct: false,
        denseTypos: false,
        negativeFlashPerMin: 0,
        offFacePerMin: 0,
        showBlackSeeds: false,
      };
    case 'S3':
      return {
        stage,
        saturation: 1.3,
        vignette: 0.25,
        playbackRate: 0.92,
        lowpassHz: 8000,
        birdMute: false,
        punctRepeatEvery: 80,
        homophoneEvery: 50,
        mimiTriplePunct: false,
        denseTypos: false,
        negativeFlashPerMin: 0,
        offFacePerMin: 0,
        showBlackSeeds: false,
      };
    case 'S4':
      return {
        stage,
        saturation: 1.4,
        vignette: 0.45,
        playbackRate: 0.84,
        lowpassHz: 4000,
        birdMute: true,
        punctRepeatEvery: 40,
        homophoneEvery: 25,
        mimiTriplePunct: true,
        denseTypos: true,
        negativeFlashPerMin: 0.4,
        offFacePerMin: 0.6,
        showBlackSeeds: true,
      };
  }
}

/** 12 petals by happiness; black seeds only at 100 */
export function petalCount(happiness: number): number {
  const h = Math.max(0, Math.min(100, happiness));
  if (h <= 0) return 0;
  if (h >= 100) return 12;
  return Math.min(12, Math.floor((h * 12) / 100));
}

export function showBlackSeeds(happiness: number): boolean {
  return happiness >= 100;
}

/** §6.5 同音错字 — 10 pairs, source stays clean; apply at render */
export const HOMOPHONE_PAIRS: readonly [string, string][] = [
  ['幸福', '辛福'],
  ['开心', '开欣'],
  ['美好', '美妤'],
  ['大家', '大伽'],
  ['微笑', '微效'],
  ['明天', '名天'],
  ['甜', '恬'],
  ['阳光', '央光'],
  ['朋友', '棚友'],
  ['永远', '泳远'],
] as const;

/**
 * Apply text corruption for dialogue render.
 * Prefer 咪咪 (isMimi=true): higher chance / denser.
 */
export function corruptText(
  source: string,
  stage: Stage,
  options: { isMimi?: boolean; sentenceIndex?: number } = {},
): string {
  const params = stageParams(stage);
  let text = source;
  const isMimi = options.isMimi === true;
  const idx = options.sentenceIndex ?? 0;

  // Homophone replacements
  if (params.homophoneEvery != null) {
    const every = isMimi ? Math.max(1, Math.floor(params.homophoneEvery / 2)) : params.homophoneEvery;
    const should = params.denseTypos || isMimi || idx % every === every - 1;
    if (should) {
      // Prefer first matching pair; for dense/mimi apply more pairs
      const pairs = params.denseTypos || isMimi ? HOMOPHONE_PAIRS : HOMOPHONE_PAIRS.slice(0, 1);
      for (const [from, to] of pairs) {
        if (text.includes(from)) {
          text = text.replace(from, to);
          if (!params.denseTypos && !isMimi) break;
        }
      }
    }
  }

  // Punctuation repeat (S2+)
  if (params.punctRepeatEvery != null) {
    const every = isMimi ? Math.max(1, Math.floor(params.punctRepeatEvery / 2)) : params.punctRepeatEvery;
    if (idx % every === every - 1 || (isMimi && stage === 'S4')) {
      text = text.replace(/([！？。])/, '$1$1');
    }
  }

  // 咪咪句尾标点 ×3 at S4
  if (params.mimiTriplePunct && isMimi) {
    text = text.replace(/([！？。])\s*$/, '$1$1$1');
    if (!/[！？。]$/.test(text)) {
      text = text + '！！！';
    }
  }

  return text;
}

/** NPC face tier with individual threshold offset (§4 / §6.4)
 *  Thresholds in the matrix are shifted by offset:
 *  咪咪 −10 → flat/off earlier; 圆圆 +15 → later; immune never.
 */
export type FaceTier = 'smile' | 'flat' | 'off';

export function faceTierForNpc(
  happiness: number,
  offset: number | 'immune',
): FaceTier {
  if (offset === 'immune') return 'smile';
  const flatAt = 81 + offset;
  const offAt = 100 + offset;
  if (happiness >= offAt) return 'off';
  if (happiness >= flatAt) return 'flat';
  return 'smile';
}
