#!/usr/bin/env bash
# ============================================================
# anytuck-multiple —— 多租户中继宿主机管理（启停/日志/重配/卸载/证书）
# 租户与改密请用浏览器：https://<RELAY_DOMAIN>/admin/
# 配置：/etc/anytuck-multiple/config
# 子命令：info | cert | status | logs | restart | up | down | reconfig | iface | lang | uninstall
# ============================================================
set -euo pipefail

CONFIG_DIR="/etc/anytuck-multiple"
CONFIG_FILE="$CONFIG_DIR/config"
uiLang="zh"

# L 按界面语言返回文案。
L() { if [[ "$uiLang" == "en" ]]; then printf '%s' "$2"; else printf '%s' "$1"; fi; }

# applyLang 规范化并应用语言代码。
applyLang() {
	case "${1:-}" in
		en|EN|english|English) uiLang="en" ;;
		*) uiLang="zh" ;;
	esac
}

# persistLang 把语言写入 .env 与 /etc 配置（决定 CLI 语言与 relay 首次启动语言；
# 面板内切换过语言后以面板落盘的 ui_lang 为准）。
persistLang() {
	setEnv RELAY_LANG "$uiLang"
	if [[ -f "$CONFIG_FILE" ]]; then
		if grep -qE '^RELAY_LANG=' "$CONFIG_FILE" 2>/dev/null; then
			sed -i "s|^RELAY_LANG=.*|RELAY_LANG=${uiLang}|" "$CONFIG_FILE"
		else
			printf 'RELAY_LANG=%s\n' "$uiLang" >>"$CONFIG_FILE"
		fi
	fi
}

loadConfig() {
	if [[ ! -f "$CONFIG_FILE" ]]; then
		echo "未找到配置 $CONFIG_FILE，请先运行 install.sh。 / Config $CONFIG_FILE not found; run install.sh first." >&2
		exit 1
	fi
	# shellcheck disable=SC1090
	source "$CONFIG_FILE"
	: "${PROJECT_DIR:?配置缺少 PROJECT_DIR}"
	: "${COMPOSE_FILE:=docker-compose.yml}"
	ENV_FILE="$PROJECT_DIR/.env"
	export COMPOSE_PROFILES="${COMPOSE_PROFILES:-}"
	export SHARED_NETWORK="${SHARED_NETWORK:-edge_shared}"
	uiLang="${RELAY_LANG:-}"
	if [[ -z "$uiLang" && -f "$ENV_FILE" ]]; then
		uiLang=$(grep -E '^RELAY_LANG=' "$ENV_FILE" 2>/dev/null | head -n1 | cut -d= -f2- || true)
	fi
	[[ "$uiLang" == "en" ]] || uiLang="zh"
}

dc() {
	# 子 shell 进项目目录，避免调用方 cwd 已失效（getcwd 报错）时拖垮 compose
	(cd "$PROJECT_DIR" && docker compose -f "$COMPOSE_FILE" "$@")
}

getEnv() {
	grep -E "^$1=" "$ENV_FILE" 2>/dev/null | head -n1 | cut -d= -f2- || true
}

setEnv() {
	local key="$1" val="$2" escaped
	escaped=$(printf '%s' "$val" | sed -e 's/[\\|&]/\\&/g')
	if grep -qE "^${key}=" "$ENV_FILE"; then
		sed -i "s|^${key}=.*|${key}=${escaped}|" "$ENV_FILE"
	else
		printf '%s=%s\n' "$key" "$val" >>"$ENV_FILE"
	fi
}

# hasDdnsProfile 判断当前部署是否启用了 ddns profile。
hasDdnsProfile() {
	[[ ",${COMPOSE_PROFILES}," == *",ddns,"* ]]
}

# adminURL 拼出管理面板地址。
adminURL() {
	local domain port
	domain=$(getEnv RELAY_DOMAIN)
	port=$(getEnv RELAY_HTTPS_PORT); port=${port:-443}
	if [[ "$port" == "443" ]]; then
		echo "https://${domain}/admin/"
	else
		echo "https://${domain}:${port}/admin/"
	fi
}

