#!/bin/bash

APP="/Applications/Caddy"
APP_SUB="$APP/Steamcommunity_302"
LAUNCH_DAEMON_DIR="/Library/LaunchDaemons"
DEFAULT_LABEL="com.sylker.caddy"
HOSTS_FILE="/etc/hosts"
SUPPORT_SRC="/var/root/Library/Application Support/com.Dogfight360.SteamCommunity302"
BLOG_URL="https://www.dogfight360.com/blog/18682/"
CONF_FILE="$APP/s302-manager.conf"
SNAPSHOT_FILE="$APP/hosts.pre302"
SAVED_HOSTS="$APP/hosts"

CADDY_API="https://caddyserver.com/api/download?os=darwin&arch=__ARCH__"

if [ -t 1 ]; then
  C_G="\033[32m"; C_Y="\033[33m"; C_R="\033[31m"; C_B="\033[36m"; C_R0="\033[0m"
else
  C_G=""; C_Y=""; C_R=""; C_B=""; C_R0=""
fi

msg_ok()   { printf "${C_G}[完成]${C_R0} %s\n" "$*"; }
msg_info() { printf "${C_B}[信息]${C_R0} %s\n" "$*"; }
msg_warn() { printf "${C_Y}[注意]${C_R0} %s\n" "$*"; }
msg_err()  { printf "${C_R}[错误]${C_R0} %s\n" "$*"; }

pause_enter() { printf "    按回车键继续…"; read -r _; }

ask() { printf "    %s" "$1"; read -r _ANS; }

ask_yes_no() {
  local hint="$1" def="$2" ans
  if [ "$def" = "y" ]; then
    printf "    %s（回车=是，输入 n=否）：" "$hint"
  else
    printf "    %s（回车=否，输入 y=是）：" "$hint"
  fi
  read -r ans
  ans=$(printf '%s' "$ans" | tr '[:upper:]' '[:lower:]')
  [ -z "$ans" ] && ans="$def"
  [ "$ans" = "y" ]
}

run_root() {
  if [ "$(id -u)" -eq 0 ]; then
    bash -c "$1"
  else
    sudo bash -c "$1"
  fi
}

owner_pair() { printf '%s:staff' "${SUDO_USER:-$(id -un)}"; }

cur_uid() { id -u "${SUDO_USER:-$(id -un)}"; }

real_home() {
  local u="${SUDO_USER:-$(id -un)}" h
  h=$(dscl . -read "/Users/$u" NFSHomeDirectory 2>/dev/null | awk '{print $2}')
  if [ -z "$h" ] || [ ! -d "$h" ]; then h=$(eval echo "~$u" 2>/dev/null); fi
  [ -z "$h" ] && h="$HOME"
  printf '%s' "$h"
}

detect_arch() {
  case "$(uname -m)" in
    arm64)  ARCH_TAG="arm64"; ARCH_NAME="Apple Silicon，M 系列芯片" ;;
    x86_64) ARCH_TAG="amd64"; ARCH_NAME="Intel 芯片" ;;
    *) msg_err "无法识别的 CPU 架构：$(uname -m)，本工具仅支持苹果电脑"; exit 1 ;;
  esac
}

fetch_file() { curl -fL --progress-bar --retry 2 -o "$2" "$1"; }

md5_of() { md5 -q "$1" 2>/dev/null; }

count_steam_lines() { grep -v '^#' "$1" 2>/dev/null | grep -ci steam; }

current_label() {
  if [ -f "$CONF_FILE" ]; then
    awk -F= '/^label=/{print $2; exit}' "$CONF_FILE"
  fi
}

active_level() {
  if [ -f "$CONF_FILE" ]; then
    awk -F= '/^level=/{print $2; exit}' "$CONF_FILE"
  fi
}

detect_level() {
  local l level pl
  level=$(active_level)
  if [ -n "$level" ]; then
    printf '%s' "$level"
    return
  fi
  l=$(active_label)
  pl=$(daemon_plist_for "$l")
  [ -f "$pl" ] && { printf 'daemon'; return; }
  pl=$(agent_plist_for "$l")
  [ -f "$pl" ] && { printf 'agent'; return; }
  printf 'unknown'
}

active_level_name() {
  case "$(detect_level)" in
    agent) printf '用户级 LaunchAgents' ;;
    *)     printf '系统级 LaunchDaemons' ;;
  esac
}

active_label() {
  local l
  l=$(current_label)
  [ -z "$l" ] && l="$DEFAULT_LABEL"
  printf '%s' "$l"
}

save_conf() {
  mkdir -p "$APP" 2>/dev/null
  {
    printf 'label=%s\n' "$1"
    printf 'level=%s\n' "$2"
  } > "$CONF_FILE"
}

