<?php
require_once __DIR__ . '/../config/database.php';

header('Content-Type: application/json');

if (!check_feature_flag('backgammon_enabled')) {
    http_response_code(403);
    echo json_encode(['error' => 'feature_disabled', 'message' => 'لعبة الطاولة معطلة حالياً']);
    exit;
}

$token = get_auth_token();

if (!$token) {
    http_response_code(401);
    echo json_encode(['error' => 'unauthorized']);
    exit;
}

$userRes = supabase_auth('user', [], $token, 'GET');
$userId = $userRes['data']['id'] ?? null;
$userName = $userRes['data']['user_metadata']['display_name'] ?? 'لاعب';

if (!$userId) {
    http_response_code(401);
    echo json_encode(['error' => 'invalid_token']);
    exit;
}

if ($_SERVER['REQUEST_METHOD'] === 'GET') {
    $action = $_GET['action'] ?? '';
    handleGet($action, $userId, $token);
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $input = json_decode(file_get_contents('php://input'), true);
    $action = $input['action'] ?? '';
    handlePost($action, $input, $userId, $userName, $token);
} else {
    http_response_code(405);
    echo json_encode(['error' => 'method_not_allowed']);
}

// ─── GET Handlers ───────────────────────────────────────────────────────────

function handleGet($action, $userId, $token) {
    switch ($action) {
        case 'status':
            $matchId = $_GET['match_id'] ?? '';
            if (!$matchId) {
                http_response_code(400);
                echo json_encode(['error' => 'match_id_required']);
                return;
            }
            $res = supabase_rest('GET', "backgammon_matches?id=eq.{$matchId}&select=*", [], SUPABASE_SERVICE_KEY);
            if (empty($res['data'])) {
                http_response_code(404);
                echo json_encode(['error' => 'not_found']);
                return;
            }
            $match = $res['data'][0];
            echo json_encode(['ok' => true, 'match' => $match]);
            break;

        default:
            http_response_code(400);
            echo json_encode(['error' => 'invalid_action']);
    }
}

// ─── POST Handlers ──────────────────────────────────────────────────────────

function handlePost($action, $input, $userId, $userName, $token) {
    switch ($action) {
        case 'create':
            createRoom($input, $userId, $userName);
            break;
        case 'join':
            joinRoom($input, $userId, $userName);
            break;
        case 'start':
            startGame($input, $userId);
            break;
        case 'roll':
            rollDice($input, $userId);
            break;
        case 'move':
            makeMove($input, $userId);
            break;
        case 'double':
            proposeDouble($input, $userId);
            break;
        case 'accept_double':
            acceptDouble($input, $userId);
            break;
        case 'decline_double':
            declineDouble($input, $userId);
            break;
        case 'leave':
            leaveGame($input, $userId);
            break;
        case 'chat':
            sendChat($input, $userId, $userName);
            break;
        case 'matchmake':
            matchmake($input, $userId, $userName);
            break;
        default:
            http_response_code(400);
            echo json_encode(['error' => 'invalid_action']);
    }
}

// ─── Constants ──────────────────────────────────────────────────────────────

function getInitialBoard() {
    return [2,0,0,0,0,-5, 0,-3,0,0,0,5, -5,0,0,0,3,0, 5,0,0,0,0,-2];
}

// ─── Room Management ────────────────────────────────────────────────────────

function generateRoomCode() {
    $chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
    $code = '';
    for ($i = 0; $i < 6; $i++) {
        $code .= $chars[random_int(0, strlen($chars) - 1)];
    }
    return $code;
}

function createRoom($input, $userId, $userName) {
    $matchLength = (int)($input['match_length'] ?? 1);
    if ($matchLength < 1) $matchLength = 1;
    $bots = $input['bots'] ?? [];
    $roomCode = generateRoomCode();

    $players = [
        ['id' => $userId, 'name' => $userName, 'type' => 'human']
    ];

    foreach ($bots as $bot) {
        if (count($players) >= 2) break;
        $players[] = [
            'id' => 'bot_0',
            'name' => $bot['name'] ?? 'بوت',
            'type' => 'bot',
            'difficulty' => $bot['difficulty'] ?? 'medium'
        ];
    }

    $status = count($players) >= 2 ? 'ready' : 'waiting';

    $data = [
        'room_code' => $roomCode,
        'status' => $status,
        'players' => json_encode($players),
        'current_turn' => 0,
        'board' => json_encode(getInitialBoard()),
        'dice' => null,
        'dice_remaining' => json_encode([]),
        'bar' => json_encode([0, 0]),
        'borne_off' => json_encode([0, 0]),
        'doubling_cube' => json_encode(['value' => 1, 'owner' => null]),
        'game_state' => json_encode(['phase' => 'waiting']),
        'moves' => json_encode([]),
        'scores' => json_encode([0, 0]),
        'match_length' => $matchLength,
        'chat' => json_encode([]),
        'host_id' => $userId,
        'result' => null
    ];

    $res = supabase_rest('POST', 'backgammon_matches', $data, SUPABASE_SERVICE_KEY);
    if ($res['status'] >= 400) {
        http_response_code(500);
        echo json_encode(['error' => 'create_failed', 'details' => $res['data']]);
        return;
    }

    echo json_encode(['ok' => true, 'match' => $res['data'][0], 'room_code' => $roomCode]);
}

function joinRoom($input, $userId, $userName) {
    $roomCode = strtoupper(trim($input['room_code'] ?? ''));
    if (!$roomCode) {
        http_response_code(400);
        echo json_encode(['error' => 'room_code_required']);
        return;
    }

    $res = supabase_rest('GET', "backgammon_matches?room_code=eq.{$roomCode}&status=eq.waiting&select=*", [], SUPABASE_SERVICE_KEY);
    if (empty($res['data'])) {
        http_response_code(404);
        echo json_encode(['error' => 'room_not_found']);
        return;
    }

    $match = $res['data'][0];
    $players = json_decode($match['players'], true);

    foreach ($players as $p) {
        if ($p['id'] === $userId) {
            echo json_encode(['ok' => true, 'match' => $match, 'already_joined' => true]);
            return;
        }
    }

    if (count($players) >= 2) {
        http_response_code(400);
        echo json_encode(['error' => 'room_full']);
        return;
    }

    $players[] = [
        'id' => $userId,
        'name' => $userName,
        'type' => 'human'
    ];

    $update = [
        'players' => json_encode($players),
        'status' => 'ready'
    ];

    $patchRes = supabase_rest('PATCH', "backgammon_matches?id=eq.{$match['id']}", $update, SUPABASE_SERVICE_KEY);
    echo json_encode(['ok' => true, 'match' => $patchRes['data'][0]]);
}

// ─── Start Game (Opening Roll) ──────────────────────────────────────────────

