from cms.views import details as cms_view_details
from django.core.exceptions import PermissionDenied
from django.db.models import F
from django.http import HttpResponse
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse_lazy as reverse
from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _

from ..forms.subjects import (
    CourseRegistrationForm, EventRegistrationForm, OrderableRegistrationForm,
    SubjectFilterForm, SubjectForm,
)
from ..models.courses import Course
from ..models.events import Event
from ..models.leprikonsite import LeprikonSite
from ..models.orderables import Orderable
from ..models.subjects import (
    Subject, SubjectPayment, SubjectRegistration, SubjectType,
)
from .generic import (
    ConfirmUpdateView, CreateView, DetailView, FilteredListView, ListView,
    UpdateView,
)


class SubjectTypeMixin:
    _models = {
        SubjectType.COURSE: Course,
        SubjectType.EVENT: Event,
        SubjectType.ORDERABLE: Orderable,
    }

    def dispatch(self, request, subject_type, *args, **kwargs):
        self.subject_type = get_object_or_404(SubjectType, slug=subject_type)
        self.model = self._models[self.subject_type.subject_type]
        return super().dispatch(request, *args, **kwargs)

    def get_placeholder(self):
        return super().get_placeholder() + ':' + self.subject_type.slug

    def get_queryset(self):
        return super().get_queryset().filter(subject_type=self.subject_type)

    def get_template_names(self):
        return [
            'leprikon/{}{}.html'.format(self.subject_type.slug, self.template_name_suffix),
            'leprikon/{}{}.html'.format(self.subject_type.subject_type, self.template_name_suffix),
            'leprikon/subject{}.html'.format(self.template_name_suffix),
        ]


class CMSSubjectTypeMixin(SubjectTypeMixin):
    def dispatch(self, request, *args, **kwargs):
        # Get current CMS Page
        cms_page = request.current_page.get_public_object()

        # Check whether it really is a Leprikon subject type application.
        # It might also be one of its sub-pages.
        if cms_page.application_urls != 'LeprikonSubjectTypeApp':
            # In such case show regular CMS Page
            return cms_view_details(request, *args, **kwargs)
        return super(CMSSubjectTypeMixin, self).dispatch(request, cms_page.application_namespace, **kwargs)


class SubjectListBaseView(FilteredListView):
    form_class = SubjectFilterForm
    preview_template = 'leprikon/subject_preview.html'
    paginate_by = 10

    def get_title(self):
        return _('{subject_type} in school year {school_year}').format(
            subject_type=self.subject_type.plural,
            school_year=self.request.school_year,
        )

    def get_message_empty(self):
        return _('No {subject_type} matching given search parameters found.').format(
            subject_type=self.subject_type.plural,
        )

    def get_form(self):
        return self.form_class(
            subject_type_type=self.subject_type.subject_type,
            subject_types=[self.subject_type],
            school_year=self.request.school_year,
            is_staff=self.request.user.is_staff,
            data=self.request.GET,
        )

    def get_queryset(self):
        return self.get_form().get_queryset()


class SubjectListView(CMSSubjectTypeMixin, SubjectListBaseView):
    pass


class SubjectListMineView(SubjectTypeMixin, SubjectListBaseView):

    def get_template_names(self):
        return [
            'leprikon/{}_list_mine.html'.format(self.subject_type.slug),
            'leprikon/{}_list_mine.html'.format(self.subject_type.subject_type),
            'leprikon/subject_list_mine.html',
        ] + super(SubjectListMineView, self).get_template_names()

    def get_title(self):
        return _('My {subject_type} in school year {school_year}').format(
            subject_type=self.subject_type.plural,
            school_year=self.request.school_year,
        )

    def get_queryset(self):
        return super(SubjectListMineView, self).get_queryset().filter(leaders=self.request.leader)


class SubjectDetailView(CMSSubjectTypeMixin, DetailView):

    def get_queryset(self):
        qs = super(SubjectDetailView, self).get_queryset()
        if not self.request.user.is_staff:
            qs = qs.filter(public=True)
        return qs


class SubjectRegistrationsView(SubjectTypeMixin, DetailView):
    template_name_suffix = '_registrations'

    def get_queryset(self):
        qs = super(SubjectRegistrationsView, self).get_queryset()
        if not self.request.user.is_staff:
            qs = qs.filter(leaders=self.request.leader)
        return qs

    def get_context_data(self, **kwargs):
        context = super(SubjectRegistrationsView, self).get_context_data(**kwargs)
        context['site'] = LeprikonSite.objects.get_current()
        return context


