<?php

namespace App\Services;

use App\Enums\ChampionshipMatchStatusEnum;
use App\Models\Championship;
use App\Models\ChampionshipMatch;
use App\Models\ChampionshipMatchScore;
use App\Models\ChampionshipTeam;
use Illuminate\Support\Collection;
use Illuminate\Validation\ValidationException;

class ChampionshipService extends BaseService
{
    /**
     * Create a new match manually (admin)
     */
    public function createMatch(Championship $championship, array $data): ChampionshipMatch
    {
        return $this->transaction(function () use ($championship, $data) {
            $this->validateMatchTeams($championship, $data);

            return ChampionshipMatch::create([
                'championship_id' => $championship->id,
                'parent_match_id' => $data['parent_match_id'] ?? null,
                'group_id' => $data['group_id'] ?? null,
                'team_a_id' => $data['team_a_id'] ?? null,
                'team_b_id' => $data['team_b_id'] ?? null,
                'court_id' => $data['court_id'] ?? null,
                'match_date' => $data['match_date'] ?? null,
                'start_time' => $data['start_time'] ?? null,
                'end_time' => $data['end_time'] ?? null,
                'stage' => $data['stage'],
                'status' => ChampionshipMatchStatusEnum::SCHEDULED->value,
                'round' => $data['round'] ?? null,
                'sort_order' => $data['sort_order'] ?? 0,
            ]);
        });
    }

    /**
     * Update match details (admin)
     */
    public function updateMatch(ChampionshipMatch $match, array $data): ChampionshipMatch
    {
        return $this->transaction(function () use ($match, $data) {
            if (isset($data['team_a_id']) || isset($data['team_b_id'])) {
                $this->validateMatchTeams($match->championship, $data, $match->id);
            }

            $match->update(array_filter([
                'team_a_id' => $data['team_a_id'] ?? $match->team_a_id,
                'team_b_id' => $data['team_b_id'] ?? $match->team_b_id,
                'court_id' => $data['court_id'] ?? $match->court_id,
                'match_date' => $data['match_date'] ?? $match->match_date,
                'start_time' => $data['start_time'] ?? $match->start_time,
                'end_time' => $data['end_time'] ?? $match->end_time,
                'stage' => $data['stage'] ?? $match->stage,
                'round' => $data['round'] ?? $match->round,
                'sort_order' => $data['sort_order'] ?? $match->sort_order,
                'group_id' => $data['group_id'] ?? $match->group_id,
                'parent_match_id' => $data['parent_match_id'] ?? $match->parent_match_id,
            ], fn ($v) => $v !== null));

            return $match->fresh(['teamA', 'teamB', 'score', 'parentMatch', 'childMatches']);
        });
    }

    /**
     * Submit match result (admin) - set winner and scores
     */
    public function submitResult(ChampionshipMatch $match, int $winnerId, int $teamAScore, int $teamBScore): ChampionshipMatch
    {
        return $this->transaction(function () use ($match, $winnerId, $teamAScore, $teamBScore) {
            // Validate winner is one of the teams
            if ($winnerId !== $match->team_a_id && $winnerId !== $match->team_b_id) {
                throw ValidationException::withMessages([
                    'winner' => [__('Winner must be one of the match teams.')],
                ]);
            }

            // Save score
            ChampionshipMatchScore::updateOrCreate(
                ['match_id' => $match->id],
                ['team_a_score' => $teamAScore, 'team_b_score' => $teamBScore]
            );

            // Update match status
            $match->update([
                'status' => ChampionshipMatchStatusEnum::FINISHED->value,
                'winner_team_id' => $winnerId,
            ]);

            return $match->fresh(['score', 'teamA', 'teamB', 'winnerTeam']);
        });
    }

