#!/usr/bin/env bash
# OSSL Agent 安装脚本（https://get.ossl.cn/index.sh）
#
#   curl -fsSL https://get.ossl.cn/index.sh | bash -s -- <token>
#
#   token    控制台生成的一次性接入令牌（24 小时内有效，用一次即作废）
#            服务器上跑的是 nginx、apache 还是容器，由 agent 启动后自己识别，不需要指定
#
# 流程：检查环境 → 按 manifest.json 选包并校验 sha256 → 停掉旧进程 → 替换二进制
#       → 用令牌登记新身份 → 注册开机自启（systemd）并启动。
# 重装 / 换机器同样用本脚本：控制台「重置绑定」拿新令牌后再执行一次即可。
#
# 整个脚本包在 main 里、最后一行才调用：经 curl | bash 执行时，bash 先读完全部内容再运行，
# 中途被子进程读走 stdin 也不会吃掉后面的脚本。

set -euo pipefail

BASE_URL="${OSSL_BASE_URL:-https://get.ossl.cn}"
INSTALL_DIR="/opt/ossl"
BIN="${INSTALL_DIR}/ossl"
LINK_PATH="/usr/local/bin/ossl"
UNIT_NAME="ossl"

info()  { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
ok()    { printf '\033[1;32m ✔\033[0m %s\n' "$*"; }
warn()  { printf '\033[1;33m ⚠\033[0m %s\n' "$*" >&2; }
die()   { printf '\033[1;31m ✘\033[0m %s\n' "$*" >&2; exit 1; }

usage() {
  cat >&2 <<EOF
用法: curl -fsSL ${BASE_URL}/index.sh | bash -s -- <token>
令牌请在 OSSL 控制台「服务器 → 创建服务器 / 连接服务器」处获取。
EOF
  exit 1
}

detect_platform() {
  OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
  case "${OS}" in
    linux|darwin) ;;
    *) die "不支持的系统: ${OS}（Windows 请到 ${BASE_URL} 下载 exe 手动安装）" ;;
  esac
  case "$(uname -m)" in
    x86_64|amd64)  ARCH="amd64" ;;
    arm64|aarch64) ARCH="arm64" ;;
    armv7*)        ARCH="armv7" ;;
    armv6*)        ARCH="armv6" ;;
    i386|i686)     ARCH="386" ;;
    mips)
      # 同一个 uname 值可能是大端或小端：看 1 的字节序
      if [ "$(printf '\001\000' | od -An -tu2 | tr -d ' ')" = "1" ]; then ARCH="mipsle"; else ARCH="mips"; fi ;;
    mipsel|mipsle) ARCH="mipsle" ;;
    *) die "不支持的架构: $(uname -m)" ;;
  esac
}

sha256_of() {
  if command -v sha256sum >/dev/null 2>&1; then sha256sum "$1" | awk '{print $1}'
  elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$1" | awk '{print $1}'
  elif command -v openssl >/dev/null 2>&1; then openssl dgst -sha256 -r "$1" | awk '{print $1}'
  else die "缺少 sha256 工具（sha256sum / shasum / openssl 任一即可）"
  fi
}

# 从 manifest.json 里取本平台的 url 与 sha256（不依赖 jq；字段顺序无关）
pick_binary() {
  awk -v os="${OS}" -v arch="${ARCH}" '
    function val(line) { sub(/^[^:]*:[ \t]*"/, "", line); sub(/".*$/, "", line); return line }
    /"os"[ \t]*:/     { o = val($0) }
    /"arch"[ \t]*:/   { a = val($0) }
    /"url"[ \t]*:/    { u = val($0) }
    /"sha256"[ \t]*:/ { s = val($0) }
    /}/ { if (o == os && a == arch && u != "" && s != "") { print u " " s; exit } o = a = u = s = "" }
  ' "$1"
}

stop_existing() {
  if [ -f "/etc/systemd/system/${UNIT_NAME}.service" ] && command -v systemctl >/dev/null 2>&1; then
    # 有 systemd 服务时必须先停服务：Restart=always 会把单独 kill 掉的进程立刻拉起来
    systemctl stop "${UNIT_NAME}" >/dev/null 2>&1 || true
  fi
  if [ -x "${BIN}" ]; then
    "${BIN}" stop </dev/null >/dev/null 2>&1 || true
  fi
}

