""" This module holds the views of the ``metaplans`` app. """

import csv

from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.utils.functional import cached_property
from django.utils.translation import ugettext as _
from django.views.generic.detail import DetailView
from django.views.generic.edit import CreateView, UpdateView
from django.views.generic.list import ListView

from mtp.apps.accounts.models import MTPUser
from mtp.apps.common.utils import EXCEL_BOM
from mtp.apps.common.views_utils import (
    AjaxResponseMixin,
    ConditionalMixin,
    RestrictToSuperuserOrTrainerMixin,
    SuccessMessageMixin,
    SyncableDeleteView,
)
from mtp.apps.import_export.exceptions import ImportDataError
from mtp.apps.import_export.views import XLSExportDetailView, ActivityXLSImportView
from mtp.apps.metaplans.resources import (
    MetaplanTemplateResource
)
from mtp.apps.trainings.models import Training, TrainingSession

from .forms import MetaplanForm, MetaplanPropositionForm
from .models import Metaplan, MetaplanProposition, MetaplanTemplate


class MetaplanSessionListView(RestrictToSuperuserOrTrainerMixin, ListView):
    """ Return Metaplan List for a session. """

    def get_queryset(self):
        session = get_object_or_404(TrainingSession, id=self.kwargs['session_id'])
        return Metaplan.objects.filter(session=session, is_deleted=False)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        session = get_object_or_404(TrainingSession, id=self.kwargs['session_id'])
        context.update({
            'session': session,
        })
        return context


class MetaplanDetailView(RestrictToSuperuserOrTrainerMixin, DetailView):
    """ Detail of Metaplan propositions. """

    model = Metaplan


class MetaplanEditBaseMixin(RestrictToSuperuserOrTrainerMixin, SuccessMessageMixin):
    """ Edit base for Metaplan. """

    model = Metaplan

    def get_success_url(self):
        return reverse(
            'metaplans:list_by_session', kwargs={'session_id': self.object.session.id})


class MetaplanCreateView(ConditionalMixin, MetaplanEditBaseMixin, CreateView):
    """ Create Metaplan. """

    form_class = MetaplanForm
    success_message = _('Your metaplan was successfully created.')

    @cached_property
    def session(self):
        """Get the session associated with this request"""

        return get_object_or_404(TrainingSession, id=self.kwargs['session_id'])

    def get_success_url(self):
        """Get the url of the redirection when create is successful"""

        return reverse(
            'metaplans:list_by_session', kwargs={'session_id': self.session.id})

    def get_initial(self):
        """Get initial data for the form"""

        initial = {'session': self.session}

        return initial

    def is_condition_met(self):
        """Only allow create when session is not closed"""

        return not self.session.is_closed

    def condition_not_met(self):
        """Action to take if the session is closed.

        Raises
        ------
        PermissionDenied
            When the session is closed.

        """

        raise PermissionDenied


class MetaplanUpdateView(MetaplanEditBaseMixin, UpdateView):
    """ Update Metaplan. """

    form_class = MetaplanForm
    success_message = _('Your metaplan was successfully updated.')


class MetaplanDeleteView(MetaplanEditBaseMixin, SyncableDeleteView):
    """ Delete Metaplan. """

    success_message = _('Your metaplan was successfully deleted.')


class MetaplanTemplateEditBaseMixin(
        AjaxResponseMixin, RestrictToSuperuserOrTrainerMixin, SuccessMessageMixin):
    """ Edit base for Metaplan Template. """

    model = MetaplanTemplate
    pk_url_kwarg = 'template_uuid'
    fields = [
        'name',
        'description'
    ]

    def get_form(self, form_class=None):
        """Override the max_length and the help_text attributes on the field name

        We can't enforce this at the model level since the "name" field
        is inherited from ``TrainingActivity``.

        For the parameters, see ``django.views.generics.edit.ModelFormMixin``
        """

        form = super().get_form(form_class)
        form.fields['name'].widget.attrs['maxlength'] = 100
        form.fields['name'].help_text = _('Maximum 100 characters')

        return form