    /**
     * Create multiple matches in bulk (from the Matches Builder)
     */
    public function createBulkMatches(Championship $championship, array $data): array
    {
        return $this->transaction(function () use ($championship, $data) {
            $matchDate  = $data['match_date'] ?? null;
            $matches    = $data['matches'];
            $allTeamIds = array_unique(array_merge(
                array_column($matches, 'team_a_id'),
                array_column($matches, 'team_b_id'),
            ));

            // Validate all teams belong to this championship in a single query
            $validCount = ChampionshipTeam::query()
                ->where('championship_id', $championship->id)
                ->whereIn('id', $allTeamIds)
                ->count();

            if ($validCount < count($allTeamIds)) {
                throw ValidationException::withMessages([
                    'matches' => [__('One or more teams do not belong to this championship.')],
                ]);
            }

            // Detect same-team, duplicate pairings, and repeated teams in this payload
            $pairings = [];
            $teamUsage = [];
            foreach ($matches as $idx => $matchData) {
                $teamA = (int) $matchData['team_a_id'];
                $teamB = (int) $matchData['team_b_id'];

                if ($teamA === $teamB) {
                    throw ValidationException::withMessages([
                        'matches.' . $idx . '.team_b_id' => [__('A team cannot play against itself.')],
                    ]);
                }

                if (in_array($teamA, $teamUsage, true) || in_array($teamB, $teamUsage, true)) {
                    throw ValidationException::withMessages([
                        'matches' => [__('A team can only be used once on the selected date.')],
                    ]);
                }

                $teamUsage[] = $teamA;
                $teamUsage[] = $teamB;

                $pairKey = min($teamA, $teamB) . '-' . max($teamA, $teamB);
                if (in_array($pairKey, $pairings, true)) {
                    throw ValidationException::withMessages([
                        'matches' => [__('Duplicate match pairing detected.')],
                    ]);
                }
                $pairings[] = $pairKey;
            }

            if ($matchDate) {
                $existingTeamsOnDate = ChampionshipMatch::query()
                    ->where('championship_id', $championship->id)
                    ->whereDate('match_date', $matchDate)
                    ->where(function ($query) use ($allTeamIds) {
                        $query->whereIn('team_a_id', $allTeamIds)
                            ->orWhereIn('team_b_id', $allTeamIds);
                    })
                    ->exists();

                if ($existingTeamsOnDate) {
                    throw ValidationException::withMessages([
                        'match_date' => [__('One or more teams already have a match on the selected date.')],
                    ]);
                }
            }

            $created = [];
            foreach ($matches as $i => $matchData) {
                $created[] = ChampionshipMatch::create([
                    'championship_id' => $championship->id,
                    'team_a_id'       => (int) $matchData['team_a_id'],
                    'team_b_id'       => (int) $matchData['team_b_id'],
                    'match_date'      => $data['match_date'] ?? null,
                    'start_time'      => $matchData['start_time'] ?: null,
                    'end_time'        => $matchData['end_time'] ?: null,
                    'court_id'        => $matchData['court_id'] ?: null,
                    'stage'           => (int) $data['stage'],
                    'round'           => $data['round'] ?? null,
                    'sort_order'      => $i + 1,
                    'status'          => ChampionshipMatchStatusEnum::SCHEDULED->value,
                ]);
            }

            return $created;
        });
    }

    /**
     * Delete a match
     */
    public function deleteMatch(ChampionshipMatch $match): void
    {
        $this->transaction(function () use ($match) {
            $match->score()?->delete();
            $match->delete();
        });
    }

    /**
     * Get bracket data for a championship (grouped by stage and sorted)
     */
    public function getBracketData(Championship $championship): Collection
    {
        return $championship->matches()
            ->with(['teamA', 'teamB', 'score', 'winnerTeam', 'parentMatch'])
            ->orderBy('stage')
            ->orderBy('sort_order')
            ->get()
            ->groupBy('stage');
    }

    /**
     * Validate match teams belong to this championship and are not the same
     */
    private function validateMatchTeams(Championship $championship, array $data, ?int $excludeMatchId = null): void
    {
        $teamAId = $data['team_a_id'] ?? null;
        $teamBId = $data['team_b_id'] ?? null;

        if ($teamAId && $teamBId && $teamAId === $teamBId) {
            throw ValidationException::withMessages([
                'teams' => [__('Team A and Team B cannot be the same.')],
            ]);
        }

        // Validate teams belong to this championship
        if ($teamAId) {
            $exists = ChampionshipTeam::where('id', $teamAId)
                ->where('championship_id', $championship->id)
                ->exists();
            if (! $exists) {
                throw ValidationException::withMessages([
                    'team_a_id' => [__('Team A does not belong to this championship.')],
                ]);
            }
        }

        if ($teamBId) {
            $exists = ChampionshipTeam::where('id', $teamBId)
                ->where('championship_id', $championship->id)
                ->exists();
            if (! $exists) {
                throw ValidationException::withMessages([
                    'team_b_id' => [__('Team B does not belong to this championship.')],
                ]);
            }
        }
    }
}
