#!/bin/sh
# Post-receive hook: push 后自动 build + start
#
# 触发场景：
#   - 非 bare repo + receive.denyCurrentBranch=updateInstead：本地推送自动部署
#
# 用途：执行 package.json 中的 `build` + `start` 脚本
#   - 实际调用 `pnpm run build`（输出丢，仅记退出码）
#   - 再后台启 `pnpm run start`（nohup，输出丢，hook 立即返回）
#
# 设计：逻辑块**封**装为独立函数，main 调**度**（shell 风格）
#   - **全**局变量：LOG_FILE（在 init_log_file **里**定**义**）、CURRENT_BRANCH
#   - **函**数**间**通过 `$LOG_FILE` / `$BUILD_EXIT` 通信
#   - **不**用 set -e —— 各步**自**己处理错**误**（env 不**存**在 / build 失败 / 旧**进**程**不**存**在）→ 让 deploy 错**误****不**挡 git push
#
# 关键前置（**解**决 push 后 hook **不**触**发** + pnpm **找**不**到**）：
#   1) cd 到 repo **顶**层 —— git-receive-pack 调 hook 时 CWD **未**必 = repo 顶
#   2) 加**载** nvm —— 非交互 shell **不**会 source .bashrc，pnpm **不**在 PATH
#   3) **验**证 PATH —— 写日**志**确认 pnpm 可**用**（**不**可用则 build **必**失**败**）

# ---- 前置：切到 repo 顶 + 加 pnpm 到 PATH ----
# 重要：git-receive-pack 调 hook 时 CWD **可**能是 `.git/` 内**部**，
# `git rev-parse --show-toplevel` 会**报**错 `must be run in a work tree`。
# 解**法**：**用** `git rev-parse --absolute-git-dir` **取**得**绝**对 .git **路**径，
# 然后 dirname **得**到 repo **顶**层。**这**两个调**用**在 .git/ 内**部**也都**能**成**功**。
GIT_DIR_ABS="$(git rev-parse --absolute-git-dir 2>/dev/null || true)"
if [ -n "$GIT_DIR_ABS" ] && [ "$GIT_DIR_ABS" != ".git" ]; then
    REPO_TOPLEVEL="$(cd "$GIT_DIR_ABS/.." && pwd)"
else
    # 兜底：**用** PWD （**非** bare repo 默**认**钩**子**在 **工**作**树**下）
    REPO_TOPLEVEL="$PWD"
fi
cd "$REPO_TOPLEVEL" || { echo "FATAL: cannot cd to $REPO_TOPLEVEL" >&2; exit 1; }

# 加**载** nvm（**非**交互 shell **不**自动 source .bashrc，pnpm **所**以**找**不**到**）
# 同 .bashrc **里**的 NVM **加**载逻辑
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

# ============================================================
# 1. init_log_file —— 定**义** LOG_FILE + 创**建** logs/ 目**录**
# ============================================================
init_log_file() {
    LOG_FILE="$(git rev-parse --show-toplevel)/logs/post-receive.log"
    mkdir -p "$(dirname "$LOG_FILE")"
}

# ============================================================
# 2. write_metadata_header —— **覆**盖写**入**元信息（**首**次**写** → **覆**盖**让日志**开**头是**这**个）
# ============================================================
write_metadata_header() {
    {
        echo "=== post-receive @ $(date -u +%Y-%m-%dT%H:%M:%SZ) ==="
        echo "branch:  $(git rev-parse --abbrev-ref HEAD)"
        echo "commit:  $(git rev-parse --short HEAD)"
        echo "cwd:     $(pwd)"
        echo ""
    } > "$LOG_FILE"
}

