#!/bin/bash
# ===========================================
# CI Staging 健康检查 + 自动回滚脚本（SSH 部署模式）
# ===========================================
#
# 在 CI Runner 上执行，通过公网 URL 检查 Staging 部署健康状态。
# 不健康则通过 SSH 自动回滚到上一个版本的镜像。
#
# 用法:
#   ./ci_staging_healthcheck.sh
#
# 环境变量:
#   STAGING_API_URL       - Staging API 地址 (默认 https://staging-api.xiaoxiajianji.com)
#   STAGING_WEB_URL       - Staging Web 地址 (默认 https://staging.xiaoxiajianji.com)
#   HEALTH_CHECK_TIMEOUT  - 健康检查总超时秒数 (默认 120)
#   SKIP_ROLLBACK         - 失败时不自动回滚 (true/false, 默认 false)
#   SKIP_NOTIFY           - 跳过通知 (true/false, 默认 false)
#   CI_NOTIFY_WEBHOOK     - 通知 Webhook URL
#
#   STAGING_SSH_HOST      - Staging 服务器 SSH 地址 (默认 47.98.113.167)
#   STAGING_SSH_USER      - SSH 用户名 (默认 root)
#   STAGING_SSH_PORT      - SSH 端口 (默认 22222)
#   STAGING_SSH_KEY       - SSH 私钥内容
#   REGISTRY_TOKEN        - Registry Token（回滚时拉取旧镜像需要）
#
#   GITHUB_SHA            - 当前 commit SHA
#   GITHUB_REF_NAME       - 分支名
#   GITHUB_RUN_ID         - CI Run ID
#   GITHUB_REPOSITORY     - 仓库名
#   GITHUB_ACTOR          - 提交者

set -eu

SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"

# 配置
STAGING_API_URL="${STAGING_API_URL:-https://staging-api.xiaoxiajianji.com}"
STAGING_WEB_URL="${STAGING_WEB_URL:-https://staging.xiaoxiajianji.com}"
HEALTH_CHECK_TIMEOUT="${HEALTH_CHECK_TIMEOUT:-120}"
SKIP_ROLLBACK="${SKIP_ROLLBACK:-false}"
SKIP_NOTIFY="${SKIP_NOTIFY:-false}"

STAGING_SSH_HOST="${STAGING_SSH_HOST:-47.98.113.167}"
STAGING_SSH_USER="${STAGING_SSH_USER:-root}"
STAGING_SSH_PORT="${STAGING_SSH_PORT:-22222}"

REGISTRY="${REGISTRY:-git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas}"
REGISTRY_USER="${REGISTRY_USER:-xiaoxia}"

# 颜色
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'

