from django import forms
from django.forms import inlineformset_factory, widgets
from django.contrib.auth.models import User, Group
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from django.db.models import Q

from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field, Div, Submit
from crispy_forms.bootstrap import Accordion, AccordionGroup

from danceschool.core.models import EventOccurrence, Event
from danceschool.core.utils.timezone import ensure_timezone
from danceschool.core.forms import LocationWithDataWidget

from .models import PrivateEvent, EventReminder

EVENT_REPEAT_CHOICES = [
    (str(timedelta(hours=1).total_seconds()), _('Hour')),
    (str(timedelta(days=1).total_seconds()), _('Day'), ),
    (str(timedelta(days=7).total_seconds()), _('Week')),
    ('months=+1', _('Month')),
    ('years=+1', _('Year')),
]

VISIBILITY_CHOICES = [
    ('all', _('All Staff')),
    ('me', _('Only To Me')),
    ('group', _('Select User Group')),
    ('users', _('Select Users')),
]

REMINDER_SET_CHOICES = [
    ('none', _('No One')),
    ('all', _('All Staff')),
    ('me', _('Only To Me')),
    ('group', _('Select User Group')),
    ('users', _('Select Users')),
]

REMINDER_TIME_CHOICES = [
    (0, _('0 minutes')),
    (30, _('30 minutes')),
    (60, _('1 hour')),
    (720, _('12 hours')),
    (4320, _('3 days')),
]


class AddPrivateEventForm(forms.ModelForm):

    visibleTo = forms.ChoiceField(label=_('Make this event visible to:'), choices=VISIBILITY_CHOICES, initial='all')

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

        if user:
            kwargs.update(initial={
                'displayToUsers': [user.id, ],
                'submissionUser': user.id,
            })

        super().__init__(*args, **kwargs)
        self.fields['submissionUser'].widget = forms.HiddenInput()
        self.fields['status'].widget = forms.HiddenInput()
        self.fields['status'].initial = Event.RegStatus.hidden
        self.helper = FormHelper()
        self.helper.form_method = 'post'
        self.helper.form_tag = False  # Our template must explicitly include the <form tag>
        self.helper.layout = Layout(
            'status',
            'submissionUser',
            'title',
            Div(
                Field('category', wrapper_class='col'),
                Field('visibleTo', wrapper_class='col'),
                css_class='form-row'),
            Div('displayToUsers'),
            Div('displayToGroup'),
            Accordion(
                AccordionGroup(_('Add A Description'), 'descriptionField', active=False),
                AccordionGroup(_('Add A Location'), Div('location', 'room', 'locationString')),
                AccordionGroup(_('Add a Link'), 'link'),
            ),
        )

    def clean(self):
        super().clean()
        visibleTo = self.cleaned_data.get('visibleTo')
        submissionUser = self.cleaned_data.get('submissionUser')

        # Set visibility based on the choice the user made
        if visibleTo == 'all':
            self.cleaned_data.pop('displayToUsers', None)
            self.cleaned_data.pop('displayToGroup', None)
        elif visibleTo == 'me':
            self.cleaned_data['displayToUsers'] = [submissionUser, ]
            self.cleaned_data.pop('displayToGroup', None)
        elif visibleTo == 'users':
            self.cleaned_data.pop('displayToGroup', None)
        elif visibleTo == 'group':
            self.cleaned_data.pop('displayToUsers', None)

    class Meta:
        model = PrivateEvent
        exclude = []
        widgets = {
            'location': LocationWithDataWidget,
        }

    class Media:
        js = (
            'timepicker/jquery.timepicker.min.js',
            'jquery-ui/jquery-ui.min.js',
            'moment/moment.min.js',
            'js/addEvent_rollups.js',
            'js/serieslocation_capacity_change.js',
        )
        css = {
            'all': (
                'timepicker/jquery.timepicker.css', 'jquery-ui/jquery-ui.min.css',
            )
        }


class AddEventOccurrenceForm(forms.ModelForm):
    allDay = forms.BooleanField(label=_('All Day'), required=False)

    def clean(self):
        startTime = self.cleaned_data['startTime']
        endTime = self.cleaned_data['endTime']
        allDay = self.cleaned_data.pop('allDay', None) or False
        if allDay:
            self.cleaned_data['startTime'] = ensure_timezone(datetime(startTime.year, startTime.month, startTime.day))
            self.cleaned_data['endTime'] = (
                ensure_timezone(datetime(endTime.year, endTime.month, endTime.day)) +
                timedelta(days=1)
            )
        if self.cleaned_data['endTime'] < self.cleaned_data['startTime']:
            raise ValidationError(_('End time cannot be before start time.'), code='invalid')

    class Meta:
        model = EventOccurrence
        exclude = []


class OccurrenceFormSetHelper(FormHelper):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.form_tag = False  # Our template must explicitly include the <form tag>
        self.layout = Layout(
            Div(
                Field('startTime', wrapper_class='col', ),
                Field('endTime', wrapper_class='col', ),
                Field('allDay', wrapper_class='col', ),
                css_class='form-row'
            ),
            Div(
                Field('extraOccurrencesToAdd', wrapper_class='col', ),
                Field('extraOccurrenceRule', wrapper_class='col', ),
                css_class="form-row"
            ),
            Div(
                Field('sendReminderTo', wrapper_class='col', ),
                Field('sendReminderWhen', wrapper_class='col', ),
                Field('sendReminderWhich', wrapper_class='col', ),
                css_class="form-row"
            ),
            Div('sendReminderGroup'),
            Div('sendReminderUsers'),
            Submit('submit', u'Submit', css_class='btn btn-primary'),
        )