function startGame($input, $userId) {
    $matchId = $input['match_id'] ?? '';
    if (!$matchId) {
        http_response_code(400);
        echo json_encode(['error' => 'match_id_required']);
        return;
    }

    $res = supabase_rest('GET', "backgammon_matches?id=eq.{$matchId}&select=*", [], SUPABASE_SERVICE_KEY);
    if (empty($res['data'])) {
        http_response_code(404);
        echo json_encode(['error' => 'not_found']);
        return;
    }

    $match = $res['data'][0];
    if ($match['host_id'] !== $userId) {
        http_response_code(403);
        echo json_encode(['error' => 'not_host']);
        return;
    }
    if ($match['status'] !== 'ready' && $match['status'] !== 'waiting') {
        http_response_code(400);
        echo json_encode(['error' => 'already_started']);
        return;
    }

    $players = json_decode($match['players'], true);

    // Fill remaining slot with bot if needed
    if (count($players) < 2) {
        $players[] = [
            'id' => 'bot_0',
            'name' => 'بوت',
            'type' => 'bot',
            'difficulty' => 'medium'
        ];
    }

    // Opening roll: each player rolls 1 die, higher goes first
    $d1 = 0;
    $d2 = 0;
    while ($d1 === $d2) {
        $d1 = random_int(1, 6);
        $d2 = random_int(1, 6);
    }

    $firstPlayer = ($d1 > $d2) ? 0 : 1;
    $dice = [$d1, $d2];
    $diceRemaining = [$d1, $d2];

    $update = [
        'status' => 'in_progress',
        'players' => json_encode($players),
        'board' => json_encode(getInitialBoard()),
        'bar' => json_encode([0, 0]),
        'borne_off' => json_encode([0, 0]),
        'dice' => json_encode($dice),
        'dice_remaining' => json_encode($diceRemaining),
        'current_turn' => $firstPlayer,
        'game_state' => json_encode(['phase' => 'moving', 'started_at' => date('c')]),
        'doubling_cube' => json_encode(['value' => 1, 'owner' => null])
    ];

    $patchRes = supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
    echo json_encode(['ok' => true, 'match' => $patchRes['data'][0], 'opening_roll' => ['d1' => $d1, 'd2' => $d2, 'first_player' => $firstPlayer]]);

    // If first player is a bot, execute bot turns
    if ($players[$firstPlayer]['type'] === 'bot') {
        executeBotTurns($matchId);
    }
}

// ─── Roll Dice ──────────────────────────────────────────────────────────────

function rollDice($input, $userId) {
    $matchId = $input['match_id'] ?? '';
    $match = getAndValidateMatch($matchId, $userId);
    if (!$match) return;

    $players = json_decode($match['players'], true);
    $currentTurn = $match['current_turn'];
    $gameState = json_decode($match['game_state'], true);

    if ($players[$currentTurn]['id'] !== $userId) {
        http_response_code(400);
        echo json_encode(['error' => 'not_your_turn']);
        return;
    }

    if ($gameState['phase'] !== 'awaiting_roll') {
        http_response_code(400);
        echo json_encode(['error' => 'not_roll_phase', 'phase' => $gameState['phase']]);
        return;
    }

    $d1 = random_int(1, 6);
    $d2 = random_int(1, 6);
    $dice = [$d1, $d2];

    // Doubles = 4 moves
    if ($d1 === $d2) {
        $diceRemaining = [$d1, $d1, $d1, $d1];
    } else {
        $diceRemaining = [$d1, $d2];
    }

    $board = json_decode($match['board'], true);
    $bar = json_decode($match['bar'], true);
    $borneOff = json_decode($match['borne_off'], true);

    // Check if any valid moves exist
    $hasValidMoves = playerHasAnyValidMove($board, $bar, $borneOff, $currentTurn, $diceRemaining);

    if (!$hasValidMoves) {
        // Auto-pass: no valid moves
        $nextTurn = 1 - $currentTurn;
        $gameState['phase'] = 'awaiting_roll';

        $moves = json_decode($match['moves'], true);
        $moves[] = [
            'player' => $currentTurn,
            'action' => 'roll',
            'dice' => $dice,
            'at' => date('c')
        ];
        $moves[] = [
            'player' => $currentTurn,
            'action' => 'no_moves',
            'at' => date('c')
        ];

        $update = [
            'dice' => json_encode($dice),
            'dice_remaining' => json_encode([]),
            'current_turn' => $nextTurn,
            'game_state' => json_encode($gameState),
            'moves' => json_encode($moves)
        ];

        $patchRes = supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
        echo json_encode(['ok' => true, 'match' => $patchRes['data'][0], 'no_moves' => true]);

        if ($players[$nextTurn]['type'] === 'bot') {
            executeBotTurns($matchId);
        }
        return;
    }

    $gameState['phase'] = 'moving';

    $moves = json_decode($match['moves'], true);
    $moves[] = [
        'player' => $currentTurn,
        'action' => 'roll',
        'dice' => $dice,
        'at' => date('c')
    ];

    $update = [
        'dice' => json_encode($dice),
        'dice_remaining' => json_encode($diceRemaining),
        'game_state' => json_encode($gameState),
        'moves' => json_encode($moves)
    ];

    $patchRes = supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
    echo json_encode(['ok' => true, 'match' => $patchRes['data'][0]]);
}

// ─── Make Move ──────────────────────────────────────────────────────────────

function makeMove($input, $userId) {
    $matchId = $input['match_id'] ?? '';
    $from = $input['from'] ?? null;
    $to = $input['to'] ?? null;

    $match = getAndValidateMatch($matchId, $userId);
    if (!$match) return;

    $players = json_decode($match['players'], true);
    $currentTurn = $match['current_turn'];
    $gameState = json_decode($match['game_state'], true);

    if ($players[$currentTurn]['id'] !== $userId) {
        http_response_code(400);
        echo json_encode(['error' => 'not_your_turn']);
        return;
    }

    if ($gameState['phase'] !== 'moving') {
        http_response_code(400);
        echo json_encode(['error' => 'not_moving_phase', 'phase' => $gameState['phase']]);
        return;
    }

    $board = json_decode($match['board'], true);
    $bar = json_decode($match['bar'], true);
    $borneOff = json_decode($match['borne_off'], true);
    $diceRemaining = json_decode($match['dice_remaining'], true);
    $moves = json_decode($match['moves'], true);

    if (empty($diceRemaining)) {
        http_response_code(400);
        echo json_encode(['error' => 'no_dice_remaining']);
        return;
    }

    // Validate and execute the move
    $moveResult = validateAndExecuteMove($board, $bar, $borneOff, $diceRemaining, $currentTurn, $from, $to);

    if (isset($moveResult['error'])) {
        http_response_code(400);
        echo json_encode(['error' => $moveResult['error']]);
        return;
    }

    $board = $moveResult['board'];
    $bar = $moveResult['bar'];
    $borneOff = $moveResult['borne_off'];
    $diceRemaining = $moveResult['dice_remaining'];

    $moves[] = [
        'player' => $currentTurn,
        'action' => 'move',
        'from' => $from,
        'to' => $to,
        'die_used' => $moveResult['die_used'],
        'hit' => $moveResult['hit'],
        'at' => date('c')
    ];

    // Check for win
    if ($borneOff[$currentTurn] >= 15) {
        return handleWin($match, $matchId, $board, $bar, $borneOff, $diceRemaining, $moves, $gameState, $players, $currentTurn, $userId);
    }

    // Check if dice_remaining empty or no more valid moves → switch turn
    $turnOver = false;
    if (empty($diceRemaining)) {
        $turnOver = true;
    } else {
        $hasMore = playerHasAnyValidMove($board, $bar, $borneOff, $currentTurn, $diceRemaining);
        if (!$hasMore) {
            $turnOver = true;
        }
    }

    if ($turnOver) {
        $nextTurn = 1 - $currentTurn;
        $gameState['phase'] = 'awaiting_roll';

        $update = [
            'board' => json_encode($board),
            'bar' => json_encode($bar),
            'borne_off' => json_encode($borneOff),
            'dice_remaining' => json_encode([]),
            'current_turn' => $nextTurn,
            'game_state' => json_encode($gameState),
            'moves' => json_encode($moves)
        ];

        $patchRes = supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
        echo json_encode(['ok' => true, 'match' => $patchRes['data'][0], 'turn_over' => true]);

        if ($players[$nextTurn]['type'] === 'bot') {
            executeBotTurns($matchId);
        }
    } else {
        $update = [
            'board' => json_encode($board),
            'bar' => json_encode($bar),
            'borne_off' => json_encode($borneOff),
            'dice_remaining' => json_encode($diceRemaining),
            'moves' => json_encode($moves)
        ];

        $patchRes = supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
        echo json_encode(['ok' => true, 'match' => $patchRes['data'][0], 'turn_over' => false]);
    }
}

