<?php

namespace App\Http\Controllers;

use App\Models\Competition;
use App\Models\Level;
use App\Models\Score;
use App\Models\Athlete;
use App\Models\AgeCategory;
use App\Models\RoundRotation;
use App\Models\AssignApparatus;
use App\Models\Apparatus;
use App\Models\CompetitionAthlete;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

class PublicLeaderboardController extends Controller
{
    public function index()
    {
        $now = now();

        $competitions = Competition::where('start_date', '<=', $now)->where('end_date', '>=', $now)->latest()->get();

        // Pass to Blade for the modal/filter UI
        $apparatuses = Apparatus::orderBy('id')->take(4)->get();

        // Optional: configurable
        $defaultCycleSeconds = 10;

        return view('leaderboard.select_competition', compact('competitions', 'apparatuses', 'defaultCycleSeconds'));
    }

    public function show($competitionId)
    {
        $now = now();

        $activeCompetitions = Competition::where('start_date', '<=', $now)
            ->where('end_date', '>=', $now)
            ->with(['scores'])
            ->where('id', $competitionId)
            ->latest()
            ->get()
            ->filter(function ($competition) {
                $currentRound = RoundRotation::where('competition_id', $competition->id)->max('round_number');

                // If there is any rotation at current round, consider it active (kept simple)
                return RoundRotation::where('competition_id', $competition->id)->where('round_number', $currentRound)->exists();
            })
            ->values();

        $apparatuses = Apparatus::orderBy('id')->get();

        // Pull age categories + levels present in this competition only
        $athleteQuery = Athlete::whereHas('competitionAthletes', function ($q) use ($competitionId) {
            $q->where('competition_id', $competitionId);
        });

        $ageCategoryIds = $athleteQuery->pluck('age_category_id')->unique()->filter()->values();
        $levelIds = $athleteQuery->pluck('level_id')->unique()->filter()->values();

        $ageCategories = AgeCategory::whereIn('id', $ageCategoryIds)->orderBy('min_age')->get();
        $levels = Level::whereIn('id', $levelIds)->orderBy('name')->get();

        // Precompute Vault (V1 + V2) combined totals for this competition
        $vault = $apparatuses->first(fn($a) => ($a->slug ?? null) === 'vault' || $a->name === 'Vault (V)');
        $vaultCombined = collect();
        if ($vault) {
            $vaultCombined = $this->vaultCombinedRanking((int) $competitionId)->keyBy('athlete_id');
        }

        $athleteRankings = [];

        foreach ($activeCompetitions as $competition) {
            foreach ($apparatuses as $apparatus) {
                foreach ($ageCategories as $age) {
                    foreach ($levels as $level) {
                        $assignedAthletes = CompetitionAthlete::where('competition_id', $competition->id)
                            ->whereHas('athlete', function ($q) use ($age, $level) {
                                $q->where('age_category_id', $age->id)->where('level_id', $level->id);
                            })
                            ->with([
                                'athlete.scores' => function ($q) use ($apparatus, $competition) {
                                    $q->where('apparatus_id', $apparatus->id)->where('competition_id', $competition->id);
                                },
                                'athlete.club',
                            ])
                            ->get()
                            ->map(fn($assignment) => $assignment->athlete);

                        if ($assignedAthletes->isEmpty()) {
                            continue;
                        }

                        // Special case: Vault leaderboard uses V1 + V2 combined totals
                        if ($vault && $apparatus->id === $vault->id) {
                            $assignedAthletes = $assignedAthletes
                                ->map(function ($ath) use ($vaultCombined) {
                                    $row = $vaultCombined->get($ath->id);
                                    $ath->vault_total = $row->vault_total ?? 0.0;
                                    $ath->v1_total = $row->v1_total ?? 0.0;
                                    $ath->v2_total = $row->v2_total ?? 0.0;
                                    return $ath;
                                })
                                ->sortByDesc('vault_total')
                                ->values();
                        }

                        if ($competition->scoring_type === 'FIG') {
                            $athleteRankings[$competition->name][$apparatus->name][$age->name] = $assignedAthletes;
                        } else {
                            $athleteRankings[$competition->name][$apparatus->name][$age->name][$level->name] = $assignedAthletes;
                        }
                    }
                }
            }
        }

        $cycleDuration = 10;

        return view('leaderboard.show', compact('activeCompetitions', 'apparatuses', 'ageCategories', 'levels', 'athleteRankings', 'cycleDuration'));
    }

