from django.db import models
# from tournaments.models import group_stage
from . import SideStoreModel, Round, GroupStage, FederalMatchCategory
from django.utils import timezone, formats
from datetime import datetime, timedelta

import uuid

from ..utils.extensions import format_seconds

class Match(SideStoreModel):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True)
    round = models.ForeignKey(Round, null=True, blank=True, on_delete=models.SET_NULL, related_name='matches')
    group_stage = models.ForeignKey(GroupStage, null=True, blank=True, on_delete=models.SET_NULL, related_name='matches')
    name = models.CharField(max_length=200, null=True, blank=True)
    start_date = models.DateTimeField(null=True, blank=True)
    end_date = models.DateTimeField(null=True, blank=True)
    index = models.IntegerField(default=0)
    #order = models.IntegerField(default=0)
    format = models.IntegerField(default=FederalMatchCategory.NINE_GAMES, choices=FederalMatchCategory.choices, null=True, blank=True)
    #court = models.CharField(max_length=50, null=True, blank=True)
    serving_team_id = models.UUIDField(null=True, blank=True)
    winning_team_id = models.UUIDField(null=True, blank=True)
    losing_team_id = models.UUIDField(null=True, blank=True)
    #broadcasted = models.BooleanField(default=False)
    disabled = models.BooleanField(default=False)
    court_index = models.IntegerField(null=True, blank=True)
    confirmed = models.BooleanField(default=False)

    def delete_dependencies(self):
        for team_score in self.team_scores.all():
            # team_score.delete_dependencies()
            team_score.delete()

    def __str__(self):
        names = " / ".join(self.player_names())
        if names:
            return f"{self.stage_name()} : {names}"
        else:
            return f"{self.stage_name()}"

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

    def tournament(self):
        if self.round:
            return self.round.tournament
        elif self.group_stage:
            return self.group_stage.tournament
        return None

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

    def court_name(self, index):
        club = None
        if self.tournament().event:
            club = self.tournament().event.club

        if club:
            return club.court_name(index)
        elif index is not None:
            return f"Terrain {index + 1}"
        return ""

    def backup_name(self):
        items = []
        if self.round:
            items.append(self.round.name())
            if self.round.index > 0:
                items.append(f" #{self.index_in_round() + 1}")
        elif self.group_stage:
            items.append(self.group_stage.name())
            items.append(f"Match #{self.index + 1}")
        return " ".join(items)

    def summon_stage_name(self):
        if self.round:
            return self.round.name()
        elif self.group_stage:
            return "Poule"
        else:
            return '--'

    def stage_name(self):
        if self.name:
            return self.name
        elif self.round:
            return self.round.name()
        elif self.group_stage:
            return self.group_stage.display_name()
        else:
            return '--'

    def get_previous_round(self):
        # Calculate the next index
        if self.round is None:
            return None

        previous_index = self.round.index + 1

        # Check if the next index is within the bounds of the rounds array
        return self.round.tournament.rounds.filter(index=previous_index, parent = self.round.parent).first()

    def get_loser_previous_round(self):
        # Calculate the next index
        if self.round is None:
            return None

        previous_index = self.round.index + 1

        previous_round = None
        # Check if the next index is within the bounds of the rounds array
        previous_round = self.round.tournament.rounds.filter(index=previous_index, parent = self.round.parent).first()
        if previous_round is None and self.round.parent is not None:
            previous_round = self.round.tournament.rounds.filter(id=self.round.parent.id).first()
            return previous_round
        return None

    def precedent_match(self, top):
        previous_round = self.get_previous_round()
        #print(previous_round)
        match = None
        if previous_round:
            matches = previous_round.matches.all()  # Retrieve the QuerySet
            match_index = self.index * 2 + 1
            if top == False:
                match_index += 1
            match = matches.filter(index=match_index).first()
        return match

    def loser_precedent_match(self, top):
        previous_round = self.get_loser_previous_round()
        match = None
        if previous_round:
            matches = previous_round.matches.all()  # Retrieve the QuerySet
            match_index = self.index * 2 + 1
            if top == False:
                match_index += 1
            match = matches.filter(index=match_index).first()
        return match

    def computed_name(self):
        if self.round and self.round.parent is None:
            return self.backup_name()
        title = self.name if self.name else self.backup_name()
        return title

    def index_in_round(self):
        if self.round is not None:
            matches = sorted(
                self.round.matches.filter(disabled=False),
                key=lambda match: match.index
            )
            try:
                index_of_self = matches.index(self)
                return index_of_self
            except ValueError:
                # `self` is not in `matches`, return self.index as a fallback
                return self.index

        return self.index


    def player_names(self):
        return map(lambda ts: ts.player_names(), self.team_scores.all())

    def default_live_team(self, names):
        image = None
        weight= None
        is_winner = False
        scores = []
        walk_out = None
        team = Team(None, image, names, scores, weight, is_winner, walk_out, False)
        return team

    def is_ready(self):
        return self.team_scores.count() == 2

    def live_teams(self):
        #print('player names from match')
        ##return map(lambda ts: ts.player_names(), self.team_scores.all())
        # List to hold the names of the teams
        teams = []

        # Check if team scores exist
        team_scores = list(self.team_scores.all())
        previous_top_match = self.precedent_match(True)
        previous_bottom_match = self.precedent_match(False)
        loser_top_match = self.loser_precedent_match(True)
        loser_bottom_match = self.loser_precedent_match(False)

        if len(team_scores) == 0:

            if (self.round and self.round.parent is None and self.round.tournament.rounds.filter(parent__isnull=True, group_stage_loser_bracket=False).count() - 1 == self.round.index):
                names = ["Qualifié"]
                team = self.default_live_team(names)
                teams.append(team)
                names = ["Qualifié"]
                team = self.default_live_team(names)
                teams.append(team)
                return teams
            if (self.group_stage):
                names = ["Équipe de poule"]
                team = self.default_live_team(names)
                teams.append(team)
                names = ["Équipe de poule"]
                team = self.default_live_team(names)
                teams.append(team)
                return teams
            elif self.round and self.round.parent:
                if loser_top_match:
                    names = [f"Perdant {loser_top_match.computed_name()}"]
                    team = self.default_live_team(names)
                    teams.append(team)
                if loser_bottom_match:
                    names = [f"Perdant {loser_bottom_match.computed_name()}"]
                    team = self.default_live_team(names)
                    teams.append(team)
                if previous_top_match:
                    names = [f"Gagnant {previous_top_match.computed_name()}"]
                    if previous_top_match.disabled:
                        names = ["Perdant tableau principal"]
                    team = self.default_live_team(names)
                    teams.append(team)
                if previous_bottom_match:
                    names = [f"Gagnant {previous_bottom_match.computed_name()}"]
                    if previous_bottom_match.disabled:
                        names = ["Perdant tableau principal"]
                    team = self.default_live_team(names)
                    teams.append(team)
            elif self.round and self.round.parent is None:
                if previous_top_match:
                    names = [f"Gagnant {previous_top_match.computed_name()}"]
                    team = self.default_live_team(names)
                    teams.append(team)
                if previous_bottom_match:
                    names = [f"Gagnant {previous_bottom_match.computed_name()}"]
                    team = self.default_live_team(names)
                    teams.append(team)
        elif len(team_scores) == 1:
            # Only one team score, handle missing one
            existing_team = team_scores[0].live_team(self)
            if (self.group_stage):
                teams.append(existing_team)
                names = ["Équipe de poule"]
                team = self.default_live_team(names)
                teams.append(team)
            elif self.round:
                if loser_top_match and loser_top_match.disabled == False and loser_top_match.end_date is None:
                    names = [f"Perdant {loser_top_match.computed_name()}"]
                    team = self.default_live_team(names)
                    teams.append(team)
                    teams.append(existing_team)
                elif loser_bottom_match:
                    names = [f"Perdant {loser_bottom_match.computed_name()}"]
                    team = self.default_live_team(names)
                    teams.append(existing_team)
                    teams.append(team)
                elif previous_top_match and previous_top_match.disabled == False and previous_top_match.end_date is None:
                    names = [f"Gagnant {previous_top_match.computed_name()}"]
                    team = self.default_live_team(names)
                    teams.append(team)
                    teams.append(existing_team)
                elif previous_bottom_match:
                    names = [f"Gagnant {previous_bottom_match.computed_name()}"]
                    team = self.default_live_team(names)
                    teams.append(existing_team)
                    teams.append(team)
                elif (self.round.parent is None and self.round.tournament.rounds.filter(parent__isnull=True, group_stage_loser_bracket=False).count() - 1 == self.round.index):
                    match_index_within_round = self.index - (int(2 ** self.round.index) - 1)
                    names = ["Qualifié"]
                    team = self.default_live_team(names)
                    if match_index_within_round < int(2 ** self.round.index) / 2:
                        teams.append(existing_team)
                        teams.append(team)
                    else:
                        teams.append(team)
                        teams.append(existing_team)

        elif len(team_scores) == 2:
            # Both team scores present
            teams.extend([team_score.live_team(self) for team_score in team_scores])

            if self.round is not None and self.round.parent is None:
                pos1 = team_scores[0].team_registration.bracket_position if hasattr(team_scores[0], 'team_registration') and team_scores[0].team_registration else None
                pos2 = team_scores[1].team_registration.bracket_position if hasattr(team_scores[1], 'team_registration') and team_scores[1].team_registration else None
                if pos1 is not None and pos2 is not None and pos1 // 2 == self.index and pos2 // 2 == self.index:
                    if pos1 > pos2:
                        teams = [team_scores[1].live_team(self), team_scores[0].live_team(self)]
                    else:
                        teams = [team_scores[0].live_team(self), team_scores[1].live_team(self)]

        else:
            teams.extend([team_score.live_team(self) for team_score in team_scores if team_score.walk_out != 1])

        return teams

    def local_start_date(self):
        timezone = self.tournament().timezone()
        return self.start_date.astimezone(timezone)

    def formatted_start_date(self):
        if self.start_date:
            local_start = self.local_start_date()
            return formats.date_format(local_start, format='H:i')
        else:
            return ''

    def time_indication(self):
        if self.end_date:
            if self.start_date:
                return self.magic_duration()
            else:
                return ''
        elif self.start_date:
            if self.started():
                if self.confirmed:
                    return self.formatted_duration()
                else:
                    return 'À suivre'
            else:
                # timezoned_datetime = timezone.localtime(self.start_date)
                timezone = self.tournament().timezone()
                local_start = self.start_date.astimezone(timezone)
                time_format ='l H:i'
                if self.tournament().day_duration >= 7:
                    time_format = 'l d M à H:i'
                if self.confirmed:
                    return formats.date_format(local_start, format=time_format)
                else:
                    return f"Estimée : {formats.date_format(local_start, format=time_format)}"
        else:
            return 'À venir...'

    def magic_duration(self):
        seconds = (self.end_date - self.start_date).total_seconds()
        average_duration = self.average_seconds_duration()

        if (average_duration / 2) > seconds or seconds > (average_duration * 2):
            seconds = average_duration

        return format_seconds(seconds)

    def average_seconds_duration(self):
        return 3 * 60 * self.total_number_of_games()

    def total_number_of_games(self):
        games = 0.0
        for team_score in self.team_scores.all():
            games += team_score.estimated_number_of_games()
        return games

    def current_duration(self):
        if self.confirmed and self.start_date:
            if self.end_date:
                return (self.end_date - self.start_date).total_seconds()
            else:
                current = (timezone.now() - self.start_date).total_seconds()
                if self.total_number_of_games() > 0 and current < self.average_seconds_duration() * 4:
                    return current
                elif self.total_number_of_games() == 0 and current < 3 * 60 * 60:
                    return current
                else:
                    return None
                # print(current)
                # print(self.average_seconds_duration())
                # print('*******')
                # if current < self.average_seconds_duration() * 4:
                #     return current
                # else:
                #     return None
                # return (timezone.now() - self.start_date).total_seconds()
        else:
            return None

    def started(self):
        if self.confirmed:
            if self.end_date:
                return True
            elif self.start_date:
                return timezone.now() > self.start_date
        return False

    def should_appear(self):
        if self.disabled is True:
            return False
        return self.team_scores.count() > 0

        # elif self.group_stage is None:
        #     if len(self.team_scores.all()) == 2:
        #         return True
        #     else:
        #         return (self.start_date or self.end_date) and len(self.team_scores.all()) > 0
        # else:
        #     return len(self.team_scores.all()) > 0

    def formatted_duration(self):

        seconds = self.current_duration()
        if seconds is None:
            return ''

        if seconds > 0:
            return format_seconds(seconds)
            # _hours = int(_seconds / 3600)
            # _minutes = int((_seconds % 3600) / 60)
            # return f"{_hours:02d}h{_minutes:02d}min"
        else :
            seconds = seconds * -1
            return format_seconds(seconds)
            # _hours = int(_seconds / 3600)
            # _minutes = int((_seconds % 3600) / 60)
            # return f"{_hours:02d}h{_minutes:02d}min"

    def live_match(self):
        title = self.computed_name()
        date = self.formatted_start_date()
        time_indication = self.time_indication()
        court = self.court_name(self.court_index)
        group_stage_name = None
        bracket_name = None
        if self.group_stage:
            group_stage_name = self.group_stage.display_name()
        else:
            bracket_name = "Match #" + f"{self.index_in_round() + 1}"

        ended = self.end_date is not None
        live_format = "Format " + FederalMatchCategory(self.format).format_label_short

        livematch = LiveMatch(self.index, title, date, time_indication, court, self.started(), ended, group_stage_name, live_format, self.start_date, self.court_index, self.disabled, bracket_name)

        for team in self.live_teams():
            livematch.add_team(team)

        return livematch

    def sorted_team_scores(self):
        if self.group_stage:
            return self.team_scores.order_by('team_registration__group_stage_position')
        else:
            return self.team_scores.order_by('team_registration__bracket_position')

    # def non_null_start_date(self):
    #     if self.start_date:
    #         return self.start_date
    #     else:
    #         return timezone.now() + timedelta(days=7)

    # def sort_value(self):
    #     sort_score = 0
    #     if self.round.index:
    #         sort_score += 1 / (self.round.index + 1) * 1000 ** 2
    #     if self.group_stage.index:
    #         sort_score += 1 / (self.group_stage.index + 1) * 1000
    #     sort_score += self.index
    #     print(sort_score)
    #     return sort_score

