"""
OpenRouter model metadata caching and lookup.

This module keeps a local cached copy of the OpenRouter model list
(downloaded from ``https://openrouter.ai/api/v1/models``) and exposes a
helper class that returns metadata for a given model in a format compatible
with litellm’s ``get_model_info``.
"""
from __future__ import annotations

import json
import time
from pathlib import Path
from typing import Dict

import requests


def _cost_per_token(val: str | None) -> float | None:
    """Convert a price string (USD per token) to a float."""
    if val in (None, "", "0"):
        return 0.0 if val == "0" else None
    try:
        return float(val)
    except Exception:  # noqa: BLE001
        return None


class OpenRouterModelManager:
    MODELS_URL = "https://openrouter.ai/api/v1/models"
    CACHE_TTL = 60 * 60 * 24  # 24 h

    def __init__(self) -> None:
        self.cache_dir = Path.home() / ".aider" / "caches"
        self.cache_file = self.cache_dir / "openrouter_models.json"
        self.content: Dict | None = None
        self.verify_ssl: bool = True
        self._cache_loaded = False

    # ------------------------------------------------------------------ #
    # Public API                                                         #
    # ------------------------------------------------------------------ #
    def set_verify_ssl(self, verify_ssl: bool) -> None:
        """Enable/disable SSL verification for API requests."""
        self.verify_ssl = verify_ssl

    def get_model_info(self, model: str) -> Dict:
        """
        Return metadata for *model* or an empty ``dict`` when unknown.

        ``model`` should use the aider naming convention, e.g.
        ``openrouter/nousresearch/deephermes-3-mistral-24b-preview:free``.
        """
        self._ensure_content()
        if not self.content or "data" not in self.content:
            return {}

        route = self._strip_prefix(model)

        # Consider both the exact id and id without any “:suffix”.
        candidates = {route}
        if ":" in route:
            candidates.add(route.split(":", 1)[0])

        record = next((item for item in self.content["data"] if item.get("id") in candidates), None)
        if not record:
            return {}

        context_len = (
            record.get("top_provider", {}).get("context_length")
            or record.get("context_length")
            or None
        )

        pricing = record.get("pricing", {})
        return {
            "max_input_tokens": context_len,
            "max_tokens": context_len,
            "max_output_tokens": context_len,
            "input_cost_per_token": _cost_per_token(pricing.get("prompt")),
            "output_cost_per_token": _cost_per_token(pricing.get("completion")),
            "litellm_provider": "openrouter",
        }

    # ------------------------------------------------------------------ #
    # Internal helpers                                                   #
    # ------------------------------------------------------------------ #
    def _strip_prefix(self, model: str) -> str:
        return model[len("openrouter/") :] if model.startswith("openrouter/") else model

    def _ensure_content(self) -> None:
        self._load_cache()
        if not self.content:
            self._update_cache()

    def _load_cache(self) -> None:
        if self._cache_loaded:
            return
        try:
            self.cache_dir.mkdir(parents=True, exist_ok=True)
            if self.cache_file.exists():
                cache_age = time.time() - self.cache_file.stat().st_mtime
                if cache_age < self.CACHE_TTL:
                    try:
                        self.content = json.loads(self.cache_file.read_text())
                    except json.JSONDecodeError:
                        self.content = None
        except OSError:
            # Cache directory might be unwritable; ignore.
            pass

        self._cache_loaded = True

    def _update_cache(self) -> None:
        try:
            response = requests.get(self.MODELS_URL, timeout=10, verify=self.verify_ssl)
            if response.status_code == 200:
                self.content = response.json()
                try:
                    self.cache_file.write_text(json.dumps(self.content, indent=2))
                except OSError:
                    pass  # Non-fatal if we can’t write the cache
        except Exception as ex:  # noqa: BLE001
            print(f"Failed to fetch OpenRouter model list: {ex}")
            try:
                self.cache_file.write_text("{}")
            except OSError:
                pass
