from django.contrib.auth.tokens import default_token_generator
from django.core.mail import send_mail
from django.shortcuts import render, redirect
from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode
from django.utils.encoding import force_bytes, force_text
from django.conf import settings
from django import forms
from django.utils.translation import gettext_lazy as _
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from django.contrib.auth.models import User
from django.contrib.auth.hashers import check_password

from urllib.parse import urlencode, urlunparse, urlparse

from judge.models import Profile
from judge.utils.email_render import render_email_message


class EmailChangeForm(forms.Form):
    new_email = forms.EmailField(label=_("New Email"))
    password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop("user", None)
        super().__init__(*args, **kwargs)

    def clean_new_email(self):
        new_email = self.cleaned_data.get("new_email")
        if User.objects.filter(email=new_email).exists():
            raise forms.ValidationError(_("An account with this email already exists."))
        return new_email

    def clean_password(self):
        password = self.cleaned_data.get("password")
        if not self.user.check_password(password):
            raise forms.ValidationError("Invalid password")
        return password


@login_required
def email_change_view(request):
    form = EmailChangeForm(request.POST or None, user=request.user)

    if request.method == "POST" and form.is_valid():
        new_email = request.POST.get("new_email")
        user = request.user
        profile = request.profile

        # Generate a token for email verification
        token = default_token_generator.make_token(user)
        uid = urlsafe_base64_encode(force_bytes(user.pk))

        # Send the email to the user
        subject = settings.SITE_NAME + " - " + _("Email Change Request")
        email_contexts = {
            "message": _(
                "We have received a request to change your email to this email. Click the button below to change your email:"
            ),
            "title": _("Email Change"),
            "button_text": _("Change Email"),
            "url_path": reverse(
                "email_change_verify", kwargs={"uidb64": uid, "token": token}
            ),
        }
        message = render_email_message(request, email_contexts)
        send_mail(
            subject,
            message,
            settings.EMAIL_HOST_USER,
            [new_email],
            html_message=message,
        )
        profile.email_change_pending = new_email
        profile.save()
        return redirect("email_change_pending")

    return render(
        request,
        "email_change/email_change.html",
        {
            "form": form,
            "title": _("Change email"),
        },
    )


def verify_email_view(request, uidb64, token):
    try:
        uid = force_text(urlsafe_base64_decode(uidb64))
        user = User.objects.get(pk=uid)
    except (TypeError, ValueError, OverflowError, User.DoesNotExist):
        user = None
    if user is not None and default_token_generator.check_token(user, token):
        profile = Profile.objects.get(user=user)
        new_email = profile.email_change_pending
        if new_email and not User.objects.filter(email=new_email).exists():
            user.email = new_email
            profile.email_change_pending = None
            user.save()
            profile.save()

            return render(
                request,
                "email_change/email_change_success.html",
                {"title": _("Success"), "user": user},
            )

    return render(
        request, "email_change/email_change_failure.html", {"title": _("Invalid")}
    )


def email_change_pending_view(request):
    return render(
        request,
        "email_change/email_change_pending.html",
        {
            "title": _("Email change pending"),
        },
    )