#!/bin/bash

# OpenEuler服务器部署脚本
# 用于在OpenEuler服务器上部署Laravel知识库系统

set -e

# 脚本配置
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_NAME="knowledge-base"
DEPLOY_DIR="/opt/${PROJECT_NAME}"
BACKUP_DIR="/opt/${PROJECT_NAME}-backup"
LOG_FILE="/var/log/${PROJECT_NAME}-deploy.log"

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

# 日志函数
log() {
    echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
}

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

log_warning() {
    echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] ⚠${NC} $1" | tee -a "$LOG_FILE"
}

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

# 显示帮助信息
show_help() {
    cat << EOF
OpenEuler服务器部署脚本

用法: $0 [选项] [镜像目录]

选项:
    -h, --help              显示此帮助信息
    -d, --deploy-dir DIR    部署目录 (默认: /opt/knowledge-base)
    -b, --backup            部署前备份现有安装
    -u, --update            更新现有部署
    -r, --rollback          回滚到上一个版本
    --skip-docker-install   跳过Docker安装
    --skip-images           跳过镜像导入
    --skip-env-setup        跳过环境配置
    --dry-run               仅显示将要执行的操作

参数:
    镜像目录                包含Docker镜像文件的目录

示例:
    $0 /path/to/docker-images           # 全新部署
    $0 -u /path/to/docker-images        # 更新现有部署
    $0 -b -d /custom/path               # 备份并部署到自定义目录
    $0 --rollback                       # 回滚到上一个版本

EOF
}

# 默认配置
BACKUP=false
UPDATE=false
ROLLBACK=false
SKIP_DOCKER_INSTALL=false
SKIP_IMAGES=false
SKIP_ENV_SETUP=false
DRY_RUN=false
IMAGES_DIR=""

# 解析命令行参数
while [[ $# -gt 0 ]]; do
    case $1 in
        -h|--help)
            show_help
            exit 0
            ;;
        -d|--deploy-dir)
            DEPLOY_DIR="$2"
            BACKUP_DIR="${DEPLOY_DIR}-backup"
            shift 2
            ;;
        -b|--backup)
            BACKUP=true
            shift
            ;;
        -u|--update)
            UPDATE=true
            shift
            ;;
        -r|--rollback)
            ROLLBACK=true
            shift
            ;;
        --skip-docker-install)
            SKIP_DOCKER_INSTALL=true
            shift
            ;;
        --skip-images)
            SKIP_IMAGES=true
            shift
            ;;
        --skip-env-setup)
            SKIP_ENV_SETUP=true
            shift
            ;;
        --dry-run)
            DRY_RUN=true
            shift
            ;;
        -*)
            log_error "未知参数: $1"
            show_help
            exit 1
            ;;
        *)
            IMAGES_DIR="$1"
            shift
            ;;
    esac
done

# 检查是否以root权限运行
if [[ $EUID -ne 0 ]]; then
    log_error "此脚本需要root权限运行"
    exit 1
fi

# 创建日志目录
mkdir -p "$(dirname "$LOG_FILE")"

log "开始OpenEuler服务器部署..."
log "部署目录: $DEPLOY_DIR"
log "备份目录: $BACKUP_DIR"
log "镜像目录: $IMAGES_DIR"

# 检查系统信息
check_system() {
    log "检查系统信息..."
    
    # 检查操作系统
    if [[ -f /etc/os-release ]]; then
        source /etc/os-release
        log "操作系统: $NAME $VERSION"
        
        if [[ "$ID" != "openEuler" ]] && [[ "$ID_LIKE" != *"rhel"* ]]; then
            log_warning "此脚本专为OpenEuler设计，当前系统: $NAME"
            read -p "是否继续? (y/N): " -n 1 -r
            echo
            if [[ ! $REPLY =~ ^[Yy]$ ]]; then
                exit 1
            fi
        fi
    else
        log_warning "无法检测操作系统版本"
    fi
    
    # 检查架构
    local arch=$(uname -m)
    log "系统架构: $arch"
    
    if [[ "$arch" != "x86_64" ]]; then
        log_warning "推荐使用x86_64架构，当前: $arch"
    fi
    
    # 检查内存
    local memory=$(free -h | awk '/^Mem:/ {print $2}')
    log "系统内存: $memory"
    
    # 检查磁盘空间
    local disk_space=$(df -h / | awk 'NR==2 {print $4}')
    log "可用磁盘空间: $disk_space"
}

