#!/usr/bin/env bash
# 重建/更新「打过 grok unknown-session-id 补丁」的 ZACP 二进制，并原子替换 + 重启 pm2。
#
# 用途：官方 install.sh 升级 zacp 会把补丁覆盖掉（~/.local/bin/zacp 指回官方新版），
#       之后「离开较长时间再发消息」会重新报
#       `prompt: {"code":-32602,...,"data":"unknown session id"}`。
#       届时把 VERSION 改成新版号跑本脚本即可。
#
# 用法：
#   bash rebuild-zacp-grokfix.sh              # 默认 0.9.0
#   VERSION=0.9.1 bash rebuild-zacp-grokfix.sh
#
# 依赖（本机已装，缺失时脚本会从国内镜像自动下载）：
#   Go 1.25.7  -> ~/.local/go1.25.7      （go.mod 要求 go 1.25.7，apt 的 1.22 不够）
#   bun 1.4.2  -> ~/.bun/bin/bun         （上游 scripts/build.sh 强制要求 bun）
set -euo pipefail

VERSION="${VERSION:-0.9.0}"
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# 本目录下所有 *.patch 依次应用（当前两个：grok 会话失效判定、聊天图片工作区直链）
PATCH_DIR="$HERE"
TEST_SRC="$HERE/session_err_test.go"

GO_DIR="${GO_DIR:-$HOME/.local/go1.25.7}"
BUN_BIN_DIR="${BUN_BIN_DIR:-$HOME/.bun/bin}"
WORK="${WORK:-/tmp/zacp-rebuild}"
SRC="$WORK/zacp-src"
GO_TGZ="${GO_TGZ:-https://mirrors.aliyun.com/golang/go1.25.7.linux-amd64.tar.gz}"
BUN_ZIP="${BUN_ZIP:-https://registry.npmmirror.com/-/binary/bun/bun-v1.4.2/bun-linux-x64.zip}"
NPM_REGISTRY="${NPM_REGISTRY:-https://registry.npmmirror.com}"
GOPROXY_="${GOPROXY_:-https://goproxy.cn,direct}"

say() { printf '\n==> %s\n' "$*"; }

# ---------- 1. 工具链 ----------
if [ ! -x "$GO_DIR/bin/go" ]; then
  say "安装 Go 1.25.7（$GO_TGZ）"
  mkdir -p "$GO_DIR" "$WORK"
  curl -fsSL --noproxy '*' -o "$WORK/go.tgz" "$GO_TGZ"
  tar -xzf "$WORK/go.tgz" -C "$GO_DIR" --strip-components=1
fi
if [ ! -x "$BUN_BIN_DIR/bun" ]; then
  say "安装 bun（$BUN_ZIP）"
  mkdir -p "$BUN_BIN_DIR" "$WORK"
  curl -fsSL --noproxy '*' -o "$WORK/bun.zip" "$BUN_ZIP"
  unzip -o -q "$WORK/bun.zip" -d "$WORK/bun-x"
  cp "$(find "$WORK/bun-x" -type f -name bun | head -1)" "$BUN_BIN_DIR/bun"
  chmod +x "$BUN_BIN_DIR/bun"
fi
export PATH="$BUN_BIN_DIR:$GO_DIR/bin:$PATH"
echo "go  : $(go version)"
echo "bun : $(bun --version)"

# ---------- 2. 源码 ----------
mkdir -p "$WORK"
if [ -d "$SRC/.git" ]; then
  say "更新源码到 v$VERSION"
  git -C "$SRC" fetch --depth 1 origin "refs/tags/v$VERSION:refs/tags/v$VERSION" 2>/dev/null || true
  git -C "$SRC" checkout -f "v$VERSION"
else
  say "克隆 zacp v$VERSION"
  git clone --depth 1 --branch "v$VERSION" https://github.com/helloxz/zacp.git "$SRC"
fi

# ---------- 3. 打补丁（先重置工作区，保证可反复执行） ----------
say "重置源码工作区并应用补丁（$PATCH_DIR/*.patch）"
git -C "$SRC" checkout -f "v$VERSION" >/dev/null
# 清掉上一次补丁新增的未跟踪文件；排除 node_modules/dist/bin，避免重复装依赖/构建产物误删
git -C "$SRC" clean -fdq -e node_modules -e dist -e bin -- backend frontend
for PATCH in "$PATCH_DIR"/*.patch; do
  name="$(basename "$PATCH")"
  if git -C "$SRC" apply "$PATCH"; then
    echo "已应用 $name"
  else
    echo "错误：$name 无法应用——上游代码可能已改动，请人工核对" >&2
    exit 1
  fi
done
cp "$TEST_SRC" "$SRC/backend/internal/acp/manager/session_err_test.go"

# ---------- 4. 构建 ----------
say "构建（前端 bun + 后端 go，约 1~2 分钟）"
( cd "$SRC" && GOFLAGS=-mod=mod GOPROXY="$GOPROXY_" bash scripts/build.sh )
OUT="$SRC/backend/bin/zacp-v${VERSION}-linux-amd64"
[ -x "$OUT" ] || { echo "错误：未找到构建产物 $OUT" >&2; exit 1; }

say "跑回归测试"
( cd "$SRC/backend" && GOFLAGS=-mod=mod go test ./internal/acp/manager/ -run TestIsUnknownSessionErr -count=1 )

# ---------- 5. 部署（原子替换，运行中的进程持旧 inode 不受影响） ----------
TARGET="$HOME/.zacp/bin/zacp-${VERSION}-grokfix"
say "部署到 $TARGET"
mkdir -p "$(dirname "$TARGET")"
cp "$OUT" "$TARGET.new"
chmod 755 "$TARGET.new"
mv -f "$TARGET.new" "$TARGET"
ln -sfn "$TARGET" "$HOME/.local/bin/zacp"
ls -l "$HOME/.local/bin/zacp"

say "重启 pm2 并自检"
pm2 restart zacp
# 冷启动不是瞬时的：43MB 二进制 + sqlite 迁移，通常 5~10s 才监听端口（这里最多等 40s）
ok=""
for i in $(seq 1 20); do
  sleep 2
  if ok="$(curl -sS --noproxy '*' -m 5 http://127.0.0.1:8680/api/v1/version 2>/dev/null)" && [ -n "$ok" ]; then
    echo "第 ${i} 次探测（$((i * 2))s）成功：$ok"
    break
  fi
done
if [ -z "$ok" ]; then
  echo "错误：zacp 40s 内未监听 :8680，检查 ~/.pm2/logs/zacp-error.log" >&2
  exit 1
fi
echo
echo "完成。回滚：ln -sfn \$HOME/.zacp/bin/zacp-${VERSION} \$HOME/.local/bin/zacp && pm2 restart zacp"
