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-avatar" style="width:28px;height:28px;border-radius:50%;background:#2a2a4a;border:2px solid ${p.color};display:flex;align-items:center;justify-content:center;font-size:14px;overflow:hidden;">
        ${p.avatar}
      </div>
      <div style="display:flex;flex-direction:column;">
        <span style="font-size:11px;font-weight:600;color:#f8fafc;">${p.name}</span>
        ${p.level ? `<span style="font-size:10px;color:#64748b;">${p.level}</span>` : ''}
      </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.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:#1a1a2e;justify-content:flex-end;">
      <div style="display:flex;justify-content:space-between;padding:6px 12px;background:#0f0f1e;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:4px;min-height:0;"></div>
      <div style="display:flex;justify-content:space-between;padding:6px 12px;background:#0f0f1e;direction:ltr;">
        ${renderPanel(panels[0])}
        ${renderPanel(panels[3])}
      </div>
      <div id="dice-area" style="display:flex;align-items:center;gap:12px;padding:12px 16px;background:#0f0f1e;border-top:1px solid rgba(255,255,255,0.06);justify-content:center;padding-bottom:max(12px, env(safe-area-inset-bottom, 0px));">
        <button class="btn btn-secondary" id="exit-btn" style="min-height:48px;min-width:48px;padding:0;font-size:14px;color:#EF4444;border-radius:50%;">✕</button>
        <div id="dice-box" style="width:52px;height:52px;background:#f8fafc;border-radius:10px;display:grid;grid-template:repeat(3,1fr)/repeat(3,1fr);padding:6px;box-shadow:0 3px 10px rgba(0,0,0,0.4),inset 0 1px 0 rgba(255,255,255,0.8);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:12px;" disabled>ارمِ النرد</button>
      </div>
    </div>
    <style>
      .pp{display:flex;align-items:center;gap:6px;padding:5px 10px;border-radius:8px;border:2px solid transparent;transition:border-color 0.3s;}
      .pp.active{border-color:var(--pc);background:rgba(255,255,255,0.05);}
      .pp-dot{width:10px;height:10px;border-radius:50%;background:var(--pc);}
      .pp span{font-size:12px;font-weight:600;color:#94a3b8;}
      .pp.active span{color:#f8fafc;}
      .pp-dice{width:28px;height:28px;background:#f8fafc;border-radius:5px;display:grid;grid-template:repeat(3,1fr)/repeat(3,1fr);padding:3px;box-shadow:0 2px 6px rgba(0,0,0,0.3);transition:transform 0.15s;opacity:0.4;}
      .pp.active .pp-dice{opacity:1;}
    </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);
  }
  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()) 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()) 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 ===
  // 1. "Thinking" before rolling — based on personality
  const thinkDelay = personality.thinkMin + Math.random() * (personality.thinkMax - personality.thinkMin);

  // 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;

  // 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) {
    for (let i = 1; i <= toPos - fromPos; i++) {
      piece.pos = fromPos + i;
      drawBoard();
      audio.play('move', 'game');
      await new Promise(r => setTimeout(r, 100));
    }
    piece.pos = fromPos;
  }

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

  if (move.type === 'capture') {
    audio.play('capture', 'game');
    juice.shake(el, 3, 150);
  } else if (move.type === 'finish') {
    audio.play('win', 'reward');
  }

  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') {
    // Entering from home — just one hop
    rules.applyMove(game, game.currentPlayer, move);
    drawBoard();
    audio.play('move', 'game');
    juice.hapticLight();
    afterMove(el, move);
    return;
  }

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

  // Hop one square at a time with vertical bounce
  for (let i = 1; i <= steps; i++) {
    piece.pos = fromPos + i;
    // Bounce up: draw piece 2px higher
    piece._bounceOffset = -2;
    drawBoard();
    audio.play('move', 'game');
    await new Promise(r => setTimeout(r, 60));
    // Bounce down: return to normal
    piece._bounceOffset = 0;
    drawBoard();
    await new Promise(r => setTimeout(r, 60));
  }

  // Apply capture/finish effects after animation
  // Re-apply via rules to handle captures properly
  piece.pos = fromPos; // Reset
  rules.applyMove(game, game.currentPlayer, move);
  drawBoard();
  afterMove(el, move);
}

