#!/bin/bash
# Docker镜像和配置文件推送脚本
# 将生产环境所需的文件和镜像推送到远程服务器

set -e

# ===========================================
# 脚本配置
# ===========================================
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"

# 默认配置
DEFAULT_REMOTE_HOST=""
DEFAULT_REMOTE_USER="root"
DEFAULT_REMOTE_PATH="/root/product/anxin"
DEFAULT_SSH_PORT="22"

# 镜像配置
FRONTEND_IMAGE="anxin-frontend"
BACKEND_IMAGE="anxin-backend"
DEFAULT_TAG="latest"

# 临时目录
TEMP_DIR="/tmp/anxin-deploy-$(date +%s)"

# ===========================================
# 颜色定义
# ===========================================
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color

# ===========================================
# 日志函数
# ===========================================
log_info() {
    echo -e "${BLUE}[INFO]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}

log_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}

log_step() {
    echo -e "${CYAN}[STEP]${NC} $(date '+%Y-%m-%d %H:%M:%S') - $1"
}

# ===========================================
# 帮助信息
# ===========================================
show_help() {
    cat << EOF
若依框架Docker部署 - 推送脚本

用法: $0 [选项]

选项:
    -h, --host HOST         远程服务器地址 (必需)
    -u, --user USER         远程服务器用户名 [默认: root]
    -p, --port PORT         SSH端口 [默认: 22]
    -d, --dest PATH         远程服务器目标路径 [默认: /opt/anxin]
    -e, --env ENV           环境 (development|staging|production) [默认: production]
    -t, --tag TAG           镜像标签 [默认: latest，会自动使用环境标签]
    --skip-images           跳过镜像推送，仅推送配置文件
    --skip-files            跳过配置文件推送，仅推送镜像
    --frontend-only         仅推送前端镜像
    --backend-only          仅推送后端镜像
    --help                  显示此帮助信息

示例:
    $0 -h 192.168.1.100                           # 推送生产环境到指定服务器
    $0 -h 192.168.1.100 -e staging                # 推送测试环境
    $0 -h 192.168.1.100 -u deploy -p 2222         # 指定用户和端口
    $0 -h 192.168.1.100 -d /home/deploy/anxin     # 指定目标路径
    $0 -h 192.168.1.100 -t v1.0.0                 # 指定镜像标签
    $0 -h 192.168.1.100 --skip-images             # 仅推送配置文件
    $0 -h 192.168.1.100 --frontend-only           # 仅推送前端镜像
    $0 -h 192.168.1.100 --backend-only            # 仅推送后端镜像

推送内容:
    配置文件:
    - docker-compose.{ENVIRONMENT}.yml
    - deploy.sh
    - environments/ 目录
    - configs/ 目录 (如果存在)
    - database/ 目录 (如果存在)
    
    Docker镜像:
    - anxin-frontend:{ENV_TAG}
    - anxin-backend:{ENV_TAG}

注意:
    - ENV_TAG 根据环境自动确定: dev/staging/prod
    - 如果指定 -t 参数，将使用自定义标签
    - 推送前请确保已运行 ./build.sh -e {ENVIRONMENT} 构建镜像

EOF
}

