#!/bin/sh
set -eu

BASE_URL="https://www.51ddns.com/downloads"
MODE="host"
INSTALL_PROXY_URL=""
INSTALL_NO_PROXY=""

usage() {
	echo "Linux/Docker: curl -fsSL ${BASE_URL}/install.sh | sudo sh -s -- [--docker] [--proxy-url '<代理URL>'] [--no-proxy '<绕过列表>'] '<统一设备令牌>' '<设备ID>'" >&2
	echo "OpenWrt: uclient-fetch -q -O /tmp/51ddns-install.sh ${BASE_URL}/install.sh && sh /tmp/51ddns-install.sh --openwrt [--proxy-url '<代理URL>'] [--no-proxy '<绕过列表>'] '<统一设备令牌>' '<设备ID>'" >&2
}

while [ "$#" -gt 0 ]; do
	case "$1" in
		--docker) MODE="docker"; shift ;;
		--openwrt) MODE="openwrt"; shift ;;
		--proxy-url)
			[ "$#" -ge 2 ] || { echo "--proxy-url 缺少参数。" >&2; usage; exit 1; }
			INSTALL_PROXY_URL="$2"
			shift 2
			;;
		--no-proxy)
			[ "$#" -ge 2 ] || { echo "--no-proxy 缺少参数。" >&2; usage; exit 1; }
			INSTALL_NO_PROXY="$2"
			shift 2
			;;
		--help|-h) usage; exit 0 ;;
		--) shift; break ;;
		-*) echo "未知参数: $1" >&2; usage; exit 1 ;;
		*) break ;;
	esac
done

DEVICE_TOKEN="${1:-}"
DEVICE_ID="${2:-}"
if [ "$#" -ne 2 ] || [ -z "$DEVICE_TOKEN" ] || [ -z "$DEVICE_ID" ]; then
	usage
	exit 1
fi
case "$INSTALL_PROXY_URL" in
	"") ;;
	http://*|https://*|socks5://*) ;;
	*) echo "代理 URL 必须使用 http、https 或 socks5。" >&2; exit 1 ;;
esac
if [ -n "$INSTALL_NO_PROXY" ] && [ -z "$INSTALL_PROXY_URL" ]; then
	echo "--no-proxy 必须与 --proxy-url 一起使用。" >&2
	exit 1
fi
case "$DEVICE_ID" in
  ????????-????-????-????-????????????) ;;
  *) echo "设备 ID 格式无效，请从添加设备向导重新复制完整命令。" >&2; exit 1 ;;
esac
if [ "$(id -u)" -ne 0 ]; then
  echo "请以 root 身份运行，或在命令中使用 sudo。" >&2
  exit 1
fi

if [ -n "$INSTALL_PROXY_URL" ]; then
	HTTP_PROXY="$INSTALL_PROXY_URL"
	HTTPS_PROXY="$INSTALL_PROXY_URL"
	http_proxy="$INSTALL_PROXY_URL"
	https_proxy="$INSTALL_PROXY_URL"
	export HTTP_PROXY HTTPS_PROXY http_proxy https_proxy
fi
if [ -n "$INSTALL_NO_PROXY" ]; then
	NO_PROXY="$INSTALL_NO_PROXY"
	no_proxy="$INSTALL_NO_PROXY"
	export NO_PROXY no_proxy
fi

machine=$(uname -m)
case "$machine" in
  x86_64|amd64) ARCH="amd64" ;;
  aarch64|arm64) ARCH="arm64" ;;
  mips|mipsel|mipsle) ARCH="mipsle" ;;
  *) echo "暂不支持处理器架构: $machine（当前支持 x86_64 与 arm64）" >&2; exit 1 ;;
esac

work_dir=$(mktemp -d "${TMPDIR:-/tmp}/51ddns-install.XXXXXX")
cleanup() { rm -rf "$work_dir"; }
trap cleanup EXIT INT TERM

