package de.nowchess.bot

import de.nowchess.api.board.{Board, Color, File, Piece, PieceType, Rank, Square}
import de.nowchess.api.game.GameContext
import de.nowchess.api.move.{Move, MoveType, PromotionPiece}
import org.scalatest.funsuite.AnyFunSuite
import org.scalatest.matchers.should.Matchers

class MoveOrderingTest extends AnyFunSuite with Matchers:

  test("queen capture ranks higher than rook capture"):
    val board = Board(Map(
      Square(File.E, Rank.R4) -> Piece.WhiteQueen,
      Square(File.E, Rank.R5) -> Piece.BlackQueen,
      Square(File.E, Rank.R6) -> Piece.BlackRook
    ))
    val context = GameContext.initial.withBoard(board).withTurn(Color.White)
    val queenCapture = Move(Square(File.E, Rank.R4), Square(File.E, Rank.R5), MoveType.Normal(true))
    val rookCapture = Move(Square(File.E, Rank.R4), Square(File.E, Rank.R6), MoveType.Normal(true))

    val queenScore = MoveOrdering.score(context, queenCapture, None)
    val rookScore = MoveOrdering.score(context, rookCapture, None)
    queenScore should be > rookScore

  test("quiet move ranks lower than capture"):
    val board = Board(Map(
      Square(File.E, Rank.R4) -> Piece.WhiteQueen,
      Square(File.E, Rank.R5) -> Piece.BlackPawn
    ))
    val context = GameContext.initial.withBoard(board).withTurn(Color.White)
    val capture = Move(Square(File.E, Rank.R4), Square(File.E, Rank.R5), MoveType.Normal(true))
    val quiet = Move(Square(File.E, Rank.R4), Square(File.D, Rank.R5), MoveType.Normal(false))

    val captureScore = MoveOrdering.score(context, capture, None)
    val quietScore = MoveOrdering.score(context, quiet, None)
    captureScore should be > quietScore

  test("TT best move ranks first"):
    val board = Board(Map(
      Square(File.E, Rank.R4) -> Piece.WhiteQueen,
      Square(File.E, Rank.R5) -> Piece.BlackPawn,
      Square(File.D, Rank.R5) -> Piece.BlackPawn
    ))
    val context = GameContext.initial.withBoard(board).withTurn(Color.White)
    val bestMove = Move(Square(File.E, Rank.R4), Square(File.D, Rank.R5), MoveType.Normal(true))
    val otherCapture = Move(Square(File.E, Rank.R4), Square(File.E, Rank.R5), MoveType.Normal(true))

    val bestScore = MoveOrdering.score(context, bestMove, Some(bestMove))
    val otherScore = MoveOrdering.score(context, otherCapture, Some(bestMove))
    bestScore should equal(Int.MaxValue)
    otherScore should be < bestScore

  test("promotion to queen ranks high"):
    val board = Board(Map(
      Square(File.E, Rank.R7) -> Piece.WhitePawn
    ))
    val context = GameContext.initial.withBoard(board).withTurn(Color.White)
    val promotionQueen = Move(Square(File.E, Rank.R7), Square(File.E, Rank.R8), MoveType.Promotion(PromotionPiece.Queen))
    val promotionKnight = Move(Square(File.E, Rank.R7), Square(File.E, Rank.R8), MoveType.Promotion(PromotionPiece.Knight))

    val queenScore = MoveOrdering.score(context, promotionQueen, None)
    val knightScore = MoveOrdering.score(context, promotionKnight, None)
    queenScore should be > knightScore
    queenScore should be > 100_000  // Queen promotion score is > 100_000

  test("en passant is treated as capture"):
    val board = Board(Map(
      Square(File.E, Rank.R5) -> Piece.WhitePawn,
      Square(File.D, Rank.R5) -> Piece.BlackPawn
    ))
    val context = GameContext.initial.withBoard(board).withTurn(Color.White)
    val epCapture = Move(Square(File.E, Rank.R5), Square(File.D, Rank.R6), MoveType.EnPassant)
    val quiet = Move(Square(File.E, Rank.R5), Square(File.E, Rank.R6), MoveType.Normal(false))

    val epScore = MoveOrdering.score(context, epCapture, None)
    val quietScore = MoveOrdering.score(context, quiet, None)
    epScore should be > quietScore

  test("sort returns moves ordered by score"):
    val board = Board(Map(
      Square(File.E, Rank.R4) -> Piece.WhiteQueen,
      Square(File.E, Rank.R5) -> Piece.BlackPawn,
      Square(File.D, Rank.R5) -> Piece.BlackRook
    ))
    val context = GameContext.initial.withBoard(board).withTurn(Color.White)
    val moves = List(
      Move(Square(File.E, Rank.R4), Square(File.D, Rank.R5), MoveType.Normal(true)),  // Rook capture
      Move(Square(File.E, Rank.R4), Square(File.E, Rank.R5), MoveType.Normal(true)),  // Pawn capture
      Move(Square(File.E, Rank.R4), Square(File.E, Rank.R6), MoveType.Normal(false))   // Quiet
    )
    val sorted = MoveOrdering.sort(context, moves, None)
    // Rook capture should be first (higher victim value)
    sorted.head.to should equal(Square(File.D, Rank.R5))
    // Pawn capture should be second
    sorted(1).to should equal(Square(File.E, Rank.R5))
    // Quiet should be last
    sorted.last.to should equal(Square(File.E, Rank.R6))

  test("castling move is quiet (not capture)"):
    val board = Board(Map(
      Square(File.E, Rank.R1) -> Piece.WhiteKing,
      Square(File.H, Rank.R1) -> Piece.WhiteRook
    ))
    val context = GameContext.initial.withBoard(board)
    val castleMove = Move(Square(File.E, Rank.R1), Square(File.G, Rank.R1), MoveType.CastleKingside)
    val score = MoveOrdering.score(context, castleMove, None)
    score should equal(0)  // Quiet move

  test("all MoveType variants are handled in victimValue"):
    val board = Board(Map(
      Square(File.E, Rank.R1) -> Piece.WhiteKing,
      Square(File.H, Rank.R1) -> Piece.WhiteRook,
      Square(File.E, Rank.R2) -> Piece.WhitePawn
    ))
    val context = GameContext.initial.withBoard(board)
    // Test castling queenside - should have victim value 0
    val castleQs = Move(Square(File.E, Rank.R1), Square(File.C, Rank.R1), MoveType.CastleQueenside)
    val scoreQs = MoveOrdering.score(context, castleQs, None)
    scoreQs should equal(0)

  test("attackerValue covers all piece types"):
    val board = Board(Map(
      Square(File.A, Rank.R1) -> Piece.WhiteRook,
      Square(File.B, Rank.R1) -> Piece.WhiteKnight,
      Square(File.C, Rank.R1) -> Piece.WhiteBishop,
      Square(File.D, Rank.R1) -> Piece.WhiteQueen,
      Square(File.E, Rank.R1) -> Piece.WhiteKing,
      Square(File.F, Rank.R2) -> Piece.WhitePawn
    ))
    val context = GameContext.initial.withBoard(board)
    // Create captures with each piece type
    val rookCapture = Move(Square(File.A, Rank.R1), Square(File.A, Rank.R8), MoveType.Normal(true))
    val knightCapture = Move(Square(File.B, Rank.R1), Square(File.A, Rank.R8), MoveType.Normal(true))
    val bishopCapture = Move(Square(File.C, Rank.R1), Square(File.A, Rank.R8), MoveType.Normal(true))
    val queenCapture = Move(Square(File.D, Rank.R1), Square(File.A, Rank.R8), MoveType.Normal(true))
    val kingCapture = Move(Square(File.E, Rank.R1), Square(File.A, Rank.R8), MoveType.Normal(true))
    val pawnCapture = Move(Square(File.F, Rank.R2), Square(File.A, Rank.R8), MoveType.Normal(true))

    // Just verify all are scored without error
    MoveOrdering.score(context, rookCapture, None) should be >= 0
    MoveOrdering.score(context, knightCapture, None) should be >= 0
    MoveOrdering.score(context, bishopCapture, None) should be >= 0
    MoveOrdering.score(context, queenCapture, None) should be >= 0
    MoveOrdering.score(context, kingCapture, None) should be >= 0
    MoveOrdering.score(context, pawnCapture, None) should be >= 0

  test("promotion capture is distinct from quiet promotion"):
    val board = Board(Map(
      Square(File.E, Rank.R7) -> Piece.WhitePawn,
      Square(File.D, Rank.R8) -> Piece.BlackPawn
    ))
    val context = GameContext.initial.withBoard(board)
    // Promotion with capture
    val promotionWithCapture = Move(Square(File.E, Rank.R7), Square(File.D, Rank.R8), MoveType.Promotion(PromotionPiece.Queen))
    // Regular queen promotion (no capture)
    val quietPromotion = Move(Square(File.E, Rank.R7), Square(File.E, Rank.R8), MoveType.Promotion(PromotionPiece.Queen))
    val score1 = MoveOrdering.score(context, promotionWithCapture, None)
    val score2 = MoveOrdering.score(context, quietPromotion, None)
    // Both should score high, but let's just verify they're scored
    score1 should be > 0
    score2 should be > 0
