<?php
/**
 * Integration test: the tournament engine against the REAL Supabase database.
 *
 * The offline suite proves the pairing maths. This proves the parts only a real
 * Postgres can: jsonb round-trips, the match_result / time_control enums, the
 * deterministic-match-id race guard, and the conditional writes.
 *
 * Everything it creates is prefixed ZZZ-ENGINE-TEST and removed in teardown,
 * which also runs if an assertion fails.
 */

// Runs against a LOCAL Postgres carrying a replica of the production types and
// tables — real enums, real jsonb, real primary keys — so no production data is
// touched.
require_once __DIR__ . '/pgshim.php';
if (!function_exists('supabaseService')) { function supabaseService() { return new PgShim(); } }
if (!function_exists('supabaseRpc')) { function supabaseRpc($f, $p = []) { return null; } }
if (!function_exists('supabase')) { function supabase($t = null) { return new PgShim(); } }

$ROOT = dirname(__DIR__);
require_once $ROOT . '/includes/chess.php';
require_once __DIR__ . '/engine_shim.php';

$PLAYERS = [
    '5ed4d72d-555f-4d19-8315-204dafd3a2e7',
    '05a1a1af-a3c3-42a7-923d-35288914b251',
    '0a2911a5-6e99-46bb-94a3-46af69d124ab',
    '991ec242-44f1-49a5-a7ba-2413839aeb79',
    'a2aba430-4852-49e0-9ff9-be6a86526052',
    'd984ada5-39c2-4377-a972-fc651563efee',
    'b5da2bf1-0d28-4297-b96e-566b72ddedd9',
    '66fb12a9-4def-48e5-afdd-fe27966e78b8',
];

$db = new PgShim();
// Seed the eight test players locally.
foreach ($PLAYERS as $i => $pid) {
    $db->insert('profiles', ['id' => $pid, 'display_name' => 'Test Player ' . ($i+1),
        'username' => 'tp' . ($i+1), 'elo_rapid' => 2000 - $i * 55, 'elo_blitz' => 1900 - $i * 40]);
}
$FAIL = [];
$tournamentId = null;
$createdMatchIds = [];

function check(string $label, bool $cond, string $detail = ''): void {
    global $FAIL;
    if ($cond) { echo "  ok    $label\n"; return; }
    $FAIL[] = $label;
    echo "  FAIL  $label" . ($detail ? " — $detail" : '') . "\n";
}

function teardown() {
    global $db, $tournamentId, $createdMatchIds;
    echo "\n-- teardown\n";
    foreach ($createdMatchIds as $mid) {
        $db->delete('mp_log', ['match_id' => 'eq.' . $mid]);
        $db->delete('matches', ['id' => 'eq.' . $mid]);
    }
    if ($tournamentId) {
        $db->delete('matches', ['tournament_id' => 'eq.' . $tournamentId]);
        $db->delete('el3ab_tournament_rounds', ['tournament_id' => 'eq.' . $tournamentId]);
        $db->delete('tournament_registrations', ['tournament_id' => 'eq.' . $tournamentId]);
        $db->delete('el3ab_tournaments', ['id' => 'eq.' . $tournamentId]);
        echo "   removed tournament $tournamentId and its rows\n";
    }
}
register_shutdown_function('teardown');

// ---------------------------------------------------------------------------
echo "=== 1. create tournament ===\n";
$created = $db->insert('el3ab_tournaments', [
    'name' => 'ZZZ-ENGINE-TEST',
    'game_key' => 'chess',
    'format' => 'swiss',
    'time_control' => 'blitz_3_2',
    'swiss_rounds' => 3,
    'rounds_total' => 3,
    'status' => TOURNAMENT_STATUS_REGISTRATION,
    'current_round' => 0,
    'min_players' => 4,
    'max_players' => 16,
    'auto_start' => true,
    'is_rated' => false,
    'starts_at' => gmdate('c', time() - 60),
    'tiebreak_rules' => ['buchholz_cut_1', 'buchholz', 'sonneborn_berger'],
]);
check('tournament row created', !isset($created['error']) && !empty($created[0]['id']), json_encode($created));
if (isset($created['error'])) { exit(1); }
$tournamentId = $created[0]['id'];
echo "   id = $tournamentId\n";

