import enum

from django.contrib.postgres import fields
from django.db import models


class MainappObjects(list):
    def __init__(self, **kwargs):
        super().__init__(list(kwargs.keys()))
        pass

    @staticmethod
    def get(**kwargs):
        return MainappObjects(**kwargs)

    @staticmethod
    def filter(**kwargs):
        return MainappObjects(**kwargs)

    @staticmethod
    def order_by(*args):
        return MainappObjects(**{i: args[i] for i in range(len(args))})


class MainappModel:
    id = None
    objects = MainappObjects
    DoesNotExist = models.ObjectDoesNotExist
    

class MainappForeignKey(models.ForeignKey):
    to_json = None


class Sport(MainappModel, models.Model):
    name = models.CharField(max_length=20, unique=True)
    clean_name = models.CharField(max_length=20, unique=True)
    display_sets = models.BooleanField(default=False)
    points = fields.JSONField(default=dict)
    enabled = models.BooleanField(default=False)

    def __str__(self):
        return f'#{self.id} {self.name}'


class Country(MainappModel, models.Model):
    name = models.CharField(max_length=30, unique=True)
    clean_name = models.CharField(max_length=30, unique=True)
    short_name = models.CharField(max_length=5)
    source_names = fields.JSONField(default=dict)

    def __str__(self):
        return f'#{self.id} {self.name}'


class GenderChoice(enum.Enum):
    MALE = 1
    FEMALE = 2


class League(MainappModel, models.Model):
    sport = MainappForeignKey(Sport, on_delete=models.CASCADE)
    country = MainappForeignKey(Country, on_delete=models.CASCADE)

    name = models.CharField(max_length=30, null=False)
    clean_name = models.CharField(max_length=30, null=False)
    gender = models.IntegerField(choices=((c.value, c) for c in GenderChoice), null=True, default=None)
    degree = models.IntegerField(null=False, default=0)

    schedule_url = models.CharField(max_length=200, null=True, default=None)
    ranking_url = models.CharField(max_length=200, null=True, default=None)
    channel_url = models.CharField(max_length=200, null=True, default=None)

    mdays = models.IntegerField(null=True, default=None)
    current_mday = models.IntegerField(null=True, default=None)
    matches_by_mday = models.IntegerField(null=True, default=None)
    team_count = models.IntegerField(null=False, default=0)

    rounds = fields.ArrayField(models.CharField(max_length=30), null=True, default=None)
    groups = fields.ArrayField(models.CharField(max_length=30), null=True, default=None)
    points = fields.JSONField(null=True, default=None)
    promotions = fields.JSONField(null=True, default=None)

    images = fields.JSONField(null=True, default=None)
    schedule = fields.JSONField(null=True, default=None)

    tags = fields.ArrayField(models.CharField(max_length=30), null=True, default=None)
    clean_tags = fields.ArrayField(models.CharField(max_length=30), null=True, default=None)
    news_count = models.IntegerField(null=False, default=0)

    error = models.CharField(max_length=100, null=True, default=None)
    trace = models.CharField(max_length=1000, null=True, default=None)
    
    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['sport', 'name', 'gender'],
                name="custom_unique_league"
            )
        ]

    @classmethod
    def navbar_sports(cls):
        leagues = cls.objects.order_by('sport_id', 'country_id', 'degree')
        sports = list()
        for league in leagues:
            if not sports or league.sport.id != sports[-1].id:
                sports.append(league.sport)
            if not hasattr(sports[-1], 'countries'):
                sports[-1].countries = [league.country]
            if league.country.id != sports[-1].countries[-1].id:
                sports[-1].countries.append(league.country)
            if not hasattr(sports[-1].countries[-1], 'leagues'):
                sports[-1].countries[-1].leagues = [league]
            else:
                sports[-1].countries[-1].leagues.append(league)
        return sports

    @property
    def to_json(self):
        return {
            'id': self.id,
            'name': self.name,
            'clean_name': self.clean_name,
            'images': self.images
        }

    def __str__(self):
        return f'#{self.id} {self.name}'


class Team(MainappModel, models.Model):
    sport = MainappForeignKey(Sport, on_delete=models.CASCADE)
    country = MainappForeignKey(Country, on_delete=models.CASCADE)

    name = models.CharField(max_length=30, null=False)
    clean_name = models.CharField(max_length=30, null=False)

    short_name = models.CharField(max_length=30, null=True, default=None)
    long_name = models.CharField(max_length=100, null=True, default=None)
    gender = models.IntegerField(choices=((c.value, c) for c in GenderChoice), null=True, default=None)

    names = fields.JSONField(null=True, default=None)
    url = models.CharField(max_length=200, null=True, default=None)
    images = fields.JSONField(null=True, default=None)

    tags = fields.ArrayField(models.CharField(max_length=50), null=True, default=None)
    clean_tags = fields.ArrayField(models.CharField(max_length=50), null=True, default=None)
    news_count = models.IntegerField(null=False, default=0)

    error = models.CharField(max_length=100, null=True, default=None)
    trace = models.CharField(max_length=1000, null=True, default=None)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['sport', 'name', 'gender'],
                name="custom_unique_team"
            )
        ]

    @property
    def to_json(self):
        return {
            'id': self.id,
            'name': self.name,
            'clean_name': self.clean_name,
            'short_name': self.short_name,
            'long_name': self.long_name,
            'images': self.images
        }

    def __str__(self):
        return f'#{self.id} {self.name}'


