<?php

header('Content-Type: application/json');
require_once __DIR__ . '/../includes/cors.php';

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { http_response_code(200); exit; }

require_once __DIR__ . '/../includes/auth.php';
require_once __DIR__ . '/../includes/supabase.php';
require_once __DIR__ . '/../includes/chess.php';
require_once __DIR__ . '/../includes/tournament-engine.php';
require_once __DIR__ . '/../config/constants.php';

$token = requireAuth();
$userId = getUserId($token);
$input = getInput();
$action = $input['action'] ?? ($_GET['action'] ?? '');

$db = supabaseService();

switch ($action) {
    case 'create-or-join':
        handleCreateOrJoin($db, $userId, $input);
        break;
    case 'report-result':
        handleReportResult($db, $userId, $input);
        break;
    case 'my-pending':
        handleMyPending($db, $userId);
        break;
    default:
        jsonError('Invalid action');
}

/**
 * Give the caller the match for their current-round pairing, creating it if it
 * does not exist yet.
 *
 * Both paired players call this at roughly the same moment. The match id is
 * derived from (tournament, round, board), so both compute the same value and
 * the primary key on `matches` decides which INSERT wins — the loser simply
 * reads back the winner's row. The previous version did a read-then-insert with
 * no lock, so both players could create their own match and each would sit
 * alone on a board.
 *
 * Colour comes from the pairing the Swiss engine produced, never re-randomised
 * here, so both clients are told the same thing.
 */
function handleCreateOrJoin($db, string $userId, array $input): void {
    $tournamentId = $input['tournament_id'] ?? '';
    if (!$tournamentId) jsonError('tournament_id is required');

    $tournament = tournamentLoad($db, $tournamentId);
    if (!$tournament) jsonError('Tournament not found', 404);

    // A player arriving is a good moment to resolve no-shows and advance the
    // round, so the person who did turn up is not left waiting on one who did not.
    tournamentTick($db, $tournament);
    $tournament = tournamentLoad($db, $tournamentId) ?: $tournament;

    if ($tournament['status'] !== TOURNAMENT_STATUS_IN_PROGRESS) {
        jsonError('Tournament is ' . $tournament['status'], 409);
    }

    $pending = tournamentPendingPairingFor($db, $tournament, $userId);
    if (!$pending) {
        jsonResponse(['waiting' => true, 'message' => 'No game to play in this round']);
    }

    $round = $pending['round'];
    $pairing = $pending['pairing'];
    $roundNumber = (int)$round['round_number'];
    $board = (int)($pairing['board'] ?? 1);

    if (!empty($pairing['is_bye'])) {
        jsonResponse([
            'bye' => true,
            'round_number' => $roundNumber,
            'message' => 'You have a bye this round',
        ]);
    }

    $whiteId = $pairing['white_id'];
    $blackId = $pairing['black_id'];
    $matchId = tournamentMatchIdFor($tournamentId, $roundNumber, $board);

    $timeControl = chessNormaliseTimeControl($tournament['time_control'] ?? null);
    $gameKey = $tournament['game_key'] ?? 'chess';
    $clock = chessTimeControlMs($timeControl);

    // Try to claim the match. A duplicate primary key here means the opponent
    // created it a moment ago, which is a success, not an error.
    $created = $db->insert('matches', [
        'id'                      => $matchId,
        'game_key'                => $gameKey,
        'match_type'              => 'tournament',
        'white_player_id'         => $whiteId,
        'black_player_id'         => $blackId,
        'status'                  => 'in_progress',
        'time_control'            => $timeControl,
        'initial_time_ms'         => $clock['initial'],
        'increment_ms'            => $clock['increment'],
        'white_time_remaining_ms' => $clock['initial'],
        'black_time_remaining_ms' => $clock['initial'],
        'tournament_id'           => $tournamentId,
        'tournament_round'        => $roundNumber,
        'pairing_id'              => $pairing['pairing_id'] ?? null,
        'starting_fen'            => CHESS_START_FEN,
        'current_fen'             => CHESS_START_FEN,
        'moves'                   => [],
        'move_count'              => 0,
        'is_rated'                => (bool)($tournament['is_rated'] ?? true),
        'started_at'              => gmdate('c'),
        'metadata'                => [
            'mode' => 'tournament',
            'round_id' => $round['id'],
            'round_number' => $roundNumber,
            'board' => $board,
        ],
    ]);

    $alreadyExisted = isset($created['error']);

    $rows = $db->get('matches', [
        'id' => 'eq.' . $matchId,
        'select' => 'id,white_player_id,black_player_id,status,time_control,current_fen,move_count,result',
        'limit' => 1,
    ]);
    $match = (is_array($rows) && !empty($rows) && !isset($rows['error'])) ? $rows[0] : null;
    if (!$match) {
        jsonError('Could not open the match for this pairing', 500);
    }

    $isWhite = ($match['white_player_id'] === $userId);

    jsonResponse([
        'match_id'       => $match['id'],
        'color'          => $isWhite ? 'w' : 'b',
        'opponent_id'    => $isWhite ? $match['black_player_id'] : $match['white_player_id'],
        'time_control'   => $match['time_control'],
        'status'         => $match['status'],
        'round_number'   => $roundNumber,
        'board'          => $board,
        'already_exists' => $alreadyExisted,
        // A match already carrying moves means we are rejoining, not starting.
        'recovered'      => (int)($match['move_count'] ?? 0) > 0,
    ]);
}

