import * as bus from '../../../core/bus.js';
import * as scene from '../../../core/scene.js';
import * as audio from '../../../core/audio.js';
import * as store from '../../../core/store.js';
import { t } from '../../../core/i18n.js';
import { createCanvas, clear } from '../../../core/canvas.js';
import * as rules from '../logic/rules.js';
import * as juice from '../../../core/juice.js';
import { getPiecePosition, getHomeBasePosition, SAFE_SQUARES, HOME_COLUMNS, SHARED_PATH } from '../logic/board-map.js';
import * as emoteSystem from '../../chess/components/emotes.js';
import * as mp from '../../../core/multiplayer.js';
import { emoji, getAsset } from '../../../core/theme.js';
import * as matchLive from '../../../core/match-live.js';
import * as net from '../../../core/net.js';

let game, validMoves, ctx, canvas, boardSize, cellSize;
let diceAnimating = false;

// Order: Red(BL), Green(TL), Yellow(TR), Blue(BR) — standard Ludo layout
const COLORS = ['#E53935', '#43A047', '#FDD835', '#1E88E5'];
const COLORS_LIGHT = ['#EF9A9A', '#A5D6A7', '#FFF59D', '#90CAF9'];
let PLAYER_NAMES = ['أنت', 'Bot 1', 'Bot 2', 'Bot 3'];
let livePoller = null;
let myPlayerIndex = 0;
let matchId = null;
let isHost = false;

function renderPanel(p) {
  return `
    <div class="pp" id="pp-${p.i}" style="--pc:${p.color};">
      <div class="pp-turn-arrow">▶</div>
      <div class="pp-avatar" style="width:30px;height:30px;border-radius:50%;background:linear-gradient(135deg,#2a2a4a,#1a1a3a);border:2.5px solid ${p.color};display:flex;align-items:center;justify-content:center;font-size:14px;overflow:hidden;box-shadow:0 2px 8px rgba(0,0,0,0.3);">
        ${p.avatar}
      </div>
      <div style="display:flex;flex-direction:column;">
        <span class="pp-name" style="font-size:11px;font-weight:700;color:#f8fafc;">${p.name}</span>
        ${p.level ? `<span style="font-size:9px;color:#64748b;">${p.level}</span>` : ''}
      </div>
      <div class="pp-dice" id="dice-${p.i}"></div>
    </div>
  `;
}

