# -*- coding: utf-8 -*-

from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

import calendar
import random
from datetime import timedelta

from django.contrib.sites.models import Site
from django.db import models
from django.contrib.auth.models import User
from django.template import loader
from django.core import mail
from django.conf import settings
from django.utils import timezone, formats
from django.utils.encoding import python_2_unicode_compatible


@python_2_unicode_compatible
class Tirage(models.Model):
    title = models.CharField("Titre", max_length=300)
    ouverture = models.DateTimeField("Date et heure d'ouverture du tirage")
    fermeture = models.DateTimeField("Date et heure de fermerture du tirage")
    tokens = models.TextField("Graine(s) du tirage", blank=True)
    active = models.BooleanField("Tirage actif", default=False)
    enable_do_tirage = models.BooleanField("Le tirage peut être lancé",
                                           default=False)

    def __str__(self):
        return "%s - %s" % (self.title, formats.localize(
            timezone.template_localtime(self.fermeture)))


@python_2_unicode_compatible
class Salle(models.Model):
    name = models.CharField("Nom", max_length=300)
    address = models.TextField("Adresse")

    def __str__(self):
        return self.name


@python_2_unicode_compatible
class CategorieSpectacle(models.Model):
    name = models.CharField('Nom', max_length=100, unique=True)

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = "Catégorie"


@python_2_unicode_compatible
class Spectacle(models.Model):
    title = models.CharField("Titre", max_length=300)
    category = models.ForeignKey(CategorieSpectacle, blank=True, null=True)
    date = models.DateTimeField("Date & heure")
    location = models.ForeignKey(Salle)
    vips = models.TextField('Personnalités', blank=True)
    description = models.TextField("Description", blank=True)
    slots_description = models.TextField("Description des places", blank=True)
    image = models.ImageField('Image', blank=True, null=True,
                              upload_to='imgs/shows/')
    ext_link = models.CharField('Lien vers le site du spectacle', blank=True,
                                max_length=500)
    price = models.FloatField("Prix d'une place")
    slots = models.IntegerField("Places")
    tirage = models.ForeignKey(Tirage)
    listing = models.BooleanField("Les places sont sur listing")
    rappel_sent = models.DateTimeField("Mail de rappel envoyé", blank=True,
                                       null=True)

    class Meta:
        verbose_name = "Spectacle"
        ordering = ("date", "title",)

    def timestamp(self):
        return "%d" % calendar.timegm(self.date.utctimetuple())

    def __str__(self):
        return "%s - %s, %s, %.02f€" % (
            self.title,
            formats.localize(timezone.template_localtime(self.date)),
            self.location,
            self.price
        )

    def send_rappel(self):
        """
        Envoie un mail de rappel à toutes les personnes qui ont une place pour
        ce spectacle.
        """
        # On récupère la liste des participants
        members = {}
        for attr in Attribution.objects.filter(spectacle=self).all():
            member = attr.participant.user
            if member.id in members:
                members[member.id][1] = 2
            else:
                members[member.id] = [member.first_name, 1, member.email]
        # Pour le BdA
        members[0] = ['BdA', 1, 'bda@ens.fr']
        members[-1] = ['BdA', 2, 'bda@ens.fr']
        # On écrit un mail personnalisé à chaque participant
        mails_to_send = []
        mail_object = str(self)
        for member in members.values():
            mail_body = loader.render_to_string('bda/mails/rappel.txt', {
                'name': member[0],
                'nb_attr': member[1],
                'show': self})
            mail_tot = mail.EmailMessage(
                mail_object, mail_body,
                settings.MAIL_DATA['rappels']['FROM'], [member[2]],
                [], headers={
                    'Reply-To': settings.MAIL_DATA['rappels']['REPLYTO']})
            mails_to_send.append(mail_tot)
        # On envoie les mails
        connection = mail.get_connection()
        connection.send_messages(mails_to_send)
        # On enregistre le fait que l'envoi a bien eu lieu
        self.rappel_sent = timezone.now()
        self.save()
        # On renvoie la liste des destinataires
        return members.values()


