package de.nowchess.bot.logic

import de.nowchess.api.board.PieceType
import de.nowchess.api.game.GameContext
import de.nowchess.api.move.{Move, MoveType, PromotionPiece}

import scala.collection.mutable

object MoveOrdering:

  class OrderingContext:
    // Killer moves: 2 per ply (depth) to track moves that caused cutoffs
    private val killerMoves = mutable.Map[Int, List[Move]]()

    // History heuristic: tracks how often a move improved alpha
    private val historyTable = mutable.Map[(Int, Int), Int]()

    def addKillerMove(ply: Int, move: Move): Unit =
      val current = killerMoves.getOrElse(ply, List())
      if current.isEmpty || (current.head.from != move.from || current.head.to != move.to) then
        killerMoves(ply) = (move :: current).take(2)

    def getKillerMoves(ply: Int): List[Move] =
      killerMoves.getOrElse(ply, List())

    def addHistory(from: Int, to: Int, bonus: Int): Unit =
      val key = (from, to)
      historyTable(key) = historyTable.getOrElse(key, 0) + bonus

    def getHistory(from: Int, to: Int): Int =
      historyTable.getOrElse((from, to), 0)

    def clear(): Unit =
      killerMoves.clear()
      historyTable.clear()

  /** Score a single move for ordering (higher = search first). */
  def score(
    context: GameContext,
    move: Move,
    ttBestMove: Option[Move],
    ply: Int = 0,
    ordering: OrderingContext = new OrderingContext()
  ): Int =
    if ttBestMove.exists(m => m.from == move.from && m.to == move.to) then
      Int.MaxValue  // TT best move always first
    else
      move.moveType match
        case MoveType.Promotion(PromotionPiece.Queen) =>
          1_000_000  // Queen promotion is always good
        case MoveType.Normal(true) =>
          100_000 + mvvLva(context, move)  // Capture
        case MoveType.EnPassant =>
          100_000 + mvvLva(context, move)  // En passant is a pawn capture
        case MoveType.Promotion(_) =>
          50_000 + mvvLva(context, move)  // Minor/rook/bishop promotion
        case _ =>
          scoreQuietMove(move, ply, ordering)  // Quiet move with history/killer heuristic

  private def scoreQuietMove(move: Move, ply: Int, ordering: OrderingContext): Int =
    val isKiller = ordering.getKillerMoves(ply).exists(k => k.from == move.from && k.to == move.to)
    val fromIdx = move.from.rank.ordinal * 8 + move.from.file.ordinal
    val toIdx = move.to.rank.ordinal * 8 + move.to.file.ordinal
    val history = ordering.getHistory(fromIdx, toIdx)
    if isKiller then 10_000 + (history / 10)
    else history / 10

  /** Sort moves: TT best move first, then by score descending. */
  def sort(
    context: GameContext,
    moves: List[Move],
    ttBestMove: Option[Move],
    ply: Int = 0,
    ordering: OrderingContext = new OrderingContext()
  ): List[Move] =
    moves.sortBy(m => -score(context, m, ttBestMove, ply, ordering))

  /** MVV-LVA score: (victim value * 10) - attacker value.
   *  Higher score = better trade (most valuable victim captured by least valuable attacker). */
  private def mvvLva(context: GameContext, move: Move): Int =
    val victim = victimValue(context, move)
    val attacker = attackerValue(context, move)
    (victim * 10) - attacker

  /** Material value of the attacking piece. */
  private def attackerValue(context: GameContext, move: Move): Int =
    context.board.pieceAt(move.from) match
      case Some(piece) =>
        piece.pieceType match
          case PieceType.Pawn => 1
          case PieceType.Knight => 3
          case PieceType.Bishop => 3
          case PieceType.Rook => 5
          case PieceType.Queen => 9
          case PieceType.King => 200  // King never captures, but include for completeness
      case None => 0

  /** Material value of the captured piece. */
  private def victimValue(context: GameContext, move: Move): Int =
    move.moveType match
      case MoveType.Normal(true) =>
        context.board.pieceAt(move.to) match
          case Some(piece) =>
            piece.pieceType match
              case PieceType.Pawn => 1
              case PieceType.Knight => 3
              case PieceType.Bishop => 3
              case PieceType.Rook => 5
              case PieceType.Queen => 9
              case PieceType.King => 200
          case None => 0
      case MoveType.EnPassant => 1  // En passant captures a pawn
      case MoveType.Promotion(_) => 0  // Promotion is not a capture (destination is empty)
      case _ => 0
