import datetime

from django import forms
from django.conf import settings
from django.db.models import Q
from django.forms.models import BaseModelFormSet
from django.template.loader import render_to_string
from django.utils.safestring import mark_safe

from ietf.wgchairs.models import WGDelegate, ProtoWriteUp
from ietf.wgchairs.accounts import get_person_for_user
from ietf.ietfworkflows.constants import REQUIRED_STATES
from ietf.ietfworkflows.utils import (get_default_workflow_for_wg, get_workflow_for_wg,
                                      update_tags, FOLLOWUP_TAG, get_state_by_name)
from ietf.ietfworkflows.models import AnnotationTag, State
from ietf.idtracker.models import PersonOrOrgInfo
from ietf.utils.mail import send_mail_text

from workflows.models import Transition

from ietf.doc.models import WriteupDocEvent
from ietf.person.models import Person, Email
from ietf.group.models import Role, RoleName
from ietf.group.utils import save_group_in_history
from ietf.name.models import DocTagName


class RelatedWGForm(forms.Form):

    can_cancel = False

    def __init__(self, *args, **kwargs):
        self.wg = kwargs.pop('wg', None)
        self.user = kwargs.pop('user', None)
        self.message = {}
        super(RelatedWGForm, self).__init__(*args, **kwargs)

    def get_message(self):
        return self.message

    def set_message(self, msg_type, msg_value):
        self.message = {'type': msg_type,
                        'value': msg_value,
                       }


class TagForm(RelatedWGForm):

    tags = forms.ModelMultipleChoiceField(AnnotationTag.objects.filter(workflow__name='Default WG Workflow'),
                                          widget=forms.CheckboxSelectMultiple, required=False)

    def save(self):
        workflow = get_workflow_for_wg(self.wg)
        workflow.selected_tags.clear()
        for tag in self.cleaned_data['tags']:
            workflow.selected_tags.add(tag)
        return workflow


class StateForm(RelatedWGForm):

    states = forms.ModelMultipleChoiceField(State.objects.filter(workflow__name='Default WG Workflow'),
                                            widget=forms.CheckboxSelectMultiple, required=False)

    def update_transitions(self, workflow):
        for transition in workflow.transitions.all():
            if not workflow.selected_states.filter(pk=transition.destination.pk).count():
                transition.delete()
                continue
            for state in transition.states.all():
                if not workflow.selected_states.filter(pk=state.pk).count():
                    transition.states.remove(state)
            if not transition.states.count():
                transition.delete()
                continue

    def save(self):
        workflow = get_workflow_for_wg(self.wg)
        workflow.selected_states.clear()
        for state in self.cleaned_data['states']:
            workflow.selected_states.add(state)
        for name in REQUIRED_STATES:
            rstate = get_state_by_name(name)
            if rstate:
                workflow.selected_states.add(rstate)
        self.update_transitions(workflow)
        return workflow


class DeleteTransitionForm(RelatedWGForm):

    transitions = forms.ModelMultipleChoiceField(Transition.objects.all(),
                                                 widget=forms.CheckboxSelectMultiple)

    def __init__(self, *args, **kwargs):
        super(DeleteTransitionForm, self).__init__(*args, **kwargs)
        workflow = get_workflow_for_wg(self.wg)
        self.fields['transitions'].queryset = self.fields['transitions'].queryset.filter(workflow=workflow)

    def save(self):
        for transition in self.cleaned_data['transitions']:
            transition.delete()


class TransitionForm(forms.ModelForm):

    states = forms.ModelMultipleChoiceField(State.objects.filter(workflow__name='Default WG Workflow'))

    class Meta:
        model = Transition
        fields = ('name', 'states', 'destination', )

    def __init__(self, *args, **kwargs):
        self.wg = kwargs.pop('wg', None)
        self.user = kwargs.pop('user', None)
        super(TransitionForm, self).__init__(*args, **kwargs)
        workflow = get_workflow_for_wg(self.wg)
        self.fields['states'].queryset = workflow.selected_states.all()
        self.fields['destination'].queryset = workflow.selected_states.all()
        self.fields['destination'].required = True
        if self.instance.pk:
            self.fields['states'].initial = [i.pk for i in self.instance.states.all()]
        self.instance.workflow = workflow

    def as_row(self):
        return self._html_output(u'<td>%(errors)s%(field)s%(help_text)s</td>', u'<td colspan="2">%s</td>', '</td>', u'<br />%s', False)

    def save(self, *args, **kwargs):
        instance = super(TransitionForm, self).save(*args, **kwargs)
        for state in self.cleaned_data['states']:
            state.transitions.add(instance)


