"""Phase 6 maintenance loop routes.""" from __future__ import annotations from typing import Annotated from fastapi import APIRouter, Body, Depends, HTTPException, Query from pydantic import BaseModel, Field from sqlalchemy.orm import Session from ont_platform.api.db_deps import get_db from ont_platform.core.maintenance import ( MaintenanceLoopService, MaintenancePermissionError, MaintenanceProposalNotFoundError, maintenance_proposal_to_dict, maintenance_run_to_dict, ) from ont_platform.storage.models import MaintenanceProposalStatus router = APIRouter(prefix="/api/v1/maintenance", tags=["maintenance"]) class MaintenanceRunRequest(BaseModel): project_id: str = "default" requested_by: str = "system" actor_role: str = "admin" low_confidence_threshold: float = Field(default=0.65, ge=0.0, le=1.0) class ProposalReviewRequest(BaseModel): reviewed_by: str actor_role: str = "admin" approve: bool reason: str | None = None @router.post("/runs") async def start_maintenance_run( request: Annotated[MaintenanceRunRequest, Body()], db: Annotated[Session, Depends(get_db)], ) -> dict: service = MaintenanceLoopService(db) try: run = await service.run( project_id=request.project_id, requested_by=request.requested_by, actor_role=request.actor_role, low_confidence_threshold=request.low_confidence_threshold, ) except MaintenancePermissionError as exc: raise HTTPException(status_code=403, detail=str(exc)) from exc except Exception as exc: db.commit() raise HTTPException(status_code=500, detail=f"Maintenance run failed: {exc}") from exc db.commit() return {"status": "success", "run": maintenance_run_to_dict(run)} @router.get("/runs") def list_maintenance_runs( db: Annotated[Session, Depends(get_db)], project_id: Annotated[str, Query()] = "default", limit: Annotated[int, Query(ge=1, le=100)] = 50, ) -> dict: service = MaintenanceLoopService(db) runs = service.list_runs(project_id=project_id, limit=limit) return {"runs": [maintenance_run_to_dict(run) for run in runs]} @router.get("/proposals") def list_maintenance_proposals( db: Annotated[Session, Depends(get_db)], project_id: Annotated[str, Query()] = "default", status: Annotated[str | None, Query()] = None, limit: Annotated[int, Query(ge=1, le=200)] = 100, ) -> dict: service = MaintenanceLoopService(db) try: proposals = service.list_proposals( project_id=project_id, status=MaintenanceProposalStatus(status) if status else None, limit=limit, ) except ValueError as exc: raise HTTPException(status_code=400, detail=f"Invalid proposal status: {status}") from exc return {"proposals": [maintenance_proposal_to_dict(proposal) for proposal in proposals]} @router.post("/proposals/{proposal_id}/review") async def review_maintenance_proposal( proposal_id: str, request: Annotated[ProposalReviewRequest, Body()], db: Annotated[Session, Depends(get_db)], ) -> dict: service = MaintenanceLoopService(db) try: proposal = await service.review_proposal( proposal_id=proposal_id, reviewed_by=request.reviewed_by, actor_role=request.actor_role, approve=request.approve, reason=request.reason, ) except MaintenancePermissionError as exc: raise HTTPException(status_code=403, detail=str(exc)) from exc except MaintenanceProposalNotFoundError as exc: raise HTTPException(status_code=404, detail=str(exc)) from exc except ValueError as exc: raise HTTPException(status_code=400, detail=str(exc)) from exc db.commit() return {"status": "success", "proposal": maintenance_proposal_to_dict(proposal)} __all__ = ["router"]