import {
  ActionRowBuilder,
  ButtonBuilder,
  ButtonStyle,
  type APIEmbed
} from 'discord.js';
import { HANGMAN_WORDS, randomInt, TRIVIA_QUESTIONS, type Locale, tf } from '@nexumi/shared';
import type { BotContext } from '../../types.js';

export const FUN_BUTTON_PREFIX = 'fun:';
export const TRIVIA_TTL_SECONDS = 120;
export const GAME_TTL_SECONDS = 3600;

const TRIVIA_KEY_PREFIX = 'fun:trivia:';
const TTT_KEY_PREFIX = 'fun:ttt:';
const C4_KEY_PREFIX = 'fun:c4:';
const HM_KEY_PREFIX = 'fun:hm:';

export type TicTacToeMark = 'X' | 'O';
export type TicTacToeCell = TicTacToeMark | null;

export type TicTacToeState = {
  gameId: string;
  guildId: string;
  channelId: string;
  playerX: string;
  playerO: string;
  board: TicTacToeCell[];
  current: TicTacToeMark;
  status: 'active' | 'draw' | 'won';
  winner?: TicTacToeMark;
};

export type Connect4Mark = 'R' | 'Y';
export type Connect4Cell = Connect4Mark | null;

export type Connect4State = {
  gameId: string;
  guildId: string;
  channelId: string;
  playerRed: string;
  playerYellow: string;
  board: Connect4Cell[][];
  current: Connect4Mark;
  status: 'active' | 'draw' | 'won';
  winner?: Connect4Mark;
};

export type HangmanState = {
  gameId: string;
  guildId: string;
  userId: string;
  word: string;
  guessed: string[];
  wrongGuesses: number;
  maxWrong: number;
  status: 'active' | 'won' | 'lost';
  letterPage: number;
};

export type TriviaSession = {
  userId: string;
  guildId: string;
  questionIndex: number;
  correctIndex: number;
};

const TTT_LINES = [
  [0, 1, 2],
  [3, 4, 5],
  [6, 7, 8],
  [0, 3, 6],
  [1, 4, 7],
  [2, 5, 8],
  [0, 4, 8],
  [2, 4, 6]
] as const;

const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');

export function createGameId(): string {
  return crypto.randomUUID().replace(/-/g, '').slice(0, 12);
}

export function checkTicTacToeWinner(board: TicTacToeCell[]): TicTacToeMark | null {
  for (const [a, b, c] of TTT_LINES) {
    const mark = board[a];
    if (mark && mark === board[b] && mark === board[c]) {
      return mark;
    }
  }
  return null;
}

export function currentTicTacToePlayerId(state: TicTacToeState): string {
  return state.current === 'X' ? state.playerX : state.playerO;
}

export function applyTicTacToeMove(state: TicTacToeState, cell: number): TicTacToeState {
  if (state.status !== 'active' || state.board[cell] !== null) {
    return state;
  }
  const board = [...state.board];
  board[cell] = state.current;
  const winner = checkTicTacToeWinner(board);
  if (winner) {
    return { ...state, board, status: 'won', winner };
  }
  if (board.every((value) => value !== null)) {
    return { ...state, board, status: 'draw' };
  }
  return { ...state, board, current: state.current === 'X' ? 'O' : 'X' };
}

export function createConnect4Board(): Connect4Cell[][] {
  return Array.from({ length: 6 }, () => Array.from({ length: 7 }, () => null));
}

function connect4DropRow(board: Connect4Cell[][], col: number): number {
  for (let row = board.length - 1; row >= 0; row -= 1) {
    if (board[row]![col] === null) {
      return row;
    }
  }
  return -1;
}

function connect4ColumnFull(board: Connect4Cell[][], col: number): boolean {
  return board[0]![col] !== null;
}