class RoleChoice(enum.Enum):
    GOALKEEPER = 1
    DEFENDER = 2
    MIDFIELDER = 3
    ATTACKER = 4


class FootChoice(enum.Enum):
    RIGHT = 1
    LEFT = 2
    BOTH = 3


class Player(MainappModel, models.Model):
    sport = MainappForeignKey(Sport, on_delete=models.CASCADE)
    team = MainappForeignKey(Team, on_delete=models.CASCADE, null=True, default=None)
    country1 = MainappForeignKey(Country, related_name='country1', on_delete=models.CASCADE, null=True, default=None)
    country2 = MainappForeignKey(Country, related_name='country2', on_delete=models.CASCADE, null=True, default=None)

    full_name = models.CharField(max_length=100, unique=True)
    clean_name = models.CharField(max_length=100, unique=True)

    first_name = models.CharField(max_length=50, null=True, default=None)
    last_name = models.CharField(max_length=50, null=True, default=None)
    birth_date = models.DateField(null=True, default=None)
    image = models.CharField(max_length=200, null=True, default=None)

    number = models.IntegerField(null=True, default=None)
    role = models.IntegerField(choices=((c.value, c) for c in RoleChoice), null=True, default=None)
    position = models.CharField(max_length=50, null=True, default=None)
    age = models.IntegerField(null=True, default=None)
    size = models.IntegerField(null=True, default=None)
    foot = models.IntegerField(choices=((c.value, c) for c in FootChoice), null=True, default=None)

    contract_end = models.DateField(null=True, default=None)
    price = models.IntegerField(null=True, default=None)

    error = models.CharField(max_length=100, null=True, default=None)
    trace = models.CharField(max_length=1000, null=True, default=None)

    @property
    def to_json(self):
        return {
            'id': self.id,
            'full_name': self.full_name,
            'first_name': self.first_name,
            'last_name': self.last_name
        }


class LegChoice(enum.Enum):
    EMPTY = 0
    FIRST = 1
    SECOND = 2
    REPLAY = 3


class StatusChoice(enum.Enum):
    FIRST_TIME = 1
    HALF_TIME = 2
    SECOND_TIME = 3
    FIRST_EXTRA = 4
    HALF_EXTRA = 5
    SECOND_EXTRA = 6
    SHOOTOUT = 7
    WAIT_SCORES = 8
    OVER = 9
    POSTPONED = 10
    CANCELLED = 11


class WinnerChoice(enum.Enum):
    HOME = 1
    AWAY = 2
    DRAW = 3


class ExtraTimeChoice(enum.Enum):
    EXTRA_TIME = 1
    SHOOTOUT = 2


class Match(MainappModel, models.Model):
    league = MainappForeignKey(League, on_delete=models.CASCADE)
    team_home = MainappForeignKey(Team, related_name='th', on_delete=models.CASCADE, null=True, default=None)
    team_away = MainappForeignKey(Team, related_name='ta', on_delete=models.CASCADE, null=True, default=None)
    player_home = MainappForeignKey(Player, related_name='ph', on_delete=models.CASCADE, null=True, default=None)
    player_away = MainappForeignKey(Player, related_name='pa', on_delete=models.CASCADE, null=True, default=None)

    mday = models.IntegerField(null=True, default=None)
    round = models.CharField(max_length=100, null=True, default=None)
    leg = models.IntegerField(choices=((c.value, c) for c in LegChoice), null=True, default=None)
    sign = models.CharField(max_length=1000, unique=True, null=False)

    base_url = models.CharField(max_length=200, null=True, default=None)
    score_url = models.CharField(max_length=200, null=True, default=None)
    live_url = models.CharField(max_length=200, null=True, default=None)
    tv_channels = fields.ArrayField(models.CharField(max_length=50), null=True, default=None)

    mday_id = models.IntegerField(null=True, default=None)
    status = models.IntegerField(choices=((c.value, c) for c in StatusChoice), null=True, default=None)
    minute = models.CharField(max_length=10, null=True, default=None)
    start_date = models.DateTimeField(null=True, default=None)
    end_date = models.DateTimeField(null=True, default=None)
    home_score = models.IntegerField(null=True, default=None)
    away_score = models.IntegerField(null=True, default=None)
    sets_score = fields.JSONField(null=True, default=None)
    winner = models.IntegerField(choices=((c.value, c) for c in WinnerChoice), null=True, default=None)
    extra_time = models.IntegerField(choices=((c.value, c) for c in ExtraTimeChoice), null=True, default=None)
    shootout_home = models.IntegerField(null=True, default=None)
    shootout_away = models.IntegerField(null=True, default=None)

    squad = fields.JSONField(null=True, default=None)
    events = fields.JSONField(null=True, default=None)
    stats = fields.JSONField(null=True, default=None)
    live = fields.JSONField(null=True, default=None)
    last_event = fields.JSONField(null=True, default=None)
    last_event_date = models.DateTimeField(null=True, default=None)

    error = models.CharField(max_length=100, null=True, default=None)
    trace = models.CharField(max_length=1000, null=True, default=None)

    @property
    def to_json(self):
        return {
            'id': self.id,
            'league': self.league.to_json,
            'team_home': self.team_home.to_json if self.team_home else None,
            'team_away': self.team_away.to_json if self.team_away else None,
            'player_home': self.player_home.to_json if self.player_home else None,
            'player_away': self.player_away.to_json if self.player_away else None,
            'mday': self.mday,
            'round': self.round,
            'leg': self.leg,
            'status': self.status,
            'minute': self.minute,
            'start_date': self.start_date,
            'home_score': self.home_score,
            'away_score': self.away_score,
            'sets_score': self.sets_score,
            'winner': self.winner,
            'extra_time': self.extra_time,
            'shootout_home': self.shootout_home,
            'shootout_away': self.shootout_away,
        }


