local MonkeyGames = LibStub("AceAddon-3.0"):GetAddon("MonkeyGames")
local Chess = MonkeyGames.Chess

local ChessAI = {}
MonkeyGames.ChessAI = ChessAI

-- ========================================================================
-- Piece values (centipawns)
-- ========================================================================

local PIECE_VALUES = {
    P = 100,
    N = 320,
    B = 330,
    R = 500,
    Q = 900,
    K = 20000,
}

-- ========================================================================
-- Piece-square tables (from white's perspective, row 1 = rank 8)
-- Values in centipawns, added to piece material value
-- ========================================================================

local PST = {}

PST.P = {
    {  0,  0,  0,  0,  0,  0,  0,  0 }, -- rank 8 (never a pawn here)
    { 50, 50, 50, 50, 50, 50, 50, 50 }, -- rank 7
    { 10, 10, 20, 30, 30, 20, 10, 10 }, -- rank 6
    {  5,  5, 10, 25, 25, 10,  5,  5 }, -- rank 5
    {  0,  0,  0, 20, 20,  0,  0,  0 }, -- rank 4
    {  5, -5,-10,  0,  0,-10, -5,  5 }, -- rank 3
    {  5, 10, 10,-20,-20, 10, 10,  5 }, -- rank 2
    {  0,  0,  0,  0,  0,  0,  0,  0 }, -- rank 1
}

PST.N = {
    {-50,-40,-30,-30,-30,-30,-40,-50 },
    {-40,-20,  0,  0,  0,  0,-20,-40 },
    {-30,  0, 10, 15, 15, 10,  0,-30 },
    {-30,  5, 15, 20, 20, 15,  5,-30 },
    {-30,  0, 15, 20, 20, 15,  0,-30 },
    {-30,  5, 10, 15, 15, 10,  5,-30 },
    {-40,-20,  0,  5,  5,  0,-20,-40 },
    {-50,-40,-30,-30,-30,-30,-40,-50 },
}

PST.B = {
    {-20,-10,-10,-10,-10,-10,-10,-20 },
    {-10,  0,  0,  0,  0,  0,  0,-10 },
    {-10,  0, 10, 10, 10, 10,  0,-10 },
    {-10,  5,  5, 10, 10,  5,  5,-10 },
    {-10,  0,  5, 10, 10,  5,  0,-10 },
    {-10, 10,  5, 10, 10,  5, 10,-10 },
    {-10,  5,  0,  0,  0,  0,  5,-10 },
    {-20,-10,-10,-10,-10,-10,-10,-20 },
}

PST.R = {
    {  0,  0,  0,  0,  0,  0,  0,  0 },
    {  5, 10, 10, 10, 10, 10, 10,  5 },
    { -5,  0,  0,  0,  0,  0,  0, -5 },
    { -5,  0,  0,  0,  0,  0,  0, -5 },
    { -5,  0,  0,  0,  0,  0,  0, -5 },
    { -5,  0,  0,  0,  0,  0,  0, -5 },
    { -5,  0,  0,  0,  0,  0,  0, -5 },
    {  0,  0,  0,  5,  5,  0,  0,  0 },
}

PST.Q = {
    {-20,-10,-10, -5, -5,-10,-10,-20 },
    {-10,  0,  0,  0,  0,  0,  0,-10 },
    {-10,  0,  5,  5,  5,  5,  0,-10 },
    { -5,  0,  5,  5,  5,  5,  0, -5 },
    {  0,  0,  5,  5,  5,  5,  0, -5 },
    {-10,  5,  5,  5,  5,  5,  0,-10 },
    {-10,  0,  5,  0,  0,  0,  0,-10 },
    {-20,-10,-10, -5, -5,-10,-10,-20 },
}

PST.K = {
    {-30,-40,-40,-50,-50,-40,-40,-30 },
    {-30,-40,-40,-50,-50,-40,-40,-30 },
    {-30,-40,-40,-50,-50,-40,-40,-30 },
    {-30,-40,-40,-50,-50,-40,-40,-30 },
    {-20,-30,-30,-40,-40,-30,-30,-20 },
    {-10,-20,-20,-20,-20,-20,-20,-10 },
    { 20, 20,  0,  0,  0,  0, 20, 20 },
    { 20, 30, 10,  0,  0, 10, 30, 20 },
}

-- ========================================================================
-- Board evaluation
-- ========================================================================

--- Evaluate the board from white's perspective.
-- Positive = white advantage, negative = black advantage.
function ChessAI:Evaluate(state)
    local score = 0
    local board = state.board

    for row = 1, 8 do
        for col = 1, 8 do
            local piece = board[row][col]
            if piece ~= "" then
                local color = piece:sub(1, 1)
                local ptype = piece:sub(2, 2)
                local material = PIECE_VALUES[ptype] or 0

                -- Piece-square table bonus
                local pst = PST[ptype]
                local pstBonus = 0
                if pst then
                    if color == "w" then
                        pstBonus = pst[row][col]
                    else
                        -- Mirror vertically for black
                        pstBonus = pst[9 - row][col]
                    end
                end

                local pieceScore = material + pstBonus
                if color == "w" then
                    score = score + pieceScore
                else
                    score = score - pieceScore
                end
            end
        end
    end

    return score
end

-- ========================================================================
-- Move ordering (for better alpha-beta pruning)
-- ========================================================================

function ChessAI:MoveOrderScore(state, move)
    local score = 0
    local board = state.board

    -- Captures: MVV-LVA (Most Valuable Victim - Least Valuable Attacker)
    local captured = board[move.toRow][move.toCol]
    if captured ~= "" then
        local victimValue = PIECE_VALUES[captured:sub(2, 2)] or 0
        local attackerValue = PIECE_VALUES[board[move.fromRow][move.fromCol]:sub(2, 2)] or 0
        score = score + (10 * victimValue - attackerValue)
    end

    -- En passant captures
    if move.enPassant then
        score = score + 900 -- treat like pawn capture, high priority
    end

    -- Promotions
    if move.promotion then
        score = score + (PIECE_VALUES[move.promotion] or 0)
    end

    return score
end

function ChessAI:OrderMoves(state, moves)
    -- Compute scores
    local scores = {}
    for i, move in ipairs(moves) do
        scores[i] = self:MoveOrderScore(state, move)
    end

    -- Simple insertion sort (good enough for ~30 moves)
    for i = 2, #moves do
        local j = i
        while j > 1 and scores[j] > scores[j - 1] do
            moves[j], moves[j - 1] = moves[j - 1], moves[j]
            scores[j], scores[j - 1] = scores[j - 1], scores[j]
            j = j - 1
        end
    end
end

-- ========================================================================
-- Minimax with alpha-beta pruning
-- ========================================================================

local DEPTH_BY_DIFFICULTY = { 2, 3, 4 }
local INF = 999999

function ChessAI:GetBestMove(state, difficulty)
    local depth = DEPTH_BY_DIFFICULTY[difficulty] or 3
    local isMaximizing = (state.turn == "w")

    local moves = Chess:GetAllLegalMoves(state)
    if #moves == 0 then return nil end

    -- For Easy difficulty, add some randomness
    if difficulty == 1 and #moves > 1 then
        -- Shuffle first to add variety
        for i = #moves, 2, -1 do
            local j = math.random(i)
            moves[i], moves[j] = moves[j], moves[i]
        end
    end

    self:OrderMoves(state, moves)

    local bestMove = moves[1]
    local bestScore = isMaximizing and -INF or INF

    for _, move in ipairs(moves) do
        local testState = Chess:CopyState(state)
        Chess:ApplyMoveRaw(testState, move)

        local score = self:Minimax(testState, depth - 1, -INF, INF, not isMaximizing)

        if isMaximizing then
            if score > bestScore then
                bestScore = score
                bestMove = move
            end
        else
            if score < bestScore then
                bestScore = score
                bestMove = move
            end
        end
    end

    return bestMove
end

function ChessAI:Minimax(state, depth, alpha, beta, isMaximizing)
    -- Terminal checks
    local status = Chess:GetGameStatus(state)
    if status == "checkmate" then
        -- Current side is checkmated
        if isMaximizing then
            return -20000 - depth -- prefer faster checkmate
        else
            return 20000 + depth
        end
    elseif status == "stalemate" or status:find("draw") then
        return 0
    end

    if depth == 0 then
        return self:Evaluate(state)
    end

    local moves = Chess:GetAllLegalMoves(state)
    self:OrderMoves(state, moves)

    if isMaximizing then
        local maxEval = -INF
        for _, move in ipairs(moves) do
            local testState = Chess:CopyState(state)
            Chess:ApplyMoveRaw(testState, move)
            local eval = self:Minimax(testState, depth - 1, alpha, beta, false)
            if eval > maxEval then maxEval = eval end
            if eval > alpha then alpha = eval end
            if beta <= alpha then break end
        end
        return maxEval
    else
        local minEval = INF
        for _, move in ipairs(moves) do
            local testState = Chess:CopyState(state)
            Chess:ApplyMoveRaw(testState, move)
            local eval = self:Minimax(testState, depth - 1, alpha, beta, true)
            if eval < minEval then minEval = eval end
            if eval < beta then beta = eval end
            if beta <= alpha then break end
        end
        return minEval
    end
end