export function applyConnect4Move(state: Connect4State, col: number): Connect4State {
  if (state.status !== 'active' || col < 0 || col >= 7 || connect4ColumnFull(state.board, col)) {
    return state;
  }
  const row = connect4DropRow(state.board, col);
  if (row < 0) {
    return state;
  }
  const board = state.board.map((line) => [...line]);
  board[row]![col] = state.current;
  const winner = checkConnect4Winner(board);
  if (winner) {
    return { ...state, board, status: 'won', winner };
  }
  if (board.every((line) => line.every((cell) => cell !== null))) {
    return { ...state, board, status: 'draw' };
  }
  return { ...state, board, current: state.current === 'R' ? 'Y' : 'R' };
}

export function checkConnect4Winner(board: Connect4Cell[][]): Connect4Mark | null {
  const directions = [
    [0, 1],
    [1, 0],
    [1, 1],
    [1, -1]
  ] as const;

  for (let row = 0; row < 6; row += 1) {
    for (let col = 0; col < 7; col += 1) {
      const start = board[row]![col];
      if (!start) {
        continue;
      }
      for (const [dr, dc] of directions) {
        let matches = 1;
        for (let step = 1; step < 4; step += 1) {
          const next = board[row + dr * step]?.[col + dc * step];
          if (next !== start) {
            break;
          }
          matches += 1;
        }
        if (matches >= 4) {
          return start;
        }
      }
    }
  }
  return null;
}

export function currentConnect4PlayerId(state: Connect4State): string {
  return state.current === 'R' ? state.playerRed : state.playerYellow;
}

export function pickHangmanWord(locale: Locale): string {
  const words = HANGMAN_WORDS[locale];
  return words[randomInt(0, words.length - 1)]!;
}

export function renderHangmanWord(word: string, guessed: string[]): string {
  return word
    .split('')
    .map((letter) => (guessed.includes(letter) ? letter : '⬜'))
    .join(' ');
}

export function applyHangmanGuess(state: HangmanState, letter: string): HangmanState {
  const normalized = letter.toUpperCase();
  if (
    state.status !== 'active' ||
    normalized.length !== 1 ||
    !/^[A-Z]$/.test(normalized) ||
    state.guessed.includes(normalized)
  ) {
    return state;
  }

  const guessed = [...state.guessed, normalized];
  const inWord = state.word.includes(normalized);
  const wrongGuesses = inWord ? state.wrongGuesses : state.wrongGuesses + 1;
  const revealed = state.word.split('').every((char) => guessed.includes(char));
  if (revealed) {
    return { ...state, guessed, wrongGuesses, status: 'won' };
  }
  if (wrongGuesses >= state.maxWrong) {
    return { ...state, guessed, wrongGuesses, status: 'lost' };
  }
  return { ...state, guessed, wrongGuesses };
}

async function saveJson(redis: BotContext['redis'], key: string, value: unknown, ttl: number): Promise<void> {
  await redis.set(key, JSON.stringify(value), 'EX', ttl);
}

async function loadJson<T>(redis: BotContext['redis'], key: string): Promise<T | null> {
  const raw = await redis.get(key);
  if (!raw) {
    return null;
  }
  return JSON.parse(raw) as T;
}

export async function saveTriviaSession(context: BotContext, session: TriviaSession): Promise<void> {
  await saveJson(context.redis, `${TRIVIA_KEY_PREFIX}${session.userId}`, session, TRIVIA_TTL_SECONDS);
}

export async function loadTriviaSession(context: BotContext, userId: string): Promise<TriviaSession | null> {
  return loadJson<TriviaSession>(context.redis, `${TRIVIA_KEY_PREFIX}${userId}`);
}

export async function deleteTriviaSession(context: BotContext, userId: string): Promise<void> {
  await context.redis.del(`${TRIVIA_KEY_PREFIX}${userId}`);
}

export async function saveTicTacToeState(context: BotContext, state: TicTacToeState): Promise<void> {
  await saveJson(context.redis, `${TTT_KEY_PREFIX}${state.gameId}`, state, GAME_TTL_SECONDS);
}

export async function loadTicTacToeState(context: BotContext, gameId: string): Promise<TicTacToeState | null> {
  return loadJson<TicTacToeState>(context.redis, `${TTT_KEY_PREFIX}${gameId}`);
}