# relayAddress 拼出客户端 WSS 地址。
relayAddress() {
	local domain port
	domain=$(getEnv RELAY_DOMAIN)
	port=$(getEnv RELAY_HTTPS_PORT); port=${port:-443}
	if [[ "$port" == "443" ]]; then
		echo "wss://${domain}/ws"
	else
		echo "wss://${domain}:${port}/ws"
	fi
}

# fetchPeerCert 用 openssl 拉取对端证书文本（subject/issuer/dates）；失败返回非 0。
fetchPeerCert() {
	local domain="$1" port="$2" pem=""
	command -v openssl >/dev/null 2>&1 || return 1
	pem=$(echo | openssl s_client -connect "127.0.0.1:${port}" -servername "$domain" 2>/dev/null | openssl x509 -noout -subject -issuer -dates 2>/dev/null) || true
	if [[ -z "$pem" ]]; then
		pem=$(echo | openssl s_client -connect "${domain}:${port}" -servername "$domain" 2>/dev/null | openssl x509 -noout -subject -issuer -dates 2>/dev/null) || true
	fi
	[[ -n "$pem" ]] || return 1
	printf '%s\n' "$pem"
}

# daysUntil 把 openssl notAfter 日期换成距今天数（失败回空）。
daysUntil() {
	local raw="$1" epoch now
	raw="${raw#notAfter=}"
	epoch=$(date -d "$raw" +%s 2>/dev/null || date -j -f "%b %d %T %Y %Z" "$raw" +%s 2>/dev/null || true)
	[[ -n "$epoch" ]] || { printf ''; return; }
	now=$(date +%s)
	echo $(( (epoch - now) / 86400 ))
}

# certSummary 打印 TLS 证书状态多行摘要（供 info / 安装结束复用）。
certSummary() {
	local domain port pem issuer notAfter days line
	domain=$(getEnv RELAY_DOMAIN)
	port=$(getEnv RELAY_HTTPS_PORT); port=${port:-443}

	if [[ "${COMPOSE_FILE:-}" == "docker-compose.npm.yml" ]]; then
		echo " $(L "证书托管" "TLS host") : $(L "由反代签发（本项目不占 TLS）" "Issued by reverse proxy (this stack has no TLS)")"
	fi

	if ! command -v openssl >/dev/null 2>&1; then
		echo " $(L "证书状态" "Certificate") : $(L "无法检测（系统缺少 openssl）" "cannot check (openssl missing)")"
		return 0
	fi

	if pem=$(fetchPeerCert "$domain" "$port"); then
		issuer=$(printf '%s\n' "$pem" | grep '^issuer=' | head -n1 | sed 's/^issuer=//')
		notAfter=$(printf '%s\n' "$pem" | grep '^notAfter=' | head -n1 | sed 's/^notAfter=//')
		days=$(daysUntil "notAfter=$notAfter")
		echo " $(L "证书状态" "Certificate") : $(L "已签发 / TLS 可连" "issued / TLS reachable")"
		[[ -n "$issuer" ]] && echo " $(L "颁发者" "Issuer")   : $issuer"
		if [[ -n "$notAfter" ]]; then
			if [[ -n "$days" ]]; then
				echo " $(L "有效期至" "Valid until") : $notAfter（$(L "剩余 ${days} 天" "${days} days left")）"
			else
				echo " $(L "有效期至" "Valid until") : $notAfter"
			fi
		fi
		return 0
	fi

	if [[ "${COMPOSE_FILE:-}" != "docker-compose.npm.yml" ]]; then
		line=$(dc logs edge 2>/dev/null | grep -iE 'certificate obtained|successfully obtained certificate|renewed certificate' | tail -n1 || true)
		if [[ -n "$line" ]]; then
			echo " $(L "证书状态" "Certificate") : $(L "已签发（edge 日志确认），但本机 HTTPS 暂不可连（检查端口/防火墙/DNS）" "issued (edge logs), but local HTTPS not reachable (check port/firewall/DNS)")"
			return 0
		fi
		line=$(dc logs edge 2>/dev/null | grep -iE 'error|failed' | grep -iE 'acme|tls|certificate|cloudflare' | tail -n1 || true)
		if [[ -n "$line" ]]; then
			echo " $(L "证书状态" "Certificate") : $(L "未就绪（签发可能失败，见 anytuck-multiple logs）" "not ready (issuance may have failed; see anytuck-multiple logs)")"
			echo " $(L "最近相关" "Recent") : ${line:0:160}"
			return 0
		fi
	fi
	echo " $(L "证书状态" "Certificate") : $(L "未就绪 / 无法建立 TLS（首次约需 30–60s，或检查 DNS / Cloudflare Token）" "not ready / TLS failed (first try ~30–60s, or check DNS / Cloudflare Token)")"
}

