"""This module contains the views of the ``accounts`` app"""

import os
import uuid

from django.conf import settings
from django.contrib import messages
from django.contrib.auth import update_session_auth_hash, REDIRECT_FIELD_NAME
from django.contrib.auth.decorators import login_required
from django.contrib.auth import login
from django.core.urlresolvers import reverse
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, redirect, render_to_response, resolve_url
from django.template import RequestContext
from django.template.response import TemplateResponse
from django.utils import timezone
from django.utils.http import is_safe_url
from django.utils.translation import ugettext as _
from django.views import generic
from django.views.decorators.debug import sensitive_post_parameters
from django.views.decorators.csrf import csrf_protect

from django_filters.views import FilterView

from mtp.apps.accounts.forms import MTPAuthenticationForm
from mtp.apps.common.notifications.tasks import send_async
from mtp.apps.common.utils import handle_uploaded_file
from mtp.apps.common.views_utils import (
    RestrictToSuperuserMixin,
    SuccessMessageMixin,
    SyncableDeleteView,
)
from mtp.apps.trainings.models import TrainingSession
from mtp.apps.translation.utils import set_language

from .filters import MTPUserFilterSet
from .forms import (
    GroupForm, PasswordChangeForm,
    UploadFileForm, UserForm, UserImportForm,
)
from .models import Group, MTPUser
from .utils import init_user, xls_import_trainee


@sensitive_post_parameters()
@csrf_protect
@login_required
def password_change(request,
                    template_name='registration/password_change_form.html',
                    post_change_redirect=None,
                    password_change_form=PasswordChangeForm,
                    current_app=None, extra_context=None):
    """
    Display the password change form and handles the password change action.

    This is lifted from the Django source code because we need to implement
    extra checks there, and unfortunately there are no class-based views (yet?)
    in django.contrib.auth, so we can't properly extend it... :-/
    """
    if post_change_redirect is None:
        post_change_redirect = reverse('password_change_done')
    else:
        post_change_redirect = resolve_url(post_change_redirect)

    if request.method == 'POST':
        form = password_change_form(user=request.user, data=request.POST)
        if form.is_valid():
            form.save()

            # === MTP-specific code starts here ===
            user = form.user
            user.last_changed_password = timezone.now()
            user.save()
            # === MTP-specific code stops here ===

            # Updating the password logs out all other sessions for the user
            # except the current one if
            # django.contrib.auth.middleware.SessionAuthenticationMiddleware
            # is enabled.
            update_session_auth_hash(request, form.user)
            return HttpResponseRedirect(post_change_redirect)
    else:
        form = password_change_form(user=request.user)

    context = {
        'form': form,
        'title': _('Password change'),
    }
    if extra_context is not None:
        context.update(extra_context)
    return TemplateResponse(request, template_name, context,
                            current_app=current_app)


class UserBaseView:
    """Base view for all views related to the users

    Define the ``current_main_section`` in the context, and the queryset.

    """

    model = MTPUser
    pk_url_kwarg = 'user_uuid'

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

        return super().get_context_data(
            current_main_section='users',
            **kwargs
        )

    def get_queryset(self):
        """Return the users list the current user is able to see

        Returns
        -------
        ``django.db.models.QuerySet``
            The django queryset of ``MTPUser``

        """

        return MTPUser.objects.filter(is_superuser=False)

    def get_success_url(self):
        """Get the default success_url"""

        return self.model.get_list_url()


class UserListView(RestrictToSuperuserMixin, UserBaseView, FilterView):
    """View to list the ``MTPUser``"""

    paginate_by = 30
    template_name = 'cms/accounts/mtpuser_list.html'
    filterset_class = MTPUserFilterSet

    def get_queryset(self):
        """Order the QuerySet by the ``username``"""

        return super().get_queryset().order_by('username')


class UserEditBaseView(SuccessMessageMixin, UserBaseView):
    """Base View to create or edit a ``MTPUser`` instance"""

    form_class = UserForm

    def get_queryset(self):
        if self.request.user.is_superuser:
            return MTPUser.objects.non_deleted()
        else:
            return super().get_queryset()

    def get_form_kwargs(self):
        kwargs = super().get_form_kwargs()

        kwargs['current_user'] = self.request.user
        return kwargs


