// CorruptionDirector：全部状态唯一真源（PRD §6 / CONTRACT §4）。
// 普通 TS 单例 + Phaser.Events.EventEmitter。
// 事件：happiness / awareness / stage / day / clue / task / celebration / ending（契约），
// 增补：negativeFlash / firstFull / npcGlance / dusk / prompt / dialogue / rain。

import Phaser from 'phaser';
import {
  STAGE_PARAMS, stageOf, type Stage, type StageParams,
  type SadnessKind, type SmallInteractionKind, type ChoiceKind, type EndingId,
  type DialoguePayload, type NpcId,
} from '../types';
import { TASKS, tasksOfDay } from '../data/tasks';
import { CLUE_AWARENESS } from '../data/clues';

const LOW_HAPPY_THRESHOLD = 30;
const LOW_HAPPY_MS = 2 * 60 * 1000; // <30 持续 2 分钟 → 欢乐庆典
const OVERFLOW_TRIGGER = 15;        // 溢出池 15 点 → 负片闪帧
const CELEBRATION_RESTORE = 50;
const DAY_LENGTH_MS = 5 * 60 * 1000; // 游戏内计时：5 分钟未到也会黄昏（7 天 ≤35 分钟，§14.16）

export class CorruptionDirector extends Phaser.Events.EventEmitter {
  private static _instance: CorruptionDirector | null = null;
  static get instance(): CorruptionDirector {
    if (!this._instance) this._instance = new CorruptionDirector();
    return this._instance;
  }

  // ---------- 状态 ----------
  playerName = '小满';
  startTime = Date.now();
  day = 1;
  happiness = 30;
  awareness = 0;
  readonly clues = new Set<string>();
  readonly tasksDone = new Set<string>();
  readonly refusedTasks = new Set<string>();
  escapedCelebrations = 0;
  /** 最近一次对话关闭的时间戳（ms）：MapScene 用它防止「关对话的同一次 E 按下」立刻重开交互 */
  dialogueClosedAt = 0;

  /** 任务进度计数（如 T1 已送几份面包），每日清空 */
  taskProgress: Record<string, number> = {};
  /** 集合型进度（如 T3 已问好 npc 列表），每日清空 */
  taskSetProgress: Record<string, Set<string>> = {};

  rainActive = false;
  celebrationActive = false;
  /** 对话/梦境/结局进行中：抑制发呆判定与移动 */
  dialogueActive = false;
  /** 场景切换过场中（床/门淡出→新场景）：抑制庆典触发，防止庆典对话被切场景覆盖后卡死 */
  sceneTransition = false;
  /** T14 进行中 */
  t14Active = false;

  /** E4 监视：D5-7 期间幸福是否曾 >10 */
  e4HappyOk = true;

  private overflowPool = 0;
  private lowHappyMs = 0;
  private negFlashTimer = 60_000;
  private firstFullDone = false;
  private dayElapsedMs = 0;

  /** 每日重置的计数 */
  private talkCounts: Record<string, number> = {};      // 纠缠判定：对同一 NPC 一天内对话次数
  private smallCounts: Record<string, number> = {};     // 小互动每类每日上限 3
  private stareDone: Record<string, boolean> = {};      // 注视古井/雾墙各每日 1 次

  /** 对话 Promise 解析器（UIScene 完成后回调） */
  private dialogueResolver: ((choice: number | null) => void) | null = null;

  private constructor() {
    super();
  }

  // ---------- 派生 ----------
  get stage(): Stage {
    return stageOf(this.happiness);
  }

  get params(): StageParams {
    return STAGE_PARAMS[this.stage];
  }

  /** 真实游玩时长（ms） */
  get playTimeMs(): number {
    return Date.now() - this.startTime;
  }

  get playTimeFormatted(): string {
    const totalSec = Math.floor(this.playTimeMs / 1000);
    const m = Math.floor(totalSec / 60);
    const s = totalSec % 60;
    return `${String(m).padStart(2, '0')}:${String(s).padStart(2, '0')}`;
  }

