"""LLM provider abstraction for Hive agents.

Supports: Ollama (local), OpenAI, OpenRouter, Anthropic.
Each provider implements the same interface for hot-swapping.
"""

from __future__ import annotations

import logging
import os
from abc import ABC, abstractmethod
from dataclasses import dataclass, field
from typing import Any

logger = logging.getLogger("hive.models")


@dataclass
class CompletionResult:
    """Result from an LLM completion request."""

    content: str
    model: str
    provider: str
    input_tokens: int = 0
    output_tokens: int = 0
    latency_ms: int = 0
    cost_usd: float = 0.0


@dataclass
class ModelInfo:
    """Information about an available model."""

    name: str
    provider: str
    context_length: int = 0
    supports_tools: bool = False


class LLMProvider(ABC):
    """Abstract base for all LLM providers."""

    provider_name: str

    @abstractmethod
    async def complete(
        self,
        messages: list[dict[str, str]],
        model: str,
        temperature: float = 0.3,
        max_tokens: int = 4096,
    ) -> CompletionResult:
        """Send a completion request."""
        ...

    @abstractmethod
    async def is_available(self) -> bool:
        """Check if provider is reachable and authenticated."""
        ...

    @abstractmethod
    async def list_models(self) -> list[ModelInfo]:
        """List available models."""
        ...

    def estimate_cost(self, input_tokens: int, output_tokens: int, model: str) -> float:
        """Estimate cost in USD. Override for cloud providers."""
        return 0.0


class OllamaProvider(LLMProvider):
    """Ollama local model provider."""

    provider_name = "ollama"

    def __init__(self, base_url: str = "http://localhost:11434") -> None:
        self.base_url = base_url

    async def complete(
        self,
        messages: list[dict[str, str]],
        model: str,
        temperature: float = 0.3,
        max_tokens: int = 4096,
    ) -> CompletionResult:
        """Send completion to Ollama."""
        # Placeholder: full implementation uses httpx to call Ollama API
        return CompletionResult(
            content="[Ollama completion placeholder]",
            model=model,
            provider="ollama",
        )

    async def is_available(self) -> bool:
        """Check if Ollama is running."""
        try:
            import httpx

            async with httpx.AsyncClient(timeout=3.0) as client:
                response = await client.get(f"{self.base_url}/api/tags")
                return response.status_code == 200
        except Exception:
            return False

    async def list_models(self) -> list[ModelInfo]:
        """List models available in Ollama."""
        try:
            import httpx

            async with httpx.AsyncClient(timeout=5.0) as client:
                response = await client.get(f"{self.base_url}/api/tags")
                if response.status_code == 200:
                    data = response.json()
                    return [ModelInfo(name=m["name"], provider="ollama") for m in data.get("models", [])]
        except Exception:
            pass
        return []


# Provider registry
PROVIDER_REGISTRY: dict[str, type[LLMProvider]] = {
    "ollama": OllamaProvider,
}


@dataclass
class ModelUsageEntry:
    """Track model usage for cost reporting."""

    timestamp: str
    agent: str
    task_type: str
    model: str
    provider: str
    input_tokens: int
    output_tokens: int
    latency_ms: int
    estimated_cost_usd: float
    success: bool


@dataclass
class ProviderManager:
    """Manages multiple LLM providers and model selection."""

    providers: dict[str, LLMProvider] = field(default_factory=dict)
    model_assignments: dict[str, str] = field(default_factory=dict)
    usage_log: list[ModelUsageEntry] = field(default_factory=list)

    async def auto_discover(self) -> dict[str, Any]:
        """Auto-discover available providers."""
        discovered: dict[str, Any] = {}

        # Check Ollama
        ollama = OllamaProvider()
        if await ollama.is_available():
            models = await ollama.list_models()
            discovered["ollama"] = {
                "available": True,
                "models": [m.name for m in models],
            }
            self.providers["ollama"] = ollama
        else:
            discovered["ollama"] = {"available": False}

        # Check cloud provider API keys
        for provider, env_var in [
            ("openai", "OPENAI_API_KEY"),
            ("anthropic", "ANTHROPIC_API_KEY"),
            ("openrouter", "OPENROUTER_API_KEY"),
        ]:
            has_key = bool(os.getenv(env_var))
            discovered[provider] = {"available": has_key, "env_var": env_var}

        return discovered

    def select_model(self, agent: str, task_type: str | None = None) -> str | None:
        """Select the best model for an agent task."""
        # Check task-specific override
        if task_type and task_type in self.model_assignments:
            return self.model_assignments[task_type]

        # Use agent default
        if agent in self.model_assignments:
            return self.model_assignments[agent]

        # Fallback
        return self.model_assignments.get("fallback")
