"""
models_client.py — drop-in BESTIARY catalog reader for any PlinyOS app.

Copy this single file into any repo that needs the model list. It reads the
catalog BESTIARY produces and gives you filtered views, with zero third-party
deps. No need to import the full bestiary engine.

Resolution order for the catalog:
  1. $BESTIARY_CATALOG (explicit path or http(s) URL)
  2. a local catalog.json next to this file
  3. the canonical monorepo path (…/03-PLINYOS/organs/bestiary/state/catalog.json)

Usage
─────
    from models_client import models, model_ids, newest

    # frontier / hosted apps (godmode, tempest, libertarium):
    api_models = models(channel="api")                 # OpenRouter-reachable
    ids = model_ids(channel="api", vendor="anthropic") # ["anthropic/claude-opus-4.8", …]

    # open-weight apps (obliteratus, incanta, pandora):
    weights = models(open_weight=True)                 # downloadable, with hf_id
    coders  = models(open_weight=True, capability="tools")

    # newest drops across everything:
    for m in newest(days=7):
        print(m["id"], m["released"])
"""

from __future__ import annotations

import json
import os
import urllib.request
from datetime import datetime, timezone, timedelta
from pathlib import Path
from typing import List, Optional

_HERE = Path(__file__).resolve().parent
_CANONICAL = _HERE.parent / "bestiary" / "state" / "catalog.json"
_LOCAL = _HERE / "catalog.json"
_CHANNEL_ALIASES = {"api": "openrouter", "hosted": "openrouter",
                    "frontier": "openrouter", "hf": "huggingface"}


def _load() -> dict:
    src = os.environ.get("BESTIARY_CATALOG", "").strip()
    if src.startswith("http://") or src.startswith("https://"):
        with urllib.request.urlopen(src, timeout=15) as r:
            return json.loads(r.read().decode("utf-8"))
    for p in (Path(src) if src else None, _LOCAL, _CANONICAL, _HERE / "state" / "catalog.json"):
        if p and p.exists():
            return json.loads(p.read_text())
    raise FileNotFoundError(
        "BESTIARY catalog not found. Run `python3 bestiary.py update`, or set "
        "$BESTIARY_CATALOG to a catalog.json path or URL."
    )


def models(channel: Optional[str] = None, vendor: Optional[str] = None,
           open_weight: Optional[bool] = None, capability: Optional[str] = None) -> List[dict]:
    """Return catalog records filtered by channel / vendor / open_weight / capability."""
    if channel in ("open-weight", "openweight", "weights"):
        open_weight, channel = True, None
    if channel:
        channel = _CHANNEL_ALIASES.get(channel, channel)
    out = []
    for m in _load().get("models", []):
        if channel and channel not in m.get("channels", []):
            continue
        if vendor and m.get("vendor") != vendor.lower():
            continue
        if open_weight is not None and bool(m.get("open_weight")) != open_weight:
            continue
        if capability and capability not in m.get("capabilities", []):
            continue
        out.append(m)
    return out


def model_ids(**kw) -> List[str]:
    """Just the ids — the common case for populating a dropdown or a config."""
    return [m["id"] for m in models(**kw)]


def newest(days: int = 7, **kw) -> List[dict]:
    """Models first-seen (≈ released) within the last `days`, newest first."""
    cutoff = datetime.now(timezone.utc) - timedelta(days=days)
    out = []
    for m in models(**kw):
        fs = m.get("first_seen") or m.get("released")
        if not fs:
            continue
        try:
            t = datetime.strptime(fs, "%Y-%m-%d").replace(tzinfo=timezone.utc)
        except ValueError:
            continue
        if t >= cutoff:
            out.append(m)
    return sorted(out, key=lambda r: r.get("first_seen") or "", reverse=True)


if __name__ == "__main__":
    cat = _load()
    print(f"catalog generated_at={cat.get('generated_at')} count={cat.get('count')}")
    print(f"  api models      : {len(models(channel='api'))}")
    print(f"  open-weight     : {len(models(open_weight=True))}")
    print(f"  new (7d)        : {len(newest(7))}")
