"""User management routes""" import logging from fastapi import APIRouter, HTTPException, Depends from db import get_database from models import UserCreate, UserUpdate from datetime import datetime from auth import get_current_user, verify_user_access log = logging.getLogger(__name__) router = APIRouter() @router.post("/register", response_model=dict) async def register_user(user_data: UserCreate, token: dict = Depends(get_current_user)): """ Register or get user (idempotent). Uses upsert pattern to ensure one user per email. If user already exists, returns existing user. Called after Firebase Google Auth on frontend. """ if user_data.email != token.get("email"): raise HTTPException(status_code=403, detail="Access denied") db = get_database() try: result = db.users.update_one( {"email": user_data.email}, { "$setOnInsert": { "email": user_data.email, "displayName": user_data.displayName or user_data.email.split("@")[0], "photoURL": user_data.photoURL, "theme": "light", "createdAt": datetime.utcnow() }, "$set": { "updatedAt": datetime.utcnow() } }, upsert=True ) user = db.users.find_one({"email": user_data.email}) if not user: raise HTTPException(status_code=500, detail="Failed to retrieve user after upsert") return { "id": str(user["_id"]), "email": user["email"], "displayName": user["displayName"], "photoURL": user.get("photoURL"), "theme": user.get("theme", "light"), "backgroundImage": user.get("backgroundImage"), "backgroundImages": user.get("backgroundImages", []), "reminder": user.get("reminder"), "createdAt": user["createdAt"].isoformat(), "updatedAt": user["updatedAt"].isoformat(), "message": "User registered successfully" if result.upserted_id else "User already exists" } except HTTPException: raise except Exception as e: log.exception("Registration failed") raise HTTPException(status_code=500, detail="Internal server error") @router.get("/by-email/{email}", response_model=dict) async def get_user_by_email(email: str, token: dict = Depends(get_current_user)): """Get user profile by email (called after Firebase Auth).""" if email != token.get("email"): raise HTTPException(status_code=403, detail="Access denied") db = get_database() try: user = db.users.find_one({"email": email}) if not user: raise HTTPException(status_code=404, detail="User not found") return { "id": str(user["_id"]), "email": user["email"], "displayName": user.get("displayName"), "photoURL": user.get("photoURL"), "theme": user.get("theme", "light"), "backgroundImage": user.get("backgroundImage"), "backgroundImages": user.get("backgroundImages", []), "reminder": user.get("reminder"), "tutorial": user.get("tutorial"), "createdAt": user["createdAt"].isoformat(), "updatedAt": user["updatedAt"].isoformat() } except HTTPException: raise except Exception: log.exception("Failed to fetch user by email") raise HTTPException(status_code=500, detail="Internal server error") @router.get("/{user_id}", response_model=dict) async def get_user_by_id(user_id: str, token: dict = Depends(get_current_user)): """Get user profile by ID.""" db = get_database() try: user = verify_user_access(user_id, db, token) return { "id": str(user["_id"]), "email": user["email"], "displayName": user.get("displayName"), "photoURL": user.get("photoURL"), "theme": user.get("theme", "light"), "backgroundImage": user.get("backgroundImage"), "backgroundImages": user.get("backgroundImages", []), "createdAt": user["createdAt"].isoformat(), "updatedAt": user["updatedAt"].isoformat() } except HTTPException: raise except Exception: log.exception("Failed to fetch user by ID") raise HTTPException(status_code=500, detail="Internal server error") @router.put("/{user_id}", response_model=dict) async def update_user(user_id: str, user_data: UserUpdate, token: dict = Depends(get_current_user)): """Update user profile.""" db = get_database() try: user = verify_user_access(user_id, db, token) user_oid = user["_id"] update_data = user_data.model_dump(exclude_unset=True) update_data["updatedAt"] = datetime.utcnow() db.users.update_one({"_id": user_oid}, {"$set": update_data}) updated = db.users.find_one({"_id": user_oid}) return { "id": str(updated["_id"]), "email": updated["email"], "displayName": updated.get("displayName"), "photoURL": updated.get("photoURL"), "theme": updated.get("theme", "light"), "backgroundImage": updated.get("backgroundImage"), "backgroundImages": updated.get("backgroundImages", []), "tutorial": updated.get("tutorial"), "createdAt": updated["createdAt"].isoformat(), "updatedAt": updated["updatedAt"].isoformat(), "message": "User updated successfully" } except HTTPException: raise except Exception: log.exception("User update failed") raise HTTPException(status_code=500, detail="Internal server error") @router.delete("/{user_id}") async def delete_user(user_id: str, token: dict = Depends(get_current_user)): """Delete user account and all associated data.""" db = get_database() try: user = verify_user_access(user_id, db, token) user_oid = user["_id"] user_result = db.users.delete_one({"_id": user_oid}) entry_result = db.entries.delete_many({"userId": user_oid}) return { "message": "User deleted successfully", "user_deleted": user_result.deleted_count, "entries_deleted": entry_result.deleted_count } except HTTPException: raise except Exception: log.exception("User deletion failed") raise HTTPException(status_code=500, detail="Internal server error")