class UserUpdateView(UserEditBaseView, generic.UpdateView):
    """View to edit a ``MTPUser`` instance"""

    success_message = _('The user was successfully updated.')

    def get_queryset(self):
        """Get the users the current user can edit

        The users are filtering accordingly to the current user type:

        - superuser: Can edit all the users
        - not superusers: Can only edit themselves
        """

        if not self.request.user.is_superuser:
            return super().get_queryset().filter(
                pk=self.request.user.pk
            )
        else:
            return super().get_queryset()

    def form_valid(self, form):
        """Update the user, and change his password if applicable.

        Parameters
        ----------
        form : instance of ``mtp.apps.accounts.forms.UserForm``
            The validated form to save.

        Returns
        -------
        instance of django.http.HttpResponseRedirect
            A redirect to the user list view

        """

        password = form.cleaned_data.get('pass1')
        user = form.save()

        if password:
            user.set_password(password)
            user.save()

        self.send_message(form.cleaned_data)

        # Change interface language according natural_language of user
        lang_code = form.cleaned_data.get('natural_language')
        response = HttpResponseRedirect(self.get_success_url())

        if self.request.user == user:
            set_language(lang_code, self.request, response)

        return response


class UserCreateView(RestrictToSuperuserMixin, UserEditBaseView, generic.CreateView):
    """View to create a ``MTPUser`` instance"""

    success_message = _('The user was successfully created.')

    def form_valid(self, form):
        """Create the new user, and send a welcome mail if applicable.

        Parameters
        ----------
        form : instance of ``mtp.apps.accounts.forms.UserForm``
            The validated form to save.

        Returns
        -------
        instance of django.http.HttpResponseRedirect
            A redirect to the user list view or the create user view

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

        """

        # We need to know if the user is a trainer
        is_trainer = bool(self.request.GET.get('trainer', False))

        password = form.cleaned_data.get('pass1')
        user = form.save()

        # TODO: Do this init_user is still needed ?
        user, password = init_user(user, password, None, is_trainer)

        # Send the welcome mail if the instance is a ``MASTER``
        if settings.INSTANCE_IS_MASTER:
            send_async.delay([user], 'user_welcome', {
                'password': password
            })

        self.send_message(form.cleaned_data)

        if '_addanother' in self.request.POST:
            return HttpResponseRedirect(self.model.get_create_url(trainer=is_trainer))
        else:
            return HttpResponseRedirect(self.get_success_url())


class UserDeleteView(SuccessMessageMixin, UserBaseView, SyncableDeleteView):
    """View to soft-delete a ``MTPUser`` instance"""

    success_message = _('The user was successfully deleted.')


class UserWelcomeView(UserBaseView, generic.DetailView):
    """View to send the welcome mail to a user"""

    def get(self, request, *args, **kwargs):
        """Send the welcome mail to the user and redirect the user"""

        user = self.get_object()

        # Send the welcome notification, without the user password
        send_async.delay([user], 'user_welcome', {
            'password': '********'
        })

        messages.success(request,
            _('A welcome mail has been successfully resent to %(username)s.' %
              {'username': user.username}))

        return HttpResponseRedirect(self.get_success_url())