export async function deleteTicTacToeState(context: BotContext, gameId: string): Promise<void> {
  await context.redis.del(`${TTT_KEY_PREFIX}${gameId}`);
}

export async function saveConnect4State(context: BotContext, state: Connect4State): Promise<void> {
  await saveJson(context.redis, `${C4_KEY_PREFIX}${state.gameId}`, state, GAME_TTL_SECONDS);
}

export async function loadConnect4State(context: BotContext, gameId: string): Promise<Connect4State | null> {
  return loadJson<Connect4State>(context.redis, `${C4_KEY_PREFIX}${gameId}`);
}

export async function deleteConnect4State(context: BotContext, gameId: string): Promise<void> {
  await context.redis.del(`${C4_KEY_PREFIX}${gameId}`);
}

export async function saveHangmanState(context: BotContext, state: HangmanState): Promise<void> {
  await saveJson(context.redis, `${HM_KEY_PREFIX}${state.gameId}`, state, GAME_TTL_SECONDS);
}

export async function loadHangmanState(context: BotContext, gameId: string): Promise<HangmanState | null> {
  return loadJson<HangmanState>(context.redis, `${HM_KEY_PREFIX}${gameId}`);
}

export async function deleteHangmanState(context: BotContext, gameId: string): Promise<void> {
  await context.redis.del(`${HM_KEY_PREFIX}${gameId}`);
}

function tttCellLabel(cell: TicTacToeCell): string {
  if (cell === 'X') return '❌';
  if (cell === 'O') return '⭕';
  return '⬜';
}

export function buildTicTacToeEmbed(locale: Locale, state: TicTacToeState): APIEmbed {
  const boardLines = [0, 1, 2]
    .map((row) => state.board.slice(row * 3, row * 3 + 3).map(tttCellLabel).join(' '))
    .join('\n');

  let description = tf(locale, 'fun.tictactoe.board', {
    x: `<@${state.playerX}>`,
    o: `<@${state.playerO}>`,
    board: boardLines
  });

  if (state.status === 'won' && state.winner) {
    const winnerId = state.winner === 'X' ? state.playerX : state.playerO;
    description += `\n\n${tf(locale, 'fun.tictactoe.won', { user: `<@${winnerId}>` })}`;
  } else if (state.status === 'draw') {
    description += `\n\n${tf(locale, 'fun.tictactoe.draw', {})}`;
  } else {
    description += `\n\n${tf(locale, 'fun.tictactoe.turn', {
      user: `<@${currentTicTacToePlayerId(state)}>`
    })}`;
  }

  return { title: tf(locale, 'fun.tictactoe.title', {}), description, color: 0x6366f1 };
}

export function buildTicTacToeComponents(state: TicTacToeState): ActionRowBuilder<ButtonBuilder>[] {
  if (state.status !== 'active') {
    return [];
  }
  const rows: ActionRowBuilder<ButtonBuilder>[] = [];
  for (let row = 0; row < 3; row += 1) {
    const builder = new ActionRowBuilder<ButtonBuilder>();
    for (let col = 0; col < 3; col += 1) {
      const cell = row * 3 + col;
      const mark = state.board[cell];
      builder.addComponents(
        new ButtonBuilder()
          .setCustomId(`${FUN_BUTTON_PREFIX}ttt:${state.gameId}:${cell}`)
          .setLabel(mark ?? String(cell + 1))
          .setStyle(mark ? ButtonStyle.Secondary : ButtonStyle.Primary)
          .setDisabled(mark !== null)
      );
    }
    rows.push(builder);
  }
  return rows;
}

function c4CellLabel(cell: Connect4Cell): string {
  if (cell === 'R') return '🔴';
  if (cell === 'Y') return '🟡';
  return '⚪';
}

