"""Shared provider configuration for eval and benchmark scripts.

Centralises API key loading, endpoint URLs, model presets, and client
construction so that every eval/benchmark script imports from here instead
of hardcoding its own provider plumbing.

Usage::

    from cortex.eval.providers import (
        get_chat_config,
        get_embed_config,
        get_available_providers,
        resolve_chat_provider,
        resolve_embed_provider,
        PRESETS,
        ENDPOINTS,
    )

    # Quick check
    print(get_available_providers())

    # Get the best available chat config (Google-first)
    cfg = resolve_chat_provider()
    # cfg = {"url": "https://...", "api_key": "sk-...", "provider": "google"}

    # Presets for common model combos
    preset = PRESETS["gemini-fast"]
    # {"chat_model": "gemini-2.5-flash", "embed_model": "gemini-embedding-2-preview", ...}
"""

from __future__ import annotations

import json
import os
from pathlib import Path
from typing import Optional, Any

# ---------------------------------------------------------------------------
# Endpoint URLs (constants — no secrets)
# ---------------------------------------------------------------------------

ENDPOINTS = {
    "google_chat": "https://generativelanguage.googleapis.com/v1beta/openai/chat/completions",
    "google_embed": "https://generativelanguage.googleapis.com/v1beta/openai/embeddings",
    "google_batch_embed": "https://generativelanguage.googleapis.com/v1beta/models/{model}:batchEmbedContents",
    "voyage_embed": "https://api.voyageai.com/v1/embeddings",
    "openrouter_chat": "https://openrouter.ai/api/v1/chat/completions",
    "venice_chat": "https://api.venice.ai/api/v1/chat/completions",
    "bailian_chat": "https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions",
}

# ---------------------------------------------------------------------------
# Model presets (named configs for common eval scenarios)
# ---------------------------------------------------------------------------

PRESETS: dict[str, dict[str, str]] = {
    "gemini-fast": {
        "chat_model": "gemini-2.5-flash",
        "embed_model": "gemini-embedding-2-preview",
        "chat_provider": "google",
        "embed_provider": "google",
        "description": "Fast Gemini generation + Gemini embeddings",
    },
    "gemini-best": {
        "chat_model": "gemini-3-flash-preview",
        "embed_model": "gemini-embedding-2-preview",
        "chat_provider": "google",
        "embed_provider": "google",
        "description": "Best reasoning (Gemini 3 Flash Preview) + Gemini embeddings",
    },
    "gemini-voyage": {
        "chat_model": "gemini-2.5-flash",
        "embed_model": "voyage-3-large",
        "chat_provider": "google",
        "embed_provider": "voyage",
        "description": "Gemini chat + Voyage embeddings (highest quality embeds)",
    },
    "openrouter-voyage": {
        "chat_model": "google/gemini-2.5-flash",
        "embed_model": "voyage-3-large",
        "chat_provider": "openrouter",
        "embed_provider": "voyage",
        "description": "OpenRouter chat fallback + Voyage embeddings",
    },
}

# ---------------------------------------------------------------------------
# Key loading — env vars only (+ optional auth.json snapshot for compat)
# ---------------------------------------------------------------------------

def _load_auth_snapshot() -> dict[str, Any]:
    """Load keys from legacy auth.json snapshot if it exists."""
    auth_path = (
        Path.home()
        / ".openclaw"
        / "workspace"
        / "core"
        / "system-snapshot"
        / "agents"
        / "main"
        / "auth.json"
    )
    if auth_path.exists():
        try:
            with open(auth_path) as f:
                return json.load(f)
        except (json.JSONDecodeError, OSError):
            pass
    return {}


_AUTH_CACHE: Optional[dict[str, Any]] = None


def _get_auth() -> dict[str, Any]:
    global _AUTH_CACHE
    if _AUTH_CACHE is None:
        _AUTH_CACHE = _load_auth_snapshot()
    return _AUTH_CACHE


def get_api_key(provider: str) -> str:
    """Get API key for a provider, checking env vars then auth.json fallback.

    Supported providers: google, voyage, openrouter, anthropic, venice, bailian
    """
    auth = _get_auth()

    env_map: dict[str, list[str]] = {
        "google": ["GOOGLE_API_KEY", "GEMINI_API_KEY"],
        "voyage": ["VOYAGE_API_KEY"],
        "openrouter": ["OPENROUTER_API_KEY"],
        "anthropic": ["ANTHROPIC_API_KEY"],
        "venice": ["VENICE_API_KEY"],
        "bailian": ["BAILIAN_API_KEY", "DASHSCOPE_API_KEY"],
    }

    # Try environment variables first
    for env_var in env_map.get(provider, []):
        val = os.environ.get(env_var, "")
        if val:
            return val

    # Fallback to auth.json snapshot
    provider_data = auth.get(provider, {})
    if isinstance(provider_data, dict):
        key = provider_data.get("key", "")
        if key:
            return key

    return ""


