#!/usr/bin/env bash
# =============================================================================
# QMDSearch NAS 端自动部署钩子 (post-receive)
#
# 这是真正的「持续部署(CD)」：当本地 deploy.sh 把代码 push 到 NAS 后，
# 此钩子自动完成 checkout → 按需构建 → 重启/重建容器 → 健康检查 → 失败回滚。
#
# 安装方式（二选一）：
#   A. 作为 bare 仓库的 hook（推荐，全自动）：
#      # 假设 NAS 上 bare 仓库在 /volume1/git/QMDSearch.git，工作树在 /volume1/docker/QMDSearch
#      cp scripts/nas-post-receive /volume1/git/QMDSearch.git/hooks/post-receive
#      chmod +x /volume1/git/QMDSearch.git/hooks/post-receive
#      # 在该 bare 仓库 config 中设置：
#      #   [core] bare = true
#      #   [hooks] 并确保工作树目录已 clone 过一次
#      # 首次需手动：git --work-tree=/volume1/docker/QMDSearch --git-dir=/volume1/git/QMDSearch.git checkout -f
#
#   B. 作为普通脚本手动触发（无 bare 仓库时）：
#      cd /volume1/docker/QMDSearch && git pull && sudo bash scripts/nas-post-receive
#
# 前置依赖（NAS 上需具备）：git、docker、docker compose、node/npm
# 可调环境变量：DEPLOY_DIR（工作树）、APP_PORT、LOG_FILE
# =============================================================================
set -uo pipefail

DEPLOY_DIR="${DEPLOY_DIR:-/volume1/docker/QMDSearch}"   # ← 改成你的 NAS 部署目录
GIT_DIR="$DEPLOY_DIR/.git"
LOG_FILE="${LOG_FILE:-$DEPLOY_DIR/deploy.log}"
APP_PORT="${APP_PORT:-8000}"
PREV_COMMIT_FILE="$DEPLOY_DIR/.last_deployed_commit"

log(){ printf '%s [nas-deploy] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" | tee -a "$LOG_FILE"; }

rollback(){
  log "回滚到上一稳定提交: ${PREV:-<无>}"
  if [[ -n "${PREV:-}" ]]; then
    git --work-tree="$DEPLOY_DIR" --git-dir="$GIT_DIR" checkout -f "$PREV"
    (cd "$DEPLOY_DIR" && docker compose restart app) | tee -a "$LOG_FILE"
    echo "$PREV" > "$PREV_COMMIT_FILE"
  fi
}

# 解析 post-receive 通过 stdin 传入的 <old> <new> <ref>
newrev=""; refname=""
while read -r oldrev newrev refname; do :; done
# 仅处理 main 分支的推送；手动运行时 newrev 为空，回退到 HEAD
if [[ -z "$newrev" ]]; then
  newrev="$(git --git-dir="$GIT_DIR" rev-parse HEAD 2>/dev/null || echo "")"
fi
if [[ -n "$refname" && "$refname" != "refs/heads/main" ]]; then
  log "忽略非 main 分支推送 ($refname)"
  exit 0
fi

log "=== 开始部署 (newrev=${newrev:0:8}) ==="

# 1) 检出到工作树
git --work-tree="$DEPLOY_DIR" --git-dir="$GIT_DIR" checkout -f
cd "$DEPLOY_DIR" || { log "无法进入 $DEPLOY_DIR"; exit 1; }

# 2) 计算变化范围
PREV="$(cat "$PREV_COMMIT_FILE" 2>/dev/null || echo "")"
if [[ -n "$PREV" && -n "$newrev" ]]; then
  CHANGED="$(git --git-dir="$GIT_DIR" diff --name-only "$PREV" "$newrev")"
else
  CHANGED="$(git --git-dir="$GIT_DIR" show --name-only --format= "$newrev")"
fi

has(){ echo "$CHANGED" | grep -qE "$1"; }
FE_CHANGED=0; DEPS_CHANGED=0
has '^frontend/(src/|public/|index\.html|package\.json|vite\.config|package-lock\.json)' && FE_CHANGED=1
has '^(pyproject\.toml|uv\.lock|Dockerfile)' && DEPS_CHANGED=1
log "变更范围 → frontend=$FE_CHANGED  deps=$DEPS_CHANGED"

# 3) 按需构建前端（dist 不入库，必须在此构建）
if [[ "$FE_CHANGED" -eq 1 ]]; then
  log "检测前端变化，重新构建..."
  if ! (cd frontend && npm ci --no-audit --no-fund && npm run build) 2>&1 | tee -a "$LOG_FILE"; then
    log "前端构建失败，中止部署（保留上一版本）"; exit 1
  fi
  ok(){ :; }; log "前端构建完成"
fi

# 4) 部署容器
if [[ "$DEPS_CHANGED" -eq 1 ]]; then
  log "依赖变化，重建镜像并重启: docker compose up -d --build app"
  docker compose up -d --build app 2>&1 | tee -a "$LOG_FILE"
else
  log "仅代码变化，重启 app 服务: docker compose restart app"
  docker compose restart app 2>&1 | tee -a "$LOG_FILE"
fi

# 5) 健康检查（失败自动回滚）
log "健康检查 http://localhost:$APP_PORT/health ..."
for i in $(seq 1 15); do
  if curl -fsS "http://localhost:$APP_PORT/health" >/dev/null 2>&1; then
    log "健康检查通过 ✓"
    [[ -n "$newrev" ]] && echo "$newrev" > "$PREV_COMMIT_FILE"
    log "=== 部署完成 ==="
    exit 0
  fi
  sleep 2
done

log "健康检查失败，触发回滚"
rollback
exit 1