daemon_plist_for()   { printf '%s/%s.plist' "$LAUNCH_DAEMON_DIR" "$1"; }
agent_plist_for() {
  local h
  h=$(real_home)
  printf '%s/Library/LaunchAgents/%s.plist' "$h" "$1"
}

installed_plist_path() {
  local l="$1" p
  p=$(daemon_plist_for "$l"); [ -f "$p" ] && { printf '%s' "$p"; return 0; }
  p=$(agent_plist_for "$l");  [ -f "$p" ] && { printf '%s' "$p"; return 0; }
  return 1
}

service_running() { pgrep -f "$APP/caddy" >/dev/null 2>&1; }

s302_proc_alive() { pgrep -f "Steamcommunity_302" >/dev/null 2>&1; }

port443_now() {
  netstat -an -p tcp 2>/dev/null | grep '\.443 ' | grep -ic LISTEN
}

s302_caddy_alive() { pgrep -f "steamcommunity_302.caddy" >/dev/null 2>&1; }

hosts_fp() {
  if [ -f "$1" ]; then
    md5 -q "$1" 2>/dev/null
  else
    printf 'none'
  fi
}

launch_s302_bg() {
  local dir="$APP_SUB" hit
  if [ ! -f "$dir/Steamcommunity_302" ]; then
    hit=$(find "$APP" -maxdepth 3 -name Steamcommunity_302 -type f 2>/dev/null | head -n 1)
    [ -n "$hit" ] && dir=$(dirname "$hit")
  fi
  if [ ! -f "$dir/Steamcommunity_302" ]; then
    msg_err "未找到 Steamcommunity302 程序本体，请先执行一键安装"
    return 1
  fi
  chmod +x "$dir/steamcommunity_302.cli" 2>/dev/null || true
  chmod +x "$dir/steamcommunity_302.caddy" 2>/dev/null || true
  chmod +x "$dir/Steamcommunity_302" 2>/dev/null || true
  msg_info "正在后台拉起图形界面，如弹出密码输入框请输入开机密码"
  msg_warn "请在界面中勾选服务并点击「启动服务」；本工具会自动检测，无需手动回车"
  (
    cd "$dir" || exit 1
    sudo -b ./Steamcommunity_302
  )
}

wait_s302_started_and_capture() {
  local last_fp cur_fp elapsed=0 captured=0 tmo=300 n started
  ABORT=0
  last_fp=$(hosts_fp "$HOSTS_FILE")
  msg_info "实时监测中：逐秒对比 hosts 变化，并探测内置反代内核进程"
  while [ "$elapsed" -lt "$tmo" ]; do
    if [ "$captured" -eq 0 ]; then
      cur_fp=$(hosts_fp "$HOSTS_FILE")
      if [ "$cur_fp" != "$last_fp" ]; then
        printf '\n'
        save_running_hosts && captured=1
        last_fp="$cur_fp"
      fi
    fi
    if s302_caddy_alive || [ "$captured" -eq 1 ]; then
      break
    fi
    if [ "$elapsed" -gt 6 ] && ! s302_proc_alive; then
      break
    fi
    sleep 2
    [ "$ABORT" -eq 1 ] && break
    elapsed=$((elapsed+2))
    printf "\r    实时监测 %3d 秒：等待你在界面点击「启动服务」…   " "$elapsed"
  done
  printf '\n'
  started=否
  s302_caddy_alive && started=是
  if [ "$captured" -eq 0 ]; then
    n=$(count_steam_lines "$HOSTS_FILE")
    if [ "$n" -gt 0 ]; then
      save_running_hosts && captured=1
    fi
  fi
  if [ "$started" = "否" ] && [ "$captured" -eq 0 ]; then
    if s302_proc_alive; then
      msg_warn "暂未检测到反代内核或 hosts 写入，请确认界面中已点击「启动服务」"
    else
      msg_warn "图形界面进程已退出，但未检测到服务启动迹象"
    fi
    ask_yes_no "强制继续后续流程吗？" n || return 1
    return 0
  fi
  msg_ok "检测到服务已启动！请回到界面点击「停止服务」并彻底退出（⌘Q），本工具会自动继续"
  elapsed=0
  while { s302_proc_alive || s302_caddy_alive; } && [ "$elapsed" -lt 600 ]; do
    sleep 2
    [ "$ABORT" -eq 1 ] && break
    elapsed=$((elapsed+2))
    printf "\r    等待图形界面退出… %3d 秒   " "$elapsed"
  done
  printf '\n'
  apply_saved_hosts || return 1
  flush_dns || true
  msg_ok "永久域名解析已固化，此后无需再打开图形界面"
}

flush_dns() {
  run_root "dscacheutil -flushcache" || return 1
  run_root "killall -HUP mDNSResponder" || return 1
  msg_ok "DNS 缓存已刷新"
}