# showCert 仅打印证书状态。
showCert() { certSummary; }

# curlOk 对 URL 发 GET，响应体含 ok 则成功（超时 5s）。
curlOk() {
	local url="$1"
	shift
	local body
	command -v curl >/dev/null 2>&1 || return 1
	body=$(curl -fsS -m 5 "$@" "$url" 2>/dev/null) || return 1
	[[ "$body" == *ok* ]]
}

# svcState 取 compose 服务状态字（如 running）；无则空。
svcState() {
	local svc="$1"
	dc ps --format '{{.Service}} {{.State}}' 2>/dev/null | awk -v s="$svc" '$1 == s { print $2; exit }'
}

# probeLocalRelay 探测本机 relay /healthz（host 直连；桥接经 edge；npm 用容器 IP）。
probeLocalRelay() {
	local domain port cip
	domain=$(getEnv RELAY_DOMAIN)
	port=$(getEnv RELAY_HTTPS_PORT); port=${port:-443}

	if curlOk "http://127.0.0.1:8443/healthz"; then
		return 0
	fi

	if [[ "${COMPOSE_FILE:-}" != "docker-compose.npm.yml" ]]; then
		if curlOk "https://${domain}:${port}/healthz" --resolve "${domain}:${port}:127.0.0.1"; then
			return 0
		fi
	fi

	if [[ "${COMPOSE_FILE:-}" == "docker-compose.npm.yml" ]]; then
		cip=$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$(dc ps -q relay 2>/dev/null | head -1)" 2>/dev/null || true)
		[[ -n "$cip" ]] && curlOk "http://${cip}:8443/healthz" && return 0
	fi
	return 1
}

# probePublicHealthz 经域名 HTTPS 探测 /healthz（先本机 --resolve，再公网）。
probePublicHealthz() {
	local domain="$1" port="$2"
	if curlOk "https://${domain}:${port}/healthz" --resolve "${domain}:${port}:127.0.0.1"; then
		echo local
		return 0
	fi
	if curlOk "https://${domain}:${port}/healthz"; then
		echo public
		return 0
	fi
	if curlOk "https://${domain}:${port}/healthz" -6; then
		echo public6
		return 0
	fi
	return 1
}