# ============================================================
# 3. copy_env_by_branch —— 根**据**分支选 env-example → .env
#   main 分支 → .env.production（prod 部署）
#   test 分支 → .env.local（test **不**碰 .env.production，**避**免**读**到 prod 域名）
#   dev 分支  → .env.local（**未**配远端**用**途，**但**保**留**）
# ============================================================
copy_env_by_branch() {
    CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
    echo "=== branch: $CURRENT_BRANCH ===" >> "$LOG_FILE"
    case "$CURRENT_BRANCH" in
        main)
            cp -f env-example/.env.production.example .env.production
            echo "=== env: copied .env.production (main → prod) ===" >> "$LOG_FILE"
            ;;
        test)
            cp -f env-example/.env.local.example .env.local
            echo "=== env: copied .env.local (test → test) ===" >> "$LOG_FILE"
            ;;
        dev)
            cp -f env-example/.env.development.example .env.local
            echo "=== env: copied .env.local (dev → dev) ===" >> "$LOG_FILE"
            ;;
        *)
            echo "=== env: UNKNOWN branch '$CURRENT_BRANCH', skip env copy ===" >> "$LOG_FILE"
            ;;
    esac
}

# ============================================================
# 4. run_build —— 跑 `pnpm run build`，输出**丢**，**捕**获退出码写日志
#   返回：build 退出码（main **用** if ! run_build 决**定**是否**继**续）
# ============================================================
run_build() {
    echo "=== build START @ $(date -u +%Y-%m-%dT%H:%M:%SZ) ===" >> "$LOG_FILE"
    pnpm run build > /dev/null 2>&1
    BUILD_EXIT=$?
    echo "=== build EXIT_CODE=$BUILD_EXIT @ $(date -u +%Y-%m-%dT%H:%M:%SZ) ===" >> "$LOG_FILE"
    echo "" >> "$LOG_FILE"
    return $BUILD_EXIT
}

# ============================================================
# 5. stop_existing_next —— 两阶段 kill + 等 1s 确**保**端口**完**全释放
#   1) 优**雅**关**闭**（SIGTERM）—— 旧**进**程**自**己 drain
#   2) 等 2s
#   3) 强杀（SIGKILL）—— **还**在**的**话
#   4) 等 1s
# ============================================================
stop_existing_next() {
    echo "=== stop existing next @ $(date -u +%Y-%m-%dT%H:%M:%SZ) ===" >> "$LOG_FILE"
    pkill -f "next start" 2>/dev/null
    pkill -f "next-server" 2>/dev/null
    sleep 2
    pkill -9 -f "next start" 2>/dev/null
    pkill -9 -f "next-server" 2>/dev/null
    sleep 1
    echo "=== stop DONE @ $(date -u +%Y-%m-%dT%H:%M:%SZ) ===" >> "$LOG_FILE"
    echo "" >> "$LOG_FILE"
}

# ============================================================
# 6. launch_next —— 后**台**启 `pnpm run start`，捕 PID
# ============================================================
launch_next() {
    echo "=== start LAUNCH @ $(date -u +%Y-%m-%dT%H:%M:%SZ) ===" >> "$LOG_FILE"
    nohup pnpm run start > /dev/null 2>&1 &
    START_PID=$!
    echo "=== start PID=$START_PID @ $(date -u +%Y-%m-%dT%H:%M:%SZ) ===" >> "$LOG_FILE"
}

# ============================================================
# 7. log_abort —— build 失败**时**写**结**束**标**记（**不**输**出** START LAUNCH）
# ============================================================
log_abort() {
    echo "=== ABORTED @ $(date -u +%Y-%m-%dT%H:%M:%SZ) (build failed, start skipped) ===" >> "$LOG_FILE"
}

# ============================================================
# main —— 调**度**所**有**步骤
# ============================================================
main() {
    init_log_file
    write_metadata_header    # **先**覆盖写元信息（**这**样 env copy 日**志****能**保留在**后**面）
    copy_env_by_branch       # **追**加 env copy 日志

    if ! run_build; then
        log_abort
        exit $BUILD_EXIT       # build 失败 → **不**启 start（**避**免**用**旧 .next **跑**新代码）
    fi

    stop_existing_next        # build 成功 → **先**关旧**进**程
    launch_next               # 再启新
}

main "$@"

exit 0