  /** 当天任务是否全部完成 */
  get dailyTasksAllDone(): boolean {
    return tasksOfDay(this.day).every((t) => this.tasksDone.has(t.id));
  }

  /** 是否可以睡觉（任务全部了结——完成或拒绝，或计时已到黄昏） */
  get canSleep(): boolean {
    if (this.day >= 7) return false;
    return this.dailySettled || this.dayElapsedMs >= DAY_LENGTH_MS;
  }

  // ---------- 新游戏 ----------
  newGame(name: string): void {
    this.playerName = name.trim() || '小满';
    this.startTime = Date.now();
    this.day = 1;
    this.happiness = 30;
    this.awareness = 0;
    this.clues.clear();
    this.tasksDone.clear();
    this.refusedTasks.clear();
    this.escapedCelebrations = 0;
    this.taskProgress = {};
    this.taskSetProgress = {};
    this.rainActive = false;
    this.celebrationActive = false;
    this.dialogueActive = false;
    this.sceneTransition = false;
    this.t14Active = false;
    this.e4HappyOk = true;
    this.overflowPool = 0;
    this.lowHappyMs = 0;
    this.firstFullDone = false;
    this.dayElapsedMs = 0;
    this.resetDaily();
    this.emit('day', this.day);
    this.emit('happiness', this.happiness);
    this.emit('awareness', this.awareness);
    this.emit('stage', this.stage, this.params);
  }

  private resetDaily(): void {
    this.talkCounts = {};
    this.smallCounts = {};
    this.stareDone = {};
    this.taskProgress = {};
    this.taskSetProgress = {};
  }

  // ---------- 幸福值 ----------
  addHappiness(delta: number): void {
    if (delta === 0) return;
    const prevStage = this.stage;

    if (delta < 0 && this.happiness >= 100) {
      // 满格态：小额扣分不再生效，累积进隐藏溢出池
      this.overflowPool += -delta;
      if (this.overflowPool >= OVERFLOW_TRIGGER) {
        this.overflowPool -= OVERFLOW_TRIGGER;
        this.emit('negativeFlash');
      }
      return;
    }

    this.happiness = Math.max(0, Math.min(100, this.happiness + delta));
    this.emit('happiness', this.happiness);

    if (this.day >= 5 && this.happiness > 10) this.e4HappyOk = false;

    if (this.happiness >= 100 && !this.firstFullDone) {
      this.firstFullDone = true;
      this.emit('firstFull');
    }
    if (this.stage !== prevStage) {
      this.emit('stage', this.stage, this.params);
    }
  }

  setHappiness(v: number): void {
    this.happiness = Math.max(0, Math.min(100, Math.round(v)));
    this.emit('happiness', this.happiness);
    if (this.day >= 5 && this.happiness > 10) this.e4HappyOk = false;
    this.emit('stage', this.stage, this.params); // 始终广播，保证 debug 立即生效
  }

  // ---------- 觉知值（只升不降） ----------
  addAwareness(delta: number): void {
    if (delta <= 0) return;
    this.awareness = Math.max(0, Math.min(100, this.awareness + delta));
    this.emit('awareness', this.awareness);
  }

  setAwareness(v: number): void {
    this.awareness = Math.max(0, Math.min(100, Math.round(v)));
    this.emit('awareness', this.awareness);
  }

  // ---------- 悲伤行为（§6.3：幸福 −5、觉知 +3、最近 NPC 转头看一次） ----------
  recordSadness(kind: SadnessKind): void {
    this.addHappiness(-5);
    this.addAwareness(3);
    this.emit('npcGlance', kind);
    this.emit('sadness', kind);
  }

  // ---------- 小互动（浇花/喂食/摸头 +1，每类每日上限 3 次） ----------
  smallInteraction(kind: SmallInteractionKind): boolean {
    const n = this.smallCounts[kind] ?? 0;
    if (n >= 3) return false;
    this.smallCounts[kind] = n + 1;
    this.addHappiness(1);
    return true;
  }

