#!/bin/bash
# 智能部署脚本 - 自动检测依赖/代码/迁移变化
#
# 用法:
#   部署/更新:    ./deploy.sh  (自动检测所有变化)
#   强制重建:     ./deploy.sh --rebuild-base
#   强制全部重建: ./deploy.sh --force

set -e
cd "$(dirname "$0")"

# 兼容 docker-compose 和 docker compose
if command -v docker-compose &> /dev/null; then
    DC="docker-compose -f docker-compose.build.yml"
else
    DC="docker compose -f docker-compose.build.yml"
fi

# 缓存文件
HASH_FILE=".deps-hash"
CODE_HASH_FILE=".code-hash"
MIGRATION_HASH_FILE=".migration-hash"

# 计算依赖文件的哈希值（包含 Dockerfile.base.local）
calc_deps_hash() {
    cat pyproject.toml frontend/package.json frontend/package-lock.json Dockerfile.base.local 2>/dev/null | md5sum | cut -d' ' -f1
}

# 计算代码文件的哈希值（包含 Dockerfile.app.local）
calc_code_hash() {
    {
        cat Dockerfile.app.local 2>/dev/null
        find src -type f -name "*.py" 2>/dev/null | sort | xargs cat 2>/dev/null
        find frontend/src -type f \( -name "*.vue" -o -name "*.ts" -o -name "*.tsx" -o -name "*.js" \) 2>/dev/null | sort | xargs cat 2>/dev/null
    } | md5sum | cut -d' ' -f1
}

# 计算迁移文件的哈希值
calc_migration_hash() {
    find alembic/versions -name "*.py" -type f 2>/dev/null | sort | xargs cat 2>/dev/null | md5sum | cut -d' ' -f1
}

# 检查依赖是否变化
check_deps_changed() {
    local current_hash=$(calc_deps_hash)
    if [ -f "$HASH_FILE" ]; then
        local saved_hash=$(cat "$HASH_FILE")
        if [ "$current_hash" = "$saved_hash" ]; then
            return 1
        fi
    fi
    return 0
}

# 检查代码是否变化
check_code_changed() {
    local current_hash=$(calc_code_hash)
    if [ -f "$CODE_HASH_FILE" ]; then
        local saved_hash=$(cat "$CODE_HASH_FILE")
        if [ "$current_hash" = "$saved_hash" ]; then
            return 1
        fi
    fi
    return 0
}

# 检查迁移是否变化
check_migration_changed() {
    local current_hash=$(calc_migration_hash)
    if [ -f "$MIGRATION_HASH_FILE" ]; then
        local saved_hash=$(cat "$MIGRATION_HASH_FILE")
        if [ "$current_hash" = "$saved_hash" ]; then
            return 1
        fi
    fi
    return 0
}

# 保存哈希
save_deps_hash() { calc_deps_hash > "$HASH_FILE"; }
save_code_hash() { calc_code_hash > "$CODE_HASH_FILE"; }
save_migration_hash() { calc_migration_hash > "$MIGRATION_HASH_FILE"; }

# 构建基础镜像
build_base() {
    echo ">>> Building base image (dependencies)..."
    docker build -f Dockerfile.base.local -t aether-base:latest .
    save_deps_hash
}

# 生成版本文件
generate_version_file() {
    # 从 git 获取版本号
    local version
    version=$(git describe --tags --always 2>/dev/null | sed 's/^v//')
    if [ -z "$version" ]; then
        version="unknown"
    fi
    echo ">>> Generating version file: $version"
    cat > src/_version.py << EOF
# Auto-generated by deploy.sh - do not edit
__version__ = '$version'
__version_tuple__ = tuple(int(x) for x in '$version'.split('-')[0].split('.') if x.isdigit())
version = __version__
version_tuple = __version_tuple__
EOF
}

# 构建应用镜像
build_app() {
    echo ">>> Building app image (code only)..."
    generate_version_file
    docker build -f Dockerfile.app.local -t aether-app:latest .
    save_code_hash
}