export function mountGame(el, params) {
  const { mode = 'bot', numPlayers = 4 } = params;
  scene.enterGameMode();

  myPlayerIndex = params.playerIndex || 0;
  matchId = params.matchId || null;
  isHost = myPlayerIndex === 0;

  if (mode === 'live' && params.players) {
    PLAYER_NAMES = params.players.map((p, i) => {
      if (i === myPlayerIndex) return 'أنت';
      if (p.startsWith('bot')) return 'Bot ' + p.split('_')[1];
      return 'لاعب ' + (i + 1);
    });
  } else {
    PLAYER_NAMES = ['أنت', 'Bot 1', 'Bot 2', 'Bot 3'];
  }

  game = rules.createGame(numPlayers);
  game.mode = mode;
  game.startTime = Date.now();
  game.turboMode = false;
  game.turnCount = 0;
  validMoves = [];
  diceAnimating = false;
  lastSyncTurnCount = 0;
  if (livePoller) { clearInterval(livePoller); livePoller = null; }

  const player = store.get('player') || {};
  const panels = [0,1,2,3].map(i => {
    const isMe = i === myPlayerIndex;
    const isBot = PLAYER_NAMES[i].startsWith('Bot');
    const avatar = isMe && player.avatar_url ? `<img src="${player.avatar_url}" style="width:100%;height:100%;object-fit:cover;border-radius:50%;">` : isBot ? '🤖' : '👤';
    const name = isMe ? (player.display_name || player.username || 'أنت') : PLAYER_NAMES[i];
    const level = isMe ? `Lv.${player.level || 1}` : (isBot ? '' : '');
    return { i, avatar, name, level, color: COLORS[i] };
  });

  el.innerHTML = `
    <div style="display:flex;flex-direction:column;height:100%;background:linear-gradient(180deg,#0d0d1a 0%,#1a1a2e 50%,#0d0d1a 100%);justify-content:flex-end;">
      <div style="display:flex;justify-content:space-between;padding:8px 12px;background:rgba(15,15,30,0.9);border-bottom:1px solid rgba(255,255,255,0.04);direction:ltr;">
        ${renderPanel(panels[1])}
        ${renderPanel(panels[2])}
      </div>
      <div id="ludo-wrap" style="flex:1;display:flex;align-items:center;justify-content:center;padding:6px;min-height:0;"></div>
      <div style="display:flex;justify-content:space-between;padding:8px 12px;background:rgba(15,15,30,0.9);border-top:1px solid rgba(255,255,255,0.04);direction:ltr;">
        ${renderPanel(panels[0])}
        ${renderPanel(panels[3])}
      </div>
      <div id="dice-area" style="display:flex;align-items:center;gap:12px;padding:14px 16px;background:linear-gradient(180deg,#12122a,#0a0a1a);border-top:1px solid rgba(228,172,56,0.15);justify-content:center;padding-bottom:max(14px, env(safe-area-inset-bottom, 0px));position:relative;overflow:hidden;">
        <div style="position:absolute;inset:0;background:radial-gradient(ellipse at 50% 0%,rgba(228,172,56,0.06) 0%,transparent 70%);pointer-events:none;"></div>
        <button class="btn btn-secondary" id="exit-btn" style="min-height:44px;min-width:44px;padding:0;font-size:13px;color:#EF4444;border-radius:50%;background:rgba(239,68,68,0.08);border:1px solid rgba(239,68,68,0.2);">✕</button>
        <div id="dice-box" style="width:56px;height:56px;background:linear-gradient(145deg,#ffffff,#f0ede8);border-radius:12px;display:grid;grid-template:repeat(3,1fr)/repeat(3,1fr);padding:7px;box-shadow:0 4px 12px rgba(0,0,0,0.4),inset 0 2px 0 rgba(255,255,255,0.9),0 0 0 2px rgba(228,172,56,0.15);transition:transform 0.15s cubic-bezier(0.34,1.56,0.64,1);">
        </div>
        <button class="btn btn-primary" id="roll-btn" style="font-size:15px;padding:14px 32px;min-height:52px;border-radius:14px;background:linear-gradient(135deg,#E4AC38,#F59E0B);box-shadow:0 4px 14px rgba(228,172,56,0.3);font-weight:800;" disabled>ارمِ النرد</button>
      </div>
    </div>
    <style>
      .pp{display:flex;align-items:center;gap:6px;padding:6px 10px;border-radius:10px;border:2px solid transparent;transition:all 0.3s cubic-bezier(0.34,1.56,0.64,1);position:relative;}
      .pp.active{border-color:var(--pc);background:rgba(255,255,255,0.06);animation:panelPulse 2s ease-in-out infinite;box-shadow:0 0 12px color-mix(in srgb, var(--pc) 20%, transparent);}
      .pp-turn-arrow{position:absolute;left:-2px;top:50%;transform:translateY(-50%) scale(0);font-size:10px;color:var(--pc);transition:transform 0.3s cubic-bezier(0.34,1.56,0.64,1);filter:drop-shadow(0 0 4px var(--pc));}
      .pp.active .pp-turn-arrow{transform:translateY(-50%) scale(1);animation:arrowBounce 1s ease-in-out infinite;}
      .pp-name{transition:color 0.3s,text-shadow 0.3s;}
      .pp.active .pp-name{color:#fff !important;text-shadow:0 0 8px color-mix(in srgb, var(--pc) 50%, transparent);}
      .pp-dice{width:28px;height:28px;background:linear-gradient(145deg,#ffffff,#f0ede8);border-radius:6px;display:grid;grid-template:repeat(3,1fr)/repeat(3,1fr);padding:3px;box-shadow:0 2px 6px rgba(0,0,0,0.3);transition:all 0.2s cubic-bezier(0.34,1.56,0.64,1);opacity:0;transform:scale(0);margin-left:auto;}
      .pp-dice.visible{opacity:1;transform:scale(1);}
      .pp-dice.rolling{animation:diceShake 0.08s infinite alternate;opacity:1;transform:scale(1);}
      .pp-dice.landed{opacity:1;transform:scale(1.15);box-shadow:0 0 12px rgba(228,172,56,0.5);}
      .pp.active .pp-avatar{animation:avatarBounce 2s ease-in-out infinite;box-shadow:0 0 12px color-mix(in srgb, var(--pc) 40%, transparent);}
      @keyframes panelPulse{0%,100%{box-shadow:0 0 8px 0 transparent;}50%{box-shadow:0 0 16px 3px color-mix(in srgb, var(--pc) 25%, transparent);}}
      @keyframes avatarBounce{0%,100%{transform:scale(1);}50%{transform:scale(1.1);}}
      @keyframes arrowBounce{0%,100%{transform:translateY(-50%) scale(1) translateX(0);}50%{transform:translateY(-50%) scale(1) translateX(3px);}}
      @keyframes diceShake{from{transform:rotate(-15deg) scale(0.85);}to{transform:rotate(15deg) scale(0.85);}}
      @keyframes dicePopIn{0%{transform:scale(0) rotate(-30deg);opacity:0;}60%{transform:scale(1.3) rotate(5deg);opacity:1;}100%{transform:scale(1) rotate(0deg);opacity:1;}}
      @keyframes diceSix{0%,100%{box-shadow:0 0 8px rgba(228,172,56,0.4);}50%{box-shadow:0 0 20px rgba(228,172,56,0.9);}}
      @keyframes rollBtnPulse{0%,100%{box-shadow:0 4px 14px rgba(228,172,56,0.3);}50%{box-shadow:0 4px 24px rgba(228,172,56,0.6);}}
      #roll-btn:not([disabled]){animation:rollBtnPulse 2s ease-in-out infinite;}
    </style>
  `;

  const wrap = el.querySelector('#ludo-wrap');
  const availableH = wrap.clientHeight || 300;
  const availableW = wrap.clientWidth || 360;
  boardSize = Math.min(availableW - 8, availableH - 8, 420);
  cellSize = boardSize / 15;
  const { canvas: c, ctx: cx } = createCanvas(wrap, boardSize, boardSize);
  c.style.cssText = 'border-radius:6px;box-shadow:0 4px 16px rgba(0,0,0,0.4);max-width:100%;max-height:100%;';
  canvas = c; ctx = cx;

  renderDiceFace(el.querySelector('#dice-box'), 1);
  for (let i = 0; i < 4; i++) {
    const md = el.querySelector(`#dice-${i}`);
    if (md) { renderMiniDice(md, 1); md.className = 'pp-dice'; }
  }
  drawBoard();
  updatePanels(el);
  el.querySelector('#roll-btn').addEventListener('click', () => handleRoll(el));
  el.querySelector('#exit-btn').addEventListener('click', () => handleExit(el));

  // Emotes + multiplayer
  const emoteWrap = el.querySelector('#ludo-wrap');
  emoteSystem.create(emoteWrap, (emote) => {
    audio.play('notification');
    // Animate from MY panel position to center
    const myPanel = el.querySelector(`#pp-${myPlayerIndex}`);
    emoteSystem.showReceived(emoteWrap, emote.emoji, myPanel);
    if (game.mode === 'live' && matchId) {
      mp.sendEmote(matchId, 'ludo', emote.key);
    }
  });

  // Live mode setup — full multiplayer integration
  if (mode === 'live' && matchId) {
    matchLive.start(matchId, 'ludo', {
      onMove: (data) => { mp.updateConnectionStatus(true); },
      onGameEnd: () => { if (!game.gameOver) { game.gameOver = true; scene.exitGameMode(); scene.replace('ludo-result', { result: 'loss' }); } }
    });
    mp.startDisconnectWatch(matchId, 'ludo', 60000);
    mp.onEmoteReceived((emote) => {
      // Animate from OPPONENT's panel position to center
      // Find which player sent it (not me)
      const senderIdx = emote.from === store.get('auth.userId') ? myPlayerIndex : (myPlayerIndex === 0 ? 1 : 0);
      const oppPanel = el.querySelector(`#pp-${senderIdx}`);
      emoteSystem.showReceived(emoteWrap, emote.key === 'gg' ? emoji('handshake', '🤝', 24) : emote.key === 'good_move' ? '👏' : '😮', oppPanel);
      audio.play('notification');
    });

    // Fetch opponent profiles and update player panels
    if (params.players) {
      params.players.forEach((pId, i) => {
        if (i === myPlayerIndex || pId.startsWith('bot')) return;
        mp.fetchOpponentProfile(pId).then(profile => {
          if (profile && !profile.error) {
            const panel = el.querySelector(`#pp-${i}`);
            if (panel) {
              const nameEl = panel.querySelector('span');
              if (nameEl) nameEl.textContent = profile.display_name || profile.username || 'لاعب';
              // Make panel tappable for friend add
              panel.style.cursor = 'pointer';
              panel.addEventListener('click', () => {
                audio.play('click');
                showOpponentPopup(el, profile);
              });
            }
          }
        });
      });
    }

    // If not my turn at start, begin polling
    if (!isMyTurn()) handleNonPlayerTurn(el);
  }

  bus.emit('game:started', { gameKey: 'ludo', mode });
}

function renderDiceFace(diceBox, value) {
  const diceUrl = getAsset('emoji_dice_' + value);
  if (diceUrl) {
    diceBox.innerHTML = `<img src="${diceUrl}" style="width:100%;height:100%;object-fit:contain;" draggable="false">`;
    diceBox.style.padding = '0';
    diceBox.style.display = 'flex';
    diceBox.style.gridTemplate = 'none';
    return;
  }
  diceBox.style.padding = '6px';
  diceBox.style.display = 'grid';
  diceBox.style.gridTemplate = 'repeat(3,1fr)/repeat(3,1fr)';
  const dots = {
    1: [0,0,0,0,1,0,0,0,0],
    2: [0,0,1,0,0,0,1,0,0],
    3: [0,0,1,0,1,0,1,0,0],
    4: [1,0,1,0,0,0,1,0,1],
    5: [1,0,1,0,1,0,1,0,1],
    6: [1,0,1,1,0,1,1,0,1],
  };
  const pattern = dots[value] || dots[1];
  diceBox.innerHTML = pattern.map(d =>
    `<div style="width:10px;height:10px;border-radius:50%;background:${d ? '#1a1a2e' : 'transparent'};margin:auto;transition:transform 0.1s;${d ? 'box-shadow:0 1px 2px rgba(0,0,0,0.3);' : ''}"></div>`
  ).join('');
}

function isMyTurn() {
  return game.currentPlayer === myPlayerIndex;
}