// ─── Doubling Cube ──────────────────────────────────────────────────────────

function proposeDouble($input, $userId) {
    $matchId = $input['match_id'] ?? '';
    $match = getAndValidateMatch($matchId, $userId);
    if (!$match) return;

    $players = json_decode($match['players'], true);
    $currentTurn = $match['current_turn'];
    $gameState = json_decode($match['game_state'], true);
    $doublingCube = json_decode($match['doubling_cube'], true);

    if ($players[$currentTurn]['id'] !== $userId) {
        http_response_code(400);
        echo json_encode(['error' => 'not_your_turn']);
        return;
    }

    if ($gameState['phase'] !== 'awaiting_roll') {
        http_response_code(400);
        echo json_encode(['error' => 'can_only_double_before_rolling']);
        return;
    }

    // Check cube ownership: only the player who doesn't own the cube (or if centered) can double
    if ($doublingCube['owner'] !== null && $doublingCube['owner'] !== $currentTurn) {
        http_response_code(400);
        echo json_encode(['error' => 'not_cube_owner']);
        return;
    }

    $gameState['phase'] = 'doubling';
    $gameState['doubler'] = $currentTurn;

    $moves = json_decode($match['moves'], true);
    $moves[] = [
        'player' => $currentTurn,
        'action' => 'double',
        'cube_value' => $doublingCube['value'] * 2,
        'at' => date('c')
    ];

    $update = [
        'game_state' => json_encode($gameState),
        'moves' => json_encode($moves)
    ];

    $patchRes = supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
    echo json_encode(['ok' => true, 'match' => $patchRes['data'][0]]);

    // If opponent is bot, auto-decide
    $opponent = 1 - $currentTurn;
    if ($players[$opponent]['type'] === 'bot') {
        botRespondToDouble($matchId, $players[$opponent]['difficulty'] ?? 'medium');
    }
}

function acceptDouble($input, $userId) {
    $matchId = $input['match_id'] ?? '';
    $match = getAndValidateMatch($matchId, $userId);
    if (!$match) return;

    $players = json_decode($match['players'], true);
    $currentTurn = $match['current_turn'];
    $gameState = json_decode($match['game_state'], true);
    $doublingCube = json_decode($match['doubling_cube'], true);

    if ($gameState['phase'] !== 'doubling') {
        http_response_code(400);
        echo json_encode(['error' => 'not_doubling_phase']);
        return;
    }

    $opponent = 1 - $currentTurn;
    if ($players[$opponent]['id'] !== $userId) {
        http_response_code(400);
        echo json_encode(['error' => 'not_the_responder']);
        return;
    }

    $doublingCube['value'] *= 2;
    $doublingCube['owner'] = $opponent;
    $gameState['phase'] = 'awaiting_roll';
    unset($gameState['doubler']);

    $moves = json_decode($match['moves'], true);
    $moves[] = [
        'player' => $opponent,
        'action' => 'accept_double',
        'cube_value' => $doublingCube['value'],
        'at' => date('c')
    ];

    $update = [
        'doubling_cube' => json_encode($doublingCube),
        'game_state' => json_encode($gameState),
        'moves' => json_encode($moves)
    ];

    $patchRes = supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
    echo json_encode(['ok' => true, 'match' => $patchRes['data'][0]]);
}

function declineDouble($input, $userId) {
    $matchId = $input['match_id'] ?? '';
    $match = getAndValidateMatch($matchId, $userId);
    if (!$match) return;

    $players = json_decode($match['players'], true);
    $currentTurn = $match['current_turn'];
    $gameState = json_decode($match['game_state'], true);
    $doublingCube = json_decode($match['doubling_cube'], true);

    if ($gameState['phase'] !== 'doubling') {
        http_response_code(400);
        echo json_encode(['error' => 'not_doubling_phase']);
        return;
    }

    $opponent = 1 - $currentTurn;
    if ($players[$opponent]['id'] !== $userId) {
        http_response_code(400);
        echo json_encode(['error' => 'not_the_responder']);
        return;
    }

    // Decliner loses current cube value
    $scores = json_decode($match['scores'], true);
    $scores[$currentTurn] += $doublingCube['value'];

    $moves = json_decode($match['moves'], true);
    $moves[] = [
        'player' => $opponent,
        'action' => 'decline_double',
        'at' => date('c')
    ];

    $matchLength = $match['match_length'];

    // Check if match is over
    if ($scores[$currentTurn] >= $matchLength) {
        $gameState['phase'] = 'match_over';
        $update = [
            'scores' => json_encode($scores),
            'game_state' => json_encode($gameState),
            'moves' => json_encode($moves),
            'status' => 'completed',
            'completed_at' => date('c'),
            'result' => "player_{$currentTurn}_wins"
        ];
        $patchRes = supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
        echo json_encode(['ok' => true, 'match' => $patchRes['data'][0], 'match_over' => true, 'winner' => $currentTurn]);
        return;
    }

    // Start new game in match
    startNewGame($matchId, $scores, $moves, $players, $gameState);
}

// ─── Move Validation & Execution ────────────────────────────────────────────

