#!/usr/bin/env python3
"""exp15 preflight — live /models listing fact-check (two-directions rule, $0).

Lists /models for every vendor whose key exists and flags the arm/judge/miner
slugs exp15 intends to use. LISTING ONLY — no generation, no billing. The alias
caveat still holds (00-provider-quirks: "not in /models" != "doesn't work"), so a
missing slug here is a flag to probe live, not a verdict.

Never prints keys. Persists raw listing to data for provenance (eval rule 1).
Run: eval/.venv/bin/python eval/exp15/preflight_models.py
"""
import json
import os
import sys
import time
from pathlib import Path

import requests
from dotenv import load_dotenv

# eval/.env holds the keys; scripts read it themselves (never printed).
EVAL_DIR = Path(__file__).resolve().parent.parent
load_dotenv(EVAL_DIR / ".env")

OUT_DIR = Path("/home/ubuntu/books/gu-zhenren/exp15")
OUT_DIR.mkdir(parents=True, exist_ok=True)
RAW = OUT_DIR / "preflight_models.json"

# (name, base_url, env_key, [target substrings to flag])
PROVIDERS = [
    ("deepseek", "https://api.deepseek.com/v1", "DEEPSEEK_API_KEY",
     ["deepseek-v4-flash", "deepseek-v4-pro", "deepseek-chat", "deepseek-reasoner"]),
    ("zai", "https://api.z.ai/api/paas/v4", "ZAI_API_KEY",
     ["glm-5", "glm-5.1", "glm-5.2", "glm-4.7", "glm-4.6", "glm-4.5-air"]),
    ("xai", "https://api.x.ai/v1", "XAI_API_KEY",
     ["grok-4.3", "grok-4"]),
    ("gemini", "https://generativelanguage.googleapis.com/v1beta/openai", "GEMINI_API_KEY",
     ["gemini-3.1-pro", "gemini-3.5", "gemini-3.1-flash", "gemini-2.5"]),
    ("openai", "https://api.openai.com/v1", "OPENAI_API_KEY",
     ["gpt-5.4", "gpt-5-mini", "gpt-5"]),
    ("mistral", "https://api.mistral.ai/v1", "MISTRAL_API_KEY",
     ["mistral-large", "mistral-medium", "mistral-small"]),
    ("kimi", "https://api.moonshot.ai/v1", "KIMI_API_KEY",
     ["kimi-k2.6", "kimi-k2.5", "kimi-k2.7", "kimi-k2"]),
]


def list_models(name, base, env_key):
    key = os.environ.get(env_key)
    if not key:
        return {"provider": name, "error": f"no key in env ({env_key})"}
    url = base.rstrip("/") + "/models"
    try:
        r = requests.get(url, headers={"Authorization": f"Bearer {key}"}, timeout=40)
    except Exception as e:  # noqa: BLE001
        return {"provider": name, "error": f"request failed: {type(e).__name__}: {e}"}
    out = {"provider": name, "status": r.status_code, "url": url}
    if r.status_code != 200:
        out["body_snippet"] = r.text[:300]
        return out
    try:
        data = r.json()
    except Exception:  # noqa: BLE001
        out["error"] = "non-json body"
        out["body_snippet"] = r.text[:300]
        return out
    items = data.get("data", data if isinstance(data, list) else [])
    ids = sorted({(m.get("id") or m.get("name") or "") for m in items if isinstance(m, dict)})
    out["count"] = len(ids)
    out["model_ids"] = ids
    return out


def main():
    results = []
    for name, base, env_key, targets in PROVIDERS:
        res = list_models(name, base, env_key)
        res["targets"] = targets
        if "model_ids" in res:
            ids = res["model_ids"]
            res["target_hits"] = {t: [m for m in ids if t in m] for t in targets}
        results.append(res)
        time.sleep(0.3)

    RAW.write_text(json.dumps(results, ensure_ascii=False, indent=2), encoding="utf-8")

    print(f"# exp15 preflight /models  (raw -> {RAW})\n")
    for res in results:
        name = res["provider"]
        if "error" in res:
            print(f"[{name}] ERROR: {res['error']}")
            continue
        if res.get("status") != 200:
            print(f"[{name}] HTTP {res['status']}: {res.get('body_snippet','')[:120]}")
            continue
        print(f"[{name}] HTTP 200, {res['count']} models")
        for t, hits in res["target_hits"].items():
            mark = "OK " if hits else "-- "
            print(f"    {mark}{t:<22} -> {hits if hits else 'NOT in /models (probe live before verdict)'}")
    print("\nNOTE: alias caveat (00-provider-quirks calendar): a slug absent here may still serve (HTTP 200).")
    print("Verdicts on slugs require BOTH this listing AND the vendor-doc workflow, per two-directions rule.")


if __name__ == "__main__":
    sys.exit(main())