# ---------------------------------------------------------------------------
# Provider availability
# ---------------------------------------------------------------------------

_CHAT_PROVIDERS = ["google", "openrouter", "venice", "anthropic", "bailian"]
_EMBED_PROVIDERS = ["voyage", "google"]


def get_available_providers() -> dict[str, list[str]]:
    """Return dict of available chat and embed providers (those with valid keys)."""
    chat = [p for p in _CHAT_PROVIDERS if get_api_key(p)]
    embed = [p for p in _EMBED_PROVIDERS if get_api_key(p)]
    return {"chat": chat, "embed": embed}


def print_provider_status() -> None:
    """Print which providers are available (call at script startup)."""
    avail = get_available_providers()
    chat_str = ", ".join(avail["chat"]) if avail["chat"] else "NONE"
    embed_str = ", ".join(avail["embed"]) if avail["embed"] else "NONE"
    print(f"🔑 Chat providers: {chat_str}")
    print(f"🔑 Embed providers: {embed_str}")
    if not avail["chat"]:
        print("⚠️  No chat providers available — set GOOGLE_API_KEY or OPENROUTER_API_KEY")
    if not avail["embed"]:
        print("⚠️  No embed providers available — set VOYAGE_API_KEY or GOOGLE_API_KEY")


# ---------------------------------------------------------------------------
# Provider resolution (chat)
# ---------------------------------------------------------------------------

def resolve_chat_provider(
    preferred: Optional[str] = None,
    fallback_chain: Optional[list[str]] = None,
) -> dict[str, str]:
    """Resolve the best available chat provider.

    Returns dict with keys: provider, url, api_key

    Args:
        preferred: Explicit provider name to try first
        fallback_chain: Custom fallback order. Default: google -> openrouter -> venice -> anthropic

    Raises:
        RuntimeError: If no chat provider is available
    """
    chain = fallback_chain or (
        [preferred] + [p for p in _CHAT_PROVIDERS if p != preferred]
        if preferred
        else _CHAT_PROVIDERS
    )

    url_map = {
        "google": ENDPOINTS["google_chat"],
        "openrouter": ENDPOINTS["openrouter_chat"],
        "venice": ENDPOINTS["venice_chat"],
        "bailian": ENDPOINTS["bailian_chat"],
        "anthropic": "https://api.anthropic.com/v1/messages",
    }

    for provider in chain:
        key = get_api_key(provider)
        if key:
            return {
                "provider": provider,
                "url": url_map.get(provider, ""),
                "api_key": key,
            }

    raise RuntimeError(
        "No chat provider available. Set one of: "
        "GOOGLE_API_KEY, OPENROUTER_API_KEY, VENICE_API_KEY, ANTHROPIC_API_KEY"
    )


def resolve_embed_provider(
    preferred: Optional[str] = None,
) -> dict[str, str]:
    """Resolve the best available embedding provider.

    Returns dict with keys: provider, url, api_key

    Args:
        preferred: Explicit provider name to try first (voyage or google)

    Raises:
        RuntimeError: If no embed provider is available
    """
    chain = (
        [preferred] + [p for p in _EMBED_PROVIDERS if p != preferred]
        if preferred
        else _EMBED_PROVIDERS
    )

    url_map = {
        "voyage": ENDPOINTS["voyage_embed"],
        "google": ENDPOINTS["google_embed"],
    }

    for provider in chain:
        key = get_api_key(provider)
        if key:
            return {
                "provider": provider,
                "url": url_map.get(provider, ""),
                "api_key": key,
            }

    raise RuntimeError(
        "No embed provider available. Set one of: VOYAGE_API_KEY, GOOGLE_API_KEY"
    )


# ---------------------------------------------------------------------------
# Config helpers (for httpx-based scripts)
# ---------------------------------------------------------------------------

def chat_headers(provider: Optional[str] = None, api_key: Optional[str] = None) -> dict[str, str]:
    """Return HTTP headers for a chat API call."""
    if api_key is None:
        cfg = resolve_chat_provider(preferred=provider)
        api_key = cfg["api_key"]
    return {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
    }


def embed_headers(provider: Optional[str] = None, api_key: Optional[str] = None) -> dict[str, str]:
    """Return HTTP headers for an embedding API call."""
    if api_key is None:
        cfg = resolve_embed_provider(preferred=provider)
        api_key = cfg["api_key"]
    return {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
    }


def google_batch_embed_url(model: str) -> str:
    """Build Google batch embed URL with API key (uses ?key= auth)."""
    key = get_api_key("google")
    return f"{ENDPOINTS['google_batch_embed'].format(model=model)}?key={key}"