function validateAndExecuteMove(&$board, &$bar, &$borneOff, &$diceRemaining, $player, $from, $to) {
    $direction = ($player === 0) ? -1 : 1; // Player 0: high→low, Player 1: low→high

    // Determine which die is used
    $dieUsed = null;

    if ($from === 'bar') {
        // Bar entry
        if ($bar[$player] <= 0) {
            return ['error' => 'no_checker_on_bar'];
        }
        if ($to === 'off') {
            return ['error' => 'cannot_bear_off_from_bar'];
        }
        $toPoint = (int)$to;

        // Calculate required die
        if ($player === 0) {
            // Enters at points 18-23, die d → point 24-d
            $dieNeeded = 24 - $toPoint;
        } else {
            // Enters at points 0-5, die d → point d-1
            $dieNeeded = $toPoint + 1;
        }

        if (!in_array($dieNeeded, $diceRemaining)) {
            return ['error' => 'invalid_die_for_bar_entry'];
        }

        // Check target not blocked
        if (isPointBlocked($board, $toPoint, $player)) {
            return ['error' => 'point_blocked'];
        }

        $dieUsed = $dieNeeded;
        $hit = false;

        // Execute: remove from bar
        $bar[$player]--;

        // Check for hit
        if (isOpponentBlot($board, $toPoint, $player)) {
            $hit = true;
            $opponent = 1 - $player;
            $bar[$opponent]++;
            $board[$toPoint] = 0;
        }

        // Place checker
        if ($player === 0) {
            $board[$toPoint]++;
        } else {
            $board[$toPoint]--;
        }

        // Remove die from remaining
        $idx = array_search($dieUsed, $diceRemaining);
        array_splice($diceRemaining, $idx, 1);

        return ['board' => $board, 'bar' => $bar, 'borne_off' => $borneOff, 'dice_remaining' => $diceRemaining, 'die_used' => $dieUsed, 'hit' => $hit];
    }

    // Must enter from bar first
    if ($bar[$player] > 0) {
        return ['error' => 'must_enter_from_bar_first'];
    }

    $fromPoint = (int)$from;

    // Check player has checker at from
    if (!hasCheckerAt($board, $fromPoint, $player)) {
        return ['error' => 'no_checker_at_point'];
    }

    if ($to === 'off') {
        // Bearing off
        if (!canBearOff($board, $bar, $player)) {
            return ['error' => 'cannot_bear_off_yet'];
        }

        // Calculate die needed for bearing off
        if ($player === 0) {
            $dieNeeded = $fromPoint + 1; // Point 0 needs die 1, point 5 needs die 6
        } else {
            $dieNeeded = 24 - $fromPoint; // Point 23 needs die 1, point 18 needs die 6
        }

        // Exact die or higher die if no checker on higher point
        if (in_array($dieNeeded, $diceRemaining)) {
            $dieUsed = $dieNeeded;
        } else {
            // Try higher die — only valid if no checker on a higher point
            $higherDice = array_filter($diceRemaining, fn($d) => $d > $dieNeeded);
            if (!empty($higherDice) && !hasCheckerOnHigherPoint($board, $fromPoint, $player)) {
                sort($higherDice);
                $dieUsed = $higherDice[0];
            } else {
                return ['error' => 'no_valid_die_for_bear_off'];
            }
        }

        // Execute bear off
        if ($player === 0) {
            $board[$fromPoint]--;
        } else {
            $board[$fromPoint]++;
        }
        $borneOff[$player]++;

        $idx = array_search($dieUsed, $diceRemaining);
        array_splice($diceRemaining, $idx, 1);

        return ['board' => $board, 'bar' => $bar, 'borne_off' => $borneOff, 'dice_remaining' => $diceRemaining, 'die_used' => $dieUsed, 'hit' => false];
    }

    // Normal move
    $toPoint = (int)$to;

    // Calculate die needed
    if ($player === 0) {
        $dieNeeded = $fromPoint - $toPoint;
    } else {
        $dieNeeded = $toPoint - $fromPoint;
    }

    if ($dieNeeded <= 0) {
        return ['error' => 'invalid_direction'];
    }

    if (!in_array($dieNeeded, $diceRemaining)) {
        return ['error' => 'no_matching_die'];
    }

    // Check target not blocked
    if (isPointBlocked($board, $toPoint, $player)) {
        return ['error' => 'point_blocked'];
    }

    $dieUsed = $dieNeeded;
    $hit = false;

    // Execute
    if ($player === 0) {
        $board[$fromPoint]--;
    } else {
        $board[$fromPoint]++;
    }

    // Check for hit
    if (isOpponentBlot($board, $toPoint, $player)) {
        $hit = true;
        $opponent = 1 - $player;
        $bar[$opponent]++;
        $board[$toPoint] = 0;
    }

    if ($player === 0) {
        $board[$toPoint]++;
    } else {
        $board[$toPoint]--;
    }

    $idx = array_search($dieUsed, $diceRemaining);
    array_splice($diceRemaining, $idx, 1);

    return ['board' => $board, 'bar' => $bar, 'borne_off' => $borneOff, 'dice_remaining' => $diceRemaining, 'die_used' => $dieUsed, 'hit' => $hit];
}

// ─── Board Helper Functions ─────────────────────────────────────────────────

function hasCheckerAt($board, $point, $player) {
    if ($player === 0) {
        return $board[$point] > 0;
    } else {
        return $board[$point] < 0;
    }
}

function isPointBlocked($board, $point, $player) {
    // A point is blocked if opponent has 2+ checkers
    if ($player === 0) {
        return $board[$point] <= -2;
    } else {
        return $board[$point] >= 2;
    }
}

function isOpponentBlot($board, $point, $player) {
    // Exactly 1 opponent checker
    if ($player === 0) {
        return $board[$point] === -1;
    } else {
        return $board[$point] === 1;
    }
}

function canBearOff($board, $bar, $player) {
    // All 15 checkers must be in home board
    if ($bar[$player] > 0) return false;

    if ($player === 0) {
        // Home = points 0-5
        for ($i = 6; $i <= 23; $i++) {
            if ($board[$i] > 0) return false;
        }
    } else {
        // Home = points 18-23
        for ($i = 0; $i <= 17; $i++) {
            if ($board[$i] < 0) return false;
        }
    }
    return true;
}

function hasCheckerOnHigherPoint($board, $fromPoint, $player) {
    // "Higher" means farther from bearing off
    if ($player === 0) {
        // Higher point index = farther from off for player 0
        for ($i = $fromPoint + 1; $i <= 5; $i++) {
            if ($board[$i] > 0) return true;
        }
    } else {
        // Lower point index = farther from off for player 1
        for ($i = $fromPoint - 1; $i >= 18; $i--) {
            if ($board[$i] < 0) return true;
        }
    }
    return false;
}

// ─── Valid Move Generation ──────────────────────────────────────────────────