# ===========================================
# 参数解析
# ===========================================
parse_args() {
    SKIP_IMAGES=false
    SKIP_FILES=false
    FRONTEND_ONLY=false
    BACKEND_ONLY=false
    
    while [[ $# -gt 0 ]]; do
        case $1 in
            -h|--host)
                REMOTE_HOST="$2"
                shift 2
                ;;
            -u|--user)
                REMOTE_USER="$2"
                shift 2
                ;;
            -p|--port)
                SSH_PORT="$2"
                shift 2
                ;;
            -d|--dest)
                REMOTE_PATH="$2"
                shift 2
                ;;
            -e|--env)
                ENVIRONMENT="$2"
                shift 2
                ;;
            -t|--tag)
                TAG="$2"
                shift 2
                ;;
            --skip-images)
                SKIP_IMAGES=true
                shift
                ;;
            --skip-files)
                SKIP_FILES=true
                shift
                ;;
            --frontend-only)
                FRONTEND_ONLY=true
                shift
                ;;
            --backend-only)
                BACKEND_ONLY=true
                shift
                ;;
            --help)
                show_help
                exit 0
                ;;
            *)
                log_error "未知参数: $1"
                show_help
                exit 1
                ;;
        esac
    done

    # 设置默认值
    REMOTE_HOST=${REMOTE_HOST:-$DEFAULT_REMOTE_HOST}
    REMOTE_USER=${REMOTE_USER:-$DEFAULT_REMOTE_USER}
    SSH_PORT=${SSH_PORT:-$DEFAULT_SSH_PORT}
    REMOTE_PATH=${REMOTE_PATH:-$DEFAULT_REMOTE_PATH}
    ENVIRONMENT=${ENVIRONMENT:-"production"}  # 推送脚本默认为生产环境
    TAG=${TAG:-$DEFAULT_TAG}

    # 验证必需参数
    if [[ -z "$REMOTE_HOST" ]]; then
        log_error "必须指定远程服务器地址 (-h|--host)"
        show_help
        exit 1
    fi
    
    # 验证互斥参数
    if [[ "$FRONTEND_ONLY" == "true" && "$BACKEND_ONLY" == "true" ]]; then
        log_error "--frontend-only 和 --backend-only 不能同时使用"
        exit 1
    fi
    
    if [[ "$SKIP_IMAGES" == "true" && ("$FRONTEND_ONLY" == "true" || "$BACKEND_ONLY" == "true") ]]; then
        log_error "--skip-images 不能与 --frontend-only 或 --backend-only 同时使用"
        exit 1
    fi
}

# ===========================================
# 系统检查
# ===========================================
check_prerequisites() {
    log_step "检查系统依赖..."
    
    # 检查SSH
    if ! command -v ssh &> /dev/null; then
        log_error "SSH未安装或不在PATH中"
        exit 1
    fi
    
    # 检查SCP
    if ! command -v scp &> /dev/null; then
        log_error "SCP未安装或不在PATH中"
        exit 1
    fi
    
    # 检查Docker (如果需要推送镜像)
    if [[ "$SKIP_IMAGES" != "true" ]] && ! command -v docker &> /dev/null; then
        log_error "Docker未安装或不在PATH中"
        exit 1
    fi
    
    log_success "系统依赖检查通过"
}

# ===========================================
# SSH执行函数
# ===========================================
# ===========================================
# SSH连接管理
# ===========================================
SSH_CONTROL_PATH=""
SSH_MASTER_STARTED=false

# 启动SSH主连接
start_ssh_master() {
    if [[ "$SSH_MASTER_STARTED" == "true" ]]; then
        return 0
    fi
    
    SSH_CONTROL_PATH="/tmp/ssh-anxin-deploy-$$"
    
    log_info "启动SSH主连接 (连接复用)..."
    
    # 启动SSH主连接
    ssh -M -S "$SSH_CONTROL_PATH" -f -N -p "$SSH_PORT" \
        -o ControlPersist=600 \
        -o ConnectTimeout=30 \
        -o ServerAliveInterval=60 \
        -o ServerAliveCountMax=3 \
        "$REMOTE_USER@$REMOTE_HOST"
    
    if [[ $? -eq 0 ]]; then
        SSH_MASTER_STARTED=true
        log_success "SSH主连接启动成功"
    else
        log_error "SSH主连接启动失败"
        return 1
    fi
}

# 停止SSH主连接
stop_ssh_master() {
    if [[ "$SSH_MASTER_STARTED" == "true" && -n "$SSH_CONTROL_PATH" ]]; then
        log_info "关闭SSH主连接..."
        ssh -S "$SSH_CONTROL_PATH" -O exit "$REMOTE_USER@$REMOTE_HOST" 2>/dev/null || true
        SSH_MASTER_STARTED=false
    fi
}

# 通用SCP传输函数，支持大文件传输和连接复用
scp_transfer() {
    local source="$1"
    local destination="$2"
    local is_recursive=${3:-false}
    
    local scp_options="-P $SSH_PORT -o ConnectTimeout=30 -o ServerAliveInterval=60 -o ServerAliveCountMax=3"
    
    # 如果SSH主连接已启动，使用连接复用
    if [[ "$SSH_MASTER_STARTED" == "true" && -n "$SSH_CONTROL_PATH" ]]; then
        scp_options="$scp_options -o ControlPath=$SSH_CONTROL_PATH"
    fi
    
    if [[ "$is_recursive" == "true" ]]; then
        scp_options="$scp_options -r"
    fi
    
    # 对于大文件，显示传输进度
    if [[ -f "$source" ]]; then
        local file_size=$(stat -f%z "$source" 2>/dev/null || stat -c%s "$source" 2>/dev/null || echo "0")
        if [[ $file_size -gt 10485760 ]]; then  # 大于10MB的文件显示进度
            scp_options="$scp_options -v"
        fi
    fi
    
    scp $scp_options "$source" "$destination"
}