async function handleRoll(el) {
  if (diceAnimating || game.rolled || game.gameOver || !isMyTurn() || game.players[myPlayerIndex].finished) return;
  diceAnimating = true;
  updatePanels(el);

  const dice = await animateDice(el, myPlayerIndex);
  game.diceValue = dice;
  game.rolled = true;
  diceAnimating = false;

  // Star burst on 6
  if (dice === 6) {
    const rect = el.querySelector('#dice-box').getBoundingClientRect();
    juice.starBurst(rect.left + 31, rect.top + 31, 8);
  }

  validMoves = rules.getValidMoves(game, myPlayerIndex, dice);
  if (validMoves.length === 0) {
    setTimeout(() => {
      game.rolled = false;
      rules.nextTurn(game);
      if (game.mode === 'live') syncLudoState(dice);
      updatePanels(el);
      drawBoard();
      if (!isMyTurn()) handleNonPlayerTurn(el);
    }, 800);
  } else if (validMoves.length === 1) {
    highlightMovablePieces(validMoves);
    setTimeout(() => doMove(el, validMoves[0]), 600);
  } else {
    highlightMovablePieces(validMoves);
    waitForPieceSelection(el, validMoves);
  }
}

function doMove(el, move) {
  animateMove(el, move);
}

function handleNonPlayerTurn(el) {
  if (game.mode === 'live') {
    const currentPlayerStr = PLAYER_NAMES[game.currentPlayer];
    if (currentPlayerStr && currentPlayerStr.startsWith('Bot') && isHost) {
      // Host runs bots locally and syncs result to server
      setTimeout(() => botLoop(el), 400);
    } else {
      // Non-host (or waiting for human opponent): poll for state updates
      startLudoPolling(el);
    }
  } else {
    // Single player — all non-player turns are bots
    setTimeout(() => botLoop(el), 400);
  }
}

async function botLoop(el) {
  if (game.gameOver || isMyTurn() || game.players[game.currentPlayer].finished) return;

  // === BOT PERSONALITY PROFILES ===
  const personalities = {
    1: { name: 'aggressive', thinkMin: 500, thinkMax: 1000, preferCapture: true },
    2: { name: 'casual', thinkMin: 1500, thinkMax: 3000, suboptimal: true },
    3: { name: 'strategic', thinkMin: 1000, thinkMax: 2000, optimal: true },
  };
  const personality = personalities[game.currentPlayer] || personalities[1];

  // === BOT HUMANIZATION: delays that mimic real player ===
  const turboMul = game.turboMode ? 0.4 : 1;
  const thinkDelay = (personality.thinkMin + Math.random() * (personality.thinkMax - personality.thinkMin)) * turboMul;

  // Show "thinking" indicator on the bot's player panel
  const botPanel = el.querySelector(`#pp-${game.currentPlayer}`);
  const botPanelSpan = botPanel ? botPanel.querySelector('span') : null;
  const originalPanelText = botPanelSpan ? botPanelSpan.textContent : '';
  if (botPanelSpan) botPanelSpan.textContent = 'يفكر...';

  await new Promise(r => setTimeout(r, thinkDelay));
  if (game.gameOver || isMyTurn()) { if (botPanelSpan) botPanelSpan.textContent = originalPanelText; return; }

  // 2. Roll dice with SAME animation as player
  if (botPanelSpan) botPanelSpan.textContent = originalPanelText;
  const dice = await animateDice(el, game.currentPlayer);

  // 3. "Deciding" which piece to move — personality-driven delay
  const decideDelay = (personality.thinkMin * 0.5 + Math.random() * (personality.thinkMax - personality.thinkMin) * 0.5) * turboMul;

  // Show thinking indicator again while deciding
  if (botPanelSpan) botPanelSpan.textContent = 'يفكر...';
  await new Promise(r => setTimeout(r, decideDelay));
  if (botPanelSpan) botPanelSpan.textContent = originalPanelText;
  if (game.gameOver || isMyTurn()) return;

  // 4. Execute move with step animation
  game.diceValue = dice;
  const move = rules.getBotMove(game, game.currentPlayer, dice);
  if (!move) {
    // No valid moves — just pass turn
    if (game.gameOver) {
      if (game.mode === 'live' && isHost) syncLudoState(dice);
      endGame(el);
      return;
    }
    rules.nextTurn(game);
    updatePanels(el);
    drawBoard();
    if (game.mode === 'live' && isHost) syncLudoState(dice);
    if (!isMyTurn()) handleNonPlayerTurn(el);
    return;
  }

  // Animate bot move step by step
  const pIdx = parseInt(move.pieceId.split('-')[0]);
  const pieceIdx = parseInt(move.pieceId.split('-')[1]);
  const piece = game.players[pIdx].pieces[pieceIdx];
  const fromPos = piece.pos;
  const toPos = move.to;

  if (move.type !== 'enter' && fromPos >= 0 && toPos > fromPos && toPos - fromPos <= 6) {
    const hopDelay = game.turboMode ? 40 : 80;
    const bounceHeight = cellSize * 0.5;
    for (let i = 1; i <= toPos - fromPos; i++) {
      piece.pos = fromPos + i;
      piece._bounceOffset = -bounceHeight;
      drawBoard();
      await new Promise(r => setTimeout(r, hopDelay));
      piece._bounceOffset = -bounceHeight * 1.2;
      drawBoard();
      await new Promise(r => setTimeout(r, hopDelay * 0.6));
      piece._bounceOffset = -bounceHeight * 0.4;
      drawBoard();
      await new Promise(r => setTimeout(r, hopDelay * 0.6));
      piece._bounceOffset = 2;
      drawBoard();
      audio.play('move', 'game');
      await new Promise(r => setTimeout(r, hopDelay * 0.4));
      piece._bounceOffset = 0;
      drawBoard();
    }
    piece.pos = fromPos;
  }

  rules.applyMove(game, game.currentPlayer, move);

  if (move.type === 'capture') {
    const capturedMe = move._capturedPlayers && move._capturedPlayers.includes(myPlayerIndex);
    if (capturedMe) {
      audio.play('lose', 'game');
      juice.hapticError();
      juice.shake(el, 8, 400);
      juice.screenFlash('rgba(229,57,53,0.25)', 500);
    } else {
      audio.play('capture', 'game');
      juice.shake(el, 3, 150);
    }
  } else if (move.type === 'finish') {
    audio.play('notification');
    const boardRect = canvas.getBoundingClientRect();
    fireworkBurst(boardRect.left + boardRect.width / 2, boardRect.top + boardRect.height / 2, COLORS[game.currentPlayer]);
  }

  if (move.type === 'capture' && Math.random() > 0.5) {
    const emoteWrap = el.querySelector('#ludo-wrap');
    setTimeout(() => {
      const botEmotes = ['😂', '💪', '🎉', '😎'];
      const botPanel2 = el.querySelector(`#pp-${game.currentPlayer}`);
      emoteSystem.showReceived(emoteWrap, botEmotes[Math.floor(Math.random() * botEmotes.length)], botPanel2);
    }, 300);
  }

  if (game.gameOver) {
    if (game.mode === 'live' && isHost) syncLudoState(dice);
    endGame(el);
    return;
  }
  rules.nextTurn(game);
  updatePanels(el);
  drawBoard();

  if (game.mode === 'live' && isHost) syncLudoState(dice);

  if (!isMyTurn()) handleNonPlayerTurn(el);
}

// === LIVE SYNC ===
let lastSyncTurnCount = 0;

