import logging
import typing
from urllib.parse import urljoin

import requests
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponse
from rest_framework import status
from social_core.backends.base import BaseAuth

from apps.google.utils import user_granted_all_required_scopes
from apps.social_auth.exceptions import GOOGLE_AUTH_MISSING_GRANTED_SCOPE_ERROR
from apps.social_auth.types import GoogleOauth2Response
from apps.user_management.models import Organization, User

logger = logging.getLogger(__name__)


def connect_user_to_google(
    strategy,
    response: GoogleOauth2Response,
    user: User,
    organization: Organization,
    *args,
    **kwargs,
):
    granted_scopes = response.get("scope", "")

    if not user_granted_all_required_scopes(granted_scopes):
        logger.warning(
            f"User {user.pk} did not grant all required scopes, redirecting w/ error message "
            f"granted_scopes={granted_scopes}"
        )

        base_url_to_redirect = urljoin(organization.grafana_url, "/a/grafana-oncall-app/users/me")
        strategy.session[
            REDIRECT_FIELD_NAME
        ] = f"{base_url_to_redirect}?google_error={GOOGLE_AUTH_MISSING_GRANTED_SCOPE_ERROR}"

        return HttpResponse(status=status.HTTP_400_BAD_REQUEST)

    # at this point everything is correct and we can persist the Google OAuth2 token + generate any other relevant
    # config for the user
    #
    # be sure to clear any pre-existing sessions, in case the user previously enecountered errors we want
    # to be sure to clear these so they do not see them again
    strategy.session.flush()

    user.save_google_oauth2_settings(response)


def disconnect_user_google_oauth2_settings(backend: typing.Type[BaseAuth], user: User, *args, **kwargs):
    """
    Don't use `google_oauth2_user.access_token` when revoking token, use `refresh_token` instead. If we use
    the access token, we may get an HTTP 400 from Google because the token may be invalid or revoked.

    https://stackoverflow.com/a/18578660/3902555
    """
    user_pk = user.pk
    google_oauth2_user = user.google_oauth2_user

    logger.info(f"Disconnecting user {user_pk} from Google OAuth2")

    try:
        backend.revoke_token(google_oauth2_user.refresh_token, google_oauth2_user.google_user_id)
    except requests.exceptions.HTTPError as e:
        response = e.response

        logger.error(f"There was an HTTP error when trying to revoke Google OAuth2 token for user={user_pk}")

        if response.status_code == 400:
            error_details = response.json()
            error_code = error_details["error"]
            error_description = error_details["error_description"]

            logger.error(
                f"There was an HTTP 400 error when trying to revoke Google OAuth2 token for user={user_pk} "
                f"error_code={error_code} error_description={error_description}"
            )

            error_codes_to_ignore = ["invalid_token"]

            if error_code not in error_codes_to_ignore:
                raise e
            else:
                logger.info(f"Google OAuth2 token for user {user_pk} is already invalid or revoked, ignoring error")

    user.reset_google_oauth2_settings()

    logger.info(f"Successfully disconnected user {user.pk} from Google OAuth2")