# 安装Docker
install_docker() {
    if [[ "$SKIP_DOCKER_INSTALL" == true ]]; then
        log "跳过Docker安装"
        return 0
    fi
    
    log "检查Docker安装状态..."
    
    if command -v docker >/dev/null 2>&1; then
        local docker_version=$(docker --version)
        log "Docker已安装: $docker_version"
        
        # 检查Docker是否运行
        if systemctl is-active --quiet docker; then
            log_success "Docker服务正在运行"
        else
            log "启动Docker服务..."
            systemctl start docker
            systemctl enable docker
            log_success "Docker服务已启动"
        fi
        return 0
    fi
    
    log "安装Docker..."
    
    if [[ "$DRY_RUN" == true ]]; then
        log "[DRY RUN] 将安装Docker"
        return 0
    fi
    
    # 更新系统包
    dnf update -y
    
    # 安装必要的包
    dnf install -y dnf-plugins-core
    
    # 添加Docker仓库
    dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    
    # 安装Docker
    dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
    
    # 启动Docker服务
    systemctl start docker
    systemctl enable docker
    
    # 验证安装
    if docker --version >/dev/null 2>&1; then
        log_success "Docker安装成功"
    else
        log_error "Docker安装失败"
        exit 1
    fi
    
    # 添加当前用户到docker组（如果不是root）
    if [[ -n "$SUDO_USER" ]]; then
        usermod -aG docker "$SUDO_USER"
        log "已将用户 $SUDO_USER 添加到docker组"
    fi
}

# 安装Docker Compose
install_docker_compose() {
    log "检查Docker Compose..."
    
    if docker compose version >/dev/null 2>&1; then
        local compose_version=$(docker compose version)
        log_success "Docker Compose已安装: $compose_version"
        return 0
    fi
    
    log "安装Docker Compose..."
    
    if [[ "$DRY_RUN" == true ]]; then
        log "[DRY RUN] 将安装Docker Compose"
        return 0
    fi
    
    # Docker Compose通常随Docker一起安装
    # 如果没有，可以手动安装
    if ! docker compose version >/dev/null 2>&1; then
        log_error "Docker Compose未找到，请手动安装"
        exit 1
    fi
}

# 备份现有部署
backup_existing() {
    if [[ "$BACKUP" == false ]]; then
        return 0
    fi
    
    if [[ ! -d "$DEPLOY_DIR" ]]; then
        log "没有现有部署需要备份"
        return 0
    fi
    
    log "备份现有部署..."
    
    if [[ "$DRY_RUN" == true ]]; then
        log "[DRY RUN] 将备份 $DEPLOY_DIR 到 $BACKUP_DIR"
        return 0
    fi
    
    # 停止现有服务
    if [[ -f "$DEPLOY_DIR/docker-compose.yml" ]]; then
        log "停止现有服务..."
        cd "$DEPLOY_DIR"
        docker compose down || true
    fi
    
    # 创建备份
    local backup_name="${BACKUP_DIR}-$(date +%Y%m%d-%H%M%S)"
    
    if cp -r "$DEPLOY_DIR" "$backup_name"; then
        log_success "备份创建成功: $backup_name"
        
        # 创建符号链接到最新备份
        rm -f "$BACKUP_DIR"
        ln -s "$backup_name" "$BACKUP_DIR"
    else
        log_error "备份创建失败"
        exit 1
    fi
}