function syncLudoState(diceRolled = null) {
  if (!matchId) return;
  const turnCount = (game.turnCount || 0) + 1;
  game.turnCount = turnCount;
  const positions = game.players.map(p => ({
    pos: p.pieces.map(pc => pc.pos),
    finished: p.finished
  }));
  const payload = {
    action: 'move',
    match_id: matchId,
    positions,
    current_turn: game.currentPlayer,
    dice_value: game.diceValue,
    game_state: JSON.stringify({ turn_count: turnCount, dice_rolled: diceRolled }),
  };
  if (game.gameOver) {
    payload.status = 'completed';
    payload.winners = game.winners;
  }
  net.post('ludo-match.php', payload).catch(() => {});
}

function startLudoPolling(el) {
  if (livePoller) return;
  livePoller = setInterval(async () => {
    if (game.gameOver) { clearInterval(livePoller); livePoller = null; return; }
    if (isMyTurn()) { clearInterval(livePoller); livePoller = null; return; }
    try {
      const data = await net.post('ludo-match.php', { action: 'get', match_id: matchId });
      if (!data || data.error) return;

      // Parse game_state to get turn_count
      let gs = {};
      try { gs = typeof data.game_state === 'string' ? JSON.parse(data.game_state) : (data.game_state || {}); } catch(e) {}
      const remoteTurnCount = gs.turn_count || 0;

      // Only apply if server state has advanced
      if (remoteTurnCount <= lastSyncTurnCount) return;
      lastSyncTurnCount = remoteTurnCount;

      // Apply remote state — positions, turn, dice
      const positions = typeof data.positions === 'string' ? JSON.parse(data.positions) : data.positions;
      if (positions) {
        positions.forEach((p, pIdx) => {
          if (p.pos) p.pos.forEach((pos, pieceIdx) => { game.players[pIdx].pieces[pieceIdx].pos = pos; });
          if (p.finished !== undefined) game.players[pIdx].finished = p.finished;
        });
      }
      game.currentPlayer = data.current_turn;
      game.diceValue = data.dice_value;
      game.turnCount = remoteTurnCount;

      // Show opponent's dice result briefly
      if (gs.dice_rolled != null) {
        const mainDice = el.querySelector('#dice-box');
        if (mainDice) renderDiceFace(mainDice, gs.dice_rolled);
      }

      // Check if game ended
      if (data.status === 'completed' && !game.gameOver) {
        game.gameOver = true;
        clearInterval(livePoller); livePoller = null;
        endGame(el);
        return;
      }

      updatePanels(el);
      drawBoard();

      // If it's now my turn, stop polling
      if (isMyTurn()) {
        clearInterval(livePoller); livePoller = null;
        audio.play('notification');
      } else {
        // If it's a bot's turn and I'm host, run it
        const currentPlayerStr = PLAYER_NAMES[game.currentPlayer];
        if (currentPlayerStr && currentPlayerStr.startsWith('Bot') && isHost) {
          clearInterval(livePoller); livePoller = null;
          setTimeout(() => botLoop(el), 400);
        }
      }
    } catch (e) {}
  }, 1500);
}

// ===== OPPONENT POPUP =====
function showOpponentPopup(el, profile) {
  const existing = document.getElementById('ludo-opp-popup');
  if (existing) { existing.remove(); return; }

  const popup = document.createElement('div');
  popup.id = 'ludo-opp-popup';
  popup.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#1a1a2e;border:1px solid rgba(255,255,255,0.1);border-radius:16px;padding:20px;z-index:200;box-shadow:0 12px 40px rgba(0,0,0,0.7);text-align:center;min-width:200px;';
  popup.innerHTML = `
    <div style="width:48px;height:48px;border-radius:50%;background:#2a2a4a;margin:0 auto 8px;display:flex;align-items:center;justify-content:center;font-size:22px;">
      ${profile.avatar_url ? `<img src="${profile.avatar_url}" style="width:100%;height:100%;border-radius:50%;object-fit:cover;">` : emoji('person', '👤', 14)}
    </div>
    <div style="font-size:15px;font-weight:700;color:#f8fafc;margin-bottom:4px;">${profile.display_name || profile.username}</div>
    <div style="font-size:11px;color:#64748b;margin-bottom:14px;">Level ${profile.level || 1}</div>
    <div style="display:flex;gap:8px;justify-content:center;">
      <button id="opp-add-friend" style="padding:8px 16px;background:#2563EB;border:none;border-radius:8px;color:#fff;font-size:12px;font-weight:600;cursor:pointer;">➕ صديق</button>
      <button id="opp-close" style="padding:8px 16px;background:rgba(255,255,255,0.06);border:1px solid rgba(255,255,255,0.08);border-radius:8px;color:#94a3b8;font-size:12px;cursor:pointer;">إغلاق</button>
    </div>
  `;
  document.body.appendChild(popup);

  popup.querySelector('#opp-close').addEventListener('click', () => popup.remove());
  popup.querySelector('#opp-add-friend').addEventListener('click', async () => {
    await mp.addFriendFromGame(profile.id);
    popup.querySelector('#opp-add-friend').textContent = '✓ تم';
    popup.querySelector('#opp-add-friend').style.background = '#34D399';
    juice.hapticLight();
    setTimeout(() => popup.remove(), 1000);
  });

  // Close on outside click
  setTimeout(() => {
    document.addEventListener('click', function close(e) {
      if (!popup.contains(e.target)) { popup.remove(); document.removeEventListener('click', close); }
    });
  }, 200);
}

// ===== PIECE SELECTION + ANIMATION =====
let highlightedPieces = [];
let selectionListener = null;

function highlightMovablePieces(moves) {
  highlightedPieces = moves.map(m => m.pieceId);
  drawBoard(); // Redraw with highlights
}

function waitForPieceSelection(el, moves) {
  // Listen for canvas tap to select a piece
  const handler = (e) => {
    const rect = canvas.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;
    const scaleX = boardSize / rect.width;
    const scaleY = boardSize / rect.height;
    const cx = x * scaleX;
    const cy = y * scaleY;

    // Find which movable piece was tapped
    for (const move of moves) {
      const pIdx = parseInt(move.pieceId.split('-')[0]);
      const pieceIdx = parseInt(move.pieceId.split('-')[1]);
      const piece = game.players[pIdx].pieces[pieceIdx];
      let pos;
      if (piece.pos === -1) pos = getHomeBasePosition(pIdx, pieceIdx, cellSize);
      else pos = getPiecePosition(piece.pos, pIdx, cellSize);
      if (!pos) continue;

      const dist = Math.sqrt((cx - pos.x) ** 2 + (cy - pos.y) ** 2);
      if (dist < cellSize * 0.6) {
        // Selected this piece!
        canvas.removeEventListener('click', handler);
        selectionListener = null;
        highlightedPieces = [];
        audio.play('click');
        juice.hapticLight();
        animateMove(el, move);
        return;
      }
    }
  };

  selectionListener = handler;
  canvas.addEventListener('click', handler);
}

