"""Pydantic v2 response models for the dashboard API.

All models are used by :mod:`llmhost.proxy.dashboard_routes` and are designed
for direct JSON serialisation by FastAPI.
"""

from __future__ import annotations

from datetime import datetime  # noqa: TC003 - Required at runtime for Pydantic model_rebuild
from typing import Any

from pydantic import BaseModel, Field

# ---------------------------------------------------------------------------
# Savings
# ---------------------------------------------------------------------------


class SavingsResponse(BaseModel):
    """Aggregate savings report for a time period."""

    total_requests: int = 0
    local_requests: int = 0
    cloud_requests: int = 0
    cache_hits: int = 0
    actual_cost: float = 0.0
    estimated_without_llmhosts: float = 0.0
    savings_amount: float = 0.0
    savings_percent: float = 0.0
    period: str = "month"
    baseline_monthly: float | None = None
    vs_baseline_savings: float | None = None
    honesty_score: float = 0.0
    methodology: str = ""


class TimelinePoint(BaseModel):
    """A single data point for a savings/cost timeline chart."""

    date: str
    requests: int = 0
    cost: float = 0.0
    savings: float = 0.0


# ---------------------------------------------------------------------------
# Metrics
# ---------------------------------------------------------------------------


class MetricsResponse(BaseModel):
    """Real-time metrics snapshot."""

    uptime: float = 0.0
    requests_total: int = 0
    rpm: float = 0.0
    cache_hit_rate: float = 0.0
    avg_latency: float = 0.0
    savings_total: float = 0.0
    by_backend: dict[str, int] = Field(default_factory=dict)
    by_model: list[str] = Field(default_factory=list)


# ---------------------------------------------------------------------------
# Request log
# ---------------------------------------------------------------------------


class RequestLogItem(BaseModel):
    """Summary of a single request in the log list."""

    id: str
    timestamp: datetime
    model_requested: str
    model_used: str
    backend: str
    tier: str
    cost: float = 0.0
    savings: float = 0.0
    cached: bool = False
    latency_ms: float = 0.0
    status: str = "ok"


class RequestLogResponse(BaseModel):
    """Paginated list of recent requests."""

    items: list[RequestLogItem] = Field(default_factory=list)
    total: int = 0
    offset: int = 0
    limit: int = 50


class CostBreakdownItem(BaseModel):
    """Single line-item in a cost breakdown."""

    label: str
    amount: float = 0.0


class RequestDetailResponse(RequestLogItem):
    """Full detail for a specific request (extends RequestLogItem)."""

    messages_preview: str = ""
    routing_decision: dict[str, Any] = Field(default_factory=dict)
    alternatives: list[dict[str, Any]] = Field(default_factory=list)
    full_cost_breakdown: list[CostBreakdownItem] = Field(default_factory=list)
    input_tokens: int = 0
    output_tokens: int = 0
    routing_reasoning: str = ""


# ---------------------------------------------------------------------------
# Backends
# ---------------------------------------------------------------------------


class BackendStatus(BaseModel):
    """Health and capability status of a single backend."""

    type: str
    url: str = ""
    healthy: bool = False
    latency_ms: float = 0.0
    models: list[str] = Field(default_factory=list)
    model_count: int = 0
    uptime_pct: float = 100.0


class BackendsResponse(BaseModel):
    """List of all registered backends and their status."""

    backends: list[BackendStatus] = Field(default_factory=list)


# ---------------------------------------------------------------------------
# Config
# ---------------------------------------------------------------------------


class ConfigResponse(BaseModel):
    """Sanitised configuration (no secrets, no raw key values)."""

    server: dict[str, Any] = Field(default_factory=dict)
    router: dict[str, Any] = Field(default_factory=dict)
    cache: dict[str, Any] = Field(default_factory=dict)


# ---------------------------------------------------------------------------
# Cache management
# ---------------------------------------------------------------------------


class CacheTierStats(BaseModel):
    """Statistics for a single cache tier."""

    tier: str
    name: str
    enabled: bool = True
    total_entries: int = 0
    total_size_bytes: int = 0
    hit_count: int = 0
    miss_count: int = 0
    hit_rate: float = 0.0
    entries_by_model: dict[str, int] = Field(default_factory=dict)
    # vCache-specific
    false_positive_rate: float | None = None
    delta_target: float | None = None
    avg_threshold: float | None = None
    min_threshold: float | None = None
    max_threshold: float | None = None
    threshold_distribution: dict[str, int] | None = None
    # Namespace-specific
    total_namespaces: int | None = None
    entries_by_namespace: dict[str, int] | None = None
    hit_rate_by_namespace: dict[str, float] | None = None


class CacheStatsResponse(BaseModel):
    """Combined cache statistics across all tiers."""

    tiers_active: list[str] = Field(default_factory=list)
    combined_hit_rate: float = 0.0
    total_entries: int = 0
    total_size_bytes: int = 0
    tiers: list[CacheTierStats] = Field(default_factory=list)


class CacheEntryItem(BaseModel):
    """A single cache entry for listing."""

    cache_key: str
    tier: str
    model: str
    hit_count: int = 0
    size_bytes: int = 0
    created_at: str = ""
    expires_at: str = ""
    namespace: str | None = None


class CacheEntriesResponse(BaseModel):
    """Paginated cache entries."""

    entries: list[CacheEntryItem] = Field(default_factory=list)
    total: int = 0
    offset: int = 0
    limit: int = 50


class CacheNamespaceItem(BaseModel):
    """A namespace bucket with stats."""

    namespace_key: str
    entry_count: int = 0
    hit_rate: float = 0.0


class CacheNamespacesResponse(BaseModel):
    """List of all namespace buckets."""

    namespaces: list[CacheNamespaceItem] = Field(default_factory=list)
    total: int = 0


class CacheInvalidateRequest(BaseModel):
    """Request to invalidate cache entries."""

    cache_keys: list[str] = Field(default_factory=list)
    namespace: str | None = None
    tier: str | None = None
    model: str | None = None
    clear_all: bool = False


class CacheInvalidateResponse(BaseModel):
    """Result of cache invalidation."""

    removed: int = 0
    tier: str | None = None


# ---------------------------------------------------------------------------
# Pydantic v2 model rebuild for deferred annotations
# ---------------------------------------------------------------------------
# When using `from __future__ import annotations`, forward references like
# `datetime` (in TYPE_CHECKING block) are not resolved until .model_rebuild()
# is called. This ensures FastAPI's /openapi.json generation works correctly.

RequestLogItem.model_rebuild()
RequestLogResponse.model_rebuild()
RequestDetailResponse.model_rebuild()
