import { createRequire } from "node:module";
import { readFile } from "node:fs/promises";
import path from "node:path";
import type { BotConfig } from "../config.ts";
import type { ChannelStatus, ChatMessage, EngineEvent } from "../types.ts";
import type { ActiveGame } from "./GameRouter.ts";

type LegacyEngine = {
  start(config: Record<string, unknown>, startedBy: string): { ok: boolean; error?: string };
  stop(): unknown;
  setStreamerId?(id: string): void;
  submitAnswer?(player: string, answer: string): unknown;
  submitGuess?(player: string, answer: string): unknown;
};

type Instance = {
  game: ActiveGame;
  channel: ChannelStatus;
  engine: LegacyEngine;
  viewers: Map<string, { kick_user_id: string; username: string }>;
};

const require = createRequire(import.meta.url);

export class LegacyGameCoordinator {
  private readonly config: BotConfig;
  private readonly emit: (event: EngineEvent) => void;
  private readonly instances = new Map<number, Instance>();

  constructor(config: BotConfig, emit: (event: EngineEvent) => void) {
    this.config = config;
    this.emit = emit;
  }

  async sync(channel: ChannelStatus, game: ActiveGame | null): Promise<void> {
    const current = this.instances.get(channel.channel_id);
    if (!game) {
      current?.engine.stop();
      this.instances.delete(channel.channel_id);
      return;
    }
    if (current?.game.game_session_id === game.game_session_id) return;
    current?.engine.stop();
    const instance = await this.create(channel, game);
    this.instances.set(channel.channel_id, instance);
    const settings = await this.loadSettings(game.game);
    const result = instance.engine.start(settings, game.state === "starting" ? "Panel FilyX" : "Reprise FilyX");
    if (!result.ok) {
      this.emit({
        id: `game-failed:${game.game_session_id}:${Date.now()}`,
        channel_id: channel.channel_id,
        type: "game.session.failed",
        payload: { game_session_id: game.game_session_id, error: result.error ?? "Démarrage refusé" }
      });
      this.instances.delete(channel.channel_id);
      return;
    }
    this.emit({
      id: `game-started:${game.game_session_id}`,
      channel_id: channel.channel_id,
      type: "game.session.started",
      payload: { game_session_id: game.game_session_id, game: game.game }
    });
  }

  handleMessage(message: ChatMessage): void {
    const instance = this.instances.get(message.channel_id);
    if (!instance || message.content.startsWith("!")) return;
    const usernameKey = message.sender.username.toLocaleLowerCase("fr");
    instance.viewers.set(usernameKey, {
      kick_user_id: message.sender.kick_user_id,
      username: message.sender.username
    });
    if (instance.game.game === "quiz" || instance.game.game === "petitbac") {
      instance.engine.submitAnswer?.(message.sender.username, message.content.trim());
    } else {
      instance.engine.submitGuess?.(message.sender.username, message.content.trim());
    }
  }

  stop(): void {
    for (const instance of this.instances.values()) instance.engine.stop();
    this.instances.clear();
  }