async function animateMove(el, move) {
  const pIdx = parseInt(move.pieceId.split('-')[0]);
  const pieceIdx = parseInt(move.pieceId.split('-')[1]);
  const piece = game.players[pIdx].pieces[pieceIdx];
  const fromPos = piece.pos;
  const toPos = move.to;

  if (move.type === 'enter') {
    // Pop-out from home base with bounce
    rules.applyMove(game, game.currentPlayer, move);
    piece._bounceOffset = -cellSize * 0.6;
    drawBoard();
    audio.play('move', 'game');
    juice.hapticMedium();
    // Bounce down
    await new Promise(r => setTimeout(r, 80));
    piece._bounceOffset = -cellSize * 0.3;
    drawBoard();
    await new Promise(r => setTimeout(r, 60));
    piece._bounceOffset = cellSize * 0.1;
    drawBoard();
    await new Promise(r => setTimeout(r, 50));
    piece._bounceOffset = 0;
    drawBoard();
    // Landing dust burst
    const pos = getPiecePosition(0, pIdx, cellSize);
    if (pos) {
      const rect = canvas.getBoundingClientRect();
      const sx = rect.left + (pos.x / boardSize) * rect.width;
      const sy = rect.top + (pos.y / boardSize) * rect.height;
      juice.burst?.(sx, sy, { count: 6, colors: [COLORS[pIdx], '#fff'], size: 4, spread: 30, duration: 400 });
    }
    afterMove(el, move);
    return;
  }

  // Step by step animation for moves on the path
  const steps = toPos - fromPos;
  if (steps <= 0 || steps > 6) {
    rules.applyMove(game, game.currentPlayer, move);
    drawBoard();
    afterMove(el, move);
    return;
  }

  // Hop one square at a time with arc-bounce
  const hopDelay = game.turboMode ? 40 : 80;
  const bounceHeight = cellSize * 0.5;
  for (let i = 1; i <= steps; i++) {
    piece.pos = fromPos + i;
    // Arc up
    piece._bounceOffset = -bounceHeight;
    drawBoard();
    await new Promise(r => setTimeout(r, hopDelay));
    // Peak
    piece._bounceOffset = -bounceHeight * 1.2;
    drawBoard();
    await new Promise(r => setTimeout(r, hopDelay * 0.6));
    // Arc down
    piece._bounceOffset = -bounceHeight * 0.4;
    drawBoard();
    await new Promise(r => setTimeout(r, hopDelay * 0.6));
    // Land with squash
    piece._bounceOffset = 2;
    drawBoard();
    audio.play('move', 'game');
    await new Promise(r => setTimeout(r, hopDelay * 0.4));
    piece._bounceOffset = 0;
    drawBoard();
  }

  // Landing effect on final square
  if (steps >= 3) {
    const pos = getPiecePosition(toPos, pIdx, cellSize);
    if (pos) {
      const rect = canvas.getBoundingClientRect();
      const sx = rect.left + (pos.x / boardSize) * rect.width;
      const sy = rect.top + (pos.y / boardSize) * rect.height;
      juice.burst?.(sx, sy, { count: 4, colors: [COLORS[pIdx], '#fff'], size: 3, spread: 20, duration: 300 });
    }
    juice.hapticLight();
  }

  // Re-apply via rules to handle captures properly
  piece.pos = fromPos;
  rules.applyMove(game, game.currentPlayer, move);
  drawBoard();
  afterMove(el, move);
}

function darkenColor(hex, amount) {
  const num = parseInt(hex.replace('#', ''), 16);
  const r = Math.max(0, ((num >> 16) & 0xFF) - amount);
  const g = Math.max(0, ((num >> 8) & 0xFF) - amount);
  const b = Math.max(0, (num & 0xFF) - amount);
  return `rgb(${r},${g},${b})`;
}

function fireworkBurst(x, y, color) {
  const lighten = (hex, pct) => {
    const num = parseInt(hex.replace('#', ''), 16);
    const r = Math.min(255, ((num >> 16) & 0xFF) + pct);
    const g = Math.min(255, ((num >> 8) & 0xFF) + pct);
    const b = Math.min(255, (num & 0xFF) + pct);
    return `rgb(${r},${g},${b})`;
  };
  const colors = [color, lighten(color, 80), lighten(color, 140), '#fff'];
  // Wave 1 — fast tight burst
  juice.burst(x, y, { count: 16, colors, size: 7, spread: 90, duration: 700 });
  // Wave 2 — delayed wider burst
  setTimeout(() => juice.burst(x, y, { count: 20, colors, size: 9, spread: 160, duration: 1000, type: 'star' }), 200);
  // Wave 3 — sparkle trail
  setTimeout(() => juice.burst(x, y, { count: 10, colors: [color, '#fff'], size: 5, spread: 200, duration: 1200 }), 450);
}

function afterMove(el, move) {
  game.rolled = false;
  const mover = game.currentPlayer;

  if (move.type === 'capture') {
    if (mover === myPlayerIndex) {
      // I captured someone — celebrate!
      audio.play('capture', 'reward');
      juice.hapticSuccess();
      juice.confetti(window.innerWidth / 2, window.innerHeight / 2, 30);
      juice.starBurst(window.innerWidth / 2, window.innerHeight / 2, 10);
      juice.screenFlash('rgba(76,175,80,0.2)', 400);
    } else {
      // A bot/opponent captured — check if MY pawn was the victim
      const capturedMe = move._capturedPlayers && move._capturedPlayers.includes(myPlayerIndex);
      if (capturedMe) {
        audio.play('lose', 'game');
        juice.hapticError();
        juice.shake(el, 8, 400);
        juice.screenFlash('rgba(229,57,53,0.25)', 500);
      } else {
        audio.play('capture', 'game');
        juice.shake(el, 3, 150);
      }
    }
  } else if (move.type === 'finish') {
    if (mover === myPlayerIndex) {
      audio.play('win', 'reward');
      juice.hapticSuccess();
      const boardRect = canvas.getBoundingClientRect();
      fireworkBurst(boardRect.left + boardRect.width / 2, boardRect.top + boardRect.height / 2, COLORS[mover]);
      juice.screenFlash(COLORS[mover] + '22', 500);
    } else {
      audio.play('notification');
    }
    const panel = el.querySelector(`#pp-${mover}`);
    if (panel) panel.animate([{ background: 'rgba(76,175,80,0.3)' }, { background: 'transparent' }], { duration: 600 });
  }

  if (game.gameOver) {
    if (game.mode === 'live') syncLudoState(game.diceValue);
    endGame(el);
    return;
  }
  rules.nextTurn(game);
  updatePanels(el);
  drawBoard();

  if (game.mode === 'live') syncLudoState(game.diceValue);

  if (!isMyTurn()) handleNonPlayerTurn(el);
}
// ===== END PIECE SELECTION + ANIMATION =====