@login_required
def users_import(request, session_id=None):
    if session_id:
        session = get_object_or_404(TrainingSession, pk=session_id)
    else:
        session = None

    if request.user.is_staff:
        if request.method == "POST":
            fileForm = UploadFileForm(request.POST, request.FILES)
            userForm = UserImportForm(request.POST)
            if userForm.is_valid():
                filename = userForm.cleaned_data['filename']
                save_path = os.path.join(settings.MEDIA_ROOT, "user_import")
                save_realname = os.path.join(save_path, filename)
                users = xls_import_trainee(save_realname, True)
                if session:
                    session.users.add(*users)
                    session.save()

                    # for user in users:
                    # send_session_add_mail(user)

                messages.success(request, _(
                    "%d users successfully imported or updated." % (len(users))
                ))
                if session:
                    return redirect('trainings:session-list')
                else:
                    return redirect('accounts:user-list')

            elif fileForm.is_valid():
                save_filename = '%s.xls' % (uuid.uuid4())
                save_path = os.path.join(settings.MEDIA_ROOT, 'user_import')
                save_realname = os.path.join(save_path, save_filename)
                handle_uploaded_file(request.FILES['file'], save_realname)

                users = xls_import_trainee(save_realname, False)
                if len(users) == 0:
                    fileForm = UploadFileForm()
                    userForm = None
                    message = "No users to import or update with this file."

                else:
                    message = "%d users will be imported with this file." % (len(users))
                    userForm = UserImportForm(initial={'filename': save_filename})
                    fileForm = None
            else:
                userForm = None

        else:
            fileForm = UploadFileForm()
            userForm = None

        return render_to_response('accounts/users_import.html',
            locals(), context_instance=RequestContext(request))

    else:
        return redirect('accounts:user-list')


class GroupBaseView:
    """Base view for all views related to the groups

    Define the ``current_main_section`` in the context, and the queryset.

    """

    model = Group
    pk_url_kwarg = 'group_uuid'

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

        return super().get_context_data(
            current_main_section='groups',
            **kwargs
        )

    def get_queryset(self):
        """Return the group list the current user is able to see

        The superusers can view and edit all the groups.
        The non-superusers can only view and edit the groups they are managing.

        Returns
        -------
        ``django.db.models.QuerySet``
            The django queryset of ``Group``

        """

        group_qs = Group.objects.non_deleted()

        if self.request.user.is_superuser:
            return group_qs
        else:
            return group_qs.managed_by(self.request.user)

    def get_success_url(self):
        """Get the default success_url"""

        return self.model.get_list_url()


class GroupListView(GroupBaseView, generic.ListView):
    """View to list the ``mtp.apps.accounts.models.Group`` instances."""

    paginate_by = 30


class GroupEditBaseView(SuccessMessageMixin, GroupBaseView):
    """Base View to edit a ``mtp.apps.accounts.models.Group`` instance."""

    form_class = GroupForm

    def get_success_url(self):
        """Return the url to redirect to when an action is done to a group

        It actually return the url of the group's list.

        """

        if '_addanother' in self.request.POST:
            return self.model.get_create_url()
        else:
            return self.model.get_list_url()


class GroupUpdateView(GroupEditBaseView, generic.UpdateView):
    """View to update a ``mtp.apps.accounts.models.Group`` instance."""

    success_message = _('The group was successfully updated.')


class GroupCreateView(GroupEditBaseView, generic.CreateView):
    """View to create a ``mtp.apps.accounts.models.Group`` instance."""

    success_message = _('The group was successfully created.')


class GroupDeleteView(SuccessMessageMixin, GroupBaseView, SyncableDeleteView):
    """View to soft-delete a ``mtp.apps.accounts.models.Group`` instance."""

    success_message = _('The group was successfully deleted.')


class LoginUserView(generic.FormView):
    template_name = 'registration/login.html'
    form_class = MTPAuthenticationForm

    def get_success_url(self):
        """Get the url to redirect to once successfully authenticated.

        Extracted ``from django.contrib.auth.views.login``

        Basically it check for a ``next`` parameter in ``POST`` or ``GET``, check for
        safety, and use it if OK. If no ``next`` or not a valid one, use the one defined in
        ``settings.LOGIN_REDIRECT_URL``.

        """

        redirect_to = self.request.POST.get(
            REDIRECT_FIELD_NAME,
            self.request.GET.get(REDIRECT_FIELD_NAME, '')
        )

        # Ensure the user-originating redirection url is safe.
        if not is_safe_url(url=redirect_to, host=self.request.get_host()):
            redirect_to = resolve_url(settings.LOGIN_REDIRECT_URL)

        return redirect_to

    def form_valid(self, form):
        user = form.get_user()
        if user is not None and user.is_active:
            login(self.request, user)
            response = super().form_valid(form)
            set_language(user.natural_language, self.request, response)
            return response
        return self.render_to_response(self.get_context_data(form=form))
