import Phaser from 'phaser';
import { NPCS, TYPO } from '../data/content';

// Corruption stage params (§6.4 唯一真源)
export interface StageParams {
  stage: number;
  saturation: number; // multiplier applied to ColorMatrix
  vignette: number;   // 0..1 strength
  playbackRate: number;
  lowpass: number;    // Hz, 0 = off (open)
  birdMute: boolean;
  negativePerMin: number; // random single-frame negative
  offFlashPerMin: number; // NPC off-expression flashes
}

const MATRIX: StageParams[] = [
  { stage: 0, saturation: 0.90, vignette: 0.0, playbackRate: 1.00, lowpass: 0, birdMute: false, negativePerMin: 0, offFlashPerMin: 0 },
  { stage: 1, saturation: 1.00, vignette: 0.0, playbackRate: 1.00, lowpass: 0, birdMute: false, negativePerMin: 0, offFlashPerMin: 0 },
  { stage: 2, saturation: 1.15, vignette: 0.0, playbackRate: 0.97, lowpass: 0, birdMute: false, negativePerMin: 0, offFlashPerMin: 0 },
  { stage: 3, saturation: 1.30, vignette: 0.30, playbackRate: 0.92, lowpass: 8000, birdMute: false, negativePerMin: 0, offFlashPerMin: 0 },
  { stage: 4, saturation: 1.40, vignette: 0.55, playbackRate: 0.84, lowpass: 4000, birdMute: true, negativePerMin: 0.4, offFlashPerMin: 0.6 },
];

export function stageFromHappiness(h: number): number {
  if (h <= 20) return 0;
  if (h <= 50) return 1;
  if (h <= 80) return 2;
  if (h <= 99) return 3;
  return 4;
}

/** CorruptionDirector — single source of truth for all state + AV params (§12). */
class DirectorClass extends Phaser.Events.EventEmitter {
  playerName = '小满';
  day = 1;
  happiness = 30;
  awareness = 0;
  clues = new Set<string>();
  tasks: Record<string, 'idle' | 'active' | 'done' | 'refused'> = {};
  flags: Record<string, any> = {};
  escapeCount = 0;
  overflowPool = 0;
  startTime = 0;
  // daily
  greetedToday = new Set<string>();
  talkCounts: Record<string, number> = {};
  smallInteract: Record<string, number> = {}; // water/feed/pat -> count (max 3)
  gazedToday = new Set<string>(); // well/fog
  refusedTasks = new Set<string>();
  lowHappySince = 0; // timestamp when happiness dropped below 30 (0 = not low)
  endingShown = false;

  reset(name: string) {
    this.playerName = name && name.trim() ? name.trim() : '小满';
    this.day = 1;
    this.happiness = 30;
    this.awareness = 0;
    this.clues = new Set();
    this.tasks = {};
    this.flags = {};
    this.escapeCount = 0;
    this.overflowPool = 0;
    this.startTime = performance.now();
    this.greetedToday = new Set();
    this.talkCounts = {};
    this.smallInteract = {};
    this.gazedToday = new Set();
    this.refusedTasks = new Set();
    this.lowHappySince = 0;
    this.endingShown = false;
    this.emit('change');
    this.emit('stage', this.stage);
  }

  resetDaily() {
    this.greetedToday = new Set();
    this.talkCounts = {};
    this.smallInteract = {};
    this.gazedToday = new Set();
  }

  get stage(): number { return stageFromHappiness(this.happiness); }
  get params(): StageParams { return MATRIX[this.stage]; }

  /** petals lit 0..12 (§6.1 向日葵表盘) */
  get petals(): number { return Phaser.Math.Clamp(Math.round((this.happiness / 100) * 12), 0, 12); }
  get blackSeeds(): boolean { return this.happiness >= 100; }

  private _clamp() {
    this.happiness = Phaser.Math.Clamp(this.happiness, 0, 100);
    this.awareness = Phaser.Math.Clamp(this.awareness, 0, 100);
  }

