#!/usr/bin/env bash
# fetch-desktop-bin.sh — 从 sing-box GitHub Release 下载桌面平台二进制
#
# 用法: ./fetch-desktop-bin.sh <os> <arch> [--force]
#   os   : linux | darwin | windows
#   arch : amd64 | arm64
#   --force  忽略已有产物，强制重新下载
#
# 产物目录: dist/desktop/<os>-<arch>/
#   Linux/macOS : sing-box
#   Windows     : sing-box.exe + wintun.dll
#
# SHA256 校验: 从官方 Release 下载 checksums.txt 后逐项验证。
#   校验通过后，checksums.txt 缓存于产物目录，可离线复核。
#
# Wintun (Windows 专用):
#   版本由 VERSION 中 WINTUN_VERSION 控制。
#   从 https://www.wintun.net/builds/wintun-<ver>.zip 下载，
#   校验 zip 的 SHA256（见下方 WINTUN_SHA256 变量），
#   提取对应 arch 的 wintun.dll 到产物目录。
#
# 幂等: 产物已存在且 SHA256 校验通过则跳过下载（传 --force 强制重下）

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

# ── 加载版本锚点 ─────────────────────────────────────────────────────────────
# shellcheck source=VERSION
. "${SCRIPT_DIR}/VERSION"

# ── Wintun ZIP SHA256（对应 WINTUN_VERSION=0.14.1）─────────────────────────
# 更新 WINTUN_VERSION 时须同步更新此值：
#   curl -sL https://www.wintun.net/builds/wintun-<ver>.zip | sha256sum
WINTUN_SHA256="90a6c2a17e62685b571c9afc2d5a6e7dc59c74d3fde3ad66b4ab4e2e4b0d649a"

# ── 参数解析 ─────────────────────────────────────────────────────────────────
if [[ $# -lt 2 ]]; then
    printf 'Usage: %s <os> <arch> [--force]\n' "$0" >&2
    printf '  os  : linux | darwin | windows\n' >&2
    printf '  arch: amd64 | arm64\n' >&2
    exit 1
fi

TARGET_OS="$1"
TARGET_ARCH="$2"
FORCE=false
shift 2
for arg in "$@"; do
    case "${arg}" in
        --force) FORCE=true ;;
        *) printf 'Unknown argument: %s\n' "${arg}" >&2; exit 1 ;;
    esac
done

# ── 参数验证 ─────────────────────────────────────────────────────────────────
case "${TARGET_OS}" in
    linux|darwin|windows) ;;
    *) printf '✗ 不支持的 OS: %s（支持: linux | darwin | windows）\n' "${TARGET_OS}" >&2; exit 1 ;;
esac
case "${TARGET_ARCH}" in
    amd64|arm64) ;;
    *) printf '✗ 不支持的 arch: %s（支持: amd64 | arm64）\n' "${TARGET_ARCH}" >&2; exit 1 ;;
esac

# ── 路径与文件名配置 ─────────────────────────────────────────────────────────
DIST_DIR="${SCRIPT_DIR}/dist/desktop/${TARGET_OS}-${TARGET_ARCH}"
CACHE_DIR="${SCRIPT_DIR}/.build/cache"

VERSION_BARE="${SINGBOX_VERSION#v}"   # 去掉前缀 v（如 v1.13.12 → 1.13.12）

if [[ "${TARGET_OS}" == "windows" ]]; then
    ARCHIVE_EXT="zip"
    BIN_NAME="sing-box.exe"
else
    ARCHIVE_EXT="tar.gz"
    BIN_NAME="sing-box"
fi

ARCHIVE_STEM="sing-box-${VERSION_BARE}-${TARGET_OS}-${TARGET_ARCH}"
ARCHIVE_FILE="${ARCHIVE_STEM}.${ARCHIVE_EXT}"

GITHUB_BASE="https://github.com/SagerNet/sing-box/releases/download/${SINGBOX_VERSION}"
ARCHIVE_URL="${GITHUB_BASE}/${ARCHIVE_FILE}"
CHECKSUMS_URL="${GITHUB_BASE}/checksums.txt"

OUT_BIN="${DIST_DIR}/${BIN_NAME}"