class TransitionFormSet(BaseModelFormSet):

    form = TransitionForm
    can_delete = True
    extra = 2
    max_num = 0
    can_order = False
    model = Transition

    def __init__(self, *args, **kwargs):
        self.wg = kwargs.pop('wg', None)
        self.user = kwargs.pop('user', None)
        super(TransitionFormSet, self).__init__(*args, **kwargs)

    def _construct_form(self, i, **kwargs):
        kwargs = kwargs or {}
        kwargs.update({'wg': self.wg, 'user': self.user})
        return super(TransitionFormSet, self)._construct_form(i, **kwargs)

    def as_table(self):
        html = u''
        csscl = 'oddrow'
        for form in self.forms:
            html += u'<tr class="%s">' % csscl
            html += form.as_row()
            html += u'</tr>'
            if csscl == 'oddrow':
                csscl = 'evenrow'
            else:
                csscl = 'oddrow'
        return mark_safe(u'\n'.join([unicode(self.management_form), html]))


def workflow_form_factory(request, wg, user):

    if request.POST.get('update_transitions', None):
        return TransitionFormSet(wg=wg, user=user, data=request.POST)
    elif request.POST.get('update_states', None):
        return StateForm(wg=wg, user=user, data=request.POST)
    return TagForm(wg=wg, user=user, data=request.POST)


class RemoveDelegateForm(RelatedWGForm):

    delete = forms.MultipleChoiceField()

    def __init__(self, *args, **kwargs):
        super(RemoveDelegateForm, self).__init__(*args, **kwargs)
        self.fields['delete'].choices = [(i.pk, i.pk) for i in self.wg.wgdelegate_set.all()]

    def save(self):
        delegates = self.cleaned_data.get('delete')
        save_group_in_history(self.wg)
        WGDelegate.objects.filter(pk__in=delegates).delete()
        self.set_message('success', 'Delegates removed')

def assign_shepherd(user, internetdraft, shepherd):
    if internetdraft.shepherd == shepherd:
        return
    
    from ietf.doc.models import save_document_in_history, DocEvent, Document

    # saving the proxy object is a bit of a mess, so convert it to a
    # proper document
    doc = Document.objects.get(name=internetdraft.name)
    
    save_document_in_history(doc)

    doc.time = datetime.datetime.now()
    doc.shepherd = shepherd
    doc.save()

    e = DocEvent(type="changed_document")
    e.time = doc.time
    e.doc = doc
    e.by = user.get_profile()
    if not shepherd:
        e.desc = u"Unassigned shepherd"
    else:
        e.desc = u"Changed shepherd to %s" % shepherd.plain_name()
    e.save()

    # update proxy too
    internetdraft.shepherd = shepherd

class AddDelegateForm(RelatedWGForm):

    email = forms.EmailField()
    form_type = forms.CharField(widget=forms.HiddenInput, initial='single')

    def __init__(self, *args, **kwargs):
        self.shepherd = kwargs.pop('shepherd', False)
        super(AddDelegateForm, self).__init__(*args, **kwargs)
        self.next_form = self

    def get_next_form(self):
        return self.next_form

    def get_person(self, email):
        persons = PersonOrOrgInfo.objects.filter(emailaddress__address=email).filter(
            Q(iesglogin__isnull=False)|
            Q(legacywgpassword__isnull=False)|
            Q(legacyliaisonuser__isnull=False)).distinct()
        if not persons:
            raise PersonOrOrgInfo.DoesNotExist
        if len(persons) > 1:
            raise PersonOrOrgInfo.MultipleObjectsReturned
        return persons[0]

    def save(self):
        email = self.cleaned_data.get('email')
        if settings.USE_DB_REDESIGN_PROXY_CLASSES:
            try:
                person = Person.objects.filter(email__address=email).exclude(user=None).distinct().get()
            except Person.DoesNotExist:
                self.next_form = NotExistDelegateForm(wg=self.wg, user=self.user, email=email, shepherd=self.shepherd)
                self.next_form.set_message('doesnotexist', 'There is no user with this email allowed to login to the system')
                return
            except Person.MultipleObjectsReturned:
                self.next_form = MultipleDelegateForm(wg=self.wg, user=self.user, email=email, shepherd=self.shepherd)
                self.next_form.set_message('multiple', 'There are multiple users with this email in the system')
                return
        else:
            try:
                person = self.get_person(email)
            except PersonOrOrgInfo.DoesNotExist:
                self.next_form = NotExistDelegateForm(wg=self.wg, user=self.user, email=email, shepherd=self.shepherd)
                self.next_form.set_message('doesnotexist', 'There is no user with this email allowed to login to the system')
                return
            except PersonOrOrgInfo.MultipleObjectsReturned:
                self.next_form = MultipleDelegateForm(wg=self.wg, user=self.user, email=email, shepherd=self.shepherd)
                self.next_form.set_message('multiple', 'There are multiple users with this email in the system')
                return
        if self.shepherd:
            self.assign_shepherd(person)
        else:
            self.create_delegate(person)

    def assign_shepherd(self, person):
        if settings.USE_DB_REDESIGN_PROXY_CLASSES:
            assign_shepherd(self.user, self.shepherd, person)
        else:
            self.shepherd.shepherd = person
            self.shepherd.save()
        self.next_form = AddDelegateForm(wg=self.wg, user=self.user, shepherd=self.shepherd)
        self.next_form.set_message('success', 'Shepherd assigned successfully')

    def create_delegate(self, person):
        if settings.USE_DB_REDESIGN_PROXY_CLASSES:
            created = False
            e = Email.objects.get(address=self.cleaned_data.get('email'))
            if not Role.objects.filter(name="delegate", group=self.wg, person=person, email=e):
                created = True
                save_group_in_history(self.wg)
                delegate, _ = Role.objects.get_or_create(
                    name=RoleName.objects.get(slug="delegate"), group=self.wg, person=e.person, email=e)
        else:
            (delegate, created) = WGDelegate.objects.get_or_create(wg=self.wg,
                                                                   person=person)
        if not created:
            self.set_message('error', 'The email belongs to a person who is already a delegate')
        else:
            self.next_form = AddDelegateForm(wg=self.wg, user=self.user)
            self.next_form.set_message('success', 'A new delegate has been added')


