""" Call Flows API — Store and manage IVR navigation trees. The system gets smarter every time you call somewhere. Thin HTTP layer over the shared data functions in call_persistence. """ from fastapi import APIRouter, Depends, HTTPException from slugify import slugify from sqlalchemy.ext.asyncio import AsyncSession from db.database import get_db from models.call_flow import ( CallFlow, CallFlowCreate, CallFlowSummary, CallFlowUpdate, ) from services import call_persistence as store router = APIRouter() @router.post("/", response_model=CallFlow) async def create_call_flow( flow: CallFlowCreate, db: AsyncSession = Depends(get_db), ): """Store a new call flow for a phone number.""" flow_id = slugify(flow.name) if await store.get_flow(db, flow_id): raise HTTPException( status_code=409, detail=f"Call flow '{flow_id}' already exists. Use PUT to update.", ) row = await store.create_flow( db, flow_id=flow_id, name=flow.name, phone_number=flow.phone_number, description=flow.description, steps=[s.model_dump() for s in flow.steps], tags=flow.tags, notes=flow.notes, ) return store.flow_to_model(row) @router.get("/", response_model=list[CallFlowSummary]) async def list_call_flows( db: AsyncSession = Depends(get_db), ): """List all stored call flows.""" rows = await store.list_flows(db) return [ CallFlowSummary( id=row.id, name=row.name, phone_number=row.phone_number, description=row.description or "", step_count=len(row.steps) if row.steps else 0, avg_hold_time=row.avg_hold_time, success_rate=row.success_rate, last_used=row.last_used, times_used=row.times_used or 0, tags=row.tags or [], ) for row in rows ] @router.get("/{flow_id}", response_model=CallFlow) async def get_call_flow( flow_id: str, db: AsyncSession = Depends(get_db), ): """Get a stored call flow by ID.""" row = await store.get_flow(db, flow_id) if not row: raise HTTPException(status_code=404, detail=f"Call flow '{flow_id}' not found") return store.flow_to_model(row) @router.get("/by-number/{phone_number}", response_model=CallFlow) async def get_flow_for_number( phone_number: str, db: AsyncSession = Depends(get_db), ): """Look up stored call flow by phone number.""" row = await store.get_flow_by_number(db, phone_number) if not row: raise HTTPException( status_code=404, detail=f"No call flow found for {phone_number}", ) return store.flow_to_model(row) @router.put("/{flow_id}", response_model=CallFlow) async def update_call_flow( flow_id: str, update: CallFlowUpdate, db: AsyncSession = Depends(get_db), ): """Update an existing call flow.""" row = await store.get_flow(db, flow_id) if not row: raise HTTPException(status_code=404, detail=f"Call flow '{flow_id}' not found") if update.name is not None: row.name = update.name if update.description is not None: row.description = update.description if update.steps is not None: row.steps = [s.model_dump() for s in update.steps] if update.tags is not None: row.tags = update.tags if update.notes is not None: row.notes = update.notes if update.last_verified is not None: row.last_verified = update.last_verified await db.flush() return store.flow_to_model(row) @router.delete("/{flow_id}") async def delete_call_flow( flow_id: str, db: AsyncSession = Depends(get_db), ): """Delete a stored call flow.""" row = await store.get_flow(db, flow_id) if not row: raise HTTPException(status_code=404, detail=f"Call flow '{flow_id}' not found") await db.delete(row) return {"status": "deleted", "flow_id": flow_id}