"""Admin router for client management.""" import re 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.client import ClientModel from models.sql.team import TeamModel from models.sql.user import UserModel from services.database import get_async_session from services.access_service import get_accessible_clients from api.middlewares.rbac_middleware import check_client_access from utils.auth_dependencies import require_super_admin, require_client_admin CLIENTS_ROUTER = APIRouter(prefix="/clients", tags=["Admin - Clients"]) def _slugify(name: str) -> str: slug = name.lower().strip() slug = re.sub(r"[^a-z0-9]+", "-", slug) return slug.strip("-") @CLIENTS_ROUTER.post("", status_code=201) async def create_client( name: str = Body(..., embed=True), review_policy: str = Body("self_approve", embed=True), _: UserModel = Depends(require_super_admin), session: AsyncSession = Depends(get_async_session), ): slug = _slugify(name) # Check slug uniqueness result = await session.execute( select(ClientModel).where(ClientModel.slug == slug) ) if result.scalar_one_or_none(): raise HTTPException(status_code=409, detail="A client with this name already exists") client = ClientModel(name=name, slug=slug, review_policy=review_policy) session.add(client) await session.flush() # Auto-create a team for this client team = TeamModel(name=f"{name} Team", client_id=client.id, is_default=False) session.add(team) await session.commit() return { "id": str(client.id), "name": client.name, "slug": client.slug, "review_policy": client.review_policy, "team_id": str(team.id), } @CLIENTS_ROUTER.get("", response_model=List[dict]) async def list_clients( admin: UserModel = Depends(require_client_admin), session: AsyncSession = Depends(get_async_session), ): clients = await get_accessible_clients(admin, session) return [ { "id": str(c.id), "name": c.name, "slug": c.slug, "logo_path": c.logo_path, "retention_days": c.retention_days, "review_policy": c.review_policy, "is_active": c.is_active, "created_at": c.created_at.isoformat() if c.created_at else None, } for c in clients ] @CLIENTS_ROUTER.get("/{client_id}") async def get_client( client_id: uuid.UUID, admin: UserModel = Depends(require_client_admin), session: AsyncSession = Depends(get_async_session), ): await check_client_access(admin, client_id, session) client = await session.get(ClientModel, client_id) if not client: raise HTTPException(status_code=404, detail="Client not found") return { "id": str(client.id), "name": client.name, "slug": client.slug, "logo_path": client.logo_path, "retention_days": client.retention_days, "review_policy": client.review_policy, "is_active": client.is_active, "created_at": client.created_at.isoformat() if client.created_at else None, } @CLIENTS_ROUTER.put("/{client_id}") async def update_client( client_id: uuid.UUID, name: Optional[str] = Body(None, embed=True), review_policy: Optional[str] = Body(None, embed=True), retention_days: Optional[int] = Body(None, embed=True), admin: UserModel = Depends(require_client_admin), session: AsyncSession = Depends(get_async_session), ): await check_client_access(admin, client_id, session) client = await session.get(ClientModel, client_id) if not client: raise HTTPException(status_code=404, detail="Client not found") if name is not None: client.name = name client.slug = _slugify(name) if review_policy is not None: client.review_policy = review_policy if retention_days is not None: client.retention_days = retention_days session.add(client) await session.commit() return { "id": str(client.id), "name": client.name, "slug": client.slug, "review_policy": client.review_policy, "retention_days": client.retention_days, } @CLIENTS_ROUTER.delete("/{client_id}") async def deactivate_client( client_id: uuid.UUID, _: UserModel = Depends(require_super_admin), session: AsyncSession = Depends(get_async_session), ): client = await session.get(ClientModel, client_id) if not client: raise HTTPException(status_code=404, detail="Client not found") client.is_active = False session.add(client) await session.commit() return {"message": "Client deactivated", "client_id": str(client.id)}