#!/usr/bin/env bash
# ====================================================================
#  云舟 QQ助手 · macOS / Linux 一键安装脚本
#  https://wpn.yfmk68.com/install.sh
#
#  用法:
#    curl -fsSL https://wpn.yfmk68.com/install.sh | bash
#
#  安全约定:
#    · 只从 wpn.yfmk68.com 与腾讯官方域名 (qq.com / gtimg.cn) 下载
#    · 下载后使用 sha256 校验; 服务端未提供校验值时明确提示并跳过
#    · 不执行任何未校验的远程代码
#    · 幂等: 重复运行会安全地覆盖 / 更新
# ====================================================================
set -e
set -u
set -o pipefail

SITE="https://wpn.yfmk68.com"
API="$SITE/api/public"
DL="$SITE/dl"
QQ_CONFIG="https://im.qq.com/proxy/domain/cdn-go.cn/qq-web/im.qq.com_new/latest/rainbow/pcConfig.json"
APP_NAME="云舟QQ助手"
CONNECT_TIMEOUT=20
MAX_TIME=1800

# ── 颜色（非 TTY 或 NO_COLOR 时自动关闭）──
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
  BOLD="$(printf '\033[1m')"
  RED="$(printf '\033[31m')"; GRN="$(printf '\033[32m')"
  YEL="$(printf '\033[33m')"; BLU="$(printf '\033[34m')"
  CYA="$(printf '\033[36m')"; RST="$(printf '\033[0m')"
else
  BOLD=""; RED=""; GRN=""; YEL=""; BLU=""; CYA=""; RST=""
fi

say()  { printf '%s %s\n' "${BLU}ℹ${RST}" "$*"; }
ok()   { printf '%s %s\n' "${GRN}✔${RST}" "$*"; }
warn() { printf '%s %s\n' "${YEL}▲${RST}" "$*" >&2; }
err()  { printf '%s %s\n' "${RED}✖${RST}" "$*" >&2; }
step() { printf '\n%s\n' "${BOLD}${CYA}▶ $*${RST}"; }

TMP=""; MOUNT=""
cleanup() {
  if [ -n "$MOUNT" ] && [ -d "$MOUNT" ]; then hdiutil detach "$MOUNT" >/dev/null 2>&1 || true; fi
  if [ -n "$TMP" ] && [ -d "$TMP" ]; then rm -rf "$TMP" 2>/dev/null || true; fi
}
die() {
  err "$*"
  printf '\n%s\n' "${YEL}下一步:${RST} 请把上面的错误信息截图发给客服，或稍后重试:"
  printf '  curl -fsSL %s/install.sh | bash\n' "$SITE"
  cleanup
  exit 1
}
trap cleanup EXIT
trap 'cleanup; exit 130' INT TERM

have() { command -v "$1" >/dev/null 2>&1; }
to_lower() { printf '%s' "$1" | tr 'A-Z' 'a-z'; }

# ── URL 白名单: 只允许本站与腾讯官方域 ──
allowed_url() {
  case "$1" in
    https://wpn.yfmk68.com/*) return 0 ;;
    https://qqdl.gtimg.cn/*|https://im.qq.com/*|https://dldir1.qq.com/*|https://dldir1v6.qq.com/*) return 0 ;;
    *) return 1 ;;
  esac
}

# ── 极简 JSON 取值（不依赖 jq / python）──
jget() {
  printf '%s' "$1" | tr ',' '\n' | sed -n 's/.*"'"$2"'"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -n 1
}

url_ok() {
  local u="$1" c=""
  c="$(curl -sSL -o /dev/null -w '%{http_code}' --head --connect-timeout 15 --max-time 30 "$u" 2>/dev/null || true)"
  [ "$c" = "200" ] && return 0
  c="$(curl -sSL -o /dev/null -w '%{http_code}' -r 0-0 --connect-timeout 15 --max-time 30 "$u" 2>/dev/null || true)"
  [ "$c" = "200" ] || [ "$c" = "206" ]
}

api_version() {
  local json u
  json="$(curl -fsSL --connect-timeout 15 --max-time 30 "$API/version?platform=$1" 2>/dev/null || true)"
  [ -z "$json" ] && return 1
  u="$(jget "$json" url)"
  [ -z "$u" ] && return 1
  printf '%s|%s|%s' "$u" "$(jget "$json" version)" "$(jget "$json" sha256)"
}

