#!/bin/sh
# Post-receive hook: push 后自动部署容器
# 默认在服务器本机构建镜像；配置 .deploy.env 后也可从镜像仓库拉取 CI 构建好的镜像。

REPO_TOPLEVEL=""
LOG_FILE=""
CURRENT_BRANCH=""
DEPLOY_ENV=""
ENV_FILE=""
START_PORT=""
IMAGE_TAG=""
IMAGE_SOURCE=""
REMOTE_IMAGE_TAG=""
CONTAINER_NAME=""
COMPOSE_PROJECT_NAME=""
COMPOSE_EXIT=0

resolve_repo_context() {
    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
}

init_log_file() {
    LOG_FILE="$REPO_TOPLEVEL/logs/post-receive.log"
    mkdir -p "$(dirname "$LOG_FILE")"
}

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"
}

sync_worktree() {
    echo "=== sync_worktree: git reset --hard HEAD @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
    git reset --hard HEAD >> "$LOG_FILE" 2>&1
}

load_deploy_config() {
    if [ ! -f ".deploy.env" ]; then
        echo "=== deploy config: .deploy.env not found, use defaults ===" >> "$LOG_FILE"
        return 0
    fi

    echo "=== deploy config: loading .deploy.env ===" >> "$LOG_FILE"
    set -a
    # shellcheck disable=SC1091
    . ./.deploy.env
    set +a
}

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
            ENV_FILE=".env.production"
            DEPLOY_ENV="prod"
            START_PORT=9185
            echo "=== env: copied .env.production (main → prod) ===" >> "$LOG_FILE"
            ;;
        pre)
            cp -f env-example/.env.local.example .env.local
            ENV_FILE=".env.local"
            DEPLOY_ENV="test"
            START_PORT=9135
            echo "=== env: copied .env.local (pre branch → test runtime) ===" >> "$LOG_FILE"
            ;;
        dev)
            cp -f env-example/.env.development.example .env.local
            ENV_FILE=".env.local"
            DEPLOY_ENV="dev"
            START_PORT=9135
            echo "=== env: copied .env.local (dev → dev) ===" >> "$LOG_FILE"
            ;;
        *)
            ENV_FILE=".env.local"
            DEPLOY_ENV="$CURRENT_BRANCH"
            START_PORT=9135
            echo "=== env: UNKNOWN branch '$CURRENT_BRANCH', use defaults ===" >> "$LOG_FILE"
            ;;
    esac
}

prepare_compose_env() {
    SHORT_SHA="$(git rev-parse --short HEAD)"
    IMAGE_SOURCE="${COZSWEET_DEPLOY_IMAGE_SOURCE:-build}"
    REMOTE_IMAGE_TAG="${COZSWEET_REMOTE_IMAGE_TAG:-${DEPLOY_ENV}-${SHORT_SHA}}"

    case "$IMAGE_SOURCE" in
        build)
            IMAGE_TAG="cozsweet-web:${DEPLOY_ENV}-${SHORT_SHA}"
            ;;
        pull)
            if [ -z "${COZSWEET_REGISTRY_IMAGE:-}" ]; then
                echo "=== missing COZSWEET_REGISTRY_IMAGE for pull deploy ===" >> "$LOG_FILE"
                exit 64
            fi
            IMAGE_TAG="${COZSWEET_REGISTRY_IMAGE}:${REMOTE_IMAGE_TAG}"
            ;;
        *)
            echo "=== unsupported COZSWEET_DEPLOY_IMAGE_SOURCE: $IMAGE_SOURCE ===" >> "$LOG_FILE"
            exit 64
            ;;
    esac

    CONTAINER_NAME="cozsweet-web-${DEPLOY_ENV}"
    COMPOSE_PROJECT_NAME="cozsweet-web-${DEPLOY_ENV}"

    export COZSWEET_IMAGE="$IMAGE_TAG"
    export COZSWEET_CONTAINER_NAME="$CONTAINER_NAME"
    export COZSWEET_ENV_FILE="$ENV_FILE"
    export COZSWEET_HOST_PORT="$START_PORT"
    export COMPOSE_PROJECT_NAME
    export DOCKER_BUILDKIT=1
    export COMPOSE_DOCKER_CLI_BUILD=1

    {
        echo "=== deploy env: $DEPLOY_ENV ==="
        echo "=== image source: $IMAGE_SOURCE ==="
        echo "=== env file: $COZSWEET_ENV_FILE ==="
        echo "=== image: $COZSWEET_IMAGE ==="
        echo "=== remote image tag: $REMOTE_IMAGE_TAG ==="
        echo "=== container: $COZSWEET_CONTAINER_NAME ==="
        echo "=== host port: $COZSWEET_HOST_PORT ==="
        echo "=== compose project: $COMPOSE_PROJECT_NAME ==="
        echo ""
    } >> "$LOG_FILE"
}

docker_compose() {
    if docker compose version >/dev/null 2>&1; then
        docker compose "$@"
        return $?
    fi

    if command -v docker-compose >/dev/null 2>&1; then
        docker-compose "$@"
        return $?
    fi

    echo "=== docker compose command not found ===" >> "$LOG_FILE"
    return 127
}

run_container_build() {
    echo "=== docker build START @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
    docker_compose build web >> "$LOG_FILE" 2>&1
    COMPOSE_EXIT=$?
    echo "=== docker build EXIT_CODE=$COMPOSE_EXIT @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
    echo "" >> "$LOG_FILE"
    return $COMPOSE_EXIT
}

pull_container_image() {
    echo "=== docker pull START @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
    docker_compose pull web >> "$LOG_FILE" 2>&1
    COMPOSE_EXIT=$?
    echo "=== docker pull EXIT_CODE=$COMPOSE_EXIT @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
    echo "" >> "$LOG_FILE"
    return $COMPOSE_EXIT
}

launch_container() {
    echo "=== docker compose up START @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
    if [ "$IMAGE_SOURCE" = "pull" ]; then
        docker_compose up -d --remove-orphans --no-build web >> "$LOG_FILE" 2>&1
    else
        docker_compose up -d --remove-orphans web >> "$LOG_FILE" 2>&1
    fi
    COMPOSE_EXIT=$?
    echo "=== docker compose up EXIT_CODE=$COMPOSE_EXIT @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
    echo "" >> "$LOG_FILE"
    return $COMPOSE_EXIT
}

log_abort() {
    echo "=== ABORTED @ $(date +%Y-%m-%dT%H:%M:%S%z) ($1) ===" >> "$LOG_FILE"
}

main() {
    resolve_repo_context
    init_log_file
    write_metadata_header

    sync_worktree
    load_deploy_config
    copy_env_by_branch
    prepare_compose_env

    if [ "$IMAGE_SOURCE" = "pull" ]; then
        if ! pull_container_image; then
            log_abort "docker pull failed, old container kept running"
            exit $COMPOSE_EXIT
        fi
    else
        if ! run_container_build; then
            log_abort "docker build failed, old container kept running"
            exit $COMPOSE_EXIT
        fi
    fi

    if ! launch_container; then
        log_abort "docker compose up failed"
        exit $COMPOSE_EXIT
    fi

    echo "=== DEPLOY DONE @ $(date +%Y-%m-%dT%H:%M:%S%z) ===" >> "$LOG_FILE"
}

main "$@"

exit 0
