# from asyncio.streams import StreamReaderProtocol
from django.db import models

from . import SideStoreModel, Tournament, FederalMatchCategory
import uuid
from ..utils.extensions import format_seconds
from datetime import timedelta
from django.utils import timezone
from django.db.models import Count, Q

class GroupStage(SideStoreModel):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
    tournament = models.ForeignKey(Tournament, on_delete=models.SET_NULL, related_name='group_stages', null=True, blank=True)
    index = models.IntegerField(default=0)
    size = models.IntegerField(default=4)
    format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True)
    start_date = models.DateTimeField(null=True, blank=True)
    name = models.CharField(max_length=200, null=True, blank=True)
    step = models.IntegerField(default=0)

    def delete_dependencies(self):
        for team_registration in self.team_registrations.all():
            team_registration.delete_dependencies()
            team_registration.delete()
        for match in self.matches.all():
            match.delete_dependencies()
            match.delete()

    def __str__(self):
        return self.display_name()

    def save(self, *args, **kwargs):
        self.store_id = str(self.get_tournament_id())
        super().save(*args, **kwargs)

    def get_tournament_id(self):
        if self.tournament:
            return self.tournament.id
        else:
            return None

    def display_name(self):
        if self.name:
            return self.name
        else:
            if self.step == 0:
                return f"Poule {self.index + 1}"
            else:
                return f"Poule {self.index + 1} - " + f"Phase {self.step + 1}"

    def next_match(self, player_registration):
        matches = self.matches_for_registration(player_registration).filter(end_date__isnull=False).order_by('start_date')
        return matches[0]

    def matches_for_registration(self, player_registration):
        team_scores = TeamScore.objects.filter(player_registrations=player_registration)
        return [ts.match for ts in team_scores] # map(lambda ts: ts.match, team_scores)

    def live_group_stages(self):
        lgs = LiveGroupStage(self.id, self.display_name(), self.step, self.index)
        gs_teams = {}


        if self.step == 0:
            # init all teams
            for team_registration in self.team_registrations.all():
                if team_registration in gs_teams:
                    team = gs_teams[team_registration]
                else:
                    team = GroupStageTeam(team_registration)
                    gs_teams[team_registration] = team
        else:
            previous = self.tournament.get_previous_live_group_stages(self.step - 1)
            for gs in previous:
                team_registration = gs.teams[self.index].team_registration
                if team_registration in gs_teams:
                    team = gs_teams[team_registration]
                else:
                    team = GroupStageTeam(team_registration)
                    gs_teams[team_registration] = team

        # compute matches
        for match in self.matches.all():
            lgs.add_match(match)
            team_scores = match.team_scores.all()
            if len(team_scores) == 2:

                ts1 = team_scores[0]
                ts2 = team_scores[1]
                scores1 = ts1.scores()
                scores2 = ts2.scores()

                if match.winning_team_id:

                    for team_score in match.team_scores.all():
                        team_registration = team_score.team_registration
                        if team_registration in gs_teams:
                            team = gs_teams[team_registration]
                            if match.winning_team_id == team_registration.id:
                                team.wins += 1
                            else:
                                team.losses += 1

                    set_total1 = 0
                    set_total2 = 0
                    game_total1 = 0
                    game_total2 = 0

                    if len(scores1) == len(scores2):
                        for i in range(len(scores1)):
                            if scores1[i] > scores2[i]:
                                set_total1 += 1
                                if i == 2 and FederalMatchCategory.last_set_is_tie_break(match.format):
                                    game_total1 += 1
                                else:
                                    game_total1 += scores1[i]
                                    game_total2 += scores2[i]
                            else:
                                set_total2 += 1
                                if i == 2 and FederalMatchCategory.last_set_is_tie_break(match.format):
                                    game_total2 += 1
                                else:
                                    game_total1 += scores1[i]
                                    game_total2 += scores2[i]

                    if ts1.team_registration in gs_teams and ts2.team_registration in gs_teams:
                        team1 = gs_teams[ts1.team_registration]
                        team2 = gs_teams[ts2.team_registration]
                        team1.display_set_difference = FederalMatchCategory.max_number_of_sets(self.format) > 1
                        team2.display_set_difference = FederalMatchCategory.max_number_of_sets(self.format) > 1
                        team1.set_diff += set_total1 - set_total2
                        team2.set_diff += set_total2 - set_total1
                        team1.game_diff += game_total1 - game_total2
                        team2.game_diff += game_total2 - game_total1


        if len(self.matches.filter(end_date__isnull=False).all()) > 0:
            teams = sorted(
                gs_teams.values(),
                key=lambda team: (-(team.wins * 1000 + team.set_diff * 100 + team.game_diff), team.team_registration.id)
            )
        else:
            teams = sorted(gs_teams.values(), key=lambda team: team.position)

        for team in teams:
            lgs.add_team(team)
        return lgs

    def starts_soon(self):
        if self.start_date:
            early_start = self.start_date - timedelta(minutes=30)
            return timezone.now() > early_start
        else:
            return False

    def has_at_least_one_started_match(self):
        for match in self.matches.all():
            if match.start_date is not None and match.start_date <= timezone.now() and match.confirmed is True:
                return True
        return False

    def is_completed(self):
        # This will use the prefetched matches if available
        matches = list(self.matches.all())
        return len(matches) > 0 and all(match.end_date is not None for match in matches)