  private async create(channel: ChannelStatus, game: ActiveGame): Promise<Instance> {
    const source = path.join(this.config.legacyRoot, "games-original", "src");
    const modules = {
      quiz: { file: "quiz-engine.js", exportName: "QuizEngine" },
      motus: { file: "motus-engine.js", exportName: "MotusEngine" },
      whoami: { file: "whoami-engine.js", exportName: "WhoamiEngine" },
      petitbac: { file: "petitbac-engine.js", exportName: "PetitbacEngine" }
    } as const;
    const target = modules[game.game];
    const loaded = require(path.join(source, target.file)) as Record<string, new (...args: unknown[]) => LegacyEngine>;
    const Engine = loaded[target.exportName];
    if (!Engine) throw new Error(`Moteur historique introuvable : ${game.game}`);
    const viewers = new Map<string, { kick_user_id: string; username: string }>();
    const broadcast = (payload: Record<string, unknown>): void => {
      const scores = this.enrichScores(payload.scores, viewers);
      const data = scores ? { ...payload, scores } : payload;
      this.emit({
        id: `overlay:${game.game_session_id}:${Date.now()}:${Math.random().toString(16).slice(2)}`,
        channel_id: channel.channel_id,
        type: "overlay.event",
        payload: {
          event_type: `game.${game.game}.${String(payload.type ?? "state")}`,
          data: { ...data, game_session_id: game.game_session_id },
          priority: 3,
          is_demo: false
        }
      });
      if (scores) {
        this.emit({
          id: `scores:${game.game_session_id}:${Date.now()}`,
          channel_id: channel.channel_id,
          type: "game.scores",
          payload: { game_session_id: game.game_session_id, scores }
        });
      }
      if (this.isContentEvent(String(payload.type ?? ""))) {
        this.emit({
          id: `content:${game.game_session_id}:${Date.now()}:${Math.random().toString(16).slice(2)}`,
          channel_id: channel.channel_id,
          type: "game.content",
          payload: {
            game_session_id: game.game_session_id,
            game: game.game,
            content_type: String(payload.type ?? "content"),
            content: data
          }
        });
      }
      if (this.isEndEvent(String(payload.type ?? ""))) {
        this.emit({
          id: `game-completed:${game.game_session_id}`,
          channel_id: channel.channel_id,
          type: "game.session.completed",
          payload: { game_session_id: game.game_session_id }
        });
      }
    };
    const chatSend = (text: string): void => {
      this.emit({
        id: `game-chat:${game.game_session_id}:${Date.now()}:${Math.random().toString(16).slice(2)}`,
        channel_id: channel.channel_id,
        type: "game.chat",
        payload: { game_session_id: game.game_session_id, text: this.rebrand(text) }
      });
    };
    const engine = new Engine(broadcast, chatSend);
    engine.setStreamerId?.(String(channel.channel_id));
    return { game, channel, engine, viewers };
  }

  private async loadSettings(game: ActiveGame["game"]): Promise<Record<string, unknown>> {
    const file = path.join(this.config.legacyRoot, "games-original", `config.${game}.json`);
    const parsed = JSON.parse(await readFile(file, "utf8")) as Record<string, unknown>;
    return this.deepRebrand(parsed) as Record<string, unknown>;
  }

  private deepRebrand(value: unknown): unknown {
    if (typeof value === "string") return this.rebrand(value);
    if (Array.isArray(value)) return value.map((item) => this.deepRebrand(item));
    if (value && typeof value === "object") {
      return Object.fromEntries(Object.entries(value).map(([key, item]) => [key, this.deepRebrand(item)]));
    }
    return value;
  }

  private rebrand(value: string): string {
    return value
      .replace(/\[emote:[^\]]+\]\s*/g, "")
      .replace(/HR3D(?:_STUDIO|-Studio| Studio)?/gi, "FilyX")
      .replace(/Le Motus de Filyx/gi, "Motus FilyX");
  }

  private enrichScores(value: unknown, viewers: Map<string, { kick_user_id: string; username: string }>): unknown[] | null {
    if (!Array.isArray(value)) return null;
    return value.map((entry) => {
      if (!entry || typeof entry !== "object") return entry;
      const score = entry as Record<string, unknown>;
      const username = String(score.player ?? score.username ?? "");
      const viewer = viewers.get(username.toLocaleLowerCase("fr"));
      return { ...score, username, kick_user_id: viewer?.kick_user_id ?? "" };
    });
  }

  private isEndEvent(type: string): boolean {
    return ["quiz-podium", "motus-end", "whoami-podium", "petitbac-podium"].includes(type);
  }

  private isContentEvent(type: string): boolean {
    return [
      "quiz-question",
      "quiz-answer",
      "motus-round-start",
      "motus-word-reveal",
      "whoami-round-start",
      "whoami-round-end",
      "petitbac-start",
      "petitbac-category-start"
    ].includes(type);
  }
}

