"""Models to solitary events, events series with an location and photos."""
import os

from ckeditor.fields import RichTextField
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import Q
from django.template.defaultfilters import slugify
from django.urls import reverse
from django.utils.timezone import now
from django.utils.translation import ugettext as _
from easy_thumbnails.fields import ThumbnailerImageField

from utils import COUNTRIES, OverwriteStorage
from .managers import EventManager


def get_upload_path(instance, filename):
    """
    Generates the desired file path and filename for an uploaded Image.
    With this function Django can save the uploaded images to subfolders that
    also have a meaning for humans.

    @param instance: an Django Object for which the Image has been uploaded.
    @type instance: a instace of an models.Model sub-class.
    @param filename: The filename of the uploaded image.
    @type filename: String
    """
    filename, extension = os.path.splitext(filename.lower())
    if isinstance(instance, Event):
        return "events/{date:%Y-%m-%d}/{name}{ext}".format(
            date=instance.start,
            name=slugify(instance.name),
            ext=extension
        )
    elif isinstance(instance, Location):
        return "events/locations/{name}{ext}".format(
            name=slugify(instance.name),
            ext=extension
        )
    elif isinstance(instance, Photo):
        return "events/{date:%Y-%m-%d}/{name}{ext}".format(
            date=instance.event.start,
            name=filename,
            ext=extension
        )


class Event(models.Model):
    """An Event that could be a tournament, a game session, or an convention."""
    name = models.CharField(_('Name'), max_length=255)
    description = RichTextField(_("Description"), blank=True)
    location = models.ForeignKey('Location', on_delete=models.PROTECT)
    start = models.DateTimeField(_('Start'))
    end = models.DateTimeField(_('End'), blank=True, null=True)
    url = models.URLField(_('Homepage'), blank=True)
    image = ThumbnailerImageField(
        _("Image"),
        upload_to=get_upload_path,
        storage=OverwriteStorage(),
        blank=True,
        null=True
    )
    mahjong_tournament = models.BooleanField(
        _('Mahjong Tournament'),
        default=False,
        help_text=_(u'This event is a tournament, different rules apply for \
        the kyu ranking.')
    )
    mahjong_season = models.PositiveSmallIntegerField(
        _('Mahjong Season'),
        blank=True,
        null=True
    )
    photo_count = models.PositiveIntegerField(default=0, editable=False)
    event_series = models.ForeignKey(
        'Event',
        blank=True,
        null=True,
        on_delete=models.SET_NULL, editable=True,
        verbose_name=_('Event Series'),
        help_text=_(u'Wenn dieser Event zu einer Veranstaltungsreihe gehört \
        werden Ort, Beschreibung, Bild und Homepage von dem hier angegebenen \
        Event übernommen.')
    )
    date_created = models.DateTimeField(
        auto_now_add=True,
        null=True,
        db_index=True,
        editable=False,
        verbose_name=_('first created at'),
    )
    date_modified = models.DateTimeField(
        auto_now=True,
        editable=False,
        verbose_name=_('latest updated at'),
    )
    objects = EventManager()

    class Meta(object):
        """order the events by start date."""
        verbose_name = _('Event')
        verbose_name_plural = _('Events')
        ordering = ('start', 'end',)

    def __str__(self):
        try:
            return "%(name)s (%(date)s)" % {'name': self.name,
                                            'date': self.start.date()}
        except:
            return "New Event Model"

    def clean(self):
        if self.end and self.end < self.start:
            raise ValidationError({
                'end': _("A event can't end before it had started")
            })

    def get_absolute_url(self):
        kwargs = {
            'pk': self.id,
            'year': self.start.strftime('%Y'),
            'month': self.start.strftime('%m')
        }
        return reverse('event-detail', kwargs=kwargs)

    def get_eventseries_form_url(self):
        kwargs = {
            'pk': self.id,
            'year': self.start.strftime('%Y'),
            'month': self.start.strftime('%m')
        }
        return reverse('eventseries-form', kwargs=kwargs)

    def get_edit_url(self):
        kwargs = {
            'pk': self.id,
            'year': self.start.strftime('%Y'),
            'month': self.start.strftime('%m')
        }
        return reverse('event-form', kwargs=kwargs)

    def get_image(self):
        if self.image:
            return self.image
        elif self.photo_count:
            return self.photo_set.all().order_by('?')[0].image
        elif self.location.image:
            return self.location.image
        else:
            return None

    @property
    def is_future_event(self):
        return self.start > now()

    def save(self, **kwargs):
        # Fülle fehlende Felder mit den Angaben der Hauptveranstaltung aus.
        if self.event_series:
            master_event = self.event_series
            self.name = self.name or master_event.name
            self.description = self.description or master_event.description
            self.location = master_event.location
            self.url = master_event.url
            self.image = self.image or master_event.image
        self.photo_count = self.photo_set.count()
        super(Event, self).save(**kwargs)

        # Update the Hanchans if necesery:
        for hanchan in self.hanchan_set.all():
            hanchan.save()