export function buildConnect4Embed(locale: Locale, state: Connect4State): APIEmbed {
  const boardText = [...state.board]
    .reverse()
    .map((row) => row.map(c4CellLabel).join(''))
    .join('\n');

  let description = tf(locale, 'fun.connect4.board', {
    red: `<@${state.playerRed}>`,
    yellow: `<@${state.playerYellow}>`,
    board: boardText
  });

  if (state.status === 'won' && state.winner) {
    const winnerId = state.winner === 'R' ? state.playerRed : state.playerYellow;
    description += `\n\n${tf(locale, 'fun.connect4.won', { user: `<@${winnerId}>` })}`;
  } else if (state.status === 'draw') {
    description += `\n\n${tf(locale, 'fun.connect4.draw', {})}`;
  } else {
    description += `\n\n${tf(locale, 'fun.connect4.turn', {
      user: `<@${currentConnect4PlayerId(state)}>`
    })}`;
  }

  return { title: tf(locale, 'fun.connect4.title', {}), description, color: 0x6366f1 };
}

export function buildConnect4Components(state: Connect4State): ActionRowBuilder<ButtonBuilder>[] {
  if (state.status !== 'active') {
    return [];
  }
  const row = new ActionRowBuilder<ButtonBuilder>();
  for (let col = 0; col < 7; col += 1) {
    row.addComponents(
      new ButtonBuilder()
        .setCustomId(`${FUN_BUTTON_PREFIX}c4:${state.gameId}:${col}`)
        .setLabel(String(col + 1))
        .setStyle(ButtonStyle.Primary)
        .setDisabled(connect4ColumnFull(state.board, col))
    );
  }
  return [row];
}

export function buildHangmanEmbed(locale: Locale, state: HangmanState): APIEmbed {
  let description = tf(locale, 'fun.hangman.progress', {
    word: renderHangmanWord(state.word, state.guessed),
    wrong: state.wrongGuesses,
    max: state.maxWrong,
    guessed: state.guessed.length > 0 ? state.guessed.join(', ') : '—'
  });

  if (state.status === 'won') {
    description += `\n\n${tf(locale, 'fun.hangman.won', { word: state.word })}`;
  } else if (state.status === 'lost') {
    description += `\n\n${tf(locale, 'fun.hangman.lost', { word: state.word })}`;
  }

  return { title: tf(locale, 'fun.hangman.title', {}), description, color: 0x6366f1 };
}

function hangmanLettersForPage(state: HangmanState): { letters: string[]; page: number; totalPages: number } {
  const remaining = ALPHABET.filter((letter) => !state.guessed.includes(letter));
  const lettersPerPage = 24;
  const totalPages = Math.max(1, Math.ceil(remaining.length / lettersPerPage));
  const page = Math.min(state.letterPage, totalPages - 1);
  const start = page * lettersPerPage;
  return {
    letters: remaining.slice(start, start + lettersPerPage),
    page,
    totalPages
  };
}

export function buildHangmanComponents(state: HangmanState): ActionRowBuilder<ButtonBuilder>[] {
  if (state.status !== 'active') {
    return [];
  }

  const { letters, page, totalPages } = hangmanLettersForPage(state);
  const rows: ActionRowBuilder<ButtonBuilder>[] = [];

  for (let i = 0; i < letters.length; i += 5) {
    const chunk = letters.slice(i, i + 5);
    const row = new ActionRowBuilder<ButtonBuilder>();
    for (const letter of chunk) {
      row.addComponents(
        new ButtonBuilder()
          .setCustomId(`${FUN_BUTTON_PREFIX}hm:${state.gameId}:${letter}`)
          .setLabel(letter)
          .setStyle(ButtonStyle.Primary)
      );
    }
    rows.push(row);
    if (rows.length >= 4) {
      break;
    }
  }

  const navRow = new ActionRowBuilder<ButtonBuilder>();
  if (totalPages > 1) {
    navRow.addComponents(
      new ButtonBuilder()
        .setCustomId(`${FUN_BUTTON_PREFIX}hm:${state.gameId}:page:${Math.max(0, page - 1)}`)
        .setLabel('◀')
        .setStyle(ButtonStyle.Secondary)
        .setDisabled(page <= 0),
      new ButtonBuilder()
        .setCustomId(`${FUN_BUTTON_PREFIX}hm:${state.gameId}:page:${Math.min(totalPages - 1, page + 1)}`)
        .setLabel('▶')
        .setStyle(ButtonStyle.Secondary)
        .setDisabled(page >= totalPages - 1)
    );
  }
  if (navRow.components.length > 0) {
    rows.push(navRow);
  }

  return rows.slice(0, 5);
}