function drawBoard() {
  const cs = cellSize;
  clear(ctx, boardSize, boardSize);

  // === ENHANCEMENT 1: Rich board background with subtle texture ===
  const bgGrad = ctx.createRadialGradient(boardSize/2, boardSize/2, 0, boardSize/2, boardSize/2, boardSize*0.7);
  bgGrad.addColorStop(0, '#FFFFF8');
  bgGrad.addColorStop(1, '#F0EDE6');
  ctx.fillStyle = bgGrad;
  ctx.fillRect(0, 0, boardSize, boardSize);

  // === ENHANCEMENT 10: Board border frame with emboss ===
  ctx.save();
  ctx.shadowColor = 'rgba(0,0,0,0.3)';
  ctx.shadowBlur = 6;
  ctx.shadowOffsetX = 0;
  ctx.shadowOffsetY = 2;
  ctx.strokeStyle = '#8B7355';
  ctx.lineWidth = 3;
  ctx.strokeRect(1.5, 1.5, boardSize-3, boardSize-3);
  ctx.restore();
  // Inner gold border
  ctx.strokeStyle = '#D4A843';
  ctx.lineWidth = 1.5;
  ctx.strokeRect(4, 4, boardSize-8, boardSize-8);

  // === ENHANCEMENT 6: Home zones with gradient fill ===
  [[0,0,9],[1,0,0],[2,9,0],[3,9,9]].forEach(([p,c,r]) => {
    const hx = c*cs, hy = r*cs, hw = 6*cs;
    const homeGrad = ctx.createLinearGradient(hx, hy, hx+hw, hy+hw);
    homeGrad.addColorStop(0, COLORS[p]);
    homeGrad.addColorStop(1, COLORS_LIGHT[p]);
    ctx.fillStyle = homeGrad;
    ctx.fillRect(hx, hy, hw, hw);
    // Inner white area with subtle shadow
    const ins = cs*0.85;
    ctx.save();
    ctx.shadowColor = 'rgba(0,0,0,0.2)';
    ctx.shadowBlur = 4;
    ctx.shadowInset = true;
    ctx.fillStyle = '#FEFEFA';
    ctx.fillRect(hx+ins, hy+ins, hw-ins*2, hw-ins*2);
    ctx.restore();
    // Decorative inner border with rounded feel
    ctx.strokeStyle = COLORS[p];
    ctx.lineWidth = 2.5;
    ctx.strokeRect(hx+ins+2, hy+ins+2, hw-ins*2-4, hw-ins*2-4);
    // Corner dots decoration
    const dotR = cs * 0.15;
    ctx.fillStyle = COLORS[p];
    [[hx+ins+8,hy+ins+8],[hx+hw-ins-8,hy+ins+8],[hx+ins+8,hy+hw-ins-8],[hx+hw-ins-8,hy+hw-ins-8]].forEach(([dx,dy]) => {
      ctx.beginPath(); ctx.arc(dx, dy, dotR, 0, Math.PI*2); ctx.fill();
    });
  });

  // Cross paths with subtle warmth
  ctx.fillStyle = '#FDFCF9';
  ctx.fillRect(6*cs,0,3*cs,6*cs);
  ctx.fillRect(6*cs,9*cs,3*cs,6*cs);
  ctx.fillRect(0,6*cs,6*cs,3*cs);
  ctx.fillRect(9*cs,6*cs,6*cs,3*cs);
  ctx.fillRect(6*cs,6*cs,3*cs,3*cs);

  // === ENHANCEMENT 5: Grid lines with depth ===
  ctx.strokeStyle = 'rgba(0,0,0,0.08)';
  ctx.lineWidth = 0.5;
  for (let i = 0; i <= 15; i++) {
    if (i >= 6 && i <= 9) { ctx.beginPath(); ctx.moveTo(i*cs,0); ctx.lineTo(i*cs,boardSize); ctx.stroke(); ctx.beginPath(); ctx.moveTo(0,i*cs); ctx.lineTo(boardSize,i*cs); ctx.stroke(); }
  }
  for (let i = 0; i <= 6; i++) {
    ctx.beginPath(); ctx.moveTo(6*cs,i*cs); ctx.lineTo(9*cs,i*cs); ctx.stroke();
    ctx.beginPath(); ctx.moveTo(6*cs,(i+9)*cs); ctx.lineTo(9*cs,(i+9)*cs); ctx.stroke();
    ctx.beginPath(); ctx.moveTo(i*cs,6*cs); ctx.lineTo(i*cs,9*cs); ctx.stroke();
    ctx.beginPath(); ctx.moveTo((i+9)*cs,6*cs); ctx.lineTo((i+9)*cs,9*cs); ctx.stroke();
  }

  // === ENHANCEMENT 5: Home columns with gradient cells ===
  for (let p = 0; p < 4; p++) {
    HOME_COLUMNS[p].forEach(([col,row], i) => {
      const cellGrad = ctx.createLinearGradient(col*cs, row*cs, col*cs+cs, row*cs+cs);
      cellGrad.addColorStop(0, COLORS_LIGHT[p]);
      cellGrad.addColorStop(1, COLORS[p] + '40');
      ctx.fillStyle = cellGrad;
      ctx.fillRect(col*cs+1, row*cs+1, cs-2, cs-2);
      // Arrow indicator toward center
      ctx.fillStyle = COLORS[p] + '60';
      ctx.font = `${cs*0.35}px sans-serif`;
      ctx.textAlign = 'center';
      ctx.textBaseline = 'middle';
      const arrows = ['→','↓','←','↑'];
      ctx.fillText(arrows[p], col*cs+cs/2, row*cs+cs/2);
    });
  }

  // === ENHANCEMENT 4: Safe squares with glow effect ===
  SAFE_SQUARES.forEach(idx => {
    const [col,row] = SHARED_PATH[idx];
    // Glow background
    const safeGrad = ctx.createRadialGradient(col*cs+cs/2, row*cs+cs/2, 0, col*cs+cs/2, row*cs+cs/2, cs*0.6);
    safeGrad.addColorStop(0, '#FFF9C4');
    safeGrad.addColorStop(1, '#FFF176');
    ctx.fillStyle = safeGrad;
    ctx.fillRect(col*cs+1, row*cs+1, cs-2, cs-2);
    // Star with shadow
    ctx.save();
    ctx.shadowColor = '#F9A825';
    ctx.shadowBlur = 4;
    ctx.fillStyle = '#F57F17';
    ctx.font = `bold ${cs*0.5}px sans-serif`;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText('★', col*cs+cs/2, row*cs+cs/2);
    ctx.restore();
  });

  // === ENHANCEMENT 2: Center triangles with gradient glow ===
  const ccx = 7.5*cs, ccy = 7.5*cs, hs = 1.5*cs;
  // Draw each triangle with gradient
  const triPoints = [
    { p: 0, pts: [[ccx-hs,ccy+hs],[ccx+hs,ccy+hs],[ccx,ccy]] },
    { p: 1, pts: [[ccx-hs,ccy-hs],[ccx-hs,ccy+hs],[ccx,ccy]] },
    { p: 2, pts: [[ccx-hs,ccy-hs],[ccx+hs,ccy-hs],[ccx,ccy]] },
    { p: 3, pts: [[ccx+hs,ccy-hs],[ccx+hs,ccy+hs],[ccx,ccy]] },
  ];
  triPoints.forEach(({p, pts}) => {
    const grad = ctx.createRadialGradient(ccx, ccy, 0, ccx, ccy, hs);
    grad.addColorStop(0, '#fff');
    grad.addColorStop(0.3, COLORS_LIGHT[p]);
    grad.addColorStop(1, COLORS[p]);
    ctx.fillStyle = grad;
    ctx.beginPath();
    ctx.moveTo(pts[0][0], pts[0][1]);
    ctx.lineTo(pts[1][0], pts[1][1]);
    ctx.lineTo(pts[2][0], pts[2][1]);
    ctx.closePath();
    ctx.fill();
  });
  // Center diamond highlight
  ctx.save();
  ctx.shadowColor = 'rgba(255,255,255,0.8)';
  ctx.shadowBlur = 6;
  ctx.fillStyle = 'rgba(255,255,255,0.6)';
  ctx.beginPath();
  ctx.arc(ccx, ccy, cs*0.3, 0, Math.PI*2);
  ctx.fill();
  ctx.restore();
  // Outer border of center
  ctx.strokeStyle = 'rgba(255,255,255,0.9)';
  ctx.lineWidth = 2;
  ctx.strokeRect(ccx-hs, ccy-hs, hs*2, hs*2);

  // Start position colored squares with glow
  const startColors = [[6,13,'#FFCDD2'],[1,6,'#C8E6C9'],[8,1,'#FFF9C4'],[13,8,'#BBDEFB']];
  startColors.forEach(([col,row,color]) => {
    ctx.fillStyle = color;
    ctx.fillRect(col*cs+1, row*cs+1, cs-2, cs-2);
    // Arrow showing entry direction
    ctx.fillStyle = 'rgba(0,0,0,0.2)';
    ctx.font = `${cs*0.4}px sans-serif`;
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.fillText('▶', col*cs+cs/2, row*cs+cs/2);
  });

  // === ENHANCEMENT 8: Ambient corner glow for current player ===
  if (game && !game.gameOver) {
    const cp = game.currentPlayer;
    const corners = [[0,boardSize],[0,0],[boardSize,0],[boardSize,boardSize]];
    const [gx, gy] = corners[cp];
    const ambGrad = ctx.createRadialGradient(gx, gy, 0, gx, gy, cs*5);
    ambGrad.addColorStop(0, COLORS[cp] + '25');
    ambGrad.addColorStop(1, 'transparent');
    ctx.fillStyle = ambGrad;
    ctx.fillRect(0, 0, boardSize, boardSize);
  }

  // Move preview — draw ghost circles at destination for highlighted pieces
  if (highlightedPieces.length > 0 && validMoves && validMoves.length > 0) {
    validMoves.forEach(move => {
      const pIdx = parseInt(move.pieceId.split('-')[0]);
      let destPos;
      if (move.type === 'enter') {
        destPos = getPiecePosition(move.to, pIdx, cs);
      } else {
        destPos = getPiecePosition(move.to, pIdx, cs);
      }
      if (destPos) {
        const r = cs * 0.42;
        ctx.beginPath();
        ctx.arc(destPos.x, destPos.y, r, 0, Math.PI * 2);
        ctx.globalAlpha = 0.3;
        ctx.fillStyle = COLORS[pIdx];
        ctx.fill();
        ctx.globalAlpha = 1.0;
        // Dashed outline
        ctx.setLineDash([3, 3]);
        ctx.strokeStyle = COLORS[pIdx];
        ctx.lineWidth = 1.5;
        ctx.stroke();
        ctx.setLineDash([]);
      }
    });
  }

  // Pieces — with highlight for selectable ones
  // First, group pieces by their cell position to handle stacking
  const cellOccupants = new Map(); // key: "x,y" -> [{pIdx, pieceIdx, piece}]
  game.players.forEach((player, pIdx) => {
    player.pieces.forEach((piece, pieceIdx) => {
      if (piece.finished) return;
      let pos;
      if (piece.pos === -1) pos = getHomeBasePosition(pIdx, pieceIdx, cs);
      else pos = getPiecePosition(piece.pos, pIdx, cs);
      if (!pos) return;
      const key = `${Math.round(pos.x)},${Math.round(pos.y)}`;
      if (!cellOccupants.has(key)) cellOccupants.set(key, []);
      cellOccupants.get(key).push({ pIdx, pieceIdx, piece, pos });
    });
  });

  // Grid offsets for 1-4 pawns sharing a cell
  const STACK_OFFSETS = [
    [[0, 0]],
    [[-0.35, -0.3], [0.35, 0.3]],
    [[-0.35, -0.35], [0.35, -0.35], [0, 0.35]],
    [[-0.35, -0.35], [0.35, -0.35], [-0.35, 0.35], [0.35, 0.35]],
  ];

  cellOccupants.forEach((occupants) => {
    const count = occupants.length;
    const offsets = STACK_OFFSETS[Math.min(count, 4) - 1];
    const scale = count === 1 ? 1 : count === 2 ? 0.72 : 0.6;

    occupants.forEach((occ, idx) => {
      const { pIdx, pieceIdx, piece, pos } = occ;
      const pieceId = `${pIdx}-${pieceIdx}`;
      const isHighlighted = highlightedPieces.includes(pieceId);
      const r = cs * 0.42 * scale;
      const offset = offsets[idx % offsets.length];
      const ox = pos.x + offset[0] * cs;
      const oy = pos.y + offset[1] * cs;

      const bounceY = piece._bounceOffset || 0;
      const drawY = oy + bounceY;

      // === ENHANCEMENT 7: Highlight glow ring for selectable ===
      if (isHighlighted) {
        ctx.save();
        ctx.shadowColor = '#E4AC38';
        ctx.shadowBlur = 8;
        ctx.beginPath(); ctx.arc(ox, drawY, r + 3, 0, Math.PI*2);
        ctx.strokeStyle = '#E4AC38'; ctx.lineWidth = 2.5; ctx.stroke();
        ctx.restore();
        ctx.beginPath(); ctx.arc(ox, drawY, r + 6, 0, Math.PI*2);
        ctx.strokeStyle = 'rgba(228,172,56,0.25)'; ctx.lineWidth = 1.5; ctx.stroke();
      }

      // === ENHANCEMENT 3: 3D pawn with gradient body ===
      // Drop shadow
      ctx.save();
      ctx.shadowColor = 'rgba(0,0,0,0.35)';
      ctx.shadowBlur = 3;
      ctx.shadowOffsetY = 2;
      ctx.beginPath(); ctx.ellipse(ox, oy + r*0.5, r*0.7, r*0.3, 0, 0, Math.PI*2);
      ctx.fillStyle = 'rgba(0,0,0,0.25)'; ctx.fill();
      ctx.restore();

      // Base (wider ellipse) with gradient
      const baseGrad = ctx.createLinearGradient(ox-r, drawY+r*0.2, ox+r, drawY+r*0.6);
      baseGrad.addColorStop(0, COLORS_LIGHT[pIdx]);
      baseGrad.addColorStop(0.5, COLORS[pIdx]);
      baseGrad.addColorStop(1, darkenColor(COLORS[pIdx], 40));
      ctx.beginPath(); ctx.ellipse(ox, drawY + r*0.35, r*0.82, r*0.42, 0, 0, Math.PI*2);
      ctx.fillStyle = baseGrad; ctx.fill();
      ctx.strokeStyle = 'rgba(255,255,255,0.7)'; ctx.lineWidth = 1.2; ctx.stroke();

      // Head (circle) with radial gradient for 3D
      const headGrad = ctx.createRadialGradient(ox - r*0.15, drawY - r*0.3, 0, ox, drawY - r*0.1, r*0.7);
      headGrad.addColorStop(0, COLORS_LIGHT[pIdx]);
      headGrad.addColorStop(0.6, COLORS[pIdx]);
      headGrad.addColorStop(1, darkenColor(COLORS[pIdx], 50));
      ctx.beginPath(); ctx.arc(ox, drawY - r*0.1, r*0.6, 0, Math.PI*2);
      ctx.fillStyle = headGrad; ctx.fill();
      ctx.strokeStyle = 'rgba(255,255,255,0.8)'; ctx.lineWidth = 1.5; ctx.stroke();

      // Shine highlight (top-left specular)
      ctx.beginPath(); ctx.arc(ox - r*0.18, drawY - r*0.3, r*0.2, 0, Math.PI*2);
      ctx.fillStyle = 'rgba(255,255,255,0.7)'; ctx.fill();
      // Smaller secondary shine
      ctx.beginPath(); ctx.arc(ox - r*0.05, drawY - r*0.45, r*0.08, 0, Math.PI*2);
      ctx.fillStyle = 'rgba(255,255,255,0.5)'; ctx.fill();
    });
  });
}

