"""Data models for the Cascade LLM Router.

Enums, dataclasses, and configuration objects shared across router modules.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from enum import Enum


class ProviderStatus(Enum):
    """Health status of a provider."""

    HEALTHY = "healthy"
    DEGRADED = "degraded"  # Working but slow or occasional errors
    UNHEALTHY = "unhealthy"  # Circuit breaker open
    DISABLED = "disabled"


class CircuitState(Enum):
    """Circuit breaker state."""

    CLOSED = "closed"  # Normal operation
    OPEN = "open"  # Failing, rejecting requests
    HALF_OPEN = "half_open"  # Testing if recovered


class ContentType(Enum):
    """Type of content in the request."""

    TEXT = "text"
    VISION = "vision"  # Contains images
    AUDIO = "audio"  # Contains audio
    MULTIMODAL = "multimodal"  # Multiple content types


@dataclass
class ProviderMetrics:
    """Metrics for a single provider."""

    total_requests: int = 0
    successful_requests: int = 0
    failed_requests: int = 0
    total_latency_ms: float = 0.0
    last_request_time: str | None = None
    last_error_time: str | None = None
    consecutive_failures: int = 0

    @property
    def avg_latency_ms(self) -> float:
        if self.total_requests == 0:
            return 0.0
        return self.total_latency_ms / self.total_requests

    @property
    def error_rate(self) -> float:
        if self.total_requests == 0:
            return 0.0
        return self.failed_requests / self.total_requests


@dataclass
class ModelCapability:
    """Capabilities a model supports."""

    name: str
    supports_vision: bool = False
    supports_audio: bool = False
    supports_tools: bool = False
    supports_json: bool = False
    supports_streaming: bool = True
    context_window: int = 4096


@dataclass
class Provider:
    """LLM provider configuration and state."""

    name: str
    type: str  # ollama, openai, anthropic
    enabled: bool
    priority: int
    tier: str | None = None  # e.g., "local", "standard_cloud", "frontier"
    url: str | None = None
    api_key: str | None = None
    base_url: str | None = None
    models: list[dict] = field(default_factory=list)

    # Runtime state
    status: ProviderStatus = ProviderStatus.HEALTHY
    metrics: ProviderMetrics = field(default_factory=ProviderMetrics)
    circuit_state: CircuitState = CircuitState.CLOSED
    circuit_opened_at: float | None = None
    half_open_calls: int = 0

    def get_default_model(self) -> str | None:
        """Get the default model for this provider."""
        for model in self.models:
            if model.get("default"):
                return model["name"]
        if self.models:
            return self.models[0]["name"]
        return None

    def get_model_with_capability(self, capability: str) -> str | None:
        """Get a model that supports the given capability."""
        for model in self.models:
            capabilities = model.get("capabilities", [])
            if capability in capabilities:
                return model["name"]
        # Fall back to default
        return self.get_default_model()

    def model_has_capability(self, model_name: str, capability: str) -> bool:
        """Check if a specific model has a capability."""
        for model in self.models:
            if model["name"] == model_name:
                capabilities = model.get("capabilities", [])
                return capability in capabilities
        return False


@dataclass
class RouterConfig:
    """Cascade router configuration."""

    timeout_seconds: int = 30
    max_retries_per_provider: int = 2
    retry_delay_seconds: int = 1
    circuit_breaker_failure_threshold: int = 5
    circuit_breaker_recovery_timeout: int = 60
    circuit_breaker_half_open_max_calls: int = 2
    cost_tracking_enabled: bool = True
    budget_daily_usd: float = 10.0
    # Multi-modal settings
    auto_pull_models: bool = True
    fallback_chains: dict = field(default_factory=dict)