function getValidMoves($board, $bar, $borneOff, $player, $dieValue) {
    $moves = [];

    // If player has checkers on bar, must enter first
    if ($bar[$player] > 0) {
        if ($player === 0) {
            $target = 24 - $dieValue; // Points 18-23
        } else {
            $target = $dieValue - 1; // Points 0-5
        }

        if ($target >= 0 && $target <= 23 && !isPointBlocked($board, $target, $player)) {
            $moves[] = ['from' => 'bar', 'to' => $target];
        }
        return $moves;
    }

    $canBear = canBearOff($board, $bar, $player);

    for ($i = 0; $i <= 23; $i++) {
        if (!hasCheckerAt($board, $i, $player)) continue;

        // Normal move
        if ($player === 0) {
            $target = $i - $dieValue;
        } else {
            $target = $i + $dieValue;
        }

        if ($target >= 0 && $target <= 23) {
            if (!isPointBlocked($board, $target, $player)) {
                $moves[] = ['from' => $i, 'to' => $target];
            }
        }

        // Bearing off
        if ($canBear) {
            if ($player === 0 && $i <= 5) {
                $dieNeeded = $i + 1;
                if ($dieValue === $dieNeeded) {
                    $moves[] = ['from' => $i, 'to' => 'off'];
                } elseif ($dieValue > $dieNeeded && !hasCheckerOnHigherPoint($board, $i, $player)) {
                    $moves[] = ['from' => $i, 'to' => 'off'];
                }
            } elseif ($player === 1 && $i >= 18) {
                $dieNeeded = 24 - $i;
                if ($dieValue === $dieNeeded) {
                    $moves[] = ['from' => $i, 'to' => 'off'];
                } elseif ($dieValue > $dieNeeded && !hasCheckerOnHigherPoint($board, $i, $player)) {
                    $moves[] = ['from' => $i, 'to' => 'off'];
                }
            }
        }
    }

    return $moves;
}

function playerHasAnyValidMove($board, $bar, $borneOff, $player, $diceRemaining) {
    $uniqueDice = array_unique($diceRemaining);
    foreach ($uniqueDice as $die) {
        $moves = getValidMoves($board, $bar, $borneOff, $player, $die);
        if (!empty($moves)) return true;
    }
    return false;
}

// ─── Win Handling ───────────────────────────────────────────────────────────

function handleWin($match, $matchId, $board, $bar, $borneOff, $diceRemaining, $moves, $gameState, $players, $winner, $userId) {
    $loser = 1 - $winner;
    $doublingCube = json_decode($match['doubling_cube'], true);
    $cubeValue = $doublingCube['value'];

    // Determine win type
    $winType = 'normal'; // 1 point
    $multiplier = 1;

    if ($borneOff[$loser] === 0) {
        // Check for backgammon: opponent has checker on bar or in winner's home
        $hasBackgammon = false;
        if ($bar[$loser] > 0) {
            $hasBackgammon = true;
        } else {
            // Check winner's home board
            if ($winner === 0) {
                // Winner's home is 0-5, check if loser (negative) has checkers there
                for ($i = 0; $i <= 5; $i++) {
                    if ($board[$i] < 0) { $hasBackgammon = true; break; }
                }
            } else {
                // Winner's home is 18-23, check if loser (positive) has checkers there
                for ($i = 18; $i <= 23; $i++) {
                    if ($board[$i] > 0) { $hasBackgammon = true; break; }
                }
            }
        }

        if ($hasBackgammon) {
            $winType = 'backgammon';
            $multiplier = 3;
        } else {
            $winType = 'gammon';
            $multiplier = 2;
        }
    }

    $pointsWon = $cubeValue * $multiplier;
    $scores = json_decode($match['scores'], true);
    $scores[$winner] += $pointsWon;

    $moves[] = [
        'player' => $winner,
        'action' => 'win',
        'win_type' => $winType,
        'points' => $pointsWon,
        'at' => date('c')
    ];

    $matchLength = $match['match_length'];

    // Check if match is over
    if ($scores[$winner] >= $matchLength) {
        $gameState['phase'] = 'match_over';
        $update = [
            'board' => json_encode($board),
            'bar' => json_encode($bar),
            'borne_off' => json_encode($borneOff),
            'dice_remaining' => json_encode([]),
            'scores' => json_encode($scores),
            'moves' => json_encode($moves),
            'game_state' => json_encode($gameState),
            'status' => 'completed',
            'completed_at' => date('c'),
            'result' => "player_{$winner}_wins_{$winType}"
        ];

        $patchRes = supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
        echo json_encode(['ok' => true, 'match' => $patchRes['data'][0], 'game_over' => true, 'match_over' => true, 'winner' => $winner, 'win_type' => $winType, 'points' => $pointsWon]);
        return;
    }

    // Start new game in match
    startNewGame($matchId, $scores, $moves, $players, $gameState);
}

function startNewGame($matchId, $scores, $moves, $players, $gameState) {
    // Opening roll for new game
    $d1 = 0;
    $d2 = 0;
    while ($d1 === $d2) {
        $d1 = random_int(1, 6);
        $d2 = random_int(1, 6);
    }

    $firstPlayer = ($d1 > $d2) ? 0 : 1;

    $gameState['phase'] = 'moving';
    unset($gameState['doubler']);

    $update = [
        'board' => json_encode(getInitialBoard()),
        'bar' => json_encode([0, 0]),
        'borne_off' => json_encode([0, 0]),
        'dice' => json_encode([$d1, $d2]),
        'dice_remaining' => json_encode([$d1, $d2]),
        'doubling_cube' => json_encode(['value' => 1, 'owner' => null]),
        'current_turn' => $firstPlayer,
        'scores' => json_encode($scores),
        'moves' => json_encode($moves),
        'game_state' => json_encode($gameState)
    ];

    $patchRes = supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
    echo json_encode(['ok' => true, 'match' => $patchRes['data'][0], 'game_over' => true, 'match_over' => false, 'new_game' => true]);

    if ($players[$firstPlayer]['type'] === 'bot') {
        executeBotTurns($matchId);
    }
}

// ─── Bot AI ─────────────────────────────────────────────────────────────────

