"""
카카오 OAuth API 클라이언트

카카오 로그인 인증 흐름을 처리하는 클라이언트입니다.
"""

import aiohttp

from config import kakao_settings

from app.user.exceptions import KakaoAPIError, KakaoAuthFailedError
from app.user.schemas.user_schema import KakaoTokenResponse, KakaoUserInfo


class KakaoOAuthClient:
    """
    카카오 OAuth API 클라이언트

    카카오 로그인 인증 흐름:
    1. get_authorization_url()로 카카오 로그인 페이지 URL 획득
    2. 사용자가 카카오에서 로그인 후 인가 코드(code) 발급
    3. get_access_token()으로 인가 코드를 액세스 토큰으로 교환
    4. get_user_info()로 사용자 정보 조회
    """

    AUTH_URL = "https://kauth.kakao.com/oauth/authorize"
    TOKEN_URL = "https://kauth.kakao.com/oauth/token"
    USER_INFO_URL = "https://kapi.kakao.com/v2/user/me"

    def __init__(self) -> None:
        self.client_id = kakao_settings.KAKAO_CLIENT_ID
        self.client_secret = kakao_settings.KAKAO_CLIENT_SECRET
        self.redirect_uri = kakao_settings.KAKAO_REDIRECT_URI

    def get_authorization_url(self) -> str:
        """
        카카오 로그인 페이지 URL 반환

        Returns:
            카카오 OAuth 인증 페이지 URL
        """
        return (
            f"{self.AUTH_URL}"
            f"?client_id={self.client_id}"
            f"&redirect_uri={self.redirect_uri}"
            f"&response_type=code"
        )

    async def get_access_token(self, code: str) -> KakaoTokenResponse:
        """
        인가 코드로 액세스 토큰 획득

        Args:
            code: 카카오 로그인 후 발급받은 인가 코드

        Returns:
            KakaoTokenResponse: 카카오 토큰 정보

        Raises:
            KakaoAuthFailedError: 토큰 발급 실패 시
            KakaoAPIError: API 호출 오류 시
        """
        try:
            async with aiohttp.ClientSession() as session:
                data = {
                    "grant_type": "authorization_code",
                    "client_id": self.client_id,
                    "redirect_uri": self.redirect_uri,
                    "code": code,
                }

                if self.client_secret:
                    data["client_secret"] = self.client_secret

                async with session.post(self.TOKEN_URL, data=data) as response:
                    result = await response.json()

                    if "error" in result:
                        error_desc = result.get(
                            "error_description", result.get("error", "알 수 없는 오류")
                        )
                        raise KakaoAuthFailedError(f"카카오 토큰 발급 실패: {error_desc}")

                    return KakaoTokenResponse(**result)

        except KakaoAuthFailedError:
            raise
        except Exception as e:
            raise KakaoAPIError(f"카카오 API 호출 중 오류 발생: {str(e)}")

    async def get_user_info(self, access_token: str) -> KakaoUserInfo:
        """
        액세스 토큰으로 사용자 정보 조회

        Args:
            access_token: 카카오 액세스 토큰

        Returns:
            KakaoUserInfo: 카카오 사용자 정보

        Raises:
            KakaoAuthFailedError: 사용자 정보 조회 실패 시
            KakaoAPIError: API 호출 오류 시
        """
        try:
            async with aiohttp.ClientSession() as session:
                headers = {"Authorization": f"Bearer {access_token}"}

                async with session.get(self.USER_INFO_URL, headers=headers) as response:
                    result = await response.json()

                    if "id" not in result:
                        raise KakaoAuthFailedError("카카오 사용자 정보를 가져올 수 없습니다.")

                    return KakaoUserInfo(**result)

        except KakaoAuthFailedError:
            raise
        except Exception as e:
            raise KakaoAPIError(f"카카오 API 호출 중 오류 발생: {str(e)}")


kakao_client = KakaoOAuthClient()
