from django.db import models
from django.core.exceptions import ValidationError
from django import forms
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext
from .mailutils import send, get_content, NOT_SENT, SENT, PARTLY_SENT
from utils import RestrictedFileField
from jdav_web.celery import app
from django.core.validators import RegexValidator
from django.conf import settings

import os


alphanumeric = RegexValidator(r'^[0-9a-zA-Z]*$', _('Only alphanumeric characters are allowed'))


class EmailAddress(models.Model):
    """Represents an email address, that is forwarded to specific members"""
    name = models.CharField(_('name'), max_length=50, validators=[alphanumeric])
    to_members = models.ManyToManyField('members.Member',
                                        verbose_name=_('Forward to participants'),
                                        blank=True)
    to_groups = models.ManyToManyField('members.Group',
                                       verbose_name=_('Forward to group'),
                                       blank=True)

    @property
    def email(self):
        return "{0}@{1}".format(self.name, settings.HOST)

    @property
    def forwards(self):
        mails = set(member.email for member in self.to_members.all())
        mails.update([member.email for group in self.to_groups.all() for member in group.member_set.all()])
        return mails

    def __str__(self):
        return self.email

    class Meta:
        verbose_name = _('email address')
        verbose_name_plural = _('email addresses')


class EmailAddressForm(forms.ModelForm):

    class Meta:
        model = EmailAddress
        exclude = []

    def clean(self):
        group = self.cleaned_data.get('to_groups')
        members = self.cleaned_data.get('to_members')
        if not group and not members:
            raise ValidationError(_('Either a group or at least'
                                    ' one member is required as forward recipient.'))



