"""Admin router for team management.""" from typing import List, Optional import uuid from fastapi import APIRouter, Body, Depends, HTTPException, Query from sqlalchemy.ext.asyncio import AsyncSession from sqlmodel import select from models.sql.team import TeamModel from models.sql.team_membership import TeamMembershipModel from models.sql.user import UserModel from services.database import get_async_session from services.access_service import get_accessible_client_ids from api.middlewares.rbac_middleware import check_team_admin from utils.auth_dependencies import get_current_user, require_client_admin TEAMS_ROUTER = APIRouter(prefix="/teams", tags=["Admin - Teams"]) @TEAMS_ROUTER.post("", status_code=201) async def create_team( name: str = Body(..., embed=True), client_id: Optional[uuid.UUID] = Body(None, embed=True), admin: UserModel = Depends(require_client_admin), session: AsyncSession = Depends(get_async_session), ): if client_id: await check_team_admin(admin, client_id, session) team = TeamModel(name=name, client_id=client_id, is_default=False) session.add(team) await session.commit() return { "id": str(team.id), "name": team.name, "client_id": str(team.client_id) if team.client_id else None, "is_default": team.is_default, } @TEAMS_ROUTER.get("", response_model=List[dict]) async def list_teams( client_id: Optional[uuid.UUID] = Query(None), admin: UserModel = Depends(require_client_admin), session: AsyncSession = Depends(get_async_session), ): query = select(TeamModel) if admin.role != "super_admin": accessible_ids = await get_accessible_client_ids(admin, session) query = query.where( (TeamModel.client_id.in_(accessible_ids)) | (TeamModel.is_default == True) # noqa: E712 ) if client_id: query = query.where(TeamModel.client_id == client_id) result = await session.execute(query.order_by(TeamModel.created_at.desc())) teams = result.scalars().all() return [ { "id": str(t.id), "name": t.name, "client_id": str(t.client_id) if t.client_id else None, "is_default": t.is_default, "created_at": t.created_at.isoformat() if t.created_at else None, } for t in teams ] @TEAMS_ROUTER.get("/{team_id}") async def get_team( team_id: uuid.UUID, admin: UserModel = Depends(require_client_admin), session: AsyncSession = Depends(get_async_session), ): team = await session.get(TeamModel, team_id) if not team: raise HTTPException(status_code=404, detail="Team not found") if team.client_id and admin.role != "super_admin": await check_team_admin(admin, team.client_id, session) # Get members result = await session.execute( select(UserModel) .join(TeamMembershipModel, TeamMembershipModel.user_id == UserModel.id) .where(TeamMembershipModel.team_id == team_id) ) members = result.scalars().all() return { "id": str(team.id), "name": team.name, "client_id": str(team.client_id) if team.client_id else None, "is_default": team.is_default, "created_at": team.created_at.isoformat() if team.created_at else None, "members": [ { "id": str(m.id), "email": m.email, "display_name": m.display_name, "role": m.role, } for m in members ], } @TEAMS_ROUTER.post("/{team_id}/members", status_code=201) async def add_team_member( team_id: uuid.UUID, user_id: uuid.UUID = Body(..., embed=True), admin: UserModel = Depends(require_client_admin), session: AsyncSession = Depends(get_async_session), ): team = await session.get(TeamModel, team_id) if not team: raise HTTPException(status_code=404, detail="Team not found") if team.client_id and admin.role != "super_admin": await check_team_admin(admin, team.client_id, session) user = await session.get(UserModel, user_id) if not user: raise HTTPException(status_code=404, detail="User not found") # Check for existing membership result = await session.execute( select(TeamMembershipModel).where( TeamMembershipModel.user_id == user_id, TeamMembershipModel.team_id == team_id, ) ) if result.scalar_one_or_none(): raise HTTPException(status_code=409, detail="User already in team") membership = TeamMembershipModel( user_id=user_id, team_id=team_id, assigned_by=admin.id, ) session.add(membership) await session.commit() return {"message": "Member added", "user_id": str(user_id), "team_id": str(team_id)} @TEAMS_ROUTER.delete("/{team_id}/members/{user_id}") async def remove_team_member( team_id: uuid.UUID, user_id: uuid.UUID, admin: UserModel = Depends(require_client_admin), session: AsyncSession = Depends(get_async_session), ): team = await session.get(TeamModel, team_id) if not team: raise HTTPException(status_code=404, detail="Team not found") if team.client_id and admin.role != "super_admin": await check_team_admin(admin, team.client_id, session) result = await session.execute( select(TeamMembershipModel).where( TeamMembershipModel.user_id == user_id, TeamMembershipModel.team_id == team_id, ) ) membership = result.scalar_one_or_none() if not membership: raise HTTPException(status_code=404, detail="Membership not found") await session.delete(membership) await session.commit() return {"message": "Member removed"} @TEAMS_ROUTER.delete("/{team_id}") async def delete_team( team_id: uuid.UUID, admin: UserModel = Depends(require_client_admin), session: AsyncSession = Depends(get_async_session), ): team = await session.get(TeamModel, team_id) if not team: raise HTTPException(status_code=404, detail="Team not found") if team.is_default: raise HTTPException(status_code=400, detail="Cannot delete the default team") if team.client_id and admin.role != "super_admin": await check_team_admin(admin, team.client_id, session) # Remove all memberships first result = await session.execute( select(TeamMembershipModel).where(TeamMembershipModel.team_id == team_id) ) memberships = result.scalars().all() for m in memberships: await session.delete(m) await session.delete(team) await session.commit() return {"message": "Team deleted"}