function afterMove(el, move) {
  game.rolled = false;
  if (move.type === 'capture') { audio.play('capture','game'); juice.shake(el,4,200); juice.hapticHeavy(); juice.confetti(window.innerWidth/2, window.innerHeight/2, 15); }
  else if (move.type === 'finish') {
    audio.play('win','reward'); juice.hapticSuccess(); juice.starBurst(window.innerWidth/2, window.innerHeight/2, 8);
    // Extra particle burst at center of the board
    const boardRect = canvas.getBoundingClientRect();
    juice.starBurst(boardSize/2 + boardRect.left, boardSize/2 + boardRect.top, 10);
  }

  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);

  // Background
  ctx.fillStyle = '#FAFAFA';
  ctx.fillRect(0, 0, boardSize, boardSize);

  // Home zones — with inner white area
  // [playerIdx, gridCol, gridRow]: Red(BL), Green(TL), Yellow(TR), Blue(BR)
  [[0,0,9],[1,0,0],[2,9,0],[3,9,9]].forEach(([p,c,r]) => {
    ctx.fillStyle = COLORS[p];
    ctx.fillRect(c*cs, r*cs, 6*cs, 6*cs);
    // Inner white area
    ctx.fillStyle = '#FAFAFA';
    const ins = cs*0.85;
    ctx.fillRect(c*cs+ins, r*cs+ins, 6*cs-ins*2, 6*cs-ins*2);
    // Inner colored border
    ctx.strokeStyle = COLORS[p];
    ctx.lineWidth = 2.5;
    ctx.strokeRect(c*cs+ins+3, r*cs+ins+3, 6*cs-ins*2-6, 6*cs-ins*2-6);
  });

  // Cross paths (white)
  ctx.fillStyle = '#fff';
  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);

  // Grid lines
  ctx.strokeStyle = '#ddd';
  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();
  }

  // Home columns
  for (let p = 0; p < 4; p++) HOME_COLUMNS[p].forEach(([col,row]) => { ctx.fillStyle = COLORS_LIGHT[p]; ctx.fillRect(col*cs+1, row*cs+1, cs-2, cs-2); });

  // Safe squares
  SAFE_SQUARES.forEach(idx => { const [col,row] = SHARED_PATH[idx]; ctx.fillStyle = '#FFF9C4'; ctx.fillRect(col*cs+1,row*cs+1,cs-2,cs-2); ctx.fillStyle = '#F9A825'; ctx.font = `${cs*0.45}px sans-serif`; ctx.textAlign = 'center'; ctx.textBaseline = 'middle'; ctx.fillText('★', col*cs+cs/2, row*cs+cs/2); });

  // Center — 4 colored triangles forming a square (finish area)
  const cx = 7.5*cs, cy = 7.5*cs, hs = 1.5*cs;
  // Red triangle (bottom → points up) — player 0
  ctx.fillStyle = COLORS[0]; ctx.beginPath();
  ctx.moveTo(cx-hs, cy+hs); ctx.lineTo(cx+hs, cy+hs); ctx.lineTo(cx, cy); ctx.closePath(); ctx.fill();
  // Green triangle (left → points right) — player 1
  ctx.fillStyle = COLORS[1]; ctx.beginPath();
  ctx.moveTo(cx-hs, cy-hs); ctx.lineTo(cx-hs, cy+hs); ctx.lineTo(cx, cy); ctx.closePath(); ctx.fill();
  // Yellow triangle (top → points down) — player 2
  ctx.fillStyle = COLORS[2]; ctx.beginPath();
  ctx.moveTo(cx-hs, cy-hs); ctx.lineTo(cx+hs, cy-hs); ctx.lineTo(cx, cy); ctx.closePath(); ctx.fill();
  // Blue triangle (right → points left) — player 3
  ctx.fillStyle = COLORS[3]; ctx.beginPath();
  ctx.moveTo(cx+hs, cy-hs); ctx.lineTo(cx+hs, cy+hs); ctx.lineTo(cx, cy); ctx.closePath(); ctx.fill();
  // Center border
  ctx.strokeStyle = '#fff'; ctx.lineWidth = 1.5;
  ctx.strokeRect(cx-hs, cy-hs, hs*2, hs*2);

  // Start position colored squares — where each player's piece enters the board
  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); });

  // 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
  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) {
        const pieceId = `${pIdx}-${pieceIdx}`;
        const isHighlighted = highlightedPieces.includes(pieceId);
        const r = cs * 0.42;

        // Apply vertical bounce offset if present
        const bounceY = piece._bounceOffset || 0;
        const drawY = pos.y + bounceY;

        // Glow ring for selectable pieces
        if (isHighlighted) {
          ctx.beginPath(); ctx.arc(pos.x, drawY, r + 4, 0, Math.PI*2);
          ctx.strokeStyle = '#E4AC38'; ctx.lineWidth = 3; ctx.stroke();
          ctx.beginPath(); ctx.arc(pos.x, drawY, r + 7, 0, Math.PI*2);
          ctx.strokeStyle = 'rgba(228,172,56,0.3)'; ctx.lineWidth = 2; ctx.stroke();
        }

        // Shadow
        ctx.beginPath(); ctx.arc(pos.x, pos.y + 2, r, 0, Math.PI*2); ctx.fillStyle = 'rgba(0,0,0,0.2)'; ctx.fill();
        // Body (pawn shape — circle on top of tapered base)
        ctx.beginPath(); ctx.arc(pos.x, drawY - r*0.15, r*0.65, 0, Math.PI*2); ctx.fillStyle = COLORS[pIdx]; ctx.fill();
        // Base
        ctx.beginPath(); ctx.ellipse(pos.x, drawY + r*0.4, r*0.8, r*0.4, 0, 0, Math.PI*2); ctx.fillStyle = COLORS[pIdx]; ctx.fill();
        // Border
        ctx.beginPath(); ctx.arc(pos.x, drawY - r*0.15, r*0.65, 0, Math.PI*2); ctx.strokeStyle = '#fff'; ctx.lineWidth = 2; ctx.stroke();
        ctx.beginPath(); ctx.ellipse(pos.x, drawY + r*0.4, r*0.8, r*0.4, 0, 0, Math.PI*2); ctx.strokeStyle = '#fff'; ctx.lineWidth = 1.5; ctx.stroke();
        // Highlight
        ctx.beginPath(); ctx.arc(pos.x - r*0.2, drawY - r*0.35, r*0.2, 0, Math.PI*2); ctx.fillStyle = 'rgba(255,255,255,0.5)'; ctx.fill();
      }
    });
  });
}