# connSummary 打印连接/健康状态：容器 + 本机中继 + 对外 HTTPS，并给总评。
connSummary() {
	local domain port edgeSt relaySt ddnsSt containersOk=1 localOk=0 publicKind="" hint=""
	local missing
	missing="$(L "缺失" "missing")"
	domain=$(getEnv RELAY_DOMAIN)
	port=$(getEnv RELAY_HTTPS_PORT); port=${port:-443}

	relaySt=$(svcState relay)
	[[ -z "$relaySt" ]] && relaySt="$missing"
	if [[ "${COMPOSE_FILE:-}" == "docker-compose.npm.yml" ]]; then
		echo " $(L "容器" "Containers")     : relay=${relaySt}"
		[[ "$relaySt" == "running" ]] || containersOk=0
	else
		edgeSt=$(svcState edge)
		[[ -z "$edgeSt" ]] && edgeSt="$missing"
		echo -n " $(L "容器" "Containers")     : edge=${edgeSt}  relay=${relaySt}"
		[[ "$edgeSt" == "running" && "$relaySt" == "running" ]] || containersOk=0
		if hasDdnsProfile; then
			ddnsSt=$(svcState ddns)
			[[ -z "$ddnsSt" ]] && ddnsSt="$missing"
			echo "  ddns=${ddnsSt}"
			[[ "$ddnsSt" == "running" ]] || containersOk=0
		else
			echo
		fi
	fi

	if probeLocalRelay; then
		localOk=1
		echo " $(L "本机中继" "Local relay") : /healthz → ok"
	else
		echo " $(L "本机中继" "Local relay") : /healthz → $(L "失败（relay 未监听或未启动）" "failed (relay not listening)")"
	fi

	if publicKind=$(probePublicHealthz "$domain" "$port"); then
		case "$publicKind" in
			local) echo " $(L "对外入口" "Public entry") : https://${domain}:${port}/healthz → ok（$(L "本机经 edge" "local via edge")）" ;;
			public) echo " $(L "对外入口" "Public entry") : https://${domain}:${port}/healthz → ok（$(L "公网" "public")）" ;;
			public6) echo " $(L "对外入口" "Public entry") : https://${domain}:${port}/healthz → ok（$(L "公网 IPv6" "public IPv6")）" ;;
		esac
	else
		echo " $(L "对外入口" "Public entry") : https://${domain}:${port}/healthz → $(L "失败" "failed")"
	fi

	if [[ "$containersOk" -eq 1 && "$localOk" -eq 1 && -n "$publicKind" ]]; then
		echo " $(L "连接状态" "Connectivity") : $(L "正常（服务与对外探测均可用）" "OK (service and public probe healthy)")"
	elif [[ "$containersOk" -eq 1 && "$localOk" -eq 1 ]]; then
		echo " $(L "连接状态" "Connectivity") : $(L "本机正常，对外不可达" "local OK, public unreachable")"
		hint="$(L "Mac 连不上多半是 DNS/防火墙/无 IPv6/端口未放行；查 Cloudflare AAAA、光猫与 anytuck-multiple logs" "If Mac cannot connect: check DNS/firewall/IPv6/port; Cloudflare AAAA, router, anytuck-multiple logs")"
		echo " $(L "建议" "Hint")     : $hint"
	elif [[ "$localOk" -eq 0 || "$containersOk" -eq 0 ]]; then
		echo " $(L "连接状态" "Connectivity") : $(L "异常（中继服务未就绪）" "error (relay not ready)")"
		echo " $(L "建议" "Hint")     : $(L "anytuck-multiple status / logs 排查后 restart" "anytuck-multiple status / logs then restart")"
	else
		echo " $(L "连接状态" "Connectivity") : $(L "部分异常" "partial failure")"
		echo " $(L "建议" "Hint")     : anytuck-multiple logs"
	fi
}

# readAdminInitial 从 relay 读出随机初始管理员密码（scratch 无 shell/cat，只能 docker cp）。
readAdminInitial() {
	local tmp cid pass
	tmp="$(mktemp)"
	cid="$(dc ps -q relay 2>/dev/null | head -n1)"
	if [[ -n "$cid" ]] && docker cp "${cid}:/data/admin.initial" "$tmp" 2>/dev/null; then
		pass=$(tr -d '\r\n' <"$tmp")
		rm -f "$tmp"
		[[ -n "$pass" ]] || return 1
		printf '%s' "$pass"
		return 0
	fi
	rm -f "$tmp"
	return 1
}

