import logging
import uuid
from datetime import timedelta
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.encoding import smart_str
from django.utils.translation import gettext_lazy as _
from model_utils.models import TimeStampedModel, TimeFramedModel


from events.managers import EventsManager

logger = logging.getLogger(__name__)


class Event(TimeFramedModel):
    """
    Event database model holding the event necessary information.
    It based on TimeFramedModel which is a model with start and end date.
    Gets a new UUID on creation for easier identification through the systems.
    """
    uid = models.UUIDField(blank=True, null=True, unique=True, verbose_name=_('UUID'))
    name = models.CharField(max_length=100, null=False, blank=False, unique=True, verbose_name=_('Name'))
    description = models.TextField(blank=True, null=True, verbose_name=_('Description'))
    sub_description = models.TextField(blank=True, null=True, verbose_name=_('Sub Description'))

    longitude = models.FloatField(blank=True, null=True, verbose_name=_('Longitude'))
    latitude = models.FloatField(blank=True, null=True, verbose_name=_('Latitude'))

    bank_account_number = models.CharField(max_length=50, blank=True, null=True, verbose_name=_('Bank account number'))
    bank_account_name = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('Bank account name'))
    bank_account_bank_code = models.CharField(max_length=10, blank=True, null=True,
                                              verbose_name=_('Bank account bank code'))
    fee = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True, verbose_name=_('Fee'))
    variable_symbol_prefix = models.CharField(max_length=10, blank=True, null=True,
                                              verbose_name=_('Variable symbol prefix'))

    # whether the registration requires confirmation email or their will be created inmediately after the form submit
    registrations_require_confirmation = models.BooleanField(default=False, verbose_name=_('Require registration confirmation'))
    is_public = models.BooleanField(default=False, verbose_name=_('Is public'))
    is_actual = models.BooleanField(default=False, verbose_name=_('Is actual'))
    registrations_enabled = models.BooleanField(default=False, verbose_name=_('Registrations enabled'))

    COLORS = [
        ('red', 'Red'),
        ('pink', 'Pink'),
        ('purple', 'Purple'),
        ('deep-purple', 'Deep Purple'),
        ('indigo', 'Indigo'),
        ('blue', 'Blue'),
        ('light-blue', 'Light Blue'),
        ('cyan', 'Cyan'),
        ('aqua', 'Aqua'),
        ('teal', 'Teal'),
        ('green', 'Green'),
        ('light-green', 'Light Green'),
        ('lime', 'Lime'),
        ('sand', 'Sand'),
        ('khaki', 'Khaki'),
        ('yellow', 'Yellow'),
        ('amber', 'Amber'),
        ('orange', 'Orange'),
        ('deep-orange', 'Deep Orange'),
        ('blue-gray', 'Blue Gray'),
        ('brown', 'Brown'),
        ('light-gray', 'Light Gray'),
        ('gray', 'Gray'),
        ('dark-gray', 'Dark Gray'),
        ('pale-red', 'Pale Red'),
        ('pale-yellow', 'Pale Yellow'),
        ('pale-green', 'Pale Green'),
        ('pale-blue', 'Pale Blue')
    ]
    color = models.CharField(max_length=20, choices=COLORS, default=COLORS[0],
                             blank=True, null=True, verbose_name=_('Color'))

    objects = EventsManager()

    class Meta:
        verbose_name = _('Event')
        verbose_name_plural = _('Events')

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        if not self.uid:
            self.uid = uuid.uuid4()
        super(Event, self).save(*args, **kwargs)

    def clean(self):
        # only one event can be actual
        if self.is_actual:
            Event.objects.exclude(pk=self.pk).update(is_actual=False)

    def get_bg_color(self):
        return f'w3-{self.color}'


class Category(models.Model):
    event = models.ForeignKey(Event, null=True, blank=True, on_delete=models.SET_NULL,
                              related_name='categories', verbose_name=_('Event'))
    name = models.CharField(max_length=100, null=False, blank=False, unique=True, verbose_name=_('Name'))
    description = models.TextField(blank=True, null=True, verbose_name=_('Description'))

    mapy_cz_url = models.URLField(blank=True, null=True, verbose_name=_('Mapy.cz URL'))
    length = models.IntegerField(blank=True, null=True, verbose_name=_('Length'))
    climb = models.IntegerField(blank=True, null=True, verbose_name=_('Climb'))
    gpx = models.FileField(upload_to='gpx/', blank=True, null=True, verbose_name=_('GPX'))

    laps_count = models.IntegerField(blank=True, null=True, default=1, verbose_name=_('Laps count'))

    class Meta:
        verbose_name = _('Category')
        verbose_name_plural = _('Categories')

    def __str__(self):
        return self.name