# 通用SSH执行函数，支持密钥和密码认证
ssh_execute() {
    local command="$1"
    local show_output=${2:-true}
    local timeout=${3:-300}  # 默认5分钟超时
    
    local ssh_options="-p $SSH_PORT -o ConnectTimeout=30 -o ServerAliveInterval=60 -o ServerAliveCountMax=3"
    
    # 如果SSH主连接已启动，使用连接复用
    if [[ "$SSH_MASTER_STARTED" == "true" && -n "$SSH_CONTROL_PATH" ]]; then
        ssh_options="$ssh_options -o ControlPath=$SSH_CONTROL_PATH"
        
        if [[ "$show_output" == "true" ]]; then
            ssh $ssh_options "$REMOTE_USER@$REMOTE_HOST" "$command"
        else
            ssh $ssh_options "$REMOTE_USER@$REMOTE_HOST" "$command" 2>/dev/null
        fi
        return $?
    fi
    
    # 首先尝试使用SSH密钥连接（非交互式）
    if ssh $ssh_options -o BatchMode=yes "$REMOTE_USER@$REMOTE_HOST" "$command" 2>/dev/null; then
        return 0
    fi
    
    # 如果SSH密钥失败，使用交互式连接（允许密码输入）
    if [[ "$show_output" == "true" ]]; then
        ssh $ssh_options "$REMOTE_USER@$REMOTE_HOST" "$command"
    else
        ssh $ssh_options "$REMOTE_USER@$REMOTE_HOST" "$command" 2>/dev/null
    fi
}

# ===========================================
# 连接测试
# ===========================================
test_connection() {
    log_step "测试远程服务器连接..."
    
    # 首先尝试使用SSH密钥连接（非交互式）
    if ssh -p "$SSH_PORT" -o ConnectTimeout=10 -o BatchMode=yes "$REMOTE_USER@$REMOTE_HOST" "echo 'Connection test successful'" &>/dev/null; then
        log_success "远程服务器连接测试成功 (使用SSH密钥)"
        return 0
    fi
    
    # 如果SSH密钥失败，尝试交互式连接（允许密码输入）
    log_info "SSH密钥认证失败，尝试密码认证..."
    if ssh -p "$SSH_PORT" -o ConnectTimeout=10 -o PasswordAuthentication=yes "$REMOTE_USER@$REMOTE_HOST" "echo 'Connection test successful'"; then
        log_success "远程服务器连接测试成功 (使用密码认证)"
        return 0
    else
        log_error "无法连接到远程服务器: $REMOTE_USER@$REMOTE_HOST:$SSH_PORT"
        log_info "请确保:"
        log_info "1. 服务器地址和端口正确"
        log_info "2. SSH密钥已配置或密码正确"
        log_info "3. 用户有相应权限"
        log_info "4. 服务器SSH服务正在运行"
        exit 1
    fi
}

# ===========================================
# 准备推送
# ===========================================
prepare_push() {
    log_step "准备推送环境..."
    
    # 创建临时目录
    mkdir -p "$TEMP_DIR"
    log_info "创建临时目录: $TEMP_DIR"
    
    # 在远程服务器创建目标目录
    log_info "在远程服务器创建目标目录..."
    ssh_execute "mkdir -p $REMOTE_PATH"
    
    log_success "推送环境准备完成"
}