sha256_of() {
  if have shasum; then shasum -a 256 "$1" | awk '{print $1}'
  elif have sha256sum; then sha256sum "$1" | awk '{print $1}'
  else printf ''; fi
}

fetch_file() {
  local url="$1" out="$2"
  allowed_url "$url" || die "拒绝下载非白名单地址: $url"
  say "下载: $url"
  curl -fL --progress-bar --connect-timeout "$CONNECT_TIMEOUT" --max-time "$MAX_TIME" \
    --retry 3 --retry-delay 2 -o "$out" "$url"
}

verify_sha() {
  local file="$1" expect="$2" sidecar="$3" act=""
  if [ -z "$expect" ] && [ -n "$sidecar" ]; then
    expect="$(curl -fsSL --connect-timeout 15 --max-time 30 "$sidecar" 2>/dev/null | awk '{print $1}' | head -n 1 || true)"
  fi
  if [ -z "$expect" ]; then
    warn "服务端未提供 sha256 校验值，已跳过完整性校验（文件仅来自 $SITE）。"
    return 0
  fi
  act="$(sha256_of "$file")"
  if [ -z "$act" ]; then warn "本机缺少 shasum/sha256sum，无法校验。"; return 0; fi
  if [ "$(to_lower "$act")" = "$(to_lower "$expect")" ]; then
    ok "sha256 校验通过: $act"
  else
    err "sha256 校验失败! 期望: $expect"
    err "                   实际: $act"
    die "安装包可能已损坏或被篡改，已中止安装。"
  fi
}

# ── 1. 探测系统与架构 ──
step "1/6 检测系统环境"
RAW_S="$(uname -s)"
RAW_M="$(uname -m)"
case "$RAW_S" in
  Darwin) OS="mac" ;;
  Linux)  OS="linux" ;;
  *) die "暂不支持的操作系统: $RAW_S（本脚本支持 macOS 与 Linux）" ;;
esac
case "$RAW_M" in
  arm64|aarch64) ARCH="arm64" ;;
  x86_64|amd64)  ARCH="x64" ;;
  *) die "暂不支持的 CPU 架构: $RAW_M" ;;
esac
have curl || die "缺少 curl。macOS 自带 curl；Linux 请先安装: sudo apt-get install -y curl"
ok "系统 $RAW_S / $RAW_M  (OS=$OS, ARCH=$ARCH)"

# Linux: 官方暂未发布
if [ "$OS" = "linux" ]; then
  warn "云舟 QQ助手 目前仅提供 macOS 与 Windows 版，Linux 版尚未发布。"
  say "请访问 $SITE 手动下载 macOS / Windows 版本；如需 Linux 版可联系客服登记。"
  cleanup
  exit 1
fi

# ── 2. 解析下载地址 ──
step "2/6 查询最新版本"
REL_URL=""; REL_VER=""; REL_SHA=""
try_platform() {
  local r u rest v s
  r="$(api_version "$1" || true)"
  [ -z "$r" ] && return 1
  u="${r%%|*}"; rest="${r#*|}"; v="${rest%%|*}"; s="${rest#*|}"
  allowed_url "$u" || return 1
  url_ok "$u" || return 1
  REL_URL="$u"; REL_VER="$v"; REL_SHA="$s"
  return 0
}

if [ "$ARCH" = "arm64" ]; then
  if try_platform mac-arm64; then
    ok "已选择 macOS Apple 芯片版 v${REL_VER:-未知}"
  else
    warn "Apple 芯片专用安装包暂不可用，自动回退 macOS 通用 / Intel 版（Apple 芯片可经 Rosetta 2 运行）。"
  fi
fi
if [ -z "$REL_URL" ]; then
  if try_platform mac-x64; then
    ok "已选择 macOS Intel 版 v${REL_VER:-未知}"
  fi
fi
if [ -z "$REL_URL" ]; then
  _v=""
  _r="$(api_version mac-x64 || true)"
  if [ -n "$_r" ]; then _rest="${_r#*|}"; _v="${_rest%%|*}"; fi
  if [ -n "$_v" ]; then
    for _name in "云舟QQ助手-$_v-mac-x64.zip" "YunzhouQQAssistant-$_v-mac-x64.zip"; do
      _u="$DL/$_name"
      if url_ok "$_u"; then REL_URL="$_u"; REL_VER="$_v"; REL_SHA=""; break; fi
    done
  fi
