package fr.iut_fbleau.HexGame;

import fr.iut_fbleau.GameAPI.*;

public class MiniMaxBot extends AbstractGamePlayer {

    private int MAXDEPTH = 5;

    /**
     * En dessous (ou égal) à ce nombre de coups restants (cases vides),
     * on considère que l'arbre est "petit" et on fait un alpha-bêta simple :
     * pas de cut-off, on explore jusqu'aux positions terminales (isGameOver()).
     */
    private int SMALL_TREE_MOVE_LIMIT = 6;

    public MiniMaxBot(Player me) {
        super(me);  // Correct constructor usage
    }

    public Boolean jesuisMinimax(){
        return true;
    }

    @Override
    public AbstractPly giveYourMove(IBoard board) {
        HexBoard hb = (HexBoard) board;
        float bestScore = -Float.MAX_VALUE;
        HexPly bestMove = null;

        // Détermine si l'arbre est petit ou grand
        int movesLeft = countLegalMoves(hb);
        boolean useCutoff = (movesLeft > SMALL_TREE_MOVE_LIMIT);
        int depthToUse = useCutoff ? MAXDEPTH : 0; // 0 car en "simple" on ne coupe pas sur depth

        for (int i = 0; i < hb.getSize(); i++) {
            for (int j = 0; j < hb.getSize(); j++) {
                HexPly move = new HexPly(hb.getCurrentPlayer(), i, j);
                if (hb.isLegal(move)) {
                    hb.doPly(move);

                    float score = minimax(hb, depthToUse, -Float.MAX_VALUE, Float.MAX_VALUE, true, useCutoff);

                    if (score > bestScore) {
                        bestScore = score;
                        bestMove = move;
                    }
                    hb.undoPly();
                }
            }
        }
        return bestMove;
    }

    /**
     * Minimax + alpha-bêta
     * - useCutoff = false : alpha-bêta "simple" -> on s'arrête uniquement sur isGameOver()
     * - useCutoff = true  : alpha-bêta avec cut-off -> arrêt si depth == 0 (heuristique)
     */
    private float minimax(HexBoard board, int depth, float alpha, float beta, boolean isMaximizing, boolean useCutoff) {
        // Toujours prioritaire : position terminale
        if (board.isGameOver()) {
            return terminalScore(board);
        }

        // Cut-off uniquement si demandé
        if (useCutoff && depth == 0) {
            return evaluateBoard(board);
        }

        if (isMaximizing) {
            float bestScore = -Float.MAX_VALUE;
            for (int i = 0; i < board.getSize(); i++) {
                for (int j = 0; j < board.getSize(); j++) {
                    HexPly move = new HexPly(board.getCurrentPlayer(), i, j);
                    if (board.isLegal(move)) {
                        board.doPly(move);

                        int nextDepth = useCutoff ? (depth - 1) : depth;
                        float score = minimax(board, nextDepth, alpha, beta, false, useCutoff);

                        bestScore = Math.max(bestScore, score);
                        alpha = Math.max(alpha, bestScore);

                        if (beta <= alpha) {
                            board.undoPly();
                            break; // Pruning
                        }

                        board.undoPly();
                    }
                }
            }
            return bestScore;
        } else {
            float bestScore = Float.MAX_VALUE;
            for (int i = 0; i < board.getSize(); i++) {
                for (int j = 0; j < board.getSize(); j++) {
                    HexPly move = new HexPly(board.getCurrentPlayer(), i, j);
                    if (board.isLegal(move)) {
                        board.doPly(move);

                        int nextDepth = useCutoff ? (depth - 1) : depth;
                        float score = minimax(board, nextDepth, alpha, beta, true, useCutoff);

                        bestScore = Math.min(bestScore, score);
                        beta = Math.min(beta, bestScore);

                        if (beta <= alpha) {
                            board.undoPly();
                            break; // Pruning
                        }

                        board.undoPly();
                    }
                }
            }
            return bestScore;
        }
    }

    /**
     * Score terminal (feuille) du point de vue de PLAYER1 :
     * - WIN  : PLAYER1 gagne
     * - LOSS : PLAYER1 perd
     */
    private float terminalScore(HexBoard board) {
        Result r = board.getResult();
        if (r == null) return 0f;
        if (r == Result.WIN) return 1000000f;
        return -1000000f;
    }

    /**
     * Heuristique actuelle (inchangée) : distance au centre des pions PLAYER1.
     * (Je ne la modifie pas pour ne pas toucher à la logique existante.)
     */
    private float evaluateBoard(HexBoard board) {
        int size = board.getSize();
        int center = size / 2;
        int score = 0;
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                if (board.getCellPlayer(i, j) == Player.PLAYER1) {
                    score += Math.abs(i - center) + Math.abs(j - center); // Distance from center
                }
            }
        }
        return score;
    }

    /**
     * Compte les coups légaux (cases vides) sur le plateau courant.
     */
    private int countLegalMoves(HexBoard board) {
        int count = 0;
        for (int i = 0; i < board.getSize(); i++) {
            for (int j = 0; j < board.getSize(); j++) {
                if (board.getCellPlayer(i, j) == null) {
                    count++;
                }
            }
        }
        return count;
    }
}