"""Analytics router — KPI dashboard endpoints."""
from datetime import date, timedelta
from typing import Optional
from fastapi import APIRouter, Depends, Query
from sqlalchemy.ext.asyncio import AsyncSession
from pydantic import BaseModel

from app.database import get_db
from app.utils.auth import require_admin_or_pm
from app.models.user import User
from app.services import kpi_service

router = APIRouter(prefix="/analytics", tags=["analytics"])


# ── Response models ────────────────────────────────────────────────────────────

class ThroughputPoint(BaseModel):
    week: str
    count: int
    completed: int


class RevenuePoint(BaseModel):
    month: str
    revenue: float
    order_count: int


class ProcessingTimeStats(BaseModel):
    avg_submit_to_complete_s: Optional[float]
    avg_submit_to_processing_s: Optional[float]
    p50_s: Optional[float]
    p95_s: Optional[float]


class ItemStatusBreakdown(BaseModel):
    pending: int
    approved: int
    rejected: int


class RenderTimeBreakdown(BaseModel):
    avg_stl_s: Optional[float]
    avg_render_s: Optional[float]
    avg_total_s: Optional[float]
    sample_count: int


class TopLevelSummary(BaseModel):
    total_orders: int
    completed_orders: int
    total_revenue: float
    total_rendering_items: int


class CategoryCount(BaseModel):
    category: str
    count: int


class ProductCategoryStats(BaseModel):
    unique_products_rendered: int
    total_products: int
    products_with_cad: int
    products_by_category: list[CategoryCount]


class OutputTypeUsagePoint(BaseModel):
    output_type: str
    count: int


class RenderStatusDistribution(BaseModel):
    pending: int
    processing: int
    completed: int
    failed: int


class RendererUsagePoint(BaseModel):
    renderer: str
    count: int


class TopProductEntry(BaseModel):
    pim_id: str
    product_name: Optional[str]
    category: str
    order_count: int


class CategoryRevenueEntry(BaseModel):
    category: str
    order_count: int
    revenue: float


class RenderBackendStatsEntry(BaseModel):
    backend: str
    total: int
    completed: int
    failed: int
    avg_render_s: float | None
    p50_render_s: float | None


class RenderTimeByOutputType(BaseModel):
    output_type: str
    job_count: int
    avg_render_s: float | None
    min_render_s: float | None
    max_render_s: float | None
    p50_render_s: float | None


class OrdersByUserEntry(BaseModel):
    full_name: str
    email: str
    role: str
    order_count: int
    revenue: float


class DashboardKPIs(BaseModel):
    summary: TopLevelSummary
    throughput: list[ThroughputPoint]
    revenue: list[RevenuePoint]
    processing_times: ProcessingTimeStats
    item_status: ItemStatusBreakdown
    render_times: RenderTimeBreakdown
    product_stats: ProductCategoryStats
    output_type_usage: list[OutputTypeUsagePoint]
    render_status: RenderStatusDistribution
    renderer_usage: list[RendererUsagePoint]
    top_products: list[TopProductEntry]
    orders_by_user: list[OrdersByUserEntry]
    category_revenue: list[CategoryRevenueEntry]
    render_backend_stats: list[RenderBackendStatsEntry]
    render_time_by_output_type: list[RenderTimeByOutputType]


# ── Endpoints ──────────────────────────────────────────────────────────────────

@router.get("/dashboard", response_model=DashboardKPIs)
async def get_dashboard_kpis(
    date_from: date | None = Query(None, description="Start date (ISO), e.g. 2025-01-01"),
    date_to: date | None = Query(None, description="End date (ISO), e.g. 2025-06-30"),
    _user: User = Depends(require_admin_or_pm),
    db: AsyncSession = Depends(get_db),
) -> DashboardKPIs:
    """Aggregate KPI data for the analytics dashboard."""
    # Default: last 12 weeks
    if date_to is None:
        date_to = date.today()
    if date_from is None:
        date_from = date_to - timedelta(weeks=12)

    df = date_from.isoformat()
    dt = date_to.isoformat()

    summary_data, throughput_data, revenue_data, proc_data, item_data, render_data = (
        await kpi_service.top_level_summary(db, df, dt),
        await kpi_service.order_throughput_by_week(db, df, dt),
        await kpi_service.revenue_overview(db, df, dt),
        await kpi_service.processing_time_stats(db, df, dt),
        await kpi_service.item_status_breakdown(db, df, dt),
        await kpi_service.render_time_breakdown(db, df, dt),
    )

    product_stats_data = await kpi_service.product_and_category_stats(db, df, dt)
    ot_usage_data = await kpi_service.output_type_usage(db, df, dt)
    render_status_data, renderer_usage_data = await kpi_service.render_status_distribution(db, df, dt)
    top_products_data = await kpi_service.top_products(db, df, dt)
    cat_revenue_data = await kpi_service.category_revenue(db, df, dt)
    users_data = await kpi_service.orders_by_user(db, df, dt)
    backend_stats_data = await kpi_service.render_backend_stats(db, df, dt)
    render_time_ot_data = await kpi_service.render_time_by_output_type(db, df, dt)

    return DashboardKPIs(
        summary=TopLevelSummary(**summary_data),
        throughput=[ThroughputPoint(**p) for p in throughput_data],
        revenue=[RevenuePoint(**p) for p in revenue_data],
        processing_times=ProcessingTimeStats(**proc_data),
        item_status=ItemStatusBreakdown(**item_data),
        render_times=RenderTimeBreakdown(**render_data),
        product_stats=ProductCategoryStats(
            **{**product_stats_data, "products_by_category": [
                CategoryCount(**c) for c in product_stats_data["products_by_category"]
            ]}
        ),
        output_type_usage=[OutputTypeUsagePoint(**p) for p in ot_usage_data],
        render_status=RenderStatusDistribution(**render_status_data),
        renderer_usage=[RendererUsagePoint(**p) for p in renderer_usage_data],
        top_products=[TopProductEntry(**p) for p in top_products_data],
        orders_by_user=[OrdersByUserEntry(**p) for p in users_data],
        category_revenue=[CategoryRevenueEntry(**p) for p in cat_revenue_data],
        render_backend_stats=[RenderBackendStatsEntry(**p) for p in backend_stats_data],
        render_time_by_output_type=[RenderTimeByOutputType(**p) for p in render_time_ot_data],
    )
