<?php
require_once __DIR__ . '/../init.php';
require_once PROJECT_ROOT . '/backend/controllers/common_function.php';
require_once PROJECT_ROOT . '/backend/controllers/db_select_helper.php';

// include other controller
require_once PROJECT_ROOT . '/backend/controllers/ActivityController.php';
require_once PROJECT_ROOT . '/backend/controllers/UserAksesController.php';

class TournamentMatchController
{
    private $conn;

    private $table = "
        tournament_match TM
        LEFT JOIN tournament T ON TM.IDTournament = T.IDTournament
        LEFT JOIN tournament_category_level TCL ON TM.IDTCL = TCL.IDTCL
        LEFT JOIN kategori K ON TCL.IDKategori = K.IDKategori
        LEFT JOIN level L ON TCL.IDLevel = L.IDLevel
        LEFT JOIN tournament_group TG ON TM.IDGroup = TG.IDGroup

        LEFT JOIN team TeamA ON TM.TeamA = TeamA.IDTeam
        LEFT JOIN team TeamB ON TM.TeamB = TeamB.IDTeam
        LEFT JOIN team Winner ON TM.WinnerTeam = Winner.IDTeam
    ";

    private $pk = "IDMatch";
    private $sv = "TournamentMatch";

    protected $displayedColumn = [

        // Match Info
        'TM.IDMatch',
        'TM.RoundNumber',
        'TM.MatchNumber',
        'TM.Status',
        'TM.MatchDate',
        'TM.NextMatchID',
        'TM.CreateAt',

        // Tournament
        'TM.IDTournament',
        'T.NamaTournament',
        'T.NamaTournamentEN',

        // Category Level
        'TM.IDTCL',
        'TCL.IDKategori',
        'K.NamaKategori',
        'K.TipeKategori',
        'TCL.IDLevel',
        'L.NamaLevel',
        'L.NamaLevelEN',

        // Group
        'TM.IDGroup',
        'TG.GroupName',

        // Teams
        'TM.TeamA',
        'TeamA.TeamName AS TeamAName',

        'TM.TeamB',
        'TeamB.TeamName AS TeamBName',

        'TM.WinnerTeam',
        'Winner.TeamName AS WinnerTeamName'
    ];

    public function __construct($conn)
    {
        $this->conn = $conn;
        $this->activityController = new ActivityController($this->conn);
        $this->userAksesController = new UserAksesController($this->conn);
    }

    public function select($post)
    {
        $listExclude = [];

        $listSearch = [
            'T.NamaTournament',
            'K.NamaKategori',
            'L.NamaLevel',
            'TG.GroupName',
            'TeamA.TeamName',
            'TeamB.TeamName',
            'Winner.TeamName'
        ];

        return runSelect(
            $this->conn,
            $this->table,
            $this->pk,
            $this->displayedColumn,
            $post,
            $listExclude,
            $listSearch
        );
    }