class Quote(models.Model):
    spectacle = models.ForeignKey(Spectacle)
    text = models.TextField('Citation')
    author = models.CharField('Auteur', max_length=200)


PAYMENT_TYPES = (
    ("cash", "Cash"),
    ("cb", "CB"),
    ("cheque", "Chèque"),
    ("autre", "Autre"),
)


@python_2_unicode_compatible
class Participant(models.Model):
    user = models.ForeignKey(User)
    choices = models.ManyToManyField(Spectacle,
                                     through="ChoixSpectacle",
                                     related_name="chosen_by")
    attributions = models.ManyToManyField(Spectacle,
                                          through="Attribution",
                                          related_name="attributed_to")
    paid = models.BooleanField("A payé", default=False)
    paymenttype = models.CharField("Moyen de paiement",
                                   max_length=6, choices=PAYMENT_TYPES,
                                   blank=True)
    tirage = models.ForeignKey(Tirage)
    choicesrevente = models.ManyToManyField(Spectacle,
                                            related_name="subscribed",
                                            blank=True)

    def __str__(self):
        return "%s - %s" % (self.user, self.tirage.title)

DOUBLE_CHOICES = (
    ("1", "1 place"),
    ("autoquit", "2 places si possible, 1 sinon"),
    ("double", "2 places sinon rien"),
)


@python_2_unicode_compatible
class ChoixSpectacle(models.Model):
    participant = models.ForeignKey(Participant)
    spectacle = models.ForeignKey(Spectacle, related_name="participants")
    priority = models.PositiveIntegerField("Priorité")
    double_choice = models.CharField("Nombre de places",
                                     default="1", choices=DOUBLE_CHOICES,
                                     max_length=10)

    def get_double(self):
        return self.double_choice != "1"
    double = property(get_double)

    def get_autoquit(self):
        return self.double_choice == "autoquit"
    autoquit = property(get_autoquit)

    def __str__(self):
        return "Vœux de %s pour %s" % (
                self.participant.user.get_full_name,
                self.spectacle.title)

    class Meta:
        ordering = ("priority",)
        unique_together = (("participant", "spectacle",),)
        verbose_name = "voeu"
        verbose_name_plural = "voeux"


@python_2_unicode_compatible
class Attribution(models.Model):
    participant = models.ForeignKey(Participant)
    spectacle = models.ForeignKey(Spectacle, related_name="attribues")
    given = models.BooleanField("Donnée", default=False)

    def __str__(self):
        return "%s -- %s, %s" % (self.participant.user, self.spectacle.title,
                                 self.spectacle.date)