function executeBotTurns($matchId) {
    $maxIterations = 100;
    $iteration = 0;

    while ($iteration < $maxIterations) {
        $iteration++;

        $res = supabase_rest('GET', "backgammon_matches?id=eq.{$matchId}&select=*", [], SUPABASE_SERVICE_KEY);
        if (empty($res['data'])) break;
        $match = $res['data'][0];

        if ($match['status'] !== 'in_progress') break;

        $players = json_decode($match['players'], true);
        $currentTurn = $match['current_turn'];
        $currentPlayer = $players[$currentTurn];

        if ($currentPlayer['type'] !== 'bot') break;

        $board = json_decode($match['board'], true);
        $bar = json_decode($match['bar'], true);
        $borneOff = json_decode($match['borne_off'], true);
        $gameState = json_decode($match['game_state'], true);
        $moves = json_decode($match['moves'], true);
        $diceRemaining = json_decode($match['dice_remaining'], true);
        $difficulty = $currentPlayer['difficulty'] ?? 'medium';

        // Phase: awaiting_roll → roll dice
        if ($gameState['phase'] === 'awaiting_roll') {
            $d1 = random_int(1, 6);
            $d2 = random_int(1, 6);
            $dice = [$d1, $d2];

            if ($d1 === $d2) {
                $diceRemaining = [$d1, $d1, $d1, $d1];
            } else {
                $diceRemaining = [$d1, $d2];
            }

            $moves[] = [
                'player' => $currentTurn,
                'action' => 'roll',
                'dice' => $dice,
                'at' => date('c'),
                'bot' => true
            ];

            // Check if any valid moves
            $hasValidMoves = playerHasAnyValidMove($board, $bar, $borneOff, $currentTurn, $diceRemaining);

            if (!$hasValidMoves) {
                // No moves — pass
                $nextTurn = 1 - $currentTurn;
                $gameState['phase'] = 'awaiting_roll';
                $moves[] = ['player' => $currentTurn, 'action' => 'no_moves', 'at' => date('c'), 'bot' => true];

                $update = [
                    'dice' => json_encode($dice),
                    'dice_remaining' => json_encode([]),
                    'current_turn' => $nextTurn,
                    'game_state' => json_encode($gameState),
                    'moves' => json_encode($moves)
                ];
                supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);

                if ($players[$nextTurn]['type'] !== 'bot') break;
                continue;
            }

            $gameState['phase'] = 'moving';
            // Update dice in match, then continue to move phase
            $update = [
                'dice' => json_encode($dice),
                'dice_remaining' => json_encode($diceRemaining),
                'game_state' => json_encode($gameState),
                'moves' => json_encode($moves)
            ];
            supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
            continue;
        }

        // Phase: moving → execute moves until dice exhausted
        if ($gameState['phase'] === 'moving') {
            $moveMade = false;

            // Try to use each remaining die
            while (!empty($diceRemaining)) {
                $uniqueDice = array_unique($diceRemaining);
                $allValidMoves = [];

                foreach ($uniqueDice as $die) {
                    $validForDie = getValidMoves($board, $bar, $borneOff, $currentTurn, $die);
                    foreach ($validForDie as $vm) {
                        $vm['die'] = $die;
                        $allValidMoves[] = $vm;
                    }
                }

                if (empty($allValidMoves)) break;

                // Bot picks a move
                $chosen = botPickMove($allValidMoves, $board, $bar, $borneOff, $currentTurn, $difficulty, $diceRemaining);

                // Execute the chosen move
                $from = $chosen['from'];
                $to = $chosen['to'];
                $moveResult = validateAndExecuteMove($board, $bar, $borneOff, $diceRemaining, $currentTurn, $from, $to);

                if (isset($moveResult['error'])) {
                    // Shouldn't happen with valid move generation, but safety break
                    break;
                }

                $board = $moveResult['board'];
                $bar = $moveResult['bar'];
                $borneOff = $moveResult['borne_off'];
                $diceRemaining = $moveResult['dice_remaining'];

                $moves[] = [
                    'player' => $currentTurn,
                    'action' => 'move',
                    'from' => $from,
                    'to' => $to,
                    'die_used' => $moveResult['die_used'],
                    'hit' => $moveResult['hit'],
                    'at' => date('c'),
                    'bot' => true
                ];

                $moveMade = true;

                // Check win
                if ($borneOff[$currentTurn] >= 15) {
                    $doublingCube = json_decode($match['doubling_cube'], true);
                    $cubeValue = $doublingCube['value'];
                    $loser = 1 - $currentTurn;

                    $winType = 'normal';
                    $multiplier = 1;
                    if ($borneOff[$loser] === 0) {
                        $hasBackgammon = false;
                        if ($bar[$loser] > 0) {
                            $hasBackgammon = true;
                        } else {
                            if ($currentTurn === 0) {
                                for ($i = 0; $i <= 5; $i++) { if ($board[$i] < 0) { $hasBackgammon = true; break; } }
                            } else {
                                for ($i = 18; $i <= 23; $i++) { if ($board[$i] > 0) { $hasBackgammon = true; break; } }
                            }
                        }
                        if ($hasBackgammon) { $winType = 'backgammon'; $multiplier = 3; }
                        else { $winType = 'gammon'; $multiplier = 2; }
                    }

                    $pointsWon = $cubeValue * $multiplier;
                    $scores = json_decode($match['scores'], true);
                    $scores[$currentTurn] += $pointsWon;
                    $matchLength = $match['match_length'];

                    $moves[] = ['player' => $currentTurn, 'action' => 'win', 'win_type' => $winType, 'points' => $pointsWon, 'at' => date('c'), 'bot' => true];

                    if ($scores[$currentTurn] >= $matchLength) {
                        $gameState['phase'] = 'match_over';
                        $update = [
                            'board' => json_encode($board),
                            'bar' => json_encode($bar),
                            'borne_off' => json_encode($borneOff),
                            'dice_remaining' => json_encode([]),
                            'scores' => json_encode($scores),
                            'moves' => json_encode($moves),
                            'game_state' => json_encode($gameState),
                            'status' => 'completed',
                            'completed_at' => date('c'),
                            'result' => "player_{$currentTurn}_wins_{$winType}"
                        ];
                        supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
                        return;
                    }

                    // New game in match
                    $nd1 = 0; $nd2 = 0;
                    while ($nd1 === $nd2) { $nd1 = random_int(1, 6); $nd2 = random_int(1, 6); }
                    $firstPlayer = ($nd1 > $nd2) ? 0 : 1;

                    $gameState['phase'] = 'moving';
                    $update = [
                        'board' => json_encode(getInitialBoard()),
                        'bar' => json_encode([0, 0]),
                        'borne_off' => json_encode([0, 0]),
                        'dice' => json_encode([$nd1, $nd2]),
                        'dice_remaining' => json_encode([$nd1, $nd2]),
                        'doubling_cube' => json_encode(['value' => 1, 'owner' => null]),
                        'current_turn' => $firstPlayer,
                        'scores' => json_encode($scores),
                        'moves' => json_encode($moves),
                        'game_state' => json_encode($gameState)
                    ];
                    supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);

                    if ($players[$firstPlayer]['type'] !== 'bot') return;
                    break 2; // Break inner while and will re-enter outer while
                }
            }

            // Turn over — switch to opponent
            $nextTurn = 1 - $currentTurn;
            $gameState['phase'] = 'awaiting_roll';

            $update = [
                'board' => json_encode($board),
                'bar' => json_encode($bar),
                'borne_off' => json_encode($borneOff),
                'dice_remaining' => json_encode([]),
                'current_turn' => $nextTurn,
                'game_state' => json_encode($gameState),
                'moves' => json_encode($moves)
            ];
            supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);

            if ($players[$nextTurn]['type'] !== 'bot') break;
            continue;
        }

        // Unknown phase for bot
        break;
    }
}