export function pickTriviaQuestion(): (typeof TRIVIA_QUESTIONS)[number] {
  return TRIVIA_QUESTIONS[randomInt(0, TRIVIA_QUESTIONS.length - 1)]!;
}

export function buildTriviaEmbed(
  locale: Locale,
  question: (typeof TRIVIA_QUESTIONS)[number],
  userId: string
): APIEmbed {
  const options = question.options[locale]
    .map((option, index) => `${String.fromCharCode(65 + index)}. ${option}`)
    .join('\n');

  return {
    title: tf(locale, 'fun.trivia.title', {}),
    description: tf(locale, 'fun.trivia.question', {
      question: question.q[locale],
      options
    }),
    footer: { text: tf(locale, 'fun.trivia.footer', { user: `<@${userId}>` }) },
    color: 0x6366f1
  };
}

export function buildTriviaComponents(
  userId: string,
  correctIndex: number,
  locale: Locale,
  question: (typeof TRIVIA_QUESTIONS)[number]
): ActionRowBuilder<ButtonBuilder>[] {
  const labels = ['A', 'B', 'C', 'D'];
  const row = new ActionRowBuilder<ButtonBuilder>();
  for (let index = 0; index < question.options[locale].length; index += 1) {
    row.addComponents(
      new ButtonBuilder()
        .setCustomId(`${FUN_BUTTON_PREFIX}trivia:${userId}:${index}:${correctIndex}`)
        .setLabel(`${labels[index]!}. ${question.options[locale][index]!.slice(0, 20)}`)
        .setStyle(ButtonStyle.Primary)
    );
  }
  return [row];
}

export function parseTriviaButton(customId: string): {
  userId: string;
  answerIndex: number;
  correctIndex: number;
} | null {
  const parts = customId.split(':');
  if (parts.length !== 5 || parts[0] !== 'fun' || parts[1] !== 'trivia') {
    return null;
  }
  const userId = parts[2];
  const answerIndex = Number(parts[3]);
  const correctIndex = Number(parts[4]);
  if (!userId || Number.isNaN(answerIndex) || Number.isNaN(correctIndex)) {
    return null;
  }
  return { userId, answerIndex, correctIndex };
}

export function parseTicTacToeButton(customId: string): { gameId: string; cell: number } | null {
  const parts = customId.split(':');
  if (parts.length !== 4 || parts[0] !== 'fun' || parts[1] !== 'ttt') {
    return null;
  }
  const gameId = parts[2];
  const cell = Number(parts[3]);
  if (!gameId || Number.isNaN(cell) || cell < 0 || cell > 8) {
    return null;
  }
  return { gameId, cell };
}

export function parseConnect4Button(customId: string): { gameId: string; col: number } | null {
  const parts = customId.split(':');
  if (parts.length !== 4 || parts[0] !== 'fun' || parts[1] !== 'c4') {
    return null;
  }
  const gameId = parts[2];
  const col = Number(parts[3]);
  if (!gameId || Number.isNaN(col) || col < 0 || col > 6) {
    return null;
  }
  return { gameId, col };
}

export function parseHangmanButton(
  customId: string
): { gameId: string; letter?: string; page?: number } | null {
  const parts = customId.split(':');
  if (parts.length < 4 || parts[0] !== 'fun' || parts[1] !== 'hm') {
    return null;
  }
  const gameId = parts[2];
  if (!gameId) {
    return null;
  }
  if (parts[3] === 'page') {
    const page = Number(parts[4]);
    if (Number.isNaN(page) || page < 0) {
      return null;
    }
    return { gameId, page };
  }
  const letter = parts[3]?.toUpperCase();
  if (!letter || !/^[A-Z]$/.test(letter)) {
    return null;
  }
  return { gameId, letter };
}