  addHappiness(n: number) {
    const prevStage = this.stage;
    if (this.happiness >= 100 && n < 0) {
      // 满格强制维持：小额扣分累积进溢出池 (§6.1)
      this.overflowPool += -n;
      if (this.overflowPool >= 15) { this.overflowPool -= 15; this.emit('negativeFlash'); }
    } else {
      this.happiness += n;
      this._clamp();
    }
    this._afterHappyChange(prevStage);
  }

  setHappiness(v: number) {
    const prevStage = this.stage;
    this.happiness = Phaser.Math.Clamp(v, 0, 100);
    this._afterHappyChange(prevStage);
  }

  private _afterHappyChange(prevStage: number) {
    this._clamp();
    if (this.happiness < 30) { if (!this.lowHappySince) this.lowHappySince = performance.now(); }
    else this.lowHappySince = 0;
    this.emit('change');
    if (this.stage !== prevStage) this.emit('stage', this.stage);
  }

  addAwareness(n: number) { this.awareness += n; this._clamp(); this.emit('change'); }
  setAwareness(v: number) { this.awareness = Phaser.Math.Clamp(v, 0, 100); this.emit('change'); }

  discoverClue(id: string): boolean {
    if (this.clues.has(id)) return false;
    this.clues.add(id);
    this.addAwareness(8); // §8 每处 +8
    this.emit('clue', id);
    return true;
  }

  /** sad behavior (§6.3): 幸福 −5, 觉知 +3, 最近 NPC 转头 */
  sadBehavior(reason: string) {
    this.addHappiness(-5);
    this.addAwareness(3);
    this.emit('sad', reason);
  }

  /** NPC effective stage with individual offset (§6.4) */
  npcStage(npcId: string): number {
    const npc = NPCS[npcId];
    if (!npc || npc.participates === false) return 0;
    const eff = Phaser.Math.Clamp(this.happiness - npc.offset, 0, 100);
    return stageFromHappiness(eff);
  }

  /** expression for an NPC given current state (§6.4). off flashes handled by caller. */
  npcExpression(npcId: string): 'smile' | 'flat' | 'off' {
    const s = this.npcStage(npcId);
    if (s <= 2) return 'smile';
    return 'flat';
  }

  playtimeString(): string {
    const ms = performance.now() - this.startTime;
    const total = Math.floor(ms / 1000);
    const mm = Math.floor(total / 60).toString().padStart(2, '0');
    const ss = (total % 60).toString().padStart(2, '0');
    return `${mm}:${ss}`;
  }

  // ---- text corruption (§6.5) applied at render time ----
  private lineCounter = 0;
  corrupt(text: string, speaker: string): string {
    this.lineCounter++;
    const s = this.stage;
    const isMimi = speaker === '咪咪';
    let out = text;
    if (s >= 3) {
      // homophone typo. Mimi always; others at density (~1 per 50 lines) unless S4.
      const applyTypo = isMimi || s >= 4 || (this.lineCounter % 50 === 0);
      if (applyTypo) {
        const limit = s >= 4 ? TYPO.length : (isMimi ? TYPO.length : 1);
        let done = 0;
        for (const [from, to] of TYPO) {
          if (done >= limit) break;
          if (out.includes(from)) { out = out.split(from).join(to); done++; }
        }
      }
    }
    if (s >= 2) {
      // 偶发标点重复 (S2 每80句1次); S4 咪咪句尾标点 x3
      if (s >= 4 && isMimi) {
        out = out.replace(/([。！？，])\s*$/g, (m, p) => p + p + p);
        out = out.replace(/([。！？])/g, (m, p) => p + p + p);
      } else if (this.lineCounter % 80 === 0) {
        out = out.replace(/([。！？])/, (m, p) => p + p);
      }
    }
    return out;
  }
}

export const Director = new DirectorClass();
export type { DirectorClass };
