import { EmbedBuilder, type ChatInputCommandInteraction } from 'discord.js';
import { EIGHT_BALL_ANSWERS, randomInt, TRIVIA_QUESTIONS, t, tf } from '@nexumi/shared';
import type { SlashCommand } from '../../types.js';
import { getGuildLocale } from '../../i18n.js';
import type { RpsChoice } from './command-definitions.js';
import {
  catCommandData,
  chooseCommandData,
  connect4CommandData,
  diceCommandData,
  dogCommandData,
  eightBallCommandData,
  flipCommandData,
  hangmanCommandData,
  memeCommandData,
  rpsCommandData,
  tictactoeCommandData,
  triviaCommandData
} from './command-definitions.js';
import {
  buildConnect4Components,
  buildConnect4Embed,
  buildHangmanComponents,
  buildHangmanEmbed,
  buildTicTacToeComponents,
  buildTicTacToeEmbed,
  buildTriviaComponents,
  buildTriviaEmbed,
  createConnect4Board,
  createGameId,
  pickHangmanWord,
  saveConnect4State,
  saveHangmanState,
  saveTicTacToeState,
  saveTriviaSession
} from './games.js';
import {
  assertMediaEnabled,
  fetchCatImage,
  fetchDogImage,
  fetchMeme,
  FunMediaError
} from './media.js';

const RPS_CHOICES: RpsChoice[] = ['rock', 'paper', 'scissors'];

function rpsWinner(a: RpsChoice, b: RpsChoice): 'player' | 'bot' | 'draw' {
  if (a === b) {
    return 'draw';
  }
  if (
    (a === 'rock' && b === 'scissors') ||
    (a === 'paper' && b === 'rock') ||
    (a === 'scissors' && b === 'paper')
  ) {
    return 'player';
  }
  return 'bot';
}

async function ensureGuild(
  interaction: ChatInputCommandInteraction,
  locale: 'de' | 'en'
): Promise<string | null> {
  if (!interaction.inGuild() || !interaction.guildId) {
    await interaction.reply({ content: t(locale, 'generic.guildOnly'), ephemeral: true });
    return null;
  }
  return interaction.guildId;
}

function parseChooseOptions(raw: string): string[] {
  return raw
    .split(',')
    .map((part) => part.trim())
    .filter((part) => part.length > 0);
}

const eightBallCommand: SlashCommand = {
  data: eightBallCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    const guildId = await ensureGuild(interaction, locale);
    if (!guildId) return;

    const question = interaction.options.getString('question', true);
    const answers = EIGHT_BALL_ANSWERS[locale];
    const answer = answers[randomInt(0, answers.length - 1)]!;

    await interaction.reply({
      content: tf(locale, 'fun.8ball.result', { question, answer }),
      ephemeral: false
    });
  }
};

const diceCommand: SlashCommand = {
  data: diceCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    const guildId = await ensureGuild(interaction, locale);
    if (!guildId) return;

    const sides = interaction.options.getInteger('sides') ?? 6;
    const count = interaction.options.getInteger('count') ?? 1;
    const rolls = Array.from({ length: count }, () => randomInt(1, sides));

    await interaction.reply({
      content: tf(locale, 'fun.dice.result', {
        rolls: rolls.join(', '),
        sides: String(sides)
      }),
      ephemeral: false
    });
  }
};

const flipCommand: SlashCommand = {
  data: flipCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    const guildId = await ensureGuild(interaction, locale);
    if (!guildId) return;

    const result = Math.random() < 0.5 ? 'heads' : 'tails';
    const label = t(locale, `economy.choice.${result}`);

    await interaction.reply({
      content: tf(locale, 'fun.flip.result', { result: label }),
      ephemeral: false
    });
  }
};

const rpsCommand: SlashCommand = {
  data: rpsCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    const guildId = await ensureGuild(interaction, locale);
    if (!guildId) return;

    const playerChoice = interaction.options.getString('choice', true) as RpsChoice;
    const botChoice = RPS_CHOICES[randomInt(0, RPS_CHOICES.length - 1)]!;
    const outcome = rpsWinner(playerChoice, botChoice);

    const key =
      outcome === 'draw'
        ? 'fun.rps.draw'
        : outcome === 'player'
          ? 'fun.rps.win'
          : 'fun.rps.lose';

    await interaction.reply({
      content: tf(locale, key, {
        player: t(locale, `fun.rps.choice.${playerChoice}`),
        bot: t(locale, `fun.rps.choice.${botChoice}`)
      }),
      ephemeral: false
    });
  }
};

const chooseCommand: SlashCommand = {
  data: chooseCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    const guildId = await ensureGuild(interaction, locale);
    if (!guildId) return;

    const raw = interaction.options.getString('options', true);
    const options = parseChooseOptions(raw);
    if (options.length < 2) {
      await interaction.reply({ content: t(locale, 'fun.choose.tooFew'), ephemeral: true });
      return;
    }

    const picked = options[randomInt(0, options.length - 1)]!;
    await interaction.reply({
      content: tf(locale, 'fun.choose.result', { choice: picked }),
      ephemeral: false
    });
  }
};

const triviaCommand: SlashCommand = {
  data: triviaCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    const guildId = await ensureGuild(interaction, locale);
    if (!guildId) return;

    const questionIndex = randomInt(0, TRIVIA_QUESTIONS.length - 1);
    const question = TRIVIA_QUESTIONS[questionIndex]!;
    const correctIndex = question.answer;

    await saveTriviaSession(context, {
      userId: interaction.user.id,
      guildId,
      questionIndex,
      correctIndex
    });

    await interaction.reply({
      embeds: [buildTriviaEmbed(locale, question, interaction.user.id)],
      components: buildTriviaComponents(interaction.user.id, correctIndex, locale, question)
    });
  }
};