  // ---------- 对话选项 ----------
  applyChoice(kind: ChoiceKind): void {
    if (kind === 'gentle') {
      this.addHappiness(2);
    } else if (kind === 'hesitant') {
      this.addAwareness(2);
    } else if (kind === 'deny') {
      this.addAwareness(2);
      this.recordSadness('deny'); // 否定选项同时算悲伤行为
    }
    // accept/refuse 由任务逻辑处理
  }

  // ---------- NPC 对话登记（纠缠：同一 NPC 一天内 ≥5 次 → 悲伤行为） ----------
  registerTalk(npcId: NpcId): void {
    if (npcId === 'bird') return;
    const n = (this.talkCounts[npcId] ?? 0) + 1;
    this.talkCounts[npcId] = n;
    if (n >= 5) {
      this.recordSadness('cling');
    }
  }

  // ---------- 注视古井/雾墙（各每日 1 次，+3 觉知） ----------
  registerStare(which: 'well' | 'fog'): boolean {
    if (this.stareDone[which]) return false;
    this.stareDone[which] = true;
    this.addAwareness(3);
    return true;
  }

  // ---------- 线索（每处 +8） ----------
  addClue(id: string): boolean {
    if (this.clues.has(id)) return false;
    this.clues.add(id);
    this.addAwareness(CLUE_AWARENESS);
    this.emit('clue', id);
    return true;
  }

  giveAllClues(): void {
    ['C1', 'C2', 'C3', 'C4', 'C5', 'C6', 'C7', 'C8', 'C9'].forEach((id) => this.addClue(id));
  }

  // ---------- 任务 ----------
  completeTask(id: string): void {
    if (this.tasksDone.has(id)) return;
    this.tasksDone.add(id);
    const def = TASKS[id];
    if (def && def.reward > 0) this.addHappiness(def.reward);
    this.emit('task', id);
    if (this.dailyTasksAllDone && this.day < 7) {
      this.emit('dusk'); // 黄昏钟声
    }
  }

  refuseTask(id: string): void {
    if (this.tasksDone.has(id) || this.refusedTasks.has(id)) return;
    this.refusedTasks.add(id);
    this.recordSadness('refuse'); // 拒绝被记录 = 悲伤行为
    this.emit('task', `refused:${id}`);
    // 拒绝也视为该任务「了结」，检查黄昏
    const allSettled = tasksOfDay(this.day).every(
      (t) => this.tasksDone.has(t.id) || this.refusedTasks.has(t.id),
    );
    if (allSettled && this.day < 7) this.emit('dusk');
  }

  isTaskActive(id: string): boolean {
    const def = TASKS[id];
    return !!def && def.day === this.day && !this.tasksDone.has(id) && !this.refusedTasks.has(id);
  }

  /** 当日任务是否全部了结（完成或拒绝） */
  get dailySettled(): boolean {
    return tasksOfDay(this.day).every(
      (t) => this.tasksDone.has(t.id) || this.refusedTasks.has(t.id),
    );
  }

  // ---------- 日期 ----------
  nextDay(): void {
    if (this.day >= 7) return;
    this.day += 1;
    this.dayElapsedMs = 0;
    this.rainActive = false;
    this.lowHappyMs = 0;
    // 新的一天不可能从庆典/睡觉过场中开始（防庆典对话被覆盖后卡死）
    this.celebrationActive = false;
    this.sceneTransition = false;
    this.resetDaily();
    this.emit('day', this.day);
  }

  setDay(d: number): void {
    this.day = Math.max(1, Math.min(7, Math.round(d)));
    this.dayElapsedMs = 0;
    this.rainActive = false;
    this.resetDaily();
    this.emit('day', this.day);
  }

  // ---------- 欢乐庆典（<30 持续 2 分钟 → 拉回 50；可逃脱） ----------
  private startCelebration(): void {
    if (this.celebrationActive || this.dialogueActive) return;
    this.celebrationActive = true;
    this.lowHappyMs = 0;
    this.emit('celebration', 'start');
  }