# 运行数据库迁移
run_migration() {
    echo ">>> Running database migration..."

    # 尝试运行 upgrade head，捕获错误
    UPGRADE_OUTPUT=$($DC exec -T app alembic upgrade head 2>&1) && {
        echo "$UPGRADE_OUTPUT"
        save_migration_hash
        return 0
    }

    # 检查是否是因为找不到旧版本（基线重置场景）
    if echo "$UPGRADE_OUTPUT" | grep -q "Can't locate revision"; then
        echo ">>> Detected baseline reset: old revision not found in migrations"
        echo ">>> Clearing old version and stamping to new baseline..."

        # 先清除旧的版本记录，再 stamp 到新基线
        $DC exec -T app python -c "
from sqlalchemy import create_engine, text
import os
engine = create_engine(os.environ['DATABASE_URL'])
with engine.connect() as conn:
    conn.execute(text('DELETE FROM alembic_version'))
    conn.commit()
print('Old version cleared')
"
        # 获取最新的迁移版本（匹配 revision_id (head) 格式）
        LATEST_VERSION=$($DC exec -T app alembic heads 2>/dev/null | grep -oE '^[0-9a-zA-Z_]+' | head -1)
        if [ -n "$LATEST_VERSION" ]; then
            $DC exec -T app alembic stamp "$LATEST_VERSION"
            echo ">>> Database stamped to $LATEST_VERSION"
            save_migration_hash
        else
            echo ">>> ERROR: Could not determine latest migration version"
            exit 1
        fi
    else
        # 其他错误，直接输出并退出
        echo "$UPGRADE_OUTPUT"
        exit 1
    fi
}

# 强制全部重建
if [ "$1" = "--force" ] || [ "$1" = "-f" ]; then
    echo ">>> Force rebuilding everything..."
    build_base
    build_app
    $DC up -d --force-recreate
    sleep 3
    run_migration
    docker image prune -f
    echo ">>> Done!"
    $DC ps
    exit 0
fi

# 强制重建基础镜像
if [ "$1" = "--rebuild-base" ] || [ "$1" = "-r" ]; then
    build_base
    echo ">>> Base image rebuilt. Run ./deploy.sh to deploy."
    exit 0
fi

# 拉取最新代码
echo ">>> Pulling latest code..."
git pull

# 标记是否需要重启
NEED_RESTART=false
BASE_REBUILT=false

# 检查基础镜像是否存在，或依赖是否变化
if ! docker image inspect aether-base:latest >/dev/null 2>&1; then
    echo ">>> Base image not found, building..."
    build_base
    BASE_REBUILT=true
    NEED_RESTART=true
elif check_deps_changed; then
    echo ">>> Dependencies changed, rebuilding base image..."
    build_base
    BASE_REBUILT=true
    NEED_RESTART=true
else
    echo ">>> Dependencies unchanged."
fi

# 检查代码或迁移是否变化，或者 base 重建了（app 依赖 base）
# 注意：迁移文件打包在镜像中，所以迁移变化也需要重建 app 镜像
MIGRATION_CHANGED=false
if check_migration_changed; then
    MIGRATION_CHANGED=true
fi

if ! docker image inspect aether-app:latest >/dev/null 2>&1; then
    echo ">>> App image not found, building..."
    build_app
    NEED_RESTART=true
elif [ "$BASE_REBUILT" = true ]; then
    echo ">>> Base image rebuilt, rebuilding app image..."
    build_app
    NEED_RESTART=true
elif check_code_changed; then
    echo ">>> Code changed, rebuilding app image..."
    build_app
    NEED_RESTART=true
elif [ "$MIGRATION_CHANGED" = true ]; then
    echo ">>> Migration files changed, rebuilding app image..."
    build_app
    NEED_RESTART=true
else
    echo ">>> Code unchanged."
fi

# 只在有变化时重启
if [ "$NEED_RESTART" = true ]; then
    echo ">>> Restarting services..."
    $DC up -d
else
    echo ">>> No changes detected, skipping restart."
fi

# 检查迁移变化（如果前面已经检测到变化并重建了镜像，这里直接运行迁移）
if [ "$MIGRATION_CHANGED" = true ]; then
    echo ">>> Running database migration..."
    sleep 3
    run_migration
else
    echo ">>> Migration unchanged."
fi

# 清理
docker image prune -f >/dev/null 2>&1 || true

echo ">>> Done!"
$DC ps