class MultipleDelegateForm(AddDelegateForm):

    email = forms.EmailField(widget=forms.HiddenInput)
    form_type = forms.CharField(widget=forms.HiddenInput, initial='multiple')
    persons = forms.ChoiceField(widget=forms.RadioSelect, help_text='Please select one person from the list')
    submit_msg = 'Designate as delegate'

    def __init__(self, *args, **kwargs):
        self.email = kwargs.pop('email', None)
        super(MultipleDelegateForm, self).__init__(*args, **kwargs)
        if not self.email:
            self.email = self.data.get('email', None)
        self.fields['email'].initial = self.email
        if settings.USE_DB_REDESIGN_PROXY_CLASSES:
            self.fields['persons'].choices = [(i.pk, unicode(i)) for i in Person.objects.filter(email__address=self.email).exclude(user=None).distinct().order_by('name')]
        else:
            self.fields['persons'].choices = [(i.pk, unicode(i)) for i in PersonOrOrgInfo.objects.filter(emailaddress__address=self.email).filter(
                    Q(iesglogin__isnull=False)|
                    Q(legacywgpassword__isnull=False)|
                    Q(legacyliaisonuser__isnull=False)).distinct().order_by('first_name')]

    def save(self):
        person_id = self.cleaned_data.get('persons')
        if settings.USE_DB_REDESIGN_PROXY_CLASSES:
            person = Person.objects.get(pk=person_id)
        else:
            person = PersonOrOrgInfo.objects.get(pk=person_id)
        if self.shepherd:
            self.assign_shepherd(person)
        else:
            self.create_delegate(person)


class NotExistDelegateForm(MultipleDelegateForm):

    email = forms.EmailField(widget=forms.HiddenInput)
    form_type = forms.CharField(widget=forms.HiddenInput, initial='notexist')
    can_cancel = True
    submit_msg = 'Send email to these addresses'

    def __init__(self, *args, **kwargs):
        super(NotExistDelegateForm, self).__init__(*args, **kwargs)
        self.email_list = []
        del(self.fields['persons'])

    def get_email_list(self):
        if self.email_list:
            return self.email_list
        email_list = [self.email]
        email_list.append('IETF Secretariat <iesg-secretary@ietf.org>')
        email_list += ['%s <%s>' % i.person.email() for i in self.wg.wgchair_set.all() if i.person.email()]
        self.email_list = email_list
        return email_list

    def as_p(self):
        email_list = self.get_email_list()
        info = render_to_string('wgchairs/notexistdelegate.html', {'email_list': email_list, 'shepherd': self.shepherd})
        return info + super(NotExistDelegateForm, self).as_p()

    def send_email(self, to_email, template):
        if self.shepherd:
            subject = 'WG shepherd needs system credentials'
        else:
            subject = 'WG Delegate needs system credentials'
        if settings.USE_DB_REDESIGN_PROXY_CLASSES:
            persons = Person.objects.filter(email__address=self.email).distinct()
        else:
            persons = PersonOrOrgInfo.objects.filter(emailaddress__address=self.email).distinct()
        body = render_to_string(template,
                                {'chair': get_person_for_user(self.user),
                                 'delegate_email': self.email,
                                 'shepherd': self.shepherd,
                                 'delegate_persons': persons,
                                 'wg': self.wg,
                                })

        send_mail_text(self.request, to_email, settings.DEFAULT_FROM_EMAIL, subject, body)

    def save(self):
        self.next_form = AddDelegateForm(wg=self.wg, user=self.user)
        if settings.DEBUG:
            self.next_form.set_message('warning', 'Email was not sent cause tool is in DEBUG mode')
        else:
            # this is ugly...
            email_list = self.get_email_list()
            delegate = email_list[0]
            secretariat = email_list[1]
            wgchairs = email_list[2:]
            self.send_email(delegate, 'wgchairs/notexistsdelegate_delegate_email.txt')
            self.send_email(secretariat, 'wgchairs/notexistsdelegate_secretariat_email.txt')
            self.send_email(wgchairs, 'wgchairs/notexistsdelegate_wgchairs_email.txt')
            self.next_form.set_message('success', 'Email sent successfully')


