#!/bin/bash

# Docker镜像导出脚本
# 用于将构建好的Docker镜像导出为tar文件，便于离线部署

set -e

# 脚本配置
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
EXPORT_DIR="${PROJECT_ROOT}/docker-images"
LOG_FILE="${EXPORT_DIR}/export.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
Docker镜像导出脚本

用法: $0 [选项]

选项:
    -h, --help          显示此帮助信息
    -o, --output DIR    指定导出目录 (默认: ./docker-images)
    -c, --compress      启用压缩 (使用gzip)
    -v, --verify        导出后验证镜像完整性
    --custom-images     导出自定义镜像列表 (用逗号分隔)
    --skip-build        跳过镜像构建，直接导出现有镜像

示例:
    $0                                    # 导出所有镜像
    $0 -c -v                             # 导出并压缩，验证完整性
    $0 -o /tmp/images --compress          # 导出到指定目录并压缩
    $0 --custom-images "mysql:8.0,redis:7-alpine"  # 导出指定镜像

EOF
}

# 默认配置
COMPRESS=false
VERIFY=false
SKIP_BUILD=false
CUSTOM_IMAGES=""

# 解析命令行参数
while [[ $# -gt 0 ]]; do
    case $1 in
        -h|--help)
            show_help
            exit 0
            ;;
        -o|--output)
            EXPORT_DIR="$2"
            shift 2
            ;;
        -c|--compress)
            COMPRESS=true
            shift
            ;;
        -v|--verify)
            VERIFY=true
            shift
            ;;
        --custom-images)
            CUSTOM_IMAGES="$2"
            shift 2
            ;;
        --skip-build)
            SKIP_BUILD=true
            shift
            ;;
        *)
            log_error "未知参数: $1"
            show_help
            exit 1
            ;;
    esac
done

# 创建导出目录
mkdir -p "$EXPORT_DIR"
mkdir -p "$(dirname "$LOG_FILE")"

log "开始Docker镜像导出过程..."
log "导出目录: $EXPORT_DIR"

# 检查Docker是否运行
if ! docker info >/dev/null 2>&1; then
    log_error "Docker未运行或无法访问"
    exit 1
fi

# 定义需要导出的镜像列表
if [[ -n "$CUSTOM_IMAGES" ]]; then
    IFS=',' read -ra IMAGES <<< "$CUSTOM_IMAGES"
else
    IMAGES=(
        "knowledge-base-app:latest"
        "mysql:8.0"
        "redis:7-alpine"
        "getmeili/meilisearch:v1.5"
    )
fi

# 构建自定义镜像（如果需要）
if [[ "$SKIP_BUILD" == false ]]; then
    log "检查是否需要构建自定义镜像..."
    
    # 检查knowledge-base-app镜像是否存在
    if ! docker image inspect knowledge-base-app:latest >/dev/null 2>&1; then
        log "构建knowledge-base-app镜像..."
        cd "$PROJECT_ROOT"
        
        if docker build --platform linux/amd64 -t knowledge-base-app:latest -f Dockerfile --target production .; then
            log_success "knowledge-base-app镜像构建成功"
        else
            log_error "knowledge-base-app镜像构建失败"
            exit 1
        fi
    else
        log_success "knowledge-base-app镜像已存在"
    fi
fi

# 拉取外部镜像
log "拉取外部镜像..."
for image in "mysql:8.0" "redis:7-alpine" "getmeili/meilisearch:v1.5"; do
    if [[ " ${IMAGES[@]} " =~ " ${image} " ]]; then
        log "拉取镜像: $image"
        if docker pull --platform linux/amd64 "$image"; then
            log_success "镜像 $image 拉取成功"
        else
            log_warning "镜像 $image 拉取失败，将尝试使用本地镜像"
        fi
    fi
done

# 导出镜像
log "开始导出镜像..."
EXPORTED_FILES=()

