"""Equipment API router: list, detail, categories.""" from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlalchemy import select, func, or_ from sqlalchemy.ext.asyncio import AsyncSession from typing import Any from app.database import get_db from app.models.equipment import EquipmentCache from app.schemas.equipment import EquipmentItem, EquipmentDetail, PaginatedResponse from app.cache import cache router = APIRouter(prefix="/api/equipment", tags=["equipment"]) @router.get("", response_model=PaginatedResponse) async def list_equipment( search: str | None = Query(None), category: str | None = Query(None), sort: str | None = Query("name_asc"), page: int = Query(1, ge=1), page_size: int = Query(20, ge=1, le=100), db: AsyncSession = Depends(get_db), ) -> Any: """Return paginated equipment list with optional search/filter/sort.""" cache_key = f"equipment:list:{search}:{category}:{sort}:{page}:{page_size}" cached = await cache.get(cache_key) if cached: return cached query = select(EquipmentCache) count_query = select(func.count(EquipmentCache.id)) if search: query = query.where(EquipmentCache.name.ilike(f"%{search}%")) count_query = count_query.where(EquipmentCache.name.ilike(f"%{search}%")) if category: query = query.where(EquipmentCache.category == category) count_query = count_query.where(EquipmentCache.category == category) if sort == "name_desc": query = query.order_by(EquipmentCache.name.desc()) else: query = query.order_by(EquipmentCache.name.asc()) total_result = await db.execute(count_query) total = total_result.scalar() or 0 offset = (page - 1) * page_size result = await db.execute(query.offset(offset).limit(page_size)) items = result.scalars().all() response = { "items": [EquipmentItem.model_validate(item).model_dump() for item in items], "total": total, "page": page, "page_size": page_size, "total_pages": (total + page_size - 1) // page_size if page_size > 0 else 0, } await cache.set(cache_key, response, ttl=3600) return response @router.get("/categories", response_model=list[str]) async def list_categories(db: AsyncSession = Depends(get_db)) -> Any: """Return all distinct equipment categories.""" cached = await cache.get("equipment:categories") if cached: return cached result = await db.execute( select(EquipmentCache.category).distinct().where(EquipmentCache.category.isnot(None)) ) categories = [row[0] for row in result.fetchall() if row[0]] await cache.set("equipment:categories", categories, ttl=3600) return categories @router.get("/{equipment_id}", response_model=EquipmentDetail) async def get_equipment(equipment_id: int, db: AsyncSession = Depends(get_db)) -> Any: """Return a single equipment detail by ID.""" cache_key = f"equipment:detail:{equipment_id}" cached = await cache.get(cache_key) if cached: return cached result = await db.execute(select(EquipmentCache).where(EquipmentCache.id == equipment_id)) item = result.scalar_one_or_none() if not item: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Equipment not found") response = EquipmentDetail.model_validate(item).model_dump() await cache.set(cache_key, response.model_dump(), ttl=3600) return response