function checkTurboMode(el) {
  if (game.turboMode) return;
  const elapsed = (Date.now() - game.startTime) / 60000;
  if (elapsed >= 12) {
    game.turboMode = true;
    const banner = document.createElement('div');
    banner.style.cssText = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background:rgba(228,172,56,0.95);color:#000;padding:10px 24px;border-radius:12px;font-size:14px;font-weight:700;z-index:100;';
    banner.textContent = '⚡ وضع السرعة!';
    el.querySelector('#ludo-wrap')?.appendChild(banner);
    banner.animate([{opacity:0,transform:'translate(-50%,-50%) scale(0.5)'},{opacity:1,transform:'translate(-50%,-50%) scale(1)'},{opacity:0,transform:'translate(-50%,-50%) translateY(-20px)'}], {duration:2000}).onfinish = () => banner.remove();
    juice.hapticHeavy();
  }
}

function updatePanels(el) {
  checkTurboMode(el);
  for (let i = 0; i < 4; i++) {
    const p = el.querySelector(`#pp-${i}`);
    if (p) {
      const wasActive = p.classList.contains('active');
      const isNowActive = i === game.currentPlayer;
      p.classList.toggle('active', isNowActive);
      if (game.players[i].finished) p.style.opacity = '0.5';
      // Hide dice when turn changes away from this player
      if (wasActive && !isNowActive) {
        const md = el.querySelector(`#dice-${i}`);
        if (md) { setTimeout(() => { md.className = 'pp-dice'; md.style.animation = ''; }, 1200); }
      }
    }
  }
  const diceArea = el.querySelector('#dice-area');
  const btn = el.querySelector('#roll-btn');
  const diceBox = el.querySelector('#dice-box');
  const myTurn = isMyTurn() && !game.players[myPlayerIndex].finished && !game.gameOver;
  if (diceArea) {
    diceArea.style.opacity = myTurn ? '1' : '0.3';
    diceArea.style.pointerEvents = myTurn ? 'auto' : 'none';
  }
  if (diceBox) diceBox.style.visibility = myTurn || diceAnimating ? 'visible' : 'hidden';
  if (btn) {
    const canRoll = myTurn && !game.rolled && !diceAnimating;
    btn.disabled = !canRoll;
    btn.style.opacity = canRoll ? '1' : '0.4';
    btn.style.visibility = myTurn ? 'visible' : 'hidden';
  }
}