ensure_app_dir() {
  run_root "mkdir -p \"$APP\"" || return 1
  run_root "chown -R \"$(owner_pair)\" \"$APP\"" || return 1
}

snapshot_hosts() {
  if [ -f "$SNAPSHOT_FILE" ]; then
    msg_info "已存在安装前的系统域名解析备份，跳过覆盖"
    return 0
  fi
  run_root "cp \"$HOSTS_FILE\" \"$SNAPSHOT_FILE\" && chown \"$(owner_pair)\" \"$SNAPSHOT_FILE\"" || return 1
  msg_ok "安装前系统域名解析已备份至：$SNAPSHOT_FILE"
}

save_running_hosts() {
  run_root "cp \"$HOSTS_FILE\" \"$SAVED_HOSTS\" && chown \"$(owner_pair)\" \"$SAVED_HOSTS\"" || return 1
  local n
  n=$(count_steam_lines "$SAVED_HOSTS")
  if [ "$n" -eq 0 ]; then
    msg_warn "捕获到的文件中没有 Steam 相关转发条目，请确认已在图形界面中启动服务"
    ask_yes_no "仍要继续吗？" n || return 1
  fi
  msg_ok "运行期域名解析已保存，包含 $n 条转发条目 → $SAVED_HOSTS"
}

apply_saved_hosts() {
  run_root "cp \"$SAVED_HOSTS\" \"$HOSTS_FILE\"" || return 1
  msg_ok "永久域名解析已写回系统"
}

preflight_443() {
  local hits
  hits=$(netstat -an -p tcp 2>/dev/null | grep '\.443 ' | grep -i LISTEN)
  if [ -n "$hits" ]; then
    msg_warn "端口 443 已被以下程序监听，若属于旧版服务可忽略："
    printf '%s\n' "$hits" | sed 's/^/        /'
    ask_yes_no "仍要继续吗？" y || { msg_err "已中止"; return 1; }
  fi
}

copy_support_files() {
  if ! run_root "[ -d \"$SUPPORT_SRC\" ]"; then
    msg_err "未找到 Steamcommunity302 的数据目录：$SUPPORT_SRC"
    msg_err "请先在图形界面中成功启动过一次服务，再重新执行本步骤"
    return 1
  fi
  run_root "cp -R \"$SUPPORT_SRC/\" \"$APP/\"" || return 1
  run_root "chown -R \"$(owner_pair)\" \"$APP\"" || return 1
  msg_ok "核心数据已复制到：$APP"
}

is_json_cfg() { head -c 4096 "$1" | grep -q '^[[:space:]]*{'; }

fix_ports() {
  local cfg="$1" before=0
  if is_json_cfg "$cfg"; then
    before=$(grep -oE '":[0-9]{4,5}"' "$cfg" | wc -l | tr -d ' ')
    [ "$before" -gt 0 ] && perl -pi -e 's/":(\d{4,5})"/":443"/g' "$cfg"
  else
    before=$(grep -cE '^[[:space:]]*:[0-9]{4,5}' "$cfg")
    [ "$before" -gt 0 ] && perl -pi -e 's/^(\s*):(\d{4,5})\b/$1:443/g' "$cfg"
  fi
  printf '%s' "$before"
}

install_caddy_binary() {
  local dest="$APP/caddy" src tmp v c
  if [ -x "$dest" ]; then
    ask_yes_no "检测到已有 Caddy 内核，直接复用吗？" y && return 0
  fi
  printf '\n    请选择 Caddy 内核的获取方式：\n'
  printf '      [1] 自动下载（官方在线打包，恒为最新版，推荐）\n'
  printf '      [2] 手动导入（我已自行下载好二进制文件）\n'
  ask "输入编号 [默认 1]：" && c="$_ANS"
  tmp="$dest.new"
  if [ "$c" = "2" ]; then
    ask "请输入 Caddy 二进制文件的完整路径：" && src="$_ANS"
    src="${src/#\~/$HOME}"
    if [ ! -f "$src" ]; then
      msg_err "文件不存在：$src"
      return 1
    fi
    cp -f "$src" "$tmp" || return 1
  else
    msg_info "正在下载 Caddy 内核，目标架构：darwin/$ARCH_TAG"
    fetch_file "${CADDY_API/__ARCH__/$ARCH_TAG}" "$tmp" \
      || { msg_err "下载失败，请检查网络后重试"; rm -f "$tmp"; return 1; }
  fi
  chmod 755 "$tmp"
  if ! v=$("$tmp" version 2>/dev/null); then
    msg_err "该文件无法作为 Caddy 内核在本机运行，已作废"
    rm -f "$tmp"
    return 1
  fi
  xattr -d com.apple.quarantine "$tmp" 2>/dev/null || true
  mv -f "$tmp" "$dest"
  msg_ok "Caddy 内核就绪：$v"
}

