#!/bin/sh
# Post-receive hook: push 后自动 build + start
#

# pnpm 环境变量
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

# git 调钩子时 cwd=GIT_DIR 并设了 GIT_DIR=.（相对）。此时：
#  1. `git rev-parse --show-toplevel` 返回 `/.../repo/.git`（不报错），
#     会让原版 `|| echo $PWD` 退路不触发、cd 误进 .git。
#  2. cd 到工作树后，GIT_DIR=. 不再指向 .git，下面 `git reset` 之类的
#     会报 `fatal: not a git repository: '.'`。
# 修正：拿绝对 git-dir，算出 worktree 根，并改用绝对 GIT_DIR。
GIT_DIR_ABS="$(git rev-parse --absolute-git-dir 2>/dev/null)"
if [ -n "$GIT_DIR_ABS" ] && [ -d "$GIT_DIR_ABS" ]; then
    REPO_TOPLEVEL="$(cd "$GIT_DIR_ABS/.." && pwd)"
    export GIT_DIR="$GIT_DIR_ABS"
else
    REPO_TOPLEVEL="$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")"
fi
unset GIT_WORK_TREE
cd "$REPO_TOPLEVEL" || { echo "FATAL: cannot cd to $REPO_TOPLEVEL" >&2; exit 1; }
echo "=== REPO_TOPLEVEL=$REPO_TOPLEVEL (GIT_DIR=$GIT_DIR) ===" >&2

# ============================================================
# 0. sync_worktree —— 将工作树同步到新 push 的 HEAD
#   背景：服务器 .git/config 设了 receive.denyCurrentBranch=updateInstead 时
#        git 自动更新工作树但会跳过此钩子；改为 false 后
#        git 不会更新工作树，手动 reset --hard 同步才能让下面
#        的 build 用上新代码。
# ============================================================
sync_worktree() {
    echo "=== sync_worktree: git reset --hard HEAD @ $(date +%Y-%m-%dT%H:%M:%S%z) ==="
    git reset --hard HEAD
}

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

# ============================================================
# 2. write_metadata_header —— 覆盖写入元信息（首次写 → 覆盖让日志开头是这个）
# ============================================================
write_metadata_header() {
    {
        echo "=== post-receive @ $(date +%Y-%m-%dT%H:%M:%S%z) ==="
        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 install` + `pnpm run build`，输出丢，捕获退出码写日志
#   返回：build 退出码（main 用 if ! run_build 决定是否继续）
# ============================================================
run_build() {
    echo "=== build START @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
    pnpm run build:deploy > /dev/null 2>&1
    BUILD_EXIT=$?
    echo "=== build EXIT_CODE=$BUILD_EXIT @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$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 +%Y-%m-%dT%H:%M:%S%z) ===" >> "$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 +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
    echo "" >> "$LOG_FILE"
}

# ============================================================
# 6. launch_next —— 后台启 `pnpm run start`，捕 PID
# ============================================================
launch_next() {
    # next stdout/stderr 接到 logs/next-server.log（覆盖模式，每次 deploy 重新开始）
    NEXT_LOG_FILE="$REPO_TOPLEVEL/logs/next-server.log"
    echo "=== start LAUNCH @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
    nohup pnpm run start > "$NEXT_LOG_FILE" 2>&1 &
    START_PID=$!
    echo "=== start PID=$START_PID @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
}

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

# ============================================================
# main —— 调度所有步骤
# ============================================================
main() {
    init_log_file
    sync_worktree            # 先同步工作树到新 HEAD（与 git config denyCurrentBranch 配合）
    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