echo "=== 2. register players ===\n";
foreach ($PLAYERS as $pid) {
    $r = $db->insert('tournament_registrations', [
        'tournament_id' => $tournamentId, 'player_id' => $pid, 'status' => 'registered',
    ]);
    if (isset($r['error'])) { check("register $pid", false, json_encode($r)); }
}
$loaded = tournamentPlayers($db, $tournamentId, 'blitz_3_2');
check('all players load back', count($loaded) === count($PLAYERS), 'got ' . count($loaded));
check('players carry ratings', !empty($loaded) && isset($loaded[0]['rating']));

echo "=== 3. start (pairs round 1) ===\n";
$start = tournamentStart($db, $tournamentId);
check('start succeeded', $start['ok'], $start['error'] ?? '');
check('round 1 exists', !empty($start['round']));
$r1 = $start['round'];
check('pairings survived the jsonb round-trip as an array', is_array($r1['pairings']), gettype($r1['pairings']));
check('correct number of boards', count($r1['pairings']) === count($PLAYERS) / 2, count($r1['pairings']) . ' boards');
foreach ($r1['pairings'] as $p) {
    check("board {$p['board']} has both colours", !empty($p['white_id']) && !empty($p['black_id']));
    check("board {$p['board']} has a pairing_id", !empty($p['pairing_id']));
}

echo "=== 4. starting twice must not create a second round 1 ===\n";
$again = tournamentStart($db, $tournamentId);
$rounds = tournamentRounds($db, $tournamentId);
check('still exactly one round', count($rounds) === 1, count($rounds) . ' rounds');

echo "=== 5. two players race to open the same board ===\n";
$p0 = $r1['pairings'][0];
$matchId = tournamentMatchIdFor($tournamentId, 1, (int)$p0['board']);
$clock = chessTimeControlMs('blitz_3_2');
$mk = fn() => $db->insert('matches', [
    'id' => $matchId, 'game_key' => 'chess', 'match_type' => 'tournament',
    'white_player_id' => $p0['white_id'], 'black_player_id' => $p0['black_id'],
    'status' => 'in_progress', 'time_control' => 'blitz_3_2',
    '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' => 1,
    'pairing_id' => $p0['pairing_id'],
    'starting_fen' => CHESS_START_FEN, 'current_fen' => CHESS_START_FEN,
    'moves' => [], 'move_count' => 0, 'is_rated' => false,
    'started_at' => gmdate('c'),
    'metadata' => ['mode' => 'tournament', 'board' => $p0['board']],
]);
$first = $mk();
$createdMatchIds[] = $matchId;
$second = $mk();
check('first insert wins', !isset($first['error']), json_encode($first));
check('second insert is rejected by the primary key', isset($second['error']), json_encode($second));
$all = $db->get('matches', ['tournament_id' => 'eq.' . $tournamentId, 'select' => 'id']);
check('exactly one match exists for that pairing', count($all) === 1, count($all) . ' matches');

echo "=== 6. jsonb columns round-trip as objects, not strings ===\n";
$row = $db->get('matches', ['id' => 'eq.' . $matchId, 'select' => 'moves,metadata,game_state', 'limit' => 1])[0];
check('moves is an array', is_array($row['moves']), gettype($row['moves']) . ' = ' . json_encode($row['moves']));
check('metadata is an array', is_array($row['metadata']), gettype($row['metadata']) . ' = ' . json_encode($row['metadata']));

