"""Budget and runway alert evaluation.""" from __future__ import annotations from dataclasses import dataclass from fin_hub.services.runway import RunwayResult @dataclass(frozen=True) class BudgetAlert: code: str severity: str summary: str detail: dict def as_dict(self) -> dict: return { "code": self.code, "severity": self.severity, "summary": self.summary, "detail": self.detail, } def evaluate_budget_alerts( *, runway: RunwayResult, allocated: float | None = None, spent: float | None = None, ) -> list[BudgetAlert]: alerts: list[BudgetAlert] = [] if runway.below_threshold and runway.monthly_burn > 0: alerts.append( BudgetAlert( code="runway_below_threshold", severity="high", summary=( f"Projected runway {runway.months_remaining:.1f} months " f"is below threshold {runway.alert_threshold_months:.1f}" ), detail=runway.as_dict(), ) ) if allocated is not None and spent is not None and allocated > 0: utilisation = spent / allocated if utilisation >= 0.8: alerts.append( BudgetAlert( code="budget_pressure", severity="medium" if utilisation < 1.0 else "high", summary=f"Budget utilisation at {utilisation * 100:.0f}%", detail={ "allocated": allocated, "spent": spent, "utilisation": utilisation, }, ) ) return alerts