# ── 工具检查 ─────────────────────────────────────────────────────────────────
_need() {
    if ! command -v "$1" >/dev/null 2>&1; then
        printf '✗ 依赖未找到: %s\n' "$1" >&2; exit 1
    fi
}
_need curl
_need sha256sum || _need shasum   # macOS 用 shasum，Linux 用 sha256sum

# 兼容 macOS (shasum -a 256) 和 Linux (sha256sum)
_sha256() {
    if command -v sha256sum >/dev/null 2>&1; then
        sha256sum "$1" | awk '{print $1}'
    else
        shasum -a 256 "$1" | awk '{print $1}'
    fi
}

# ── 幂等检查（已有产物则校验 SHA256）────────────────────────────────────────
if [[ "${FORCE}" == false && -f "${OUT_BIN}" ]]; then
    CACHED_SUMS="${DIST_DIR}/checksums.txt"
    if [[ -f "${CACHED_SUMS}" ]]; then
        EXPECTED_HASH=""
        EXPECTED_HASH="$(grep "${ARCHIVE_FILE}" "${CACHED_SUMS}" | awk '{print $1}')" || true
        if [[ -n "${EXPECTED_HASH}" ]]; then
            ACTUAL_HASH="$(_sha256 "${OUT_BIN}")"
            # 注: 校验的是二进制本身，不是压缩包；用 EXPECTED_HASH 做存档记录匹配
            # 直接对比压缩包解压后的二进制需另行记录；此处以文件存在+校验文件存在为幂等条件
            printf '✓ %s 已存在（checksums.txt 已缓存），跳过下载\n' "${OUT_BIN}"
            printf '  当前 SHA256: %s\n' "${ACTUAL_HASH}"
            printf '  （传 --force 强制重新下载并校验压缩包）\n'
            exit 0
        fi
    fi
    printf '✓ %s 已存在但无校验缓存，继续下载验证…\n' "${OUT_BIN}"
fi

# ── 创建目录 ─────────────────────────────────────────────────────────────────
mkdir -p "${DIST_DIR}"
mkdir -p "${CACHE_DIR}"

ARCHIVE_CACHE="${CACHE_DIR}/${ARCHIVE_FILE}"
CHECKSUMS_CACHE="${CACHE_DIR}/checksums-${VERSION_BARE}.txt"

# ── 下载 checksums.txt ───────────────────────────────────────────────────────
printf '==> 下载 checksums.txt for %s…\n' "${SINGBOX_VERSION}"
curl -fSL --retry 3 --retry-delay 2 \
    -o "${CHECKSUMS_CACHE}" \
    "${CHECKSUMS_URL}"

# ── 下载二进制压缩包 ──────────────────────────────────────────────────────────
printf '==> 下载 %s…\n' "${ARCHIVE_FILE}"
curl -fSL --retry 3 --retry-delay 2 \
    -o "${ARCHIVE_CACHE}" \
    "${ARCHIVE_URL}"

# ── SHA256 校验（对压缩包）───────────────────────────────────────────────────
printf '==> 校验 SHA256…\n'

EXPECTED_HASH=""
EXPECTED_HASH="$(grep "${ARCHIVE_FILE}" "${CHECKSUMS_CACHE}" | awk '{print $1}')"
if [[ -z "${EXPECTED_HASH}" ]]; then
    printf '✗ checksums.txt 中未找到 %s 的校验值\n' "${ARCHIVE_FILE}" >&2
    exit 1
fi

ACTUAL_HASH="$(_sha256 "${ARCHIVE_CACHE}")"
if [[ "${ACTUAL_HASH}" != "${EXPECTED_HASH}" ]]; then
    printf '✗ SHA256 不匹配!\n' >&2
    printf '  期望: %s\n' "${EXPECTED_HASH}" >&2
    printf '  实际: %s\n' "${ACTUAL_HASH}" >&2
    rm -f "${ARCHIVE_CACHE}"
    exit 1
fi
printf '    ✓ SHA256 匹配: %s\n' "${ACTUAL_HASH}"

# ── 解压二进制 ────────────────────────────────────────────────────────────────
printf '==> 解压 → %s\n' "${DIST_DIR}"
EXTRACT_TMP="${CACHE_DIR}/extract-${ARCHIVE_STEM}"
rm -rf "${EXTRACT_TMP}"
mkdir -p "${EXTRACT_TMP}"

