from fastapi import APIRouter, Depends from pydantic import BaseModel, Field import asyncpg from app.core.responses import ok from app.dependencies import get_db, require_admin from app.exceptions import NotFoundError router = APIRouter(prefix="/services", tags=["Admin — Services"]) class ServiceCreate(BaseModel): name: str = Field(min_length=1, max_length=120) description: str | None = None duration_minutes: int = Field(default=60, ge=5, le=480) price: float = Field(default=0.0, ge=0) is_active: bool = True class ServiceUpdate(BaseModel): name: str | None = Field(None, min_length=1, max_length=120) description: str | None = None duration_minutes: int | None = Field(None, ge=5, le=480) price: float | None = Field(None, ge=0) is_active: bool | None = None @router.get("") async def list_services( db: asyncpg.Connection = Depends(get_db), _: dict = Depends(require_admin), ): rows = await db.fetch( "SELECT * FROM services ORDER BY price ASC, name ASC" ) return ok([dict(r) for r in rows]) @router.post("", status_code=201) async def create_service( body: ServiceCreate, db: asyncpg.Connection = Depends(get_db), _: dict = Depends(require_admin), ): row = await db.fetchrow( """ INSERT INTO services (name, description, duration_minutes, price, is_active) VALUES ($1, $2, $3, $4, $5) RETURNING * """, body.name, body.description, body.duration_minutes, body.price, body.is_active, ) return ok(dict(row)) @router.put("/{service_id}") async def update_service( service_id: str, body: ServiceUpdate, db: asyncpg.Connection = Depends(get_db), _: dict = Depends(require_admin), ): updates = {k: v for k, v in body.model_dump().items() if v is not None} if not updates: row = await db.fetchrow("SELECT * FROM services WHERE id = $1", service_id) if not row: raise NotFoundError("service") return ok(dict(row)) set_clauses = [f"{k} = ${i + 2}" for i, k in enumerate(updates)] row = await db.fetchrow( f"UPDATE services SET {', '.join(set_clauses)}, updated_at = now() WHERE id = $1 RETURNING *", service_id, *updates.values(), ) if not row: raise NotFoundError("service") return ok(dict(row)) @router.delete("/{service_id}", status_code=204) async def delete_service( service_id: str, db: asyncpg.Connection = Depends(get_db), _: dict = Depends(require_admin), ): result = await db.execute("DELETE FROM services WHERE id = $1", service_id) if result == "DELETE 0": raise NotFoundError("service")