const SHARED_PATH_LENGTH = 52;
const HOME_ENTRY_POS = 50;
const HOME_STRETCH = 5;
const FINISH_POS = HOME_ENTRY_POS + 1 + HOME_STRETCH; // 56 — the 6th home column square (outer middle of center 3x3)
// Must match board-map.js START_SQUARES exactly
const START_POSITIONS = [0, 13, 26, 39];
const SAFE_SQUARES = [0, 8, 13, 21, 26, 34, 39, 47];

// Player order: 0=Red(BL), 1=Green(TL), 2=Yellow(TR), 3=Blue(BR)
export const COLORS = ['red', 'green', 'yellow', 'blue'];
export const COLOR_CSS = ['#E53935', '#43A047', '#FDD835', '#1E88E5'];

export function createGame(numPlayers = 4) {
  const players = [];
  for (let i = 0; i < numPlayers; i++) {
    players.push({
      color: COLORS[i],
      pieces: [
        { id: `${i}-0`, pos: -1, finished: false },
        { id: `${i}-1`, pos: -1, finished: false },
        { id: `${i}-2`, pos: -1, finished: false },
        { id: `${i}-3`, pos: -1, finished: false }
      ],
      finished: false,
      consecutiveSixes: 0,
      rollsWithoutSix: 0
    });
  }
  return {
    players,
    currentPlayer: 0,
    diceValue: null,
    rolled: false,
    numPlayers,
    winners: [],
    gameOver: false,
    extraTurn: false
  };
}

export function rollDice(game, playerIdx) {
  if (!game || playerIdx == null) return Math.floor(Math.random() * 6) + 1;
  const player = game.players[playerIdx];
  const ctx = getDiceContext(game, playerIdx);
  const weights = computeDiceWeights(ctx);
  const result = weightedRoll(weights);
  if (result === 6) {
    player.rollsWithoutSix = 0;
  } else {
    player.rollsWithoutSix = (player.rollsWithoutSix || 0) + 1;
  }
  return result;
}

function getDiceContext(game, playerIdx) {
  const player = game.players[playerIdx];
  const piecesHome = player.pieces.filter(p => p.pos === -1 && !p.finished).length;
  const piecesFinished = player.pieces.filter(p => p.finished).length;
  const rollsWithoutSix = player.rollsWithoutSix || 0;

  const progress = player.pieces.reduce((sum, p) => {
    if (p.finished) return sum + FINISH_POS;
    if (p.pos === -1) return sum;
    return sum + p.pos;
  }, 0) / (FINISH_POS * 4) * 100;

  const othersProgress = game.players
    .filter((_, i) => i !== playerIdx && !game.players[i].finished)
    .map(op => op.pieces.reduce((s, p) => s + (p.finished ? FINISH_POS : p.pos === -1 ? 0 : p.pos), 0) / (FINISH_POS * 4) * 100);
  const avgOthersProgress = othersProgress.length ? othersProgress.reduce((a, b) => a + b, 0) / othersProgress.length : 0;
  const maxOthersProgress = othersProgress.length ? Math.max(...othersProgress) : 0;

  const elapsed = game.startTime ? (Date.now() - game.startTime) / 60000 : 0;
  const isHuman = game.humanPlayers && game.humanPlayers.includes(playerIdx);
  const humanCount = game.humanPlayers ? game.humanPlayers.length : 1;

  return { playerIdx, piecesHome, piecesFinished, rollsWithoutSix, progress, avgOthersProgress, maxOthersProgress, elapsed, isHuman, humanCount };
}