log_info()  { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn()  { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
log_step()  { echo -e "${BLUE}[STEP]${NC} $1"; }

# ===========================================
# SSH 工具函数
# ===========================================
SSH_KEY_PATH=""

setup_ssh() {
    # 查找或创建 SSH 密钥
    if [ -f /root/.ssh/xiaoxia_runtime_builder ]; then
        SSH_KEY_PATH="/root/.ssh/xiaoxia_runtime_builder"
    elif [ -f "$HOME/.ssh/xiaoxia_runtime_builder" ]; then
        SSH_KEY_PATH="$HOME/.ssh/xiaoxia_runtime_builder"
    elif [ -n "${STAGING_SSH_KEY:-}" ]; then
        SSH_KEY_PATH="$HOME/.ssh/staging_deploy_key"
        mkdir -p "$HOME/.ssh"
        printf '%s\n' "$STAGING_SSH_KEY" > "$SSH_KEY_PATH"
        chmod 600 "$SSH_KEY_PATH"
    else
        log_error "没有可用的 SSH 密钥"
        return 1
    fi

    ssh-keyscan -p "$STAGING_SSH_PORT" -H "$STAGING_SSH_HOST" >> ~/.ssh/known_hosts 2>/dev/null || true
    log_info "SSH 已配置: ${STAGING_SSH_USER}@${STAGING_SSH_HOST}:${STAGING_SSH_PORT}"
}

run_ssh() {
    local cmd="$1"
    ssh -p "$STAGING_SSH_PORT" -i "$SSH_KEY_PATH" -o StrictHostKeyChecking=no \
        "${STAGING_SSH_USER}@${STAGING_SSH_HOST}" "$cmd"
}

# ===========================================
# 1. 记录部署前各服务的镜像版本（用于回滚）
# ===========================================
ROLLBACK_API_TAG=""
ROLLBACK_WORKER_TAG=""
ROLLBACK_WEB_TAG=""

save_rollback_target() {
    log_step "记录当前 staging 各服务镜像版本（回滚目标）..."

    # 通过 SSH 获取当前运行的容器镜像
    local api_image worker_image web_image
    api_image=$(run_ssh "docker inspect --format '{{.Config.Image}}' xiaoxia-api-staging 2>/dev/null || echo ''")
    worker_image=$(run_ssh "docker inspect --format '{{.Config.Image}}' xiaoxia-worker-staging 2>/dev/null || echo ''")
    web_image=$(run_ssh "docker inspect --format '{{.Config.Image}}' xiaoxia-web-staging 2>/dev/null || echo ''")

    # 提取 tag（镜像名是 xiaoxia-saas-api:abc123 或 git.xiaoxiajianji.com/.../xiaoxia-saas-api:staging 格式）
    ROLLBACK_API_TAG=$(echo "$api_image" | sed 's/.*://' || echo "")
    ROLLBACK_WORKER_TAG=$(echo "$worker_image" | sed 's/.*://' || echo "")
    ROLLBACK_WEB_TAG=$(echo "$web_image" | sed 's/.*://' || echo "")

    log_info "  API:     ${ROLLBACK_API_TAG:-未知}"
    log_info "  Worker:  ${ROLLBACK_WORKER_TAG:-未知}"
    log_info "  Web:     ${ROLLBACK_WEB_TAG:-未知}"

    # 验证三个服务版本是否一致
    if [ -n "$ROLLBACK_API_TAG" ] && [ -n "$ROLLBACK_WORKER_TAG" ] && [ -n "$ROLLBACK_WEB_TAG" ]; then
        if [ "$ROLLBACK_API_TAG" = "$ROLLBACK_WORKER_TAG" ] && [ "$ROLLBACK_API_TAG" = "$ROLLBACK_WEB_TAG" ]; then
            log_info "  ✅ 三个服务版本一致: $ROLLBACK_API_TAG"
            export ROLLBACK_TAG="$ROLLBACK_API_TAG"
        else
            log_warn "  ⚠️  三个服务版本不一致，回滚时将分别使用各自版本"
            export ROLLBACK_API_TAG ROLLBACK_WORKER_TAG ROLLBACK_WEB_TAG
            export ROLLBACK_TAG_MIXED="true"
        fi
    else
        log_warn "  ⚠️  未能获取全部服务版本，回滚功能可能受限"
    fi
}

# ===========================================
# 2. 健康检查（公网视角）
# ===========================================
health_check() {
    local timeout="$HEALTH_CHECK_TIMEOUT"
    local start_time
    start_time=$(date +%s)

    log_step "公网健康检查（超时 ${timeout}s）..."
    log_info "  API: ${STAGING_API_URL}/health"
    log_info "  Web: ${STAGING_WEB_URL}/"

    local api_ok=false
    local web_ok=false
    local api_docs_ok=false
    local login_api_ok=false

    while [ $(( $(date +%s) - start_time )) -lt "$timeout" ]; do
        # 检查 API health
        if [ "$api_ok" = false ] && curl -sf --max-time 10 "${STAGING_API_URL}/health" >/dev/null 2>&1; then
            log_info "✅ API 健康检查通过"
            api_ok=true
        fi

        # 检查 Web 首页
        if [ "$web_ok" = false ] && curl -sf --max-time 10 "$STAGING_WEB_URL/" >/dev/null 2>&1; then
            log_info "✅ Web 前端检查通过"
            web_ok=true
        fi

        # 检查 API docs（服务完全启动的标志）
        if [ "$api_docs_ok" = false ]; then
            HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "${STAGING_API_URL}/docs" 2>/dev/null || echo "000")
            if [ "$HTTP_CODE" = "200" ]; then
                log_info "✅ API Docs 检查通过"
                api_docs_ok=true
            fi
        fi

        # 检查登录 API（业务逻辑正常的标志）
        if [ "$login_api_ok" = false ]; then
            HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 -X POST \
                "${STAGING_API_URL}/api/v1/auth/login" \
                -H "Content-Type: application/json" \
                -d '{"email":"smoke@test.com","password":"wrong"}' 2>/dev/null || echo "000")
            if [ "$HTTP_CODE" = "401" ] || [ "$HTTP_CODE" = "422" ]; then
                log_info "✅ 登录 API 检查通过（HTTP $HTTP_CODE，符合预期）"
                login_api_ok=true
            fi
        fi

        # 都通过了就退出
        if [ "$api_ok" = true ] && [ "$web_ok" = true ] && [ "$api_docs_ok" = true ] && [ "$login_api_ok" = true ]; then
            log_info "🎉 所有健康检查通过！"
            return 0
        fi

        sleep 5
    done

    # 超时了
    log_error "❌ 健康检查超时 (${timeout}s)"
    [ "$api_ok" = false ]       && log_error "  - API health 未通过"
    [ "$web_ok" = false ]       && log_error "  - Web 前端未通过"
    [ "$api_docs_ok" = false ]  && log_error "  - API Docs 未通过"
    [ "$login_api_ok" = false ] && log_error "  - 登录 API 未通过"

    return 1
}

