<?php

namespace App\Services;

use App\Models\Tournament;
use App\Models\TournamentMatch;
use App\Models\TournamentParticipant;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class TournamentBracketService
{
    // ─────────────────────────────────────────────────────────────────────────
    // PUBLIC API
    // ─────────────────────────────────────────────────────────────────────────

    /**
     * Public wrapper: generate knockout bracket from a pre-ordered seed list.
     * playerIds must be ordered: index 0 = seed 1, index 1 = seed 2, etc.
     */
    public function generateKnockoutFromSeeds(Tournament $tournament, array $playerIds): void
    {
        $this->generateKnockout($tournament, $playerIds);
    }

    /**
     * Generate full bracket for a tournament.
     * Clears any existing matches first.
     */
    public function generate(Tournament $tournament): void
    {
        DB::transaction(function () use ($tournament) {
            // Remove existing matches (reset next_match_id FK first)
            TournamentMatch::where('tournament_id', $tournament->id)
                ->update(['next_match_id' => null, 'series_parent_id' => null]);
            TournamentMatch::where('tournament_id', $tournament->id)->delete();

            $participants = $tournament->participants()
                ->with('user')
                ->orderBy('seed')
                ->get();

            // Seed if not set yet
            $this->assignSeeds($participants);

            if ($tournament->format === 'group_knockout') {
                $this->generateGroupStage($tournament, $participants);
                $tournament->update(['current_phase' => 'group']);
            } else {
                $this->generateKnockout($tournament, $participants->pluck('user_id')->toArray());
                $tournament->update(['current_phase' => 'knockout']);
            }
        });
    }

    /**
     * Advance group stage → knockout once all group matches are completed.
     */
    public function advanceToKnockout(Tournament $tournament): bool
    {
        $incomplete = TournamentMatch::where('tournament_id', $tournament->id)
            ->where('phase', 'group')
            ->where('status', 'pending')
            ->exists();

        if ($incomplete) return false;

        $pendingTiebreaker = TournamentMatch::where('tournament_id', $tournament->id)
            ->where('phase', 'tiebreaker')
            ->where('status', 'pending')
            ->exists();

        if ($pendingTiebreaker) return false;

        DB::transaction(function () use ($tournament) {
            $advancers = $this->resolveGroupAdvancers($tournament);
            $this->generateKnockout($tournament, $advancers);
            $tournament->update(['current_phase' => 'knockout']);
        });

        return true;
    }


    /**
     * Submit score for a single leg of a tournament match.
     * - Group phase: draw allowed → both get 1 pt
     * - Golden goal / knockout: draw NOT allowed
     * - For BO2+: automatically creates next leg or resolves series winner
     */
    public function submitScore(TournamentMatch $match, int $score1, int $score2): void
    {
        $tournament = $match->tournament;
        $isFinal    = $tournament->isFinalMatch($match->getSeriesRoot());
        $phase      = $isFinal ? 'final' : ($match->phase === 'group' ? 'group' : 'knockout');
        $bo         = $tournament->getBoForPhase($phase);

        // Draw rules:
        // - group: always allowed
        // - golden goal: never allowed
        // - BO2 leg 1 or 2: draw allowed (aggregate/golden goal will decide)
        // - BO1 / BO3+ knockout: no draw
        $rootId          = $match->series_parent_id ?? $match->id;
        $legsPlayedSoFar = TournamentMatch::where(function ($q) use ($rootId) {
            $q->where('id', $rootId)->orWhere('series_parent_id', $rootId);
        })->where('status', 'completed')->count();

        $drawAllowed = $match->phase === 'group'
            || ($bo === 2 && !$match->is_golden_goal)  // BO2: draw ok, aggregate decides
            || $match->is_golden_goal === false && $bo >= 3 && false; // BO3+ no draw (handled below)

        // Golden goal: absolute no draw
        if ($match->is_golden_goal && $score1 === $score2) {
            throw new \InvalidArgumentException(
                'Golden Goal: harus ada pencetak gol pertama. Jika skor masih imbang, lanjutkan bermain.'
            );
        }

        // BO1 / BO3+ knockout: no draw
        if ($match->phase === 'knockout' && !$match->is_golden_goal && $bo !== 2 && $score1 === $score2) {
            throw new \InvalidArgumentException(
                'Draw tidak diizinkan di fase Knockout BO' . $bo . '. Masukkan skor yang berbeda.'
            );
        }

        DB::transaction(function () use ($match, $score1, $score2) {
            $isDraw   = ($score1 === $score2);
            $winnerId = $isDraw ? null : ($score1 > $score2 ? $match->player1_id : $match->player2_id);

            $match->update([
                'score1'    => $score1,
                'score2'    => $score2,
                'winner_id' => $winnerId,
                'is_draw'   => $isDraw,
                'status'    => 'completed',
            ]);

            if ($match->phase === 'group' && $isDraw) {
                return;
            }

            if ($match->phase === 'tiebreaker' && $winnerId) {
                return;
            }

            if ($match->phase === 'knockout') {
                // For draws in BO2, pass player1 as placeholder (handleKnockoutLegComplete will resolve)
                $effectiveWinner = $winnerId ?? $match->player1_id;
                $this->handleKnockoutLegComplete($match, $effectiveWinner);
            }
        });
    }

    /**
     * Handle BO series logic after a knockout leg completes.
     *
     * BO rules:
     * - BO1: 1 leg, langsung selesai
     * - BO2: 2 leg WAJIB dimainkan semua, menang berdasarkan leg wins terbanyak
     *        Jika 1-1 (draw series) → aggregate gol menentukan, jika masih sama → leg ke-3 (golden goal)
     * - BO3: siapa duluan menang 2 leg
     * - BO5: siapa duluan menang 3 leg
     */
    private function handleKnockoutLegComplete(TournamentMatch $leg, int $winnerId): void
    {
        $tournament = $leg->tournament;

        // Determine effective BO for this match
        $isFinal = $tournament->isFinalMatch($leg->getSeriesRoot());
        $phase   = $isFinal ? 'final' : 'knockout';
        $bo      = $tournament->getBoForPhase($phase);

        // Collect all completed legs in this series
        $rootId  = $leg->series_parent_id ?? $leg->id;
        $allLegs = TournamentMatch::where(function ($q) use ($rootId) {
                $q->where('id', $rootId)->orWhere('series_parent_id', $rootId);
            })
            ->where('status', 'completed')
            ->get();

        $totalLegsPlayed = $allLegs->count();

        // Count wins and aggregate goals per player
        $wins = []; $goals = [];
        foreach ($allLegs as $l) {
            if ($l->winner_id) {
                $wins[$l->winner_id] = ($wins[$l->winner_id] ?? 0) + 1;
            }
            if ($l->player1_id) {
                $goals[$l->player1_id] = ($goals[$l->player1_id] ?? 0) + ($l->score1 ?? 0);
                $goals[$l->player2_id] = ($goals[$l->player2_id] ?? 0) + ($l->score2 ?? 0);
            }
        }

        // --- BO1: selesai setelah 1 leg ---
        if ($bo <= 1) {
            $this->resolveKnockoutSeriesWinner($leg, $winnerId, $rootId);
            return;
        }

        // --- BO2: harus main 2 leg penuh ---
        if ($bo === 2) {
            if ($totalLegsPlayed < 2) {
                $existingPendingLeg = TournamentMatch::where('series_parent_id', $rootId)
                    ->where('status', 'pending')
                    ->where('leg_number', $totalLegsPlayed + 1)
                    ->first();

                if (!$existingPendingLeg) {
                    $this->createNextLeg($leg, $rootId, $totalLegsPlayed + 1);
                }
                return;
            }

            // Kedua leg sudah selesai — tentukan pemenang
            $p1 = $leg->player1_id;
            $p2 = $leg->player2_id;
            $w1 = $wins[$p1] ?? 0;
            $w2 = $wins[$p2] ?? 0;

            if ($w1 > $w2) {
                $this->resolveKnockoutSeriesWinner($leg, $p1, $rootId);
            } elseif ($w2 > $w1) {
                $this->resolveKnockoutSeriesWinner($leg, $p2, $rootId);
            } else {
                // 1-1 draw — cek tiebreak_mode
                $useAggregate = $tournament->useAggregateTiebreak();

                if ($useAggregate) {
                    // Mode Agregat: bandingkan total gol kedua leg
                    $g1 = $goals[$p1] ?? 0;
                    $g2 = $goals[$p2] ?? 0;
                    if ($g1 > $g2) {
                        $this->resolveKnockoutSeriesWinner($leg, $p1, $rootId);
                    } elseif ($g2 > $g1) {
                        $this->resolveKnockoutSeriesWinner($leg, $p2, $rootId);
                    } else {
                        // Agregat pun sama → golden goal decider
                        $this->createNextLeg($leg, $rootId, $totalLegsPlayed + 1, true);
                    }
                } else {
                    // Mode Golden Goal: langsung leg ke-3 tanpa cek agregat
                    $this->createNextLeg($leg, $rootId, $totalLegsPlayed + 1, true);
                }
            }
            return;
        }

        // --- BO3 / BO5: siapa duluan menang ceil(bo/2) leg ---
        $winsNeeded = (int) ceil($bo / 2); // BO3→2, BO5→3

        $seriesWinner = null;
        foreach ($wins as $uid => $w) {
            if ($w >= $winsNeeded) {
                $seriesWinner = $uid;
                break;
            }
        }

        if ($seriesWinner) {
            $this->resolveKnockoutSeriesWinner($leg, $seriesWinner, $rootId);
        } elseif ($totalLegsPlayed >= $bo) {
            // Semua leg dimainkan tanpa winner (tidak mungkin di BO ganjil, tapi jaga-jaga)
            arsort($wins);
            $winner = array_key_first($wins) ?? $winnerId;
            $this->resolveKnockoutSeriesWinner($leg, $winner, $rootId);
        } else {
            // Cek apakah leg berikutnya sudah ada (dibuat saat bracket generate)
            $nextLegNum = $totalLegsPlayed + 1;
            $existingNext = TournamentMatch::where(function($q) use ($rootId) {
                $q->where('id', $rootId)->orWhere('series_parent_id', $rootId);
            })->where('leg_number', $nextLegNum)->where('status', 'pending')->first();

            if (!$existingNext) {
                $this->createNextLeg($leg, $rootId, $nextLegNum);
            }
            // Leg sudah ada — tidak perlu buat lagi, tunggu input skor
        }
    }

    /**
     * Create the next leg for a BO series.
     */
    private function createNextLeg(
        TournamentMatch $leg,
        int $rootId,
        int $nextLegNumber,
        bool $isGoldenGoal = false
    ): void {
        TournamentMatch::create([
            'tournament_id'    => $leg->tournament_id,
            'round'            => $leg->round,
            'match_number'     => TournamentMatch::where('tournament_id', $leg->tournament_id)->max('match_number') + 1,
            'leg_number'       => $nextLegNumber,
            'series_parent_id' => $rootId,
            'phase'            => 'knockout',
            'player1_id'       => $leg->player1_id,
            'player2_id'       => $leg->player2_id,
            'status'           => 'pending',
            'is_golden_goal'   => $isGoldenGoal,
            'next_match_id'    => $leg->next_match_id,
            'next_match_slot'  => $leg->next_match_slot,
        ]);
    }

    /**
     * Propagate series winner into next match slot and mark loser.
     */
    private function resolveKnockoutSeriesWinner(TournamentMatch $leg, int $seriesWinner, int $rootId): void
    {
        if ($leg->next_match_id) {
            $next  = TournamentMatch::find($leg->next_match_id);
            $field = $leg->next_match_slot ? 'player2_id' : 'player1_id';
            if ($next) $next->update([$field => $seriesWinner]);
        }

        $loserId = $seriesWinner === $leg->player1_id ? $leg->player2_id : $leg->player1_id;
        if ($loserId) {
            TournamentParticipant::where('tournament_id', $leg->tournament_id)
                ->where('user_id', $loserId)
                ->update(['status' => 'eliminated']);
        }

        // Check if entire knockout phase is done (no more pending leg-1 matches without a series winner)
        $pendingCount = TournamentMatch::where('tournament_id', $leg->tournament_id)
            ->where('phase', 'knockout')
            ->where('status', 'pending')
            ->where(function ($q) {
                $q->whereNull('series_parent_id')->orWhere('leg_number', 1);
            })
            ->count();

        // Also count pending additional legs
        $pendingLegs = TournamentMatch::where('tournament_id', $leg->tournament_id)
            ->where('phase', 'knockout')
            ->where('status', 'pending')
            ->where('leg_number', '>', 1)
            ->count();

        if ($pendingCount === 0 && $pendingLegs === 0) {
            TournamentParticipant::where('tournament_id', $leg->tournament_id)
                ->where('user_id', $seriesWinner)
                ->update(['status' => 'champion']);
            Tournament::where('id', $leg->tournament_id)
                ->update(['status' => 'completed']);
        }
    }


    /**
     * Check if any group ties need tiebreaker matches, and create them.
     * Returns the number of tiebreaker matches created.
     */
    public function createTiebreakerMatches(Tournament $tournament): int
    {
        $standings = $this->buildGroupStandings($tournament);
        $perGroup  = (int) $tournament->groups_per_advance;
        $created   = 0;

        foreach ($standings as $group => $rows) {
            uasort($rows, fn($a, $b) =>
                $b['pts'] <=> $a['pts'] ?: ($b['gf'] - $b['ga']) <=> ($a['gf'] - $a['ga']) ?: $b['gf'] <=> $a['gf']
            );

            $sorted  = array_keys($rows);
            $cutoff  = $perGroup - 1;

            if (!isset($sorted[$cutoff]) || !isset($sorted[$cutoff + 1])) continue;

            $cutoffPts = $rows[$sorted[$cutoff]]['pts'];
            $belowPts  = $rows[$sorted[$cutoff + 1]]['pts'];

            if ($cutoffPts !== $belowPts) continue;

            $tied = array_filter($sorted, fn($uid) => $rows[$uid]['pts'] === $cutoffPts);

            $existing = TournamentMatch::where('tournament_id', $tournament->id)
                ->where('phase', 'tiebreaker')
                ->where('group_name', $group)
                ->count();

            if ($existing > 0) continue;

            $tiedPlayers = array_values($tied);
            $matchNumber = TournamentMatch::where('tournament_id', $tournament->id)->max('match_number') + 1;

            for ($i = 0; $i < count($tiedPlayers) - 1; $i++) {
                for ($j = $i + 1; $j < count($tiedPlayers); $j++) {
                    TournamentMatch::create([
                        'tournament_id'  => $tournament->id,
                        'round'          => 99,
                        'match_number'   => $matchNumber++,
                        'leg_number'     => 1,
                        'phase'          => 'tiebreaker',
                        'group_name'     => $group,
                        'player1_id'     => $tiedPlayers[$i],
                        'player2_id'     => $tiedPlayers[$j],
                        'status'         => 'pending',
                        'is_golden_goal' => true,
                    ]);
                    $created++;
                }
            }
        }

        return $created;
    }

    // ─────────────────────────────────────────────────────────────────────────
    // GROUP STAGE
    // ─────────────────────────────────────────────────────────────────────────

    private function generateGroupStage(Tournament $tournament, Collection $participants): void
    {
        $groupCount  = max(2, (int) $tournament->group_count);
        $groups      = $this->splitIntoGroups($participants->pluck('user_id')->toArray(), $groupCount);
        $matchNumber = 1;
        $groupNames  = range('A', 'Z');
        $bo          = $tournament->getBoForPhase('group');

        foreach ($groups as $gi => $members) {
            $groupLabel = $groupNames[$gi] ?? ('G' . ($gi + 1));
            for ($a = 0; $a < count($members); $a++) {
                for ($b = $a + 1; $b < count($members); $b++) {
                    // Create leg 1 (root)
                    $root = TournamentMatch::create([
                        'tournament_id' => $tournament->id,
                        'round'         => 1,
                        'match_number'  => $matchNumber++,
                        'leg_number'    => 1,
                        'phase'         => 'group',
                        'group_name'    => $groupLabel,
                        'player1_id'    => $members[$a],
                        'player2_id'    => $members[$b],
                        'status'        => 'pending',
                    ]);

                    // Create additional legs for BO2+
                    for ($leg = 2; $leg <= $bo; $leg++) {
                        TournamentMatch::create([
                            'tournament_id'    => $tournament->id,
                            'round'            => 1,
                            'match_number'     => $matchNumber++,
                            'leg_number'       => $leg,
                            'series_parent_id' => $root->id,
                            'phase'            => 'group',
                            'group_name'       => $groupLabel,
                            'player1_id'       => $members[$a],
                            'player2_id'       => $members[$b],
                            'status'           => 'pending',
                        ]);
                    }
                }
            }
        }
    }


    private function resolveGroupAdvancers(Tournament $tournament): array
    {
        $perGroup  = (int) $tournament->groups_per_advance;
        $standings = $this->buildGroupStandings($tournament);
        $advancers = [];

        foreach ($standings as $group => $rows) {
            uasort($rows, fn($a, $b) =>
                $b['pts'] <=> $a['pts']
                    ?: ($b['gf'] - $b['ga']) <=> ($a['gf'] - $a['ga'])
                    ?: $b['gf'] <=> $a['gf']
            );

            $tiebreakers = TournamentMatch::where('tournament_id', $tournament->id)
                ->where('phase', 'tiebreaker')
                ->where('group_name', $group)
                ->where('status', 'completed')
                ->get();

            $tbWins = [];
            foreach ($tiebreakers as $tb) {
                if ($tb->winner_id) {
                    $tbWins[$tb->winner_id] = ($tbWins[$tb->winner_id] ?? 0) + 1;
                }
            }

            $sorted = array_keys($rows);
            usort($sorted, function($a, $b) use ($rows, $tbWins) {
                $ptsDiff = $rows[$b]['pts'] <=> $rows[$a]['pts'];
                if ($ptsDiff) return $ptsDiff;
                $gdDiff = ($rows[$b]['gf'] - $rows[$b]['ga']) <=> ($rows[$a]['gf'] - $rows[$a]['ga']);
                if ($gdDiff) return $gdDiff;
                $gfDiff = $rows[$b]['gf'] <=> $rows[$a]['gf'];
                if ($gfDiff) return $gfDiff;
                return ($tbWins[$b] ?? 0) <=> ($tbWins[$a] ?? 0);
            });

            $advancers = array_merge($advancers, array_slice($sorted, 0, $perGroup));
        }

        return $advancers;
    }

    private function buildGroupStandings(Tournament $tournament): array
    {
        // Only count leg 1 OR tiebreaker for standings; BO group legs aggregate goals per player-pair
        $matches = TournamentMatch::where('tournament_id', $tournament->id)
            ->whereIn('phase', ['group', 'tiebreaker'])
            ->get();

        $standings = [];

        foreach ($matches as $m) {
            $g = $m->group_name ?? 'A';
            foreach ([$m->player1_id, $m->player2_id] as $uid) {
                if (!$uid) continue;
                if (!isset($standings[$g][$uid])) {
                    $standings[$g][$uid] = ['pts'=>0,'w'=>0,'d'=>0,'l'=>0,'gf'=>0,'ga'=>0,'played'=>0];
                }
            }
        }

        // Group matches by series (root = series_parent_id ?? id)
        $seriesResults = [];
        foreach ($matches as $m) {
            if ($m->phase !== 'group' || $m->status !== 'completed') continue;
            $rootId = $m->series_parent_id ?? $m->id;
            if (!isset($seriesResults[$rootId])) {
                $seriesResults[$rootId] = [
                    'group' => $m->group_name ?? 'A',
                    'p1'    => $m->player1_id,
                    'p2'    => $m->player2_id,
                    'gf1'   => 0, 'gf2' => 0,
                    'legs'  => 0, 'completed' => 0,
                    'w1'    => 0, 'w2' => 0, 'draws' => 0,
                ];
            }
            $seriesResults[$rootId]['gf1']      += $m->score1 ?? 0;
            $seriesResults[$rootId]['gf2']      += $m->score2 ?? 0;
            $seriesResults[$rootId]['completed'] += 1;
            if ($m->is_draw) {
                $seriesResults[$rootId]['draws']++;
            } elseif ($m->winner_id) {
                if ($m->winner_id === $m->player1_id) $seriesResults[$rootId]['w1']++;
                else $seriesResults[$rootId]['w2']++;
            }
        }

        // Process series results into standings
        foreach ($seriesResults as $rootId => $s) {
            $g  = $s['group'];
            $p1 = $s['p1'];
            $p2 = $s['p2'];
            if (!$p1 || !$p2) continue;

            // Determine series outcome based on aggregate
            $totalGf1 = $s['gf1'];
            $totalGf2 = $s['gf2'];
            $w1       = $s['w1'];
            $w2       = $s['w2'];

            if ($w1 > $w2) {
                // P1 wins series
                $standings[$g][$p1]['pts'] += 3; $standings[$g][$p1]['w']++;
                $standings[$g][$p2]['l']++;
            } elseif ($w2 > $w1) {
                // P2 wins series
                $standings[$g][$p2]['pts'] += 3; $standings[$g][$p2]['w']++;
                $standings[$g][$p1]['l']++;
            } else {
                // Draw series (BO2 can end 1-1)
                $standings[$g][$p1]['pts'] += 1; $standings[$g][$p1]['d']++;
                $standings[$g][$p2]['pts'] += 1; $standings[$g][$p2]['d']++;
            }

            $standings[$g][$p1]['gf'] += $totalGf1;
            $standings[$g][$p1]['ga'] += $totalGf2;
            $standings[$g][$p1]['played']++;
            $standings[$g][$p2]['gf'] += $totalGf2;
            $standings[$g][$p2]['ga'] += $totalGf1;
            $standings[$g][$p2]['played']++;
        }

        // Process tiebreaker matches
        foreach ($matches as $m) {
            if ($m->phase !== 'tiebreaker' || $m->status !== 'completed') continue;
            $g = $m->group_name ?? 'A';
            $p1 = $m->player1_id; $p2 = $m->player2_id;
            if ($m->winner_id) {
                $winner = $m->winner_id;
                $loser  = $winner === $p1 ? $p2 : $p1;
                if ($winner) $standings[$g][$winner]['w']++;
                if ($loser)  $standings[$g][$loser]['l']++;
            }
        }

        return $standings;
    }


    // ─────────────────────────────────────────────────────────────────────────
    // KNOCKOUT BRACKET
    // ─────────────────────────────────────────────────────────────────────────

    private function generateKnockout(Tournament $tournament, array $playerIds): void
    {
        $count = count($playerIds);
        if ($count < 2) return;

        $size = $this->nextPowerOf2($count);
        while (count($playerIds) < $size) {
            $playerIds[] = null;
        }

        $seeded      = $this->seedBracket($playerIds, $size);
        $totalRounds = 0;
        $tmp = $size;
        while ($tmp > 1) { $totalRounds++; $tmp >>= 1; }
        $matchNumber = TournamentMatch::where('tournament_id', $tournament->id)->max('match_number') + 1;
        $matchMap    = [];

        for ($round = 1; $round <= $totalRounds; $round++) {
            $matchesInRound = $size / pow(2, $round);
            $matchMap[$round] = [];

            // Determine BO for this round
            $isFinalRound = ($round === $totalRounds);
            $phase        = $isFinalRound ? 'final' : 'knockout';
            $bo           = $tournament->getBoForPhase($phase === 'final' ? 'final' : 'knockout');

            for ($m = 1; $m <= $matchesInRound; $m++) {
                $p1 = $p2 = null;
                if ($round === 1) {
                    $p1 = $seeded[($m - 1) * 2]     ?? null;
                    $p2 = $seeded[($m - 1) * 2 + 1] ?? null;
                }

                $status = $p1 && $p2 ? 'pending' : ($p1 || $p2 ? 'bye' : 'pending');

                // Create leg 1 (root)
                $tm = TournamentMatch::create([
                    'tournament_id' => $tournament->id,
                    'round'         => $round,
                    'match_number'  => $matchNumber++,
                    'leg_number'    => 1,
                    'phase'         => 'knockout',
                    'player1_id'    => $p1,
                    'player2_id'    => $p2,
                    'status'        => $status,
                ]);
                $matchMap[$round][$m] = $tm->id;

                if ($tm->status === 'bye') {
                    $winnerId = $p1 ?? $p2;
                    $tm->update(['winner_id' => $winnerId, 'status' => 'completed']);
                    continue; // No extra legs for BYE
                }

                // Create additional legs for BO2+ (only for real matches)
                if ($p1 && $p2 && $bo > 1) {
                    for ($leg = 2; $leg <= $bo; $leg++) {
                        TournamentMatch::create([
                            'tournament_id'    => $tournament->id,
                            'round'            => $round,
                            'match_number'     => $matchNumber++,
                            'leg_number'       => $leg,
                            'series_parent_id' => $tm->id,
                            'phase'            => 'knockout',
                            'player1_id'       => $p1,
                            'player2_id'       => $p2,
                            'status'           => 'pending',
                        ]);
                    }
                }
            }
        }

        // Wire next_match_id for leg-1 matches only
        for ($round = 1; $round < $totalRounds; $round++) {
            $matchesInRound = count($matchMap[$round]);
            for ($m = 1; $m <= $matchesInRound; $m++) {
                $nextMatchPos = (int) ceil($m / 2);
                $slot         = ($m % 2 === 1) ? 0 : 1;
                $currentId    = $matchMap[$round][$m];
                $nextId       = $matchMap[$round + 1][$nextMatchPos] ?? null;

                if ($nextId) {
                    TournamentMatch::where('id', $currentId)->update([
                        'next_match_id'   => $nextId,
                        'next_match_slot' => $slot,
                    ]);
                    // Also update any extra legs for this slot
                    TournamentMatch::where('series_parent_id', $currentId)->update([
                        'next_match_id'   => $nextId,
                        'next_match_slot' => $slot,
                    ]);

                    // Propagate BYE winner immediately
                    $current = TournamentMatch::find($currentId);
                    if ($current && $current->winner_id) {
                        $field = $slot ? 'player2_id' : 'player1_id';
                        TournamentMatch::where('id', $nextId)->update([$field => $current->winner_id]);
                    }
                }
            }
        }
    }


    // ─────────────────────────────────────────────────────────────────────────
    // HELPERS
    // ─────────────────────────────────────────────────────────────────────────

    private function assignSeeds(Collection $participants): void
    {
        $participants->each(function ($p, $i) {
            if (!$p->seed) $p->update(['seed' => $i + 1]);
        });
    }

    private function splitIntoGroups(array $players, int $groups): array
    {
        shuffle($players);
        $result = array_fill(0, $groups, []);
        foreach ($players as $i => $uid) {
            $result[$i % $groups][] = $uid;
        }
        return $result;
    }

    private function seedBracket(array $players, int $size): array
    {
        // Standard bracket seeding: 1 vs size, 2 vs (size-1), etc.
        // Creates a bracket where top seeds avoid each other until late rounds.
        $bracket = array_fill(0, $size, null);
        $seeds   = $this->buildSeedPositions($size);

        foreach ($players as $i => $uid) {
            if (isset($seeds[$i]) && $seeds[$i] < $size) {
                $bracket[$seeds[$i]] = $uid;
            }
        }

        return $bracket;
    }

    /**
     * Build bracket seed positions using the standard single-elimination method.
     * For size=8: positions [0,7,4,3,2,5,6,1] (1 vs 8, 4 vs 5, 3 vs 6, 2 vs 7)
     * This guarantees top half seeds face bottom half in each round.
     */
    private function buildSeedPositions(int $size): array
    {
        // Start with positions for 2 players
        $slots = [0, 1];

        // Double until we reach desired size
        while (count($slots) < $size) {
            $newSlots = [];
            $half     = count($slots) * 2;
            foreach ($slots as $slot) {
                // Each existing slot becomes two: the slot itself and its mirror
                $newSlots[] = $slot * 2;
                $newSlots[] = $half - 1 - $slot * 2;
            }
            $slots = $newSlots;
        }

        return array_slice($slots, 0, $size);
    }

    private function nextPowerOf2(int $n): int
    {
        $p = 1;
        while ($p < $n) $p *= 2;
        return $p;
    }

    // ─────────────────────────────────────────────────────────────────────────
    // READ HELPERS (for views)
    // ─────────────────────────────────────────────────────────────────────────

    public function getGroupStandings(Tournament $tournament): array
    {
        // Load all group/tiebreaker matches
        $allMatches = TournamentMatch::where('tournament_id', $tournament->id)
            ->whereIn('phase', ['group', 'tiebreaker'])
            ->with(['player1', 'player2', 'winner', 'club1', 'club2'])
            ->get();

        $standings = $this->buildGroupStandings($tournament);
        $players   = [];

        foreach ($allMatches as $m) {
            if ($m->player1_id && $m->player1) $players[$m->player1_id] = $m->player1;
            if ($m->player2_id && $m->player2) $players[$m->player2_id] = $m->player2;
        }

        foreach ($standings as &$group) {
            uasort($group, fn($a, $b) =>
                $b['pts'] <=> $a['pts']
                    ?: ($b['gf'] - $b['ga']) <=> ($a['gf'] - $a['ga'])
                    ?: $b['gf'] <=> $a['gf']
            );
        }

        // Only show ROOT matches (leg_number=1 or no series_parent) for display
        // Load all extra legs per series for sub-detail
        $rootGroupMatches = $allMatches->where('phase', 'group')
            ->filter(fn($m) => $m->series_parent_id === null);

        // Bulk-load extra legs for root matches (with club picks)
        $rootIds = $rootGroupMatches->pluck('id')->toArray();
        $extraLegs = TournamentMatch::whereIn('series_parent_id', $rootIds)
            ->with(['player1', 'player2', 'club1', 'club2'])
            ->orderBy('leg_number')
            ->get()
            ->groupBy('series_parent_id');

        // Load ban records for all root matches
        $allBans = \App\Models\TournamentMatchBan::whereIn('series_root_id', $rootIds)
            ->with(['club', 'player'])
            ->get()
            ->groupBy('series_root_id');

        // Attach series legs and ban records to each root match
        foreach ($rootGroupMatches as $match) {
            $legs = collect([$match]);
            if (isset($extraLegs[$match->id])) {
                $legs = $legs->merge($extraLegs[$match->id]);
            }
            $match->setRelation('seriesLegs', $legs->sortBy('leg_number')->values());
            $match->setRelation('seriesBanRecords', $allBans[$match->id] ?? collect());
        }

        $tiebreakerMatches = $allMatches->where('phase', 'tiebreaker')
            ->filter(fn($m) => $m->series_parent_id === null);

        return [
            'standings'   => $standings,
            'players'     => $players,
            'matches'     => $rootGroupMatches,
            'tiebreakers' => $tiebreakerMatches,
        ];
    }

    public function getKnockoutBracket(Tournament $tournament): array
    {
        // Only return leg-1 matches for bracket display
        $matches = TournamentMatch::where('tournament_id', $tournament->id)
            ->where('phase', 'knockout')
            ->where('leg_number', 1)
            ->with(['player1', 'player2', 'winner', 'club1', 'club2'])
            ->orderBy('round')
            ->orderBy('match_number')
            ->get();

        $rootIds = $matches->pluck('id')->toArray();

        // Load all extra legs with club picks
        $extraLegs = TournamentMatch::whereIn('series_parent_id', $rootIds)
            ->with(['player1', 'player2', 'winner', 'club1', 'club2'])
            ->orderBy('leg_number')
            ->get()
            ->groupBy('series_parent_id');

        // Load all bans for these series (all legs)
        $allBans = \App\Models\TournamentMatchBan::whereIn('series_root_id', $rootIds)
            ->with(['club', 'player'])
            ->get()
            ->groupBy('series_root_id');

        foreach ($matches as $match) {
            $legs = collect([$match]);
            if (isset($extraLegs[$match->id])) {
                $legs = $legs->merge($extraLegs[$match->id]);
            }
            $match->setRelation('seriesLegs', $legs->sortBy('leg_number')->values());
            $match->setRelation('seriesBanRecords', $allBans[$match->id] ?? collect());
        }

        $rounds = [];
        foreach ($matches as $m) {
            $rounds[$m->round][] = $m;
        }

        return $rounds;
    }
}
