/**
 * @jest-environment jsdom
 */

import { Knight } from '../../../js/pieces/Knight.js';
import { Board } from '../../../js/game/Board.js';

describe('Knight', () => {
  let board;

  beforeEach(() => {
    board = new Board();
    board.clear();
  });

  describe('L-Shaped Movement', () => {
    test('knight in center can move to all 8 positions', () => {
      const knight = new Knight('white', { row: 4, col: 4 });
      board.setPiece(4, 4, knight);

      const moves = knight.getValidMoves(board);

      const expectedMoves = [
        { row: 2, col: 3 }, // Up 2, Left 1
        { row: 2, col: 5 }, // Up 2, Right 1
        { row: 3, col: 2 }, // Up 1, Left 2
        { row: 3, col: 6 }, // Up 1, Right 2
        { row: 5, col: 2 }, // Down 1, Left 2
        { row: 5, col: 6 }, // Down 1, Right 2
        { row: 6, col: 3 }, // Down 2, Left 1
        { row: 6, col: 5 }  // Down 2, Right 1
      ];

      expect(moves).toHaveLength(8);
      expectedMoves.forEach(expected => {
        expect(moves).toContainEqual(expected);
      });
    });

    test('knight in corner has limited moves', () => {
      const knight = new Knight('white', { row: 0, col: 0 });
      board.setPiece(0, 0, knight);

      const moves = knight.getValidMoves(board);

      const expectedMoves = [
        { row: 1, col: 2 },
        { row: 2, col: 1 }
      ];

      expect(moves).toHaveLength(2);
      expectedMoves.forEach(expected => {
        expect(moves).toContainEqual(expected);
      });
    });

    test('knight on edge has restricted moves', () => {
      const knight = new Knight('white', { row: 4, col: 0 });
      board.setPiece(4, 0, knight);

      const moves = knight.getValidMoves(board);

      // Can only move to right side
      expect(moves).toHaveLength(4);
      moves.forEach(move => {
        expect(move.col).toBeGreaterThan(0);
      });
    });

    test('knight moves exactly 2+1 squares', () => {
      const knight = new Knight('white', { row: 4, col: 4 });
      board.setPiece(4, 4, knight);

      const moves = knight.getValidMoves(board);

      moves.forEach(move => {
        const rowDiff = Math.abs(move.row - 4);
        const colDiff = Math.abs(move.col - 4);

        // L-shape: either (2,1) or (1,2)
        const isLShape = (rowDiff === 2 && colDiff === 1) || (rowDiff === 1 && colDiff === 2);
        expect(isLShape).toBe(true);
      });
    });
  });

  describe('Jumping Over Pieces', () => {
    test('knight can jump over own pieces', () => {
      const knight = new Knight('white', { row: 7, col: 1 });
      const blockingPawn = { type: 'pawn', color: 'white', position: { row: 6, col: 1 } };

      board.setPiece(7, 1, knight);
      board.setPiece(6, 1, blockingPawn);

      const moves = knight.getValidMoves(board);

      // Should still be able to move despite blocked adjacent squares
      expect(moves.length).toBeGreaterThan(0);
      expect(moves).toContainEqual({ row: 5, col: 0 });
      expect(moves).toContainEqual({ row: 5, col: 2 });
    });

    test('knight can jump over opponent pieces', () => {
      const knight = new Knight('white', { row: 4, col: 4 });

      // Surround knight with pieces
      board.setPiece(4, 4, knight);
      board.setPiece(3, 4, { type: 'pawn', color: 'black', position: { row: 3, col: 4 } });
      board.setPiece(5, 4, { type: 'pawn', color: 'black', position: { row: 5, col: 4 } });
      board.setPiece(4, 3, { type: 'pawn', color: 'black', position: { row: 4, col: 3 } });
      board.setPiece(4, 5, { type: 'pawn', color: 'black', position: { row: 4, col: 5 } });

      const moves = knight.getValidMoves(board);

      // Should still have all 8 moves available
      expect(moves).toHaveLength(8);
    });

    test('knight trapped by own pieces on destination squares', () => {
      const knight = new Knight('white', { row: 7, col: 1 });

      board.setPiece(7, 1, knight);
      // Place white pieces on all possible destinations
      board.setPiece(6, 3, { type: 'pawn', color: 'white', position: { row: 6, col: 3 } });
      board.setPiece(5, 0, { type: 'pawn', color: 'white', position: { row: 5, col: 0 } });
      board.setPiece(5, 2, { type: 'pawn', color: 'white', position: { row: 5, col: 2 } });

      const moves = knight.getValidMoves(board);

      expect(moves).toHaveLength(0);
    });
  });

  describe('Capture Mechanics', () => {
    test('knight can capture opponent pieces', () => {
      const knight = new Knight('white', { row: 4, col: 4 });
      const blackPawn = { type: 'pawn', color: 'black', position: { row: 2, col: 3 } };

      board.setPiece(4, 4, knight);
      board.setPiece(2, 3, blackPawn);

      const moves = knight.getValidMoves(board);

      expect(moves).toContainEqual({ row: 2, col: 3 });
    });

    test('knight cannot capture own pieces', () => {
      const knight = new Knight('white', { row: 4, col: 4 });
      const whitePawn = { type: 'pawn', color: 'white', position: { row: 2, col: 3 } };

      board.setPiece(4, 4, knight);
      board.setPiece(2, 3, whitePawn);

      const moves = knight.getValidMoves(board);

      expect(moves).not.toContainEqual({ row: 2, col: 3 });
    });

    test('knight can capture multiple opponent pieces', () => {
      const knight = new Knight('white', { row: 4, col: 4 });

      board.setPiece(4, 4, knight);
      board.setPiece(2, 3, { type: 'pawn', color: 'black', position: { row: 2, col: 3 } });
      board.setPiece(2, 5, { type: 'pawn', color: 'black', position: { row: 2, col: 5 } });
      board.setPiece(6, 3, { type: 'pawn', color: 'black', position: { row: 6, col: 3 } });

      const moves = knight.getValidMoves(board);

      expect(moves).toContainEqual({ row: 2, col: 3 });
      expect(moves).toContainEqual({ row: 2, col: 5 });
      expect(moves).toContainEqual({ row: 6, col: 3 });
    });
  });

  describe('Board Boundaries', () => {
    test('knight at a1 (bottom-left corner)', () => {
      const knight = new Knight('white', { row: 7, col: 0 });
      board.setPiece(7, 0, knight);

      const moves = knight.getValidMoves(board);

      expect(moves).toHaveLength(2);
      expect(moves).toContainEqual({ row: 6, col: 2 });
      expect(moves).toContainEqual({ row: 5, col: 1 });
    });

    test('knight at h8 (top-right corner)', () => {
      const knight = new Knight('black', { row: 0, col: 7 });
      board.setPiece(0, 7, knight);

      const moves = knight.getValidMoves(board);

      expect(moves).toHaveLength(2);
      expect(moves).toContainEqual({ row: 1, col: 5 });
      expect(moves).toContainEqual({ row: 2, col: 6 });
    });

    test('knight moves stay within board bounds', () => {
      // Test all board positions
      for (let row = 0; row < 8; row++) {
        for (let col = 0; col < 8; col++) {
          const knight = new Knight('white', { row, col });
          board.clear();
          board.setPiece(row, col, knight);

          const moves = knight.getValidMoves(board);

          moves.forEach(move => {
            expect(move.row).toBeGreaterThanOrEqual(0);
            expect(move.row).toBeLessThan(8);
            expect(move.col).toBeGreaterThanOrEqual(0);
            expect(move.col).toBeLessThan(8);
          });
        }
      }
    });
  });

  describe('Special Positions', () => {
    test('knight fork - attacking two pieces simultaneously', () => {
      const knight = new Knight('white', { row: 4, col: 4 });
      const blackKing = { type: 'king', color: 'black', position: { row: 2, col: 3 } };
      const blackQueen = { type: 'queen', color: 'black', position: { row: 2, col: 5 } };

      board.setPiece(4, 4, knight);
      board.setPiece(2, 3, blackKing);
      board.setPiece(2, 5, blackQueen);

      const moves = knight.getValidMoves(board);

      // Knight can attack both king and queen
      expect(moves).toContainEqual({ row: 2, col: 3 });
      expect(moves).toContainEqual({ row: 2, col: 5 });
    });

    test('knight starting positions from initial board', () => {
      board = new Board();
      board.setupInitialPosition();

      const whiteKnight1 = board.getPiece(7, 1);
      const whiteKnight2 = board.getPiece(7, 6);
      const blackKnight1 = board.getPiece(0, 1);
      const blackKnight2 = board.getPiece(0, 6);

      expect(whiteKnight1.type).toBe('knight');
      expect(whiteKnight2.type).toBe('knight');
      expect(blackKnight1.type).toBe('knight');
      expect(blackKnight2.type).toBe('knight');

      // From starting position, each knight has 2 possible moves
      expect(whiteKnight1.getValidMoves(board)).toHaveLength(2);
      expect(whiteKnight2.getValidMoves(board)).toHaveLength(2);
    });
  });
});
