"""
Kakao API Client for OAuth 2.0 Authentication
pharmacy-pos-qr-system용 경량 버전
"""
import os
import requests
import json
import logging
from datetime import datetime, timedelta
from typing import Dict, Any, Tuple
from urllib.parse import urlencode

logger = logging.getLogger(__name__)


class KakaoAPIClient:
    """카카오 API 클라이언트 (마일리지 적립용)"""

    def __init__(self):
        self.client_id = os.getenv('KAKAO_CLIENT_ID', '')
        self.client_secret = os.getenv('KAKAO_CLIENT_SECRET', '')
        self.redirect_uri = os.getenv(
            'KAKAO_REDIRECT_URI',
            'https://mile.0bin.in/claim/kakao/callback'
        )

        self.auth_base_url = 'https://kauth.kakao.com'
        self.api_base_url = 'https://kapi.kakao.com'

        self.session = requests.Session()
        self.session.headers.update({
            'Content-Type': 'application/x-www-form-urlencoded',
            'User-Agent': 'YouthPharmacy-Mileage/1.0'
        })

    def get_authorization_url(self, state: str = None) -> str:
        """카카오 OAuth 인증 URL 생성"""
        params = {
            'client_id': self.client_id,
            'redirect_uri': self.redirect_uri,
            'response_type': 'code',
            'scope': 'profile_nickname,profile_image,account_email,name,phone_number,birthday'
        }

        if state:
            params['state'] = state

        return f"{self.auth_base_url}/oauth/authorize?{urlencode(params)}"

    def get_access_token(self, authorization_code: str) -> Tuple[bool, Dict[str, Any]]:
        """Authorization Code로 Access Token 요청"""
        url = f"{self.auth_base_url}/oauth/token"

        data = {
            'grant_type': 'authorization_code',
            'client_id': self.client_id,
            'client_secret': self.client_secret,
            'redirect_uri': self.redirect_uri,
            'code': authorization_code
        }

        try:
            headers = {'Content-Type': 'application/x-www-form-urlencoded'}
            response = self.session.post(url, data=data, headers=headers)

            logger.info(f"카카오 토큰 응답 상태: {response.status_code}")
            response.raise_for_status()

            token_data = response.json()

            if 'expires_in' in token_data:
                expires_at = datetime.now() + timedelta(seconds=token_data['expires_in'])
                token_data['expires_at'] = expires_at.isoformat()

            return True, token_data

        except requests.exceptions.RequestException as e:
            logger.error(f"카카오 토큰 요청 실패: {e}")

            error_details = {
                'error': 'token_request_failed',
                'error_description': f'Failed to get access token: {e}'
            }
            try:
                if hasattr(e, 'response') and e.response is not None:
                    kakao_error = e.response.json()
                    logger.error(f"카카오 API 오류: {kakao_error}")
                    error_details.update(kakao_error)
            except Exception:
                pass

            return False, error_details

        except json.JSONDecodeError as e:
            logger.error(f"카카오 응답 JSON 파싱 실패: {e}")
            return False, {
                'error': 'invalid_response',
                'error_description': f'Invalid JSON response: {e}'
            }

    def refresh_access_token(self, refresh_token: str) -> Tuple[bool, Dict[str, Any]]:
        """Refresh Token으로 Access Token 갱신"""
        url = f"{self.auth_base_url}/oauth/token"

        data = {
            'grant_type': 'refresh_token',
            'client_id': self.client_id,
            'refresh_token': refresh_token,
        }
        if self.client_secret:
            data['client_secret'] = self.client_secret

        try:
            headers = {'Content-Type': 'application/x-www-form-urlencoded'}
            response = self.session.post(url, data=data, headers=headers)

            logger.info(f"카카오 토큰 갱신 응답 상태: {response.status_code}")
            response.raise_for_status()

            token_data = response.json()
            if 'expires_in' in token_data:
                expires_at = datetime.now() + timedelta(seconds=token_data['expires_in'])
                token_data['expires_at'] = expires_at.isoformat()

            return True, token_data

        except requests.exceptions.RequestException as e:
            logger.error(f"카카오 토큰 갱신 실패: {e}")
            error_details = {
                'error': 'token_refresh_failed',
                'error_description': f'Failed to refresh access token: {e}'
            }
            try:
                if hasattr(e, 'response') and e.response is not None:
                    kakao_error = e.response.json()
                    logger.error(f"카카오 API 오류: {kakao_error}")
                    error_details.update(kakao_error)
            except Exception:
                pass
            return False, error_details

    def get_user_info_with_refresh(
        self,
        access_token: str,
        refresh_token: str,
        token_expires_at: str = None
    ) -> Tuple[bool, Dict[str, Any], Dict[str, Any]]:
        """저장된 토큰으로 사용자 정보 조회 (만료 시 자동 갱신)

        Returns:
            (성공여부, 사용자정보/에러, 갱신된 토큰 데이터 또는 빈 dict)
        """
        new_token_data = {}

        # 만료 확인: 5분 이내면 미리 갱신
        if token_expires_at:
            try:
                expires = datetime.fromisoformat(token_expires_at)
                if datetime.now() >= expires - timedelta(minutes=5):
                    logger.info("Access token 만료 임박, 갱신 시도")
                    success, refreshed = self.refresh_access_token(refresh_token)
                    if success:
                        access_token = refreshed['access_token']
                        new_token_data = refreshed
                    else:
                        return False, refreshed, {}
            except (ValueError, TypeError) as e:
                logger.warning(f"token_expires_at 파싱 실패, 기존 토큰으로 시도: {e}")

        # 사용자 정보 조회
        success, user_info = self.get_user_info(access_token)

        if not success and refresh_token:
            # 실패 시 갱신 후 재시도
            logger.info("사용자 정보 조회 실패, 토큰 갱신 후 재시도")
            refresh_ok, refreshed = self.refresh_access_token(refresh_token)
            if refresh_ok:
                access_token = refreshed['access_token']
                new_token_data = refreshed
                success, user_info = self.get_user_info(access_token)

        return success, user_info, new_token_data

    def get_user_info(self, access_token: str) -> Tuple[bool, Dict[str, Any]]:
        """Access Token으로 사용자 정보 조회"""
        url = f"{self.api_base_url}/v2/user/me"

        headers = {
            'Authorization': f'Bearer {access_token}',
            'Content-Type': 'application/x-www-form-urlencoded'
        }

        try:
            response = requests.get(url, headers=headers)
            response.raise_for_status()

            user_data = response.json()
            normalized_user = self._normalize_user_data(user_data)

            return True, normalized_user

        except requests.exceptions.RequestException as e:
            logger.error(f"카카오 사용자 정보 조회 실패: {e}")
            return False, {
                'error': 'user_info_failed',
                'error_description': f'Failed to get user info: {e}'
            }

    def _normalize_user_data(self, kakao_user: Dict[str, Any]) -> Dict[str, Any]:
        """카카오 사용자 데이터를 정규화"""
        kakao_account = kakao_user.get('kakao_account', {})
        profile = kakao_account.get('profile', {})

        normalized = {
            'kakao_id': str(kakao_user.get('id')),
            'nickname': profile.get('nickname'),
            'profile_image': profile.get('profile_image_url'),
            'thumbnail_image': profile.get('thumbnail_image_url'),
            'email': kakao_account.get('email'),
            'is_email_verified': kakao_account.get('is_email_verified', False),
            'name': kakao_account.get('name'),
            'phone_number': kakao_account.get('phone_number'),
            'birthday': kakao_account.get('birthday'),        # MMDD 형식
            'birthyear': kakao_account.get('birthyear'),      # YYYY 형식
        }

        # None 값 제거
        normalized = {k: v for k, v in normalized.items() if v is not None}

        return normalized


# 싱글톤
_kakao_client = None


def get_kakao_client() -> KakaoAPIClient:
    """카카오 클라이언트 인스턴스 반환"""
    global _kakao_client
    if _kakao_client is None:
        _kakao_client = KakaoAPIClient()
    return _kakao_client