fi
[ -z "$REL_URL" ] && die "未找到可用的 macOS 安装包（服务端可能正在维护）。请稍后重试或访问 $SITE 手动下载。"

# ── 3. 下载并校验 ──
step "3/6 下载安装包"
TMP="$(mktemp -d "${TMPDIR:-/tmp}/wpn-install.XXXXXX")" || die "无法创建临时目录"
ART="$TMP/artifact"
fetch_file "$REL_URL" "$ART" || die "下载失败（网络中断或文件不存在）。请检查网络后重试，或访问 $SITE 手动下载。"
BYTES="$(wc -c < "$ART" | tr -d ' ')"
ok "下载完成: $(awk -v b="$BYTES" 'BEGIN{printf "%.1f", b/1048576}') MB"

say "校验安装包完整性（sha256）..."
verify_sha "$ART" "$REL_SHA" "$REL_URL.sha256"

case "$REL_URL" in
  *.dmg) KIND="dmg" ;;
  *.zip) KIND="zip" ;;
  *) KIND="dmg" ;;
esac

# ── 4. 安装 ──
step "4/6 安装 $APP_NAME"
DEST="/Applications"
SUDO=""
if [ ! -w "$DEST" ]; then
  SUDO="sudo"
  say "写入 /Applications 需要管理员权限，可能提示输入开机密码。"
fi
INSTALLED_APP=""
if [ "$KIND" = "dmg" ]; then
  have hdiutil || die "缺少 hdiutil，无法挂载 dmg。"
  MOUNT="$TMP/mnt"; mkdir -p "$MOUNT"
  hdiutil attach -nobrowse -quiet -mountpoint "$MOUNT" "$ART" || die "挂载 dmg 失败（文件可能已损坏）。"
  APP_SRC="$(find "$MOUNT" -maxdepth 1 -name '*.app' -print | head -n 1)"
  if [ -z "$APP_SRC" ]; then hdiutil detach "$MOUNT" >/dev/null 2>&1 || true; MOUNT=""; die "dmg 内未找到 .app 应用。"; fi
  APP_BASE="$(basename "$APP_SRC")"
  say "安装应用到 $DEST/$APP_BASE ..."
  if [ -n "$SUDO" ]; then
    sudo rm -rf "${DEST:?}/${APP_BASE:?}"
    sudo ditto "$APP_SRC" "$DEST/$APP_BASE" || die "拷贝应用到 /Applications 失败。"
    sudo xattr -dr com.apple.quarantine "$DEST/$APP_BASE" 2>/dev/null || true
  else
    rm -rf "${DEST:?}/${APP_BASE:?}"
    ditto "$APP_SRC" "$DEST/$APP_BASE" || die "拷贝应用到 /Applications 失败。"
    xattr -dr com.apple.quarantine "$DEST/$APP_BASE" 2>/dev/null || true
  fi
  hdiutil detach "$MOUNT" >/dev/null 2>&1 || true; MOUNT=""
  INSTALLED_APP="$DEST/$APP_BASE"
else
  have ditto || die "缺少 ditto，无法解压 zip。"
  mkdir -p "$TMP/uz"
  ditto -x -k "$ART" "$TMP/uz" || die "解压安装包失败。"
  APP_SRC="$(find "$TMP/uz" -maxdepth 2 -name '*.app' -print | head -n 1)"
  [ -z "$APP_SRC" ] && die "压缩包内未找到 .app 应用。"
  APP_BASE="$(basename "$APP_SRC")"
  if [ -n "$SUDO" ]; then
    sudo rm -rf "${DEST:?}/${APP_BASE:?}"; sudo ditto "$APP_SRC" "$DEST/$APP_BASE" || die "拷贝应用到 /Applications 失败。"
    sudo xattr -dr com.apple.quarantine "$DEST/$APP_BASE" 2>/dev/null || true
  else
    rm -rf "${DEST:?}/${APP_BASE:?}"; ditto "$APP_SRC" "$DEST/$APP_BASE" || die "拷贝应用到 /Applications 失败。"
    xattr -dr com.apple.quarantine "$DEST/$APP_BASE" 2>/dev/null || true
  fi
  INSTALLED_APP="$DEST/$APP_BASE"
