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

import argparse
import json
import subprocess
from pathlib import Path
from typing import Any


DEFAULT_REGISTRY = (
    Path(__file__).resolve().parents[1]
    / "app-instance"
    / "runtime"
    / "registry"
    / "instances.json"
)
DEFAULT_OUTPUT = Path(__file__).resolve().parent / "runtime" / "conf.d" / "instances.conf"


def load_instances(path: Path) -> list[dict[str, Any]]:
    if not path.exists():
        return []
    try:
        data = json.loads(path.read_text(encoding="utf-8"))
    except json.JSONDecodeError:
        return []
    items = data.get("instances", [])
    return [item for item in items if isinstance(item, dict)]


def is_container_running(container_name: str) -> bool:
    name = container_name.strip()
    if not name:
        return False
    try:
        result = subprocess.run(
            ["docker", "inspect", "-f", "{{.State.Running}}", name],
            check=False,
            capture_output=True,
            text=True,
        )
    except OSError:
        return False
    return result.returncode == 0 and result.stdout.strip().lower() == "true"


def render_server(instance_host: str, container_name: str, upstream_port: int) -> str:
    return f"""server {{
  listen 80;
  server_name {instance_host};

  location / {{
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Host $host;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;
    proxy_pass http://{container_name}:{upstream_port};
  }}
}}
"""


def render(instances: list[dict[str, Any]], upstream_port: int) -> str:
    blocks = [
        "server {\n"
        "  listen 80 default_server;\n"
        "  server_name _;\n"
        "  return 404;\n"
        "}\n"
    ]
    seen_hosts: set[str] = set()
    for item in sorted(instances, key=lambda value: str(value.get("instance_host", ""))):
        instance_host = str(item.get("instance_host", "") or "").strip()
        container_name = str(item.get("container_name", "") or "").strip()
        if (
            not instance_host
            or not container_name
            or instance_host in seen_hosts
            or not is_container_running(container_name)
        ):
            continue
        seen_hosts.add(instance_host)
        blocks.append(render_server(instance_host, container_name, upstream_port))
    return "\n".join(blocks)


def main() -> int:
    parser = argparse.ArgumentParser(description="Render nginx routes for app instances.")
    parser.add_argument("--registry", default=str(DEFAULT_REGISTRY), help="Registry JSON path.")
    parser.add_argument("--output", default=str(DEFAULT_OUTPUT), help="Nginx output config path.")
    parser.add_argument("--upstream-port", default=8080, type=int, help="App container upstream port.")
    args = parser.parse_args()

    registry_path = Path(args.registry).expanduser()
    output_path = Path(args.output).expanduser()
    output_path.parent.mkdir(parents=True, exist_ok=True)

    contents = render(load_instances(registry_path), args.upstream_port)
    output_path.write_text(contents, encoding="utf-8")
    print(output_path)
    return 0


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