#!/bin/sh
# cloudflared installer
#   curl -fsSL https://dl.runsh.de/cloudflared/install.sh | sh
#
# Env overrides:
#   CF_VERSION           版本号，默认取 latest.txt（如 2026.9.1）
#   CF_RELEASE_BASE      主镜像，默认 https://dl.runsh.de/cloudflared
#   CF_RELEASE_FALLBACK_BASE  备用镜像，默认 https://dl2.runsh.de/cloudflared
#   CF_INSTALL_DIR       安装目录，不填则自动探测
#                        （/usr/local/bin → /usr/bin → /opt/bin；OpenWrt 优先 /usr/bin）
#   CF_SKIP_VERIFY=1     跳过 SHA256 校验
set -eu

FALLBACK_VERSION="2026.9.1"
GITHUB_BASE="https://github.com/cloudflare/cloudflared/releases/download"

if [ -n "${CF_RELEASE_BASE:-}" ]; then
  RELEASE_BASE="${CF_RELEASE_BASE%/}"
  FALLBACK_BASE="${CF_RELEASE_FALLBACK_BASE:-}"
else
  RELEASE_BASE="https://dl.runsh.de/cloudflared"
  FALLBACK_BASE="${CF_RELEASE_FALLBACK_BASE:-https://dl2.runsh.de/cloudflared}"
fi
FALLBACK_BASE="${FALLBACK_BASE%/}"

say() { printf '%s\n' "$*"; }
die() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }

have() { command -v "$1" >/dev/null 2>&1; }

have curl || have wget || die "需要 curl 或 wget"

fetch() { # fetch <url> <outfile>
  if have curl; then
    curl -kfsSL --retry 2 --connect-timeout 15 -o "$2" "$1" && return 0
    # 少数网络/中间设备上 HTTP/2 会报 (16) framing error，降到 HTTP/1.1 再试一次
    rm -f "$2" 2>/dev/null || true
    curl -kfsSL --http1.1 --retry 2 --connect-timeout 15 -o "$2" "$1"
  else
    wget -q --no-check-certificate -O "$2" "$1"
  fi
}

fetch_stdout() {
  if have curl; then
    curl -kfsSL --retry 1 --connect-timeout 10 "$1"
  else
    wget -q --no-check-certificate -O- "$1"
  fi
}

# ---------- 版本 ----------
VERSION="${CF_VERSION:-}"
if [ -z "$VERSION" ]; then
  VERSION="$(fetch_stdout "$RELEASE_BASE/latest.txt" 2>/dev/null | tr -d ' \t\r\n' || true)"
fi
if [ -z "$VERSION" ] && [ -n "$FALLBACK_BASE" ]; then
  VERSION="$(fetch_stdout "$FALLBACK_BASE/latest.txt" 2>/dev/null | tr -d ' \t\r\n' || true)"
fi
[ -n "$VERSION" ] || VERSION="$FALLBACK_VERSION"
case "$VERSION" in
  *[!0-9A-Za-z._-]*|'') die "版本号非法: $VERSION" ;;
esac

# ---------- 平台 ----------
OS="$(uname -s 2>/dev/null || echo unknown)"
ARCH="$(uname -m 2>/dev/null || echo unknown)"
IS_TGZ=0

case "$OS" in
  Linux)
    case "$ARCH" in
      x86_64|amd64)             ASSET="cloudflared-linux-amd64" ;;
      i386|i486|i586|i686|x86)  ASSET="cloudflared-linux-386" ;;
      aarch64|arm64)            ASSET="cloudflared-linux-arm64" ;;
      armv7l|armv7*|armv8l)     ASSET="cloudflared-linux-armhf" ;;
      armv6l|armv6*|armv5*|arm) ASSET="cloudflared-linux-arm" ;;
      mips*) die "cloudflared 官方不提供 MIPS 构建（检测到 $ARCH），这类路由器装不了" ;;
      *) die "不支持的 Linux 架构: $ARCH" ;;
    esac
    ;;
  Darwin)
    IS_TGZ=1
    case "$ARCH" in
      arm64|aarch64) ASSET="cloudflared-darwin-arm64.tgz" ;;
      x86_64|amd64)  ASSET="cloudflared-darwin-amd64.tgz" ;;
      *) die "不支持的 macOS 架构: $ARCH" ;;
    esac
    ;;
  *) die "此脚本仅支持 Linux 和 macOS，Windows 请直接下载 $RELEASE_BASE/$VERSION/cloudflared-windows-amd64.exe（检测到 OS: $OS）" ;;
esac

IS_ROOT=0
[ "$(id -u 2>/dev/null || echo 1)" = "0" ] && IS_ROOT=1

SUDO=""
if [ "$IS_ROOT" = "0" ] && have sudo; then SUDO="sudo"; fi

in_path() { case ":$PATH:" in *":$1:"*) return 0 ;; *) return 1 ;; esac; }

if [ -n "${CF_INSTALL_DIR:-}" ]; then
  CANDIDATES="${CF_INSTALL_DIR%/}"
