from __future__ import annotations

from typing import Any

from django.core.exceptions import PermissionDenied
from django.http import HttpRequest, HttpResponse, HttpResponseBase
from django.urls import reverse
from django.utils.decorators import method_decorator
from django.utils.http import urlencode
from django.views.generic.edit import FormView

from allauth.account.internal.decorators import login_not_required
from allauth.socialaccount.adapter import get_adapter
from allauth.socialaccount.helpers import (
    complete_social_login,
    render_authentication_error,
)
from allauth.socialaccount.models import SocialLogin
from allauth.socialaccount.providers.base.constants import AuthError
from allauth.socialaccount.providers.base.views import BaseLoginView
from allauth.socialaccount.providers.dummy.forms import AuthenticateForm
from allauth.socialaccount.providers.dummy.provider import DummyProvider


@method_decorator(login_not_required, name="dispatch")
class LoginView(BaseLoginView):
    provider_id = DummyProvider.id


login = LoginView.as_view()


class AuthenticateView(FormView):
    form_class = AuthenticateForm
    template_name = "dummy/authenticate_form.html"

    @method_decorator(login_not_required)
    def dispatch(
        self, request: HttpRequest, *args: Any, **kwargs: Any
    ) -> HttpResponseBase:
        state_id = request.GET.get("state")
        if not state_id:
            raise PermissionDenied()
        self.state_id = state_id
        self.provider = get_adapter().get_provider(self.request, DummyProvider.id)
        if request.method == "POST" and request.POST.get("action") == "cancel":
            return render_authentication_error(
                request,
                self.provider,
                error=AuthError.CANCELLED,
                extra_context={"state_id": self.state_id},
            )

        return super().dispatch(request, *args, **kwargs)

    def form_valid(self, form) -> HttpResponse:
        login = self.provider.sociallogin_from_response(self.request, form.cleaned_data)
        login.state = SocialLogin.unstash_state(self.request)
        return complete_social_login(self.request, login)

    def get_context_data(self, **kwargs) -> dict:
        ret = super().get_context_data(**kwargs)
        ret["action_url"] = (
            f"{reverse('dummy_authenticate')}?{urlencode({'state': self.state_id})}"
        )
        return ret


authenticate = AuthenticateView.as_view()
