"""Hub user management — super admin only.""" from typing import Optional from fastapi import APIRouter, Depends, HTTPException, Query from pydantic import BaseModel, EmailStr from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, func, desc from app.auth.dependencies import require_super_admin from app.auth.password import hash_password from app.database import get_db from app.models.user import HubUser, UserRole router = APIRouter(prefix="/api/users", tags=["users"]) class UserCreate(BaseModel): email: EmailStr full_name: str password: str role: UserRole = UserRole.school_admin school_id: Optional[str] = None class UserUpdate(BaseModel): full_name: Optional[str] = None is_active: Optional[bool] = None school_id: Optional[str] = None @router.get("") async def list_users( page: int = Query(1, ge=1), per_page: int = Query(25), search: Optional[str] = Query(None), _admin: HubUser = Depends(require_super_admin), db: AsyncSession = Depends(get_db), ): stmt = select(HubUser).order_by(desc(HubUser.created_at)) if search: stmt = stmt.where(HubUser.email.ilike(f"%{search}%") | HubUser.full_name.ilike(f"%{search}%")) total = (await db.execute(select(func.count()).select_from(stmt.subquery()))).scalar_one() users = (await db.execute(stmt.offset((page - 1) * per_page).limit(per_page))).scalars().all() return { "items": [{"id": u.id, "email": u.email, "full_name": u.full_name, "role": u.role.value, "school_id": u.school_id, "is_active": u.is_active, "created_at": u.created_at.isoformat()} for u in users], "total": total, } @router.post("", status_code=201) async def create_user( body: UserCreate, _admin: HubUser = Depends(require_super_admin), db: AsyncSession = Depends(get_db), ): existing = (await db.execute(select(HubUser).where(HubUser.email == body.email))).scalar_one_or_none() if existing: raise HTTPException(409, "Email already exists") if len(body.password) < 8: raise HTTPException(422, "Password must be at least 8 characters") user = HubUser(email=body.email, full_name=body.full_name, hashed_password=hash_password(body.password), role=body.role, school_id=body.school_id) db.add(user) await db.commit() return {"id": user.id, "email": user.email, "full_name": user.full_name, "role": user.role.value} @router.put("/{user_id}") async def update_user( user_id: str, body: UserUpdate, _admin: HubUser = Depends(require_super_admin), db: AsyncSession = Depends(get_db), ): user = (await db.execute(select(HubUser).where(HubUser.id == user_id))).scalar_one_or_none() if not user: raise HTTPException(404) for field, value in body.model_dump(exclude_none=True).items(): setattr(user, field, value) await db.commit() return {"id": user.id, "email": user.email, "is_active": user.is_active}