is_running() {
  "${BIN}" status </dev/null 2>/dev/null | grep -q "running"
}

has_systemd() {
  command -v systemctl >/dev/null 2>&1 && [ -d /run/systemd/system ]
}

main() {
  # 令牌取最后一个参数：兼容旧安装命令「<service> <token>」，服务类型已由 agent 自行识别
  local token="${!#:-}"
  [ "$#" -ge 1 ] && [ -n "${token}" ] || usage
  [ "$#" -ge 2 ] && warn "安装命令中的服务类型参数已不再需要，已忽略"
  [ "$(id -u)" -eq 0 ] || die "需要 root 权限：curl -fsSL ${BASE_URL}/index.sh | sudo bash -s -- <token>"
  command -v curl >/dev/null 2>&1 || die "缺少 curl"

  detect_platform
  info "系统 ${OS}/${ARCH}"

  # 全局变量：EXIT trap 在 main 返回后才执行，局部变量那时已不存在
  TMP_DIR="$(mktemp -d)"
  trap 'rm -rf "${TMP_DIR}"' EXIT
  local tmp="${TMP_DIR}"

  # ---------- 1. 选包 ----------
  curl -fsSL --retry 3 "${BASE_URL}/manifest.json" -o "${tmp}/manifest.json" || die "获取版本清单失败: ${BASE_URL}/manifest.json"
  local entry url want version
  entry="$(pick_binary "${tmp}/manifest.json")"
  [ -n "${entry}" ] || die "当前版本没有 ${OS}/${ARCH} 的安装包"
  url="${entry%% *}"
  want="${entry##* }"
  version="$(awk -F'"' '/"latest"[ \t]*:/ {print $4; exit}' "${tmp}/manifest.json")"
  info "安装 OSSL Agent v${version}"

  # ---------- 2. 下载并校验 ----------
  curl -fL --retry 3 --progress-bar "${url}" -o "${tmp}/ossl" || die "下载失败: ${url}"
  local got
  got="$(sha256_of "${tmp}/ossl")"
  [ "${got}" = "${want}" ] || die "安装包校验失败（sha256 不一致），请稍后重试"
  chmod 0755 "${tmp}/ossl"
  ok "下载完成，sha256 校验通过"

  # ---------- 3. 替换 ----------
  if [ -x "${BIN}" ]; then
    info "检测到已安装，停止旧进程"
    stop_existing
  fi
  mkdir -p "${INSTALL_DIR}"
  mv -f "${tmp}/ossl" "${BIN}"
  mkdir -p "$(dirname "${LINK_PATH}")"
  ln -sf "${BIN}" "${LINK_PATH}"
  ok "已安装到 ${BIN}"

  # ---------- 4. 登记（前台完成，失败当场报告）并启动 ----------
  info "登记身份"
  "${BIN}" start -t="${token}" </dev/null || true
  local i
  for i in 1 2 3 4 5 6 7 8 9 10; do
    is_running && break
    sleep 1
  done
  is_running || die "登记或启动失败：原因见上方输出（日志 ${INSTALL_DIR}/client.log）；令牌失效请在控制台重新获取安装命令"
  ok "登记成功，agent 已启动"

  # ---------- 5. 开机自启 ----------
  if has_systemd; then
    "${BIN}" stop </dev/null >/dev/null 2>&1 || true
    "${BIN}" install </dev/null >/dev/null
    systemctl start "${UNIT_NAME}"
    sleep 2
    if systemctl is-active --quiet "${UNIT_NAME}"; then
      ok "已注册 systemd 服务 ${UNIT_NAME}（开机自启、异常自动拉起）"
    else
      die "systemd 服务启动失败：journalctl -u ${UNIT_NAME} -n 50"
    fi
  else
    warn "未检测到 systemd：agent 已在后台运行，但不会开机自启（重启后需手动执行 ossl start）"
  fi

  cat <<EOF

OSSL Agent v${version} 安装完成。agent 已开始识别本机的网站服务，回到控制台即可看到识别结果。

  查看状态   ossl status
  查看日志   tail -f ${INSTALL_DIR}/client.log
  卸载       curl -fsSL ${BASE_URL}/uninstall.sh | bash
EOF
}

main "$@"
