"""Google Calendar integration endpoints.""" from typing import Annotated from fastapi import APIRouter, Depends, HTTPException, Query, status from google_auth_oauthlib.flow import Flow from sqlalchemy.orm import Session from app.core.config import settings from app.core.deps import get_current_user, get_db from app.models.google_calendar_token import GoogleCalendarToken from app.models.user import User router = APIRouter() @router.get("/integrations/google/connect") def connect_google( current_user: Annotated[User, Depends(get_current_user)], ) -> dict[str, str]: """ Start Google OAuth flow. Returns authorization URL that user should visit to grant access. """ if not settings.google_client_id or not settings.google_client_secret: raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Google Calendar integration not configured", ) try: flow = Flow.from_client_config( { "web": { "client_id": settings.google_client_id, "client_secret": settings.google_client_secret, "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "redirect_uris": [settings.google_redirect_uri], } }, scopes=[ "https://www.googleapis.com/auth/calendar", "https://www.googleapis.com/auth/calendar.events", ], redirect_uri=settings.google_redirect_uri, ) authorization_url, state = flow.authorization_url( access_type="offline", include_granted_scopes="true", prompt="consent" ) # Note: In production, store state in session/cache and validate it in callback return {"authorization_url": authorization_url, "state": state} except Exception as e: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to start OAuth flow: {str(e)}", ) @router.get("/integrations/google/callback") def google_callback( code: Annotated[str, Query()], state: Annotated[str, Query()], db: Annotated[Session, Depends(get_db)], current_user: Annotated[User, Depends(get_current_user)], ) -> dict[str, str]: """ Handle Google OAuth callback. Exchange authorization code for tokens and store them. """ if not settings.google_client_id or not settings.google_client_secret: raise HTTPException( status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Google Calendar integration not configured", ) try: flow = Flow.from_client_config( { "web": { "client_id": settings.google_client_id, "client_secret": settings.google_client_secret, "auth_uri": "https://accounts.google.com/o/oauth2/auth", "token_uri": "https://oauth2.googleapis.com/token", "redirect_uris": [settings.google_redirect_uri], } }, scopes=[ "https://www.googleapis.com/auth/calendar", "https://www.googleapis.com/auth/calendar.events", ], redirect_uri=settings.google_redirect_uri, state=state, ) # Exchange code for tokens flow.fetch_token(code=code) credentials = flow.credentials # Store tokens token_record = ( db.query(GoogleCalendarToken) .filter(GoogleCalendarToken.user_id == current_user.id) .first() ) if token_record: token_record.access_token = credentials.token # type: ignore[assignment] token_record.refresh_token = credentials.refresh_token # type: ignore[assignment] token_record.token_expiry = credentials.expiry # type: ignore[assignment] else: token_record = GoogleCalendarToken( user_id=current_user.id, # type: ignore[arg-type] access_token=credentials.token, refresh_token=credentials.refresh_token, token_expiry=credentials.expiry, ) db.add(token_record) db.commit() return {"message": "Google Calendar connected successfully"} except Exception as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=f"OAuth failed: {str(e)}", ) @router.delete("/integrations/google/disconnect") def disconnect_google( db: Annotated[Session, Depends(get_db)], current_user: Annotated[User, Depends(get_current_user)], ) -> dict[str, str]: """ Disconnect Google Calendar. Removes stored tokens for the current user. """ token_record = ( db.query(GoogleCalendarToken) .filter(GoogleCalendarToken.user_id == current_user.id) .first() ) if token_record: db.delete(token_record) db.commit() return {"message": "Google Calendar disconnected"} @router.get("/integrations/google/status") def google_status( db: Annotated[Session, Depends(get_db)], current_user: Annotated[User, Depends(get_current_user)], ) -> dict[str, bool | str | None]: """ Check Google Calendar connection status. Returns whether user has connected their Google Calendar account. """ token_record = ( db.query(GoogleCalendarToken) .filter(GoogleCalendarToken.user_id == current_user.id) .first() ) return { "connected": token_record is not None, "expires_at": token_record.token_expiry.isoformat() if token_record and token_record.token_expiry else None, }