class LiveGroupStage:
    def __init__(self, id, title, step, index):
        self.id = str(id)
        self.title = title
        self.teams = []
        self.start = None
        self.end = None
        self.step = step
        self.index = index

    def add_match(self, match):
        if self.start and match.start_date and match.start_date < self.start:
            self.start = match.start_date
        elif not self.start:
            self.start = match.start_date
        if self.end and match.end_date and match.end_date > self.end:
            self.end = match.end_date
        elif not self.end:
            self.end = match.end_date

    def add_team(self, team):
        self.teams.append(team)

    def formatted_duration(self):
        if self.start and self.end:
            return format_seconds((self.end - self.start).total_seconds())
        else:
            return ""

    def to_dict(self):
        return {
            "id": self.id,
            "title": self.title,
            "teams": [team.to_dict() for team in self.teams],
            "duration": self.formatted_duration(),
            "started": self.started()
        }

    def started(self):
        if self.start is None:
            return False
        return self.start < timezone.now()

class GroupStageTeam:
    def __init__(self, team_registration):
        self.names = team_registration.shortened_team_names()
        if team_registration.group_stage_position:
            self.position = team_registration.group_stage_position
        else:
            self.position = 0
        self.wins = 0
        self.losses = 0
        self.diff = 0
        self.set_diff = 0
        self.game_diff = 0
        self.display_set_difference = False
        if team_registration.player_registrations.count() == 0:
            weight = None
        else:
            weight = team_registration.weight
        self.weight = weight
        self.team_registration = team_registration
        self.qualified = team_registration.qualified

    def wins_losses(self):
        return f"{self.wins}/{self.losses}"

    def match_count(self):
        return self.wins + self.losses

    def formatted_diff(self):
        if self.display_set_difference:
            return f"{self.formatted_set_diff()} {self.sets_word_localized()}" + " / " + f"{self.formatted_game_diff()} {self.games_word_localized()}"
        else:
            return self.formatted_game_diff()

    def sets_word_localized(self):
        if abs(self.set_diff) > 1:
            return "sets"
        else:
            return "set"

    def games_word_localized(self):
        if abs(self.game_diff) > 1:
            return "jeux"
        else:
            return "jeu"

    def formatted_set_diff(self):
        if self.set_diff >= 0:
            return f"+{self.set_diff}"
        else:
            return f"{self.set_diff}"

    def formatted_game_diff(self):
        if self.game_diff >= 0:
            return f"+{self.game_diff}"
        else:
            return f"{self.game_diff}"

    def to_dict(self):
        return {
            "names": self.names,
            "win_loss": self.wins_losses(),
            "diff": self.formatted_diff(),
            "weight": self.weight,
            "match_count": self.match_count(),
            "qualified": self.qualified,
        }
