export type PlayerColor = 'black' | 'white';
export type GameStatus = 'waiting' | 'playing' | 'finished';
export type BoardCell = null | 'black' | 'white';

export class GomokuGame {
  public readonly board: BoardCell[][];
  public currentPlayerColor: null | PlayerColor;
  public status: GameStatus;
  public winnerColor: null | PlayerColor | 'draw';
  public history: { row: number; col: number }[];

  private readonly boardSize = 15;
  private moveCount = 0;

  constructor() {
    this.board = Array.from({ length: this.boardSize }, () =>
      Array(this.boardSize).fill(null),
    );
    this.currentPlayerColor = 'black';
    this.status = 'waiting';
    this.winnerColor = null;
    this.history = [];
  }

  public makeMove(
    playerColor: PlayerColor,
    row: number,
    col: number,
  ): { success: boolean; error?: string } {
    // Validate it's the player's turn
    if (this.currentPlayerColor !== playerColor) {
      return { success: false, error: 'Not your turn' };
    }

    // Validate move is within bounds
    if (row < 0 || row >= this.boardSize || col < 0 || col >= this.boardSize) {
      return { success: false, error: 'Move out of bounds' };
    }

    // Validate cell is empty
    if (this.board[row][col] !== null) {
      return { success: false, error: 'Cell already occupied' };
    }

    // Make the move
    this.board[row][col] = playerColor;
    this.moveCount++;

    // If this was the first move, declare the game to have begun
    if (this.status === 'waiting') {
      this.status = 'playing';
    }

    // Add the move to the game's history
    this.history.push({ row, col });

    // Check for win condition
    if (this.checkWin(row, col, playerColor)) {
      this.winnerColor = playerColor;
      this.status = 'finished';
      this.currentPlayerColor = null;
      return { success: true };
    }

    // Check for draw condition
    if (this.moveCount === this.boardSize * this.boardSize) {
      this.winnerColor = 'draw';
      this.status = 'finished';
      this.currentPlayerColor = null;
      return { success: true };
    }

    // Switch turns
    this.currentPlayerColor = playerColor === 'black' ? 'white' : 'black';

    return { success: true };
  }

  public resign(resigningPlayerColor: PlayerColor) {
    this.winnerColor = resigningPlayerColor === 'white' ? 'black' : 'white';
    this.status = 'finished';
    this.currentPlayerColor = null;
  }

  public undoMove() {
    if (this.history.length === 0) {
      return;
    }

    const lastMove = this.history.pop();
    if (lastMove) {
      this.board[lastMove.row][lastMove.col] = null;
      this.moveCount--;
      this.currentPlayerColor =
        this.currentPlayerColor === 'black' ? 'white' : 'black';
      if (this.status === 'finished') {
        this.status = 'playing';
        this.winnerColor = null;
      }
    }
  }

  public declareDraw() {
    this.status = 'finished';
    this.winnerColor = 'draw';
    this.currentPlayerColor = null;
  }

  private checkWin(row: number, col: number, color: PlayerColor): boolean {
    const directions = [
      [1, 0], // vertical
      [0, 1], // horizontal
      [1, 1], // diagonal down-right
      [1, -1], // diagonal down-left
    ];

    for (const [dx, dy] of directions) {
      let count = 1;

      // Check in positive direction
      for (let i = 1; i < 5; i++) {
        const newRow = row + dx * i;
        const newCol = col + dy * i;
        if (
          newRow < 0 ||
          newRow >= this.boardSize ||
          newCol < 0 ||
          newCol >= this.boardSize
        ) {
          break;
        }
        if (this.board[newRow][newCol] === color) {
          count++;
        } else {
          break;
        }
      }

      // Check in negative direction
      for (let i = 1; i < 5; i++) {
        const newRow = row - dx * i;
        const newCol = col - dy * i;
        if (
          newRow < 0 ||
          newRow >= this.boardSize ||
          newCol < 0 ||
          newCol >= this.boardSize
        ) {
          break;
        }
        if (this.board[newRow][newCol] === color) {
          count++;
        } else {
          break;
        }
      }

      if (count >= 5) {
        return true;
      }
    }

    return false;
  }
}
