from django.conf import settings as djsettings
from django.db import models
from django.db.models import query, Q
from django.utils.translation import gettext_lazy as _

from orchestra.contrib.contacts import settings as contacts_settings
from orchestra.contrib.contacts.models import Contact
from orchestra.models.fields import MultiSelectField
from orchestra.utils.mail import send_email_template

from . import settings


class Queue(models.Model):
    name = models.CharField(_("name"), max_length=128, unique=True)
    verbose_name = models.CharField(_("verbose_name"), max_length=128, blank=True)
    default = models.BooleanField(_("default"), default=False)
    notify = MultiSelectField(_("notify"), max_length=256, blank=True,
        choices=Contact.EMAIL_USAGES,
        default=contacts_settings.CONTACTS_DEFAULT_EMAIL_USAGES,
        help_text=_("Contacts to notify by email"))

    def __str__(self):
        return self.verbose_name or self.name

    def save(self, *args, **kwargs):
        """ mark as default queue if needed """
        existing_default = Queue.objects.filter(default=True)
        if self.default:
            existing_default.update(default=False)
        elif not existing_default:
            self.default = True
        super(Queue, self).save(*args, **kwargs)


class TicketQuerySet(query.QuerySet):
    def involved_by(self, user, *args, **kwargs):
        qset = Q(creator=user) | Q(owner=user) | Q(messages__author=user)
        return self.filter(qset, *args, **kwargs).distinct()


class Ticket(models.Model):
    HIGH = 'HIGH'
    MEDIUM = 'MEDIUM'
    LOW = 'LOW'
    PRIORITIES = (
        (HIGH, 'High'),
        (MEDIUM, 'Medium'),
        (LOW, 'Low'),
    )

    NEW = 'NEW'
    IN_PROGRESS = 'IN_PROGRESS'
    RESOLVED = 'RESOLVED'
    FEEDBACK = 'FEEDBACK'
    REJECTED = 'REJECTED'
    CLOSED = 'CLOSED'
    STATES = (
        (NEW, 'New'),
        (IN_PROGRESS, 'In Progress'),
        (RESOLVED, 'Resolved'),
        (FEEDBACK, 'Feedback'),
        (REJECTED, 'Rejected'),
        (CLOSED, 'Closed'),
    )

    creator = models.ForeignKey(djsettings.AUTH_USER_MODEL, verbose_name=_("created by"),
        related_name='tickets_created', null=True, on_delete=models.SET_NULL)
    creator_name = models.CharField(_("creator name"), max_length=256, blank=True)
    owner = models.ForeignKey(djsettings.AUTH_USER_MODEL, null=True, blank=True,
        on_delete=models.SET_NULL,
        related_name='tickets_owned', verbose_name=_("assigned to"))
    queue = models.ForeignKey(Queue, related_name='tickets', null=True, blank=True,
        on_delete=models.SET_NULL)
    subject = models.CharField(_("subject"), max_length=256)
    description = models.TextField(_("description"))
    priority = models.CharField(_("priority"), max_length=32, choices=PRIORITIES, default=MEDIUM)
    state = models.CharField(_("state"), max_length=32, choices=STATES, default=NEW)
    created_at = models.DateTimeField(_("created"), auto_now_add=True, db_index=True)
    updated_at = models.DateTimeField(_("modified"), auto_now=True)
    cc = models.TextField("CC", help_text=_("emails to send a carbon copy to"), blank=True)

    objects = TicketQuerySet.as_manager()

    class Meta:
        ordering = ['-updated_at']

    def __str__(self):
        return str(self.pk)

    def get_notification_emails(self):
        """ Get emails of the users related to the ticket """
        emails = list(settings.ISSUES_SUPPORT_EMAILS)
        emails.append(self.creator.email)
        if self.owner:
            emails.append(self.owner.email)
        for contact in self.creator.contacts.all():
            if self.queue and set(contact.email_usage).union(set(self.queue.notify)):
                emails.append(contact.email)
        for message in self.messages.distinct('author'):
            emails.append(message.author.email)
        return set(emails + self.get_cc_emails())

    def notify(self, message=None, content=None):
        """ Send an email to ticket stakeholders notifying an state update """
        emails = self.get_notification_emails()
        template = 'issues/ticket_notification.mail'
        html_template = 'issues/ticket_notification_html.mail'
        context = {
            'ticket': self,
            'ticket_message': message
        }
        send_email_template(template, context, emails, html=html_template)

    def save(self, *args, **kwargs):
        """ notify stakeholders of new ticket """
        new_issue = not self.pk
        if not self.creator_name and self.creator:
            self.creator_name = self.creator.get_full_name()
        super(Ticket, self).save(*args, **kwargs)
        if new_issue:
            # PK should be available for rendering the template
            self.notify()

    def is_involved_by(self, user):
        """ returns whether user has participated or is referenced on the ticket
            as owner or member of the group
        """
        return Ticket.objects.filter(pk=self.pk).involved_by(user).exists()

    def get_cc_emails(self):
        return self.cc.split(',') if self.cc else []

    def mark_as_read_by(self, user):
        self.trackers.get_or_create(user=user)

    def mark_as_unread_by(self, user):
        self.trackers.filter(user=user).delete()

    def mark_as_unread(self):
        self.trackers.all().delete()

    def is_read_by(self, user):
        return self.trackers.filter(user=user).exists()

    def reject(self):
        self.state = Ticket.REJECTED
        self.save(update_fields=('state', 'updated_at'))

    def resolve(self):
        self.state = Ticket.RESOLVED
        self.save(update_fields=('state', 'updated_at'))

    def close(self):
        self.state = Ticket.CLOSED
        self.save(update_fields=('state', 'updated_at'))

    def take(self, user):
        self.owner = user
        self.save(update_fields=('state', 'updated_at'))


class Message(models.Model):
    ticket = models.ForeignKey('issues.Ticket', on_delete=models.CASCADE,
        verbose_name=_("ticket"), related_name='messages')
    author = models.ForeignKey(djsettings.AUTH_USER_MODEL, on_delete=models.CASCADE,
        verbose_name=_("author"), related_name='ticket_messages')
    author_name = models.CharField(_("author name"), max_length=256, blank=True)
    content = models.TextField(_("content"))
    created_at = models.DateTimeField(_("created at"), auto_now_add=True)

    class Meta:
        get_latest_by = 'id'

    def __str__(self):
        return "#%i" % self.id

    def save(self, *args, **kwargs):
        """ notify stakeholders of ticket update """
        if not self.pk:
            self.ticket.mark_as_unread()
            self.ticket.mark_as_read_by(self.author)
            self.ticket.notify(message=self)
            self.author_name = self.author.get_full_name()
        super(Message, self).save(*args, **kwargs)

    @property
    def number(self):
        return self.ticket.messages.filter(id__lte=self.id).count()


class TicketTracker(models.Model):
    """ Keeps track of user read tickets """
    ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE,
        verbose_name=_("ticket"), related_name='trackers')
    user = models.ForeignKey(djsettings.AUTH_USER_MODEL, on_delete=models.CASCADE,
        verbose_name=_("user"), related_name='ticket_trackers')

    class Meta:
        unique_together = (
            ('ticket', 'user'),
        )