    public function getSummary($post)
    {
        try {

            $IDTournament = intval($post['IDTournament'] ?? 0);
            $IDTCL = intval($post['IDTCL'] ?? 0);
            $IDMatch = intval($post['IDMatch'] ?? 0); // <-- NEW

            if ($IDTournament <= 0)
                throw new Exception("IDTournament tidak valid.");

            // ===============================
            // TOURNAMENT INFO
            // ===============================
            $stmt = $this->conn->prepare("
            SELECT IDTournament, NamaTournament, TournamentFormat, StatusTournament,
                   PointWin, PointLose, PointDraw,
                   ReportGeneratedName, ReportGeneratedBy, ReportGeneratedAt
            FROM tournament
            WHERE IDTournament = ?
        ");
            $stmt->bind_param("i", $IDTournament);
            $stmt->execute();
            $t = $stmt->get_result()->fetch_assoc();

            $format = $t['TournamentFormat'] ?? 'round_robin';

            $tournamentInfo = [
                'IDTournament' => $IDTournament,
                'NamaTournament' => $t['NamaTournament'] ?? '',
                'TournamentFormat' => $format,
                'StatusTournament' => $t['StatusTournament'] ?? '',
                'PointWin' => intval($t['PointWin'] ?? 3),
                'PointLose' => intval($t['PointLose'] ?? 0),
                'PointDraw' => intval($t['PointDraw'] ?? 1),
                'ReportGeneratedName' => $t['ReportGeneratedName'] ?? null,
                'ReportGeneratedPath' => isset($t['ReportGeneratedName']) ? ROOT_DOMAIN . "admin/attachments/tournament/$IDTournament/" . $t['ReportGeneratedName'] : null,
                'ReportGeneratedBy' => $t['ReportGeneratedBy'] ?? null,
                'ReportGeneratedAt' => $t['ReportGeneratedAt'] ?? null
            ];

            // ===============================
            // GROUPS
            // ===============================
            $stmtG = $this->conn->prepare("
            SELECT IDGroup, GroupName, IDTCL
            FROM tournament_group
            WHERE IDTournament = ?
            ORDER BY IDGroup ASC
        ");
            $stmtG->bind_param("i", $IDTournament);
            $stmtG->execute();
            $groups = $stmtG->get_result()->fetch_all(MYSQLI_ASSOC);

            // ===============================
            // TCL
            // ===============================
            $stmtTCL = $this->conn->prepare("
            SELECT tcl.IDTCL, tcl.IDTournament, tcl.IDKategori, tcl.IDLevel,
                   tcl.MaxSlot, tcl.RegistrationFee,
                   k.NamaKategori, k.TipeKategori,
                   l.NamaLevel, l.NamaLevelEN
            FROM tournament_category_level tcl
            LEFT JOIN kategori k ON tcl.IDKategori = k.IDKategori
            LEFT JOIN level l ON tcl.IDLevel = l.IDLevel
            WHERE tcl.IDTournament = ?
            ORDER BY tcl.IDTCL ASC
        ");
            $stmtTCL->bind_param("i", $IDTournament);
            $stmtTCL->execute();
            $listTCL = $stmtTCL->get_result()->fetch_all(MYSQLI_ASSOC);

            // ===============================
            // MATCHES (WITH IDMatch FILTER)
            // ===============================
            $sqlM = "
            SELECT TM.IDMatch, TM.IDGroup,
                   TM.TeamA, TeamA.TeamName AS TeamAName,
                   TM.TeamB, TeamB.TeamName AS TeamBName,
                   TM.WinnerTeam, Winner.TeamName AS WinnerTeamName,
                   TM.RoundNumber, TM.MatchNumber,
                   TM.MatchDate, TM.Status, TM.IDTCL
            FROM tournament_match TM
            LEFT JOIN team TeamA ON TM.TeamA = TeamA.IDTeam
            LEFT JOIN team TeamB ON TM.TeamB = TeamB.IDTeam
            LEFT JOIN team Winner ON TM.WinnerTeam = Winner.IDTeam
            WHERE TM.IDTournament = ?
        ";

            $params = [$IDTournament];

            if ($IDMatch > 0) {
                $sqlM .= " AND TM.IDMatch = ?";
                $params[] = $IDMatch;
            } else {
                if ($IDTCL > 0) {
                    $sqlM .= " AND TM.IDTCL = ?";
                    $params[] = $IDTCL;
                }
            }

            $sqlM .= " ORDER BY TM.RoundNumber ASC, TM.MatchNumber ASC";

            $stmtM = $this->conn->prepare($sqlM);
            $types = str_repeat('i', count($params));
            $stmtM->bind_param($types, ...$params);
            $stmtM->execute();
            $matches = $stmtM->get_result()->fetch_all(MYSQLI_ASSOC);

            // ===============================
            // MATCH SETS (FILTER BY IDMatch IF EXISTS)
            // ===============================
            $sets = [];

            if ($IDMatch > 0) {

                $stmtS = $this->conn->prepare("
                SELECT IDSet, IDMatch, SetNumber,
                       ScoreTeamA, ScoreTeamB
                FROM tournament_match_set
                WHERE IDMatch = ?
                ORDER BY SetNumber
            ");
                $stmtS->bind_param("i", $IDMatch);
                $stmtS->execute();
                $sets = $stmtS->get_result()->fetch_all(MYSQLI_ASSOC);

            } else {

                $matchIds = array_column($matches, 'IDMatch');

                if (!empty($matchIds)) {
                    $in = implode(',', array_map('intval', $matchIds));

                    $sqlS = "
                    SELECT IDSet, IDMatch, SetNumber,
                           ScoreTeamA, ScoreTeamB
                    FROM tournament_match_set
                    WHERE IDMatch IN ($in)
                    ORDER BY IDMatch, SetNumber
                ";

                    $resS = $this->conn->query($sqlS);
                    while ($r = $resS->fetch_assoc())
                        $sets[] = $r;
                }
            }

            // ===============================
            // GROUP TEAMS
            // ===============================
            $groupTeams = [];

            foreach ($groups as $g) {
                $stmtT = $this->conn->prepare("
                SELECT tgt.IDGroup,
                       tgt.IDRegistration AS IDTeam,
                       COALESCE(t.TeamName, '') AS TeamName
                FROM tournament_group_team tgt
                INNER JOIN tournament_registration tr ON tgt.IDRegistration = tr.IDRegistration
                LEFT JOIN team t ON tr.IDTeam = t.IDTeam
                WHERE tgt.IDGroup = ?
                ORDER BY tgt.IDRegistration ASC
            ");
                $stmtT->bind_param("i", $g['IDGroup']);
                $stmtT->execute();
                $rows = $stmtT->get_result()->fetch_all(MYSQLI_ASSOC);
                foreach ($rows as $r)
                    $groupTeams[] = $r;
            }

            // ===============================
            // STANDINGS (if any)
            // ===============================
            $standings = [];
            $stmtStand = $this->conn->prepare("SELECT IDStanding, IDTournament, IDGroup, IDTeam, Played, Win, Lose, Draw, Points, ScoreFor, ScoreAgainst FROM tournament_standing WHERE IDTournament = ? ORDER BY IDGroup, Points DESC, Win DESC");
            if ($stmtStand) {
                $stmtStand->bind_param("i", $IDTournament);
                $stmtStand->execute();
                $standings = $stmtStand->get_result()->fetch_all(MYSQLI_ASSOC);
            }

            return [
                'Status' => 1,
                'Keterangan' => 'OK',
                'TournamentInfo' => $tournamentInfo,
                'TournamentFormat' => $format,
                'listTCL' => $listTCL,
                'listMatches' => $matches,
                'listMatchSets' => $sets,
                'listGroups' => $groups,
                'listGroupTeams' => $groupTeams,
                'listStanding' => $standings
            ];

        } catch (Exception $e) {
            return [
                'Status' => 0,
                'Keterangan' => $e->getMessage()
            ];
        }
    }

    public function saveMatchesBulk($post)
    {
        try {
            $IDTournament = intval($post['IDTournament'] ?? 0);
            $IDGroup = intval($post['IDGroup'] ?? 0);
            $IDUserLogin = $post['IDUserLogin'] ?? 0;

            if ($IDTournament <= 0)
                throw new Exception("IDTournament tidak valid.");

            $matches = json_decode($post['matches'] ?? '[]', true);
            if (!is_array($matches))
                throw new Exception('Invalid matches payload');

            // basic akses check (modify)
            $AksesNeeded = $this->sv . ".Modify";
            $userAksesFilterParam = [["IDUser", $IDUserLogin, 1]];
            $userAksesselectParam = [
                "filters" => $userAksesFilterParam,
                "rows_per_page" => 1,
                "page_number" => 1,
                "no_count" => true
            ];

            $akses = $this->userAksesController->select($userAksesselectParam);
            if ($akses['Status'] != 1 || empty($akses['list_data']))
                throw new Exception("Akses tidak valid.");

            $listAkses = array_column($akses['list_data'], 'AksesName');
            if (!in_array($AksesNeeded, $listAkses))
                throw new Exception("User tidak memiliki otoritas akses - [$AksesNeeded]");

            $UserLogin = $akses['Username'];

            return runWithTransaction($this->conn, function ($conn) use ($matches, $IDTournament, $IDGroup, $UserLogin) {

                // ensure tournament ongoing and editable
                $stmtT = $conn->prepare("SELECT StatusTournament, TournamentFormat FROM tournament WHERE IDTournament = ? LIMIT 1");
                $stmtT->bind_param("i", $IDTournament);
                $stmtT->execute();
                $t = $stmtT->get_result()->fetch_assoc();
                if (!$t)
                    throw new Exception("Tournament tidak ditemukan.");
                if ($t['StatusTournament'] !== 'ongoing')
                    throw new Exception("Match hanya dapat diubah saat tournament ongoing.");

                // validate duplicates per round
                $roundMap = [];
                foreach ($matches as $m) {
                    $IDMatch = intval($m['IDMatch'] ?? 0);
                    $TeamA = intval($m['TeamA'] ?? 0);
                    $TeamB = intval($m['TeamB'] ?? 0);
                    $Round = intval($m['RoundNumber'] ?? 0);

                    if ($TeamA <= 0 || $TeamB <= 0)
                        throw new Exception('Team invalid');
                    if ($TeamA == $TeamB)
                        throw new Exception('TeamA dan TeamB tidak boleh sama.');

                    $roundMap[$Round]['teams'][$TeamA] = ($roundMap[$Round]['teams'][$TeamA] ?? 0) + 1;
                    $roundMap[$Round]['teams'][$TeamB] = ($roundMap[$Round]['teams'][$TeamB] ?? 0) + 1;
                    $pairKey = ($TeamA < $TeamB) ? "$TeamA:$TeamB" : "$TeamB:$TeamA";
                    $roundMap[$Round]['pairs'][$pairKey] = ($roundMap[$Round]['pairs'][$pairKey] ?? 0) + 1;
                }

                foreach ($roundMap as $rn => $info) {
                    foreach ($info['teams'] as $tid => $cnt) {
                        if ($cnt > 1)
                            throw new Exception("Satu team tampil lebih dari sekali pada ronde $rn");
                    }
                    foreach ($info['pairs'] as $pk => $cnt) {
                        if ($cnt > 1)
                            throw new Exception("Pertandingan duplikat pada ronde $rn");
                    }
                }

                // update matches
                $stmtCheck = $conn->prepare("
                    SELECT IDMatch, Status 
                    FROM tournament_match 
                    WHERE IDMatch = ? 
                    AND IDTournament = ?
                    AND IDGroup = ?
                    LIMIT 1
                ");

                $stmtUpdate = $conn->prepare("
                UPDATE tournament_match 
                SET TeamA = ?, 
                    TeamB = ?, 
                    MatchDate = ?, 
                    LastUpdateBy = ?, 
                    LastUpdateAt = NOW() 
                WHERE IDMatch = ?
            ");

                foreach ($matches as $m) {

                    $IDMatch = intval($m['IDMatch'] ?? 0);
                    $TeamA = intval($m['TeamA'] ?? 0);
                    $TeamB = intval($m['TeamB'] ?? 0);
                    $MatchDateRaw = $m['MatchDate'] ?? null;
                    $MatchDate = null;

                    if (!empty($MatchDateRaw)) {
                        $dt = date_create($MatchDateRaw);
                        if ($dt && date_format($dt, 'Y-m-d H:i:s') !== false) {
                            $MatchDate = date_format($dt, 'Y-m-d H:i:s');
                        }
                    }

                    if ($IDMatch <= 0)
                        throw new Exception("IDMatch tidak valid");
                    if ($TeamA <= 0 || $TeamB <= 0)
                        throw new Exception("Team invalid");
                    if ($TeamA == $TeamB)
                        throw new Exception("TeamA dan TeamB tidak boleh sama.");

                    // check match exists & pending
                    $stmtCheck->bind_param("iii", $IDMatch, $IDTournament, $IDGroup);
                    $stmtCheck->execute();
                    $ex = $stmtCheck->get_result()->fetch_assoc();

                    if (!$ex)
                        throw new Exception("Match ID $IDMatch tidak ditemukan.");
                    if ($ex['Status'] !== 'pending')
                        throw new Exception("Hanya match pending yang boleh dimodify.");

                    $stmtUpdate->bind_param("iissi", $TeamA, $TeamB, $MatchDate, $UserLogin, $IDMatch);
                    $stmtUpdate->execute();

                    if ($stmtUpdate->affected_rows < 0) {
                        throw new Exception("Gagal update match ID $IDMatch");
                    }
                }

                return ["Status" => 1, "Keterangan" => "Matches updated"];
            });

        } catch (Exception $e) {
            return ["Status" => 0, "Keterangan" => $e->getMessage()];
        }
    }

    public function generate_match($post)
    {
        try {

            $IDTournament = intval($post['IDTournament'] ?? 0);
            $IDUserLogin = $post['IDUserLogin'] ?? 0;

            if ($IDTournament <= 0)
                throw new Exception("IDTournament tidak valid.");

            // =====================================
            // VALIDASI AKSES
            // =====================================
            $AksesNeeded = $this->sv . ".GenerateAutomatic";
            $userAksesFilterParam = [["IDUser", $IDUserLogin, 1]];
            $userAksesselectParam = [
                "filters" => $userAksesFilterParam,
                "rows_per_page" => 1,
                "page_number" => 1,
                "no_count" => true
            ];

            $akses = $this->userAksesController->select($userAksesselectParam);

            if ($akses['Status'] != 1 || empty($akses['list_data']))
                throw new Exception("Akses tidak valid.");

            $listAkses = array_column($akses['list_data'], 'AksesName');
            if (!in_array($AksesNeeded, $listAkses))
                throw new Exception("User tidak memiliki otoritas akses - [$AksesNeeded]");

            $UserLogin = $akses['Username']; // <-- ambil username untuk audit trail

            return runWithTransaction($this->conn, function ($conn) use ($IDTournament, $UserLogin) {

                // =====================================
                // CEK STATUS & FORMAT
                // =====================================
                $stmt = $conn->prepare("
                SELECT StatusTournament, TournamentFormat
                FROM tournament
                WHERE IDTournament = ?
            ");
                $stmt->bind_param("i", $IDTournament);
                $stmt->execute();
                $t = $stmt->get_result()->fetch_assoc();

                if (!$t)
                    throw new Exception("Tournament tidak ditemukan.");

                if ($t['StatusTournament'] !== 'ongoing')
                    throw new Exception("Match hanya dapat digenerate saat status tournament = ongoing.");

                $format = $t['TournamentFormat'];

                $this->validateTournamentEditable($conn, $IDTournament);

                // =====================================
                // CEK MATCH EXISTING
                // =====================================
                $stmtExisting = $conn->prepare("
                        SELECT 
                            COUNT(*) as total,
                            SUM(CASE WHEN Status <> 'pending' THEN 1 ELSE 0 END) as non_pending
                        FROM tournament_match
                        WHERE IDTournament = ?
                ");
                $stmtExisting->bind_param("i", $IDTournament);
                $stmtExisting->execute();
                $existing = $stmtExisting->get_result()->fetch_assoc();

                if ($existing['total'] > 0) {

                    // Jika ada match yg sudah bukan pending → STOP
                    if ($existing['non_pending'] > 0) {
                        throw new Exception("Tidak bisa generate ulang karena sudah ada match yang berjalan/selesai.");
                    }

                    // Jika semua masih pending → HAPUS SEMUA
                    $stmtDelete = $conn->prepare("
        DELETE FROM tournament_match
        WHERE IDTournament = ?
    ");
                    $stmtDelete->bind_param("i", $IDTournament);
                    $stmtDelete->execute();
                }

                // =====================================
                // AMBIL GROUP
                // =====================================
                $stmtGroup = $conn->prepare("
                SELECT IDGroup, IDTCL
                FROM tournament_group
                WHERE IDTournament = ?
            ");
                $stmtGroup->bind_param("i", $IDTournament);
                $stmtGroup->execute();
                $groups = $stmtGroup->get_result()->fetch_all(MYSQLI_ASSOC);

                if (empty($groups))
                    throw new Exception("Belum ada group.");

                $totalMatchCreated = 0;

                foreach ($groups as $group) {

                    $IDGroup = $group['IDGroup'];
                    $IDTCL = $group['IDTCL'];

                    // Cek sudah ada match?
                    $stmtCheck = $conn->prepare("
                    SELECT 1 FROM tournament_match
                    WHERE IDGroup = ?
                    LIMIT 1
                ");
                    $stmtCheck->bind_param("i", $IDGroup);
                    $stmtCheck->execute();
                    if ($stmtCheck->get_result()->num_rows > 0)
                        continue;

                    // Ambil team
                    $stmtTeam = $conn->prepare("
                    SELECT IDRegistration
                    FROM tournament_group_team
                    WHERE IDGroup = ?
                ");
                    $stmtTeam->bind_param("i", $IDGroup);
                    $stmtTeam->execute();
                    $teams = $stmtTeam->get_result()->fetch_all(MYSQLI_ASSOC);

                    if (count($teams) < 2)
                        continue;

                    $teamIDs = array_column($teams, 'IDRegistration');

                    // =========================
                    // SINGLE ELIMINATION
                    // =========================
                    if ($format == 'single_elimination') {

                        $count = count($teamIDs);

                        // =========================
                        // VALIDASI POWER OF TWO
                        // =========================
                        if ($count < 2) {
                            throw new Exception("Minimal 2 tim untuk single elimination.");
                        }

                        // cek power of 2
                        // contoh valid: 2,4,8,16,32...
                        if (($count & ($count - 1)) !== 0) {
                            throw new Exception(
                                "Jumlah tim pada single elimination harus 2, 4, 8, 16, dst. " .
                                "Jumlah saat ini: $count tim."
                            );
                        }

                        $roundNumber = 1;

                        for ($i = 0; $i < $count; $i += 2) {

                            $matchNumber = ($i / 2) + 1;

                            $stmtInsert = $conn->prepare("
                            INSERT INTO tournament_match
                            (IDTournament, IDTCL, IDGroup,
                            RoundNumber, MatchNumber,
                            TeamA, TeamB, Status,
                            CreateBy, CreateAt,
                            LastUpdateBy, LastUpdateAt)
                            VALUES (?, ?, ?, ?, ?, ?, ?, 'pending',
                                    ?, NOW(),
                                    ?, NOW())
                        ");

                            $stmtInsert->bind_param(
                                "iiiiiiiss",
                                $IDTournament,
                                $IDTCL,
                                $IDGroup,
                                $roundNumber,
                                $matchNumber,
                                $teamIDs[$i],
                                $teamIDs[$i + 1],
                                $UserLogin,
                                $UserLogin
                            );

                            $stmtInsert->execute();
                            $totalMatchCreated++;
                        }

                    }
                    // =========================
                    // ROUND ROBIN (CIRCLE METHOD)
                    // =========================
                    else {

                        $teamIDs = array_values($teamIDs);
                        $count = count($teamIDs);

                        // Jika ganjil, tambahkan dummy (BYE)
                        $isOdd = false;
                        if ($count % 2 != 0) {
                            $teamIDs[] = null;
                            $count++;
                            $isOdd = true;
                        }

                        $totalRounds = $count - 1;
                        $half = $count / 2;

                        $teams = $teamIDs;

                        for ($round = 1; $round <= $totalRounds; $round++) {

                            $matchNumber = 1;

                            for ($i = 0; $i < $half; $i++) {

                                $teamA = $teams[$i];
                                $teamB = $teams[$count - 1 - $i];

                                if ($teamA !== null && $teamB !== null) {

                                    $stmtInsert = $conn->prepare("
                    INSERT INTO tournament_match
                    (IDTournament, IDTCL, IDGroup,
                     RoundNumber, MatchNumber,
                     TeamA, TeamB, Status,
                     CreateBy, CreateAt,
                     LastUpdateBy, LastUpdateAt)
                    VALUES (?, ?, ?, ?, ?, ?, ?, 'pending',
                            ?, NOW(),
                            ?, NOW())
                ");

                                    $stmtInsert->bind_param(
                                        "iiiiiiiss",
                                        $IDTournament,
                                        $IDTCL,
                                        $IDGroup,
                                        $round,
                                        $matchNumber,
                                        $teamA,
                                        $teamB,
                                        $UserLogin,
                                        $UserLogin
                                    );

                                    $stmtInsert->execute();
                                    $totalMatchCreated++;
                                    $matchNumber++;
                                }
                            }

                            // ROTASI (kecuali index 0)
                            $fixed = $teams[0];
                            $rotate = array_slice($teams, 1);

                            array_unshift($rotate, array_pop($rotate));

                            $teams = array_merge([$fixed], $rotate);
                        }
                    }
                }

                if ($totalMatchCreated == 0)
                    throw new Exception("Tidak ada match yang bisa digenerate.");

                return [
                    "Status" => 1,
                    "Keterangan" => "Berhasil generate $totalMatchCreated match."
                ];
            });

        } catch (Exception $e) {
            return [
                "Status" => 0,
                "Keterangan" => $e->getMessage()
            ];
        }
    }



    public function clear_matches($post)
    {
        try {

            $IDTournament = intval($post['IDTournament'] ?? 0);
            $IDUserLogin = intval($post['IDUserLogin'] ?? 0);
            $IDGroup = intval($post['IDGroup'] ?? 0);

            if ($IDTournament <= 0)
                throw new Exception("IDTournament tidak valid.");

            // ================================
            // VALIDASI AKSES
            // ================================
            $AksesNeeded = $this->sv . ".ClearMatches";

            $akses = $this->userAksesController->select([
                "filters" => [["IDUser", $IDUserLogin, 1]],
                "rows_per_page" => 1,
                "page_number" => 1,
                "no_count" => true
            ]);

            if ($akses['Status'] != 1 || empty($akses['list_data']))
                throw new Exception("Akses tidak valid.");

            $listAkses = array_column($akses['list_data'], 'AksesName');
            if (!in_array($AksesNeeded, $listAkses))
                throw new Exception("User tidak memiliki otoritas akses - [$AksesNeeded]");

            return runWithTransaction($this->conn, function ($conn) use ($IDTournament, $IDGroup) {

                $groupSql = $IDGroup > 0 ? " AND IDGroup = ?" : "";

                // ================================
                // CEK STATUS & FORMAT
                // ================================
                $stmt = $conn->prepare("
                SELECT StatusTournament, TournamentFormat
                FROM tournament
                WHERE IDTournament = ?
            ");
                $stmt->bind_param("i", $IDTournament);
                $stmt->execute();
                $t = $stmt->get_result()->fetch_assoc();

                if (!$t)
                    throw new Exception("Tournament tidak ditemukan.");

                if (in_array($t['StatusTournament'], ['finish', 'canceled']))
                    throw new Exception("Tournament sudah selesai / dibatalkan.");

                $format = $t['TournamentFormat'];

                // ================================
                // VALIDASI MATCH
                // ================================
                if ($IDGroup > 0) {
                    $stmtMatch = $conn->prepare("
                    SELECT Status, RoundNumber
                    FROM tournament_match
                    WHERE IDTournament = ? AND IDGroup = ?
                ");
                    $stmtMatch->bind_param("ii", $IDTournament, $IDGroup);
                } else {
                    $stmtMatch = $conn->prepare("
                    SELECT Status, RoundNumber
                    FROM tournament_match
                    WHERE IDTournament = ?
                ");
                    $stmtMatch->bind_param("i", $IDTournament);
                }

                $stmtMatch->execute();
                $res = $stmtMatch->get_result();

                $maxRound = 0;

                while ($row = $res->fetch_assoc()) {

                    if ($row['Status'] !== 'pending')
                        throw new Exception("Masih ada match yang sudah berjalan/selesai.");

                    $maxRound = max($maxRound, intval($row['RoundNumber']));
                }

                if ($format == 'single_elimination') {
                    if ($maxRound > 1)
                        throw new Exception("Bracket sudah masuk ronde berikutnya, tidak bisa dihapus.");
                }

                // ================================
                // DELETE SETS
                // ================================
                if ($IDGroup > 0) {
                    $stmtDelSet = $conn->prepare("
                    DELETE ms FROM tournament_match_set ms
                    JOIN tournament_match m ON m.IDMatch = ms.IDMatch
                    WHERE m.IDTournament = ? AND m.IDGroup = ?
                ");
                    $stmtDelSet->bind_param("ii", $IDTournament, $IDGroup);
                } else {
                    $stmtDelSet = $conn->prepare("
                    DELETE ms FROM tournament_match_set ms
                    JOIN tournament_match m ON m.IDMatch = ms.IDMatch
                    WHERE m.IDTournament = ?
                ");
                    $stmtDelSet->bind_param("i", $IDTournament);
                }

                $stmtDelSet->execute();

                // ================================
                // DELETE MATCH
                // ================================
                if ($IDGroup > 0) {
                    $stmtDel = $conn->prepare("
                    DELETE FROM tournament_match
                    WHERE IDTournament = ? AND IDGroup = ?
                ");
                    $stmtDel->bind_param("ii", $IDTournament, $IDGroup);
                } else {
                    $stmtDel = $conn->prepare("
                    DELETE FROM tournament_match
                    WHERE IDTournament = ?
                ");
                    $stmtDel->bind_param("i", $IDTournament);
                }

                $stmtDel->execute();

                return [
                    "Status" => 1,
                    "Keterangan" => "Match berhasil dihapus."
                ];
            });

        } catch (Exception $e) {
            return [
                "Status" => 0,
                "Keterangan" => $e->getMessage()
            ];
        }
    }
    public function clear_all_scores($post)
    {
        try {

            $IDTournament = intval($post['IDTournament'] ?? 0);
            $IDUserLogin = intval($post['IDUserLogin'] ?? 0);
            $IDGroup = intval($post['IDGroup'] ?? 0);

            if ($IDTournament <= 0)
                throw new Exception("IDTournament tidak valid.");

            // ================================
            // VALIDASI AKSES
            // ================================
            $AksesNeeded = $this->sv . ".ClearMatches";

            $akses = $this->userAksesController->select([
                "filters" => [["IDUser", $IDUserLogin, 1]],
                "rows_per_page" => 1,
                "page_number" => 1,
                "no_count" => true
            ]);

            if ($akses['Status'] != 1 || empty($akses['list_data']))
                throw new Exception("Akses tidak valid.");

            $listAkses = array_column($akses['list_data'], 'AksesName');
            if (!in_array($AksesNeeded, $listAkses))
                throw new Exception("User tidak memiliki otoritas akses - [$AksesNeeded]");

            return runWithTransaction($this->conn, function ($conn) use ($IDTournament, $IDGroup) {

                // ================================
                // CEK STATUS & FORMAT
                // ================================
                $stmt = $conn->prepare("
                SELECT StatusTournament, TournamentFormat
                FROM tournament
                WHERE IDTournament = ?
            ");
                $stmt->bind_param("i", $IDTournament);
                $stmt->execute();
                $t = $stmt->get_result()->fetch_assoc();

                if (!$t)
                    throw new Exception("Tournament tidak ditemukan.");

                if (in_array($t['StatusTournament'], ['finish', 'canceled']))
                    throw new Exception("Tournament sudah selesai / dibatalkan.");

                $format = $t['TournamentFormat'];

                // ================================
                // VALIDASI SINGLE ELIMINATION
                // ================================
                if ($format == 'single_elimination') {

                    if ($IDGroup > 0) {
                        $stmtMatch = $conn->prepare("
                        SELECT Status, RoundNumber
                        FROM tournament_match
                        WHERE IDTournament = ? AND IDGroup = ?
                    ");
                        $stmtMatch->bind_param("ii", $IDTournament, $IDGroup);
                    } else {
                        $stmtMatch = $conn->prepare("
                        SELECT Status, RoundNumber
                        FROM tournament_match
                        WHERE IDTournament = ?
                    ");
                        $stmtMatch->bind_param("i", $IDTournament);
                    }

                    $stmtMatch->execute();
                    $res = $stmtMatch->get_result();

                    $maxRound = 0;

                    while ($row = $res->fetch_assoc()) {

                        if ($row['Status'] !== 'pending')
                            throw new Exception("Masih ada match yang sudah berjalan/selesai.");

                        $maxRound = max($maxRound, intval($row['RoundNumber']));
                    }

                    if ($maxRound > 1)
                        throw new Exception("Bracket sudah masuk ronde berikutnya, tidak bisa reset score.");
                }

                // ================================
                // DELETE ALL SETS
                // ================================
                if ($IDGroup > 0) {
                    $stmtDelSet = $conn->prepare("
                    DELETE ms FROM tournament_match_set ms
                    JOIN tournament_match m ON m.IDMatch = ms.IDMatch
                    WHERE m.IDTournament = ? AND m.IDGroup = ?
                ");
                    $stmtDelSet->bind_param("ii", $IDTournament, $IDGroup);
                } else {
                    $stmtDelSet = $conn->prepare("
                    DELETE ms FROM tournament_match_set ms
                    JOIN tournament_match m ON m.IDMatch = ms.IDMatch
                    WHERE m.IDTournament = ?
                ");
                    $stmtDelSet->bind_param("i", $IDTournament);
                }

                $stmtDelSet->execute();

                // ================================
                // RESET MATCH
                // ================================
                if ($IDGroup > 0) {
                    $stmtReset = $conn->prepare("
                    UPDATE tournament_match
                    SET Status = 'pending',
                        WinnerTeam = NULL
                    WHERE IDTournament = ? AND IDGroup = ?
                ");
                    $stmtReset->bind_param("ii", $IDTournament, $IDGroup);
                } else {
                    $stmtReset = $conn->prepare("
                    UPDATE tournament_match
                    SET Status = 'pending',
                        WinnerTeam = NULL
                    WHERE IDTournament = ?
                ");
                    $stmtReset->bind_param("i", $IDTournament);
                }

                $stmtReset->execute();

                return [
                    "Status" => 1,
                    "Keterangan" => "Score berhasil direset."
                ];
            });

        } catch (Exception $e) {
            return [
                "Status" => 0,
                "Keterangan" => $e->getMessage()
            ];
        }
    }

    public function update($post)
    {
        try {

            $IDMatch = intval($post['IDMatch'] ?? 0);
            $IDTournament = intval($post['IDTournament'] ?? 0);
            $IDUserLogin = $post['IDUserLogin'] ?? 0;

            if ($IDTournament <= 0)
                throw new Exception("IDTournament tidak valid.");

            $isInsert = ($IDMatch < 1);

            // ======================================
            // CEK AKSES
            // ======================================
            $AksesNeeded = $this->sv . ($isInsert ? ".Insert" : ".Modify");

            $userAksesFilterParam = [["IDUser", $IDUserLogin, 1]];
            $userAksesselectParam = [
                "filters" => $userAksesFilterParam,
                "rows_per_page" => 1,
                "page_number" => 1,
                "no_count" => true
            ];

            $akses = $this->userAksesController->select($userAksesselectParam);

            if ($akses['Status'] != 1 || empty($akses['list_data']))
                throw new Exception("Akses tidak valid.");

            $listAkses = array_column($akses['list_data'], 'AksesName');
            if (!in_array($AksesNeeded, $listAkses))
                throw new Exception("User tidak memiliki otoritas akses - [$AksesNeeded]");

            $UserLogin = $akses['Username'];

            return runWithTransaction($this->conn, function ($conn) use ($post, $IDMatch, $IDTournament, $UserLogin, $isInsert) {

                // ======================================
                // VALIDASI STATUS TOURNAMENT
                // ======================================
                $stmtT = $conn->prepare("
                SELECT StatusTournament
                FROM tournament
                WHERE IDTournament = ?
            ");
                $stmtT->bind_param("i", $IDTournament);
                $stmtT->execute();
                $t = $stmtT->get_result()->fetch_assoc();

                if (!$t)
                    throw new Exception("Tournament tidak ditemukan.");

                if ($t['StatusTournament'] !== 'ongoing')
                    throw new Exception("Match hanya dapat diubah saat tournament ongoing.");

                // ======================================
                // VALIDASI ROUND > 1
                // ======================================
                $this->validateTournamentEditable($conn, $IDTournament);



                $TeamA = intval($post['TeamA']);
                $TeamB = intval($post['TeamB']);
                $RoundNumber = intval($post['RoundNumber']);
                $IDGroup = intval($post['IDGroup']);

                if ($TeamA == $TeamB)
                    throw new Exception("TeamA dan TeamB tidak boleh sama.");

                // ======================================
                // VALIDASI DUPLIKASI VS
                // ======================================
                $stmtDup = $conn->prepare("
                SELECT 1
                FROM tournament_match
                WHERE IDTournament = ?
                AND IDGroup = ?
                AND RoundNumber = ?
                AND (
                    (TeamA = ? AND TeamB = ?)
                    OR
                    (TeamA = ? AND TeamB = ?)
                )
                " . (!$isInsert ? "AND IDMatch != ?" : "") . "
                LIMIT 1
            ");

                if ($isInsert) {
                    $stmtDup->bind_param(
                        "iiiiiii",
                        $IDTournament,
                        $IDGroup,
                        $RoundNumber,
                        $TeamA,
                        $TeamB,
                        $TeamB,
                        $TeamA
                    );
                } else {
                    $stmtDup->bind_param(
                        "iiiiiiii",
                        $IDTournament,
                        $IDGroup,
                        $RoundNumber,
                        $TeamA,
                        $TeamB,
                        $TeamB,
                        $TeamA,
                        $IDMatch
                    );
                }

                $stmtDup->execute();
                if ($stmtDup->get_result()->num_rows > 0)
                    throw new Exception("Match dengan kombinasi team tersebut sudah ada di ronde ini.");

                // ======================================
                // MODIFY
                // ======================================
                if (!$isInsert) {

                    // pastikan match masih pending
                    $stmtCheck = $conn->prepare("
                    SELECT Status
                    FROM tournament_match
                    WHERE IDMatch = ?
                ");
                    $stmtCheck->bind_param("i", $IDMatch);
                    $stmtCheck->execute();
                    $existing = $stmtCheck->get_result()->fetch_assoc();

                    if (!$existing)
                        throw new Exception("Match tidak ditemukan.");

                    if ($existing['Status'] !== 'pending')
                        throw new Exception("Hanya match pending yang boleh dimodify.");

                    $stmt = $conn->prepare("
                    UPDATE tournament_match
                    SET TeamA = ?, 
                        TeamB = ?,
                        MatchDate = ?,
                        LastUpdateBy = ?,
                        LastUpdateAt = NOW()
                    WHERE IDMatch = ?
                ");

                    $stmt->bind_param(
                        "iissi",
                        $TeamA,
                        $TeamB,
                        $post['MatchDate'],
                        $UserLogin,
                        $IDMatch
                    );

                    $stmt->execute();

                    return [
                        "Status" => 1,
                        "Keterangan" => "Match berhasil diupdate."
                    ];
                }

                // ======================================
                // INSERT
                // ======================================
                $stmt = $conn->prepare("
                INSERT INTO tournament_match
                (IDTournament, IDTCL, IDGroup,
                 RoundNumber, MatchNumber,
                 TeamA, TeamB, Status,
                 MatchDate,
                 CreateBy, CreateAt,
                 LastUpdateBy, LastUpdateAt)
                VALUES (?, ?, ?, ?, ?, ?, ?, 'pending',
                        ?, ?, NOW(),
                        ?, NOW())
            ");

                $stmt->bind_param(
                    "iiiiiisss",
                    $post['IDTournament'],
                    $post['IDTCL'],
                    $post['IDGroup'],
                    $post['RoundNumber'],
                    $post['MatchNumber'],
                    $TeamA,
                    $TeamB,
                    $post['MatchDate'],
                    $UserLogin,
                    $UserLogin
                );

                $stmt->execute();

                return [
                    "Status" => 1,
                    "Keterangan" => "Match manual berhasil ditambahkan."
                ];
            });

        } catch (Exception $e) {
            return [
                "Status" => 0,
                "Keterangan" => $e->getMessage()
            ];
        }
    }

    public function delete($post)
    {
        try {

            $IDMatch = intval($post['IDMatch'] ?? 0);
            $IDUserLogin = $post['IDUserLogin'] ?? 0;

            if ($IDMatch <= 0)
                throw new Exception("IDMatch tidak valid.");

            $AksesNeeded = $this->sv . ".Delete";

            $userAksesFilterParam = [["IDUser", $IDUserLogin, 1]];
            $userAksesselectParam = [
                "filters" => $userAksesFilterParam,
                "rows_per_page" => 1,
                "page_number" => 1,
                "no_count" => true
            ];

            $akses = $this->userAksesController->select($userAksesselectParam);

            if ($akses['Status'] != 1 || empty($akses['list_data']))
                throw new Exception("Akses tidak valid.");

            $listAkses = array_column($akses['list_data'], 'AksesName');
            if (!in_array($AksesNeeded, $listAkses))
                throw new Exception("User tidak memiliki otoritas akses - [$AksesNeeded]");

            return runWithTransaction($this->conn, function ($conn) use ($IDMatch) {

                $stmt = $conn->prepare("
                SELECT IDTournament, Status
                FROM tournament_match
                WHERE IDMatch = ?
            ");
                $stmt->bind_param("i", $IDMatch);
                $stmt->execute();
                $data = $stmt->get_result()->fetch_assoc();

                if (!$data)
                    throw new Exception("Match tidak ditemukan.");

                if ($data['Status'] !== 'pending')
                    throw new Exception("Hanya match pending yang boleh dihapus.");

                // Cek round > 1
                $this->validateTournamentEditable($conn, $data['IDTournament']);

                $stmtDelete = $conn->prepare("
                DELETE FROM tournament_match
                WHERE IDMatch = ?
            ");
                $stmtDelete->bind_param("i", $IDMatch);
                $stmtDelete->execute();

                return [
                    "Status" => 1,
                    "Keterangan" => "Match berhasil dihapus."
                ];
            });

        } catch (Exception $e) {
            return [
                "Status" => 0,
                "Keterangan" => $e->getMessage()
            ];
        }
    }

    public function delete_score($post)
    {
        try {
            $IDMatch = intval($post['IDMatch'] ?? 0);
            $IDUserLogin = $post['IDUserLogin'] ?? 0;

            if ($IDMatch <= 0)
                throw new Exception("IDMatch tidak valid.");

            $AksesNeeded = $this->sv . ".Delete";
            $userAksesFilterParam = [["IDUser", $IDUserLogin, 1]];
            $userAksesselectParam = [
                "filters" => $userAksesFilterParam,
                "rows_per_page" => 1,
                "page_number" => 1,
                "no_count" => true
            ];

            $akses = $this->userAksesController->select($userAksesselectParam);
            if ($akses['Status'] != 1 || empty($akses['list_data']))
                throw new Exception("Akses tidak valid.");
            $listAkses = array_column($akses['list_data'], 'AksesName');
            if (!in_array($AksesNeeded, $listAkses))
                throw new Exception("User tidak memiliki otoritas akses - [$AksesNeeded]");

            $UserLogin = $akses['Username'];

            return runWithTransaction($this->conn, function ($conn) use ($IDMatch, $UserLogin) {
                // fetch match
                $stmt = $conn->prepare("SELECT IDTournament, Status, RoundNumber FROM tournament_match WHERE IDMatch = ? LIMIT 1");
                $stmt->bind_param("i", $IDMatch);
                $stmt->execute();
                $m = $stmt->get_result()->fetch_assoc();
                if (!$m)
                    throw new Exception("Match tidak ditemukan.");

                $IDTournament = intval($m['IDTournament']);

                // ensure tournament not finished/cancelled
                $stmtT = $conn->prepare("SELECT StatusTournament, TournamentFormat FROM tournament WHERE IDTournament = ? LIMIT 1");
                $stmtT->bind_param("i", $IDTournament);
                $stmtT->execute();
                $t = $stmtT->get_result()->fetch_assoc();
                if (!$t)
                    throw new Exception("Tournament tidak ditemukan.");
                $tStatus = strtolower($t['StatusTournament'] ?? '');
                if (in_array($tStatus, ['finished', 'cancelled']))
                    throw new Exception("Tidak boleh menghapus score karena tournament sudah selesai/ditangguhkan.");

                // if single_elimination, ensure there is no later round existing (cannot delete if later rounds created)
                $format = strtolower($t['TournamentFormat'] ?? 'round_robin');
                if ($format === 'single_elimination') {
                    $currentRound = intval($m['RoundNumber'] ?? 0);
                    $stmtR = $conn->prepare("SELECT 1 FROM tournament_match WHERE IDTournament = ? AND RoundNumber > ? LIMIT 1");
                    $stmtR->bind_param("ii", $IDTournament, $currentRound);
                    $stmtR->execute();
                    if ($stmtR->get_result()->num_rows > 0)
                        throw new Exception("Tidak bisa menghapus score karena sudah ada ronde selanjutnya pada tournament single elimination.");
                }

                // delete sets for the match
                $stmtDel = $conn->prepare("DELETE FROM tournament_match_set WHERE IDMatch = ?");
                $stmtDel->bind_param("i", $IDMatch);
                $stmtDel->execute();

                // reset match status to pending and clear winner
                $stmtUpd = $conn->prepare("UPDATE tournament_match SET Status = 'pending', WinnerTeam = NULL, LastUpdateBy = ?, LastUpdateAt = NOW() WHERE IDMatch = ?");
                $stmtUpd->bind_param("si", $UserLogin, $IDMatch);
                $stmtUpd->execute();

                return ["Status" => 1, "Keterangan" => "Score berhasil dihapus dan match direset ke pending."];
            });

        } catch (Exception $e) {
            return ["Status" => 0, "Keterangan" => $e->getMessage()];
        }
    }

    private function validateTournamentEditable($conn, $IDTournament)
    {
        // Ambil format tournament
        $stmtFormat = $conn->prepare("
        SELECT TournamentFormat
        FROM tournament
        WHERE IDTournament = ?
        LIMIT 1
    ");
        $stmtFormat->bind_param("i", $IDTournament);
        $stmtFormat->execute();
        $formatRow = $stmtFormat->get_result()->fetch_assoc();

        if (!$formatRow) {
            throw new Exception("Tournament tidak ditemukan.");
        }

        $format = $formatRow['TournamentFormat'];

        // ==========================================
        // SINGLE ELIMINATION
        // ==========================================
        if ($format === 'single_elimination') {

            $stmt = $conn->prepare("
            SELECT 1 
            FROM tournament_match
            WHERE IDTournament = ?
            AND RoundNumber > 1
            LIMIT 1
        ");
            $stmt->bind_param("i", $IDTournament);
            $stmt->execute();

            if ($stmt->get_result()->num_rows > 0) {
                throw new Exception("Ronde pertama sudah selesai. Tidak bisa modify/generate match.");
            }
        }

        // ==========================================
        // ROUND ROBIN
        // ==========================================
        else if ($format === 'round_robin') {

            $stmt = $conn->prepare("
            SELECT 1
            FROM tournament_match
            WHERE IDTournament = ?
            AND Status <> 'pending'
            LIMIT 1
        ");
            $stmt->bind_param("i", $IDTournament);
            $stmt->execute();

            if ($stmt->get_result()->num_rows > 0) {
                throw new Exception("Sudah ada match yang berjalan/selesai. Tidak bisa modify/generate ulang.");
            }
        }
    }

    public function generateNextRound($post)
    {
        try {

            $IDTournament = intval($post['IDTournament'] ?? 0);
            $IDGroup = intval($post['IDGroup'] ?? 0);
            $RoundNumber = intval($post['RoundNumber'] ?? 0);

            if ($IDTournament <= 0)
                throw new Exception("IDTournament tidak valid.");

            if ($IDGroup <= 0)
                throw new Exception("IDGroup tidak valid.");

            if ($RoundNumber <= 0)
                throw new Exception("RoundNumber tidak valid.");

            return runWithTransaction($this->conn, function ($conn) use ($IDTournament, $IDGroup, $RoundNumber) {

                // =============================
                // CEK TOURNAMENT
                // =============================
                $stmtT = $conn->prepare("
                SELECT StatusTournament, TournamentFormat
                FROM tournament
                WHERE IDTournament = ?
                LIMIT 1
            ");
                $stmtT->bind_param("i", $IDTournament);
                $stmtT->execute();
                $t = $stmtT->get_result()->fetch_assoc();

                if (!$t)
                    throw new Exception("Tournament tidak ditemukan.");

                if ($t['StatusTournament'] !== 'ongoing')
                    throw new Exception("Next round hanya saat tournament ongoing.");

                if ($t['TournamentFormat'] !== 'single_elimination')
                    throw new Exception("Next round hanya untuk single elimination.");

                // =============================
                // CEK ADA ROUND BERIKUTNYA DI GROUP INI?
                // =============================
                $stmtNext = $conn->prepare("
                SELECT 1 FROM tournament_match
                WHERE IDTournament = ?
                AND IDGroup = ?
                AND RoundNumber > ?
                LIMIT 1
            ");
                $stmtNext->bind_param("iii", $IDTournament, $IDGroup, $RoundNumber);
                $stmtNext->execute();

                if ($stmtNext->get_result()->num_rows > 0)
                    throw new Exception("Round selanjutnya sudah ada pada group ini.");

                // =============================
                // AMBIL MATCH CURRENT ROUND (GROUP INI SAJA)
                // =============================
                $stmtMatch = $conn->prepare("
                SELECT IDMatch, WinnerTeam, Status, IDTCL
                FROM tournament_match
                WHERE IDTournament = ?
                AND IDGroup = ?
                AND RoundNumber = ?
                ORDER BY MatchNumber ASC
            ");
                $stmtMatch->bind_param("iii", $IDTournament, $IDGroup, $RoundNumber);
                $stmtMatch->execute();
                $matches = $stmtMatch->get_result()->fetch_all(MYSQLI_ASSOC);

                if (empty($matches))
                    throw new Exception("Match tidak ditemukan.");

                $winners = [];
                $IDTCL = $matches[0]['IDTCL']; // semua dalam 1 group sama TCL

                foreach ($matches as $m) {

                    if ($m['Status'] !== 'finished')
                        throw new Exception("Semua match harus finished.");

                    if (empty($m['WinnerTeam']))
                        throw new Exception("WinnerTeam belum lengkap.");

                    $winners[] = $m['WinnerTeam'];
                }

                // =============================
                // CEK FINAL
                // =============================
                if (count($matches) == 1)
                    throw new Exception("Sudah final round. Tidak bisa next round.");

                // =============================
                // GENERATE ROUND BARU
                // =============================
                $newRound = $RoundNumber + 1;
                $matchNumber = 1;

                for ($i = 0; $i < count($winners); $i += 2) {

                    if (!isset($winners[$i + 1]))
                        break;

                    $stmtInsert = $conn->prepare("
                    INSERT INTO tournament_match
                    (IDTournament, IDTCL, IDGroup,
                     RoundNumber, MatchNumber,
                     TeamA, TeamB, Status,
                     CreateAt, LastUpdateAt)
                    VALUES (?, ?, ?, ?, ?, ?, ?, 'pending',
                            NOW(), NOW())
                ");

                    $stmtInsert->bind_param(
                        "iiiiiii",
                        $IDTournament,
                        $IDTCL,
                        $IDGroup,
                        $newRound,
                        $matchNumber,
                        $winners[$i],
                        $winners[$i + 1]
                    );

                    $stmtInsert->execute();
                    $newMatchID = $stmtInsert->insert_id;

                    // UPDATE NEXTMATCHID KE MATCH LAMA
                    $stmtUpdate = $conn->prepare("
                    UPDATE tournament_match
                    SET NextMatchID = ?
                    WHERE IDMatch = ?
                ");

                    $stmtUpdate->bind_param("ii", $newMatchID, $matches[$i]['IDMatch']);
                    $stmtUpdate->execute();

                    $stmtUpdate->bind_param("ii", $newMatchID, $matches[$i + 1]['IDMatch']);
                    $stmtUpdate->execute();

                    $matchNumber++;
                }

                return [
                    "Status" => 1,
                    "Keterangan" => "Next round berhasil dibuat untuk group ini."
                ];
            });

        } catch (Exception $e) {
            return ["Status" => 0, "Keterangan" => $e->getMessage()];
        }
    }

    public function cancelRound($post)
    {
        try {

            $IDTournament = intval($post['IDTournament'] ?? 0);
            $IDGroup = intval($post['IDGroup'] ?? 0);
            $RoundNumber = intval($post['RoundNumber'] ?? 0);

            if ($IDTournament <= 0)
                throw new Exception("IDTournament tidak valid.");

            if ($IDGroup <= 0)
                throw new Exception("IDGroup tidak valid.");

            if ($RoundNumber <= 1)
                throw new Exception("Tidak bisa cancel round pertama.");

            return runWithTransaction($this->conn, function ($conn) use ($IDTournament, $IDGroup, $RoundNumber) {

                // CEK TOURNAMENT
                $stmtT = $conn->prepare("
                SELECT StatusTournament, TournamentFormat
                FROM tournament
                WHERE IDTournament = ?
            ");
                $stmtT->bind_param("i", $IDTournament);
                $stmtT->execute();
                $t = $stmtT->get_result()->fetch_assoc();

                if ($t['StatusTournament'] !== 'ongoing')
                    throw new Exception("Cancel hanya saat ongoing.");

                if ($t['TournamentFormat'] !== 'single_elimination')
                    throw new Exception("Cancel hanya untuk single elimination.");

                // CEK MATCH ROUND INI MASIH PENDING SEMUA
                $stmtCheck = $conn->prepare("
                SELECT 1 FROM tournament_match
                WHERE IDTournament = ?
                AND IDGroup = ?
                AND RoundNumber = ?
                AND (Status <> 'pending' OR WinnerTeam IS NOT NULL)
                LIMIT 1
            ");
                $stmtCheck->bind_param("iii", $IDTournament, $IDGroup, $RoundNumber);
                $stmtCheck->execute();

                if ($stmtCheck->get_result()->num_rows > 0)
                    throw new Exception("Round sudah berjalan/selesai.");

                // DELETE ROUND INI (GROUP INI SAJA)
                $stmtDelete = $conn->prepare("
                DELETE FROM tournament_match
                WHERE IDTournament = ?
                AND IDGroup = ?
                AND RoundNumber = ?
            ");
                $stmtDelete->bind_param("iii", $IDTournament, $IDGroup, $RoundNumber);
                $stmtDelete->execute();

                // RESET NEXTMATCHID DI ROUND SEBELUMNYA
                $prevRound = $RoundNumber - 1;

                $stmtReset = $conn->prepare("
                UPDATE tournament_match
                SET NextMatchID = NULL
                WHERE IDTournament = ?
                AND IDGroup = ?
                AND RoundNumber = ?
            ");
                $stmtReset->bind_param("iii", $IDTournament, $IDGroup, $prevRound);
                $stmtReset->execute();

                return [
                    "Status" => 1,
                    "Keterangan" => "Round berhasil dicancel untuk group ini."
                ];
            });

        } catch (Exception $e) {
            return ["Status" => 0, "Keterangan" => $e->getMessage()];
        }
    }

    private function getRoundLabel($totalTeams, $roundNumber)
    {
        $totalRounds = ceil(log($totalTeams, 2));
        $remaining = $totalRounds - $roundNumber + 1;

        if ($remaining == 1)
            return "Final";
        if ($remaining == 2)
            return "Semi Final";
        if ($remaining == 3)
            return "Quarter Final";

        return "Round " . $roundNumber;
    }

}
?>