var BackgammonConstants = (function() {
    'use strict';

    // Starting position: positive = player 0 (white), negative = player 1 (black)
    // Points numbered 0-23
    var INITIAL_BOARD = [2, 0, 0, 0, 0, -5, 0, -3, 0, 0, 0, 5, -5, 0, 0, 0, 3, 0, 5, 0, 0, 0, 0, -2];

    // Player 0 moves high→low (direction -1), Player 1 moves low→high (direction +1)
    var PLAYER_DIRECTION = { 0: -1, 1: 1 };

    // Home board ranges
    var HOME_RANGE = { 0: [0, 5], 1: [18, 23] };

    // Bar entry target points per die value
    // Player 0 enters at 24 - die, Player 1 enters at die - 1
    var BAR_ENTRY_POINTS = { 0: function(die) { return 24 - die; }, 1: function(die) { return die - 1; } };

    var TOTAL_CHECKERS = 15;

    // Pip positions for rendering dice faces 1-6 (3x3 grid)
    var DICE_FACES = {
        1: [4],
        2: [2, 6],
        3: [2, 4, 6],
        4: [0, 2, 6, 8],
        5: [0, 2, 4, 6, 8],
        6: [0, 2, 3, 5, 6, 8]
    };

    // Quick chat messages (Arabic)
    var QUICK_MESSAGES = ['حظ سعيد', 'يلا', 'هههه', 'اخخخ', 'GG', 'شكرا', 'يا سلام!', 'مبروك'];

    /**
     * Check if all of a player's checkers are in their home board (none on bar).
     */
    function isAllHome(board, bar, player) {
        if (bar[player] > 0) return false;

        var homeStart = HOME_RANGE[player][0];
        var homeEnd = HOME_RANGE[player][1];
        var count = 0;

        for (var i = 0; i < 24; i++) {
            if (player === 0 && board[i] > 0) {
                if (i < homeStart || i > homeEnd) return false;
                count += board[i];
            } else if (player === 1 && board[i] < 0) {
                if (i < homeStart || i > homeEnd) return false;
                count += Math.abs(board[i]);
            }
        }
        return true;
    }

    /**
     * Get valid moves for a single die value.
     * Returns array of {from, to, hit} objects.
     * from: point index or 'bar'; to: point index or 'off'
     */
    function getValidMoves(board, bar, borneOff, player, dieValue) {
        var moves = [];
        var sign = (player === 0) ? 1 : -1; // positive for player 0, negative for player 1

        // If player has checkers on the bar, must enter first
        if (bar[player] > 0) {
            var target = (player === 0) ? 24 - dieValue : dieValue - 1;
            // Check if target is not blocked by opponent
            var occupant = board[target] * sign;
            if (occupant >= 0 || Math.abs(board[target]) <= 1) {
                var hit = (board[target] * sign < 0) && Math.abs(board[target]) === 1;
                moves.push({ from: 'bar', to: target, hit: hit });
            }
            return moves; // Must enter from bar first
        }

        var allHome = isAllHome(board, bar, player);

        // Normal moves and bearing off
        for (var i = 0; i < 24; i++) {
            var checkers = board[i] * sign;
            if (checkers <= 0) continue; // No player checkers here

            var dest;
            if (player === 0) {
                dest = i - dieValue;
            } else {
                dest = i + dieValue;
            }

            // Bearing off
            if (allHome) {
                var canBearOff = false;
                if (player === 0) {
                    // Player 0 home: points 0-5, bear off when dest < 0
                    if (dest < 0) {
                        if (dest === -1) {
                            // Exact roll
                            canBearOff = true;
                        } else {
                            // Die is higher than needed — only allowed from highest occupied point
                            var highest = -1;
                            for (var h = 5; h >= 0; h--) {
                                if (board[h] > 0) { highest = h; break; }
                            }
                            if (i === highest) canBearOff = true;
                        }
                    }
                } else {
                    // Player 1 home: points 18-23, bear off when dest > 23
                    if (dest > 23) {
                        if (dest === 24) {
                            // Exact roll
                            canBearOff = true;
                        } else {
                            // Die is higher than needed — only allowed from highest occupied point
                            var highestP1 = -1;
                            for (var h2 = 18; h2 <= 23; h2++) {
                                if (board[h2] < 0) { highestP1 = h2; break; }
                            }
                            if (i === highestP1) canBearOff = true;
                        }
                    }
                }
                if (canBearOff) {
                    moves.push({ from: i, to: 'off', hit: false });
                }
            }

            // Normal move (dest within board)
            if (dest >= 0 && dest <= 23) {
                var destOccupant = board[dest] * sign;
                // Not blocked: blocked means 2+ opponent checkers
                if (destOccupant >= -1) {
                    var isHit = (destOccupant === -1);
                    moves.push({ from: i, to: dest, hit: isHit });
                }
            }
        }

        return moves;
    }

    /**
     * Apply a single move and return new game state.
     */
    function applyMove(board, bar, borneOff, player, move) {
        var newBoard = board.slice();
        var newBar = { 0: bar[0], 1: bar[1] };
        var newBorneOff = { 0: borneOff[0], 1: borneOff[1] };
        var sign = (player === 0) ? 1 : -1;
        var opponent = 1 - player;

        // Remove checker from source
        if (move.from === 'bar') {
            newBar[player]--;
        } else {
            newBoard[move.from] -= sign;
        }

        // Place checker at destination
        if (move.to === 'off') {
            newBorneOff[player]++;
        } else {
            // Handle hit
            if (move.hit) {
                newBoard[move.to] += sign; // remove opponent's single checker
                newBar[opponent]++;
            }
            newBoard[move.to] += sign;
        }

        return { board: newBoard, bar: newBar, borneOff: newBorneOff };
    }

    /**
     * Check if a player can make any move with any of the remaining dice.
     */
    function canMove(board, bar, borneOff, player, diceRemaining) {
        for (var i = 0; i < diceRemaining.length; i++) {
            var moves = getValidMoves(board, bar, borneOff, player, diceRemaining[i]);
            if (moves.length > 0) return true;
        }
        return false;
    }

    /**
     * Generate all possible complete move sequences for given dice.
     * Returns array of move-sequence arrays.
     * Enforces: must use maximum number of dice possible.
     * If only one die is usable, prefer the higher one.
     */
    function getAllMovesForDice(board, bar, borneOff, player, diceRemaining) {
        var allSequences = [];
        var maxLength = 0;

        function dfs(curBoard, curBar, curBorneOff, remainingDice, sequence) {
            var foundMove = false;

            for (var i = 0; i < remainingDice.length; i++) {
                var die = remainingDice[i];
                var moves = getValidMoves(curBoard, curBar, curBorneOff, player, die);

                if (moves.length > 0) {
                    foundMove = true;
                    var nextDice = remainingDice.slice();
                    nextDice.splice(i, 1);

                    for (var m = 0; m < moves.length; m++) {
                        var moveWithDie = { from: moves[m].from, to: moves[m].to, hit: moves[m].hit, dieUsed: die };
                        var result = applyMove(curBoard, curBar, curBorneOff, player, moveWithDie);
                        var newSeq = sequence.concat([moveWithDie]);
                        dfs(result.board, result.bar, result.borneOff, nextDice, newSeq);
                    }
                }
            }

            if (!foundMove || remainingDice.length === 0) {
                if (sequence.length > maxLength) {
                    maxLength = sequence.length;
                }
                allSequences.push(sequence);
            }
        }

        dfs(board, bar, borneOff, diceRemaining, []);

        // Filter: keep only sequences that use the maximum number of dice
        var filtered = [];
        for (var i = 0; i < allSequences.length; i++) {
            if (allSequences[i].length === maxLength) {
                filtered.push(allSequences[i]);
            }
        }

        // If only one die is usable (maxLength === 1) and we had 2 different dice,
        // prefer the higher die
        if (maxLength === 1 && diceRemaining.length === 2 && diceRemaining[0] !== diceRemaining[1]) {
            var higherDie = Math.max(diceRemaining[0], diceRemaining[1]);
            var higherMoves = [];
            var lowerMoves = [];

            for (var j = 0; j < filtered.length; j++) {
                var move = filtered[j][0];
                // Determine which die was used by checking distance
                var dist;
                if (move.from === 'bar') {
                    if (player === 0) dist = 24 - move.to;
                    else dist = move.to + 1;
                } else if (move.to === 'off') {
                    if (player === 0) dist = move.from + 1;
                    else dist = 24 - move.from;
                } else {
                    dist = Math.abs(move.to - move.from);
                }

                if (dist === higherDie) {
                    higherMoves.push(filtered[j]);
                } else {
                    lowerMoves.push(filtered[j]);
                }
            }

            // Only prefer higher if higher moves exist; otherwise keep lower
            if (higherMoves.length > 0) {
                filtered = higherMoves;
            }
        }

        return filtered;
    }

    /**
     * Check if player has won (all 15 checkers borne off).
     */
    function checkWin(borneOff, player) {
        return borneOff[player] === TOTAL_CHECKERS;
    }

    /**
     * Determine win type: 'normal', 'gammon', or 'backgammon'.
     */
    function getWinType(board, bar, borneOff, winner) {
        var loser = 1 - winner;

        // If opponent has borne off at least 1 checker, it's a normal win
        if (borneOff[loser] > 0) return 'normal';

        // Gammon: opponent has 0 borne off
        // Backgammon: opponent has 0 borne off AND (has checker on bar OR in winner's home)
        var hasOnBar = bar[loser] > 0;
        var hasInWinnerHome = false;

        var winnerHomeStart = HOME_RANGE[winner][0];
        var winnerHomeEnd = HOME_RANGE[winner][1];
        var loserSign = (loser === 0) ? 1 : -1;

        for (var i = winnerHomeStart; i <= winnerHomeEnd; i++) {
            if (board[i] * loserSign > 0) {
                hasInWinnerHome = true;
                break;
            }
        }

        if (hasOnBar || hasInWinnerHome) return 'backgammon';
        return 'gammon';
    }

    /**
     * Calculate pip count for a player (total distance to bear off).
     */
    function pipCount(board, bar, player) {
        var total = 0;

        // Bar checkers: 25 pips each
        total += bar[player] * 25;

        for (var i = 0; i < 24; i++) {
            if (player === 0 && board[i] > 0) {
                // Player 0: checker at point X contributes X+1 pips
                total += board[i] * (i + 1);
            } else if (player === 1 && board[i] < 0) {
                // Player 1: checker at point X contributes 24-X pips
                total += Math.abs(board[i]) * (24 - i);
            }
        }

        return total;
    }

    /**
     * Roll two dice.
     */
    function rollDice() {
        var d1 = Math.floor(Math.random() * 6) + 1;
        var d2 = Math.floor(Math.random() * 6) + 1;
        return [d1, d2];
    }

    /**
     * Get remaining dice to play. Doubles give 4 moves.
     */
    function getDiceRemaining(d1, d2) {
        if (d1 === d2) {
            return [d1, d1, d1, d1];
        }
        return [d1, d2];
    }

    return {
        INITIAL_BOARD: INITIAL_BOARD,
        PLAYER_DIRECTION: PLAYER_DIRECTION,
        HOME_RANGE: HOME_RANGE,
        BAR_ENTRY_POINTS: BAR_ENTRY_POINTS,
        TOTAL_CHECKERS: TOTAL_CHECKERS,
        DICE_FACES: DICE_FACES,
        QUICK_MESSAGES: QUICK_MESSAGES,
        isAllHome: isAllHome,
        getValidMoves: getValidMoves,
        applyMove: applyMove,
        canMove: canMove,
        getAllMovesForDice: getAllMovesForDice,
        checkWin: checkWin,
        getWinType: getWinType,
        pipCount: pipCount,
        rollDice: rollDice,
        getDiceRemaining: getDiceRemaining
    };
})();
