"""Analytics API endpoints for summaries and funnels.""" from __future__ import annotations from decimal import Decimal from fastapi import APIRouter, Depends, Query from pydantic import BaseModel, ConfigDict, field_serializer from app.api.deps import get_analytics_service, get_organization_context from app.models.deal import DealStage, DealStatus from app.services.analytics_service import AnalyticsService, DealSummary, StageBreakdown from app.services.organization_service import OrganizationContext def _decimal_to_str(value: Decimal) -> str: normalized = value.normalize() return format(normalized, "f") router = APIRouter(prefix="/analytics", tags=["analytics"]) class StatusSummaryModel(BaseModel): model_config = ConfigDict(from_attributes=True) status: DealStatus count: int amount_sum: Decimal @field_serializer("amount_sum") def serialize_amount_sum(self, value: Decimal) -> str: return _decimal_to_str(value) class WonStatisticsModel(BaseModel): model_config = ConfigDict(from_attributes=True) count: int amount_sum: Decimal average_amount: Decimal @field_serializer("amount_sum", "average_amount") def serialize_decimal_fields(self, value: Decimal) -> str: return _decimal_to_str(value) class NewDealsWindowModel(BaseModel): model_config = ConfigDict(from_attributes=True) days: int count: int class DealSummaryResponse(BaseModel): model_config = ConfigDict(from_attributes=True) by_status: list[StatusSummaryModel] won: WonStatisticsModel new_deals: NewDealsWindowModel total_deals: int class StageBreakdownModel(BaseModel): model_config = ConfigDict(from_attributes=True) stage: DealStage total: int by_status: dict[DealStatus, int] conversion_to_next: float | None class DealFunnelResponse(BaseModel): stages: list[StageBreakdownModel] @router.get("/deals/summary", response_model=DealSummaryResponse) async def deals_summary( days: int = Query(30, ge=1, le=180), context: OrganizationContext = Depends(get_organization_context), service: AnalyticsService = Depends(get_analytics_service), ) -> DealSummaryResponse: """Return aggregated deal statistics for the current organization.""" summary: DealSummary = await service.get_deal_summary(context.organization_id, days=days) return DealSummaryResponse.model_validate(summary) @router.get("/deals/funnel", response_model=DealFunnelResponse) async def deals_funnel( context: OrganizationContext = Depends(get_organization_context), service: AnalyticsService = Depends(get_analytics_service), ) -> DealFunnelResponse: """Return funnel breakdown by stages and statuses.""" breakdowns: list[StageBreakdown] = await service.get_deal_funnel(context.organization_id) return DealFunnelResponse( stages=[StageBreakdownModel.model_validate(item) for item in breakdowns], )