function computeDiceWeights(ctx) {
  const w = [1, 1, 1, 1, 1, 1];
  const timeFactor = Math.min(1, ctx.elapsed / 15);

  // --- HUMAN LUCK EDGE ---
  // Humans get a subtle persistent advantage over bots (feels lucky without being obvious)
  if (ctx.isHuman) {
    w[4] += 0.12;  // slight 5 boost (good moves)
    w[5] += 0.18;  // slight 6 boost (enter + extra turn)
  }

  // --- RUBBER BAND: whoever is behind gets help ---
  const deficit = ctx.avgOthersProgress - ctx.progress;
  if (deficit > 10) {
    const rubberBand = Math.min(0.6, deficit / 50);
    w[5] += rubberBand;
    w[4] += rubberBand * 0.5;
  }
  // If human is far behind the leader, stronger comeback boost
  if (ctx.isHuman && ctx.maxOthersProgress - ctx.progress > 20) {
    w[5] += 0.25;
  }

  // --- MOMENTUM DAMPER: leader gets slightly cooled ---
  if (ctx.progress - ctx.avgOthersProgress > 18 && !ctx.isHuman) {
    w[5] = Math.max(0.7, w[5] - 0.3);
    w[4] = Math.max(0.8, w[4] - 0.15);
  }

  // --- STUCK RESCUE: escalates with each failed roll ---
  // After 2 rolls without six, start boosting hard; by roll 4-5 it's nearly guaranteed
  if (ctx.piecesHome === 4) {
    const frustration = Math.min(ctx.rollsWithoutSix, 5);
    const base = ctx.isHuman ? 0.5 : 0.3;
    const escalation = frustration * (ctx.isHuman ? 0.6 : 0.4);
    w[5] += base + escalation;
  } else if (ctx.piecesHome >= 3) {
    const frustration = Math.min(ctx.rollsWithoutSix, 4);
    w[5] += (ctx.isHuman ? 0.3 : 0.2) + frustration * 0.25;
  }

  // --- FINISHING PUSH: accelerate endgame so it stays under 15 min ---
  if (ctx.elapsed > 8) {
    const push = 0.2 + timeFactor * 0.6;
    w[5] += push;
    // Everyone gets higher rolls late game to move pieces faster
    w[3] += timeFactor * 0.2;
    w[4] += timeFactor * 0.2;
  }

  // --- CLOSE-TO-FINISH excitement: if player has 2+ pieces finished, help them wrap up ---
  if (ctx.piecesFinished >= 2) {
    w[5] += 0.15;
    w[4] += 0.1;
  }

  return w;
}

function weightedRoll(weights) {
  const total = weights.reduce((a, b) => a + b, 0);
  let r = Math.random() * total;
  for (let i = 0; i < 6; i++) {
    r -= weights[i];
    if (r <= 0) return i + 1;
  }
  return 6;
}

export function getValidMoves(game, playerIdx, dice) {
  const player = game.players[playerIdx];
  const moves = [];

  for (const piece of player.pieces) {
    if (piece.finished) continue;

    if (piece.pos === -1) {
      if (dice === 6) {
        moves.push({ pieceId: piece.id, from: -1, to: 0, type: 'enter' });
      }
    } else {
      // Pawn turns into home column directly from position 50 (skipping the corner)
      const newPos = piece.pos + dice;

      if (newPos === FINISH_POS) {
        moves.push({ pieceId: piece.id, from: piece.pos, to: newPos, type: 'finish' });
      } else if (newPos < FINISH_POS) {
        if (newPos > HOME_ENTRY_POS) {
          moves.push({ pieceId: piece.id, from: piece.pos, to: newPos, type: 'home' });
        } else {
          const globalPos = (newPos + START_POSITIONS[playerIdx]) % SHARED_PATH_LENGTH;
          const captured = checkCapture(game, playerIdx, globalPos);
          moves.push({ pieceId: piece.id, from: piece.pos, to: newPos, type: captured ? 'capture' : 'move' });
        }
      }
      // newPos > FINISH_POS = overshoot, invalid
    }
  }

  return moves;
}