# showInfo 打印连接信息、证书、连通性与初始管理员密码。
showInfo() {
	local initPass
	echo "$(L "==== 连接信息 ====" "==== Connection info ====")"
	echo "WSS：  $(relayAddress)"
	echo "$(L "面板" "Admin")： $(adminURL)"
	echo "$(L "用户名" "Username")： admin"
	if initPass=$(readAdminInitial); then
		echo "$(L "初始密码" "Initial password")： $initPass"
		echo "$(L "提示：首次登录后请在面板「设置」修改密码；改密后初始密码文件会删除。" "Hint: change password under Settings after first login; the initial file is then removed.")"
	else
		echo "$(L "初始密码：已清除（说明已改过密）。忘记请用面板重置或重装。" "Initial password: cleared (already changed). Reset in panel or reinstall if forgotten.")"
	fi
	certSummary
	connSummary
}

cmdStatus() { dc ps; }
cmdLogs() { dc logs -f --tail=200 "$@"; }
cmdRestart() { dc up -d --force-recreate; echo "$(L "✓ 已重启" "✓ Restarted")"; }
cmdUp() { dc up -d --build; echo "$(L "✓ 已启动/重建" "✓ Started / rebuilt")"; }
cmdDown() { dc down; echo "$(L "✓ 已停止" "✓ Stopped")"; }

# reconfigure 修改中继域名 / 端口（回车保留原值），写回 .env 并重建生效。
reconfigure() {
	local domain port
	read -rp "$(L "完整中继域名" "Relay domain") [$(getEnv RELAY_DOMAIN)]: " domain </dev/tty
	read -rp "$(L "对外 HTTPS 端口" "HTTPS port") [$(getEnv RELAY_HTTPS_PORT)]: " port </dev/tty
	[[ -n "$domain" ]] && setEnv RELAY_DOMAIN "$domain"
	[[ -n "$port" ]] && setEnv RELAY_HTTPS_PORT "$port"
	echo "$(L "→ 重建以生效..." "→ Rebuilding...")"
	dc up -d --force-recreate
	echo "$(L "✓ 已更新。WSS：$(relayAddress)" "✓ Updated. WSS: $(relayAddress)")"
	echo "$(L "  面板：$(adminURL)" "  Admin: $(adminURL)")"
	echo "$(L "提示：改域名/端口后记得同步更新 DNS 与客户端/面板访问地址。" "Note: update DNS and client/admin URLs after domain/port change.")"
}

# listInterfaces 列出候选网卡（排除 lo）。
listInterfaces() {
	if command -v ip >/dev/null 2>&1; then
		ip -o link show 2>/dev/null | awk -F': ' '{print $2}' | grep -v '^lo$' | tr '\n' ' '
	fi
}

# changeIface 更换对外网卡名（仅 DDNS_IP_SOURCE=interface，即动态 IPv6）。
changeIface() {
	local current iface source
	if ! hasDdnsProfile; then
		echo "$(L "当前部署未启用 ddns profile，无需设置对外网卡。" "ddns profile is not enabled; WAN interface not required.")"
		echo "$(L "动态 IPv6 请用 install 模式 3；动态 IPv4 请用模式 4（默认 url 查公网 IP）。" "Use install mode 3 for dynamic IPv6; mode 4 for dynamic IPv4 (public-IP URL by default).")"
		return 0
	fi
	source=$(getEnv DDNS_IP_SOURCE); source=${source:-interface}
	if [[ "$source" != "interface" ]]; then
		echo "$(L "当前 DDNS 用公网 URL 取 IP（$source），不读网卡，无需更换对外网卡。" "DDNS uses public-IP URL ($source), not the NIC; WAN interface change is not needed.")"
		echo "$(L "动态 IPv4（模式 4）属此情况。若误改过 .env，请保持 DDNS_IP_SOURCE=url、DDNS_RECORD_TYPE=A。" "This is expected for dynamic IPv4 (mode 4). Keep DDNS_IP_SOURCE=url and DDNS_RECORD_TYPE=A.")"
		return 0
	fi
	current=$(getEnv DDNS_INTERFACE)
	echo "$(L "可用网卡：" "Interfaces:")$(listInterfaces)"
	read -rp "$(L "对外网卡名（读取全局 IPv6）" "WAN interface (global IPv6)")[$current]: " iface </dev/tty
	iface="${iface:-$current}"
	if [[ -z "$iface" ]]; then
		echo "$(L "网卡名不能为空，已取消。" "Interface name empty; cancelled.")" >&2
		return 1
	fi
	setEnv DDNS_INTERFACE "$iface"
	setEnv DDNS_IP_SOURCE "interface"
	echo "$(L "→ 重建 ddns 容器以生效..." "→ Recreating ddns...")"
	dc up -d --force-recreate ddns >/dev/null
	echo "$(L "✓ 已更新对外网卡：" "✓ WAN interface updated:")$iface"
	echo "$(L "提示：可执行 anytuck-multiple logs 查看 ddns 是否写到正确的 AAAA。" "Hint: anytuck-multiple logs to verify AAAA updates.")"
}