@python_2_unicode_compatible
class SpectacleRevente(models.Model):
    attribution = models.OneToOneField(Attribution,
                                       related_name="revente")
    date = models.DateTimeField("Date de mise en vente",
                                default=timezone.now)
    answered_mail = models.ManyToManyField(Participant,
                                           related_name="wanted",
                                           blank=True)
    seller = models.ForeignKey(Participant,
                               related_name="original_shows",
                               verbose_name="Vendeur")
    soldTo = models.ForeignKey(Participant, blank=True, null=True,
                               verbose_name="Vendue à")

    notif_sent = models.BooleanField("Notification envoyée",
                                     default=False)
    tirage_done = models.BooleanField("Tirage effectué",
                                      default=False)

    @property
    def expiration_time(self):
        # L'acheteur doit être connu au plus 12h avant le spectacle
        remaining_time = (self.attribution.spectacle.date
                          - self.date - timedelta(hours=13))
        # Au minimum, on attend 2 jours avant le tirage
        delay = min(remaining_time, timedelta(days=2))
        # On a aussi 1h pour changer d'avis
        return self.date + delay + timedelta(hours=1)

    def expiration_time_str(self):
        return self.expiration_time \
                   .astimezone(timezone.get_current_timezone()) \
                   .strftime('%d/%m/%y à %H:%M')

    @property
    def shotgun(self):
        # Soit on a dépassé le délai du tirage, soit il reste peu de
        # temps avant le spectacle
        # On se laisse 5min de marge pour cron
        return (timezone.now() > self.expiration_time + timedelta(minutes=5) or
                (self.attribution.spectacle.date <= timezone.now() +
                timedelta(days=1))) and (timezone.now() >= self.date +
                                         timedelta(minutes=15))

    def __str__(self):
        return "%s -- %s" % (self.seller,
                             self.attribution.spectacle.title)

    class Meta:
        verbose_name = "Revente"

    def send_notif(self):
        inscrits = self.attribution.spectacle.subscribed.select_related('user')

        mails_to_send = []
        mail_object = "%s" % (self.attribution.spectacle)
        for participant in inscrits:
            mail_body = loader.render_to_string('bda/mails/revente.txt', {
                'user': participant.user,
                'spectacle': self.attribution.spectacle,
                'revente': self,
                'domain': Site.objects.get_current().domain})
            mail_tot = mail.EmailMessage(
                mail_object, mail_body,
                settings.MAIL_DATA['revente']['FROM'],
                [participant.user.email],
                [], headers={
                    'Reply-To': settings.MAIL_DATA['revente']['REPLYTO']})
            mails_to_send.append(mail_tot)

        connection = mail.get_connection()
        connection.send_messages(mails_to_send)
        self.notif_sent = True
        self.save()

    def mail_shotgun(self):
        """
        Envoie un mail à toutes les personnes intéréssées par le spectacle pour
        leur indiquer qu'il est désormais disponible au shotgun.
        """
        inscrits = self.attribution.spectacle.subscribed.select_related('user')

        mails_to_send = []
        mail_object = "%s" % (self.attribution.spectacle)
        for participant in inscrits:
            mail_body = loader.render_to_string('bda/mails/shotgun.txt', {
                'user': participant.user,
                'spectacle': self.attribution.spectacle,
                'domain': Site.objects.get_current(),
                'mail': self.attribution.participant.user.email})
            mail_tot = mail.EmailMessage(
                mail_object, mail_body,
                settings.MAIL_DATA['revente']['FROM'],
                [participant.user.email],
                [], headers={
                    'Reply-To': settings.MAIL_DATA['revente']['REPLYTO']})
            mails_to_send.append(mail_tot)

        connection = mail.get_connection()
        connection.send_messages(mails_to_send)
        self.notif_sent = True
        self.save()

    def tirage(self):
        """
        Lance le tirage au sort associé à la revente. Un gagnant est choisi
        parmis les personnes intéressées par le spectacle. Les personnes sont
        ensuites prévenues par mail du résultat du tirage.
        """
        inscrits = list(self.answered_mail.all())
        spectacle = self.attribution.spectacle
        seller = self.seller

        if inscrits:
            mails = []
            mail_subject = "BdA-Revente : {:s}".format(spectacle.title)

            # Envoie un mail au gagnant et au vendeur
            winner = random.choice(inscrits)
            self.soldTo = winner
            context = {
                'acheteur': winner.user,
                'vendeur': seller.user,
                'spectacle': spectacle,
            }
            mails.append(mail.EmailMessage(
                mail_subject,
                loader.render_to_string('bda/mails/revente-winner.txt',
                                        context),
                from_email=settings.MAIL_DATA['revente']['FROM'],
                to=[winner.user.email],
                reply_to=[seller.user.email],
            ))
            mails.append(mail.EmailMessage(
                mail_subject,
                loader.render_to_string('bda/mails/revente-seller.txt',
                                        context),
                from_email=settings.MAIL_DATA['revente']['FROM'],
                to=[seller.user.email],
                reply_to=[winner.user.email],
            ))

            # Envoie un mail aux perdants
            for inscrit in inscrits:
                if inscrit == winner:
                    continue

                mail_body = loader.render_to_string(
                    'bda/mails/revente-loser.txt',
                    {'acheteur': inscrit.user,
                     'vendeur': seller.user,
                     'spectacle': spectacle}
                )
                mails.append(mail.EmailMessage(
                    mail_subject, mail_body,
                    from_email=settings.MAIL_DATA['revente']['FROM'],
                    to=[inscrit.user.email],
                    reply_to=[settings.MAIL_DATA['revente']['REPLYTO']],
                ))
            mail.get_connection().send_messages(mails)
        self.tirage_done = True
        self.save()