def add_form_factory(request, wg, user, shepherd=False):
    if request.method != 'POST' or request.POST.get('update_shepehrd'):
        return AddDelegateForm(wg=wg, user=user, shepherd=shepherd)

    if request.POST.get('form_type', None) == 'multiple':
        f = MultipleDelegateForm(wg=wg, user=user, data=request.POST.copy(), shepherd=shepherd)
    elif request.POST.get('form_type', None) == 'notexist':
        f = NotExistDelegateForm(wg=wg, user=user, data=request.POST.copy(), shepherd=shepherd)
    elif request.POST.get('form_type', None) == 'single':
        f = AddDelegateForm(wg=wg, user=user, data=request.POST.copy(), shepherd=shepherd)
    else:
        f = AddDelegateForm(wg=wg, user=user, shepherd=shepherd)

    f.request = request
    return f

class WriteUpEditForm(RelatedWGForm):

    writeup = forms.CharField(widget=forms.Textarea, required=False)
    followup = forms.BooleanField(required=False)
    comment = forms.CharField(widget=forms.Textarea, required=False)

    def __init__(self, *args, **kwargs):
        self.doc = kwargs.pop('doc', None)
        self.doc_writeup = self.doc.protowriteup_set.all()
        if self.doc_writeup.count():
            self.doc_writeup = self.doc_writeup[0]
        else:
            self.doc_writeup = None
        super(WriteUpEditForm, self).__init__(*args, **kwargs)
        self.person = get_person_for_user(self.user)

    def get_writeup(self):
        return self.data.get('writeup', self.doc_writeup and self.doc_writeup.writeup or '')

    def save(self):
        if settings.USE_DB_REDESIGN_PROXY_CLASSES:
            e = WriteupDocEvent(type="changed_protocol_writeup")
            e.doc = self.doc
            e.by = self.person
            e.desc = e.get_type_display()
            e.text = self.cleaned_data['writeup']
            e.save()
            from ietf.wgchairs.models import ProtoWriteUpProxy
            self.doc_writeup = ProtoWriteUpProxy.objects.get(pk=e.pk)
        else:
            if not self.doc_writeup:
                self.doc_writeup = ProtoWriteUp.objects.create(
                    person=self.person,
                    draft=self.doc,
                    writeup=self.cleaned_data['writeup'])
            else:
                self.doc_writeup.writeup = self.cleaned_data['writeup']
                self.doc_writeup.save()

        if self.data.get('modify_tag', False):
            followup = self.cleaned_data.get('followup', False)
            comment = self.cleaned_data.get('comment', False)
            try:
                shepherd = self.doc.shepherd
            except PersonOrOrgInfo.DoesNotExist:
                shepherd = None
            if shepherd:
                if settings.USE_DB_REDESIGN_PROXY_CLASSES:
                    extra_notify = [shepherd.formatted_email()]
                else:
                    extra_notify = ['%s <%s>' % shepherd.email()]
            else:
                extra_notify = []
            if settings.USE_DB_REDESIGN_PROXY_CLASSES:
                tags = DocTagName.objects.filter(slug="sheph-u")
            else:
                tags = [FOLLOWUP_TAG]
            if followup:
                update_tags(self.request, self.doc, comment, self.person, set_tags=tags, extra_notify=extra_notify)
            else:
                update_tags(self.request, self.doc, comment, self.person, reset_tags=tags, extra_notify=extra_notify)
        return self.doc_writeup

    def is_valid(self):
        if self.data.get('confirm', False) and self.data.get('modify_tag', False):
            self.fields['comment'].required = True
        else:
            self.fields['comment'].required = False
        return super(WriteUpEditForm, self).is_valid()
