import { GomokuGame } from './game-instance';
import { expect, test, describe } from 'bun:test';

describe('GameInstance', () => {
  test('should initialize with correct default state', () => {
    const game = new GomokuGame();

    expect(game.id).toBeDefined();
    expect(game.board.length).toBe(15);
    expect(game.board[0].length).toBe(15);
    expect(game.currentPlayer).toBeNull();
    expect(game.status).toBe('waiting');
    expect(game.winner).toBeNull();
    expect(game.players).toEqual({});
  });

  test('should add players correctly', () => {
    const game = new GomokuGame();

    const player1 = 'player1-uuid';
    const player2 = 'player2-uuid';

    // First player joins as black
    const joined1 = game.addPlayer(player1);
    expect(joined1).toBe(true);

    // Second player joins as white
    const joined2 = game.addPlayer(player2);
    expect(joined2).toBe(true);

    // Game should now be in playing state
    expect(game.status).toBe('playing');
    expect(game.currentPlayer).toBe('black');
    expect(game.players.black).toBe(player1);
    expect(game.players.white).toBe(player2);
  });

  test('should prevent more than two players from joining', () => {
    const game = new GomokuGame();

    const player1 = 'player1-uuid';
    const player2 = 'player2-uuid';
    const player3 = 'player3-uuid';

    game.addPlayer(player1);
    game.addPlayer(player2);

    // Third player tries to join (should fail)
    const joined = game.addPlayer(player3);
    expect(joined).toBe(false);

    // Players object should remain unchanged, only two players should be present
    expect(game.players.black).toBe(player1);
    expect(game.players.white).toBe(player2);
    expect(Object.values(game.players).length).toBe(2);
  });

  test('should validate moves correctly', () => {
    const game = new GomokuGame();

    const player1 = 'player1-uuid';
    const player2 = 'player2-uuid';

    game.addPlayer(player1);
    game.addPlayer(player2);

    // Player black makes first move
    const move1 = game.makeMove(player1, 7, 7);
    expect(move1.success).toBe(true);

    // Same player tries to move again (should fail)
    const move2 = game.makeMove(player1, 7, 8);
    expect(move2.success).toBe(false);

    // White player makes a move
    const move3 = game.makeMove(player2, 7, 8);
    expect(move3.success).toBe(true);

    // Try to place on occupied cell (should fail)
    const move4 = game.makeMove(player2, 7, 7);
    expect(move4.success).toBe(false);

    // Try to place out of bounds (should fail)
    const move5 = game.makeMove(player2, 15, 15);
    expect(move5.success).toBe(false);
  });

  test('should detect win conditions', () => {
    const game = new GomokuGame();

    const player1 = 'player1-uuid';
    const player2 = 'player2-uuid';

    game.addPlayer(player1);
    game.addPlayer(player2);

    // Create a horizontal win for black
    for (let col = 0; col < 5; col++) {
      game.makeMove(player1, 7, col);
      // Switch to other player for next move
      if (col < 4) game.makeMove(player2, 8, col);
    }

    expect(game.winner).toBe('black');
    expect(game.status).toBe('finished');
  });

  test('should detect draw condition', () => {
    const game = new GomokuGame();

    const player1 = 'player1-uuid';
    const player2 = 'player2-uuid';

    game.addPlayer(player1);
    game.addPlayer(player2);

    // Create a pattern that doesn't result in a win but fills the board
    // We'll use a simple alternating pattern
    for (let row = 0; row < 15; row++) {
      for (let col = 0; col < 15; col++) {
        const currentPlayer = game.currentPlayer!;
        const playerId = game.players[currentPlayer]!;

        // Make move
        const result = game.makeMove(playerId, row, col);

        // If we can't make a move, it means someone won already
        if (!result.success) {
          expect(game.winner).not.toBeNull();
          return;
        }
      }
    }

    expect(game.winner).toBe('draw');
    expect(game.status).toBe('finished');
  });
});
