"""Decisions router: CRUD for decision processes and their steps.""" from __future__ import annotations import uuid from fastapi import APIRouter, Depends, HTTPException, Query, status from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload from app.database import get_db from app.models.decision import Decision, DecisionStep from app.models.user import DuniterIdentity from app.schemas.decision import ( DecisionCreate, DecisionOut, DecisionStepCreate, DecisionStepOut, DecisionUpdate, ) from app.services.auth_service import get_current_identity router = APIRouter() # ── Helpers ───────────────────────────────────────────────────────────────── async def _get_decision(db: AsyncSession, decision_id: uuid.UUID) -> Decision: """Fetch a decision by ID with its steps eagerly loaded, or raise 404.""" result = await db.execute( select(Decision) .options(selectinload(Decision.steps)) .where(Decision.id == decision_id) ) decision = result.scalar_one_or_none() if decision is None: raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Decision introuvable") return decision # ── Decision routes ───────────────────────────────────────────────────────── @router.get("/", response_model=list[DecisionOut]) async def list_decisions( db: AsyncSession = Depends(get_db), decision_type: str | None = Query(default=None, description="Filtrer par type de decision"), status_filter: str | None = Query(default=None, alias="status", description="Filtrer par statut"), skip: int = Query(default=0, ge=0), limit: int = Query(default=50, ge=1, le=200), ) -> list[DecisionOut]: """List all decisions with optional filters.""" stmt = select(Decision).options(selectinload(Decision.steps)) if decision_type is not None: stmt = stmt.where(Decision.decision_type == decision_type) if status_filter is not None: stmt = stmt.where(Decision.status == status_filter) stmt = stmt.order_by(Decision.created_at.desc()).offset(skip).limit(limit) result = await db.execute(stmt) decisions = result.scalars().unique().all() return [DecisionOut.model_validate(d) for d in decisions] @router.post("/", response_model=DecisionOut, status_code=status.HTTP_201_CREATED) async def create_decision( payload: DecisionCreate, db: AsyncSession = Depends(get_db), identity: DuniterIdentity = Depends(get_current_identity), ) -> DecisionOut: """Create a new decision process.""" decision = Decision( **payload.model_dump(), created_by_id=identity.id, ) db.add(decision) await db.commit() await db.refresh(decision) # Reload with steps (empty at creation) decision = await _get_decision(db, decision.id) return DecisionOut.model_validate(decision) @router.get("/{id}", response_model=DecisionOut) async def get_decision( id: uuid.UUID, db: AsyncSession = Depends(get_db), ) -> DecisionOut: """Get a single decision with all its steps.""" decision = await _get_decision(db, id) return DecisionOut.model_validate(decision) @router.put("/{id}", response_model=DecisionOut) async def update_decision( id: uuid.UUID, payload: DecisionUpdate, db: AsyncSession = Depends(get_db), identity: DuniterIdentity = Depends(get_current_identity), ) -> DecisionOut: """Update a decision's metadata (title, description, status, protocol).""" decision = await _get_decision(db, id) update_data = payload.model_dump(exclude_unset=True) for field, value in update_data.items(): setattr(decision, field, value) await db.commit() await db.refresh(decision) # Reload with steps decision = await _get_decision(db, decision.id) return DecisionOut.model_validate(decision) # ── Decision Step routes ──────────────────────────────────────────────────── @router.post("/{id}/steps", response_model=DecisionStepOut, status_code=status.HTTP_201_CREATED) async def add_step( id: uuid.UUID, payload: DecisionStepCreate, db: AsyncSession = Depends(get_db), identity: DuniterIdentity = Depends(get_current_identity), ) -> DecisionStepOut: """Add a step to a decision process.""" # Verify decision exists decision = await _get_decision(db, id) step = DecisionStep( decision_id=decision.id, **payload.model_dump(), ) db.add(step) await db.commit() await db.refresh(step) return DecisionStepOut.model_validate(step)