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

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

if (!check_feature_flag('domino_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', "domino_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];
            $match = sanitizeMatchForPlayer($match, $userId);
            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 'play':
            playTile($input, $userId);
            break;
        case 'draw':
            drawTile($input, $userId);
            break;
        case 'pass':
            passTurn($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']);
    }
}

// ─── Tile Generation ────────────────────────────────────────────────────────

function generateAllTiles() {
    $tiles = [];
    for ($i = 0; $i <= 6; $i++) {
        for ($j = $i; $j <= 6; $j++) {
            $tiles[] = ['id' => "tile_{$i}_{$j}", 'left' => $i, 'right' => $j];
        }
    }
    return $tiles;
}

function shuffleAndDeal($mode, $playerCount) {
    $tiles = generateAllTiles();
    shuffle($tiles);

    $hands = [];
    for ($p = 0; $p < $playerCount; $p++) {
        $hands[$p] = array_splice($tiles, 0, 7);
    }

    $boneyard = ($mode === '2p') ? $tiles : [];
    return ['hands' => $hands, 'boneyard' => $boneyard];
}

function findFirstPlayer($hands, $roundNumber, $previousWinner) {
    if ($roundNumber > 1 && $previousWinner !== null) {
        return $previousWinner;
    }

    // Round 1: who has [6|6]?
    foreach ($hands as $idx => $hand) {
        foreach ($hand as $tile) {
            if ($tile['left'] === 6 && $tile['right'] === 6) return $idx;
        }
    }

    // No [6|6]? Highest double
    for ($d = 5; $d >= 0; $d--) {
        foreach ($hands as $idx => $hand) {
            foreach ($hand as $tile) {
                if ($tile['left'] === $d && $tile['right'] === $d) return $idx;
            }
        }
    }

    // No doubles at all? Highest pip total
    $best = -1;
    $bestIdx = 0;
    foreach ($hands as $idx => $hand) {
        foreach ($hand as $tile) {
            $total = $tile['left'] + $tile['right'];
            if ($total > $best) { $best = $total; $bestIdx = $idx; }
        }
    }
    return $bestIdx;
}

// ─── Board Logic ────────────────────────────────────────────────────────────

function getPlayableEnds($board, $spinner) {
    if (empty($board)) {
        return ['any' => true];
    }

    $ends = [];
    $spinnerHasEast = false;
    $spinnerHasWest = false;

    foreach ($board as $entry) {
        if (isset($entry['side'])) {
            if ($entry['side'] === 'east') $spinnerHasEast = true;
            if ($entry['side'] === 'west') $spinnerHasWest = true;
        }
    }

    // Find exposed ends for each direction
    $eastChain = array_filter($board, fn($e) => ($e['side'] ?? '') === 'east');
    $westChain = array_filter($board, fn($e) => ($e['side'] ?? '') === 'west');
    $northChain = array_filter($board, fn($e) => ($e['side'] ?? '') === 'north');
    $southChain = array_filter($board, fn($e) => ($e['side'] ?? '') === 'south');

    if (!empty($eastChain)) {
        $last = end($eastChain);
        $ends['east'] = $last['exposed_end'];
    } elseif ($spinner) {
        $ends['east'] = $spinner['left'];
    }

    if (!empty($westChain)) {
        $last = end($westChain);
        $ends['west'] = $last['exposed_end'];
    } elseif ($spinner) {
        $ends['west'] = $spinner['left'];
    }

    // North/south only available once east AND west both have at least one tile
    if ($spinnerHasEast && $spinnerHasWest && $spinner) {
        if (!empty($northChain)) {
            $last = end($northChain);
            $ends['north'] = $last['exposed_end'];
        } else {
            $ends['north'] = $spinner['left'];
        }
        if (!empty($southChain)) {
            $last = end($southChain);
            $ends['south'] = $last['exposed_end'];
        } else {
            $ends['south'] = $spinner['left'];
        }
    }

    return $ends;
}

function canPlayerPlay($hand, $ends) {
    if (isset($ends['any'])) return true;
    foreach ($hand as $tile) {
        foreach ($ends as $side => $val) {
            if ($tile['left'] === $val || $tile['right'] === $val) return true;
        }
    }
    return false;
}

function tileMatchesEnd($tile, $endValue) {
    return $tile['left'] === $endValue || $tile['right'] === $endValue;
}

function getExposedEnd($tile, $matchedValue) {
    if ($tile['left'] === $matchedValue) return $tile['right'];
    return $tile['left'];
}

// ─── Round/Match Scoring ────────────────────────────────────────────────────

function pipCount($hand) {
    $total = 0;
    foreach ($hand as $tile) {
        $total += $tile['left'] + $tile['right'];
    }
    return $total;
}

function scoreRound($hands, $winnerIdx, $players, $mode) {
    $score = 0;
    if ($mode === '4p_teams') {
        // Teams: 0+2 vs 1+3
        $winnerTeam = $winnerIdx % 2;
        foreach ($hands as $idx => $hand) {
            if ($idx % 2 !== $winnerTeam) {
                $score += pipCount($hand);
            }
        }
    } else {
        foreach ($hands as $idx => $hand) {
            if ($idx !== $winnerIdx) {
                $score += pipCount($hand);
            }
        }
    }
    return $score;
}

function scoreBlockedRound($hands, $players, $mode) {
    if ($mode === '4p_teams') {
        $team0Pips = pipCount($hands[0]) + pipCount($hands[2]);
        $team1Pips = pipCount($hands[1]) + pipCount($hands[3]);
        if ($team0Pips < $team1Pips) {
            return ['winner_idx' => 0, 'score' => $team1Pips - $team0Pips];
        } elseif ($team1Pips < $team0Pips) {
            return ['winner_idx' => 1, 'score' => $team0Pips - $team1Pips];
        }
        return ['winner_idx' => null, 'score' => 0];
    } else {
        $minPips = PHP_INT_MAX;
        $winnerIdx = 0;
        foreach ($hands as $idx => $hand) {
            $pips = pipCount($hand);
            if ($pips < $minPips) {
                $minPips = $pips;
                $winnerIdx = $idx;
            }
        }
        $score = 0;
        foreach ($hands as $idx => $hand) {
            if ($idx !== $winnerIdx) $score += pipCount($hand);
        }
        return ['winner_idx' => $winnerIdx, 'score' => $score - $minPips];
    }
}

// ─── Security: hide other players' hands ────────────────────────────────────

function sanitizeMatchForPlayer($match, $userId) {
    $players = json_decode($match['players'], true);
    $hands = json_decode($match['hands'], true);

    $playerIdx = null;
    foreach ($players as $idx => $p) {
        if ($p['id'] === $userId) { $playerIdx = $idx; break; }
    }

    $sanitizedHands = [];
    foreach ($hands as $idx => $hand) {
        if ((int)$idx === $playerIdx) {
            $sanitizedHands[$idx] = $hand;
        } else {
            $sanitizedHands[$idx] = count($hand);
        }
    }

    $match['hands'] = json_encode($sanitizedHands);
    return $match;
}

// ─── 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) {
    $mode = $input['mode'] ?? '2p';
    if (!in_array($mode, ['2p', '4p_teams'])) $mode = '2p';

    $playerCount = ($mode === '4p_teams') ? 4 : 2;
    $targetScore = ($mode === '4p_teams') ? 150 : 100;
    $bots = $input['bots'] ?? [];
    $roomCode = generateRoomCode();

    $players = [
        ['id' => $userId, 'name' => $userName, 'color' => 'P1', 'type' => 'human', 'connected' => true]
    ];

    $colors = ['P1', 'P2', 'P3', 'P4'];
    foreach ($bots as $bot) {
        $idx = count($players);
        if ($idx >= $playerCount) break;
        $players[] = [
            'id' => 'bot_' . $idx,
            'name' => $bot['name'] ?? ('بوت ' . $idx),
            'color' => $colors[$idx],
            'type' => 'bot',
            'difficulty' => $bot['difficulty'] ?? 'easy',
            'connected' => true
        ];
    }

    $data = [
        'room_code' => $roomCode,
        'status' => 'waiting',
        'mode' => $mode,
        'player_count' => $playerCount,
        'players' => json_encode($players),
        'current_turn' => 0,
        'board' => json_encode([]),
        'hands' => json_encode([]),
        'boneyard' => json_encode([]),
        'scores' => json_encode(array_fill(0, $playerCount, 0)),
        'round_number' => 1,
        'round_history' => json_encode([]),
        'target_score' => $targetScore,
        'moves' => json_encode([]),
        'game_state' => json_encode(['phase' => 'waiting']),
        'chat' => json_encode([]),
        'host_id' => $userId
    ];

    $res = supabase_rest('POST', 'domino_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', "domino_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) >= $match['player_count']) {
        http_response_code(400);
        echo json_encode(['error' => 'room_full']);
        return;
    }

    $colors = ['P1', 'P2', 'P3', 'P4'];
    $usedColors = array_map(fn($p) => $p['color'], $players);
    $nextColor = '';
    foreach ($colors as $c) {
        if (!in_array($c, $usedColors)) { $nextColor = $c; break; }
    }

    $players[] = [
        'id' => $userId,
        'name' => $userName,
        'color' => $nextColor,
        'type' => 'human',
        'connected' => true
    ];

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

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', "domino_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'] !== 'waiting') {
        http_response_code(400);
        echo json_encode(['error' => 'already_started']);
        return;
    }

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

    // Fill remaining slots with bots
    $colors = ['P1', 'P2', 'P3', 'P4'];
    $usedColors = array_map(fn($p) => $p['color'], $players);
    $botIdx = count($players);
    while (count($players) < $playerCount) {
        $nextColor = '';
        foreach ($colors as $c) {
            if (!in_array($c, $usedColors)) { $nextColor = $c; break; }
        }
        $usedColors[] = $nextColor;
        $players[] = [
            'id' => 'bot_' . $botIdx,
            'name' => 'بوت ' . $botIdx,
            'color' => $nextColor,
            'type' => 'bot',
            'difficulty' => 'medium',
            'connected' => true
        ];
        $botIdx++;
    }

    $deal = shuffleAndDeal($mode, $playerCount);
    $firstPlayer = findFirstPlayer($deal['hands'], 1, null);

    $update = [
        'status' => 'in_progress',
        'players' => json_encode($players),
        'hands' => json_encode($deal['hands']),
        'boneyard' => json_encode($deal['boneyard']),
        'board' => json_encode([]),
        'spinner' => null,
        'current_turn' => $firstPlayer,
        'game_state' => json_encode(['phase' => 'play', 'started_at' => date('c'), 'consecutive_passes' => 0])
    ];

    $patchRes = supabase_rest('PATCH', "domino_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);

    $responseMatch = $patchRes['data'][0];
    $responseMatch = sanitizeMatchForPlayer($responseMatch, $userId);
    echo json_encode(['ok' => true, 'match' => $responseMatch]);

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

// ─── Play a Tile ────────────────────────────────────────────────────────────

function playTile($input, $userId) {
    $matchId = $input['match_id'] ?? '';
    $tileId = $input['tile_id'] ?? '';
    $side = $input['side'] ?? '';

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

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

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

    $hands = json_decode($match['hands'], true);
    $board = json_decode($match['board'], true);
    $spinner = $match['spinner'] ? json_decode($match['spinner'], true) : null;
    $gameState = json_decode($match['game_state'], true);

    $hand = $hands[$currentTurn];

    // Find tile in hand
    $tileIdx = null;
    $tile = null;
    foreach ($hand as $idx => $t) {
        if ($t['id'] === $tileId) { $tileIdx = $idx; $tile = $t; break; }
    }
    if ($tile === null) {
        http_response_code(400);
        echo json_encode(['error' => 'tile_not_in_hand']);
        return;
    }

    // First tile on empty board
    if (empty($board)) {
        $isDouble = ($tile['left'] === $tile['right']);
        $boardEntry = [
            'tile' => $tile,
            'side' => null,
            'isSpinner' => $isDouble,
            'exposed_end' => null
        ];
        $board[] = $boardEntry;
        if ($isDouble) {
            $spinner = $tile;
        }
        array_splice($hands[$currentTurn], $tileIdx, 1);
    } else {
        // Validate side
        $ends = getPlayableEnds($board, $spinner);
        if (!isset($ends[$side])) {
            http_response_code(400);
            echo json_encode(['error' => 'invalid_side', 'available' => array_keys($ends)]);
            return;
        }

        $endValue = $ends[$side];
        if (!tileMatchesEnd($tile, $endValue)) {
            http_response_code(400);
            echo json_encode(['error' => 'tile_does_not_match', 'end_value' => $endValue]);
            return;
        }

        $exposedEnd = getExposedEnd($tile, $endValue);
        $isDouble = ($tile['left'] === $tile['right']);
        if ($isDouble) $exposedEnd = $tile['left'];

        // Check if this is the first double and no spinner yet
        if ($isDouble && !$spinner) {
            $spinner = $tile;
        }

        $boardEntry = [
            'tile' => $tile,
            'side' => $side,
            'isSpinner' => false,
            'exposed_end' => $exposedEnd
        ];
        $board[] = $boardEntry;
        array_splice($hands[$currentTurn], $tileIdx, 1);
    }

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

    $gameState['consecutive_passes'] = 0;

    // Check round end
    $roundResult = checkRoundEnd($hands, json_decode($match['boneyard'], true), $match['mode'], $players, $currentTurn);

    if ($roundResult) {
        return handleRoundEnd($match, $matchId, $roundResult, $hands, $board, $spinner, $moves, $gameState, $players, $userId);
    }

    // Next turn
    $nextTurn = ($currentTurn + 1) % count($players);

    $update = [
        'board' => json_encode($board),
        'hands' => json_encode($hands),
        'spinner' => $spinner ? json_encode($spinner) : null,
        'moves' => json_encode($moves),
        'current_turn' => $nextTurn,
        'game_state' => json_encode($gameState)
    ];

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

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

// ─── Draw a Tile (2P only) ──────────────────────────────────────────────────

function drawTile($input, $userId) {
    $matchId = $input['match_id'] ?? '';

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

    if ($match['mode'] !== '2p') {
        http_response_code(400);
        echo json_encode(['error' => 'draw_not_allowed_in_team_mode']);
        return;
    }

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

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

    $hands = json_decode($match['hands'], true);
    $boneyard = json_decode($match['boneyard'], true);
    $board = json_decode($match['board'], true);
    $spinner = $match['spinner'] ? json_decode($match['spinner'], true) : null;

    // Validate player can't play
    $ends = getPlayableEnds($board, $spinner);
    if (canPlayerPlay($hands[$currentTurn], $ends)) {
        http_response_code(400);
        echo json_encode(['error' => 'must_play_if_possible']);
        return;
    }

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

    // Draw a tile
    $drawnTile = array_pop($boneyard);
    $hands[$currentTurn][] = $drawnTile;

    $moves = json_decode($match['moves'], true);
    $moves[] = [
        'player' => $currentTurn,
        'action' => 'draw',
        'tile_count' => count($boneyard),
        'at' => date('c')
    ];

    // Check if drawn tile can be played — if not, turn passes
    $canNowPlay = canPlayerPlay($hands[$currentTurn], $ends);
    $nextTurn = $canNowPlay ? $currentTurn : ($currentTurn + 1) % count($players);

    $gameState = json_decode($match['game_state'], true);
    if (!$canNowPlay) {
        $gameState['consecutive_passes'] = ($gameState['consecutive_passes'] ?? 0) + 1;
    }

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

    // Check if round blocked after draw
    $roundResult = checkRoundEnd($hands, $boneyard, $match['mode'], $players, $currentTurn);
    if ($roundResult) {
        return handleRoundEnd($match, $matchId, $roundResult, $hands, $board, $spinner, $moves, $gameState, $players, $userId);
    }

    $patchRes = supabase_rest('PATCH', "domino_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
    $responseMatch = sanitizeMatchForPlayer($patchRes['data'][0], $userId);
    echo json_encode(['ok' => true, 'match' => $responseMatch, 'drawn_tile' => $drawnTile, 'can_play' => $canNowPlay]);

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

// ─── Pass Turn (4P only) ────────────────────────────────────────────────────

function passTurn($input, $userId) {
    $matchId = $input['match_id'] ?? '';

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

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

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

    $hands = json_decode($match['hands'], true);
    $board = json_decode($match['board'], true);
    $spinner = $match['spinner'] ? json_decode($match['spinner'], true) : null;
    $boneyard = json_decode($match['boneyard'], true);

    // Validate player truly can't play
    $ends = getPlayableEnds($board, $spinner);
    if (canPlayerPlay($hands[$currentTurn], $ends)) {
        http_response_code(400);
        echo json_encode(['error' => 'must_play_if_possible']);
        return;
    }

    $gameState = json_decode($match['game_state'], true);
    $gameState['consecutive_passes'] = ($gameState['consecutive_passes'] ?? 0) + 1;

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

    $nextTurn = ($currentTurn + 1) % count($players);

    // Check if round blocked (all players passed)
    $roundResult = checkRoundEnd($hands, $boneyard, $match['mode'], $players, $currentTurn);
    if ($roundResult) {
        return handleRoundEnd($match, $matchId, $roundResult, $hands, $board, $spinner, $moves, $gameState, $players, $userId);
    }

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

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

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

// ─── Round End Detection ────────────────────────────────────────────────────

function checkRoundEnd($hands, $boneyard, $mode, $players, $lastPlayerIdx) {
    // Check if current player emptied their hand (domino!)
    if (empty($hands[$lastPlayerIdx])) {
        return ['type' => 'domino', 'winner_idx' => $lastPlayerIdx];
    }

    // Check if game is blocked (no one can play)
    // In 2P with boneyard remaining, not blocked yet
    if ($mode === '2p' && !empty($boneyard)) return null;

    // Check if ALL players can't play (blocked)
    // We detect this via consecutive_passes equaling player count
    $allBlocked = true;
    $board = null; // We'd need board state here — but we track via consecutive_passes in game_state
    // This check happens at higher level via consecutive_passes
    return null;
}

function isGameBlocked($hands, $boneyard, $board, $spinner, $mode) {
    if ($mode === '2p' && !empty($boneyard)) return false;

    $ends = getPlayableEnds($board, $spinner);
    foreach ($hands as $hand) {
        if (canPlayerPlay($hand, $ends)) return false;
    }
    return true;
}

function handleRoundEnd($match, $matchId, $roundResult, $hands, $board, $spinner, $moves, $gameState, $players, $userId) {
    $scores = json_decode($match['scores'], true);
    $roundHistory = json_decode($match['round_history'], true);
    $mode = $match['mode'];

    $roundScore = 0;
    $winnerIdx = $roundResult['winner_idx'];

    if ($roundResult['type'] === 'domino') {
        $roundScore = scoreRound($hands, $winnerIdx, $players, $mode);
    } else {
        $blocked = scoreBlockedRound($hands, $players, $mode);
        $winnerIdx = $blocked['winner_idx'];
        $roundScore = $blocked['score'];
    }

    // Award score to winner (or team)
    if ($winnerIdx !== null) {
        if ($mode === '4p_teams') {
            $team = $winnerIdx % 2;
            $scores[$team] = ($scores[$team] ?? 0) + $roundScore;
        } else {
            $scores[$winnerIdx] = ($scores[$winnerIdx] ?? 0) + $roundScore;
        }
    }

    $roundHistory[] = [
        'round' => $match['round_number'],
        'winner_idx' => $winnerIdx,
        'score' => $roundScore,
        'type' => $roundResult['type'],
        'remaining_pips' => array_map(fn($h) => pipCount($h), $hands)
    ];

    // Check match end
    $targetScore = $match['target_score'];
    $matchWinner = null;
    foreach ($scores as $idx => $s) {
        if ($s >= $targetScore) {
            $matchWinner = $idx;
            break;
        }
    }

    if ($matchWinner !== null) {
        $gameState['phase'] = 'match_over';
        $update = [
            'board' => json_encode($board),
            'hands' => json_encode($hands),
            'spinner' => $spinner ? json_encode($spinner) : null,
            'moves' => json_encode($moves),
            'scores' => json_encode($scores),
            'round_history' => json_encode($roundHistory),
            'game_state' => json_encode($gameState),
            'status' => 'completed',
            'completed_at' => date('c'),
            'result' => ($mode === '4p_teams' ? "team_{$matchWinner}" : "player_{$matchWinner}") . '_wins'
        ];

        $patchRes = supabase_rest('PATCH', "domino_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
        $responseMatch = sanitizeMatchForPlayer($patchRes['data'][0], $userId);
        echo json_encode(['ok' => true, 'match' => $responseMatch, 'round_over' => true, 'match_over' => true, 'winner' => $matchWinner]);
        return;
    }

    // Start new round
    $newRound = $match['round_number'] + 1;
    $deal = shuffleAndDeal($mode, count($players));
    $firstPlayer = findFirstPlayer($deal['hands'], $newRound, $winnerIdx);

    $gameState['phase'] = 'play';
    $gameState['consecutive_passes'] = 0;

    $update = [
        'board' => json_encode([]),
        'hands' => json_encode($deal['hands']),
        'boneyard' => json_encode($deal['boneyard']),
        'spinner' => null,
        'moves' => json_encode([]),
        'scores' => json_encode($scores),
        'round_number' => $newRound,
        'round_history' => json_encode($roundHistory),
        'current_turn' => $firstPlayer,
        'game_state' => json_encode($gameState)
    ];

    $patchRes = supabase_rest('PATCH', "domino_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
    $responseMatch = sanitizeMatchForPlayer($patchRes['data'][0], $userId);
    echo json_encode(['ok' => true, 'match' => $responseMatch, 'round_over' => true, 'round_score' => $roundScore, 'round_winner' => $winnerIdx]);

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

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

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

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

        $res = supabase_rest('GET', "domino_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;

        $hands = json_decode($match['hands'], true);
        $board = json_decode($match['board'], true);
        $spinner = $match['spinner'] ? json_decode($match['spinner'], true) : null;
        $boneyard = json_decode($match['boneyard'], true);
        $gameState = json_decode($match['game_state'], true);
        $moves = json_decode($match['moves'], true);
        $mode = $match['mode'];

        $hand = $hands[$currentTurn];
        $ends = getPlayableEnds($board, $spinner);

        // First move on empty board
        if (empty($board)) {
            $tile = botChooseFirstTile($hand, $currentPlayer['difficulty'] ?? 'easy');
            $isDouble = ($tile['left'] === $tile['right']);
            $board[] = ['tile' => $tile, 'side' => null, 'isSpinner' => $isDouble, 'exposed_end' => null];
            if ($isDouble) $spinner = $tile;
            $hands[$currentTurn] = array_values(array_filter($hand, fn($t) => $t['id'] !== $tile['id']));
            $moves[] = ['player' => $currentTurn, 'action' => 'play', 'tile' => $tile, 'side' => null, 'at' => date('c'), 'bot' => true];
            $gameState['consecutive_passes'] = 0;
        } elseif (canPlayerPlay($hand, $ends)) {
            // Bot plays
            $choice = botChoosePlay($hand, $ends, $board, $currentPlayer['difficulty'] ?? 'easy', $moves);
            $tile = $choice['tile'];
            $side = $choice['side'];
            $endValue = $ends[$side];
            $exposedEnd = getExposedEnd($tile, $endValue);
            $isDouble = ($tile['left'] === $tile['right']);
            if ($isDouble) $exposedEnd = $tile['left'];
            if ($isDouble && !$spinner) $spinner = $tile;

            $board[] = ['tile' => $tile, 'side' => $side, 'isSpinner' => false, 'exposed_end' => $exposedEnd];
            $hands[$currentTurn] = array_values(array_filter($hand, fn($t) => $t['id'] !== $tile['id']));
            $moves[] = ['player' => $currentTurn, 'action' => 'play', 'tile' => $tile, 'side' => $side, 'at' => date('c'), 'bot' => true];
            $gameState['consecutive_passes'] = 0;
        } elseif ($mode === '2p' && !empty($boneyard)) {
            // Bot draws
            $drawnTile = array_pop($boneyard);
            $hands[$currentTurn][] = $drawnTile;
            $moves[] = ['player' => $currentTurn, 'action' => 'draw', 'at' => date('c'), 'bot' => true];

            // Keep drawing until can play or boneyard empty
            $newEnds = getPlayableEnds($board, $spinner);
            while (!canPlayerPlay($hands[$currentTurn], $newEnds) && !empty($boneyard)) {
                $drawnTile = array_pop($boneyard);
                $hands[$currentTurn][] = $drawnTile;
                $moves[] = ['player' => $currentTurn, 'action' => 'draw', 'at' => date('c'), 'bot' => true];
            }

            // Now try to play
            if (canPlayerPlay($hands[$currentTurn], $newEnds)) {
                $choice = botChoosePlay($hands[$currentTurn], $newEnds, $board, $currentPlayer['difficulty'] ?? 'easy', $moves);
                $tile = $choice['tile'];
                $side = $choice['side'];
                $endValue = $newEnds[$side];
                $exposedEnd = getExposedEnd($tile, $endValue);
                $isDouble = ($tile['left'] === $tile['right']);
                if ($isDouble) $exposedEnd = $tile['left'];
                if ($isDouble && !$spinner) $spinner = $tile;

                $board[] = ['tile' => $tile, 'side' => $side, 'isSpinner' => false, 'exposed_end' => $exposedEnd];
                $hands[$currentTurn] = array_values(array_filter($hands[$currentTurn], fn($t) => $t['id'] !== $tile['id']));
                $moves[] = ['player' => $currentTurn, 'action' => 'play', 'tile' => $tile, 'side' => $side, 'at' => date('c'), 'bot' => true];
                $gameState['consecutive_passes'] = 0;
            } else {
                $gameState['consecutive_passes'] = ($gameState['consecutive_passes'] ?? 0) + 1;
                $moves[] = ['player' => $currentTurn, 'action' => 'pass', 'at' => date('c'), 'bot' => true];
            }
        } else {
            // Bot passes (4P mode or 2P with empty boneyard)
            $gameState['consecutive_passes'] = ($gameState['consecutive_passes'] ?? 0) + 1;
            $moves[] = ['player' => $currentTurn, 'action' => 'pass', 'at' => date('c'), 'bot' => true];
        }

        // Check round end
        $playerCount = count($players);
        $roundOver = false;
        $roundResult = null;

        if (empty($hands[$currentTurn])) {
            $roundResult = ['type' => 'domino', 'winner_idx' => $currentTurn];
            $roundOver = true;
        } elseif (($gameState['consecutive_passes'] ?? 0) >= $playerCount) {
            $roundResult = ['type' => 'blocked', 'winner_idx' => null];
            $roundOver = true;
        } elseif (isGameBlocked($hands, $boneyard, $board, $spinner, $mode)) {
            $roundResult = ['type' => 'blocked', 'winner_idx' => null];
            $roundOver = true;
        }

        if ($roundOver) {
            $scores = json_decode($match['scores'], true);
            $roundHistory = json_decode($match['round_history'], true);

            if ($roundResult['type'] === 'domino') {
                $roundScore = scoreRound($hands, $roundResult['winner_idx'], $players, $mode);
                $winnerIdx = $roundResult['winner_idx'];
            } else {
                $blocked = scoreBlockedRound($hands, $players, $mode);
                $winnerIdx = $blocked['winner_idx'];
                $roundScore = $blocked['score'];
            }

            if ($winnerIdx !== null) {
                if ($mode === '4p_teams') {
                    $team = $winnerIdx % 2;
                    $scores[$team] = ($scores[$team] ?? 0) + $roundScore;
                } else {
                    $scores[$winnerIdx] = ($scores[$winnerIdx] ?? 0) + $roundScore;
                }
            }

            $roundHistory[] = [
                'round' => $match['round_number'],
                'winner_idx' => $winnerIdx,
                'score' => $roundScore,
                'type' => $roundResult['type']
            ];

            $targetScore = $match['target_score'];
            $matchWinner = null;
            foreach ($scores as $idx => $s) {
                if ($s >= $targetScore) { $matchWinner = $idx; break; }
            }

            if ($matchWinner !== null) {
                $gameState['phase'] = 'match_over';
                $update = [
                    'board' => json_encode($board),
                    'hands' => json_encode($hands),
                    'boneyard' => json_encode($boneyard),
                    'spinner' => $spinner ? json_encode($spinner) : null,
                    'moves' => json_encode($moves),
                    'scores' => json_encode($scores),
                    'round_history' => json_encode($roundHistory),
                    'game_state' => json_encode($gameState),
                    'status' => 'completed',
                    'completed_at' => date('c'),
                    'result' => ($mode === '4p_teams' ? "team_{$matchWinner}" : "player_{$matchWinner}") . '_wins'
                ];
                supabase_rest('PATCH', "domino_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);
                break;
            }

            // New round
            $newRound = $match['round_number'] + 1;
            $deal = shuffleAndDeal($mode, $playerCount);
            $firstPlayer = findFirstPlayer($deal['hands'], $newRound, $winnerIdx);
            $gameState['phase'] = 'play';
            $gameState['consecutive_passes'] = 0;

            $update = [
                'board' => json_encode([]),
                'hands' => json_encode($deal['hands']),
                'boneyard' => json_encode($deal['boneyard']),
                'spinner' => null,
                'moves' => json_encode([]),
                'scores' => json_encode($scores),
                'round_number' => $newRound,
                'round_history' => json_encode($roundHistory),
                'current_turn' => $firstPlayer,
                'game_state' => json_encode($gameState)
            ];
            supabase_rest('PATCH', "domino_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);

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

        // Next turn
        $nextTurn = ($currentTurn + 1) % $playerCount;
        $update = [
            'board' => json_encode($board),
            'hands' => json_encode($hands),
            'boneyard' => json_encode($boneyard),
            'spinner' => $spinner ? json_encode($spinner) : null,
            'moves' => json_encode($moves),
            'current_turn' => $nextTurn,
            'game_state' => json_encode($gameState)
        ];
        supabase_rest('PATCH', "domino_matches?id=eq.{$matchId}", $update, SUPABASE_SERVICE_KEY);

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

function botChooseFirstTile($hand, $difficulty) {
    // Prefer [6|6], then highest double, then highest pip tile
    $doubles = array_filter($hand, fn($t) => $t['left'] === $t['right']);
    if (!empty($doubles)) {
        usort($doubles, fn($a, $b) => $b['left'] - $a['left']);
        return $doubles[0];
    }
    usort($hand, fn($a, $b) => ($b['left'] + $b['right']) - ($a['left'] + $a['right']));
    return $hand[0];
}

function botChoosePlay($hand, $ends, $board, $difficulty, $moves) {
    $validPlays = [];
    foreach ($hand as $tile) {
        foreach ($ends as $side => $val) {
            if (tileMatchesEnd($tile, $val)) {
                $validPlays[] = ['tile' => $tile, 'side' => $side, 'pips' => $tile['left'] + $tile['right']];
            }
        }
    }

    if (empty($validPlays)) return null;

    if ($difficulty === 'easy') {
        return $validPlays[array_rand($validPlays)];
    }

    if ($difficulty === 'medium') {
        // Play highest pip count first
        usort($validPlays, fn($a, $b) => $b['pips'] - $a['pips']);
        return $validPlays[0];
    }

    // Hard: score each play
    $bestScore = -999;
    $bestPlay = $validPlays[0];
    foreach ($validPlays as $play) {
        $score = $play['pips'] * 2; // Prefer high-pip tiles
        if ($play['tile']['left'] === $play['tile']['right']) $score += 5; // Play doubles early
        // Prefer tiles that keep variety
        $remaining = array_filter($hand, fn($t) => $t['id'] !== $play['tile']['id']);
        $values = [];
        foreach ($remaining as $t) { $values[$t['left']] = true; $values[$t['right']] = true; }
        $score += count($values) * 3;
        if ($score > $bestScore) { $bestScore = $score; $bestPlay = $play; }
    }
    return $bestPlay;
}

// ─── 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]['connected'] = false;
    $players[$playerIdx]['name'] = 'بوت (غادر)';

    $update = ['players' => json_encode($players)];
    supabase_rest('PATCH', "domino_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', "domino_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';
    $mode = $input['mode'] ?? '2p';

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

    $existing = supabase_rest('GET', "domino_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', "domino_queue?user_id=eq.{$userId}", [], SUPABASE_SERVICE_KEY);
            echo json_encode(['ok' => true, 'status' => 'matched', 'match_id' => $entry['match_id']]);
            return;
        }

        // Try to find opponents
        $needed = ($mode === '4p_teams') ? 3 : 1;
        $opponents = supabase_rest('GET', "domino_queue?user_id=neq.{$userId}&match_id=is.null&mode=eq.{$mode}&select=*&limit={$needed}", [], SUPABASE_SERVICE_KEY);

        if (!empty($opponents['data']) && count($opponents['data']) >= $needed) {
            $allPlayers = [['id' => $userId, 'name' => $userName, 'color' => 'P1', 'type' => 'human', 'connected' => true]];
            $colors = ['P2', 'P3', 'P4'];
            $opponentIds = [];

            foreach ($opponents['data'] as $idx => $opp) {
                if ($idx >= $needed) break;
                $allPlayers[] = ['id' => $opp['user_id'], 'name' => $opp['user_name'] ?? 'لاعب', 'color' => $colors[$idx], 'type' => 'human', 'connected' => true];
                $opponentIds[] = $opp['user_id'];
            }

            $playerCount = ($mode === '4p_teams') ? 4 : 2;
            $targetScore = ($mode === '4p_teams') ? 150 : 100;
            $deal = shuffleAndDeal($mode, $playerCount);
            $firstPlayer = findFirstPlayer($deal['hands'], 1, null);

            $matchData = [
                'room_code' => generateRoomCode(),
                'status' => 'in_progress',
                'mode' => $mode,
                'player_count' => $playerCount,
                'players' => json_encode($allPlayers),
                'current_turn' => $firstPlayer,
                'board' => json_encode([]),
                'hands' => json_encode($deal['hands']),
                'boneyard' => json_encode($deal['boneyard']),
                'spinner' => null,
                'scores' => json_encode(array_fill(0, $playerCount, 0)),
                'round_number' => 1,
                'round_history' => json_encode([]),
                'target_score' => $targetScore,
                'moves' => json_encode([]),
                'game_state' => json_encode(['phase' => 'play', 'started_at' => date('c'), 'consecutive_passes' => 0]),
                'chat' => json_encode([]),
                'host_id' => $userId
            ];

            $matchRes = supabase_rest('POST', 'domino_matches', $matchData, SUPABASE_SERVICE_KEY);
            if (!empty($matchRes['data'])) {
                $newMatchId = $matchRes['data'][0]['id'];
                foreach ($opponentIds as $oppId) {
                    supabase_rest('PATCH', "domino_queue?user_id=eq.{$oppId}", ['match_id' => $newMatchId], SUPABASE_SERVICE_KEY);
                }
                supabase_rest('DELETE', "domino_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', 'domino_queue', [
        'user_id' => $userId,
        'user_name' => $userName,
        'mode' => $mode
    ], 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', "domino_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;
}