class Team:
    def __init__(self, id, image, names, scores, weight, is_winner, walk_out, is_lucky_loser):
        # print(f"image = {image}, names= {names}, scores ={scores}, weight={weight}, win={is_winner}")
        if id is not None:
            self.id = str(id)
        else:
            self.id = None
        self.image = image
        self.names = names
        self.scores = scores
        self.weight = weight
        self.is_winner = is_winner
        self.walk_out = walk_out
        self.is_lucky_loser = is_lucky_loser

    def is_walk_out(self):
        return self.walk_out is not None

    def to_dict(self):
        return {
            "image": self.image,
            "names": self.names,
            "scores": self.scores,
            "weight": self.weight,
            "is_winner": self.is_winner,
            "walk_out": self.walk_out,
            "is_walk_out": self.is_walk_out(),
            "is_lucky_loser": self.is_lucky_loser,
            "id": self.id
        }

class LiveMatch:
    def __init__(self, index, title, date, time_indication, court, started, ended, group_stage_name, format, start_date, court_index, disabled, bracket_name):
        self.index = index
        self.title = title
        self.date = date
        self.teams = []
        self.time_indication = time_indication
        self.court = court
        self.started = started
        self.ended = ended
        self.has_walk_out = False
        self.group_stage_name = group_stage_name
        self.format = format
        self.disabled = disabled
        self.start_date = start_date
        self.court_index = court_index
        self.bracket_name = bracket_name

    def add_team(self, team):
        self.teams.append(team)
        if team.is_walk_out() is True:
            self.has_walk_out = True

    def to_dict(self):
        return {
            "index": self.index,
            "title": self.title,
            "date": self.date,
            "teams": [team.to_dict() for team in self.teams],
            "time_indication": self.time_indication,
            "court": self.court,
            "started": self.started,
            "ended": self.ended,
            "has_walk_out": self.has_walk_out,
            "group_stage_name": self.group_stage_name,
            "format": self.format,
            "disabled": self.disabled,
            "court_index": self.court_index,
            "bracket_name": self.bracket_name
        }

    def show_time_indication(self):
        for team in self.teams:
            if team.is_walk_out() and len(team.scores) == 0:
                return False
        return True

    def should_show_scores(self):
        for team in self.teams:
            if len(team.scores) > 0:
                return True
        return False