build_plist_tmp() {
  local label="$1" cfg="$2" adapter="$3" tmp
  tmp=$(mktemp /tmp/s302mgr.XXXXXX)
  cat > "$tmp" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>$label</string>
    <key>ProgramArguments</key>
    <array>
        <string>$APP/caddy</string>
        <string>run</string>
        <string>--config</string>
        <string>$cfg</string>
        <string>--adapter</string>
        <string>$adapter</string>
    </array>
    <key>WorkingDirectory</key>
    <string>$APP</string>
    <key>StandardOutPath</key>
    <string>$APP/caddy.log</string>
    <key>StandardErrorPath</key>
    <string>$APP/caddy.error.log</string>
    <key>RunAtLoad</key>
    <true/>
    <key>KeepAlive</key>
    <true/>
</dict>
</plist>
EOF
  printf '%s' "$tmp"
}

register_service() {
  local level="$1" label="$2" cfg="$3" adapter="$4" tmp dst uid
  uid=$(cur_uid)
  tmp=$(build_plist_tmp "$label" "$cfg" "$adapter")
  if [ "$level" = "daemon" ]; then
    dst=$(daemon_plist_for "$label")
    run_root "launchctl bootout system \"$dst\"" 2>/dev/null || true
    run_root "cp \"$tmp\" \"$dst\" && chown root:wheel \"$dst\" && chmod 644 \"$dst\"" \
      || { rm -f "$tmp"; return 1; }
    run_root "launchctl bootstrap system \"$dst\"" || { rm -f "$tmp"; return 1; }
    msg_ok "已注册为系统级开机自启服务，标签：$label"
  else
    dst=$(agent_plist_for "$label")
    mkdir -p "$(dirname "$dst")" || { rm -f "$tmp"; return 1; }
    launchctl bootout "gui/$uid/$label" 2>/dev/null || true
    launchctl unload -w "$dst" 2>/dev/null || true
    cp -f "$tmp" "$dst" || { rm -f "$tmp"; return 1; }
    chmod 644 "$dst"
    [ "$(id -u)" -eq 0 ] && chown "$(owner_pair)" "$dst"
    launchctl bootstrap "gui/$uid" "$dst" 2>/dev/null || launchctl load -w "$dst" \
      || { rm -f "$tmp"; return 1; }
    msg_ok "已注册为用户级登录自启服务，标签：$label"
  fi
  rm -f "$tmp"
}

start_service() {
  local label level pl uid
  label=$(active_label); level=$(active_level); uid=$(cur_uid)
  pl=$(installed_plist_path "$label")
  if [ -z "$pl" ]; then
    msg_err "尚未注册开机自启服务，请先执行一键安装"
    return 1
  fi
  if service_running; then
    msg_info "反代服务本来就处于运行状态"
    return 0
  fi
  if [ "$(basename "$(dirname "$pl")")" = "LaunchDaemons" ]; then
    run_root "launchctl bootstrap system \"$pl\"" 2>/dev/null || true
  else
    launchctl bootstrap "gui/$uid" "$pl" 2>/dev/null || launchctl load -w "$pl" 2>/dev/null || true
  fi
  sleep 1
  if service_running; then
    msg_ok "反代服务已启动"
  else
    msg_err "启动失败，请检查配置文件与端口占用情况"
    return 1
  fi
}

stop_service() {
  local label pl uid
  label=$(active_label); uid=$(cur_uid)
  pl=$(installed_plist_path "$label")
  if [ -n "$pl" ]; then
    if [ "$(basename "$(dirname "$pl")")" = "LaunchDaemons" ]; then
      run_root "launchctl bootout system \"$pl\"" 2>/dev/null || true
    else
      launchctl bootout "gui/$uid/$label" 2>/dev/null || true
      launchctl unload "$pl" 2>/dev/null || true
    fi
  fi
  run_root "pkill -f \"$APP/caddy\"" 2>/dev/null || true
  sleep 1
  if service_running; then
    msg_err "停止失败"
    return 1
  fi
  msg_ok "反代服务已停止"
}

restart_service() {
  local label level uid
  label=$(active_label); level=$(active_level); uid=$(cur_uid)
  if [ -z "$(installed_plist_path "$label")" ]; then
    msg_err "尚未注册开机自启服务，请先执行一键安装"
    return 1
  fi
  if [ "$level" = "agent" ]; then
    launchctl kickstart -k "gui/$uid/$label" 2>/dev/null \
      || stop_service
  else
    run_root "launchctl kickstart -k system/$label" 2>/dev/null || stop_service
  fi
  sleep 2
  if service_running; then
    msg_ok "反代服务已重启"
  else
    start_service
  fi
}

