"""Admin users endpoints.""" from typing import Annotated, Any from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlmodel.ext.asyncio.session import AsyncSession from app.core.database import get_db from app.core.dependencies import get_admin_user from app.models.plan import Plan from app.models.user import User from app.repositories.plan import PlanRepository from app.repositories.user import SortOrder, UserRepository, UserSortField, UserStatus from app.schemas.auth import UserResponse from app.schemas.user import UserUpdate router = APIRouter( prefix="/users", tags=["admin-users"], dependencies=[Depends(get_admin_user)], ) def _user_to_response(user: User) -> UserResponse: """Convert User model to UserResponse.""" return UserResponse( id=user.id, email=user.email, name=user.name, picture=user.picture, role=user.role, credits=user.credits, is_active=user.is_active, plan={ "id": user.plan.id, "name": user.plan.name, "max_credits": user.plan.max_credits, "features": [], # Add features if needed } if user.plan else {}, created_at=user.created_at, updated_at=user.updated_at, ) @router.get("/") async def list_users( # noqa: PLR0913 session: Annotated[AsyncSession, Depends(get_db)], page: Annotated[int, Query(description="Page number", ge=1)] = 1, limit: Annotated[int, Query(description="Items per page", ge=1, le=100)] = 50, search: Annotated[str | None, Query(description="Search in name or email")] = None, sort_by: Annotated[ UserSortField, Query(description="Sort by field"), ] = UserSortField.NAME, sort_order: Annotated[SortOrder, Query(description="Sort order")] = SortOrder.ASC, status_filter: Annotated[ UserStatus, Query(description="Filter by status"), ] = UserStatus.ALL, ) -> dict[str, Any]: """Get all users with pagination, search, and filters (admin only).""" user_repo = UserRepository(session) users, total_count = await user_repo.get_all_with_plan_paginated( page=page, limit=limit, search=search, sort_by=sort_by, sort_order=sort_order, status_filter=status_filter, ) total_pages = (total_count + limit - 1) // limit # Ceiling division return { "users": [_user_to_response(user) for user in users], "total": total_count, "page": page, "limit": limit, "total_pages": total_pages, } @router.get("/{user_id}") async def get_user( user_id: int, session: Annotated[AsyncSession, Depends(get_db)], ) -> UserResponse: """Get a specific user by ID (admin only).""" user_repo = UserRepository(session) user = await user_repo.get_by_id_with_plan(user_id) if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found", ) return _user_to_response(user) @router.patch("/{user_id}") async def update_user( user_id: int, user_update: UserUpdate, session: Annotated[AsyncSession, Depends(get_db)], ) -> UserResponse: """Update a user (admin only).""" user_repo = UserRepository(session) user = await user_repo.get_by_id_with_plan(user_id) if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found", ) update_data = user_update.model_dump(exclude_unset=True) # If plan_id is being updated, validate it exists if "plan_id" in update_data: plan_repo = PlanRepository(session) plan = await plan_repo.get_by_id(update_data["plan_id"]) if not plan: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="Plan not found", ) updated_user = await user_repo.update(user, update_data) # Need to refresh the plan relationship after update await session.refresh(updated_user, ["plan"]) return _user_to_response(updated_user) @router.post("/{user_id}/disable") async def disable_user( user_id: int, session: Annotated[AsyncSession, Depends(get_db)], ) -> dict[str, str]: """Disable a user (admin only).""" user_repo = UserRepository(session) user = await user_repo.get_by_id_with_plan(user_id) if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found", ) await user_repo.update(user, {"is_active": False}) return {"message": "User disabled successfully"} @router.post("/{user_id}/enable") async def enable_user( user_id: int, session: Annotated[AsyncSession, Depends(get_db)], ) -> dict[str, str]: """Enable a user (admin only).""" user_repo = UserRepository(session) user = await user_repo.get_by_id_with_plan(user_id) if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found", ) await user_repo.update(user, {"is_active": True}) return {"message": "User enabled successfully"} @router.get("/plans/list") async def list_plans( session: Annotated[AsyncSession, Depends(get_db)], ) -> list[Plan]: """Get all plans for user editing (admin only).""" plan_repo = PlanRepository(session) return await plan_repo.get_all()