from __future__ import annotations

import argparse
import shutil
import subprocess
from dataclasses import dataclass
from pathlib import Path


ROOT = Path(__file__).resolve().parent.parent
GENERATED_ROOT = ROOT / "ebay_client" / "generated"
CODEGEN_PYTHON = ROOT / ".venv_codegen" / "Scripts" / "python.exe"


@dataclass(frozen=True)
class ApiSpec:
    name: str
    spec_path: Path
    output_path: Path


API_SPECS = {
    "media": ApiSpec(
        name="media",
        spec_path=ROOT / "commerce_media_v1_beta_oas3.yaml",
        output_path=GENERATED_ROOT / "media",
    ),
    "notification": ApiSpec(
        name="notification",
        spec_path=ROOT / "commerce_notification_v1_oas3.yaml",
        output_path=GENERATED_ROOT / "notification",
    ),
    "inventory": ApiSpec(
        name="inventory",
        spec_path=ROOT / "sell_inventory_v1_oas3.yaml",
        output_path=GENERATED_ROOT / "inventory",
    ),
    "fulfillment": ApiSpec(
        name="fulfillment",
        spec_path=ROOT / "sell_fulfillment_v1_oas3.yaml",
        output_path=GENERATED_ROOT / "fulfillment",
    ),
    "account": ApiSpec(
        name="account",
        spec_path=ROOT / "sell_account_v1_oas3.yaml",
        output_path=GENERATED_ROOT / "account",
    ),
    "feed": ApiSpec(
        name="feed",
        spec_path=ROOT / "sell_feed_v1_oas3.yaml",
        output_path=GENERATED_ROOT / "feed",
    ),
}


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description="Generate isolated low-level clients from the eBay OpenAPI contracts.")
    parser.add_argument("--api", choices=sorted(API_SPECS), help="Generate only one API package.")
    parser.add_argument("--fail-on-warning", action="store_true", help="Fail if the generator emits warnings.")
    return parser.parse_args()


def run_generation(spec: ApiSpec, *, fail_on_warning: bool) -> None:
    if not spec.spec_path.exists():
        raise FileNotFoundError(f"OpenAPI spec not found: {spec.spec_path}")
    if not CODEGEN_PYTHON.exists():
        raise FileNotFoundError(
            "Code generation interpreter not found. Create .venv_codegen with a supported Python version first."
        )

    if spec.output_path.exists():
        shutil.rmtree(spec.output_path)
    spec.output_path.mkdir(parents=True, exist_ok=True)

    command = [
        str(CODEGEN_PYTHON),
        "-m",
        "datamodel_code_generator",
        "--input",
        str(spec.spec_path),
        "--input-file-type",
        "openapi",
        "--output",
        str(spec.output_path / "models.py"),
        "--output-model-type",
        "pydantic_v2.BaseModel",
        "--target-python-version",
        "3.11",
        "--reuse-model",
        "--use-schema-description",
        "--field-constraints",
        "--use-double-quotes",
    ]
    if fail_on_warning:
        command.append("--disable-warnings")

    subprocess.run(command, check=True, cwd=str(ROOT))
    normalize_generated_module(spec.output_path / "models.py")
    (spec.output_path / "__init__.py").write_text(
        '"""Generated Pydantic models from the OpenAPI contract."""\n\nfrom .models import *\n',
        encoding="utf-8",
    )


def normalize_generated_module(file_path: Path) -> None:
    raw_bytes = file_path.read_bytes()
    try:
        content = raw_bytes.decode("utf-8")
    except UnicodeDecodeError:
        content = raw_bytes.decode("cp1252")

    content = content.replace("\u00a0", " ")
    file_path.write_text(content, encoding="utf-8")


def main() -> int:
    args = parse_args()
    specs = [API_SPECS[args.api]] if args.api else [API_SPECS[name] for name in sorted(API_SPECS)]
    for spec in specs:
        print(f"Generating {spec.name} from {spec.spec_path.name} -> {spec.output_path}")
        run_generation(spec, fail_on_warning=args.fail_on_warning)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