for image in "${IMAGES[@]}"; do
    log "导出镜像: $image"
    
    # 检查镜像是否存在
    if ! docker image inspect "$image" >/dev/null 2>&1; then
        log_error "镜像 $image 不存在，跳过导出"
        continue
    fi
    
    # 生成文件名（替换特殊字符）
    filename=$(echo "$image" | sed 's/[\/:]/_/g')
    output_file="${EXPORT_DIR}/${filename}.tar"
    
    # 导出镜像
    if docker save -o "$output_file" "$image"; then
        log_success "镜像 $image 导出成功: $output_file"
        EXPORTED_FILES+=("$output_file")
        
        # 显示文件大小
        size=$(du -h "$output_file" | cut -f1)
        log "文件大小: $size"
    else
        log_error "镜像 $image 导出失败"
        continue
    fi
    
    # 压缩文件（如果启用）
    if [[ "$COMPRESS" == true ]]; then
        log "压缩文件: $output_file"
        if gzip "$output_file"; then
            compressed_file="${output_file}.gz"
            log_success "文件压缩成功: $compressed_file"
            
            # 更新文件列表
            EXPORTED_FILES=("${EXPORTED_FILES[@]/$output_file}")
            EXPORTED_FILES+=("$compressed_file")
            
            # 显示压缩后大小
            compressed_size=$(du -h "$compressed_file" | cut -f1)
            original_size=$(du -h "$output_file" 2>/dev/null | cut -f1 || echo "N/A")
            log "压缩后大小: $compressed_size (原始: $original_size)"
        else
            log_error "文件压缩失败"
        fi
    fi
done

# 验证导出的镜像（如果启用）
if [[ "$VERIFY" == true ]]; then
    log "验证导出的镜像..."
    
    for file in "${EXPORTED_FILES[@]}"; do
        if [[ -f "$file" ]]; then
            log "验证文件: $file"
            
            # 检查文件完整性
            if [[ "$file" == *.gz ]]; then
                # 验证gzip文件
                if gzip -t "$file"; then
                    log_success "压缩文件完整性验证通过"
                else
                    log_error "压缩文件完整性验证失败"
                fi
            else
                # 验证tar文件
                if tar -tf "$file" >/dev/null 2>&1; then
                    log_success "tar文件完整性验证通过"
                else
                    log_error "tar文件完整性验证失败"
                fi
            fi
        fi
    done
fi

# 生成镜像清单
manifest_file="${EXPORT_DIR}/images-manifest.txt"
log "生成镜像清单: $manifest_file"

cat > "$manifest_file" << EOF
# Docker镜像导出清单
# 生成时间: $(date)
# 导出目录: $EXPORT_DIR
# 压缩: $COMPRESS
# 验证: $VERIFY

EOF

for file in "${EXPORTED_FILES[@]}"; do
    if [[ -f "$file" ]]; then
        filename=$(basename "$file")
        size=$(du -h "$file" | cut -f1)
        checksum=$(sha256sum "$file" | cut -d' ' -f1)
        
        echo "文件: $filename" >> "$manifest_file"
        echo "大小: $size" >> "$manifest_file"
        echo "SHA256: $checksum" >> "$manifest_file"
        echo "" >> "$manifest_file"
    fi
done

# 生成导入脚本
import_script="${EXPORT_DIR}/import-images.sh"
log "生成导入脚本: $import_script"

cat > "$import_script" << 'EOF'
#!/bin/bash

# Docker镜像导入脚本
# 自动生成，用于导入导出的Docker镜像

set -e

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

echo "开始导入Docker镜像..."

# 检查Docker是否运行
if ! docker info >/dev/null 2>&1; then
    echo "错误: Docker未运行或无法访问"
    exit 1
fi

# 导入所有tar文件
for file in "$SCRIPT_DIR"/*.tar*; do
    if [[ -f "$file" ]]; then
        echo "导入镜像: $(basename "$file")"
        
        if [[ "$file" == *.gz ]]; then
            # 解压并导入
            if gunzip -c "$file" | docker load; then
                echo "✓ 镜像导入成功"
            else
                echo "✗ 镜像导入失败"
            fi
        else
            # 直接导入
            if docker load -i "$file"; then
                echo "✓ 镜像导入成功"
            else
                echo "✗ 镜像导入失败"
            fi
        fi
    fi
done

echo "镜像导入完成"
echo "可用镜像列表:"
docker images
EOF

chmod +x "$import_script"

# 显示总结
log_success "镜像导出完成！"
log "导出的文件:"
for file in "${EXPORTED_FILES[@]}"; do
    if [[ -f "$file" ]]; then
        size=$(du -h "$file" | cut -f1)
        log "  - $(basename "$file") ($size)"
    fi
done

log "生成的文件:"
log "  - images-manifest.txt (镜像清单)"
log "  - import-images.sh (导入脚本)"

total_size=$(du -sh "$EXPORT_DIR" | cut -f1)
log "总大小: $total_size"

log_success "所有文件已保存到: $EXPORT_DIR"