fi
ok "已安装: $INSTALLED_APP"

# ── 5. 补齐缺失组件 ──
step "5/6 检查并补齐缺失组件（QQ）"
QQ_APP="/Applications/QQ.app"
if [ -d "$QQ_APP" ]; then
  ok "已检测到 QQ。"
else
  warn "未检测到 QQ（/Applications/QQ.app），自动获取腾讯官方安装包..."
  QJSON="$(curl -fsSL --connect-timeout 15 --max-time 30 "$QQ_CONFIG" 2>/dev/null || true)"
  QQ_URL="$(printf '%s' "$QJSON" | tr ',' '\n' | grep -o '"downloadUrl":"[^"]*\.dmg"' | head -n 1 | sed 's/.*:"//; s/"$//' || true)"
  if [ -z "$QQ_URL" ]; then
    warn "无法获取官方 QQ 下载地址，请手动安装: https://im.qq.com/macqq/"
  else
    QQ_DMG="$HOME/Downloads/QQ-official.dmg"
    if fetch_file "$QQ_URL" "$QQ_DMG"; then
      ok "QQ 安装包已下载: $QQ_DMG"
      QM="$TMP/qdm"; mkdir -p "$QM"
      if hdiutil attach -nobrowse -quiet -mountpoint "$QM" "$QQ_DMG" 2>/dev/null; then
        QSRC="$(find "$QM" -maxdepth 1 -name 'QQ.app' -print | head -n 1)"
        if [ -n "$QSRC" ]; then
          if [ -n "$SUDO" ]; then sudo rm -rf "$QQ_APP"; sudo ditto "$QSRC" "$QQ_APP" 2>/dev/null || true
          else rm -rf "$QQ_APP"; ditto "$QSRC" "$QQ_APP" 2>/dev/null || true; fi
          xattr -dr com.apple.quarantine "$QQ_APP" 2>/dev/null || true
        fi
        hdiutil detach "$QM" >/dev/null 2>&1 || true
      fi
    fi
    if [ -d "$QQ_APP" ]; then
      ok "QQ 已自动安装完成。"
    else
      warn "无法全自动安装 QQ，已为你打开官方安装包。"
      warn "请把 QQ 拖入 Applications，完成后重新运行本脚本:"
      printf '  curl -fsSL %s/install.sh | bash\n' "$SITE"
      open "$QQ_DMG" 2>/dev/null || true
      cleanup
      exit 1
    fi
  fi
fi

# ── NapCat 运行时检测（不自动执行第三方代码）──
NAP_DIR="$HOME/Library/Containers/com.tencent.qq/Data/Documents/napcat"
if [ -d "$NAP_DIR" ] || [ -d "$QQ_APP/Contents/Resources/app/napcat" ]; then
  ok "已检测到 NapCat 运行时注入。"
else
  warn "未检测到 NapCat 运行时（若客户端已内置可忽略本步）。"
  echo "  QQ 机器人运行时 NapCat 需要手动安装（官方 GUI 安装器，约 2 分钟）:"
  echo "    1) 打开 https://github.com/NapNeko/NapCat-Mac-Installer/releases/latest"
  echo "    2) 选择「切换程序入口 NapCat」并安装"
  echo "       （首次需在 系统设置 -> 隐私与安全性 -> App 管理 中授权）"
  echo "    3) 先启动 QQ，再启动本软件"
  echo "  出于安全考虑，本脚本不会自动注入或执行第三方未校验代码。"
fi

# ── 6. 启动 ──
step "6/6 启动 $APP_NAME"
if [ -n "$INSTALLED_APP" ] && [ -d "$INSTALLED_APP" ]; then
  open "$INSTALLED_APP" || warn "自动启动失败，请手动打开: $INSTALLED_APP"
  ok "已启动: $(basename "$INSTALLED_APP")"
else
  warn "未找到已安装的应用，请手动打开。"
fi

printf '\n%s\n' "${GRN}${BOLD}安装完成 🎉${RST}"
say "首次使用请在客户端内扫码登录 QQ。"
say "若窗口未出现，请在「启动台 / 应用程序」中手动打开。"