"""
Cache modeli payperq.

`/v1/models` na api.ppq.ai zwraca pełen katalog (~350 modeli, wszystkie
type=chat). Cache 5 min — lista zmienia się rzadko, a frontend pokazuje ją
tylko w dropdown przy zmianie modelu.

Filtrujemy do modeli, które:
  * mają `text` w output_modalities,
  * wspierają `tools` w supported_parameters (bez tego JOU agent się sypie
    w pierwszej rundzie — wszystkie wywołania narzędzi są tool_use'ami).

Zwracamy zredukowaną reprezentację: {id, name, owned_by, popular, pricing}.
Frontend dalej sortuje/grupuje.
"""

from __future__ import annotations

import asyncio
import logging
import time
from typing import Any

import httpx

logger = logging.getLogger("mcp_nostr_explorer.models_catalog")

PPQ_MODELS_URL = "https://api.ppq.ai/v1/models"
_CACHE_TTL_S = 5 * 60.0
_FETCH_TIMEOUT_S = 10.0

_lock = asyncio.Lock()
_cache: list[dict[str, Any]] | None = None
_cache_at: float = 0.0


def _zredukuj(item: dict) -> dict:
    pricing = item.get("pricing") or {}
    id_ = item.get("id", "")
    owner_norm = (item.get("owned_by") or "").lower().lstrip("~")
    # PPQ /v1/models zwraca id-y Anthropic z kropkami w wersji (claude-opus-4.7),
    # ale Anthropic SDK / Messages API używa myślników (claude-opus-4-7) — i to
    # jest format który adapter wysyła w `messages.create(model=...)`. Żeby
    # dropdown pokazywał format faktycznie używany przez agenta, zamieniamy
    # kropki na myślniki dla modeli Anthropic-owned. Display name (`name`)
    # zostawiamy nienaruszony — tam kropki są bardziej czytelne ("Claude Opus 4.7").
    if owner_norm == "anthropic":
        id_ = id_.replace(".", "-")
    return {
        "id": id_,
        "name": item.get("name") or item.get("id", ""),
        "owned_by": item.get("owned_by", ""),
        "popular": bool(item.get("popular")),
        "pricing": {
            "input_per_1M": pricing.get("input_per_1M_tokens"),
            "output_per_1M": pricing.get("output_per_1M_tokens"),
            "currency": pricing.get("currency", "USD"),
        },
    }


def _wspiera_tools(item: dict) -> bool:
    sp = item.get("supported_parameters") or []
    return "tools" in sp


def _zwraca_text(item: dict) -> bool:
    arch = item.get("architecture") or {}
    out = arch.get("output_modalities") or []
    return "text" in out


def reset_cache() -> None:
    """Do testów — wyrzuca cache, następne wywołanie znów uderzy w API."""
    global _cache, _cache_at
    _cache = None
    _cache_at = 0.0


async def _pobierz(api_key: str | None) -> list[dict]:
    headers = {}
    if api_key:
        headers["Authorization"] = f"Bearer {api_key}"
    async with httpx.AsyncClient(timeout=_FETCH_TIMEOUT_S, headers=headers) as cli:
        r = await cli.get(PPQ_MODELS_URL)
        r.raise_for_status()
        body = r.json()
    items = body.get("data") if isinstance(body, dict) else body
    if not isinstance(items, list):
        raise ValueError(f"PPQ /v1/models: niespodziewany format ({type(body).__name__})")
    return items


async def fetch_chat_models(api_key: str | None = None) -> list[dict]:
    """Zwraca listę modeli (zredukowane recordy). Cache 5 min."""
    global _cache, _cache_at
    now = time.monotonic()
    if _cache is not None and (now - _cache_at) < _CACHE_TTL_S:
        return _cache
    async with _lock:
        now = time.monotonic()
        if _cache is not None and (now - _cache_at) < _CACHE_TTL_S:
            return _cache
        try:
            raw = await _pobierz(api_key)
        except Exception as e:
            if _cache is not None:
                logger.warning(
                    f"PPQ models niedostępne ({type(e).__name__}: {e}) — "
                    "zwracam stary cache"
                )
                return _cache
            raise
        filtered = [
            _zredukuj(it)
            for it in raw
            if isinstance(it, dict) and _wspiera_tools(it) and _zwraca_text(it)
        ]
        # Stabilny porządek: popularne najpierw, dalej alfabetycznie po nazwie.
        filtered.sort(key=lambda x: (not x["popular"], x["owned_by"], x["name"]))
        _cache = filtered
        _cache_at = time.monotonic()
        return filtered