# ===========================================
# 推送配置文件
# ===========================================
push_config_files() {
    if [[ "$SKIP_FILES" == "true" ]]; then
        log_info "跳过配置文件推送"
        return 0
    fi
    
    log_step "推送配置文件..."
    
    cd "$SCRIPT_DIR"
    
    # 推送对应环境的docker-compose文件
    local compose_file="docker-compose.${ENVIRONMENT}.yml"
    log_info "推送 $compose_file..."
    if [[ -f "$compose_file" ]]; then
        scp_transfer "$compose_file" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/"
    else
        log_error "Docker Compose文件不存在: $compose_file"
        return 1
    fi
    
    # 推送deploy.sh
    log_info "推送 deploy.sh..."
    scp_transfer "deploy.sh" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/"
    
    # 推送environments目录
    log_info "推送 environments/ 目录..."
    scp_transfer "environments" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/" true
    
    # 推送configs目录（如果存在）
    if [[ -d "configs" ]]; then
        log_info "推送 configs/ 目录..."
        scp_transfer "configs" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/" true
    fi
    
    # 推送database目录（如果存在）
    if [[ -d "database" ]]; then
        log_info "推送 database/ 目录..."
        scp_transfer "database" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/" true
    fi
    
    # 设置deploy.sh执行权限
    log_info "设置deploy.sh执行权限..."
    ssh_execute "chmod +x $REMOTE_PATH/deploy.sh"
    
    log_success "配置文件推送完成"
}

# ===========================================
# 推送Docker镜像
# ===========================================
push_docker_images() {
    if [[ "$SKIP_IMAGES" == "true" ]]; then
        log_info "跳过Docker镜像推送"
        return 0
    fi
    
    log_step "推送Docker镜像..."
    
    # 根据环境确定镜像标签
    local env_tag
    case $ENVIRONMENT in
        production) env_tag="prod" ;;
        staging) env_tag="staging" ;;
        development) env_tag="dev" ;;
    esac
    
    # 如果指定了自定义标签，使用自定义标签，否则使用环境标签
    local image_tag
    if [[ "$TAG" != "latest" ]]; then
        image_tag="$TAG"
    else
        image_tag="$env_tag"
    fi
    
    # 根据参数确定要推送的镜像
    local images=()
    if [[ "$FRONTEND_ONLY" == "true" ]]; then
        images=("${FRONTEND_IMAGE}:${image_tag}")
        log_info "仅推送前端镜像"
    elif [[ "$BACKEND_ONLY" == "true" ]]; then
        images=("${BACKEND_IMAGE}:${image_tag}")
        log_info "仅推送后端镜像"
    else
        images=("${FRONTEND_IMAGE}:${image_tag}" "${BACKEND_IMAGE}:${image_tag}")
        log_info "推送前端和后端镜像"
    fi
    
    for image in "${images[@]}"; do
        log_info "处理镜像: $image"
        
        # 检查本地镜像是否存在
        if ! docker images --format "{{.Repository}}:{{.Tag}}" | grep -q "^$image$"; then
            log_error "本地镜像不存在: $image"
            log_info "请先运行构建脚本: ./build.sh -e $ENVIRONMENT"
            if [[ "$TAG" != "latest" ]]; then
                log_info "或者: ./build.sh -e $ENVIRONMENT -t $TAG"
            fi
            exit 1
        fi
        
        # 导出镜像到tar文件
        local image_file="$TEMP_DIR/$(echo $image | tr ':/' '_').tar"
        log_info "导出镜像到: $image_file"
        docker save -o "$image_file" "$image"
        
        # 推送镜像文件到远程服务器
        log_info "推送镜像文件到远程服务器..."
        scp_transfer "$image_file" "$REMOTE_USER@$REMOTE_HOST:$REMOTE_PATH/"
        
        # 在远程服务器加载镜像
        local remote_image_file="$REMOTE_PATH/$(basename $image_file)"
        log_info "在远程服务器加载镜像..."
        ssh_execute "docker load -i $remote_image_file && rm $remote_image_file"
        
        log_success "镜像推送完成: $image"
    done
    
    log_success "Docker镜像推送完成"
}