pick_probe_host() {
  [ -f "$SAVED_HOSTS" ] || return 1
  awk '!/^#/ && NF>=2 { for(i=2;i<=NF;i++) if (tolower($i) ~ /steam/) {print $i; exit} }' "$SAVED_HOSTS"
}

show_status() {
  local l pl listen host code cur saved reg lvl running
  l=$(active_label)
  pl=$(installed_plist_path "$l")
  if [ -n "$pl" ]; then reg="已注册"; else reg="未注册"; fi
  case "$(detect_level)" in
    agent) lvl="用户级 LaunchAgents" ;;
    daemon) lvl="系统级 LaunchDaemons" ;;
    *) lvl="未设置" ;;
  esac
  running=否
  service_running && running=是
  listen=$(netstat -an -p tcp 2>/dev/null | grep '\.443 ' | grep -ic LISTEN)
  printf '\n%s\n' "──────────────────────────────────────────────"
  printf '  %-12s： %s\n' "开机自启" "$reg"
  printf '  %-12s： %s\n' "注册级别" "$lvl"
  printf '  %-12s： %s\n' "服务标签" "$l"
  printf '  %-12s： %s\n' "Caddy 进程" "$running"
  printf '  %-12s： %s 个\n' "443 端口监听" "$listen"
  host=$(pick_probe_host)
  if [ -n "$host" ] && service_running; then
    code=$(curl -sk -o /dev/null -w '%{http_code}' --resolve "$host:443:127.0.0.1" "https://$host/" --max-time 8)
    if [ -n "$code" ] && [ "$code" != "000" ]; then
      printf '  %-12s： HTTP %s（%s）\n' "反向代理探针" "$code" "$host"
    else
      printf '  %-12s： 探测超时，代理可能异常\n' "反向代理探针"
    fi
  fi
  cur=$(count_steam_lines "$HOSTS_FILE")
  saved=$(count_steam_lines "$SAVED_HOSTS")
  printf '  %-12s： %s 条\n' "系统内转发条目" "$cur"
  printf '  %-12s： %s 条\n' "永久解析条目" "$saved"
  if [ -f "$SNAPSHOT_FILE" ]; then
    printf '  %-12s： 存在（%s）\n' "安装前备份" "$SNAPSHOT_FILE"
  else
    printf '  %-12s： 不存在\n' "安装前备份"
  fi
  printf '%s\n' "──────────────────────────────────────────────"
}

gui_and_capture() {
  print_section "打开 Steamcommunity302 图形界面"
  launch_s302_bg || return 1
  wait_s302_started_and_capture
}

hosts_menu() {
  while :; do
    print_section "域名解析 hosts 管理"
    printf '   [1] 查看当前系统的 hosts 文件\n'
    printf '   [2] 查看永久 hosts 文件\n'
    printf '   [3] 对比：当前系统 与 安装前备份 的差异\n'
    printf '   [4] 将永久 hosts 应用到系统\n'
    printf '   [5] 还原为安装前的 hosts 备份\n'
    printf '   [6] 编辑永久 hosts 文件\n'
    printf '   [0] 返回主菜单\n'
    ask "请选择：" && local c="$_ANS"
    case "$c" in
      1)
        printf '\n───── %s ─────\n' "$HOSTS_FILE"
        cat "$HOSTS_FILE"
        printf '%s\n' "──────────────"
        ;;
      2)
        if [ ! -f "$SAVED_HOSTS" ]; then msg_warn "永久 hosts 尚未生成"; continue; fi
        printf '\n───── %s ─────\n' "$SAVED_HOSTS"
        cat "$SAVED_HOSTS"
        printf '%s\n' "──────────────"
        ;;
      3)
        if [ ! -f "$SNAPSHOT_FILE" ]; then msg_warn "尚无安装前备份可供对比"; continue; fi
        printf '\n───── 当前系统 相对 安装前 的差异 ─────\n'
        diff -u "$SNAPSHOT_FILE" "$HOSTS_FILE" 2>/dev/null | head -n 120
        printf '%s\n' "──────────────"
        ;;
      4)
        if [ ! -f "$SAVED_HOSTS" ]; then msg_warn "永久 hosts 尚未生成"; continue; fi
        apply_saved_hosts && flush_dns
        ;;
      5)
        if [ ! -f "$SNAPSHOT_FILE" ]; then msg_warn "尚无安装前备份"; continue; fi
        ask_yes_no "确定把系统 hosts 还原到安装前的状态吗？" n || continue
        run_root "cp \"$SNAPSHOT_FILE\" \"$HOSTS_FILE\"" && flush_dns && msg_ok "已还原为安装前状态"
        ;;
      6)
        "${EDITOR:-vi}" "$SAVED_HOSTS" 2>/dev/null || msg_err "未能打开编辑器"
        ;;
      0|q|Q) return 0 ;;
      *) msg_warn "没有这个选项，请重新输入" ;;
    esac
  done
}