# Create your models here.
class Message(models.Model):
    """Represents a message that can be sent to some members"""
    subject = models.CharField(_('subject'), max_length=50)
    content = models.TextField(_('content'))
    to_groups = models.ManyToManyField('members.Group',
                                       verbose_name=_('to group'),
                                       blank=True)
    to_freizeit = models.ForeignKey('members.Freizeit',
                                    verbose_name=_('to freizeit'),
                                    on_delete=models.CASCADE,
                                    blank=True,
                                    null=True)
    to_notelist = models.ForeignKey('members.MemberNoteList',
                                      verbose_name=_('to notes list'),
                                      on_delete=models.CASCADE,
                                      blank=True,
                                      null=True)
    to_members = models.ManyToManyField('members.Member',
                                        verbose_name=_('to member'),
                                        blank=True)
    reply_to = models.ManyToManyField('members.Member',
                                      verbose_name=_('reply to participant'),
                                      blank=True,
                                      related_name='reply_to')
    reply_to_email_address = models.ManyToManyField('mailer.EmailAddress',
                                                    verbose_name=_('reply to custom email address'),
                                                    blank=True,
                                                    related_name='reply_to_email_addr')
    sent = models.BooleanField(_('sent'), default=False)

    def __str__(self):
        return self.subject

    def get_recipients(self):
        recipients = [g.name for g in self.to_groups.all()]
        if self.to_freizeit is not None:
            recipients.append(self.to_freizeit.name)
        if self.to_notelist is not None:
            recipients.append(self.to_notelist.title)
        if 3 > self.to_members.count() > 0:
            recipients.extend([m.name for m in self.to_members.all()])
        elif self.to_members.count() > 2:
            recipients.append(gettext('Some other members'))
        return ", ".join(recipients)
    get_recipients.short_description = _('recipients')

    def submit(self):
        """Sends the mail to the specified group of members"""
        # recipients
        members = set()
        # get all the members of the selected groups
        groups = [gr.member_set.all() for gr in self.to_groups.all()]
        members.update([m for gr in groups for m in gr])
        # get all the individually picked members
        members.update(self.to_members.all())
        # get all the members of the selected freizeit
        if self.to_freizeit is not None:
            members.update([mol.member for mol in
                            self.to_freizeit.membersonlist.all()])
            members.update(self.to_freizeit.jugendleiter.all())
        # get all the members of the selected notes list
        if self.to_notelist is not None:
            members.update([mol.member for mol in
                            self.to_notelist.membersonlist.all()])
        filtered = [m for m in members if m.gets_newsletter]
        print("sending mail to", filtered)

        attach = [a.f.path for a in Attachment.objects.filter(msg__id=self.pk)
                  if a.f.name]
        recipients_with_reminder = [m for m in filtered if not m.registered]
        recipients_without_reminder = [m for m in filtered if m.registered]

        emails_rem = [member.email for member in recipients_with_reminder]
        emails_rem.extend([member.email_parents for member in recipients_with_reminder
                       if member.email_parents and member.cc_email_parents])
        emails_no_rem = [member.email for member in recipients_without_reminder]
        emails_no_rem.extend([member.email_parents for member in recipients_without_reminder
                       if member.email_parents and member.cc_email_parents])
        # remove any underscores from subject to prevent Arne from using
        # terrible looking underscores in subjects
        self.subject = self.subject.replace('_', ' ')
        # generate message id
        message_id = "<{pk}@{domain}>".format(pk=self.pk, domain=settings.DOMAIN)
        # reply to addresses
        reply_to_unfiltered = [jl.association_email for jl in self.reply_to.all()]
        reply_to_unfiltered.extend([ml.email for ml in self.reply_to_email_address.all()])
        # remove sending address from reply-to field (probably unnecessary since it's removed by
        # the mail provider anyways)
        reply_to = [mail for mail in reply_to_unfiltered if mail != settings.DEFAULT_SENDING_MAIL ]
        try:
            success1 = send(self.subject, get_content(self.content, registration_complete=False),
                            settings.DEFAULT_SENDING_MAIL,
                            emails_rem,
                            message_id=message_id,
                            attachments=attach,
                            reply_to=reply_to)
            success2 = send(self.subject, get_content(self.content, registration_complete=True),
                            settings.DEFAULT_SENDING_MAIL,
                            emails_no_rem,
                            message_id=message_id,
                            attachments=attach,
                            reply_to=reply_to)
            if (success1 == SENT or success1 == PARTLY_SENT) and (success2 == SENT or success2 == PARTLY_SENT):
                self.sent = True
            for a in Attachment.objects.filter(msg__id=self.pk):
                if a.f.name:
                    os.remove(a.f.path)
                a.delete()
            success = SENT
        except Exception as e:
            print("Exception caught", e)
            success = NOT_SENT
        finally:
            self.save()
        return success

    class Meta:
        verbose_name = _('message')
        verbose_name_plural = _('messages')
        permissions = (
            ("submit_mails", _("Can submit mails")),
        )


class MessageForm(forms.ModelForm):

    class Meta:
        model = Message
        exclude = []

    def clean(self):
        group = self.cleaned_data.get('to_groups')
        freizeit = self.cleaned_data.get('to_freizeit')
        notelist = self.cleaned_data.get('to_notelist')
        members = self.cleaned_data.get('to_members')
        if not group and freizeit is None and not members and notelist is None:
            raise ValidationError(_('Either a group, a memberlist or at least'
                                    ' one member is required as recipient'))
        reply_to = self.cleaned_data.get('reply_to')
        reply_to_email_address = self.cleaned_data.get('reply_to_email_address')
        if not reply_to and not reply_to_email_address:
            raise ValidationError(_('At least one reply-to recipient is required. '
                                    'Use the info mail if you really want no reply-to recipient.'))

class Attachment(models.Model):
    """Represents an attachment to an email"""
    msg = models.ForeignKey(Message, on_delete=models.CASCADE)
    # file (not naming it file because of builtin)
    f = RestrictedFileField(_('file'),
                            upload_to='attachments',
                            blank=True,
                            max_upload_size=10485760)

    def __str__(self):
        return os.path.basename(self.f.name) if self.f.name else _("Empty")

    class Meta:
        verbose_name = _('attachment')
        verbose_name_plural = _('attachments')