function updatePanels(el) {
  for (let i = 0; i < 4; i++) {
    const p = el.querySelector(`#pp-${i}`);
    if (p) p.classList.toggle('active', i === game.currentPlayer);
  }
  const btn = el.querySelector('#roll-btn');
  if (btn) {
    const canRoll = isMyTurn() && !game.rolled && !diceAnimating && !game.gameOver;
    btn.disabled = !canRoll;
    btn.style.opacity = canRoll ? '1' : '0.4';
  }
}

function animateDice(el, playerIdx) {
  return new Promise(resolve => {
    const mainDice = el.querySelector('#dice-box');
    const miniDice = el.querySelector(`#dice-${playerIdx}`);
    let count = 0;
    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)`;
      if (miniDice) miniDice.style.transform = `rotate(${rot * 0.5}deg) scale(0.85)`;
      count++;
      if (count > 14) {
        clearInterval(shakeAnim);
        const dice = rules.rollDice();
        game.diceValue = dice;
        renderDiceFace(mainDice, dice);
        if (miniDice) renderMiniDice(miniDice, dice);
        mainDice.style.transform = 'scale(1.25)';
        if (miniDice) miniDice.style.transform = 'scale(1.15)';
        setTimeout(() => {
          mainDice.style.transform = 'scale(1)';
          if (miniDice) miniDice.style.transform = 'scale(1)';
        }, 150);
        audio.play('dice', 'game');
        juice.hapticHeavy();
        juice.shake(el.querySelector('#dice-area'), 3, 150);
        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)'; }, 600);
          juice.hapticSuccess();
        }
        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.innerHTML = pattern.map(d =>
    `<div style="width:5px;height:5px;border-radius:50%;background:${d ? '#1a1a2e' : 'transparent'};margin:auto;"></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 result = game.winners[0] === myPlayerIndex ? 'win' : 'loss';
  if (result === 'win') { juice.confetti(window.innerWidth/2, window.innerHeight/3, 40); juice.hapticSuccess(); audio.play('win','reward'); }
  else { audio.play('lose','game'); juice.hapticError(); }
  setTimeout(() => { scene.exitGameMode(); scene.replace('ludo-result', { result, winners: game.winners }); bus.emit('game:ended', { gameKey: 'ludo', result }); }, 1500);
}
