from fastapi import APIRouter, HTTPException, Depends, Header from typing import Optional from api.models.schemas import ( UserRegistration, UserCredentials, UserProfileUpdate, PasswordChange, TokenResponse, AuthResponse, UserProfile, Conversation, ConversationDetail ) from api.services.auth_service import auth_service router = APIRouter(tags=["auth"]) # ── Dependency for protected routes ──────────────────────────────────────────── def get_current_user(authorization: Optional[str] = Header(None)): """Extract user from JWT token""" if not authorization: raise HTTPException(status_code=401, detail="Missing authorization header") try: scheme, token = authorization.split() if scheme.lower() != "bearer": raise HTTPException(status_code=401, detail="Invalid auth scheme") except ValueError: raise HTTPException(status_code=401, detail="Invalid authorization header") payload = auth_service.verify_token(token) if not payload: raise HTTPException(status_code=401, detail="Invalid or expired token") return payload # ──────────────────────────────────────────────────────────────────────────────── # PUBLIC ENDPOINTS # ──────────────────────────────────────────────────────────────────────────────── @router.post("/register", response_model=AuthResponse) async def register(user_data: UserRegistration): """Register new user""" success, message, user_id = auth_service.register_user( email=user_data.email, password=user_data.password, full_name=user_data.full_name, dob=user_data.dob, sex=user_data.sex, medical_history=user_data.medical_history, geolocation=user_data.geolocation ) if not success: raise HTTPException(status_code=400, detail=message) return AuthResponse( success=True, message=message, data={"user_id": user_id} ) @router.post("/login", response_model=TokenResponse) async def login(credentials: UserCredentials): """Login user and return JWT token""" success, message, user_data = auth_service.login_user( email=credentials.email, password=credentials.password ) if not success: raise HTTPException(status_code=401, detail=message) access_token = auth_service.create_access_token( user_id=user_data["user_id"], email=user_data["email"] ) return TokenResponse( access_token=access_token, user=UserProfile(**user_data) ) # ──────────────────────────────────────────────────────────────────────────────── # PROTECTED ENDPOINTS # ──────────────────────────────────────────────────────────────────────────────── @router.get("/profile", response_model=UserProfile) async def get_profile(current_user=Depends(get_current_user)): """Get current user profile""" user_data = auth_service.get_user_by_id(current_user["sub"]) if not user_data: raise HTTPException(status_code=404, detail="User not found") return UserProfile(**user_data) @router.put("/profile", response_model=AuthResponse) async def update_profile( profile_data: UserProfileUpdate, current_user=Depends(get_current_user) ): """Update user profile""" success, message = auth_service.update_user_profile( user_id=current_user["sub"], email=profile_data.email, full_name=profile_data.full_name, dob=profile_data.dob, sex=profile_data.sex, medical_history=profile_data.medical_history, geolocation=profile_data.geolocation ) if not success: raise HTTPException(status_code=400, detail=message) return AuthResponse(success=True, message=message) @router.post("/change-password", response_model=AuthResponse) async def change_password( pwd_change: PasswordChange, current_user=Depends(get_current_user) ): """Change user password""" success, message = auth_service.change_password( user_id=current_user["sub"], old_password=pwd_change.old_password, new_password=pwd_change.new_password ) if not success: raise HTTPException(status_code=400, detail=message) return AuthResponse(success=True, message=message) @router.delete("/account", response_model=AuthResponse) async def delete_account( password: str, current_user=Depends(get_current_user) ): """Delete user account""" success, message = auth_service.delete_user( user_id=current_user["sub"], password=password ) if not success: raise HTTPException(status_code=400, detail=message) return AuthResponse(success=True, message=message) # ──────────────────────────────────────────────────────────────────────────────── # CONVERSATION ENDPOINTS # ──────────────────────────────────────────────────────────────────────────────── @router.post("/conversations", response_model=AuthResponse) async def create_conversation( title: Optional[str] = None, current_user=Depends(get_current_user) ): """Create new conversation""" success, message, conversation_id = auth_service.create_conversation( user_id=current_user["sub"], title=title or "New Conversation" ) if not success: raise HTTPException(status_code=400, detail=message) return AuthResponse( success=True, message=message, data={"conversation_id": conversation_id} ) @router.get("/conversations", response_model=dict) async def get_conversations(current_user=Depends(get_current_user)): """Get all user conversations""" success, message, conversations = auth_service.get_conversations(current_user["sub"]) if not success: raise HTTPException(status_code=400, detail=message) return { "success": True, "message": message, "conversations": conversations } @router.delete("/conversations/{conversation_id}", response_model=AuthResponse) async def delete_conversation( conversation_id: str, current_user=Depends(get_current_user) ): """Delete conversation""" success, message = auth_service.delete_conversation( user_id=current_user["sub"], conversation_id=conversation_id ) if not success: raise HTTPException(status_code=400, detail=message) return AuthResponse(success=True, message=message) @router.get("/conversations/{conversation_id}") async def get_conversation_detail( conversation_id: str, current_user=Depends(get_current_user) ): """Get conversation detail with all messages and diagnosis""" from api.services.database_interface import conversation_db conv_data = auth_service._load_conversation(current_user["sub"], conversation_id) if not conv_data: raise HTTPException(status_code=404, detail="Conversation not found") # Load messages from JSONL messages = conversation_db.get_conversation_messages( current_user["sub"], conversation_id ) # Load diagnosis if exists diagnosis_data = conversation_db.get_diagnosis_from_conversation( current_user["sub"], conversation_id ) return { "conversation_id": conversation_id, "title": conv_data.get("title"), "created_at": conv_data.get("created_at"), "messages": messages, "diagnosis": diagnosis_data.get("diagnosis") if diagnosis_data else None, "recommendations": diagnosis_data.get("recommendations") if diagnosis_data else None }