class SubjectUpdateView(SubjectTypeMixin, UpdateView):
    form_class = SubjectForm

    def get_title(self):
        return _('Change {subject_type} {subject}').format(
            subject_type=self.subject_type.name_akuzativ,
            subject=self.object.name,
        )

    def get_message(self):
        return _('The changes in {subject_type} {subject} have been saved.').format(
            subject_type=self.subject_type.name_genitiv,
            subject=self.object.name,
        )

    def get_queryset(self):
        qs = super(SubjectUpdateView, self).get_queryset()
        if not self.request.user.is_staff:
            qs = qs.filter(leaders=self.request.leader)
        return qs


class SubjectMixin:
    def dispatch(self, request, pk, **kwargs):
        lookup_kwargs = {'subject_type': self.subject_type, 'id': int(pk)}
        if not self.request.user.is_staff:
            lookup_kwargs['public'] = True
        self.subject = get_object_or_404(Subject, **lookup_kwargs)
        if not self.subject.registration_allowed:
            return redirect(self.subject)
        self.request.school_year = self.subject.school_year
        return super().dispatch(request, **kwargs)


class SubjectRegistrationFormBaseView(CreateView):
    back_url = reverse('leprikon:registration_list')
    submit_label = _('Submit registration')
    message = _('The registration has been saved. We will inform you about its further processing.')
    template_name_suffix = '_registration_form'
    _form_classes = {
        SubjectType.COURSE: CourseRegistrationForm,
        SubjectType.EVENT: EventRegistrationForm,
        SubjectType.ORDERABLE: OrderableRegistrationForm,
    }

    def get_title(self):
        return _('Registration for {subject_type} {subject}').format(
            subject_type=self.subject_type.name_akuzativ,
            subject=self.subject.name,
        )

    def get_form_class(self):
        return self._form_classes[self.subject_type.subject_type]

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()
        kwargs['subject'] = self.subject
        kwargs['user'] = self.request.user
        return kwargs


class SubjectRegistrationFormView(CMSSubjectTypeMixin, SubjectMixin, SubjectRegistrationFormBaseView):
    pass


class UserRegistrationMixin:
    _attrs = {
        SubjectType.COURSE: 'courseregistration',
        SubjectType.EVENT: 'eventregistration',
        SubjectType.ORDERABLE: 'orderableregistration',
    }

    def get_object(self):
        subject_registration = super().get_object()
        if subject_registration.user != self.request.user:
            raise PermissionDenied()
        return getattr(
            subject_registration,
            self._attrs[subject_registration.subject_type_type],
        )

    def get_queryset(self):
        return SubjectRegistration.objects.annotate(
            subject_type_type=F('subject__subject_type__subject_type'),
        )

    def get_template_names(self):
        return [self.template_name] if self.template_name else [
            'leprikon/{}_registration{}.html'.format(self.object.subject.subject_type.slug,
                                                     self.template_name_suffix),
            'leprikon/{}_registration{}.html'.format(self.object.subject.subject_type.subject_type,
                                                     self.template_name_suffix),
            'leprikon/subject_registration{}.html'.format(self.template_name_suffix),
        ]


class PDFMixin:
    event = 'pdf'

    def get(self, request, *args, **kwargs):
        obj = self.get_object()
        response = HttpResponse(content_type='application/pdf')
        response['Content-Disposition'] = 'attachment; filename="{}"'.format(obj.get_pdf_filename(self.event))
        return obj.write_pdf(self.event, response)


class SubjectRegistrationPdfView(PDFMixin, UserRegistrationMixin, DetailView):
    pass


class SubjectRegistrationPaymentRequestView(PDFMixin, UserRegistrationMixin, DetailView):
    event = 'payment_request'


class SubjectRegistrationCancelView(UserRegistrationMixin, ConfirmUpdateView):
    template_name_suffix = '_cancel'
    title = _('Registration cancellation request')

    def get_queryset(self):
        return super().get_queryset().filter(canceled=None)

    def get_question(self):
        return _('Are you sure You want to cancel the registration "{}"?').format(self.object)

    def get_message(self):
        return _('The cancellation request for {} has been saved.').format(self.object)

    def confirmed(self):
        if self.object.approved:
            self.object.cancelation_requested = now()
            self.object.cancelation_requested_by = self.request.user
            self.object.save()
        else:
            self.object.refuse(self.request.user)


class UserPaymentMixin:
    model = SubjectPayment

    def get_queryset(self):
        return super(UserPaymentMixin, self).get_queryset().filter(
            registration__user=self.request.user
        ).order_by('-accounted')


class SubjectPaymentsListView(UserPaymentMixin, ListView):
    template_name = 'leprikon/payments.html'
    paginate_by = 20

    def get_title(self):
        return _('Payments')


class SubjectPaymentPdfView(PDFMixin, UserPaymentMixin, DetailView):
    pass