download() {
  source_url="$1"
  destination="$2"
  if command -v curl >/dev/null 2>&1; then
    curl -fL --retry 3 --connect-timeout 15 "$source_url" -o "$destination"
  elif command -v uclient-fetch >/dev/null 2>&1; then
    uclient-fetch -q -O "$destination" "$source_url"
  elif command -v wget >/dev/null 2>&1; then
    wget -O "$destination" "$source_url"
  else
    echo "需要 curl、uclient-fetch 或 wget 中的任意一个下载工具。" >&2
    exit 1
  fi
}

resolve_agent_release() {
  release_os="$1"
  release_arch="$2"
  sums="$work_dir/SHA256SUMS"
  if [ ! -f "$sums" ]; then
    download "$BASE_URL/SHA256SUMS" "$sums"
  fi
  suffix="_${release_os}_${release_arch}.tar.gz"
  matches=$(awk -v suffix="$suffix" '{ sub(/\r$/, "", $2) } length($1) == 64 && $1 !~ /[^0-9a-f]/ && index($2, "51ddns-agent_") == 1 && substr($2, length($2) - length(suffix) + 1) == suffix { print $1 " " $2 }' "$sums")
  if [ -z "$matches" ] || [ "$(printf '%s\n' "$matches" | awk 'END { print NR }')" -ne 1 ]; then
    echo "公开校验清单中没有唯一的 ${release_os}/${release_arch} Agent 安装包。" >&2
    exit 1
  fi
  expected=${matches%% *}
  release_file=${matches#* }
  case "$release_file" in
    *[!A-Za-z0-9._-]*|"") echo "公开校验清单中的文件名无效。" >&2; exit 1 ;;
    51ddns-agent_*"$suffix") ;;
    *) echo "公开校验清单中的平台信息无效。" >&2; exit 1 ;;
  esac
}

resolve_docker_release() {
  release_metadata="$work_dir/release.json"
  download "$BASE_URL/release.json" "$release_metadata"
  DOCKER_VERSION=$(sed -n 's/^[[:space:]]*"dockerVersion":[[:space:]]*"\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)".*/\1/p' "$release_metadata")
  DOCKER_FILE=$(sed -n 's/^[[:space:]]*"dockerFile":[[:space:]]*"\([A-Za-z0-9._-]*\)".*/\1/p' "$release_metadata")
  DOCKER_SHA256=$(sed -n 's/^[[:space:]]*"dockerSha256":[[:space:]]*"\([0-9a-f]*\)".*/\1/p' "$release_metadata")
  case "$DOCKER_VERSION" in *[!0-9.]*|"") echo "Docker 发布版本无效。" >&2; exit 1 ;; esac
  if [ "$DOCKER_FILE" != "51ddns-agent_${DOCKER_VERSION}_docker_amd64.tar.gz" ]; then
    echo "Docker 发布文件名无效。" >&2
    exit 1
  fi
  case "$DOCKER_SHA256" in *[!0-9a-f]*|"") echo "Docker 发布校验值无效。" >&2; exit 1 ;; esac
  if [ "${#DOCKER_SHA256}" -ne 64 ]; then
    echo "Docker 发布校验值无效。" >&2
    exit 1
  fi
}

verify_sha256() {
  expected="$1"
  target="$2"
  if command -v sha256sum >/dev/null 2>&1; then
    printf '%s  %s\n' "$expected" "$target" | sha256sum -c - >/dev/null
  elif command -v shasum >/dev/null 2>&1; then
    actual=$(shasum -a 256 "$target" | awk '{print $1}')
    [ "$actual" = "$expected" ]
  else
    echo "系统缺少 SHA-256 校验工具，已停止安装。" >&2
    exit 1
  fi
}

ensure_linux_installation_id() {
  umask 077
  mkdir -p /etc/51ddns
  chmod 0700 /etc/51ddns
  if grep -Eq '^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$' /etc/51ddns/install.id 2>/dev/null; then
    return
  fi
  if [ ! -r /proc/sys/kernel/random/uuid ]; then
    echo "系统无法生成稳定安装 ID，已停止安装。" >&2
    exit 1
  fi
  tr -d '\r\n' < /proc/sys/kernel/random/uuid > /etc/51ddns/install.id
  printf '\n' >> /etc/51ddns/install.id
  chmod 0600 /etc/51ddns/install.id
}