function animateDice(el, playerIdx) {
  return new Promise(resolve => {
    const mainDice = el.querySelector('#dice-box');
    const miniDice = el.querySelector(`#dice-${playerIdx}`);
    const isMe = playerIdx === myPlayerIndex;

    // Show mini dice in rolling state
    if (miniDice) {
      miniDice.className = 'pp-dice rolling';
      renderMiniDice(miniDice, 1);
    }

    // Show main dice for everyone (not just self)
    if (mainDice) {
      mainDice.style.visibility = 'visible';
      mainDice.style.opacity = '1';
    }

    let count = 0;
    const maxFrames = game.turboMode ? 8 : 14;
    const shakeAnim = setInterval(() => {
      const randVal = Math.floor(Math.random() * 6) + 1;
      renderDiceFace(mainDice, randVal);
      if (miniDice) renderMiniDice(miniDice, randVal);
      const rx = (Math.random() - 0.5) * 16;
      const ry = (Math.random() - 0.5) * 16;
      const rot = (Math.random() - 0.5) * 30;
      mainDice.style.transform = `translate(${rx}px,${ry}px) rotate(${rot}deg) scale(0.9)`;
      count++;
      if (count > maxFrames) {
        clearInterval(shakeAnim);
        const dice = rules.rollDice(game, game.currentPlayer);
        game.diceValue = dice;
        renderDiceFace(mainDice, dice);
        if (miniDice) renderMiniDice(miniDice, dice);

        // Pop-in landing on main dice
        mainDice.style.transform = 'scale(1.3)';
        setTimeout(() => { mainDice.style.transform = 'scale(1)'; }, 180);

        // Mini dice lands with pop
        if (miniDice) {
          miniDice.className = 'pp-dice landed';
          miniDice.style.animation = 'dicePopIn 0.35s cubic-bezier(0.34,1.56,0.64,1)';
          if (dice === 6) miniDice.style.animation += ', diceSix 1s ease-in-out infinite';
        }

        audio.play('dice', 'game');
        juice.hapticHeavy();
        juice.shake(el.querySelector(isMe ? '#dice-area' : `#pp-${playerIdx}`), 4, 200);

        if (dice === 6) {
          mainDice.style.boxShadow = '0 0 20px #E4AC38, 0 4px 14px rgba(0,0,0,0.4)';
          setTimeout(() => { mainDice.style.boxShadow = '0 4px 14px rgba(0,0,0,0.4),inset 0 1px 0 rgba(255,255,255,0.8)'; }, 800);
          juice.hapticSuccess();
          // Flash the player panel gold on a 6
          const panel = el.querySelector(`#pp-${playerIdx}`);
          if (panel) panel.animate([{background:'rgba(228,172,56,0.3)'},{background:'transparent'}], {duration:600});
        }

        resolve(dice);
      }
    }, 55);
  });
}

function renderMiniDice(miniDice, value) {
  const dots = { 1:[0,0,0,0,1,0,0,0,0], 2:[0,0,1,0,0,0,1,0,0], 3:[0,0,1,0,1,0,1,0,0], 4:[1,0,1,0,0,0,1,0,1], 5:[1,0,1,0,1,0,1,0,1], 6:[1,0,1,1,0,1,1,0,1] };
  const pattern = dots[value] || dots[1];
  miniDice.style.display = 'grid';
  miniDice.style.gridTemplate = 'repeat(3,1fr)/repeat(3,1fr)';
  miniDice.style.padding = '3px';
  miniDice.innerHTML = pattern.map(d =>
    `<div style="width:5px;height:5px;border-radius:50%;background:${d ? '#1a1a2e' : 'transparent'};margin:auto;${d ? 'box-shadow:0 1px 1px rgba(0,0,0,0.2);' : ''}"></div>`
  ).join('');
}

function handleExit(el) {
  if (game.gameOver) return;
  if (!confirm('هل تريد الخروج من المباراة؟')) return;

  game.gameOver = true;
  audio.play('click');

  if (game.mode === 'live' && matchId) {
    // Multiplayer: notify server to replace player with bot
    net.post('ludo-match.php', { action: 'leave', match_id: matchId, player_index: myPlayerIndex }).catch(() => {});
    mp.stopDisconnectWatch();
    if (livePoller) { clearInterval(livePoller); livePoller = null; }
  }

  scene.exitGameMode();
  scene.replace('ludo-result', { result: 'loss', resigned: true });
  bus.emit('game:ended', { gameKey: 'ludo', result: 'loss', resigned: true });
}

function endGame(el) {
  game.gameOver = true;
  if (matchId) matchLive.session?.destroy?.();

  const myRank = game.winners.indexOf(myPlayerIndex);
  // Ranks 0,1,2 = 1st,2nd,3rd place (podium). Rank 3 = 4th (loser)
  const isLoser = myRank === 3 || myRank === -1;
  const result = isLoser ? 'loss' : 'win';
  const place = myRank >= 0 ? myRank + 1 : 4;

  if (!isLoser) {
    juice.confetti(window.innerWidth / 2, window.innerHeight / 3, 50);
    juice.starBurst(window.innerWidth / 2, window.innerHeight / 3, 15);
    juice.hapticSuccess();
    audio.play('win', 'reward');
    juice.screenFlash('rgba(76,175,80,0.12)', 600);
    if (place === 1) {
      fireworkBurst(window.innerWidth / 2, window.innerHeight / 3, COLORS[myPlayerIndex]);
    }
  } else {
    audio.play('lose', 'game');
    juice.hapticError();
    juice.screenFlash('rgba(229,57,53,0.12)', 500);
  }

  setTimeout(() => {
    scene.exitGameMode();
    scene.replace('ludo-result', { result, place, winners: game.winners });
    bus.emit('game:ended', { gameKey: 'ludo', result, place });
  }, 1500);
}