# switchLang 切换 CLI 语言并写回 .env（Web 面板语言请在面板「设置」里切换）。
switchLang() {
	local ans next
	echo "$(L "当前语言：中文" "Current language: English")"
	echo "  1) 中文"
	echo "  2) English"
	read -rp "$(L "选择 1/2" "Choose 1/2") [$( [[ "$uiLang" == "en" ]] && echo 2 || echo 1 )]: " ans </dev/tty
	ans="${ans:-$( [[ "$uiLang" == "en" ]] && echo 2 || echo 1 )}"
	case "$ans" in
		2|en|EN|english|English) next="en" ;;
		*) next="zh" ;;
	esac
	applyLang "$next"
	persistLang
	echo "$(L "✓ 已切换为中文（已写入 .env / 配置）" "✓ Switched to English (saved to .env / config)")"
	echo "$(L "提示：Web 面板语言请在面板「设置 → 界面语言」切换（面板选择优先，落盘 /data/ui_lang）" "Note: switch the Web panel language in the panel (Settings → Panel language); the panel choice wins (stored at /data/ui_lang)")"
}

# setLangCmd 子命令：anytuck-multiple lang [zh|en]。
setLangCmd() {
	local arg="${1:-}"
	if [[ -z "$arg" ]]; then
		switchLang
		return
	fi
	case "$arg" in
		zh|ZH|cn|CN|chinese|Chinese) applyLang zh; persistLang ;;
		en|EN|english|English) applyLang en; persistLang ;;
		*)
			echo "$(L "用法: anytuck-multiple lang [zh|en]" "Usage: anytuck-multiple lang [zh|en]")" >&2
			return 1
			;;
	esac
	echo "$(L "✓ 已切换为中文（已写入 .env / 配置）" "✓ Switched to English (saved to .env / config)")"
	echo "$(L "提示：Web 面板语言请在面板「设置 → 界面语言」切换（面板选择优先，落盘 /data/ui_lang）" "Note: switch the Web panel language in the panel (Settings → Panel language); the panel choice wins (stored at /data/ui_lang)")"
}

# uninstallAll 卸载：停容器、删命令与配置；可选删数据卷与项目目录。
uninstallAll() {
	local ans delvol delproj delimg
	read -rp "$(L "确认卸载中继服务多用户版？将停止并移除容器 [y/N] " "Uninstall multi-user relay service and remove containers? [y/N] ")" ans </dev/tty
	[[ "${ans,,}" == "y" ]] || { echo "$(L "已取消" "Cancelled")"; return; }

	read -rp "$(L "是否同时删除数据卷（证书 + 用户/管理员数据将丢失）？[y/N] " "Also delete volumes (certs + user/admin data)? [y/N] ")" delvol </dev/tty
	if [[ "${delvol,,}" == "y" ]]; then
		dc down -v || true
	else
		dc down || true
	fi

	rm -f /usr/local/bin/anytuck-multiple
	rm -rf "$CONFIG_DIR"

	read -rp "$(L "是否删除本项目构建的 Docker 镜像（edge/relay/ddns）？[y/N] " "Delete project Docker images (edge/relay/ddns)? [y/N] ")" delimg </dev/tty
	if [[ "${delimg,,}" == "y" ]]; then
		removeImages
	fi

	read -rp "$(L "是否删除项目目录 $PROJECT_DIR ？[y/N] " "Delete project directory $PROJECT_DIR? [y/N] ")" delproj </dev/tty
	if [[ "${delproj,,}" == "y" ]]; then
		rm -rf "$PROJECT_DIR"
		echo "✓ $(L "已卸载（含项目目录）" "Uninstalled (including project directory)")"
	else
		echo "✓ $(L "已卸载 anytuck-multiple 命令与配置（项目目录 $PROJECT_DIR 保留，可自行删除）" "Removed CLI and config (project dir $PROJECT_DIR kept)")"
	fi
}