    public function refresh($competitionId)
    {
        // NOTE: This "overall" is raw SUM of all final_score rows and will over-count if you have multiple judges.
        // If you want a clean overall (apparatus-best + Vault sum), ask and I’ll swap in an overallCombined() helper.
        $overall = Score::select('athlete_id', DB::raw('SUM(final_score) as total_score'))->where('competition_id', $competitionId)->groupBy('athlete_id')->with('athlete')->orderByDesc('total_score')->get();

        $apparatuses = Apparatus::orderBy('id')->get();

        $apparatusRankings = [];
        foreach ($apparatuses as $app) {
            // Vault uses V1 + V2 combined
            if (($app->slug ?? null) === 'vault' || $app->name === 'Vault (V)') {
                $apparatusRankings[$app->name] = $this->vaultCombinedRanking((int) $competitionId);
            } else {
                // Other apparatus: best score per athlete (MAX panel policy)
                $apparatusRankings[$app->name] = Score::selectRaw('athlete_id, MAX(final_score) AS best_score')->where('competition_id', $competitionId)->where('apparatus_id', $app->id)->groupBy('athlete_id')->with('athlete')->orderByDesc('best_score')->get();
            }
        }

        return response()->json([
            'overall' => $overall,
            'apparatusRankings' => $apparatusRankings,
        ]);
    }

    public function liveScreen($competitionId, $apparatusId, Request $request)
    {
        $competition = Competition::findOrFail($competitionId);

        // Admin-configurable duration (fallback to 10s)
        $cycleDuration = (int) ($competition->live_cycle_seconds ?? 10);

        $apparatus = null;
        $judgeId = $request->query('judge_id'); // optional query param

        if ($apparatusId !== 'all') {
            $apparatus = Apparatus::findOrFail($apparatusId);

            // If no judge was passed, try to find one from competition_judge table
            if (!$judgeId) {
                $assigned = AssignApparatus::where('competition_id', $competitionId)->where('apparatus_id', $apparatusId)->first();

                $judgeId = $assigned?->user_id; // null-safe
            }
        }

        return view('leaderboard.live', compact('competition', 'apparatus', 'judgeId', 'cycleDuration', 'apparatusId'));
    }

    public function liveFeed($competitionId, $apparatusId, Request $request)
    {
        $judgeId = $request->query('judge_id');
        $since = $request->query('since');
        $limit = (int) ($request->query('limit') ?? 30);

        $q = Score::with('athlete')->where('competition_id', $competitionId);

        if ($apparatusId !== 'all') {
            $q->where('apparatus_id', $apparatusId);
        }
        if ($judgeId) {
            $q->where('created_by_user_id', $judgeId);
        }
        if ($since) {
            $q->where('created_at', '>', $since);
        }

        $scores = $q->orderBy('created_at', 'asc')->limit($limit)->get();

        $items = $scores->map(function (Score $s) {
            $ath = $s->athlete;
            $eParts = collect([$s->e1_score, $s->e2_score, $s->e3_score, $s->e4_score])->filter(fn($v) => $v !== null);
            $eScore = $eParts->count() ? round($eParts->avg(), 3) : null;

            return [
                'id' => $s->id,
                'created_at' => $s->created_at->toIso8601String(),
                'athlete_name' => trim(($ath->first_name ?? '') . ' ' . ($ath->last_name ?? '')) ?: 'Athlete',
                'club_name' => $ath->club_name ?? null,
                'photo_url' => $ath->photo_url ? asset($ath->photo_url) : null,
                'd_score' => $s->d_score,
                'e_score' => $eScore,
                'nd' => $s->nd_score ?? 0,
                'penalty' => $s->penalty ?? 0,
                'final_score' => $s->final_score,
            ];
        });

        return response()->json([
            'items' => $items,
            'serverNow' => now()->toIso8601String(),
        ]);
    }

    /**
     * Vault leaderboard = Vault 1 + Vault 2 (if available).
     * Uses MAX(final_score) per apparatus per athlete (panel policy).
     */
    private function vaultCombinedRanking(int $competitionId)
    {
        $v1Id = Apparatus::idVault1();
        $v2Id = Apparatus::idVault2();

        if (!$v1Id) {
            return collect();
        }

        // Build combined totals with conditional aggregation
        $rows = Score::selectRaw(
            "
            athlete_id,
            MAX(CASE WHEN apparatus_id = ? THEN final_score ELSE 0 END) AS v1_total,
            MAX(CASE WHEN apparatus_id = ? THEN final_score ELSE 0 END) AS v2_total,
            (MAX(CASE WHEN apparatus_id = ? THEN final_score ELSE 0 END)
             + MAX(CASE WHEN apparatus_id = ? THEN final_score ELSE 0 END)) AS vault_total
            ",
            [$v1Id, $v2Id ?? 0, $v1Id, $v2Id ?? 0],
        )
            ->where('competition_id', $competitionId)
            ->groupBy('athlete_id')
            ->with('athlete')
            ->orderByDesc('vault_total')
            ->get();

        return $rows;
    }
}
