#!/usr/bin/env python3
from __future__ import annotations

import json
import sys
from typing import Any
from urllib.parse import urlparse
from urllib.request import urlopen
from datetime import datetime


def load_openapi(source: str) -> dict:
    """Load openapi.json from file path or URL"""
    parsed = urlparse(source)
    if parsed.scheme in ("http", "https"):
        with urlopen(source) as resp:
            return json.load(resp)
    else:
        with open(source, "r", encoding="utf-8") as f:
            return json.load(f)


def py_type(schema: dict) -> str:
    """OpenAPI schema -> Python type string"""
    if not schema:
        return "Any"
    
    if not isinstance(schema, dict):
        return "Any"

    if "$ref" in schema:
        return schema["$ref"].split("/")[-1]

    if "anyOf" in schema:
        types = []
        nullable = False
        for s in schema["anyOf"]:
            if s.get("type") == "null":
                nullable = True
            else:
                types.append(py_type(s))
        t = " | ".join(sorted(set(types))) or "Any"
        return f"{t} | None" if nullable else t

    t = schema.get("type")

    if t == "string":
        if schema.get("format") == "date-time":
            return "datetime"
        return "str"

    if t == "integer":
        return "int"

    if t == "number":
        return "float"

    if t == "boolean":
        return "bool"

    if t == "array":
        return f"list[{py_type(schema.get('items', {}))}]"

    if t == "object":
        if "additionalProperties" in schema:
            additional_props = schema["additionalProperties"]
            if isinstance(additional_props, bool):
                return "dict[str, Any]"
            return f"dict[str, {py_type(additional_props)}]"
        return "dict[str, Any]"

    return "Any"


def gen_model(name: str, schema: dict) -> str:
    required = set(schema.get("required", []))
    props = schema.get("properties", {})

    lines = [f"class {name}(BaseModel):"]

    if not props:
        lines.append("    pass")
        return "\n".join(lines)

    for prop, pschema in props.items():
        t = py_type(pschema)
        default = "" if prop in required else " = None"
        lines.append(f"    {prop}: {t}{default}")

    return "\n".join(lines)


def main():
    if len(sys.argv) != 2:
        print("Usage: python gen_models.py <openapi.json | url>", file=sys.stderr)
        sys.exit(1)

    spec = load_openapi(sys.argv[1])
    schemas = spec.get("components", {}).get("schemas", {})

    print("from __future__ import annotations")
    print("from pydantic import BaseModel")
    print("from typing import Any")
    print("from datetime import datetime")
    print()

    for name, schema in schemas.items():
        print(gen_model(name, schema))
        print()


if __name__ == "__main__":
    main()