class Source(MainappModel, models.Model):
    sport = MainappForeignKey(Sport, on_delete=models.CASCADE)

    name = models.CharField(max_length=30, null=False)
    clean_name = models.CharField(max_length=30, null=False)

    image_name = models.CharField(max_length=30, null=False)
    big_image_name = models.CharField(max_length=30, null=False)

    feed_url = models.CharField(max_length=200, null=False)

    error = models.CharField(max_length=100, null=True, default=None)
    trace = models.CharField(max_length=1000, null=True, default=None)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=['sport', 'name'],
                name="custom_unique_source"
            )
        ]

    def __str__(self):
        return f'#{self.id} {self.name}'

    @property
    def to_json(self):
        return {
            'id': self.id,
            'name': self.name,
            'clean_name': self.clean_name,
            'image_name': self.image_name,
            'big_image_name': self.big_image_name
        }


class News(MainappModel, models.Model):
    source = MainappForeignKey(Source, on_delete=models.CASCADE)
    league = MainappForeignKey(League, on_delete=models.CASCADE, null=True, default=None)
    team = MainappForeignKey(Team, on_delete=models.CASCADE, null=True, default=None)

    title = models.CharField(max_length=500, unique=True)
    clean_title = models.CharField(max_length=500, unique=True)
    link = models.CharField(max_length=500, unique=True)
    pub_date = models.DateTimeField(null=True, default=None)
    description = models.CharField(max_length=1000, null=True, default=None)
    image = models.CharField(max_length=500, null=True, default=None)

    teaser = models.CharField(max_length=2000, null=True, default=None)
    author = models.CharField(max_length=100, null=True, default=None)
    content = fields.ArrayField(models.CharField(max_length=5000), null=True, default=None)
    redirect = models.CharField(max_length=500, null=True, default=None)

    haystack = models.CharField(max_length=2000, null=True, default=None)
    tags = fields.ArrayField(models.CharField(max_length=50), null=True, default=None)
    clean_tags = fields.ArrayField(models.CharField(max_length=50), null=True, default=None)

    error = models.CharField(max_length=100, null=True, default=None)
    trace = models.CharField(max_length=1000, null=True, default=None)

    def __str__(self):
        return f'#{self.id} {self.title}'

    @property
    def to_json(self):
        # noinspection PyTypeChecker
        return {
            'id': self.id,
            'title': self.title,
            'clean_title': self.clean_title,
            'pub_date': self.pub_date,
            'description': self.description,
            'link': self.link,
            'image': self.image,
            'source': self.source.to_json,
            'tags': list(zip(self.tags, self.clean_tags)),
            'redirect': self.redirect
        }


class Utils:
    SYMBOLS = {
        'á': 'a', 'Á': 'a', 'à': 'e', 'À': 'a', 'â': 'a', 'Â': 'a', 'ä': 'a', 'Ä': 'a', 'ã': 'a',
        'ç': 'c',
        'é': 'e', 'É': 'e', 'è': 'e', 'È': 'e', 'ê': 'e', 'Ê': 'e', 'ë': 'e', 'Ë': 'e',
        'í': 'i', 'Í': 'i', 'ì': 'i', 'Ì': 'i', 'î': 'i', 'Î': 'i', 'ï': 'i', 'Ï': 'i',
        'ñ': 'n',
        'ó': 'o', 'Ó': 'o', 'ò': 'o', 'Ò': 'o', 'ô': 'o', 'Ô': 'o', 'ö': 'o', 'Ö': 'o', 'ø': 'o',
        'ú': 'u', 'Ú': 'u', 'ù': 'u', 'Ù': 'u', 'û': 'u', 'Û': 'u', 'ü': 'u', 'Ü': 'u'
    }

    @classmethod
    def sanitize(cls, s):
        return ''.join([
            c if ord('a') <= ord(c) <= ord('z') or ord('0') <= ord(c) <= ord('9')
            else chr(ord(c) - ord('A') + ord('a')) if ord('A') <= ord(c) <= ord('Z')
            else cls.SYMBOLS.get(c, '-')
            for c in s
        ]) if s is not None else None
