from http import HTTPStatus

from flask import request

from app.extensions.utils.oauth_helper import OAuthGoogle, OAuthKakao, OAuthApple
from app.http.requests.v1.oauth_request import (
    GetOAuthLoginRequest,
    GetOAuthAdminLoginRequest,
)
from app.http.responses import failure_response
from app.http.responses.presenters.v1.oauth_presenter import OAuthPresenter
from app.http.view import api
from core.domains.oauth.enum.oauth_enum import ProviderEnum
from core.domains.oauth.use_case.v1.oauth_use_case import (
    CreateGuestJWTUseCase,
    CreateAdminJWTUseCase,
)
from core.exceptions import (
    NotFoundException,
    InvalidRequestException,
    TokenValidationErrorException,
)
from core.use_case_output import UseCaseFailureOutput, FailureType


@api.route("/v1/oauth/google", methods=["GET"])
def login_google_view():
    """
        header : Bearer token from Google
        return : Groot Guest access_token
    """
    try:
        oauth = OAuthGoogle(request.headers.get("Authorization"))
        uuid_v4 = request.args.get("uuid")
    except NotFoundException as e:
        return failure_response(
            UseCaseFailureOutput(
                type=f"{e.type_.get('msg')}",
                message=FailureType.NOT_FOUND_ERROR,
                code=HTTPStatus.NOT_FOUND,
            )
        )
    except InvalidRequestException as e:
        return failure_response(
            UseCaseFailureOutput(
                type=f"{e.message}",
                message=FailureType.INVALID_REQUEST_ERROR,
                code=HTTPStatus.BAD_REQUEST,
            )
        )

    validation_result = oauth.request_validation()
    provider_id: str = validation_result.json().get("sub")

    try:
        validation_result.raise_for_status()
        dto = GetOAuthLoginRequest(
            provider_id=str(provider_id),
            provider=str(ProviderEnum.GOOGLE.value),
            uuid=uuid_v4,
        ).validate_request_and_make_dto()
    except InvalidRequestException as e:
        return failure_response(
            UseCaseFailureOutput(
                type=f"{e.message}",
                message=FailureType.INVALID_REQUEST_ERROR,
                code=HTTPStatus.BAD_REQUEST,
            )
        )
    except Exception:
        return failure_response(
            UseCaseFailureOutput(
                type=f"Failed get user info from Google, error: {validation_result.json().get('error_description')}",
                message=FailureType.UNAUTHORIZED_ERROR,
                code=HTTPStatus.UNAUTHORIZED,
            )
        )

    return OAuthPresenter().transform(CreateGuestJWTUseCase().execute(dto=dto))


@api.route("/v1/oauth/kakao", methods=["GET"])
def login_kakao_view():
    """
        header : Bearer token from Kakao
        return : Groot Guest access_token
    """
    try:
        oauth = OAuthKakao(request.headers.get("Authorization"))
        uuid_v4 = request.args.get("uuid")
    except NotFoundException as e:
        return failure_response(
            UseCaseFailureOutput(
                type=f"{e.type_.get('msg')}",
                message=FailureType.NOT_FOUND_ERROR,
                code=HTTPStatus.NOT_FOUND,
            )
        )
    except InvalidRequestException as e:
        return failure_response(
            UseCaseFailureOutput(
                type=f"{e.message}",
                message=FailureType.INVALID_REQUEST_ERROR,
                code=HTTPStatus.BAD_REQUEST,
            )
        )

    validation_result = oauth.request_validation()
    provider_id: str = validation_result.json().get("id")

    try:
        validation_result.raise_for_status()
        dto = GetOAuthLoginRequest(
            provider_id=str(provider_id),
            provider=str(ProviderEnum.KAKAO.value),
            uuid=uuid_v4,
        ).validate_request_and_make_dto()
    except InvalidRequestException as e:
        return failure_response(
            UseCaseFailureOutput(
                type=f"{e.message}",
                message=FailureType.INVALID_REQUEST_ERROR,
                code=HTTPStatus.BAD_REQUEST,
            )
        )
    except Exception:
        return failure_response(
            UseCaseFailureOutput(
                type=f"Failed get user info from Kakao, error: {validation_result.json().get('msg')}",
                message=FailureType.UNAUTHORIZED_ERROR,
                code=HTTPStatus.UNAUTHORIZED,
            )
        )

    return OAuthPresenter().transform(CreateGuestJWTUseCase().execute(dto=dto))