class MetaplanTemplateCreateView(MetaplanTemplateEditBaseMixin, CreateView):
    """ Create Metaplan Template. """

    fields = [
        'name',
        'description'
    ]
    success_message = _('Your metaplan template was successfully created.')

    @cached_property
    def training(self):
        """Returns the current training based on the ``training_uuid``
        argument in the url.

        Raises
        ------
        ``django.http.Http404``
            If the training doesn't exists.

        """
        return get_object_or_404(
            Training,
            pk=self.kwargs['training_uuid']
        )

    def get_context_data(self, **kwargs):
        """Add ``training`` to the context.

        For the parameters, see ``django.forms.generic.base.ContextMixin``
        """

        return super().get_context_data(training=self.training, **kwargs)

    def form_valid(self, form):
        """Set the ``training``, save the metaplan template and
        redirect the user.

        Parameters
        ----------
        form : instance of ``django.forms.models.ModelForm``
            The validated form to save.

        Returns
        -------
        instance of django.http.HttpResponseRedirect
            A redirect to the list of the training activity.

        Notes
        -----
        This method doesn't call ``super`` but implements all the necessary
        stuff (saving and redirecting)

        """
        self.object = form.save(commit=False)
        self.object.training = self.training
        self.object.save()

        return super().form_valid(form)


class MetaplanTemplateDetailView(MetaplanTemplateEditBaseMixin, DetailView):
    """ Details of a Metaplan Template. """


class MetaplanTemplateUpdateView(MetaplanTemplateEditBaseMixin, UpdateView):
    """ Update Metaplan Template. """

    success_message = _('Your metaplan template was successfully updated.')


class MetaplanTemplateDeleteView(MetaplanTemplateEditBaseMixin, SyncableDeleteView):
    """ Delete Metaplan Template. """

    success_message = _('Your metaplan template was successfully deleted.')


class MetaplanPropositionEditBaseMixin(RestrictToSuperuserOrTrainerMixin, SuccessMessageMixin):
    """ Edit base for Metaplan Proposition. """

    model = MetaplanProposition

    def get_success_url(self):
        return reverse(
            'metaplans:detail',
            kwargs={'pk': self.kwargs['metaplan_id']}
        )


class MetaplanPropositionCreateView(MetaplanPropositionEditBaseMixin, CreateView):
    """ Create Metaplan Proposition. """

    form_class = MetaplanPropositionForm
    success_message = _('Your metaplan proposition was successfully created.')

    def get_initial(self):
        metaplan = get_object_or_404(Metaplan, id=self.kwargs['metaplan_id'])
        initial = {'metaplan': metaplan}
        return initial

    def get_form(self, form_class=None):
        form = super(MetaplanPropositionCreateView, self).get_form(form_class)
        metaplan = get_object_or_404(Metaplan, id=self.kwargs['metaplan_id'])

        # Change field queryset for adapt them to context session and metaplan
        session_users_ids = metaplan.session.users.values_list('id', flat=True)
        form.fields['user'].queryset = MTPUser.objects.filter(
            id__in=session_users_ids
        )
        return form


class MetaplanExportView(RestrictToSuperuserOrTrainerMixin, DetailView):
    """ Export Metaplan propositions. """

    model = Metaplan

    def get(self, *args, **kwargs):
        super(MetaplanExportView, self).get(*args, **kwargs)

        # Create the HttpResponse object with the appropriate CSV header.
        response = HttpResponse(content_type='text/csv')
        response['Content-Disposition'] = 'attachment; filename="metaplan.csv"'

        writer = csv.writer(response, delimiter=';')

        # Header
        writer.writerow([EXCEL_BOM])  # BOM for excel
        writer.writerow([
            'Metaplan Propositions',
            self.object.name])
        writer.writerow([])

        # Generation of CSV
        grouped_propositions = self.object.get_grouped_propositions()
        for group, propositions in grouped_propositions.items():
            writer.writerow([
                'Group',
                'n°' + str(group),
                '%s propositions' % (len(propositions))])
            for ind, proposition in enumerate(propositions):
                writer.writerow([str(ind + 1),
                                 proposition.text,
                                 proposition.user])

        return response


class XLSMetaplanTemplateExportView(XLSExportDetailView):
    """Export XLS Metaplan Template"""

    resource = MetaplanTemplateResource
    pk_url_kwarg = 'metaplan_template_uuid'


class XLSMetaplanTemplateImportView(ActivityXLSImportView):

    def import_data(self, sheet_name):
        resource = MetaplanTemplateResource
        data_object = self.reader.read_values(
            sheet_name,
            len(resource.get_fields(allow_nested=False))
        )

        if self.is_compatible(data_object, resource):
            obj, __ = self.create_object(
                data_object, resource=resource, force_creation=True,
                training=self.training)
        else:
            fields = resource.get_display_fields(allow_nested=False)
            raise ImportDataError(
                _("Data is not compatible, we need this fields %(fields)s.") % {'fields': fields},
                line=data_object.line
            )

        return obj
