#!/usr/bin/env python3
"""Fetch model metadata from Charm Hyper (or Hyper Staging).

Hyper's `/v1/models` endpoint returns all required fields in a single call,
no authentication required.

Usage:
    hyper.py [--staging] [--json | --tsv]
"""

from __future__ import annotations

import argparse
import json
import sys
import urllib.error
import urllib.request
from typing import Any, TypedDict

from models import Model, clean_name, emit_json, emit_tsv

PROD_URL = "https://hyper.charm.land/v1/models"
STAGING_URL = "https://hyper.charmcli.dev/v1/models"


class HyperModel(TypedDict):
    id: str
    display_name: str
    context_window: int
    max_output_tokens: int
    supports_reasoning: bool
    supports_attachments: bool


def _http_get_json(url: str) -> dict[str, Any]:
    """Perform a GET request and return the parsed JSON body."""
    req = urllib.request.Request(url)
    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            body = resp.read().decode("utf-8")
    except urllib.error.HTTPError as e:
        sys.exit(f"HTTP {e.code} fetching {url}: {e.read().decode('utf-8', 'replace')}")
    except urllib.error.URLError as e:
        sys.exit(f"Network error fetching {url}: {e.reason}")
    return json.loads(body)


def _build_model(raw: HyperModel) -> Model:
    return Model(
        id=raw["id"],
        name=clean_name(raw["display_name"]),
        context_length=raw["context_window"],
        max_completion_tokens=raw["max_output_tokens"],
        supports_reasoning=raw["supports_reasoning"],
        supports_image=raw["supports_attachments"],
    )


def fetch_models(staging: bool = False) -> list[Model]:
    url = STAGING_URL if staging else PROD_URL
    payload = _http_get_json(url)
    data = payload.get("data") or []
    return [_build_model(d) for d in data]


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "--staging",
        action="store_true",
        help="Fetch from Hyper Staging (hyper.charmcli.dev) instead of Hyper.",
    )
    fmt = parser.add_mutually_exclusive_group()
    fmt.add_argument("--tsv", action="store_const", dest="format", const="tsv")
    fmt.add_argument("--json", action="store_const", dest="format", const="json")
    parser.set_defaults(format="tsv")
    args = parser.parse_args()

    models = fetch_models(staging=args.staging)

    if args.format == "json":
        emit_json(models, sys.stdout)
    else:
        emit_tsv(models, sys.stdout)


if __name__ == "__main__":
    main()