install_docker() {
  if [ "$ARCH" != "amd64" ]; then
    echo "Docker 一键部署当前仅支持 x86_64；ARM64 请使用 Linux 一键部署。" >&2
    exit 1
  fi
  if ! command -v docker >/dev/null 2>&1; then
    echo "未检测到 Docker，请先按 https://docs.docker.com/engine/install/ 安装。" >&2
    exit 1
  fi
  resolve_docker_release
  archive="$work_dir/51ddns-agent-docker.tar.gz"
  download "$BASE_URL/$DOCKER_FILE" "$archive"
  verify_sha256 "$DOCKER_SHA256" "$archive"
  gzip -dc "$archive" | docker load >/dev/null

	 device_slug=$(printf '%s' "$DEVICE_ID" | tr -d '-' | cut -c1-12)
	 container_name="51ddns-agent-$device_slug"
	 secret_dir="/etc/51ddns/docker/$device_slug"
  umask 077
  mkdir -p "$secret_dir"
  chmod 0700 "$secret_dir"
	 printf '%s\n' "$DEVICE_TOKEN" > "$secret_dir/device.token"
	 printf '%s\n' "$DEVICE_ID" > "$secret_dir/device.id"
	 printf '%s\n' "$INSTALL_PROXY_URL" > "$secret_dir/proxy.url"
	 printf '%s\n' "$INSTALL_NO_PROXY" > "$secret_dir/no-proxy"
	 chown 65532:65532 "$secret_dir/device.token"
	 chown 65532:65532 "$secret_dir/device.id"
	 chown 65532:65532 "$secret_dir/proxy.url"
	 chown 65532:65532 "$secret_dir/no-proxy"
	 chmod 0600 "$secret_dir/device.token" "$secret_dir/device.id" "$secret_dir/proxy.url" "$secret_dir/no-proxy"
  if docker container inspect "$container_name" >/dev/null 2>&1; then
    docker rm -f "$container_name" >/dev/null
  fi
  docker run -d \
    --name "$container_name" \
    --restart always \
    --network host \
    --security-opt no-new-privileges:true \
		 --mount "type=bind,src=$secret_dir/device.token,dst=/run/secrets/device_token,readonly" \
		 --mount "type=bind,src=$secret_dir/device.id,dst=/run/secrets/device_id,readonly" \
		 --mount "type=bind,src=$secret_dir/proxy.url,dst=/run/secrets/proxy_url,readonly" \
		 --mount "type=bind,src=$secret_dir/no-proxy,dst=/run/secrets/no_proxy,readonly" \
    --mount "type=volume,src=${container_name}-data,dst=/var/lib/51ddns" \
		 -e DEVICE_TOKEN_FILE=/run/secrets/device_token \
		 -e DEVICE_ID_FILE=/run/secrets/device_id \
		 -e PROXY_URL_FILE=/run/secrets/proxy_url \
		 -e NO_PROXY_FILE=/run/secrets/no_proxy \
    -e CONTROL_API_URL=https://api.51ddns.com \
    "51ddns/agent:${DOCKER_VERSION}" >/dev/null
  echo "51DDNS Docker Agent 已安装并启动：$container_name"
}

