import os

from corres2math.tokens import email_validation_token
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.models import User
from django.core.exceptions import PermissionDenied, ValidationError
from django.db import transaction
from django.http import FileResponse, Http404
from django.shortcuts import redirect, resolve_url
from django.urls import reverse_lazy
from django.utils.http import urlsafe_base64_decode
from django.utils.translation import gettext_lazy as _
from django.views.generic import CreateView, DetailView, RedirectView, TemplateView, UpdateView, View
from magic import Magic

from .forms import CoachRegistrationForm, PhotoAuthorizationForm, SignupForm, StudentRegistrationForm, UserForm
from .models import StudentRegistration


class SignupView(CreateView):
    model = User
    form_class = SignupForm
    template_name = "registration/signup.html"

    def get_context_data(self, **kwargs):
        context = super().get_context_data()

        context["student_registration_form"] = StudentRegistrationForm(self.request.POST or None)
        context["coach_registration_form"] = CoachRegistrationForm(self.request.POST or None)

        return context

    @transaction.atomic
    def form_valid(self, form):
        role = form.cleaned_data["role"]
        if role == "participant":
            registration_form = StudentRegistrationForm(self.request.POST)
        else:
            registration_form = CoachRegistrationForm(self.request.POST)

        if not registration_form.is_valid():
            return self.form_invalid(form)

        ret = super().form_valid(form)
        registration = registration_form.instance
        registration.user = form.instance
        registration.save()

        return ret

    def get_success_url(self):
        return reverse_lazy("registration:email_validation_sent")


class UserValidateView(TemplateView):
    """
    A view to validate the email address.
    """
    title = _("Email validation")
    template_name = 'registration/email_validation_complete.html'
    extra_context = {"title": _("Validate email")}

    def get(self, *args, **kwargs):
        """
        With a given token and user id (in params), validate the email address.
        """
        assert 'uidb64' in kwargs and 'token' in kwargs

        self.validlink = False
        user = self.get_user(kwargs['uidb64'])
        token = kwargs['token']

        # Validate the token
        if user is not None and email_validation_token.check_token(user, token):
            self.validlink = True
            user.registration.email_confirmed = True
            user.registration.save()
        return self.render_to_response(self.get_context_data(), status=200 if self.validlink else 400)

    def get_user(self, uidb64):
        """
        Get user from the base64-encoded string.
        """
        try:
            # urlsafe_base64_decode() decodes to bytestring
            uid = urlsafe_base64_decode(uidb64).decode()
            user = User.objects.get(pk=uid)
        except (TypeError, ValueError, OverflowError, User.DoesNotExist, ValidationError):
            user = None
        return user

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['user_object'] = self.get_user(self.kwargs["uidb64"])
        context['login_url'] = resolve_url(settings.LOGIN_URL)
        if self.validlink:
            context['validlink'] = True
        else:
            context.update({
                'title': _('Email validation unsuccessful'),
                'validlink': False,
            })
        return context


class UserValidationEmailSentView(TemplateView):
    """
    Display the information that the validation link has been sent.
    """
    template_name = 'registration/email_validation_email_sent.html'
    extra_context = {"title": _('Email validation email sent')}


class UserResendValidationEmailView(LoginRequiredMixin, DetailView):
    """
    Rensend the email validation link.
    """
    model = User
    extra_context = {"title": _("Resend email validation link")}

    def get(self, request, *args, **kwargs):
        user = self.get_object()
        user.registration.send_email_validation_link()
        return redirect('registration:email_validation_sent')


class MyAccountDetailView(LoginRequiredMixin, RedirectView):
    def get_redirect_url(self, *args, **kwargs):
        return reverse_lazy("registration:user_detail", args=(self.request.user.pk,))


class UserDetailView(LoginRequiredMixin, DetailView):
    model = User
    context_object_name = "user_object"
    template_name = "registration/user_detail.html"

    def dispatch(self, request, *args, **kwargs):
        user = request.user
        if not user.registration.is_admin and user.pk != kwargs["pk"]:
            raise PermissionDenied
        return super().dispatch(request, *args, **kwargs)


class UserUpdateView(LoginRequiredMixin, UpdateView):
    model = User
    form_class = UserForm
    template_name = "registration/update_user.html"

    def dispatch(self, request, *args, **kwargs):
        user = request.user
        if not user.registration.is_admin and user.pk != kwargs["pk"]:
            raise PermissionDenied
        return super().dispatch(request, *args, **kwargs)

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        user = self.get_object()
        context["registration_form"] = user.registration.form_class(data=self.request.POST or None,
                                                                    instance=self.object.registration)
        return context

    @transaction.atomic
    def form_valid(self, form):
        user = form.instance
        registration_form = user.registration.form_class(data=self.request.POST or None,
                                                         instance=self.object.registration)
        if not registration_form.is_valid():
            return self.form_invalid(form)

        registration_form.save()
        return super().form_valid(form)

    def get_success_url(self):
        return reverse_lazy("registration:user_detail", args=(self.object.pk,))


class UserUploadPhotoAuthorizationView(LoginRequiredMixin, UpdateView):
    model = StudentRegistration
    form_class = PhotoAuthorizationForm
    template_name = "registration/upload_photo_authorization.html"

    def dispatch(self, request, *args, **kwargs):
        user = request.user
        if not user.registration.is_admin and user.registration.pk != kwargs["pk"]:
            raise PermissionDenied
        return super().dispatch(request, *args, **kwargs)

    @transaction.atomic
    def form_valid(self, form):
        old_instance = StudentRegistration.objects.get(pk=self.object.pk)
        if old_instance.photo_authorization:
            old_instance.photo_authorization.delete()
        return super().form_valid(form)

    def get_success_url(self):
        return reverse_lazy("registration:user_detail", args=(self.object.user.pk,))


class PhotoAuthorizationView(LoginRequiredMixin, View):
    def get(self, request, *args, **kwargs):
        filename = kwargs["filename"]
        path = f"media/authorization/photo/{filename}"
        if not os.path.exists(path):
            raise Http404
        student = StudentRegistration.objects.get(photo_authorization__endswith=filename)
        user = request.user
        if not user.registration.is_admin and user.pk != student.user.pk:
            raise PermissionDenied
        mime = Magic(mime=True)
        mime_type = mime.from_file(path)
        ext = mime_type.split("/")[1].replace("jpeg", "jpg")
        true_file_name = _("Photo authorization of {student}.{ext}").format(student=str(student), ext=ext)
        return FileResponse(open(path, "rb"), content_type=mime_type, filename=true_file_name)


class UserImpersonateView(LoginRequiredMixin, RedirectView):
    def dispatch(self, request, *args, **kwargs):
        """
        An administrator can log in through this page as someone else, and act as this other person.
        """
        if self.request.user.registration.is_admin:
            if not User.objects.filter(pk=kwargs["pk"]).exists():
                raise Http404
            session = request.session
            session["admin"] = request.user.pk
            session["_fake_user_id"] = kwargs["pk"]
            return redirect(request.path)
        return super().dispatch(request, *args, **kwargs)

    def get_redirect_url(self, *args, **kwargs):
        return reverse_lazy("registration:user_detail", args=(kwargs["pk"],))


class ResetAdminView(LoginRequiredMixin, View):
    """
    Return to admin view, clear the session field that let an administrator to log in as someone else.
    """

    def dispatch(self, request, *args, **kwargs):
        user = request.user
        if not user.is_authenticated:
            return self.handle_no_permission()
        if "_fake_user_id" in request.session:
            del request.session["_fake_user_id"]
        return redirect(request.GET.get("path", "/"))