""" Script Flow Management API. [AC-AISVC-71, AC-AISVC-72, AC-AISVC-73] Script flow CRUD endpoints. """ import logging import uuid from typing import Any from fastapi import APIRouter, Depends, Header, HTTPException from sqlalchemy.ext.asyncio import AsyncSession from app.core.database import get_session from app.models.entities import ScriptFlowCreate, ScriptFlowUpdate from app.services.flow.flow_service import ScriptFlowService logger = logging.getLogger(__name__) router = APIRouter(prefix="/admin/script-flows", tags=["Script Flows"]) def get_tenant_id(x_tenant_id: str = Header(..., alias="X-Tenant-Id")) -> str: """Extract tenant ID from header.""" if not x_tenant_id: raise HTTPException(status_code=400, detail="X-Tenant-Id header is required") return x_tenant_id @router.get("") async def list_flows( tenant_id: str = Depends(get_tenant_id), is_enabled: bool | None = None, session: AsyncSession = Depends(get_session), ) -> dict[str, Any]: """ [AC-AISVC-72] List all script flows for a tenant. """ logger.info(f"[AC-AISVC-72] Listing script flows for tenant={tenant_id}, is_enabled={is_enabled}") service = ScriptFlowService(session) flows = await service.list_flows(tenant_id, is_enabled) data = [] for f in flows: linked_rule_count = await service._get_linked_rule_count(tenant_id, f.id) data.append({ "id": str(f.id), "name": f.name, "description": f.description, "step_count": len(f.steps), "is_enabled": f.is_enabled, "linked_rule_count": linked_rule_count, "created_at": f.created_at.isoformat(), "updated_at": f.updated_at.isoformat(), }) return {"data": data} @router.post("", status_code=201) async def create_flow( body: ScriptFlowCreate, tenant_id: str = Depends(get_tenant_id), session: AsyncSession = Depends(get_session), ) -> dict[str, Any]: """ [AC-AISVC-71] Create a new script flow. """ logger.info(f"[AC-AISVC-71] Creating script flow for tenant={tenant_id}, name={body.name}") service = ScriptFlowService(session) try: flow = await service.create_flow(tenant_id, body) except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) return { "id": str(flow.id), "name": flow.name, "description": flow.description, "step_count": len(flow.steps), "is_enabled": flow.is_enabled, "created_at": flow.created_at.isoformat(), "updated_at": flow.updated_at.isoformat(), } @router.get("/{flow_id}") async def get_flow_detail( flow_id: uuid.UUID, tenant_id: str = Depends(get_tenant_id), session: AsyncSession = Depends(get_session), ) -> dict[str, Any]: """ [AC-AISVC-73] Get script flow detail with complete step definitions. """ logger.info(f"[AC-AISVC-73] Getting flow detail for tenant={tenant_id}, id={flow_id}") service = ScriptFlowService(session) detail = await service.get_flow_detail(tenant_id, flow_id) if not detail: raise HTTPException(status_code=404, detail="Flow not found") return detail @router.put("/{flow_id}") async def update_flow( flow_id: uuid.UUID, body: ScriptFlowUpdate, tenant_id: str = Depends(get_tenant_id), session: AsyncSession = Depends(get_session), ) -> dict[str, Any]: """ [AC-AISVC-73] Update script flow definition. """ logger.info(f"[AC-AISVC-73] Updating flow for tenant={tenant_id}, id={flow_id}") service = ScriptFlowService(session) try: flow = await service.update_flow(tenant_id, flow_id, body) except ValueError as e: raise HTTPException(status_code=400, detail=str(e)) if not flow: raise HTTPException(status_code=404, detail="Flow not found") return { "id": str(flow.id), "name": flow.name, "description": flow.description, "step_count": len(flow.steps), "is_enabled": flow.is_enabled, "created_at": flow.created_at.isoformat(), "updated_at": flow.updated_at.isoformat(), } @router.delete("/{flow_id}", status_code=204) async def delete_flow( flow_id: uuid.UUID, tenant_id: str = Depends(get_tenant_id), session: AsyncSession = Depends(get_session), ) -> None: """ Delete a script flow. """ logger.info(f"Deleting flow for tenant={tenant_id}, id={flow_id}") service = ScriptFlowService(session) success = await service.delete_flow(tenant_id, flow_id) if not success: raise HTTPException(status_code=404, detail="Flow not found")