# ===========================================
# 3. 执行回滚（SSH 重新部署旧版本）
# ===========================================
do_rollback() {
    log_step "执行回滚：通过 SSH 重新部署旧版本镜像..."

    local rollback_tag="${ROLLBACK_TAG:-}"
    if [ -z "$rollback_tag" ] && [ "${ROLLBACK_TAG_MIXED:-}" != "true" ]; then
        log_error "没有可回滚的版本记录，无法自动回滚"
        return 1
    fi

    # 如果版本不一致，用 API 的版本作为回滚目标
    if [ -z "$rollback_tag" ]; then
        rollback_tag="$ROLLBACK_API_TAG"
    fi

    if [ -z "$rollback_tag" ]; then
        log_error "无法确定回滚版本"
        return 1
    fi

    log_info "回滚目标版本: $rollback_tag"

    # 构建回滚脚本（直接部署旧版本镜像，不跑 migration）
    local rollback_script=$(cat << 'ROLLBACK_EOF'
#!/bin/sh
set -eu

IMAGE_TAG="$1"
REGISTRY_TOKEN="$2"
REGISTRY="${REGISTRY:-git.xiaoxiajianji.com/xiaoxia/xiaoxia-saas}"
REGISTRY_USER="${REGISTRY_USER:-xiaoxia}"

ENV_FILE="${ENV_FILE:-/var/lib/xiaoxia-saas-staging/.env}"
GENERATED_DIR="${GENERATED_DIR:-/var/lib/xiaoxia-saas-staging/generated}"
LEGACY_ASSETS_DIR="${LEGACY_ASSETS_DIR:-/var/lib/xiaoxia-saas-staging/legacy-assets}"

echo "=== Rollback to $IMAGE_TAG ==="

# 登录 Registry
if [ -n "$REGISTRY_TOKEN" ]; then
    REGISTRY_HOST=$(echo "$REGISTRY" | cut -d/ -f1)
    printf %s "$REGISTRY_TOKEN" | docker login "$REGISTRY_HOST" -u "$REGISTRY_USER" --password-stdin 2>/dev/null || true
fi

# Pull 旧版本镜像
LOCAL_API="xiaoxia-saas-api:${IMAGE_TAG}"
LOCAL_WORKER="xiaoxia-saas-worker:${IMAGE_TAG}"
LOCAL_WEB="xiaoxia-saas-web:${IMAGE_TAG}"

docker pull "${REGISTRY}/xiaoxia-saas-api:${IMAGE_TAG}"
docker pull "${REGISTRY}/xiaoxia-saas-worker:${IMAGE_TAG}"
docker pull "${REGISTRY}/xiaoxia-saas-web:${IMAGE_TAG}"

docker tag "${REGISTRY}/xiaoxia-saas-api:${IMAGE_TAG}" "$LOCAL_API"
docker tag "${REGISTRY}/xiaoxia-saas-worker:${IMAGE_TAG}" "$LOCAL_WORKER"
docker tag "${REGISTRY}/xiaoxia-saas-web:${IMAGE_TAG}" "$LOCAL_WEB"

echo "Rollback images pulled."

# 停止当前容器（回滚不跑 migration，避免数据问题）
docker rm -f xiaoxia-api-staging 2>/dev/null || true
docker rm -f xiaoxia-worker-staging 2>/dev/null || true
docker rm -f xiaoxia-web-staging 2>/dev/null || true

LOG_OPTS="--log-driver json-file --log-opt max-size=50m --log-opt max-file=3"

# 启动 API（回滚不跑 migration）
echo "Starting API (rollback)..."
docker run -d \
  --name xiaoxia-api-staging \
  --env-file "$ENV_FILE" \
  --network xiaoxia-net-staging \
  -p 127.0.0.1:8000:8000 \
  -e APP_ENV=staging \
  -e APP_VERSION="$IMAGE_TAG" \
  -e GENERATED_FILES_DIR=/app/generated \
  -e GENERATED_FILES_URL_PREFIX=/generated-files \
  -e PUBLIC_API_BASE_URL=https://staging-api.xiaoxiajianji.com \
  -v "$GENERATED_DIR:/app/generated" \
  --restart unless-stopped \
  --health-cmd "python -c \"import urllib.request; urllib.request.urlopen('http://localhost:8000/health', timeout=5)\"" \
  --health-interval 30s \
  --health-timeout 10s \
  --health-retries 3 \
  --health-start-period 40s \
  $LOG_OPTS \
  "$LOCAL_API"

# 启动 Worker
echo "Starting Worker (rollback)..."
docker run -d \
  --name xiaoxia-worker-staging \
  --env-file "$ENV_FILE" \
  --network xiaoxia-net-staging \
  -e APP_ENV=staging \
  -e APP_VERSION="$IMAGE_TAG" \
  -e WORKER_CONCURRENCY=1 \
  -e WORKER_MAX_TASKS_PER_CHILD=100 \
  -e GENERATED_FILES_DIR=/app/generated \
  -e GENERATED_FILES_URL_PREFIX=/generated-files \
  -e PUBLIC_API_BASE_URL=https://staging-api.xiaoxiajianji.com \
  -v "$GENERATED_DIR:/app/generated" \
  --restart unless-stopped \
  --health-cmd "sh -c \"grep -q celery /proc/1/cmdline || exit 1\"" \
  --health-interval 30s \
  --health-timeout 10s \
  --health-retries 3 \
  --health-start-period 30s \
  $LOG_OPTS \
  "$LOCAL_WORKER"

# 启动 Web
LEGACY_VOLUME=""
if [ -d "$LEGACY_ASSETS_DIR" ] && [ "$(ls -A "$LEGACY_ASSETS_DIR" 2>/dev/null)" ]; then
  LEGACY_VOLUME="-v ${LEGACY_ASSETS_DIR}:/usr/share/nginx/html/assets-legacy/assets:ro"
fi

echo "Starting Web (rollback)..."
docker run -d \
  --name xiaoxia-web-staging \
  --network xiaoxia-net-staging \
  -p 127.0.0.1:3001:80 \
  --restart unless-stopped \
  $LEGACY_VOLUME \
  --health-cmd "wget --spider -q http://127.0.0.1:80" \
  --health-interval 30s \
  --health-timeout 5s \
  --health-retries 3 \
  $LOG_OPTS \
  "$LOCAL_WEB"

# 等待 API 健康
echo "Waiting for API (rollback)..."
i=0
while [ "$i" -lt 40 ]; do
  if curl -sf --max-time 5 http://127.0.0.1:8000/health >/dev/null 2>&1; then
    echo "API healthy (rollback)."
    break
  fi
  i=$((i + 1))
  sleep 3
done

# 等待 Web 健康
echo "Waiting for Web (rollback)..."
i=0
while [ "$i" -lt 15 ]; do
  if curl -sf --max-time 5 http://127.0.0.1:3001/ >/dev/null 2>&1; then
    echo "Web healthy (rollback)."
    break
  fi
  i=$((i + 1))
  sleep 2
done

echo "=== Rollback complete: $IMAGE_TAG ==="
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Image}}" | grep staging
ROLLBACK_EOF
)

    # 将脚本 base64 编码后通过 SSH 执行
    local script_b64
    script_b64=$(echo "$rollback_script" | base64 -w 0)

    log_info "在 staging 服务器上执行回滚脚本..."
    if run_ssh "echo '$script_b64' | base64 -d | sh -s -- '$rollback_tag' '${REGISTRY_TOKEN:-}'" 2>&1; then
        log_info "✅ 回滚命令执行完成"
        return 0
    else
        log_error "❌ 回滚命令执行失败"
        return 1
    fi
}