else
  if [ "$IS_ROOT" = "1" ] || [ -n "$SUDO" ]; then
    # OpenWrt / 绿联等嵌入式系统常没有 /usr/local/bin，或它不在 PATH 里
    if [ -f /etc/openwrt_release ] || [ ! -d /usr/local/bin ]; then
      PREFERRED="/usr/bin /opt/bin /usr/local/bin /usr/sbin"
    else
      PREFERRED="/usr/local/bin /usr/bin /opt/bin"
    fi
    CREATABLE="/usr/local/bin"
  else
    PREFERRED="$HOME/.local/bin $HOME/bin"
    CREATABLE="$HOME/.local/bin"
  fi

  # 第一轮：已存在且在 PATH 里的；第二轮：存在但不在 PATH 的
  CANDIDATES=""
  NOT_IN_PATH=""
  for d in $PREFERRED; do
    if [ -d "$d" ]; then
      if in_path "$d"; then CANDIDATES="$CANDIDATES $d"; else NOT_IN_PATH="$NOT_IN_PATH $d"; fi
    fi
  done
  CANDIDATES="$CANDIDATES$NOT_IN_PATH"
  case " $CANDIDATES " in *" $CREATABLE "*) ;; *) CANDIDATES="$CANDIDATES $CREATABLE" ;; esac

  # 已经装过就原地升级：解析软链接，装到它真正所在的目录
  # （否则 /usr/local/bin/cloudflared -> /usr/bin/cloudflared 这种会被写成实体文件，老版本留在 /usr/bin）
  EXISTING="$(command -v cloudflared 2>/dev/null || true)"
  if [ -n "$EXISTING" ]; then
    RESOLVED="$(readlink -f "$EXISTING" 2>/dev/null || true)"
    [ -n "$RESOLVED" ] && EXISTING="$RESOLVED"
    EXIST_DIR="$(dirname "$EXISTING" 2>/dev/null || true)"
    if [ -n "$EXIST_DIR" ] && [ -d "$EXIST_DIR" ]; then
      DEDUPED=""
      for d in $CANDIDATES; do
        [ "$d" = "$EXIST_DIR" ] || DEDUPED="$DEDUPED $d"
      done
      CANDIDATES="$EXIST_DIR$DEDUPED"
      UPGRADE_NOTE="$EXISTING"
    fi
  fi
fi

TMP_DIR="${TMPDIR:-/tmp}/cloudflared-install-$$"
cleanup() { rm -rf "$TMP_DIR" 2>/dev/null || true; }
trap cleanup EXIT INT TERM
mkdir -p "$TMP_DIR"

say "[cloudflared] 系统:   $OS / $ARCH"
say "[cloudflared] 版本:   $VERSION"
say "[cloudflared] 文件:   $ASSET"
[ -n "${UPGRADE_NOTE:-}" ] && say "[cloudflared] 已装:   $UPGRADE_NOTE（原地升级）"
say "[cloudflared] 候选目录:$CANDIDATES"

# ---------- 下载 ----------
DL="$TMP_DIR/$ASSET"
URL=""
BASES="$RELEASE_BASE/$VERSION"
[ -n "$FALLBACK_BASE" ] && BASES="$BASES $FALLBACK_BASE/$VERSION"
BASES="$BASES $GITHUB_BASE/$VERSION"
for base in $BASES; do
  say "[cloudflared] 下载: $base/$ASSET"
  if fetch "$base/$ASSET" "$DL"; then URL="$base"; break; fi
  rm -f "$DL"
  say "[cloudflared] 该镜像失败，尝试下一个…"
done
[ -n "$URL" ] || die "所有镜像都下载失败"

# ---------- 校验 ----------
if [ "${CF_SKIP_VERIFY:-0}" != "1" ] && have sha256sum; then
  if fetch_stdout "$URL/SHA256SUMS.txt" > "$TMP_DIR/SHA256SUMS.txt" 2>/dev/null; then
    EXPECTED="$(awk -v f="$ASSET" '$2 == f || $2 == "*" f {print $1}' "$TMP_DIR/SHA256SUMS.txt" | head -n1)"
    if [ -n "$EXPECTED" ]; then
      ACTUAL="$(sha256sum "$DL" | awk '{print $1}')"
      [ "$EXPECTED" = "$ACTUAL" ] || die "SHA256 校验失败 (期望 $EXPECTED，实际 $ACTUAL)"
      say "[cloudflared] SHA256 校验通过"
    fi
  fi
fi

# ---------- 解包 ----------
BIN="$DL"
if [ "$IS_TGZ" = "1" ]; then
  have tar || die "需要 tar"
  tar -xzf "$DL" -C "$TMP_DIR"
  BIN="$TMP_DIR/cloudflared"
  [ -f "$BIN" ] || die "压缩包里没找到 cloudflared"
fi

chmod +x "$BIN"

# ---------- 安装 ----------
INSTALL_DIR=""
for dir in $CANDIDATES; do
  $SUDO mkdir -p "$dir" 2>/dev/null || continue
  STAGED="$dir/.cloudflared-install-$$"
  if $SUDO cp -f "$BIN" "$STAGED" 2>/dev/null && $SUDO chmod 755 "$STAGED" 2>/dev/null; then
    # 真跑一次，能挡住 noexec 挂载和架构选错
    if $SUDO "$STAGED" --version >/dev/null 2>&1 && $SUDO mv -f "$STAGED" "$dir/cloudflared" 2>/dev/null; then
      INSTALL_DIR="$dir"
      break
    fi
  fi
  $SUDO rm -f "$STAGED" 2>/dev/null || true
  say "[cloudflared] $dir 不可用，试下一个…"
done

[ -n "$INSTALL_DIR" ] || die "没有可写且可执行的安装目录，指定一个：CF_INSTALL_DIR=/usr/bin sh -c \"\$(curl -fsSL $RELEASE_BASE/install.sh)\""

say ""
say "[cloudflared] 安装完成: $INSTALL_DIR/cloudflared"
"$INSTALL_DIR/cloudflared" --version || true

case ":$PATH:" in
  *":$INSTALL_DIR:"*) ;;
  *) say ""
     say "[cloudflared] 注意: $INSTALL_DIR 不在 PATH 里，请加上："
     say "  export PATH=\"$INSTALL_DIR:\$PATH\"" ;;
esac

say ""
say "下一步："
say "  cloudflared tunnel login"
say "  cloudflared tunnel create <名字>"
say "  cloudflared service install <token>   # 用 token 直接跑"