function botPickMove($allValidMoves, $board, $bar, $borneOff, $player, $difficulty, $diceRemaining) {
    if ($difficulty === 'easy') {
        return $allValidMoves[array_rand($allValidMoves)];
    }

    if ($difficulty === 'medium') {
        // Heuristic scoring per move
        $bestScore = -9999;
        $bestMove = $allValidMoves[0];

        foreach ($allValidMoves as $move) {
            $score = 0;
            $from = $move['from'];
            $to = $move['to'];

            // Bear off bonus
            if ($to === 'off') {
                $score += 5;
            } else {
                $toPoint = (int)$to;
                // Hit bonus
                if (isOpponentBlot($board, $toPoint, $player)) {
                    $score += 3;
                }
                // Make point (land where you already have 1 checker)
                if ($player === 0 && $board[$toPoint] === 1) {
                    $score += 2;
                } elseif ($player === 1 && $board[$toPoint] === -1) {
                    $score += 2;
                }
                // Leaving a blot penalty
                if ($from !== 'bar') {
                    $fromPoint = (int)$from;
                    if ($player === 0 && $board[$fromPoint] === 2) {
                        $score -= 4; // Leaving one behind = blot
                    } elseif ($player === 1 && $board[$fromPoint] === -2) {
                        $score -= 4;
                    }
                }
                // Advance toward home bonus
                $score += 1;
            }

            if ($score > $bestScore) {
                $bestScore = $score;
                $bestMove = $move;
            }
        }

        return $bestMove;
    }

    // Hard: positional evaluation
    $bestScore = -99999;
    $bestMove = $allValidMoves[0];

    foreach ($allValidMoves as $move) {
        // Simulate the move
        $simBoard = $board;
        $simBar = $bar;
        $simBorneOff = $borneOff;
        $simDice = $diceRemaining;
        $from = $move['from'];
        $to = $move['to'];

        $result = validateAndExecuteMove($simBoard, $simBar, $simBorneOff, $simDice, $player, $from, $to);
        if (isset($result['error'])) continue;

        $simBoard = $result['board'];
        $simBar = $result['bar'];
        $simBorneOff = $result['borne_off'];

        $score = evaluatePosition($simBoard, $simBar, $simBorneOff, $player);

        if ($score > $bestScore) {
            $bestScore = $score;
            $bestMove = $move;
        }
    }

    return $bestMove;
}

function evaluatePosition($board, $bar, $borneOff, $player) {
    $score = 0;
    $opponent = 1 - $player;

    // Borne off count (high priority)
    $score += $borneOff[$player] * 10;
    $score -= $borneOff[$opponent] * 10;

    // Bar pressure
    $score -= $bar[$player] * 15;
    $score += $bar[$opponent] * 15;

    // Pip count (lower is better for current player)
    $myPips = 0;
    $oppPips = 0;
    for ($i = 0; $i <= 23; $i++) {
        if ($player === 0) {
            if ($board[$i] > 0) $myPips += $board[$i] * ($i + 1);
            if ($board[$i] < 0) $oppPips += abs($board[$i]) * (24 - $i);
        } else {
            if ($board[$i] < 0) $myPips += abs($board[$i]) * (24 - $i);
            if ($board[$i] > 0) $oppPips += $board[$i] * ($i + 1);
        }
    }
    $score -= $myPips * 0.5;
    $score += $oppPips * 0.5;

    // Blot exposure (penalty for single checkers)
    for ($i = 0; $i <= 23; $i++) {
        if ($player === 0 && $board[$i] === 1) {
            $score -= 3;
        } elseif ($player === 1 && $board[$i] === -1) {
            $score -= 3;
        }
    }

    // Prime detection (consecutive blocked points)
    $primeLength = 0;
    $maxPrime = 0;
    for ($i = 0; $i <= 23; $i++) {
        if (($player === 0 && $board[$i] >= 2) || ($player === 1 && $board[$i] <= -2)) {
            $primeLength++;
            if ($primeLength > $maxPrime) $maxPrime = $primeLength;
        } else {
            $primeLength = 0;
        }
    }
    $score += $maxPrime * 5;

    // Anchors in opponent's home (good for player)
    if ($player === 0) {
        for ($i = 18; $i <= 23; $i++) {
            if ($board[$i] >= 2) $score += 3;
        }
    } else {
        for ($i = 0; $i <= 5; $i++) {
            if ($board[$i] <= -2) $score += 3;
        }
    }

    return $score;
}

function botRespondToDouble($matchId, $difficulty) {
    $res = supabase_rest('GET', "backgammon_matches?id=eq.{$matchId}&select=*", [], SUPABASE_SERVICE_KEY);
    if (empty($res['data'])) return;
    $match = $res['data'][0];

    $players = json_decode($match['players'], true);
    $currentTurn = $match['current_turn'];
    $opponent = 1 - $currentTurn;
    $board = json_decode($match['board'], true);
    $bar = json_decode($match['bar'], true);
    $borneOff = json_decode($match['borne_off'], true);
    $doublingCube = json_decode($match['doubling_cube'], true);
    $gameState = json_decode($match['game_state'], true);
    $moves = json_decode($match['moves'], true);

    // Bot decision: evaluate position
    $posScore = evaluatePosition($board, $bar, $borneOff, $opponent);

    $accept = true;
    if ($difficulty === 'easy') {
        $accept = (random_int(0, 100) > 40);
    } elseif ($difficulty === 'medium') {
        $accept = ($posScore > -20);
    } else {
        $accept = ($posScore > -30);
    }

    if ($accept) {
        $doublingCube['value'] *= 2;
        $doublingCube['owner'] = $opponent;
        $gameState['phase'] = 'awaiting_roll';
        unset($gameState['doubler']);

        $moves[] = ['player' => $opponent, 'action' => 'accept_double', 'cube_value' => $doublingCube['value'], 'at' => date('c'), 'bot' => true];

        $update = [
            'doubling_cube' => json_encode($doublingCube),
            'game_state' => json_encode($gameState),
            'moves' => json_encode($moves)
        ];
        supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
    } else {
        // Decline — current player wins
        $scores = json_decode($match['scores'], true);
        $scores[$currentTurn] += $doublingCube['value'];
        $matchLength = $match['match_length'];

        $moves[] = ['player' => $opponent, 'action' => 'decline_double', 'at' => date('c'), 'bot' => true];

        if ($scores[$currentTurn] >= $matchLength) {
            $gameState['phase'] = 'match_over';
            $update = [
                'scores' => json_encode($scores),
                'game_state' => json_encode($gameState),
                'moves' => json_encode($moves),
                'status' => 'completed',
                'completed_at' => date('c'),
                'result' => "player_{$currentTurn}_wins"
            ];
            supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
        } else {
            // New game
            $nd1 = 0; $nd2 = 0;
            while ($nd1 === $nd2) { $nd1 = random_int(1, 6); $nd2 = random_int(1, 6); }
            $firstPlayer = ($nd1 > $nd2) ? 0 : 1;
            $gameState['phase'] = 'moving';
            unset($gameState['doubler']);

            $update = [
                'board' => json_encode(getInitialBoard()),
                'bar' => json_encode([0, 0]),
                'borne_off' => json_encode([0, 0]),
                'dice' => json_encode([$nd1, $nd2]),
                'dice_remaining' => json_encode([$nd1, $nd2]),
                'doubling_cube' => json_encode(['value' => 1, 'owner' => null]),
                'current_turn' => $firstPlayer,
                'scores' => json_encode($scores),
                'moves' => json_encode($moves),
                'game_state' => json_encode($gameState)
            ];
            supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);

            if ($players[$firstPlayer]['type'] === 'bot') {
                executeBotTurns($matchId);
            }
        }
    }
}

// ─── Leave/Chat/Matchmaking ─────────────────────────────────────────────────