# ===========================================
# 4. 发送通知
# ===========================================
send_notification() {
    local status="$1"   # success / failure / rollback
    local detail="$2"

    if [ "${SKIP_NOTIFY:-false}" = "true" ]; then
        log_info "跳过通知（SKIP_NOTIFY=true）"
        return 0
    fi

    local webhook="${CI_NOTIFY_WEBHOOK:-}"
    if [ -z "$webhook" ]; then
        log_warn "未配置 CI_NOTIFY_WEBHOOK，跳过通知"
        return 0
    fi

    if [ -f "$SCRIPT_DIR/deploy_notify.py" ]; then
        python3 "$SCRIPT_DIR/deploy_notify.py" \
            --status "$status" \
            --detail "$detail" \
            --webhook "$webhook" \
            --env staging \
            2>/dev/null || log_warn "通知发送失败（非致命）"
    else
        log_warn "找不到 deploy_notify.py，跳过通知"
    fi
}

# ===========================================
# 主流程
# ===========================================
main() {
    echo ""
    echo "==========================================="
    echo "  CI Staging 健康检查 + 自动回滚（SSH模式）"
    echo "==========================================="
    echo ""

    local deploy_status="success"
    local deploy_detail=""

    # 1. 设置 SSH
    if ! setup_ssh; then
        log_error "SSH 配置失败，无法执行回滚"
    fi

    # 2. 记录部署前状态（回滚目标）
    save_rollback_target || true

    # 3. 健康检查（公网视角）
    if ! health_check; then
        log_error "健康检查失败"
        deploy_status="failure"
        deploy_detail="公网健康检查超时，部署后服务未正常响应"

        # 自动回滚
        if [ "${SKIP_ROLLBACK:-false}" != "true" ]; then
            log_warn "开始自动回滚..."
            if do_rollback; then
                deploy_status="rollback"
                deploy_detail="健康检查失败，已自动回滚到上一版本 (${ROLLBACK_TAG:-未知})"

                # 回滚后再检查一下公网状态
                log_info "回滚完成，重新检查公网健康状态..."
                if health_check; then
                    log_info "✅ 回滚后服务已恢复"
                    deploy_detail="${deploy_detail}，回滚后服务已恢复"
                else
                    log_error "⚠️  回滚后健康检查仍未通过，请手动排查"
                    deploy_detail="${deploy_detail}，但回滚后仍未恢复，请紧急排查"
                fi
            else
                deploy_detail="健康检查失败且回滚失败，请手动排查"
            fi
        fi

        send_notification "$deploy_status" "$deploy_detail"

        # 失败时退出非零，让 CI Job 标记为失败
        exit 1
    fi

    # 4. 成功
    log_info ""
    log_info "=================================="
    log_info "  ✅ Staging 部署成功！"
    log_info "=================================="

    deploy_detail="部署成功，所有健康检查通过 (${GITHUB_SHA:-未知版本})"
    send_notification "success" "$deploy_detail"
}

main "$@"