export function applyMove(game, playerIdx, move) {
  const player = game.players[playerIdx];
  const piece = player.pieces.find(p => p.id === move.pieceId);
  if (!piece) return game;

  game.extraTurn = false;

  if (move.type === 'enter') {
    piece.pos = 0;
    const globalPos = START_POSITIONS[playerIdx];
    move._capturedPlayers = captureAt(game, playerIdx, globalPos);
    game.extraTurn = true;
  } else if (move.type === 'finish') {
    piece.pos = move.to;
    piece.finished = true;
    game.extraTurn = true;
    if (player.pieces.every(p => p.finished)) {
      player.finished = true;
      game.winners.push(playerIdx);
      // Game ends when 3 players finish — the 4th is the loser
      if (game.winners.length >= 3) {
        const lastPlayer = game.players.findIndex((p, i) => !p.finished);
        if (lastPlayer !== -1) game.winners.push(lastPlayer);
        game.gameOver = true;
      }
    }
  } else if (move.type === 'capture') {
    piece.pos = move.to;
    const globalPos = (move.to + START_POSITIONS[playerIdx]) % SHARED_PATH_LENGTH;
    move._capturedPlayers = captureAt(game, playerIdx, globalPos);
    game.extraTurn = true;
  } else if (move.type === 'home') {
    piece.pos = move.to;
  } else {
    piece.pos = move.to;
  }

  if (game.diceValue === 6) {
    game.extraTurn = true;
    player.consecutiveSixes = (player.consecutiveSixes || 0) + 1;
    if (player.consecutiveSixes >= 3) {
      // Triple-six penalty: lose extra turn only, pawn stays in place
      player.consecutiveSixes = 0;
      game.extraTurn = false;
    }
  } else {
    player.consecutiveSixes = 0;
  }

  return game;
}

export function nextTurn(game) {
  if (game.extraTurn) {
    game.extraTurn = false;
    return;
  }
  do {
    game.currentPlayer = (game.currentPlayer + 1) % game.numPlayers;
  } while (game.players[game.currentPlayer].finished && !game.gameOver);
}

function checkCapture(game, playerIdx, globalPos) {
  if (SAFE_SQUARES.includes(globalPos)) return false;

  for (let i = 0; i < game.numPlayers; i++) {
    if (i === playerIdx) continue;
    for (const piece of game.players[i].pieces) {
      if (piece.pos === -1 || piece.finished) continue;
      if (piece.pos > HOME_ENTRY_POS) continue;
      const theirGlobal = (piece.pos + START_POSITIONS[i]) % SHARED_PATH_LENGTH;
      if (theirGlobal === globalPos) return true;
    }
  }
  return false;
}

function captureAt(game, playerIdx, globalPos) {
  const captured = [];
  if (SAFE_SQUARES.includes(globalPos)) return captured;

  for (let i = 0; i < game.numPlayers; i++) {
    if (i === playerIdx) continue;
    for (const piece of game.players[i].pieces) {
      if (piece.pos === -1 || piece.finished) continue;
      if (piece.pos > HOME_ENTRY_POS) continue;
      const theirGlobal = (piece.pos + START_POSITIONS[i]) % SHARED_PATH_LENGTH;
      // Double-check: never capture a piece sitting on ANY safe square
      if (SAFE_SQUARES.includes(theirGlobal)) continue;
      if (theirGlobal === globalPos) {
        piece.pos = -1;
        if (!captured.includes(i)) captured.push(i);
      }
    }
  }
  return captured;
}

export function getBotMove(game, playerIdx, dice) {
  const moves = getValidMoves(game, playerIdx, dice);
  if (moves.length === 0) return null;

  // Priority: finish > capture > enter > advance furthest
  const finishes = moves.filter(m => m.type === 'finish');
  if (finishes.length > 0) return finishes[0];

  const captures = moves.filter(m => m.type === 'capture');
  if (captures.length > 0) return captures[0];

  const enters = moves.filter(m => m.type === 'enter');
  if (enters.length > 0) return enters[0];

  // Advance the piece that's furthest along (closest to home)
  const sorted = moves.sort((a, b) => b.to - a.to);
  return sorted[0];
}
