"""
Google OAuth utilities for authentication.
Handles Google OAuth flow and token verification.
"""

import logging
import time
import requests
from typing import Optional, Dict, Any
from urllib.parse import urlencode
from google.auth.transport import requests as google_requests
from google.oauth2 import id_token
from google.auth.exceptions import GoogleAuthError

from config import settings

logger = logging.getLogger(__name__)


class GoogleOAuthError(Exception):
    """Custom exception for Google OAuth errors."""
    pass


def verify_google_token(token: str) -> Optional[Dict[str, Any]]:
    """
    Verify Google ID token and return user information.
    
    Args:
        token: Google ID token
        
    Returns:
        User information from Google or None if verification fails
    """
    try:
        # Verify the token
        idinfo = id_token.verify_oauth2_token(
            token, 
            google_requests.Request(), 
            settings.GOOGLE_CLIENT_ID
        )
        
        # Check if the token was issued for our app
        if idinfo['aud'] != settings.GOOGLE_CLIENT_ID:
            logger.warning("Token audience mismatch")
            return None
            
        # Check if the token has expired
        # exp is a Unix timestamp in seconds
        if idinfo['exp'] < time.time():
            logger.warning("Token has expired")
            return None
            
        # Return user information
        return {
            'sub': idinfo['sub'],  # Google user ID
            'email': idinfo['email'],
            'name': idinfo.get('name', ''),
            'given_name': idinfo.get('given_name', ''),
            'family_name': idinfo.get('family_name', ''),
            'picture': idinfo.get('picture', ''),
            'email_verified': idinfo.get('email_verified', False)
        }
        
    except GoogleAuthError as e:
        logger.error(f"Google token verification error: {e}")
        return None
    except Exception as e:
        logger.error(f"Unexpected error during Google token verification: {e}")
        return None


def get_google_user_info(access_token: str) -> Optional[Dict[str, Any]]:
    """
    Get user information from Google using access token.
    
    Args:
        access_token: Google access token
        
    Returns:
        User information from Google or None if request fails
    """
    try:
        headers = {'Authorization': f'Bearer {access_token}'}
        response = requests.get(
            'https://www.googleapis.com/oauth2/v2/userinfo',
            headers=headers
        )
        
        if response.status_code == 200:
            return response.json()
        else:
            logger.error(f"Failed to get Google user info: {response.status_code}")
            return None
            
    except Exception as e:
        logger.error(f"Error getting Google user info: {e}")
        return None


def create_google_oauth_url() -> str:
    """
    Create Google OAuth URL for authorization.
    
    Returns:
        Google OAuth authorization URL
    """
    base_url = "https://accounts.google.com/o/oauth2/v2/auth"
    params = {
        'client_id': settings.GOOGLE_CLIENT_ID,
        'redirect_uri': settings.GOOGLE_REDIRECT_URI,
        'response_type': 'code',
        'scope': 'openid email profile',
        'access_type': 'offline',
        'prompt': 'consent'
    }
    
    # Build query string with proper URL encoding
    query_string = urlencode(params)
    return f"{base_url}?{query_string}"


def exchange_code_for_tokens(code: str) -> Optional[Dict[str, Any]]:
    """
    Exchange authorization code for access and refresh tokens.
    
    Args:
        code: Authorization code from Google
        
    Returns:
        Token response from Google or None if exchange fails
    """
    try:
        token_url = "https://oauth2.googleapis.com/token"

        def _try_exchange(redirect_uri: str) -> Optional[Dict[str, Any]]:
            payload = {
                'client_id': settings.GOOGLE_CLIENT_ID,
                'client_secret': settings.GOOGLE_CLIENT_SECRET,
                'code': code,
                'grant_type': 'authorization_code',
                'redirect_uri': redirect_uri,
            }
            resp = requests.post(token_url, data=payload)
            if resp.status_code == 200:
                return resp.json()
            logger.warning(
                "Google code exchange failed: status=%s redirect_uri=%s body=%s",
                resp.status_code,
                redirect_uri,
                resp.text[:500],
            )
            return None

        # Try with configured redirect URI (for classic redirect flow)
        result = _try_exchange(settings.GOOGLE_REDIRECT_URI)
        if result:
            return result

        # Try with 'postmessage' redirect URI (for GIS popup code flow)
        return _try_exchange('postmessage')

    except Exception as e:
        logger.error(f"Error exchanging code for tokens: {e}")
        return None