#!/usr/bin/env python3
"""生成 Termux Agent config.json — 连接顺序 ① NAS 局域网 ② 本机 Docker ③ NAS 外网 frp。

与 agent.py BIND-03 / workphone.mdc §零.十 一致。
用法:
  python3 build_agent_config.py --device-id xgfe65eimrrofyws --out /tmp/config.json
  python3 build_agent_config.py --print-primary   # 只输出当前可达主服 ws 基址
"""
from __future__ import annotations

import argparse
import json
import socket
import sys
import urllib.request
from typing import List, Optional, Tuple

SDK_PORT = 8899
NAS_LAN_HOSTS = ("192.168.110.101", "192.168.1.201")
NAS_FRP_HOST = "open.quwanzhi.com"
HEARTBEAT = 10
PROJECT_ID = "cunkebao"


def _tcp_ok(host: str, port: int, timeout: float = 2.5) -> bool:
    try:
        with socket.create_connection((host, port), timeout=timeout):
            return True
    except OSError:
        return False


def _health_ok(host: str, port: int = SDK_PORT, timeout: float = 3.0) -> bool:
    try:
        url = f"http://{host}:{port}/health"
        with urllib.request.urlopen(url, timeout=timeout) as resp:
            return resp.status == 200
    except Exception:
        return False


def pick_mac_lan_ip() -> str:
    import subprocess

    try:
        out = subprocess.check_output(["ifconfig"], text=True, stderr=subprocess.DEVNULL)
    except Exception:
        return "127.0.0.1"
    ips: List[str] = []
    for line in out.splitlines():
        parts = line.strip().split()
        if len(parts) >= 2 and parts[0] == "inet":
            ip = parts[1]
            if ip.startswith("127.") or ip.startswith("198.18."):
                continue
            ips.append(ip)
    for ip in ips:
        if ip.startswith("192.168.110."):
            return ip
    for ip in ips:
        if ip.startswith("192.168.") or ip.startswith("10."):
            return ip
    return ips[0] if ips else "127.0.0.1"


def resolve_primary(for_phone: bool = False) -> Tuple[str, str, List[str]]:
    """返回 (primary_ws_base, tier, ordered_ws_bases)。

    手机/本机统一顺序（BIND-03 · workphone.mdc §零.十）：
    ① NAS 局域网 → ② 本机 Mac Docker 局域网 → ③ NAS 外网 frp；
    Agent 运行时 TCP 探测，连不上自动切下一候选。
    """
    mac_ip = pick_mac_lan_ip()
    nas_bases = [(f"ws://{h}:{SDK_PORT}/ws/device", "nas_lan") for h in NAS_LAN_HOSTS]
    frp_base = (f"ws://{NAS_FRP_HOST}:{SDK_PORT}/ws/device", "nas_frp")
    mac_base = (f"ws://{mac_ip}:{SDK_PORT}/ws/device", "mac_docker") if mac_ip != "127.0.0.1" else None

    ordered: List[Tuple[str, str]] = list(nas_bases)
    if mac_base:
        ordered.append(mac_base)
    ordered.append(frp_base)

    if for_phone:
        # 手机 OTA：固定 NAS 为主连接写入 config，public_servers 按序探测（局域网优先于公网）
        primary_ws, tier = nas_bases[0][0], "nas_lan"
        bases = [b for b, _ in ordered]
        seen = set()
        ordered_bases: List[str] = []
        for b in bases:
            if b not in seen:
                seen.add(b)
                ordered_bases.append(b)
        return primary_ws, tier, ordered_bases

    primary_ws, tier = ordered[0][0], ordered[0][1]
    for ws_base, t in ordered:
        host = ws_base.split("//")[1].split(":")[0]
        if _health_ok(host, SDK_PORT):
            primary_ws, tier = ws_base, t
            break

    bases: List[str] = []
    seen = set()
    for ws_base, _ in ordered:
        if ws_base not in seen:
            seen.add(ws_base)
            bases.append(ws_base)
    bases = [primary_ws] + [b for b in bases if b != primary_ws]
    return primary_ws, tier, bases


def build_config(device_id: str, for_phone: bool = False) -> dict:
    primary_ws, tier, bases = resolve_primary(for_phone=for_phone)
    return {
        "device_id": device_id,
        "server_url": primary_ws,
        "public_servers": [b for b in bases if b != primary_ws],
        "heartbeat_interval": HEARTBEAT,
        "project_id": PROJECT_ID,
        "connection_priority": ["nas_lan", "mac_docker", "nas_frp"],
        "_meta": {"primary_tier": tier, "generator": "build_agent_config.py"},
    }


def main() -> int:
    ap = argparse.ArgumentParser()
    ap.add_argument("--device-id", default="xgfe65eimrrofyws")
    ap.add_argument("--out", default="")
    ap.add_argument("--print-primary", action="store_true")
    ap.add_argument("--for-phone", action="store_true", help="手机 OTA：NAS→frp→Mac")
    args = ap.parse_args()

    primary_ws, tier, _ = resolve_primary(for_phone=args.for_phone)
    if args.print_primary:
        print(primary_ws)
        print(tier, file=sys.stderr)
        return 0

    cfg = build_config(args.device_id, for_phone=args.for_phone)
    text = json.dumps(cfg, ensure_ascii=False, indent=2)
    if args.out:
        with open(args.out, "w", encoding="utf-8") as f:
            f.write(text)
    else:
        print(text)
    return 0


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