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

  var C = LudoConstants;

  function getEligiblePieces(player, diceValue, positions, activePlayers) {
    var eligible = [];
    var playerPositions = positions[player];

    for (var i = 0; i < 4; i++) {
      var pos = playerPositions[i];
      var isInBase = C.BASE_POSITIONS[player].indexOf(pos) !== -1;
      var isHome = pos === C.HOME_POSITIONS[player];

      if (isHome) continue;

      if (isInBase) {
        if (diceValue === 6) eligible.push(i);
        continue;
      }

      var newPos = calculateNewPosition(player, pos, diceValue);
      if (newPos !== null) eligible.push(i);
    }

    return eligible;
  }

  function calculateNewPosition(player, currentPos, diceValue) {
    var isInHomeEntrance = C.HOME_ENTRANCE[player].indexOf(currentPos) !== -1;

    if (isInHomeEntrance) {
      var homeIdx = C.HOME_ENTRANCE[player].indexOf(currentPos);
      var stepsLeft = 5 - homeIdx;
      if (diceValue > stepsLeft) return null;
      if (diceValue === stepsLeft) return C.HOME_POSITIONS[player];
      return C.HOME_ENTRANCE[player][homeIdx + diceValue];
    }

    var turningPoint = C.TURNING_POINTS[player];
    var startPos = C.START_POSITIONS[player];

    var distanceToTurn = (turningPoint - currentPos + 52) % 52;
    if (distanceToTurn === 0 && diceValue <= 6) {
      if (diceValue <= 5) return C.HOME_ENTRANCE[player][diceValue - 1];
      return null;
    }

    if (diceValue > distanceToTurn && distanceToTurn > 0 && distanceToTurn < 6) {
      var overflow = diceValue - distanceToTurn;
      if (overflow <= 5) return C.HOME_ENTRANCE[player][overflow - 1];
      return null;
    }

    var newPos = (currentPos + diceValue) % 52;
    return newPos;
  }

  function choosePieceEasy(eligible) {
    if (eligible.length === 0) return -1;
    return eligible[Math.floor(Math.random() * eligible.length)];
  }

  function choosePieceHard(player, diceValue, positions, activePlayers, eligible) {
    if (eligible.length === 0) return -1;
    if (eligible.length === 1) return eligible[0];

    var bestScore = -Infinity;
    var bestPiece = eligible[0];

    eligible.forEach(function(pieceIdx) {
      var score = scorePieceMove(player, pieceIdx, diceValue, positions, activePlayers);
      if (score > bestScore) {
        bestScore = score;
        bestPiece = pieceIdx;
      }
    });

    return bestPiece;
  }

  function scorePieceMove(player, pieceIdx, diceValue, positions, activePlayers) {
    var score = 0;
    var currentPos = positions[player][pieceIdx];
    var isInBase = C.BASE_POSITIONS[player].indexOf(currentPos) !== -1;

    if (isInBase && diceValue === 6) {
      score += 30;
      var startPos = C.START_POSITIONS[player];
      if (canKillAt(player, startPos, positions, activePlayers)) {
        score += 50;
      }
      return score;
    }

    var newPos = calculateNewPosition(player, currentPos, diceValue);
    if (newPos === null) return -Infinity;

    if (newPos === C.HOME_POSITIONS[player]) {
      score += 100;
      return score;
    }

    var isEnteringHome = C.HOME_ENTRANCE[player].indexOf(newPos) !== -1;
    if (isEnteringHome) {
      score += 25;
    }

    if (newPos >= 0 && newPos <= 51) {
      if (canKillAt(player, newPos, positions, activePlayers)) {
        score += 50;
      }

      if (C.SAFE_POSITIONS.indexOf(newPos) !== -1) {
        score += 20;
      } else if (isInDanger(player, newPos, positions, activePlayers)) {
        score -= 15;
      }

      if (isBlockingOpponentExit(player, newPos, positions, activePlayers)) {
        score += 15;
      }
    }

    var progress = getProgressDistance(player, currentPos);
    var maxProgress = 0;
    positions[player].forEach(function(pos, idx) {
      if (idx === pieceIdx) return;
      if (C.BASE_POSITIONS[player].indexOf(pos) !== -1) return;
      if (pos === C.HOME_POSITIONS[player]) return;
      var p = getProgressDistance(player, pos);
      if (p > maxProgress) maxProgress = p;
    });
    if (progress < maxProgress) {
      score += 10;
    }

    return score;
  }

  function canKillAt(player, position, positions, activePlayers) {
    if (C.SAFE_POSITIONS.indexOf(position) !== -1) return false;

    for (var i = 0; i < activePlayers.length; i++) {
      var opponent = activePlayers[i];
      if (opponent === player) continue;

      for (var j = 0; j < 4; j++) {
        if (positions[opponent][j] === position) return true;
      }
    }
    return false;
  }

  function isInDanger(player, position, positions, activePlayers) {
    for (var i = 0; i < activePlayers.length; i++) {
      var opponent = activePlayers[i];
      if (opponent === player) continue;

      for (var j = 0; j < 4; j++) {
        var oppPos = positions[opponent][j];
        if (C.BASE_POSITIONS[opponent].indexOf(oppPos) !== -1) continue;
        if (oppPos === C.HOME_POSITIONS[opponent]) continue;
        if (C.HOME_ENTRANCE[opponent].indexOf(oppPos) !== -1) continue;

        for (var dice = 1; dice <= 6; dice++) {
          var oppNewPos = (oppPos + dice) % 52;
          if (oppNewPos === position) return true;
        }
      }
    }
    return false;
  }

  function isBlockingOpponentExit(player, position, positions, activePlayers) {
    for (var i = 0; i < activePlayers.length; i++) {
      var opponent = activePlayers[i];
      if (opponent === player) continue;
      if (position === C.START_POSITIONS[opponent]) return true;
    }
    return false;
  }

  function getProgressDistance(player, position) {
    if (C.BASE_POSITIONS[player].indexOf(position) !== -1) return 0;
    if (position === C.HOME_POSITIONS[player]) return 57;

    var homeIdx = C.HOME_ENTRANCE[player].indexOf(position);
    if (homeIdx !== -1) return 52 + homeIdx;

    var start = C.START_POSITIONS[player];
    return (position - start + 52) % 52;
  }

  function takeTurn(player, diceValue, positions, activePlayers, difficulty) {
    var eligible = getEligiblePieces(player, diceValue, positions, activePlayers);
    if (eligible.length === 0) return -1;

    if (difficulty === 'hard') {
      return choosePieceHard(player, diceValue, positions, activePlayers, eligible);
    }
    return choosePieceEasy(eligible);
  }

  return {
    takeTurn: takeTurn,
    getEligiblePieces: getEligiblePieces,
    calculateNewPosition: calculateNewPosition
  };
})();