# removeImages 删除本项目构建的镜像。
removeImages() {
	local imgs
	imgs=$(docker images --format '{{.Repository}}:{{.Tag}}' 2>/dev/null | grep '^anytuck-relay-multiple/' || true)
	if [[ -z "$imgs" ]]; then
		echo "$(L "无本项目镜像可删" "No project images to remove")"
		return
	fi
	# shellcheck disable=SC2086
	docker rmi $imgs >/dev/null 2>&1 || echo "$(L "部分镜像删除失败（可能仍被容器引用）" "Some images could not be removed (still in use?)")"
	echo "✓ $(L "已删除镜像" "Removed images")：$(echo "$imgs" | tr '\n' ' ')"
}

# menu 交互菜单（运维项与单用户对齐；租户/改密仍走 Web）。
menu() {
	while true; do
		if [[ "$uiLang" == "en" ]]; then
			cat <<'EOF'

========== AnyTuck Multi-User Relay ==========
 1) Connection info (WSS + admin + password + cert + status)
 2) Change domain / port
 3) Change WAN interface
 4) Container status
 5) Logs (Ctrl-C to exit)
 6) Restart
 7) Start / rebuild
 8) Stop
 9) Switch language
10) Uninstall
 0) Exit
==============================================
EOF
		else
			cat <<'EOF'

========== AnyTuck 中继管理（多用户）==========
 1) 查看连接信息（WSS + 面板 + 密码 + 证书 + 连接状态）
 2) 修改中继域名 / 端口
 3) 更换对外网卡
 4) 查看运行状态
 5) 查看日志（Ctrl-C 退出）
 6) 重启
 7) 启动 / 重建
 8) 停止
 9) 切换语言
10) 卸载
 0) 退出
==============================================
EOF
		fi
		read -rp "$(L "选择: " "Choice: ")" c </dev/tty
		case "$c" in
			1) showInfo ;;
			2) reconfigure ;;
			3) changeIface ;;
			4) cmdStatus ;;
			5) cmdLogs ;;
			6) cmdRestart ;;
			7) cmdUp ;;
			8) cmdDown ;;
			9) switchLang ;;
			10) uninstallAll; exit 0 ;;
			0) exit 0 ;;
			*) echo "$(L "无效选项" "Invalid option")" ;;
		esac
	done
}

loadConfig
# 避免调用方 cwd 已删除时 bash 刷 getcwd 错
cd "$PROJECT_DIR" 2>/dev/null || cd / || true
case "${1:-}" in
	info) showInfo ;;
	cert|certs) showCert ;;
	status|ps) cmdStatus ;;
	logs) shift; cmdLogs "$@" ;;
	restart) cmdRestart ;;
	up) cmdUp ;;
	down) cmdDown ;;
	reconfig) reconfigure ;;
	iface|interface) changeIface ;;
	lang|language) setLangCmd "${2:-}" ;;
	uninstall) uninstallAll ;;
	"") menu ;;
	*)
		echo "$(L "用法: anytuck-multiple [info|cert|status|logs|restart|up|down|reconfig|iface|lang|uninstall]" "Usage: anytuck-multiple [info|cert|status|logs|restart|up|down|reconfig|iface|lang|uninstall]")"
		exit 1
		;;
esac