class Location(models.Model):
    name = models.CharField(_("Name"), max_length=200)
    description = RichTextField(_("Description"), blank=True)
    image = ThumbnailerImageField(
        _("Image"),
        upload_to=get_upload_path,
        storage=OverwriteStorage(),
        blank=True,
        null=True
    )
    url = models.URLField(_('Homepage'), blank=True)
    postal_code = models.CharField(_('Postal Code'), max_length=6)
    street_address = models.CharField(_('Street Address'), max_length=127)
    locality = models.CharField(_('Locality'), max_length=127)
    country = models.CharField(_('Country'), max_length=2, choices=COUNTRIES)
    date_created = models.DateTimeField(
        auto_now_add=True,
        db_index=True,
        editable=False,
        null=True,
        verbose_name=_('first created at'),
    )
    date_modified = models.DateTimeField(
        auto_now=True,
        editable=False,
        verbose_name=_('latest updated at'),
    )

    class Meta(object):
        verbose_name = _('Venue')
        verbose_name_plural = _('Venues')

    def __str__(self):
        return self.name

    @property
    def address(self):
        address = (self.street_address, self.locality, self.country,)
        return ','.join(address)


class Photo(models.Model):
    name = models.CharField(_("Name"), max_length=100, blank=True)
    image = ThumbnailerImageField(
        _("Image"),
        upload_to=get_upload_path,
        storage=OverwriteStorage()
    )
    event = models.ForeignKey('events.Event', on_delete=models.PROTECT, )
    description = models.TextField(
        _("Description"),
        max_length=300,
        blank=True
    )
    photographer = models.ForeignKey(settings.AUTH_USER_MODEL,
                                     on_delete=models.PROTECT)
    on_startpage = models.BooleanField(
        _("Startpage"),
        default=False,
        db_index=True,
        help_text=_('Display this Photo on the Startpage Teaser')
    )
    created_date = models.DateTimeField(_("Published on"))
    views = models.PositiveIntegerField(
        _("Number of views"),
        editable=False,
        default=0
    )
    date_created = models.DateTimeField(
        auto_now_add=True,
        db_index=True,
        editable=False,
        null=True,
        verbose_name=_('first created at'),
    )
    date_modified = models.DateTimeField(
        auto_now=True,
        editable=False,
        verbose_name=_('latest updated at'),
    )

    metadata = None
    orientation = 1

    class Meta:
        get_latest_by = "created_date"
        ordering = ["created_date"]
        db_table = 'events_photo'
        verbose_name = _('Event Image')
        verbose_name_plural = _('Event Images')

    def __str__(self):
        return os.path.basename(self.image.name)

    def rotate(self, rotate):
        # TODO: Eine vernüftigte Methode ohne viele Abhängigkeiten finden um
        # die Bilder bei Bedarf zu drehen.
        if rotate == 'clockwise':
            pass
        elif rotate == 'counter-clockwise':
            pass
        self.save()

    def save(self, **kwargs):
        super(Photo, self).save(**kwargs)
        self.event.save()

    def get_absolute_url(self):
        return reverse(
            'event-photo',
            kwargs={'event': self.event.id, 'pk': self.id}
        )

    @property
    def next_photo(self):
        queryset = Photo.objects.filter(created_date__gt=self.created_date)
        queryset = queryset.order_by('created_date')
        try:
            if self.event.event_series:
                return queryset.filter(
                    Q(event=self.event) |
                    Q(event=self.event.event_series) |
                    Q(event__event_series=self.event.event_series)
                )[0]
            else:
                return queryset.filter(
                    Q(event=self.event) |
                    Q(event__event_series=self.event)
                )[0]
        except IndexError:
            return None

    @property
    def previous_photo(self):
        queryset = Photo.objects.filter(created_date__lt=self.created_date)
        queryset = queryset.order_by('-created_date')
        try:
            if self.event.event_series:
                return queryset.filter(
                    Q(event=self.event) |
                    Q(event=self.event.event_series) |
                    Q(event__event_series=self.event.event_series)
                )[0]
            else:
                return queryset.filter(
                    Q(event=self.event) |
                    Q(event__event_series=self.event)
                )[0]
        except IndexError:
            return None
