import secrets
import urllib.parse
import aiohttp


# Google OIDC endpoints
GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth"
GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token"
GOOGLE_USERINFO_URL = "https://openidconnect.googleapis.com/v1/userinfo"


def generate_auth_url(client_id, callback_url, state=None):
    state = secrets.token_urlsafe(32) if state is None else state
    params = {
        "client_id": client_id,
        "redirect_uri": callback_url,
        "response_type": "code",
        "scope": "openid email profile",
        "state": state,
        "access_type": "offline",
        "prompt": "select_account",
    }
    auth_url = f"{GOOGLE_AUTH_URL}?{urllib.parse.urlencode(params)}"
    return auth_url, state


async def exchange_code_for_tokens(client_id, client_secret, code, callback_url, state, stored_state):

    if not state or not stored_state or not secrets.compare_digest(state, stored_state):
        raise ValueError("Invalid state parameter. Potential CSRF attack.")
    
    if not client_id or not client_secret:
        raise ValueError("Client ID and Client Secret must be provided.")
    
    if not code:
        raise ValueError("Authorization code must be provided.")

    # Exchange authorization code for tokens
    async with aiohttp.ClientSession() as session:
        async with session.post(GOOGLE_TOKEN_URL, data={
            "code": code,
            "client_id": client_id,
            "client_secret": client_secret,
            "redirect_uri": callback_url,
            "grant_type": "authorization_code",
        }, timeout=10) as token_response:
            if token_response.status != 200:
                text = await token_response.text()
                raise ValueError(f"Failed to exchange code for tokens: {text}")
            token_data = await token_response.json()

    return token_data


async def get_user_info(token_data):
    async with aiohttp.ClientSession() as session:
        async with session.get(GOOGLE_USERINFO_URL, headers={
            "Authorization": f"Bearer {token_data.get('access_token')}"
        }, timeout=10) as userinfo_response:
            if userinfo_response.status != 200:
                text = await userinfo_response.text()
                raise ValueError(f"Failed to get user info: {text}")
            user_info = await userinfo_response.json()

    return user_info