install_openwrt() {
  os_name="linux"
  resolve_agent_release "$os_name" "$ARCH"
  archive="$work_dir/51ddns-agent.tar.gz"
  download "$BASE_URL/$release_file" "$archive"
  verify_sha256 "$expected" "$archive"
  tar -xzf "$archive" -C "$work_dir"
  mkdir -p /usr/lib/51ddns /etc/51ddns /var/lib/51ddns
  chmod 0755 /usr/lib/51ddns
  chmod 0700 /etc/51ddns /var/lib/51ddns
  cp "$work_dir/51ddns-agent" /usr/lib/51ddns/51ddns-agent
  cp "$work_dir/frpc" /usr/lib/51ddns/frpc
  chmod 0755 /usr/lib/51ddns/51ddns-agent /usr/lib/51ddns/frpc
	 printf '%s\n' "$DEVICE_TOKEN" > /etc/51ddns/device.token
	 printf '%s\n' "$DEVICE_ID" > /etc/51ddns/device.id
	 printf '%s\n' "$INSTALL_PROXY_URL" > /etc/51ddns/proxy.url
	 printf '%s\n' "$INSTALL_NO_PROXY" > /etc/51ddns/no-proxy
	 chmod 0600 /etc/51ddns/device.token /etc/51ddns/device.id /etc/51ddns/proxy.url /etc/51ddns/no-proxy
  cat > /etc/init.d/51ddns-agent <<'INIT'
#!/bin/sh /etc/rc.common
USE_PROCD=1
START=95
STOP=10
start_service() {
  procd_open_instance
  procd_set_param command /usr/lib/51ddns/51ddns-agent --device-token-file /etc/51ddns/device.token --device-id-file /etc/51ddns/device.id --proxy-url-file /etc/51ddns/proxy.url --no-proxy-file /etc/51ddns/no-proxy --frpc-path /usr/lib/51ddns/frpc --config-path /var/lib/51ddns/frpc.toml
  procd_set_param respawn 3600 5 5
  procd_set_param stdout 1
  procd_set_param stderr 1
  procd_close_instance
}
INIT
  chmod 0755 /etc/init.d/51ddns-agent
  /etc/init.d/51ddns-agent enable
  # A first-time `restart` asks procd to remove a service that does not yet
  # exist and prints the misleading "Command failed: Not found" message.
  # Stop quietly, start cleanly, and verify the Agent before reporting success.
  /etc/init.d/51ddns-agent stop >/dev/null 2>&1 || true
  /etc/init.d/51ddns-agent start
  sleep 2
  if command -v pidof >/dev/null 2>&1; then
    agent_running() { pidof 51ddns-agent >/dev/null 2>&1; }
  else
    agent_running() { ps | grep '[5]1ddns-agent' >/dev/null 2>&1; }
  fi
  if ! agent_running; then
    echo "51DDNS Agent 安装完成，但服务启动失败。最近日志：" >&2
    logread -e 51ddns-agent 2>/dev/null | tail -n 20 >&2 || true
    exit 1
  fi
  echo "51DDNS OpenWrt Agent 已安装并启动。"
}

install_host() {
  kernel=$(uname -s)
  case "$kernel" in
    Linux) OS="linux" ;;
    Darwin) OS="darwin" ;;
    *) echo "该一键脚本不支持系统: $kernel；Windows 请使用 install.ps1。" >&2; exit 1 ;;
  esac
  if [ "$OS" = "darwin" ] && { [ -n "$INSTALL_PROXY_URL" ] || [ -n "$INSTALL_NO_PROXY" ]; }; then
	 echo "原生代理参数当前支持 Linux、Docker 与 OpenWrt；macOS 暂不支持。" >&2
	 exit 1
  fi
  resolve_agent_release "$OS" "$ARCH"
  archive="$work_dir/51ddns-agent.tar.gz"
  download "$BASE_URL/$release_file" "$archive"
  verify_sha256 "$expected" "$archive"
  tar -xzf "$archive" -C "$work_dir"
  if [ "$OS" = "linux" ]; then
    ensure_linux_installation_id
  fi
  if [ -n "$INSTALL_PROXY_URL" ] && [ -n "$INSTALL_NO_PROXY" ]; then
	 sh "$work_dir/install.sh" --proxy-url "$INSTALL_PROXY_URL" --no-proxy "$INSTALL_NO_PROXY" "$DEVICE_TOKEN" "$DEVICE_ID"
  elif [ -n "$INSTALL_PROXY_URL" ]; then
	 sh "$work_dir/install.sh" --proxy-url "$INSTALL_PROXY_URL" "$DEVICE_TOKEN" "$DEVICE_ID"
  elif [ -n "$INSTALL_NO_PROXY" ]; then
	 sh "$work_dir/install.sh" --no-proxy "$INSTALL_NO_PROXY" "$DEVICE_TOKEN" "$DEVICE_ID"
  else
	 sh "$work_dir/install.sh" "$DEVICE_TOKEN" "$DEVICE_ID"
  fi
}

case "$MODE" in
  docker) install_docker ;;
  openwrt) install_openwrt ;;
  host) install_host ;;
esac