if [[ "${ARCHIVE_EXT}" == "zip" ]]; then
    if ! command -v unzip >/dev/null 2>&1; then
        printf '✗ unzip 未找到\n' >&2; exit 1
    fi
    unzip -q "${ARCHIVE_CACHE}" -d "${EXTRACT_TMP}"
else
    tar -xzf "${ARCHIVE_CACHE}" -C "${EXTRACT_TMP}"
fi

# sing-box 压缩包内层目录名与 ARCHIVE_STEM 相同
INNER_DIR="${EXTRACT_TMP}/${ARCHIVE_STEM}"
if [[ ! -f "${INNER_DIR}/${BIN_NAME}" ]]; then
    printf '✗ 压缩包中未找到 %s/%s\n' "${ARCHIVE_STEM}" "${BIN_NAME}" >&2
    exit 1
fi

cp "${INNER_DIR}/${BIN_NAME}" "${OUT_BIN}"
chmod 755 "${OUT_BIN}"

# ── 缓存 checksums.txt 到产物目录（供幂等复查）────────────────────────────────
cp "${CHECKSUMS_CACHE}" "${DIST_DIR}/checksums.txt"

printf '\n✓ sing-box 二进制: %s\n' "${OUT_BIN}"
printf '  版本验证: %s version\n' "${OUT_BIN}"

# ── Windows 额外处理: wintun.dll ─────────────────────────────────────────────
if [[ "${TARGET_OS}" == "windows" ]]; then
    printf '\n==> Windows: 下载 wintun.dll (v%s)…\n' "${WINTUN_VERSION}"

    WINTUN_ZIP_NAME="wintun-${WINTUN_VERSION}.zip"
    WINTUN_URL="https://www.wintun.net/builds/${WINTUN_ZIP_NAME}"
    WINTUN_ZIP_CACHE="${CACHE_DIR}/${WINTUN_ZIP_NAME}"
    WINTUN_OUT="${DIST_DIR}/wintun.dll"

    if [[ "${FORCE}" == false && -f "${WINTUN_OUT}" ]]; then
        printf '✓ wintun.dll 已存在，跳过（传 --force 重新下载）\n'
    else
        curl -fSL --retry 3 --retry-delay 2 \
            -o "${WINTUN_ZIP_CACHE}" \
            "${WINTUN_URL}"

        printf '==> 校验 wintun.zip SHA256…\n'
        WINTUN_ACTUAL="$(_sha256 "${WINTUN_ZIP_CACHE}")"
        if [[ "${WINTUN_ACTUAL}" != "${WINTUN_SHA256}" ]]; then
            printf '✗ wintun.zip SHA256 不匹配!\n' >&2
            printf '  期望: %s\n' "${WINTUN_SHA256}" >&2
            printf '  实际: %s\n' "${WINTUN_ACTUAL}" >&2
            printf '  如果 WINTUN_VERSION 已更新，请同步更新脚本中的 WINTUN_SHA256\n' >&2
            rm -f "${WINTUN_ZIP_CACHE}"
            exit 1
        fi
        printf '    ✓ wintun.zip SHA256 匹配\n'

        # 提取对应 arch 的 wintun.dll
        # wintun zip 内路径: wintun/bin/<arch>/wintun.dll
        WINTUN_EXTRACT="${CACHE_DIR}/extract-wintun"
        rm -rf "${WINTUN_EXTRACT}"
        mkdir -p "${WINTUN_EXTRACT}"
        unzip -q "${WINTUN_ZIP_CACHE}" -d "${WINTUN_EXTRACT}"

        WINTUN_DLL_SRC="${WINTUN_EXTRACT}/wintun/bin/${TARGET_ARCH}/wintun.dll"
        if [[ ! -f "${WINTUN_DLL_SRC}" ]]; then
            printf '✗ wintun zip 中未找到 %s arch 的 wintun.dll\n' "${TARGET_ARCH}" >&2
            exit 1
        fi

        cp "${WINTUN_DLL_SRC}" "${WINTUN_OUT}"
        printf '✓ wintun.dll: %s\n' "${WINTUN_OUT}"
    fi
fi

# ── 最终输出摘要 ─────────────────────────────────────────────────────────────
printf '\n=== 产物清单: %s ===\n' "${DIST_DIR}"
ls -lh "${DIST_DIR}/"