function handleReportResult($db, string $userId, array $input): void {
    $matchId = $input['match_id'] ?? '';
    $tournamentId = $input['tournament_id'] ?? '';
    if (!$matchId || !$tournamentId) jsonError('match_id and tournament_id are required');

    $matches = $db->get('matches', [
        'id' => 'eq.' . $matchId,
        'select' => 'id,game_key,white_player_id,black_player_id,status,result,tournament_id,tournament_round,metadata',
        'limit' => 1,
    ]);
    $match = is_array($matches) && !empty($matches) && !isset($matches['error']) ? $matches[0] : null;
    if (!$match) jsonError('Match not found', 404);
    if ($match['tournament_id'] !== $tournamentId) jsonError('Match does not belong to this tournament');
    if ($match['white_player_id'] !== $userId && $match['black_player_id'] !== $userId) {
        jsonError('Not authorized for this match', 403);
    }

    // The result is whatever the server already settled on when the match was
    // finalised — it is never taken from the caller. This endpoint exists only
    // so an older client calling it still nudges the round forward; the real
    // recording happens in game.php's finaliseMatch().
    if ($match['status'] !== 'completed' || empty($match['result'])) {
        jsonResponse(['ok' => false, 'pending' => true, 'message' => 'Match has not been finalised yet']);
    }

    $meta = is_array($match['metadata'] ?? null) ? $match['metadata'] : (json_decode((string)($match['metadata'] ?? '{}'), true) ?: []);
    $derived = [
        'result' => $match['result'],
        'reason' => $meta['end_reason'] ?? 'unknown',
        'winner' => $meta['winner'] ?? null,
    ];

    tournamentRecordMatchResult($db, $match, $derived);

    jsonResponse(['ok' => true, 'result' => $derived['result']]);
}

/**
 * Everything the caller currently owes a game in, across every tournament they
 * are registered for.
 */
function handleMyPending($db, string $userId): void {
    $regs = $db->get('tournament_registrations', [
        'player_id' => 'eq.' . $userId,
        'status' => 'eq.registered',
        'select' => 'tournament_id',
    ]);
    if (!is_array($regs) || isset($regs['error']) || empty($regs)) {
        jsonResponse(['pending' => []]);
    }

    $pending = [];
    foreach ($regs as $reg) {
        $tournament = tournamentLoad($db, $reg['tournament_id']);
        if (!$tournament) continue;
        tournamentTick($db, $tournament);
        $tournament = tournamentLoad($db, $reg['tournament_id']) ?: $tournament;
        if ($tournament['status'] !== TOURNAMENT_STATUS_IN_PROGRESS) continue;

        $found = tournamentPendingPairingFor($db, $tournament, $userId);
        if (!$found) continue;

        $pairing = $found['pairing'];
        $round = $found['round'];
        $isBye = !empty($pairing['is_bye']);
        $opponentId = $isBye ? null
            : (($pairing['white_id'] === $userId) ? $pairing['black_id'] : $pairing['white_id']);

        $opponentName = null;
        $opponentAvatar = null;
        if ($opponentId) {
            $profiles = $db->get('profiles', ['id' => 'eq.' . $opponentId, 'select' => 'display_name,username,avatar_url', 'limit' => 1]);
            if (is_array($profiles) && !empty($profiles) && !isset($profiles['error'])) {
                $opponentName = $profiles[0]['display_name'] ?? $profiles[0]['username'] ?? null;
                $opponentAvatar = $profiles[0]['avatar_url'] ?? null;
            }
        }

        $pending[] = [
            'tournament_id'   => $tournament['id'],
            'tournament_name' => $tournament['name'],
            'round_id'        => $round['id'],
            'round_number'    => (int)$round['round_number'],
            'board'           => $pairing['board'] ?? null,
            'pairing_id'      => $pairing['pairing_id'] ?? null,
            'color'           => $isBye ? null : (($pairing['white_id'] === $userId) ? 'w' : 'b'),
            'opponent_id'     => $opponentId,
            'opponent_name'   => $opponentName,
            'opponent_avatar' => $opponentAvatar,
            'is_bye'          => $isBye,
            'time_control'    => $tournament['time_control'],
            'game_key'        => $tournament['game_key'],
        ];
    }

    jsonResponse(['pending' => $pending]);
}
