"""Sandbox Docker network 管理。

`zcbot-sandbox-net` 是 docker bridge,**默有 outbound NAT**(走 host 默 bridge 路由)。
sandbox 容器同接此 net + iptables OUTPUT 红线段 DROP(init.sh)挡 cloud metadata /
loopback / 内网 / PG IP。

**dogfood 阶段**(当前):容器可访问公网(让模型能 `pip install` / `curl` 公开域名),
iptables 仍挡内网 + cloud metadata。

**外部用户开放时**(§7.7 Stage C Step 4,DESIGN §7.5 #2):
network 改 `--internal`(完全禁 outbound)+ 起 zcbot-proxy 容器接此 net + sandbox
容器 env `HTTP_PROXY` 指向 proxy + proxy 做 allowlist / 字节计量 / audit。届时
network 从 bridge 改 internal,需手动 rm + recreate(已 running 的容器先全停)。

操作幂等:create 前 inspect 探测,已存在直接返;若已存在但 Internal=true(上一版
遗留),打 warn 提示 ── 不自动 rm 避免破坏现有连着的容器(详 RUN.md "Sandbox
网络迁移"段)。
"""
from __future__ import annotations

import json
import subprocess

NETWORK_NAME = "zcbot-sandbox-net"


def ensure_network() -> None:
    """创建 `zcbot-sandbox-net`(若不存在);若已存在且 Internal=True 仅 warn。失败 raise。"""
    inspect = subprocess.run(
        ["docker", "network", "inspect", NETWORK_NAME],
        capture_output=True, text=True,
    )
    if inspect.returncode == 0:
        # 已存在 ── 检测 Internal 属性,若 true 给迁移提示
        try:
            data = json.loads(inspect.stdout)
            if data and isinstance(data, list) and data[0].get("Internal") is True:
                print(
                    f"[warn] network {NETWORK_NAME} is --internal (legacy);"
                    f" sandbox 容器将无法 outbound。手动 `docker network rm {NETWORK_NAME}`"
                    f" 后重启 web,会自动 recreate 为非 internal(详 RUN.md)"
                )
        except (json.JSONDecodeError, IndexError, AttributeError):
            pass
        return
    r = subprocess.run(
        ["docker", "network", "create", NETWORK_NAME],
        capture_output=True, text=True,
    )
    if r.returncode != 0:
        raise RuntimeError(
            f"docker network create {NETWORK_NAME} failed: {r.stderr.strip()}"
        )