const tictactoeCommand: SlashCommand = {
  data: tictactoeCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    const guildId = await ensureGuild(interaction, locale);
    if (!guildId || !interaction.channel) return;

    const opponent = interaction.options.getUser('user', true);
    if (opponent.id === interaction.user.id) {
      await interaction.reply({ content: t(locale, 'fun.common.selfChallenge'), ephemeral: true });
      return;
    }
    if (opponent.bot) {
      await interaction.reply({ content: t(locale, 'fun.common.botChallenge'), ephemeral: true });
      return;
    }

    const gameId = createGameId();
    const state = {
      gameId,
      guildId,
      channelId: interaction.channel.id,
      playerX: interaction.user.id,
      playerO: opponent.id,
      board: Array.from({ length: 9 }, () => null) as Array<'X' | 'O' | null>,
      current: 'X' as const,
      status: 'active' as const
    };

    await saveTicTacToeState(context, state);

    await interaction.reply({
      content: tf(locale, 'fun.tictactoe.challenge', {
        challenger: `<@${interaction.user.id}>`,
        opponent: `<@${opponent.id}>`
      }),
      embeds: [buildTicTacToeEmbed(locale, state)],
      components: buildTicTacToeComponents(state)
    });
  }
};

const connect4Command: SlashCommand = {
  data: connect4CommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    const guildId = await ensureGuild(interaction, locale);
    if (!guildId || !interaction.channel) return;

    const opponent = interaction.options.getUser('user', true);
    if (opponent.id === interaction.user.id) {
      await interaction.reply({ content: t(locale, 'fun.common.selfChallenge'), ephemeral: true });
      return;
    }
    if (opponent.bot) {
      await interaction.reply({ content: t(locale, 'fun.common.botChallenge'), ephemeral: true });
      return;
    }

    const gameId = createGameId();
    const state = {
      gameId,
      guildId,
      channelId: interaction.channel.id,
      playerRed: interaction.user.id,
      playerYellow: opponent.id,
      board: createConnect4Board(),
      current: 'R' as const,
      status: 'active' as const
    };

    await saveConnect4State(context, state);

    await interaction.reply({
      content: tf(locale, 'fun.connect4.challenge', {
        challenger: `<@${interaction.user.id}>`,
        opponent: `<@${opponent.id}>`
      }),
      embeds: [buildConnect4Embed(locale, state)],
      components: buildConnect4Components(state)
    });
  }
};

const hangmanCommand: SlashCommand = {
  data: hangmanCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    const guildId = await ensureGuild(interaction, locale);
    if (!guildId) return;

    const gameId = createGameId();
    const state = {
      gameId,
      guildId,
      userId: interaction.user.id,
      word: pickHangmanWord(locale),
      guessed: [] as string[],
      wrongGuesses: 0,
      maxWrong: 6,
      status: 'active' as const,
      letterPage: 0
    };

    await saveHangmanState(context, state);

    await interaction.reply({
      embeds: [buildHangmanEmbed(locale, state)],
      components: buildHangmanComponents(state)
    });
  }
};

async function handleMediaError(
  interaction: ChatInputCommandInteraction,
  locale: 'de' | 'en',
  error: unknown
): Promise<void> {
  if (error instanceof FunMediaError) {
    const key = error.code === 'disabled' ? 'fun.media.disabled' : 'fun.media.fetchFailed';
    const content = t(locale, key);
    if (interaction.deferred || interaction.replied) {
      await interaction.editReply({ content });
      return;
    }
    await interaction.reply({ content, ephemeral: true });
    return;
  }
  throw error;
}

const memeCommand: SlashCommand = {
  data: memeCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    const guildId = await ensureGuild(interaction, locale);
    if (!guildId) return;

    try {
      await assertMediaEnabled(context.redis, guildId);
      await interaction.deferReply();
      const meme = await fetchMeme();
      const embed = new EmbedBuilder()
        .setTitle(meme.title)
        .setImage(meme.url)
        .setColor(0x6366f1);
      await interaction.editReply({ embeds: [embed] });
    } catch (error) {
      await handleMediaError(interaction, locale, error);
    }
  }
};

const catCommand: SlashCommand = {
  data: catCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    const guildId = await ensureGuild(interaction, locale);
    if (!guildId) return;

    try {
      await assertMediaEnabled(context.redis, guildId);
      await interaction.deferReply();
      const url = await fetchCatImage();
      const embed = new EmbedBuilder()
        .setTitle(t(locale, 'fun.cat.title'))
        .setImage(url)
        .setColor(0x6366f1);
      await interaction.editReply({ embeds: [embed] });
    } catch (error) {
      await handleMediaError(interaction, locale, error);
    }
  }
};

const dogCommand: SlashCommand = {
  data: dogCommandData,
  async execute(interaction, context) {
    const locale = await getGuildLocale(context.prisma, interaction.guildId);
    const guildId = await ensureGuild(interaction, locale);
    if (!guildId) return;

    try {
      await assertMediaEnabled(context.redis, guildId);
      await interaction.deferReply();
      const url = await fetchDogImage();
      const embed = new EmbedBuilder()
        .setTitle(t(locale, 'fun.dog.title'))
        .setImage(url)
        .setColor(0x6366f1);
      await interaction.editReply({ embeds: [embed] });
    } catch (error) {
      await handleMediaError(interaction, locale, error);
    }
  }
};

export const funCommands: SlashCommand[] = [
  eightBallCommand,
  diceCommand,
  flipCommand,
  rpsCommand,
  chooseCommand,
  triviaCommand,
  tictactoeCommand,
  connect4Command,
  hangmanCommand,
  memeCommand,
  catCommand,
  dogCommand
];
