from functools import wraps
from logging import getLogger

from django.contrib.auth import get_user_model, login
from django.http import Http404
from django.shortcuts import redirect, render
from django.urls import reverse
from django.views.decorators.cache import never_cache

from ..core.exceptions import Banned
from ..users.bans import get_user_ban
from ..users.decorators import deny_banned_ips
from ..users.registration import send_welcome_email
from .client import (
    create_login_url,
    get_access_token,
    get_code_grant,
    get_user_data,
)
from .exceptions import (
    OAuth2Error,
    OAuth2UserAccountDeactivatedError,
    OAuth2UserDataValidationError,
)
from .user import get_user_from_data

logger = getLogger("misago.oauth2")

User = get_user_model()


def oauth2_view(f):
    f = deny_banned_ips(f)

    @wraps(f)
    @never_cache
    def wrapped_oauth2_view(request):
        if not request.settings.enable_oauth2_client:
            raise Http404()

        return f(request)

    return wrapped_oauth2_view


@oauth2_view
def oauth2_login(request):
    redirect_to = create_login_url(request)
    return redirect(redirect_to)


@oauth2_view
def oauth2_complete(request):
    try:
        code_grant = get_code_grant(request)
        token = get_access_token(request, code_grant)
        user_data, raw_data = get_user_data(request, token)
        user, created = get_user_from_data(request, user_data, raw_data)

        if not user.is_active:
            raise OAuth2UserAccountDeactivatedError()

        if not user.is_staff:
            if user_ban := get_user_ban(user, request.cache_versions):
                raise Banned(user_ban)
    except OAuth2UserDataValidationError as error:
        logger.exception(
            "OAuth2 Profile Error",
            extra={
                f"error[{error_index}]": str(error_msg)
                for error_index, error_msg in enumerate(error.error_list)
            },
        )
        return render(
            request,
            "misago/errorpages/oauth2_profile.html",
            {
                "error": error,
                "error_list": error.error_list,
            },
            status=400,
        )
    except OAuth2Error as error:
        logger.exception("OAuth2 Error")
        return render(
            request,
            "misago/errorpages/oauth2.html",
            {"error": error},
            status=400,
        )

    if created and request.settings.oauth2_send_welcome_email:
        send_welcome_email(request, user)

    if not user.requires_activation:
        login(request, user)

    return redirect(reverse("misago:index"))
