#!/usr/bin/env bash
# build-ios.sh — gomobile bind sing-box libbox → dist/ios/Libbox.xcframework
#
# 用法: ./build-ios.sh [--force]
#   --force  忽略已有产物，强制重新构建
#
# 依赖:
#   - macOS + Xcode（含 iOS SDK & Simulator SDK）
#   - Go : VERSION 中 GO_VERSION 指定的版本
#   - gomobile: 脚本自动安装至 $GOPATH/bin
#
# 产物: dist/ios/Libbox.xcframework
#   含两个 slice: iphoneos (device) + iphonesimulator (x86_64 + arm64)
#
# Build tags 说明（对齐官方 SFI — sing-box for iOS）:
#   with_quic      — QUIC/HTTP3 协议支持（Hysteria2 必须）
#   with_utls      — uTLS 指纹模拟（TLS 防检测）
#   with_clash_api — Clash 兼容 API（面板控制必须）
#   with_gvisor    — gVisor TUN 网络栈
#
# ⚠ iOS Network Extension 内存上限敏感
#   11F 实测若遇 "NEProvider memory limit exceeded" 崩溃，
#   回此脚本逐步裁剪 BUILD_TAGS，推荐优先裁减顺序：
#     1. 去掉 with_grpc   → 减约 4 MB（需 gRPC 入站时保留）
#     2. 去掉 with_gvisor → 减约 2 MB（失去 gVisor TUN stack 回退，改用系统栈）
#     3. 去掉 with_quic   → 仅在完全不使用 Hysteria2/QUIC 时考虑
#   裁减后执行 ./build-ios.sh --force 重出包，重新接入 Flutter 工程（见 11F）。

set -euo pipefail

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

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

# ── 参数解析 ─────────────────────────────────────────────────────────────────
FORCE=false
for arg in "$@"; do
    case "${arg}" in
        --force) FORCE=true ;;
        *) printf 'Unknown argument: %s\n' "${arg}" >&2; exit 1 ;;
    esac
done

# ── 路径配置 ─────────────────────────────────────────────────────────────────
DIST_DIR="${SCRIPT_DIR}/dist/ios"
OUT_XCFW="${DIST_DIR}/Libbox.xcframework"
BUILD_SRC="${SCRIPT_DIR}/.build/singbox"

# ── Build tags ────────────────────────────────────────────────────────────────
BUILD_TAGS="with_quic,with_utls,with_clash_api,with_gvisor"
# 取消注释以增加功能（会增大 XCFramework 体积 / 内存占用）:
# BUILD_TAGS="${BUILD_TAGS},with_grpc"
# BUILD_TAGS="${BUILD_TAGS},with_ech"
# BUILD_TAGS="${BUILD_TAGS},with_wireguard"

# ── M3 内存裁剪决策记录（tsk_nsobbj_rJdy0）────────────────────────────────────
# NE 进程内存上限实测（MemoryMonitor 打点）：
#   初始 build tags（with_quic,with_utls,with_clash_api,with_gvisor）：
#     预估 RSS：~20-25 MB（含 Go runtime ~8MB + gVisor ~4MB + QUIC ~5MB + uTLS ~2MB + 基础~6MB）
#     旧设备（≤ A9）15MB 上限下会触顶；新设备（≥ A12）50MB 上限下可运行
#
#   若真机测试出现 "NEProvider memory limit exceeded" / jetsam 杀进程，按以下顺序裁减：
#     Step 1: 去掉 with_gvisor → 改用系统 TUN 网络栈；减约 2-4 MB
#             取消注释下一行：
# BUILD_TAGS="with_quic,with_utls,with_clash_api"
#     Step 2: 去掉 with_grpc  → 若不需要 gRPC 入站；减约 4 MB
#             取消注释下一行：
# BUILD_TAGS="with_quic,with_utls,with_clash_api"   # 已无 gvisor，此行等效
#     Step 3: 去掉 with_quic  → 仅在放弃 Hysteria2/QUIC 协议时考虑；减约 3 MB
#             取消注释下一行：
# BUILD_TAGS="with_utls,with_clash_api"
#
#   裁减后执行: ./build-ios.sh --force 重出 XCFramework，然后在 Xcode 中
#   重新拖入 dist/ios/Libbox.xcframework（或更新 SPM/Pods 引用）。
#   在此注释中记录最终选定的 build tags 及峰值 RSS（由 MemoryMonitor 汇报）。
#
#   当前状态：等待真机 10min 压测结果（MemoryMonitor 每 10s 打点）。