  completeCelebration(): void {
    if (!this.celebrationActive) return;
    this.celebrationActive = false;
    const prevStage = this.stage;
    this.happiness = CELEBRATION_RESTORE;
    this.emit('happiness', this.happiness);
    if (this.day >= 5) this.e4HappyOk = false;
    if (this.stage !== prevStage) this.emit('stage', this.stage, this.params);
    this.emit('celebration', 'end');
  }

  escapeCelebration(): void {
    if (!this.celebrationActive) return;
    this.celebrationActive = false;
    this.escapedCelebrations += 1;
    this.emit('celebration', 'escape');
  }

  // ---------- 雨 ----------
  setRain(active: boolean): void {
    if (this.rainActive === active) return;
    this.rainActive = active;
    this.emit('rain', active);
  }

  // ---------- 每帧计时（由 MapScene.update 驱动） ----------
  tick(deltaMs: number): void {
    this.dayElapsedMs += deltaMs;

    // E4 监视：D5-7 幸福曾 >10 即失格（含被动状态，不靠加减分触发）
    if (this.day >= 5 && this.happiness > 10) this.e4HappyOk = false;

    // 连续低幸福计时（真实时间）
    if (this.happiness < LOW_HAPPY_THRESHOLD && !this.celebrationActive && !this.dialogueActive && !this.sceneTransition) {
      this.lowHappyMs += deltaMs;
      if (this.lowHappyMs >= LOW_HAPPY_MS) {
        this.startCelebration();
      }
    } else if (this.happiness >= LOW_HAPPY_THRESHOLD) {
      this.lowHappyMs = 0;
    }

    // S4 随机单帧负片（0.4 次/分钟 → 平均间隔 150 秒）
    if (this.stage === 4) {
      this.negFlashTimer -= deltaMs;
      if (this.negFlashTimer <= 0) {
        this.emit('negativeFlash');
        this.negFlashTimer = 100_000 + Math.random() * 100_000;
      }
    }
  }

  // ---------- 结局判定（§9 优先级：E4 > E3 > E2 > E1） ----------
  evaluateEnding(atFogWall: boolean): EndingId {
    if (this.e4HappyOk && this.happiness <= 10 && this.day >= 5 && this.escapedCelebrations >= 3) return 'E4';
    if (atFogWall && this.awareness >= 70 && this.clues.size >= 6) return 'E3';
    if (this.awareness >= 30) return 'E2';
    if (this.happiness >= 90) return 'E1';
    // 觉知 ≤29 但幸福 <90 的边界：按最接近的「回收」处理
    return 'E2';
  }

  startEnding(id: EndingId): void {
    this.emit('ending', id);
  }

  // ---------- 对话总线（MapScene → UIScene，Promise 化） ----------
  showDialogue(payload: DialoguePayload): Promise<number | null> {
    // 重入保护：上一个对话的 resolver 若仍悬挂（被新对话覆盖），
    // 必须先将其解析，否则 await 它的一方（如梦境过场）会永久卡住
    if (this.dialogueResolver) this.resolveDialogue(null);
    this.dialogueActive = true;
    return new Promise((resolve) => {
      this.dialogueResolver = resolve;
      this.emit('dialogue', payload);
    });
  }

  /** UIScene 对话结束时调用 */
  resolveDialogue(choice: number | null): void {
    this.dialogueActive = false;
    this.dialogueClosedAt = Date.now();
    const r = this.dialogueResolver;
    this.dialogueResolver = null;
    if (r) r(choice);
  }

  /** debug 用：强制清空进行中的一次性状态（对话/庆典/T14），避免传送后卡死 */
  forceIdle(): void {
    this.celebrationActive = false;
    this.t14Active = false;
    this.rainActive = false;
    this.sceneTransition = false;
    this.resolveDialogue(null);
  }
}

export const director = CorruptionDirector.instance;
