from django.db.models import Q, F, Value, CharField, IntegerField
from django.utils.translation import gettext
from django.utils.html import format_html
from django.apps import apps

from dal import autocomplete
from datetime import timedelta
from dateutil.parser import parse

from danceschool.core.models import Customer, Event, EventOccurrence
from danceschool.core.utils.timezone import ensure_localtime


class RegisterAutoComplete(autocomplete.Select2QuerySetView):

    def get_result_value(self, result):
        """Return the value of a result."""
        return '{} {} ({})'.format(
            result.get('firstName'), result.get('lastName'), result.get('guestType')
        )

    def get_result_label(self, result):
        """Return the label of a result."""
        if result.get('guestType') != 'Customer':
            return format_html(
                '<span data-id="{id}" data-type="{guestType}" data-model-type="{modelType}"' +
                'data-guest-list-id="{guestListId}">{firstName} {lastName} ({guestType})</span>',
                **result
            )
        return format_html(
            '<span data-id="{id}" data-type="{guestType}" data-model-type="{modelType}"' +
            'data-guest-list-id="{guestListId}">{firstName} {lastName}</span>',
            **result
        )

    def get_queryset(self):
        # Filter out results for unauthenticated users.
        if not self.request.user.has_perm('core.can_autocomplete_users'):
            return Customer.objects.none()

        try:
            date = parse(self.forwarded.get('date', ''))
        except ValueError:
            date = None

        name_filters = Q()
        if self.q:
            words = self.q.split(' ')
            lastName = words.pop()
            firstName = words.pop() if words else lastName
            name_filters = Q(firstName__icontains=firstName) | Q(lastName__icontains=lastName)

        customer_filters = Q()
        if date:
            start = ensure_localtime(date)
            end = ensure_localtime(date) + timedelta(days=1)

            today_occs = EventOccurrence.objects.filter(
                endTime__gte=start, startTime__lte=end
            )
            today_events = Event.objects.filter(eventoccurrence__in=today_occs).distinct()

            customer_filters = (
                Q(eventregistration__event__in=today_events) &
                Q(eventregistration__registration__final=True) & (
                    Q(eventregistration__dropIn=False) | (
                        Q(eventregistration__dropIn=True) &
                        Q(eventregistration__occurrences__in=today_occs)
                    )
                )
            )

        queryset = Customer.objects.annotate(
            firstName=F('first_name'), lastName=F('last_name'),
            modelType=Value('Customer', output_field=CharField()),
            guestListId=Value(None, output_field=IntegerField()),
            guestType=Value(gettext('Customer'), output_field=CharField()),
        ).filter(name_filters).filter(customer_filters).values(
            'id', 'modelType', 'guestListId', 'firstName', 'lastName',
            'guestType',
        )

        if date and apps.is_installed('danceschool.guestlist'):

            GuestList = apps.get_model('guestlist', 'GuestList')

            # Needed to avoid DatabaseError from ORDER BY in SQL subqueries.
            queryset = queryset.order_by()

            # This is the same logic as the appliesToEvent() method of GuestList
            applicable_lists = GuestList.objects.filter(
                Q(individualEvents__in=today_events) |
                Q(eventSessions__in=today_events.filter(session__isnull=False).values_list(
                    'session', flat=True
                )) |
                Q(seriesCategories__in=today_events.filter(
                    series__category__isnull=False
                ).values_list('series__category', flat=True)) |
                Q(eventCategories__in=today_events.filter(
                    publicevent__category__isnull=False
                ).values_list('publicevent__category', flat=True))
            )

            for this_list in applicable_lists:
                for this_event in today_events:
                    queryset = queryset.union(
                        this_list.getListForEvent(
                            this_event, filters=name_filters, includeRegistrants=False
                        ).order_by()
                    )

            # Now that all the subqueries are together, order by the common
            # lastName and firstName fields.
            queryset = queryset.values(
                'id', 'modelType', 'guestListId', 'firstName', 'lastName',
                'guestType',
            ).distinct().order_by(
                'lastName', 'firstName'
            )

        return queryset
