#!/usr/bin/env bash
# ============================================================
# build_binary.sh — 用 Nuitka 把整个 agentpaas 打成单个原生可执行文件
# (standalone + onefile)。用户拿到后【无需安装 Python】，直接 ./agentpaas serve。
#
# 与 --build-secure(.so wheel)的区别：
#   --build-secure : 仍是 wheel，用户要有匹配的 Python；保护的是源码
#   --build-binary : 单文件二进制，内嵌 Python + 依赖 + 前端 + 内置包；保护源码 + 免装环境
#
# 产物：dist-binary/agentpaas（绑定「平台 + 架构」，如 macOS arm64）。
#
# 用法:  scripts/build_binary.sh [PYTHON]
#   PYTHON 默认 python3，须为原生架构、3.10+、带头文件（macOS 推荐 Homebrew python3.11）。
# 环境:
#   PROVIDERS  要打进二进制的 LLM SDK，空格分隔，默认 "anthropic openai"
#              （dashscope 走 openai 兼容/HTTP、ollama 走 HTTP，均无需 SDK）
#   ONEFILE    1=单文件(默认，体验最好但打包耗资源)；0=standalone 目录
#              (产出一个文件夹，更省内存、更稳，适合 CI/资源受限机器)
#
# 前置:  C 编译器（macOS: xcode-select --install）
# ============================================================
set -e

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
cd "$PROJECT_DIR"

RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
info()  { echo -e "${BLUE}[INFO]${NC}  $1"; }
ok()    { echo -e "${GREEN}[OK]${NC}    $1"; }
warn()  { echo -e "${YELLOW}[WARN]${NC}  $1"; }
fail()  { echo -e "${RED}[FAIL]${NC}  $1"; exit 1; }

PYTHON="${1:-python3}"
PROVIDERS="${PROVIDERS:-anthropic openai}"
ONEFILE="${ONEFILE:-1}"
command -v "$PYTHON" >/dev/null 2>&1 || fail "未找到 Python: $PYTHON"

# ── 架构/版本/头文件检查（与 build_secure 一致）──────────────
read -r PYARCH HAVE_H <<EOF
$("$PYTHON" - <<'PY'
import platform, sysconfig, os
inc = sysconfig.get_path('include')
print(f"{platform.machine()} {int(os.path.exists(os.path.join(inc,'Python.h')))}")
PY
)
EOF
HOSTARCH="$(uname -m)"
[ "$PYARCH" = "$HOSTARCH" ] || fail "Python 架构($PYARCH) ≠ 主机($HOSTARCH)。请用原生 $HOSTARCH 的 Python（如 Homebrew python3.11），不要用 Rosetta 下的 x86_64 Python。"
[ "$HAVE_H" = "1" ] || fail "缺 Python.h 头文件。请装带头文件的 Python（如 Homebrew）。"

# ── 构建专用 venv：装 nuitka + 应用 + provider SDK ─────────
BUILD_VENV="$PROJECT_DIR/.nuitka-venv"
[ -x "$BUILD_VENV/bin/python" ] || "$PYTHON" -m venv "$BUILD_VENV"
BPY="$BUILD_VENV/bin/python"
info "准备构建环境（装 nuitka + 应用 + provider）..."
"$BPY" -m pip install -q --upgrade pip nuitka >/dev/null
# onefile 是 standalone：依赖必须真实安装进构建环境，Nuitka 才能跟进打包
"$BPY" -m pip install -q ./lambdagent ./agentpaas $PROVIDERS

# 仅对真正装上的 provider 生成 --include-module
PROVIDER_FLAGS=()
for p in $PROVIDERS; do
    if "$BPY" -c "import $p" >/dev/null 2>&1; then
        PROVIDER_FLAGS+=("--include-module=$p")
        ok "provider 已纳入: $p"
    else
        warn "provider 未装上，跳过: $p"
    fi
done

# ── Nuitka standalone + onefile ───────────────────────────
OUT="$PROJECT_DIR/build_binary_out"
rm -rf "$OUT"; mkdir -p "$OUT"
ENTRY="agentpaas/src/agentpaas/__main__.py"

MODE_FLAGS=(--standalone)
[ "$ONEFILE" = "1" ] && MODE_FLAGS+=(--onefile)
info "Nuitka 打包二进制（mode: $([ "$ONEFILE" = 1 ] && echo onefile单文件 || echo standalone目录)，首次可能 10+ 分钟）..."
"$BPY" -m nuitka \
    "${MODE_FLAGS[@]}" \
    --output-filename=agentpaas \
    --output-dir="$OUT" \
    --include-package=uvicorn \
    --include-module=uvloop --include-module=httptools \
    --include-module=websockets --include-module=h11 \
    --include-package=agentpaas --include-package=lambdagent \
    "${PROVIDER_FLAGS[@]}" \
    --include-data-dir=webui-dist=webui-dist \
    --include-data-dir=agentexample/agentpacks=agentexample/agentpacks \
    --include-data-dir=agentexample/skills=agentexample/skills \
    --python-flag=no_docstrings \
    --assume-yes-for-downloads \
    "$ENTRY"

# 平台标签 + 可执行后缀（Windows 走 git-bash，uname 形如 MINGW64_NT）
case "$(uname -s)" in
    MINGW*|MSYS*|CYGWIN*) OSLABEL=windows; EXE=".exe" ;;
    Darwin)               OSLABEL=darwin;  EXE="" ;;
    *)                    OSLABEL=$(uname -s | tr 'A-Z' 'a-z'); EXE="" ;;
esac
PLATSUFFIX="$OSLABEL-$HOSTARCH"
mkdir -p dist-binary
if [ "$ONEFILE" = "1" ]; then
    BIN="$OUT/agentpaas$EXE"
    [ -x "$BIN" ] || fail "未生成二进制"
    cp "$BIN" "dist-binary/agentpaas-$PLATSUFFIX$EXE"
    cp "$BIN" "dist-binary/agentpaas$EXE"
    echo ""
    ok "单文件二进制已生成 → dist-binary/agentpaas$EXE  ($(du -h "$BIN" | cut -f1))"
    echo "  用户用法（无需 Python）:  ./agentpaas$EXE serve --port 8000"
else
    # standalone：产出一个目录，整目录即发行件
    DISTDIR="$OUT/__main__.dist"
    [ -x "$DISTDIR/agentpaas$EXE" ] || fail "未生成 standalone 目录二进制"
    rm -rf "dist-binary/agentpaas-$PLATSUFFIX.app"
    cp -R "$DISTDIR" "dist-binary/agentpaas-$PLATSUFFIX.app"
    echo ""
    ok "standalone 目录已生成 → dist-binary/agentpaas-$PLATSUFFIX.app/  ($(du -sh "$DISTDIR" | cut -f1))"
    echo "  用户用法（无需 Python）:  ./agentpaas-$PLATSUFFIX.app/agentpaas$EXE serve --port 8000"
fi
echo ""
warn "提醒: 二进制绑定 $PLATSUFFIX —— 换平台/架构需在对应机器重新构建。"
warn "      未打包: chromadb 向量库、PDF 外部工具(pandoc/xelatex)——用到这些功能仍需系统侧依赖。"