resolve_latest_package() {
  local tmp flat
  tmp=$(mktemp /tmp/s302.blog.XXXXXX)
  msg_info "正在从官方博客解析最新版本…"
  curl -sfL --compressed --max-time 120 --speed-time 25 --speed-limit 512 \
    --retry 1 -A "NulCorePivot/1.0" -o "$tmp" "$BLOG_URL" \
    || { rm -f "$tmp"; msg_warn "博客访问失败，将回退到内置已知版本"; return 1; }
  URL_ARM=$(grep -oE 'https://[^"]*Steamcommunity_302\.[0-9.]+_osx_arm64\.tar\.gz' "$tmp" | head -n 1)
  URL_X64=$(grep -oE 'https://[^"]*Steamcommunity_302\.[0-9.]+_osx_x64\.tar\.gz' "$tmp" | head -n 1)
  flat=$(sed 's/<br>/\n/g' "$tmp")
  MD5_ARM=$(printf '%s\n' "$flat" | grep -oiE '[a-f0-9]{32}[[:space:]]+[^[:space:]<]*osx_arm64\.tar\.gz' | head -n 1 | awk '{print $1}')
  MD5_X64=$(printf '%s\n' "$flat" | grep -oiE '[a-f0-9]{32}[[:space:]]+[^[:space:]<]*osx_x64\.tar\.gz' | head -n 1 | awk '{print $1}')
  rm -f "$tmp"
  if [ -z "$URL_ARM" ] || [ -z "$URL_X64" ]; then
    msg_warn "未能解析出最新直链，将回退到内置已知版本"
    return 1
  fi
  PKG_VER=$(basename "$URL_ARM" | sed -E 's/.*302\.([0-9.]+)_osx.*/\1/')
  [ -z "$PKG_VER" ] && PKG_VER="最新"
  return 0
}

pick_pkg_vars() {
  if [ "$ARCH_TAG" = "arm64" ]; then
    PKG_URL="$URL_ARM"; PKG_MD5="$MD5_ARM"
  else
    PKG_URL="$URL_X64"; PKG_MD5="$MD5_X64"
  fi
}

extract_source_to_app() {
  local src="$1" kind="$2"
  case "$kind" in
    tar) tar -xzf "$src" -C "$APP" ;;
    zip) unzip -oq "$src" -d "$APP" ;;
    dir)
      if [ "$(cd "$src" && pwd)" = "$(cd "$APP" 2>/dev/null && pwd)" ]; then
        return 0
      fi
      run_root "cp -R \"$src/\" \"$APP/\""
      ;;
    *) return 1 ;;
  esac
  run_root "chown -R \"$(owner_pair)\" \"$APP\"" 2>/dev/null || true
  if ! find "$APP" -maxdepth 3 \( -name run.command -o -name Steamcommunity_302 \) 2>/dev/null | grep -q .; then
    msg_err "源内容中未找到 Steamcommunity302 程序本体（run.command 或主程序）"
    return 1
  fi
  msg_ok "程序文件已就位：$APP"
}

fetch_package() {
  local got archive
  pick_pkg_vars
  archive=$(mktemp /tmp/s302.pkg.XXXXXX).tar.gz
  printf '    目标版本：%s\n' "$PKG_VER"
  msg_info "正在下载 Steamcommunity302 官方安装包…"
  fetch_file "$PKG_URL" "$archive" \
    || { msg_err "下载失败，请检查网络后重试"; rm -f "$archive"; return 1; }
  if [ -n "$PKG_MD5" ]; then
    got=$(md5_of "$archive")
    if [ "$got" != "$PKG_MD5" ]; then
      msg_err "官方校验值比对不符，安装包已作废"
      printf '    实测校验值：%s\n    官方校验值：%s\n' "$got" "$PKG_MD5"
      rm -f "$archive"
      return 1
    fi
    msg_ok "官方校验值比对通过"
  else
    msg_warn "该版本未提供校验值，跳过完整性比对"
  fi
  extract_source_to_app "$archive" tar || { rm -f "$archive"; return 1; }
  rm -f "$archive"
}

manual_install_source() {
  local src
  ask "请输入压缩包或已解压目录的完整路径：" && src="$_ANS"
  src="${src/#\~/$HOME}"
  if [ -d "$src" ]; then
    extract_source_to_app "$src" dir || return 1
    return 0
  fi
  if [ ! -f "$src" ]; then
    msg_err "路径不存在：$src"
    return 1
  fi
  case "$src" in
    *.tar.gz|*.tgz) extract_source_to_app "$src" tar || return 1 ;;
    *.zip)          extract_source_to_app "$src" zip || return 1 ;;
    *)
      msg_err "暂只支持 tar.gz 与 zip 压缩包，其他格式请先解压后再选择目录方式"
      return 1
      ;;
  esac
  return 0
}