# ── 幂等检查 ─────────────────────────────────────────────────────────────────
if [[ "${FORCE}" == false && -d "${OUT_XCFW}" ]]; then
    printf '✓ %s 已存在，跳过构建（传 --force 强制重建）\n' "${OUT_XCFW}"
    exit 0
fi

# ── 前置检查 ─────────────────────────────────────────────────────────────────
printf '==> 检查前置依赖…\n'

if [[ "$(uname)" != "Darwin" ]]; then
    printf '✗ iOS 构建仅支持 macOS\n' >&2
    exit 1
fi
if ! command -v xcodebuild >/dev/null 2>&1; then
    printf '✗ xcodebuild 未找到，请安装 Xcode\n' >&2
    exit 1
fi
if ! command -v go >/dev/null 2>&1; then
    printf '✗ go 未找到，请安装 Go %s\n' "${GO_VERSION}" >&2
    exit 1
fi
if ! command -v git >/dev/null 2>&1; then
    printf '✗ git 未找到\n' >&2
    exit 1
fi

ACTUAL_GO="$(go version | awk '{print $3}' | sed 's/go//')"
XCODE_VER="$(xcodebuild -version 2>/dev/null | head -1)"
printf '    Go 版本: %s（期望 %s）\n' "${ACTUAL_GO}" "${GO_VERSION}"
printf '    Xcode: %s\n' "${XCODE_VER}"

# ── 安装 gomobile ─────────────────────────────────────────────────────────────
printf '==> 安装 gomobile@%s…\n' "${GOMOBILE_VERSION}"
go install "golang.org/x/mobile/cmd/gomobile@${GOMOBILE_VERSION}"
go install "golang.org/x/mobile/cmd/gobind@${GOMOBILE_VERSION}"

printf '==> gomobile init…\n'
gomobile init

# ── 准备 sing-box 源码 ────────────────────────────────────────────────────────
printf '==> 准备 sing-box %s 源码…\n' "${SINGBOX_VERSION}"

EXISTING_TAG=""
if [[ -d "${BUILD_SRC}/.git" ]]; then
    EXISTING_TAG="$(git -C "${BUILD_SRC}" describe --tags --exact-match 2>/dev/null)" || EXISTING_TAG="unknown"
fi

if [[ "${EXISTING_TAG}" == "${SINGBOX_VERSION}" ]]; then
    printf '    源码已是 %s，跳过克隆\n' "${SINGBOX_VERSION}"
else
    if [[ -d "${BUILD_SRC}" ]]; then
        printf '    已有源码版本 %s ≠ %s，清理后重新克隆…\n' "${EXISTING_TAG}" "${SINGBOX_VERSION}"
        rm -rf "${BUILD_SRC}"
    fi
    mkdir -p "${BUILD_SRC%/*}"
    git clone --depth 1 --branch "${SINGBOX_VERSION}" \
        https://github.com/SagerNet/sing-box.git "${BUILD_SRC}"
fi

# ── 构建 XCFramework ──────────────────────────────────────────────────────────
printf '==> 构建 Libbox.xcframework\n'
printf '    target : ios,iossimulator\n'
printf '    tags   : %s\n' "${BUILD_TAGS}"

mkdir -p "${DIST_DIR}"

# 若已有旧产物先清理（gomobile 输出到固定路径时不会自动覆盖目录）
if [[ -d "${OUT_XCFW}" ]]; then
    rm -rf "${OUT_XCFW}"
fi

(
    cd "${BUILD_SRC}"
    go mod download
    gomobile bind \
        -target ios,iossimulator \
        -tags "${BUILD_TAGS}" \
        -o "${OUT_XCFW}" \
        ./experimental/libbox
)

printf '\n✓ 构建完成: %s\n' "${OUT_XCFW}"
printf '  Slice 验证:\n'
if command -v xcodebuild >/dev/null 2>&1; then
    xcodebuild -create-xcframework -output /dev/null 2>&1 || true
fi
# 列出 XCFramework 内的 framework slice 目录
for slice_dir in "${OUT_XCFW}"/*/; do
    printf '    %s\n' "${slice_dir}"
done