echo "=== 7. every derived result is accepted by the match_result enum ===\n";
foreach (['checkmate', 'resign', 'timeout', 'stalemate', 'agreement', 'abandon', 'aborted'] as $reason) {
    $d = chessDeriveResult($reason, CHESS_START_FEN, 'w');
    $res = $db->update('matches', ['result' => $d['result']], ['id' => 'eq.' . $matchId]);
    check("enum accepts '{$d['result']}' (from $reason)", !isset($res['error']), json_encode($res));
}
$db->update('matches', ['result' => null, 'status' => 'in_progress'], ['id' => 'eq.' . $matchId]);

echo "=== 8. play the whole tournament ===\n";
for ($round = 1; $round <= 3; $round++) {
    $t = tournamentLoad($db, $tournamentId);
    if ($t['status'] !== TOURNAMENT_STATUS_IN_PROGRESS) break;
    $rn = (int)$t['current_round'];
    $r = tournamentRound($db, $tournamentId, $rn);
    check("round $rn is open", $r && $r['status'] === ROUND_STATUS_IN_PROGRESS);
    if (!$r) break;

    $seats = [];
    foreach ($r['pairings'] as $p) {
        if (!empty($p['is_bye'])) { $seats[] = $p['white_id']; continue; }
        $seats[] = $p['white_id']; $seats[] = $p['black_id'];
    }
    check("round $rn seats everyone exactly once",
          count($seats) === count($PLAYERS) && count(array_unique($seats)) === count($PLAYERS),
          count($seats) . ' seats / ' . count(array_unique($seats)) . ' unique');

    foreach ($r['pairings'] as $i => $p) {
        if (!empty($p['is_bye'])) continue;
        $res = [0 => 'white_wins', 1 => 'black_wins', 2 => 'draw'][($i + $rn) % 3];
        tournamentRecordMatchResult($db, [
            'id' => tournamentMatchIdFor($tournamentId, $rn, (int)$p['board']),
            'game_key' => 'chess',
            'tournament_id' => $tournamentId,
            'tournament_round' => $rn,
            'white_player_id' => $p['white_id'],
            'black_player_id' => $p['black_id'],
        ], ['result' => $res, 'reason' => 'checkmate', 'winner' => $res === 'white_wins' ? 'white' : ($res === 'black_wins' ? 'black' : null)]);
    }
}

$t = tournamentLoad($db, $tournamentId);
check('tournament reached completed', $t['status'] === TOURNAMENT_STATUS_COMPLETED,
      "status={$t['status']} round={$t['current_round']}");
check('all 3 rounds were created', count(tournamentRounds($db, $tournamentId)) === 3);

echo "=== 9. standings ===\n";
$standings = tournamentStandings($db, $t);
check('standings cover every player', count($standings) === count($PLAYERS), count($standings) . ' rows');
$total = array_sum(array_column($standings, 'points'));
check('points conserved (1.0 per game)', abs($total - 12.0) < 0.001, "total=$total, expected 12.0");
check('ranks are 1..N', array_column($standings, 'rank') === range(1, count($PLAYERS)));
check('tiebreaks present', isset($standings[0]['tiebreaks']['buchholz_cut_1']));
foreach (array_slice($standings, 0, 4) as $s) {
    printf("     %d. %-38s %.1f  bh1=%.1f sb=%.1f\n", $s['rank'], substr($s['name'], 0, 36), $s['points'],
        $s['tiebreaks']['buchholz_cut_1'], $s['tiebreaks']['sonneborn_berger']);
}

echo "=== 10. final placings written back ===\n";
$regs = $db->get('tournament_registrations', ['tournament_id' => 'eq.' . $tournamentId, 'select' => 'player_id,final_standing']);
$placed = array_filter($regs, fn($r) => $r['final_standing'] !== null);
check('every player has a final standing', count($placed) === count($PLAYERS), count($placed) . ' placed');

echo "\n";
if ($FAIL) { echo count($FAIL) . " FAILURE(S)\n"; exit(1); }
echo "ALL INTEGRATION CHECKS PASSED\n";