choose_install_mode() {
  local c
  printf '\n    请选择 Steamcommunity302 的获取方式：\n'
  printf '      [1] 自动安装（联网获取官方最新版并自动校验，推荐）\n'
  printf '      [2] 手动安装（我已下载好压缩包，或已有解压好的目录）\n'
  ask "输入编号 [默认 1]：" && c="$_ANS"
  if [ "$c" = "2" ]; then
    manual_install_source
    return
  fi
  if resolve_latest_package; then
    msg_ok "已解析到官方最新版本：$PKG_VER"
    pick_pkg_vars
    fetch_package
  else
    msg_err "联网获取失败，请改用手动安装：先到官方博客下载安装包"
    msg_info "官方下载页：$BLOG_URL"
    manual_install_source
  fi
}

choose_level() {
  local c
  printf '\n    请选择开机自启的注册级别：\n'
  printf '      [1] 系统级 LaunchDaemons（推荐，开机即启，随系统守护）\n'
  printf '      [2] 用户级 LaunchAgents（登录后自启，权限更收敛）\n'
  ask "输入编号 [默认 1]：" && c="$_ANS"
  case "$c" in
    2) printf 'agent' ;;
    *) printf 'daemon' ;;
  esac
}

do_install() {
  print_section "一键安装 · Steamcommunity302 本地反代持久化"
  detect_arch
  printf '    系统型号：%s\n    指令集：darwin/%s\n' "$ARCH_NAME" "$ARCH_TAG"
  preflight_443 || return 1
  ensure_app_dir || return 1
  snapshot_hosts || return 1
  if [ -f "$APP_SUB/Steamcommunity_302" ] || [ -n "$(find "$APP" -maxdepth 3 -name run.command 2>/dev/null | head -n 1)" ]; then
    msg_info "检测到 Steamcommunity302 程序本体已存在，跳过获取"
  else
    choose_install_mode || return 1
  fi
  launch_s302_bg || return 1
  wait_s302_started_and_capture || return 1
  copy_support_files || return 1
  local cfg n adapter label level adapter_name
  cfg=$(ls "$APP"/*.caddy.json 2>/dev/null | sort | head -n 1)
  if [ -z "$cfg" ]; then
    msg_err "$APP 下未找到任何 *.caddy.json 配置文件"
    return 1
  fi
  msg_info "使用配置文件：$cfg"
  n=$(fix_ports "$cfg")
  if [ "$n" -gt 0 ]; then
    msg_ok "已将 $n 处随机监听端口统一改写为 443"
  else
    msg_warn "未发现需要改写的随机端口，可能早已是 443"
  fi
  install_caddy_binary || return 1
  printf "\n    请输入开机自启标签 [默认 %s]：" "$DEFAULT_LABEL"
  read -r label
  [ -z "$label" ] && label="$DEFAULT_LABEL"
  if ! printf '%s' "$label" | grep -qE '^[A-Za-z0-9._-]+$'; then
    msg_err "标签只能包含字母、数字、点、横线、下划线"
    return 1
  fi
  level=$(choose_level)
  adapter_name="caddyfile"
  is_json_cfg "$cfg" && adapter_name="json"
  register_service "$level" "$label" "$cfg" "$adapter_name" || return 1
  save_conf "$label" "$level"
  run_root "chown -R \"$(owner_pair)\" \"$APP\"" 2>/dev/null || true
  flush_dns || true
  sleep 2
  show_status
  msg_ok "全部完成！反代内核将长期常驻并随机自启，彻底摆脱图形界面"
}

uninstall_flow() {
  print_section "卸载向导"
  local choice l pl uid
  l=$(active_label); uid=$(cur_uid)
  printf '   [1] 仅移除开机自启服务（保留 %s 数据，方便日后重装）\n' "$APP"
  printf '   [2] 彻底清除（服务 + 数据目录 + 可选还原域名解析）\n'
  printf '   [0] 返回\n'
  ask "请选择卸载范围：" && choice="$_ANS"
  case "$choice" in
    1|2) ;;
    *) return 0 ;;
  esac
  pl=$(daemon_plist_for "$l")
  if [ -f "$pl" ]; then
    run_root "launchctl bootout system \"$pl\"" 2>/dev/null || true
    run_root "rm -f \"$pl\""
    msg_ok "已移除系统级服务注册：$l"
  fi
  pl=$(agent_plist_for "$l")
  if [ -f "$pl" ]; then
    launchctl bootout "gui/$uid/$l" 2>/dev/null || true
    launchctl unload "$pl" 2>/dev/null || true
    rm -f "$pl" 2>/dev/null || run_root "rm -f \"$pl\""
    msg_ok "已移除用户级服务注册：$l"
  fi
  run_root "pkill -f \"$APP/caddy\"" 2>/dev/null || true
  flush_dns || true
  if [ "$choice" = "2" ]; then
    if [ -f "$SNAPSHOT_FILE" ]; then
      printf '    当前系统转发条目：%s 条；安装前备份：%s 条\n' \
        "$(count_steam_lines "$HOSTS_FILE")" "$(count_steam_lines "$SNAPSHOT_FILE")"
      if ask_yes_no "用安装前备份还原系统域名解析吗？" y; then
        run_root "cp \"$SNAPSHOT_FILE\" \"$HOSTS_FILE\"" && msg_ok "域名解析已还原"
      fi
    fi
    if [ -d "$APP" ]; then
      if ask_yes_no "删除整个数据目录吗？路径：$APP" n; then
        run_root "rm -rf \"$APP\"" && msg_ok "数据目录已删除"
      fi
    fi
  fi
  flush_dns || true
  msg_ok "卸载流程结束，感谢使用"
}

print_section() {
  printf '\n════════════ %s ════════════\n' "$1"
}

print_menu() {
  local l st
  l=$(active_label)
  if service_running; then st="● 反代运行中"
  elif [ -n "$(installed_plist_path "$l")" ]; then st="○ 已持久化但未运行"
  else st="✕ 未安装"; fi
  printf '\n╔═══════════════════════════════════════════════╗\n'
  printf '║   Steamcommunity 302                          ║\n'
  printf '║   macOS 本地反向代理持久化管理工具            ║\n'
  printf '╠═══════════════════════════════════════════════╣\n'
  printf '║  数据目录：%s\n' "$APP"
  printf '║  服务标签：%s\n' "$l"
  printf '║  注册级别：%s\n' "$(active_level_name)"
  printf '║  当前架构：%s\n' "${ARCH_NAME:-待检测}"
  printf '║  运行状态：%s\n' "$st"
  printf '╠────────────── 功能列表 ───────────────────────╣\n'
  printf '║  [1] 一键安装（下载配置 + 开机自启）          ║\n'
  printf '║  [2] 启动反代        [3] 停止反代             ║\n'
  printf '║  [4] 运行状态与健康检查                       ║\n'
  printf '║  [5] 打开 302 图形界面（调整勾选服务）        ║\n'
  printf '║  [6] 刷新 DNS 缓存   [7] hosts 管理           ║\n'
  printf '║  [8] 卸载            [0] 退出                 ║\n'
  printf '╚═══════════════════════════════════════════════╝\n'
}

menu_loop() {
  detect_arch
  while :; do
    print_menu
    printf '请输入编号后回车 > '
    read -r c
    case "$c" in
      1) do_install ;;
      2) start_service ;;
      3) stop_service ;;
      4) show_status ;;
      5) gui_and_capture ;;
      6) flush_dns ;;
      7) hosts_menu ;;
      8) uninstall_flow ;;
      0|q|Q|quit|exit)
        printf '再见，祝游戏愉快！\n'
        exit 0
        ;;
      "")
        ;;
      *)
        msg_warn "没有这个选项，请输入菜单中的编号"
        ;;
    esac
  done
}

usage_text() {
  printf '%s\n' \
    "Steamcommunity 302 · macOS 本地反向代理持久化管理工具" \
    "用法：./$(basename "$0") [子命令]，不带参数时进入交互菜单" \
    "  install       一键安装（引导式，含开机自启注册）" \
    "  start|stop|restart   反代服务开关" \
    "  status        运行状态与健康检查" \
    "  gui           打开 Steamcommunity302 图形界面" \
    "  dns           刷新 DNS 缓存" \
    "  hosts         hosts 管理菜单" \
    "  uninstall     卸载向导"
}

main() {
  [ "$(uname)" = "Darwin" ] || { msg_err "本工具仅支持在 macOS 上运行"; exit 1; }
  if [ $# -eq 0 ]; then
    menu_loop
    return
  fi
  case "$1" in
    install) do_install ;;
    start) start_service ;;
    stop) stop_service ;;
    restart) restart_service ;;
    status) detect_arch; show_status ;;
    gui) gui_and_capture ;;
    dns) flush_dns ;;
    hosts) hosts_menu ;;
    uninstall) uninstall_flow ;;
    help|-h|--help) usage_text ;;
    *) usage_text; exit 2 ;;
  esac
}

ABORT=0
trap 'ABORT=1; printf "\n"' INT
main "$@"