# 回滚部署
rollback_deployment() {
    if [[ "$ROLLBACK" == false ]]; then
        return 0
    fi
    
    log "回滚部署..."
    
    if [[ ! -L "$BACKUP_DIR" ]] || [[ ! -d "$BACKUP_DIR" ]]; then
        log_error "没有找到备份，无法回滚"
        exit 1
    fi
    
    if [[ "$DRY_RUN" == true ]]; then
        log "[DRY RUN] 将从 $BACKUP_DIR 回滚"
        return 0
    fi
    
    # 停止当前服务
    if [[ -f "$DEPLOY_DIR/docker-compose.yml" ]]; then
        log "停止当前服务..."
        cd "$DEPLOY_DIR"
        docker compose down || true
    fi
    
    # 恢复备份
    rm -rf "$DEPLOY_DIR"
    cp -r "$BACKUP_DIR" "$DEPLOY_DIR"
    
    # 启动服务
    cd "$DEPLOY_DIR"
    docker compose up -d
    
    log_success "回滚完成"
    exit 0
}

# 导入Docker镜像
import_images() {
    if [[ "$SKIP_IMAGES" == true ]]; then
        log "跳过镜像导入"
        return 0
    fi
    
    if [[ -z "$IMAGES_DIR" ]] || [[ ! -d "$IMAGES_DIR" ]]; then
        log_error "镜像目录不存在或未指定: $IMAGES_DIR"
        exit 1
    fi
    
    log "导入Docker镜像..."
    
    # 查找镜像文件
    local image_files=($(find "$IMAGES_DIR" -name "*.tar*" -type f))
    
    if [[ ${#image_files[@]} -eq 0 ]]; then
        log_error "在 $IMAGES_DIR 中没有找到镜像文件"
        exit 1
    fi
    
    log "找到 ${#image_files[@]} 个镜像文件"
    
    if [[ "$DRY_RUN" == true ]]; then
        for file in "${image_files[@]}"; do
            log "[DRY RUN] 将导入: $(basename "$file")"
        done
        return 0
    fi
    
    # 导入镜像
    for file in "${image_files[@]}"; do
        local filename=$(basename "$file")
        log "导入镜像: $filename"
        
        if [[ "$file" == *.gz ]]; then
            # 解压并导入
            if gunzip -c "$file" | docker load; then
                log_success "镜像导入成功: $filename"
            else
                log_error "镜像导入失败: $filename"
                exit 1
            fi
        else
            # 直接导入
            if docker load -i "$file"; then
                log_success "镜像导入成功: $filename"
            else
                log_error "镜像导入失败: $filename"
                exit 1
            fi
        fi
    done
    
    # 显示导入的镜像
    log "已导入的镜像:"
    docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.Size}}"
}

# 设置部署环境
setup_deployment() {
    log "设置部署环境..."
    
    if [[ "$DRY_RUN" == true ]]; then
        log "[DRY RUN] 将创建部署目录: $DEPLOY_DIR"
        return 0
    fi
    
    # 创建部署目录
    mkdir -p "$DEPLOY_DIR"
    cd "$DEPLOY_DIR"
    
    # 复制配置文件（如果镜像目录包含）
    if [[ -n "$IMAGES_DIR" ]]; then
        # 查找配置文件
        local config_files=(
            "docker-compose.yml"
            ".env.production"
            "docker"
        )
        
        for config in "${config_files[@]}"; do
            if [[ -e "$IMAGES_DIR/../$config" ]]; then
                log "复制配置: $config"
                cp -r "$IMAGES_DIR/../$config" .
            fi
        done
    fi
    
    # 创建必要的目录
    mkdir -p storage/{mysql,redis,meilisearch,app,logs}
    mkdir -p storage/logs/{app,queue}
    
    # 设置权限
    chown -R 1000:1000 storage/
    chmod -R 755 storage/
    
    log_success "部署环境设置完成"
}

# 配置环境变量
setup_environment() {
    if [[ "$SKIP_ENV_SETUP" == true ]]; then
        log "跳过环境配置"
        return 0
    fi
    
    log "配置环境变量..."
    
    local env_file="$DEPLOY_DIR/.env"
    
    if [[ "$DRY_RUN" == true ]]; then
        log "[DRY RUN] 将创建环境配置文件: $env_file"
        return 0
    fi
    
    # 生成随机密钥
    local app_key="base64:$(openssl rand -base64 32)"
    local db_password=$(openssl rand -base64 16)
    local meilisearch_key=$(openssl rand -base64 32)
    
    # 创建环境配置文件
    cat > "$env_file" << EOF
# Laravel知识库系统 - 生产环境配置
# 生成时间: $(date)

# 应用配置
APP_NAME="知识库系统"
APP_ENV=production
APP_KEY=$app_key
APP_DEBUG=false
APP_URL=http://localhost

# 数据库配置
DB_CONNECTION=mysql
DB_HOST=mysql
DB_PORT=3306
DB_DATABASE=knowledge_base
DB_USERNAME=knowledge_user
DB_PASSWORD=$db_password

# Redis配置
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=

# 缓存配置
CACHE_STORE=redis
SESSION_DRIVER=redis
QUEUE_CONNECTION=redis

# 搜索配置
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://meilisearch:7700
MEILISEARCH_KEY=$meilisearch_key

# 日志配置
LOG_CHANNEL=stack
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=error

# 文件系统
FILESYSTEM_DISK=local

# 邮件配置
MAIL_MAILER=log
MAIL_HOST=localhost
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS="hello@example.com"
MAIL_FROM_NAME="\${APP_NAME}"
EOF
    
    log_success "环境配置文件创建成功"
    log_warning "请根据实际情况修改 $env_file 中的配置"
}

# 启动服务
start_services() {
    log "启动服务..."
    
    if [[ "$DRY_RUN" == true ]]; then
        log "[DRY RUN] 将启动Docker Compose服务"
        return 0
    fi
    
    cd "$DEPLOY_DIR"
    
    # 检查配置文件
    if [[ ! -f "docker-compose.yml" ]]; then
        log_error "docker-compose.yml 文件不存在"
        exit 1
    fi
    
    if [[ ! -f ".env" ]]; then
        log_error ".env 文件不存在"
        exit 1
    fi
    
    # 启动服务
    if docker compose up -d; then
        log_success "服务启动成功"
    else
        log_error "服务启动失败"
        exit 1
    fi
    
    # 等待服务就绪
    log "等待服务就绪..."
    sleep 30
    
    # 检查服务状态
    docker compose ps
    
    # 运行Laravel初始化命令
    log "运行Laravel初始化..."
    docker compose exec -T app php artisan migrate --force || log_warning "数据库迁移失败"
    docker compose exec -T app php artisan storage:link || log_warning "存储链接创建失败"
    
    log_success "部署完成！"
}

# 显示部署信息
show_deployment_info() {
    log "部署信息:"
    log "部署目录: $DEPLOY_DIR"
    log "访问地址: http://$(hostname -I | awk '{print $1}')"
    log "管理命令:"
    log "  查看日志: cd $DEPLOY_DIR && docker compose logs -f"
    log "  重启服务: cd $DEPLOY_DIR && docker compose restart"
    log "  停止服务: cd $DEPLOY_DIR && docker compose down"
    log "  更新应用: cd $DEPLOY_DIR && docker compose pull && docker compose up -d"
}

# 主执行流程
main() {
    check_system
    
    # 处理回滚
    rollback_deployment
    
    # 备份现有部署
    backup_existing
    
    # 安装Docker
    install_docker
    install_docker_compose
    
    # 导入镜像
    import_images
    
    # 设置部署环境
    setup_deployment
    
    # 配置环境
    setup_environment
    
    # 启动服务
    start_services
    
    # 显示部署信息
    show_deployment_info
}

# 执行主流程
main