class EventOccurrenceCustomFormSet(forms.BaseInlineFormSet):
    ''' Formset for occurrences added via the Private Events form '''

    def add_fields(self, form, index):
        super().add_fields(form, index)
        form.fields['startTime'] = forms.SplitDateTimeField(
            label=_("Start Time"), input_time_formats=['%I:%M%p', '%-I:%M%p'],
            widget=widgets.SplitDateTimeWidget
        )
        form.fields['endTime'] = forms.SplitDateTimeField(
            label=_("End Time"), input_time_formats=['%I:%M%p', '%-I:%M%p'],
            widget=widgets.SplitDateTimeWidget
        )
        form.fields['extraOccurrencesToAdd'] = forms.IntegerField(
            label=_("Repeat __ times:"), initial=0, min_value=0, max_value=100,
            required=False
        )
        form.fields['extraOccurrenceRule'] = forms.ChoiceField(
            label=_("Every:"), choices=EVENT_REPEAT_CHOICES, required=False
        )
        form.fields['sendReminderTo'] = forms.ChoiceField(
            label=_("Send A Reminder To:"), choices=REMINDER_SET_CHOICES,
            initial='none', required=False
        )
        form.fields['sendReminderWhen'] = forms.ChoiceField(
            label=_("Before:"), choices=REMINDER_TIME_CHOICES,
            initial=0, required=False
        )
        form.fields['sendReminderWhich'] = forms.ChoiceField(
            label=_("Ahead Of:"),
            choices=(
                ('all', _('All Occurrences')),
                ('first', _('First Occurrence Only'))
            ),
            initial='all', required=False
        )
        form.fields['sendReminderGroup'] = forms.ModelChoiceField(
            label=_("Remind Group:"), queryset=Group.objects.all(),
            required=False
        )
        form.fields['sendReminderUsers'] = forms.ModelMultipleChoiceField(
            label=_("Remind Users:"),
            queryset=User.objects.filter(staffmember__isnull=False),
            required=False
        )

    def setReminder(self, occurrence):
        '''
        This function is called to create the actual reminders for each occurrence that is created.
        '''
        sendReminderTo = self[0].cleaned_data['sendReminderTo']
        sendReminderWhen = self[0].cleaned_data['sendReminderWhen']
        sendReminderGroup = self[0].cleaned_data['sendReminderGroup']
        sendReminderUsers = self[0].cleaned_data['sendReminderUsers']

        # Set the new reminder's time
        new_reminder_time = occurrence.startTime - timedelta(minutes=int(float(sendReminderWhen)))
        new_reminder = EventReminder(eventOccurrence=occurrence, time=new_reminder_time)
        new_reminder.save()

        # Set reminders based on the choice the user made
        if sendReminderTo == 'all':
            user_set = User.objects.filter(Q(staffmember__isnull=False) | Q(is_staff=True))
        elif sendReminderTo == 'me':
            user_set = User.objects.filter(id=occurrence.event.submissionUser.id)
        elif sendReminderTo == 'users':
            user_set = User.objects.filter(**{'id__in': sendReminderUsers})
        elif sendReminderTo == 'group':
            user_set = User.objects.filter(**{'groups': sendReminderGroup})
        else:
            user_set = []

        for user in user_set:
            new_reminder.notifyList.add(user)

    def save(self):
        extraOccurrencesToAdd = self[0].cleaned_data['extraOccurrencesToAdd']
        extraOccurrenceRule = self[0].cleaned_data['extraOccurrenceRule']
        sendReminderTo = self[0].cleaned_data['sendReminderTo']
        sendReminderWhich = self[0].cleaned_data['sendReminderWhich']

        # Save this actual occurrence first
        this_occurrence = super().save()

        # Create a reminder for this occurrence if applicable
        if sendReminderTo in ['all', 'me', 'users', 'group']:
            self.setReminder(this_occurrence[0])

        # Create new occurrences based on the number of occurrences to add and the occurrence rule given
        remainingOccurrences = extraOccurrencesToAdd

        # These will be repopulated with each loop and used to calculate the next event times
        old_startTime = this_occurrence[0].startTime
        old_endTime = this_occurrence[0].endTime

        # Make a new occurrence for each requested new occurrence.  Also, make a reminder if requested
        while remainingOccurrences > 0:
            if 'months' not in extraOccurrenceRule and 'years' not in extraOccurrenceRule:
                time_diff = timedelta(seconds=float(extraOccurrenceRule))
                new_startTime = old_startTime + time_diff
                new_endTime = old_endTime + time_diff
            elif 'months' in extraOccurrenceRule:
                num_months = int(float(extraOccurrenceRule.replace('months=', '')))
                new_startTime = old_startTime + relativedelta(months=num_months)
                new_endTime = old_endTime + relativedelta(months=num_months)
            elif 'years' in extraOccurrenceRule:
                num_years = int(float(extraOccurrenceRule.replace('years=', '')))
                new_startTime = old_startTime + relativedelta(years=num_years)
                new_endTime = old_endTime + relativedelta(years=num_years)

            # Create the occurrence
            new_occ = EventOccurrence(event=this_occurrence[0].event, startTime=new_startTime, endTime=new_endTime)
            new_occ.save()

            # Create the reminders
            if sendReminderWhich == 'all' and sendReminderTo in ['all', 'me', 'users', 'group']:
                self.setReminder(new_occ)

            # Finally, prepare for the next iteration of the loop
            old_startTime = new_startTime
            old_endTime = new_endTime
            remainingOccurrences -= 1


EventOccurrenceFormSet = inlineformset_factory(
    PrivateEvent, EventOccurrence,
    formset=EventOccurrenceCustomFormSet,
    form=AddEventOccurrenceForm,
    exclude=[],
    extra=1, can_delete=False
)