@api.route("/v1/oauth/apple", methods=["GET"])
def login_apple_view():
    """
        header : Bearer token from Apple
        return : Groot Guest access_token
    """
    try:
        oauth = OAuthApple(request.headers.get("Authorization"))
        uuid_v4 = request.args.get("uuid")
        validation_result = oauth.request_validation()
        provider_id: str = validation_result
        dto = GetOAuthLoginRequest(
            provider_id=str(provider_id),
            provider=str(ProviderEnum.APPLE.value),
            uuid=uuid_v4,
        ).validate_request_and_make_dto()
    except NotFoundException as e:
        return failure_response(
            UseCaseFailureOutput(
                type=f"{e.type_.get('msg')}",
                message=FailureType.NOT_FOUND_ERROR,
                code=HTTPStatus.NOT_FOUND,
            )
        )
    except InvalidRequestException as e:
        return failure_response(
            UseCaseFailureOutput(
                type=f"{e.message}",
                message=FailureType.INVALID_REQUEST_ERROR,
                code=HTTPStatus.BAD_REQUEST,
            )
        )
    except TokenValidationErrorException as e:
        return failure_response(
            UseCaseFailureOutput(
                type=f"{e.type_.get('msg')}",
                message=FailureType.UNAUTHORIZED_ERROR,
                code=HTTPStatus.UNAUTHORIZED,
            )
        )

    return OAuthPresenter().transform(CreateGuestJWTUseCase().execute(dto=dto))


@api.route("/v1/oauth/admin", methods=["GET"])
def login_admin_view():
    """
        header : Bearer token from Google, Kakao
        return : Groot Guest access_token
    """
    try:
        provider = request.args.get("provider")  # google, kakao

        if provider == "google":
            oauth = OAuthGoogle(request.headers.get("Authorization"))
        else:  # kakao
            oauth = OAuthKakao(request.headers.get("Authorization"))

    except NotFoundException as e:
        return failure_response(
            UseCaseFailureOutput(
                type=f"{e.type_.get('msg')}",
                message=FailureType.NOT_FOUND_ERROR,
                code=HTTPStatus.NOT_FOUND,
            )
        )
    except InvalidRequestException as e:
        return failure_response(
            UseCaseFailureOutput(
                type=f"{e.message}",
                message=FailureType.INVALID_REQUEST_ERROR,
                code=HTTPStatus.BAD_REQUEST,
            )
        )

    validation_result = oauth.request_validation()
    if provider == "google":
        provider_id: str = validation_result.json().get("sub")
        provider = str(ProviderEnum.GOOGLE.value)
    else:  # kakao
        provider_id: str = validation_result.json().get("id")
        provider = (str(ProviderEnum.KAKAO.value),)

    try:
        validation_result.raise_for_status()
        dto = GetOAuthAdminLoginRequest(
            provider_id=str(provider_id), provider=provider
        ).validate_request_and_make_dto()
    except InvalidRequestException as e:
        return failure_response(
            UseCaseFailureOutput(
                type=f"{e.message}",
                message=FailureType.INVALID_REQUEST_ERROR,
                code=HTTPStatus.BAD_REQUEST,
            )
        )
    except Exception:
        return failure_response(
            UseCaseFailureOutput(
                type=f"Failed get admin user info from social login, error: {validation_result.json().get('error_description')}",
                message=FailureType.UNAUTHORIZED_ERROR,
                code=HTTPStatus.UNAUTHORIZED,
            )
        )

    return OAuthPresenter().transform(CreateAdminJWTUseCase().execute(dto=dto))