class Registration(TimeStampedModel):
    uid = models.UUIDField(blank=True, null=True, verbose_name=_('UUID'))
    event = models.ForeignKey(Event, null=True, blank=True, on_delete=models.SET_NULL,
                              related_name='registrations', verbose_name=_('Event'))
    variable_symbol = models.CharField(max_length=10, blank=True, null=True, verbose_name=_('Variable symbol'))

    # data from form
    first_name = models.CharField(max_length=30, blank=True, null=True, verbose_name=_('First name'))
    last_name = models.CharField(max_length=30, blank=True, null=True, verbose_name=_('Last name'))
    email = models.EmailField(blank=True, null=True, verbose_name=_('Email'))
    nick_name = models.CharField(max_length=30, blank=True, null=True, verbose_name=_('Nick name'))
    phone = models.CharField(max_length=20, blank=True, null=True, verbose_name=_('Phone'))
    category = models.CharField(max_length=30, blank=True, null=True, verbose_name=_('Category'))
    text = models.TextField(blank=True, null=True, verbose_name=_('Text'))

    # flags
    is_confirmed = models.BooleanField(default=False, verbose_name=_('Is confirmed'))
    is_paid = models.BooleanField(default=False, verbose_name=_('Is paid'))
    is_finished = models.BooleanField(default=False, verbose_name=_('Is finished'))
    is_presented = models.BooleanField(default=False, verbose_name=_('Is presented'))

    # race data
    start_number = models.IntegerField(blank=True, null=True, verbose_name=_('Start number'))
    chip_id = models.CharField(max_length=30, blank=True, null=True, verbose_name=_('Chip ID'))
    category_object = models.ForeignKey(Category, null=True, blank=True, on_delete=models.SET_NULL,
                                        related_name='registrations', verbose_name=_('Category'))

    class Meta:
        verbose_name = _('Registration')
        verbose_name_plural = _('Registrations')

    def __str__(self):
        return f'{self.get_name()} - {self.event}'

    def save(self, *args, **kwargs):
        if not self.uid:
            self.uid = uuid.uuid4()
        super(Registration, self).save(*args, **kwargs)

    def get_name(self):
        if self.nick_name:
            return smart_str(self.nick_name)
        if not self.first_name and not self.last_name:
            return smart_str(self.email)

        return smart_str(f'{self.first_name} {self.last_name}')

    @property
    def status(self):
        """
        Returns the status of the registration.
        :return:
        """
        if self.is_finished:
            return 'Dokončena'
        if self.is_paid:
            return 'Zaplacena'
        else:
            return 'Nezaplacena'

    @property
    def is_ended(self):
        """
        Returns whether the racer has finished all the laps.
        :return:
        """
        return self.laps.filter(end__isnull=False).count() == self.category_object.laps_count

    @property
    def total_duration(self):
        if not self.is_ended:
            return timedelta(0)

        duration = timedelta(0)
        for lap in self.laps.all():
            duration += lap.duration

        return duration


class Lap(TimeStampedModel):
    registration = models.ForeignKey(Registration, null=True, blank=True, on_delete=models.SET_NULL,
                                     related_name='laps', verbose_name=_('Registration'))
    number = models.IntegerField(blank=True, null=True, verbose_name=_('Number'))
    start = models.DateTimeField(blank=True, null=True, verbose_name=_('Start'))
    end = models.DateTimeField(blank=True, null=True, verbose_name=_('End'))

    class Meta:
        verbose_name = _('Lap')
        verbose_name_plural = _('Laps')

    def __str__(self):
        return f'{self.number} - {self.registration}'

    @property
    def duration(self):
        """
        Returns the duration of the lap.
        :return:
        """
        if self.start and self.end:
            return (self.end - self.start)

        return timedelta(0)


class HTMLSection(TimeStampedModel):
    event = models.ForeignKey(Event, null=True, blank=True, on_delete=models.SET_NULL,
                              related_name='html_sections', verbose_name=_('Event'))
    identifier = models.CharField(max_length=100, null=False, blank=False,
                                  verbose_name=_('Identifier'))
    menu = models.CharField(max_length=100, null=False, blank=False,
                            verbose_name=_('Menu'))
    html = models.TextField(blank=True, null=True, verbose_name=_('HTML'))
    order_index = models.IntegerField(default=0, verbose_name=_('Order index'))

    class Meta:
        verbose_name = _('HTML Section')
        verbose_name_plural = _('HTML Sections')
        unique_together = (('event', 'identifier'),)

    def __str__(self):
        return f'{self.identifier} - {self.event}'

    def clean(self):
        # check if the order index is unique for the event
        if HTMLSection.objects.filter(event=self.event, order_index=self.order_index).exclude(pk=self.pk).exists():
            raise ValidationError(_('Order index must be unique for the event'))