function leaveGame($input, $userId) {
    $matchId = $input['match_id'] ?? '';
    $match = getAndValidateMatch($matchId, $userId);
    if (!$match) return;

    $players = json_decode($match['players'], true);
    $playerIdx = -1;
    foreach ($players as $idx => $p) {
        if ($p['id'] === $userId) { $playerIdx = $idx; break; }
    }

    if ($playerIdx === -1) {
        http_response_code(400);
        echo json_encode(['error' => 'not_in_match']);
        return;
    }

    $players[$playerIdx]['type'] = 'bot';
    $players[$playerIdx]['difficulty'] = 'medium';
    $players[$playerIdx]['name'] = 'بوت (غادر)';

    $update = ['players' => json_encode($players)];
    supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
    echo json_encode(['ok' => true]);

    // If it's now this bot's turn, execute
    if ($match['current_turn'] === $playerIdx) {
        executeBotTurns($matchId);
    }
}

function sendChat($input, $userId, $userName) {
    $matchId = $input['match_id'] ?? '';
    $text = trim($input['text'] ?? '');

    if (!$text || strlen($text) > 200) {
        http_response_code(400);
        echo json_encode(['error' => 'invalid_message']);
        return;
    }

    $match = getAndValidateMatch($matchId, $userId);
    if (!$match) return;

    $chat = json_decode($match['chat'], true);
    $chat[] = [
        'sender_id' => $userId,
        'sender_name' => $userName,
        'text' => htmlspecialchars($text, ENT_QUOTES, 'UTF-8'),
        'at' => date('c')
    ];

    supabase_rest('PATCH', "backgammon_matches?id=eq.{$matchId}", ['chat' => json_encode($chat)], SUPABASE_SERVICE_KEY);
    echo json_encode(['ok' => true]);
}

function matchmake($input, $userId, $userName) {
    $subAction = $input['sub_action'] ?? 'join';
    $matchLength = (int)($input['match_length'] ?? 1);
    if ($matchLength < 1) $matchLength = 1;

    if ($subAction === 'leave') {
        supabase_rest('DELETE', "backgammon_queue?user_id=eq.{$userId}", [], SUPABASE_SERVICE_KEY);
        echo json_encode(['ok' => true, 'status' => 'left']);
        return;
    }

    if ($subAction === 'poll') {
        $existing = supabase_rest('GET', "backgammon_queue?user_id=eq.{$userId}&select=*", [], SUPABASE_SERVICE_KEY);
        if (!empty($existing['data']) && !empty($existing['data'][0]['match_id'])) {
            $matchId = $existing['data'][0]['match_id'];
            supabase_rest('DELETE', "backgammon_queue?user_id=eq.{$userId}", [], SUPABASE_SERVICE_KEY);
            echo json_encode(['ok' => true, 'status' => 'matched', 'match_id' => $matchId]);
            return;
        }
        echo json_encode(['ok' => true, 'status' => 'waiting']);
        return;
    }

    // sub_action = 'join'
    $existing = supabase_rest('GET', "backgammon_queue?user_id=eq.{$userId}&select=*", [], SUPABASE_SERVICE_KEY);
    if (!empty($existing['data'])) {
        $entry = $existing['data'][0];
        if (!empty($entry['match_id'])) {
            supabase_rest('DELETE', "backgammon_queue?user_id=eq.{$userId}", [], SUPABASE_SERVICE_KEY);
            echo json_encode(['ok' => true, 'status' => 'matched', 'match_id' => $entry['match_id']]);
            return;
        }

        // Try to find an opponent
        $opponents = supabase_rest('GET', "backgammon_queue?user_id=neq.{$userId}&match_id=is.null&match_length=eq.{$matchLength}&select=*&limit=1", [], SUPABASE_SERVICE_KEY);

        if (!empty($opponents['data'])) {
            $opp = $opponents['data'][0];
            $allPlayers = [
                ['id' => $userId, 'name' => $userName, 'type' => 'human'],
                ['id' => $opp['user_id'], 'name' => $opp['user_name'] ?? 'لاعب', 'type' => 'human']
            ];

            // Opening roll
            $d1 = 0; $d2 = 0;
            while ($d1 === $d2) { $d1 = random_int(1, 6); $d2 = random_int(1, 6); }
            $firstPlayer = ($d1 > $d2) ? 0 : 1;

            $matchData = [
                'room_code' => generateRoomCode(),
                'status' => 'in_progress',
                'players' => json_encode($allPlayers),
                'current_turn' => $firstPlayer,
                'board' => json_encode(getInitialBoard()),
                'dice' => json_encode([$d1, $d2]),
                'dice_remaining' => json_encode([$d1, $d2]),
                'bar' => json_encode([0, 0]),
                'borne_off' => json_encode([0, 0]),
                'doubling_cube' => json_encode(['value' => 1, 'owner' => null]),
                'game_state' => json_encode(['phase' => 'moving', 'started_at' => date('c')]),
                'moves' => json_encode([]),
                'scores' => json_encode([0, 0]),
                'match_length' => $matchLength,
                'chat' => json_encode([]),
                'host_id' => $userId,
                'result' => null
            ];

            $matchRes = supabase_rest('POST', 'backgammon_matches', $matchData, SUPABASE_SERVICE_KEY);
            if (!empty($matchRes['data'])) {
                $newMatchId = $matchRes['data'][0]['id'];
                supabase_rest('PATCH', "backgammon_queue?user_id=eq.{$opp['user_id']}", ['match_id' => $newMatchId], SUPABASE_SERVICE_KEY);
                supabase_rest('DELETE', "backgammon_queue?user_id=eq.{$userId}", [], SUPABASE_SERVICE_KEY);
                echo json_encode(['ok' => true, 'status' => 'matched', 'match_id' => $newMatchId]);
                return;
            }
        }

        echo json_encode(['ok' => true, 'status' => 'waiting']);
        return;
    }

    // Join queue
    supabase_rest('POST', 'backgammon_queue', [
        'user_id' => $userId,
        'user_name' => $userName,
        'match_length' => $matchLength
    ], SUPABASE_SERVICE_KEY);
    echo json_encode(['ok' => true, 'status' => 'queued']);
}

// ─── Helpers ────────────────────────────────────────────────────────────────

function getAndValidateMatch($matchId, $userId) {
    if (!$matchId) {
        http_response_code(400);
        echo json_encode(['error' => 'match_id_required']);
        return null;
    }

    $res = supabase_rest('GET', "backgammon_matches?id=eq.{$matchId}&select=*", [], SUPABASE_SERVICE_KEY);
    if (empty($res['data'])) {
        http_response_code(404);
        echo json_encode(['error' => 'not_found']);
        return null;
    }

    $match = $res['data'][0];
    if ($match['status'] === 'completed' || $match['status'] === 'aborted') {
        http_response_code(400);
        echo json_encode(['error' => 'game_ended']);
        return null;
    }

    $players = json_decode($match['players'], true);
    $found = false;
    foreach ($players as $p) {
        if ($p['id'] === $userId) { $found = true; break; }
    }
    if (!$found) {
        http_response_code(403);
        echo json_encode(['error' => 'not_in_match']);
        return null;
    }

    return $match;
}