# ===========================================
# 验证推送结果
# ===========================================
verify_push() {
    log_step "验证推送结果..."
    
    # 验证配置文件
    if [[ "$SKIP_FILES" != "true" ]]; then
        log_info "验证配置文件..."
        local files=("docker-compose.${ENVIRONMENT}.yml" "deploy.sh" "environments")
        for file in "${files[@]}"; do
            if ssh_execute "test -e $REMOTE_PATH/$file" false; then
                log_success "✓ $file"
            else
                log_error "✗ $file 不存在"
            fi
        done
    fi
    
    # 验证Docker镜像
    if [[ "$SKIP_IMAGES" != "true" ]]; then
        log_info "验证Docker镜像..."
        
        # 根据环境确定镜像标签
        local env_tag
        case $ENVIRONMENT in
            production) env_tag="prod" ;;
            staging) env_tag="staging" ;;
            development) env_tag="dev" ;;
        esac
        
        # 如果指定了自定义标签，使用自定义标签，否则使用环境标签
        local image_tag
        if [[ "$TAG" != "latest" ]]; then
            image_tag="$TAG"
        else
            image_tag="$env_tag"
        fi
        
        # 根据参数确定要验证的镜像
        local images=()
        if [[ "$FRONTEND_ONLY" == "true" ]]; then
            images=("${FRONTEND_IMAGE}:${image_tag}")
        elif [[ "$BACKEND_ONLY" == "true" ]]; then
            images=("${BACKEND_IMAGE}:${image_tag}")
        else
            images=("${FRONTEND_IMAGE}:${image_tag}" "${BACKEND_IMAGE}:${image_tag}")
        fi
        
        for image in "${images[@]}"; do
            if ssh_execute "docker images --format '{{.Repository}}:{{.Tag}}' | grep -q '^$image$'" false; then
                log_success "✓ $image"
            else
                log_error "✗ $image 不存在"
            fi
        done
    fi
    
    log_success "推送结果验证完成"
}

# ===========================================
# 显示部署信息
# ===========================================
show_deploy_info() {
    log_info "推送完成! 部署信息:"
    echo "----------------------------------------"
    echo "远程服务器: $REMOTE_USER@$REMOTE_HOST:$SSH_PORT"
    echo "部署路径: $REMOTE_PATH"
    echo "环境: $ENVIRONMENT"
    echo "镜像标签: $TAG"
    
    # 显示推送的镜像类型
    if [[ "$SKIP_IMAGES" != "true" ]]; then
        if [[ "$FRONTEND_ONLY" == "true" ]]; then
            echo "推送镜像: 仅前端镜像"
        elif [[ "$BACKEND_ONLY" == "true" ]]; then
            echo "推送镜像: 仅后端镜像"
        else
            echo "推送镜像: 前端和后端镜像"
        fi
    else
        echo "推送镜像: 跳过"
    fi
    
    echo "----------------------------------------"
    echo "下一步操作:"
    echo "1. 登录远程服务器:"
    echo "   ssh -p $SSH_PORT $REMOTE_USER@$REMOTE_HOST"
    echo ""
    echo "2. 进入部署目录:"
    echo "   cd $REMOTE_PATH"
    echo ""
    echo "3. 启动服务:"
    echo "   ./deploy.sh start -e $ENVIRONMENT"
    echo ""
    echo "4. 查看服务状态:"
    echo "   ./deploy.sh status -e $ENVIRONMENT"
    echo ""
    echo "5. 查看服务日志:"
    echo "   ./deploy.sh logs -e $ENVIRONMENT"
    echo "----------------------------------------"
}

# ===========================================
# 主函数
# ===========================================
main() {
    # 显示脚本信息
    log_info "若依框架Docker部署 - 推送脚本"
    log_info "执行时间: $(date '+%Y-%m-%d %H:%M:%S')"
    
    # 解析参数
    parse_args "$@"
    
    # 检查系统依赖
    check_prerequisites
    
    # 测试连接
    test_connection
    
    # 如果连接测试成功但不是使用SSH密钥，启动SSH主连接进行连接复用
    if ! ssh -p "$SSH_PORT" -o ConnectTimeout=10 -o BatchMode=yes "$REMOTE_USER@$REMOTE_HOST" "echo 'test'" &>/dev/null; then
        log_info "检测到密码认证，启用SSH连接复用以减少密码输入次数"
        start_ssh_master
    fi
    
    # 准备推送
    prepare_push
    
    # 推送配置文件
    push_config_files
    
    # 推送Docker镜像
    push_docker_images
    
    # 验证推送结果
    verify_push
    
    # 显示部署信息
    show_deploy_info
    
    log_success "推送脚本执行完成!"
}

# ===========================================
# 错误处理和清理
# ===========================================
cleanup() {
    # 停止SSH主连接
    stop_ssh_master
    
    # 清理临时文件
    if [[ -n "$TEMP_DIR" && -d "$TEMP_DIR" ]]; then
        log_info "清理临时文件..."
        rm -rf "$TEMP_DIR"
        log_success "临时文件清理完成"
    fi
}

trap 'log_error "脚本执行被中断"; cleanup; exit 1' INT TERM
trap 'cleanup' EXIT

# 执行主函数
main "$@"