#!/bin/bash

#===============================================================================
# VPS 快照备份脚本 v3.15
# 支持: Ubuntu, Debian, CentOS, Alpine
# 功能: 智能识别应用 + Docker迁移 + 数据备份 + Telegram通知
#===============================================================================

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'

CONFIG_FILE="/etc/vps-snapshot.conf"
LOG_FILE="/var/log/vps-snapshot.log"

print_banner() {
    echo -e "${BLUE}"
    echo "╔═══════════════════════════════════════════════════════════╗"
    echo "║           VPS 快照备份脚本 v3.15                           ║"
    echo "║       智能识别 + Docker迁移 + 数据备份                    ║"
    echo "╚═══════════════════════════════════════════════════════════╝"
    echo -e "${NC}"
}

log() { echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] $1${NC}"; echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"; }
error() { echo -e "${RED}[ERROR] $1${NC}"; echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: $1" >> "$LOG_FILE"; }
warn() { echo -e "${YELLOW}[WARN] $1${NC}"; }
info() { echo -e "${CYAN}[INFO] $1${NC}"; }

#===============================================================================
# 系统检测
#===============================================================================

detect_os() {
    if [ -f /etc/os-release ]; then . /etc/os-release; echo "$ID"
    elif [ -f /etc/redhat-release ]; then echo "centos"
    else echo "unknown"; fi
}

install_deps() {
    local os=$(detect_os)
    log "检测系统: $os"
    case $os in
        ubuntu|debian) apt-get update -qq && apt-get install -y -qq rsync sshpass curl tar gzip jq ;;
        centos|rhel|fedora) yum install -y -q rsync sshpass curl tar gzip jq ;;
        alpine) apk add --no-cache rsync sshpass curl tar gzip jq ;;
        *) error "不支持的系统: $os"; exit 1 ;;
    esac
}

check_deps() {
    local missing=""
    command -v rsync &>/dev/null || missing+=" rsync"
    # sshpass is optional when using key auth
    command -v jq &>/dev/null || missing+=" jq"
    
    if [ -n "$missing" ]; then
        warn "缺少依赖:$missing，正在安装..."
        install_deps
    fi
}

#===============================================================================
# 智能识别应用
#===============================================================================

detect_apps() {
    info "🔍 扫描已安装的应用..."
    echo ""
    
    local apps=()
    
    # Docker
    if command -v docker &>/dev/null && docker info &>/dev/null; then
        local containers=$(docker ps -q | wc -l)
        local images=$(docker images -q | wc -l)
        echo -e "  ${GREEN}✓${NC} Docker: $containers 个运行中容器, $images 个镜像"
        apps+=("docker")
    fi
    
    # Docker Compose
    if command -v docker-compose &>/dev/null || docker compose version &>/dev/null 2>&1; then
        local compose_files=$(find /opt /root /home -name "docker-compose*.yml" -o -name "compose*.yml" 2>/dev/null | wc -l)
        echo -e "  ${GREEN}✓${NC} Docker Compose: $compose_files 个配置文件"
        apps+=("compose")
    fi
    
    # Nginx
    if command -v nginx &>/dev/null || [ -d /etc/nginx ]; then
        echo -e "  ${GREEN}✓${NC} Nginx"
        apps+=("nginx")
    fi
    
    # MySQL/MariaDB
    if command -v mysql &>/dev/null || [ -d /var/lib/mysql ]; then
        echo -e "  ${GREEN}✓${NC} MySQL/MariaDB"
        apps+=("mysql")
    fi
    
    # PostgreSQL
    if command -v psql &>/dev/null || [ -d /var/lib/postgresql ]; then
        echo -e "  ${GREEN}✓${NC} PostgreSQL"
        apps+=("postgresql")
    fi
    
    # Redis
    if command -v redis-cli &>/dev/null || [ -d /var/lib/redis ]; then
        echo -e "  ${GREEN}✓${NC} Redis"
        apps+=("redis")
    fi
    
    # MongoDB
    if command -v mongod &>/dev/null || [ -d /var/lib/mongodb ]; then
        echo -e "  ${GREEN}✓${NC} MongoDB"
        apps+=("mongodb")
    fi
    
    # Node.js/NPM
    if command -v node &>/dev/null; then
        local node_ver=$(node -v 2>/dev/null || echo "unknown")
        local npm_global=$(npm list -g --depth=0 2>/dev/null | wc -l)
        echo -e "  ${GREEN}✓${NC} Node.js $node_ver ($npm_global 个全局包)"
        apps+=("nodejs")
    fi
    
    # PM2
    if command -v pm2 &>/dev/null; then
        local pm2_apps=$(pm2 jlist 2>/dev/null | jq length 2>/dev/null || echo "0")
        echo -e "  ${GREEN}✓${NC} PM2: $pm2_apps 个应用"
        apps+=("pm2")
    fi
    
    # Python/Pip
    if command -v python3 &>/dev/null; then
        local py_ver=$(python3 --version 2>/dev/null | cut -d' ' -f2)
        echo -e "  ${GREEN}✓${NC} Python $py_ver"
        apps+=("python")
    fi
    
    # PHP
    if command -v php &>/dev/null; then
        local php_ver=$(php -v 2>/dev/null | head -1 | cut -d' ' -f2)
        echo -e "  ${GREEN}✓${NC} PHP $php_ver"
        apps+=("php")
    fi
    
    # 1Panel
    if [ -d /opt/1panel ] || command -v 1pctl &>/dev/null; then
        echo -e "  ${GREEN}✓${NC} 1Panel"
        apps+=("1panel")
    fi
    
    # 宝塔
    if [ -d /www/server/panel ]; then
        echo -e "  ${GREEN}✓${NC} 宝塔面板"
        apps+=("bt")
    fi
    
    echo ""
    # 输出应用列表（供脚本解析）
    echo "APPS:${apps[*]}"
}

#===============================================================================
# Docker 专用迁移
#===============================================================================

docker_export() {
    local output_dir="${1:-/var/snapshots}"
    mkdir -p "$output_dir"
    
    log "📦 导出 Docker 数据..."
    
    # 导出所有镜像
    local images=$(docker images --format "{{.Repository}}:{{.Tag}}" | grep -v "<none>")
    if [ -n "$images" ]; then
        log "导出镜像..."
        docker save $images | gzip > "$output_dir/docker-images.tar.gz"
        info "镜像已保存: docker-images.tar.gz"
    fi
    
    # 导出容器配置
    log "导出容器配置..."
    docker ps -a --format '{{json .}}' > "$output_dir/docker-containers.json"
    
    # 导出 volumes
    local volumes=$(docker volume ls -q)
    if [ -n "$volumes" ]; then
        log "导出 Volumes..."
        mkdir -p "$output_dir/volumes"
        for vol in $volumes; do
            docker run --rm -v "$vol:/data" -v "$output_dir/volumes:/backup" \
                alpine tar czf "/backup/${vol}.tar.gz" -C /data . 2>/dev/null || true
        done
        info "Volumes 已保存"
    fi
    
    # 导出 docker-compose 文件
    log "查找 docker-compose 文件..."
    find /opt /root /home -name "docker-compose*.yml" -o -name "compose*.yml" 2>/dev/null | while read f; do
        local dir=$(dirname "$f")
        local name=$(echo "$dir" | tr '/' '_')
        mkdir -p "$output_dir/compose"
        cp -r "$dir" "$output_dir/compose/$name" 2>/dev/null || true
    done
    
    log "Docker 数据导出完成"
}

docker_import() {
    local input_dir="${1:-/var/snapshots}"
    
    log "📥 导入 Docker 数据..."
    
    # 检查 Docker 是否安装
    if ! command -v docker &>/dev/null; then
        warn "Docker 未安装，正在安装..."
        curl -fsSL https://get.docker.com | sh
        systemctl start docker
        systemctl enable docker
    fi
    
    # 导入镜像
    if [ -f "$input_dir/docker-images.tar.gz" ]; then
        log "导入镜像..."
        gunzip -c "$input_dir/docker-images.tar.gz" | docker load
        info "镜像导入完成"
    fi
    
    # 恢复 volumes
    if [ -d "$input_dir/volumes" ]; then
        log "恢复 Volumes..."
        for vol_file in "$input_dir/volumes"/*.tar.gz; do
            [ -f "$vol_file" ] || continue
            local vol_name=$(basename "$vol_file" .tar.gz)
            docker volume create "$vol_name" 2>/dev/null || true
            docker run --rm -v "$vol_name:/data" -v "$input_dir/volumes:/backup" \
                alpine tar xzf "/backup/${vol_name}.tar.gz" -C /data 2>/dev/null || true
        done
        info "Volumes 恢复完成"
    fi
    
    # 恢复 compose 项目
    if [ -d "$input_dir/compose" ]; then
        log "恢复 Compose 项目..."
        for proj in "$input_dir/compose"/*; do
            [ -d "$proj" ] || continue
            local name=$(basename "$proj")
            local target="/opt/${name//_//}"
            mkdir -p "$(dirname "$target")"
            cp -r "$proj" "$target" 2>/dev/null || true
        done
        info "Compose 项目已恢复到 /opt"
    fi
    
    log "Docker 数据导入完成"
}

#===============================================================================
# 应用数据备份
#===============================================================================

backup_app_data() {
    local output_dir="${1:-/var/snapshots}"
    
    mkdir -p "$output_dir"
    log "📦 备份应用数据..."
    
    local backup_paths=""
    
    #---------------------------------------------------------------------------
    # 智能扫描系统目录（自动识别有数据的目录）
    #---------------------------------------------------------------------------
    log "🔍 智能扫描系统目录..."
    
    # 核心配置目录 - 必备
    [ -d /etc ] && backup_paths+=" /etc"
    
    # 用户目录
    [ -d /root ] && backup_paths+=" /root"
    [ -d /home ] && [ "$(ls -A /home 2>/dev/null)" ] && backup_paths+=" /home"
    
    # 手动安装的程序和数据
    [ -d /usr/local/bin ] && [ "$(ls -A /usr/local/bin 2>/dev/null)" ] && backup_paths+=" /usr/local/bin"
    [ -d /usr/local/sbin ] && [ "$(ls -A /usr/local/sbin 2>/dev/null)" ] && backup_paths+=" /usr/local/sbin"
    [ -d /usr/local/etc ] && [ "$(ls -A /usr/local/etc 2>/dev/null)" ] && backup_paths+=" /usr/local/etc"
    
    # /usr/local 下的应用目录（x-ui, xray 等）
    if [ -d /usr/local ]; then
        for dir in /usr/local/*/; do
            [ -d "$dir" ] || continue
            local dirname=$(basename "$dir")
            # 跳过标准目录（bin/sbin/etc/lib/share/include/man/src/games）
            case "$dirname" in
                bin|sbin|etc|lib|lib64|share|include|man|src|games|libexec) continue ;;
            esac
            backup_paths+=" $dir"
        done
    fi
    
    # /opt 应用目录（排除 containerd）
    if [ -d /opt ]; then
        for dir in /opt/*/; do
            [ -d "$dir" ] || continue
            local dirname=$(basename "$dir")
            # 跳过 containerd（Docker 内部数据）
            [ "$dirname" = "containerd" ] && continue
            backup_paths+=" $dir"
        done
    fi
    
    # /var/www 网站目录
    [ -d /var/www ] && [ "$(ls -A /var/www 2>/dev/null)" ] && backup_paths+=" /var/www"
    
    # 显示扫描结果
    log "📋 将备份以下目录:"
    for p in $backup_paths; do
        local size=$(du -sh "$p" 2>/dev/null | cut -f1)
        echo "    $p ($size)"
    done
    
    #---------------------------------------------------------------------------
    # 特定应用数据导出（数据库等需要特殊处理）
    #---------------------------------------------------------------------------
    
    # Nginx
    [ -d /etc/nginx ] && log "  ✓ Nginx 配置"
    
    # MySQL
    if [ -d /var/lib/mysql ]; then
        log "导出 MySQL 数据库..."
        mkdir -p "$output_dir/mysql"
        if command -v mysqldump &>/dev/null; then
            mysqldump --all-databases > "$output_dir/mysql/all-databases.sql" 2>/dev/null || true
        fi
    fi
    
    # PostgreSQL
    if [ -d /var/lib/postgresql ]; then
        log "导出 PostgreSQL 数据库..."
        mkdir -p "$output_dir/postgresql"
        if command -v pg_dumpall &>/dev/null; then
            sudo -u postgres pg_dumpall > "$output_dir/postgresql/all-databases.sql" 2>/dev/null || true
        fi
    fi
    
    # Redis
    [ -d /var/lib/redis ] && backup_paths+=" /var/lib/redis"
    
    # MongoDB
    if [ -d /var/lib/mongodb ]; then
        log "导出 MongoDB..."
        mkdir -p "$output_dir/mongodb"
        if command -v mongodump &>/dev/null; then
            mongodump --out "$output_dir/mongodb" 2>/dev/null || true
        fi
    fi
    
    # Node.js/NPM 全局包
    if command -v npm &>/dev/null; then
        log "导出 NPM 全局包列表..."
        npm list -g --depth=0 --json > "$output_dir/npm-global.json" 2>/dev/null || true
    fi
    
    # PM2 应用
    if command -v pm2 &>/dev/null; then
        log "导出 PM2 配置..."
        pm2 save 2>/dev/null || true
        [ -f ~/.pm2/dump.pm2 ] && cp ~/.pm2/dump.pm2 "$output_dir/pm2-dump.json"
    fi
    
    # Python pip
    if command -v pip3 &>/dev/null; then
        log "导出 pip 包列表..."
        pip3 freeze > "$output_dir/pip-requirements.txt" 2>/dev/null || true
    fi
    
    # 1Panel
    [ -d /opt/1panel ] && log "  ✓ 1Panel"
    
    # 宝塔
    [ -d /www ] && backup_paths+=" /www" && log "  ✓ 宝塔面板"
    
    # 打包（排除快照目录和无用文件）
    local snap_dir="${LOCAL_DIR:-/var/snapshots}"
    if [ -n "$backup_paths" ]; then
        log "📦 打包数据目录..."
        tar --exclude='*.sock' --exclude='*.pid' --exclude='node_modules' \
            --exclude='.npm' --exclude='.cache' --exclude='__pycache__' \
            --exclude='*.log' --exclude='/var/log/*' \
            --exclude='/var/cache/*' --exclude='/tmp/*' \
            --exclude="$snap_dir" --exclude='/var/snapshots' \
            --exclude='/root/.cache' --exclude='/root/.local/share/Trash' \
            -czf "$output_dir/app-data.tar.gz" $backup_paths 2>/dev/null || true
        local data_size=$(du -h "$output_dir/app-data.tar.gz" 2>/dev/null | cut -f1)
        info "数据已保存: $output_dir/app-data.tar.gz ($data_size)"
    fi
}

#===============================================================================
# 一键迁移
#===============================================================================

do_migrate() {
    print_banner
    info "🚀 一键迁移向导"
    echo ""
    
    # 检测应用
    local apps_output=$(detect_apps)
    local apps=$(echo "$apps_output" | grep "^APPS:" | sed 's/^APPS://')
    
    echo ""
    read -p "输入目标服务器 IP: " target_ip
    read -p "输入目标服务器端口 [22]: " target_port
    target_port=${target_port:-22}
    read -p "输入目标服务器用户 [root]: " target_user
    target_user=${target_user:-root}
    read -s -p "输入目标服务器密码: " target_pass
    echo ""
    
    # 测试连接
    log "测试连接..."
    if ! sshpass -p "$target_pass" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 \
        -p "$target_port" "$target_user@$target_ip" "echo ok" &>/dev/null; then
        error "无法连接到目标服务器"
        return 1
    fi
    info "连接成功"
    
    local tmp_dir="/tmp/migrate_$$"
    mkdir -p "$tmp_dir"
    
    # Docker 迁移
    if [[ " $apps " =~ " docker " ]]; then
        log "检测到 Docker，开始导出..."
        docker_export "$tmp_dir"
    fi
    
    # 应用数据备份
    log "备份应用数据..."
    backup_app_data "$tmp_dir"
    
    # 打包所有数据
    log "打包迁移数据..."
    local migrate_file="/tmp/migrate_data.tar.gz"
    tar -czf "$migrate_file" -C "$tmp_dir" . 2>/dev/null
    local size=$(du -h "$migrate_file" | cut -f1)
    info "迁移包大小: $size"
    
    # 传输到目标服务器
    log "传输到目标服务器..."
    sshpass -p "$target_pass" rsync -avz --progress \
        -e "ssh -o StrictHostKeyChecking=no -p $target_port" \
        "$migrate_file" "$target_user@$target_ip:/tmp/"
    
    # 在目标服务器上恢复
    log "在目标服务器上恢复..."
    sshpass -p "$target_pass" ssh -o StrictHostKeyChecking=no \
        -p "$target_port" "$target_user@$target_ip" bash << 'REMOTE_SCRIPT'
set -e
cd /tmp
mkdir -p /tmp/restore_data
tar -xzf migrate_data.tar.gz -C /tmp/restore_data

# 安装 Docker
if [ -f /tmp/restore_data/docker-images.tar.gz ]; then
    if ! command -v docker &>/dev/null; then
        echo "安装 Docker..."
        curl -fsSL https://get.docker.com | sh
        systemctl start docker
        systemctl enable docker
    fi
    echo "导入 Docker 镜像..."
    gunzip -c /tmp/restore_data/docker-images.tar.gz | docker load
fi

# 恢复 volumes
if [ -d /tmp/restore_data/volumes ]; then
    echo "恢复 Docker Volumes..."
    for vol_file in /tmp/restore_data/volumes/*.tar.gz; do
        [ -f "$vol_file" ] || continue
        vol_name=$(basename "$vol_file" .tar.gz)
        docker volume create "$vol_name" 2>/dev/null || true
        docker run --rm -v "$vol_name:/data" -v "/tmp/restore_data/volumes:/backup" \
            alpine tar xzf "/backup/${vol_name}.tar.gz" -C /data 2>/dev/null || true
    done
fi

# 恢复应用数据
if ls /tmp/restore_data/app-data.tar.gz &>/dev/null; then
    echo "恢复应用数据..."
    tar -xzf /tmp/restore_data/app-data.tar.gz -C / 2>/dev/null || true
fi

# 恢复 NPM 全局包
if [ -f /tmp/restore_data/npm-global.json ] && command -v npm &>/dev/null; then
    echo "恢复 NPM 全局包..."
    cat /tmp/restore_data/npm-global.json | jq -r '.dependencies | keys[]' | \
        xargs -I {} npm install -g {} 2>/dev/null || true
fi

# 恢复 pip 包
if [ -f /tmp/restore_data/pip-requirements.txt ] && command -v pip3 &>/dev/null; then
    echo "恢复 pip 包..."
    pip3 install -r /tmp/restore_data/pip-requirements.txt 2>/dev/null || true
fi

# 清理
rm -rf /tmp/restore_data /tmp/migrate_data.tar.gz

echo "✅ 迁移完成"
REMOTE_SCRIPT

    # 清理本地临时文件
    rm -rf "$tmp_dir" "$migrate_file"
    
    log "🎉 迁移完成！"
    echo ""
    info "请登录目标服务器检查服务状态"
    
    read -p "是否关闭当前服务器? (输入 '确认关闭' 执行): " confirm
    if [ "$confirm" = "确认关闭" ]; then
        warn "服务器将在 10 秒后关机..."
        sleep 10
        shutdown -h now
    fi
}

#===============================================================================
# 快照备份
#===============================================================================

create_snapshot() {
    local output_dir="${1:-/var/snapshots}"
    local name="${2:-snapshot}"
    local timestamp=$(date '+%Y%m%d_%H%M%S')
    local snapshot_file="$output_dir/${name}_${timestamp}.tar.gz"
    
    mkdir -p "$output_dir"
    log "📸 创建快照..."
    
    # TG通知 - 开始
    send_tg "🔄 *开始创建系统快照*
🖥️ 本机: ${VPS_NAME:-$(hostname)}
⏰ 时间: $(date '+%Y-%m-%d %H:%M:%S')"
    
    # 检测应用并备份
    detect_apps > /dev/null
    
    # Docker 数据 - 导出到临时目录
    local tmp_dir="/tmp/snapshot_$$"
    mkdir -p "$tmp_dir"
    
    # 保存已安装包列表（用于完整恢复）
    log "保存系统包列表..."
    if command -v dpkg &>/dev/null; then
        dpkg --get-selections | grep -v deinstall | awk '{print $1}' > "$tmp_dir/installed-packages.txt"
    elif command -v rpm &>/dev/null; then
        rpm -qa --qf '%{NAME}\n' > "$tmp_dir/installed-packages.txt"
    elif command -v apk &>/dev/null; then
        apk list -I 2>/dev/null | awk -F' ' '{print $1}' | sed 's/-[0-9].*//' > "$tmp_dir/installed-packages.txt"
    fi
    
    # 保存二进制文件列表（用于完整恢复 - 追踪手动安装的程序）
    log "保存二进制文件列表..."
    {
        ls -1 /usr/local/bin/ 2>/dev/null
        ls -1 /usr/local/sbin/ 2>/dev/null
        ls -1 /opt/ 2>/dev/null
    } | sort -u > "$tmp_dir/binary-files.txt"
    
    # 保存 systemd 服务列表
    ls -1 /etc/systemd/system/*.service 2>/dev/null | xargs -I{} basename {} > "$tmp_dir/systemd-services.txt" 2>/dev/null || true
    
    if command -v docker &>/dev/null && docker info &>/dev/null; then
        docker_export "$tmp_dir"
    fi
    
    # 应用数据 - 导出到临时目录
    backup_app_data "$tmp_dir"
    
    # 打包临时目录内容
    tar -czf "$snapshot_file" -C "$tmp_dir" . 2>/dev/null || true
    
    # 清理临时目录
    rm -rf "$tmp_dir"
    
    local size=$(du -h "$snapshot_file" | cut -f1)
    log "快照已创建: $snapshot_file ($size)"
    
    # TG通知 - 快照完成
    send_tg "📸 *系统快照创建成功*
🖥️ 本机: ${VPS_NAME:-$(hostname)}
📦 文件: $(basename $snapshot_file)
📏 大小: $size
🕒 时间: $(date '+%Y-%m-%d %H:%M:%S')"
    
    # 清理本地旧快照
    cleanup_local
    
    echo "$snapshot_file"
}

#===============================================================================
# 清理旧快照
#===============================================================================

cleanup_local() {
    local snap_dir="${LOCAL_DIR:-/var/snapshots}"
    local keep=${LOCAL_KEEP:-3}
    local vps_name="${VPS_NAME:-snapshot}"
    
    # 删除临时的app-data文件
    rm -f "$snap_dir"/app-data.tar.gz 2>/dev/null
    
    # 只统计VPS名称开头的快照
    local count=$(ls -1 "$snap_dir"/${vps_name}_*.tar.gz 2>/dev/null | wc -l)
    if [ "$count" -gt "$keep" ]; then
        log "🧹 清理本地旧快照 (保留$keep个)..."
        ls -t "$snap_dir"/${vps_name}_*.tar.gz | tail -n +$((keep+1)) | xargs rm -f
    fi
}

cleanup_remote() {
    [ -z "$REMOTE_IP" ] && return
    local days=${REMOTE_KEEP_DAYS:-30}
    local remote_path="${REMOTE_DIR:-/backup}/${VPS_NAME:-$(hostname)}"
    
    log "🧹 清理远程旧快照 (保留${days}天)..."
    _ssh_wrap ssh "${REMOTE_USER:-root}@$REMOTE_IP" \
        "find $remote_path -name '*.tar.gz' -mtime +$days -delete 2>/dev/null" || true
}

#===============================================================================
# 本地恢复
#===============================================================================

do_restore_local() {
    load_config 2>/dev/null || true
    local snap_dir="${LOCAL_DIR:-/var/snapshots}"
    
    echo ""
    info "📂 本地快照列表:"
    ls -lh "$snap_dir"/*.tar.gz 2>/dev/null || { error "无快照"; return 1; }
    echo ""
    read -p "输入快照文件名: " snap_file
    
    [ ! -f "$snap_dir/$snap_file" ] && { error "文件不存在"; return 1; }
    
    # 选择恢复模式
    echo ""
    info "选择恢复模式:"
    echo "  1) 完整恢复 (恢复到快照时状态，删除后来安装的软件)"
    echo "  2) 仅恢复数据 (只恢复Docker和应用数据，保留现有系统)"
    echo ""
    read -p "请选择 [1-2]: " restore_mode
    
    case "$restore_mode" in
        1) do_full_restore "$snap_dir/$snap_file" ;;
        2) do_data_restore "$snap_dir/$snap_file" ;;
        *) error "无效选择"; return 1 ;;
    esac
}

# 完整恢复 - 恢复到快照时的完整状态
do_full_restore() {
    local snap_file="$1"
    log "🔄 完整恢复: $(basename $snap_file)"
    
    # 解压到临时目录
    local tmp_dir="/tmp/restore_$$"
    mkdir -p "$tmp_dir"
    if ! tar -xzf "$snap_file" -C "$tmp_dir" 2>/dev/null; then
        error "快照文件损坏或解压失败"
        rm -rf "$tmp_dir"
        return 1
    fi
    
    # 读取快照时的已安装包列表
    if [ -f "$tmp_dir/installed-packages.txt" ]; then
        log "恢复系统包状态..."
        
        # 获取当前已安装的包
        local current_pkgs="/tmp/current_pkgs_$$.txt"
        if command -v dpkg &>/dev/null; then
            dpkg --get-selections | grep -v deinstall | awk '{print $1}' > "$current_pkgs"
        elif command -v rpm &>/dev/null; then
            rpm -qa --qf '%{NAME}\n' > "$current_pkgs"
        elif command -v apk &>/dev/null; then
            apk list -I 2>/dev/null | awk -F' ' '{print $1}' | sed 's/-[0-9].*//' > "$current_pkgs"
        fi
        
        # 找出快照后新安装的包
        if [ -f "$current_pkgs" ]; then
            local new_pkgs=$(comm -23 <(sort "$current_pkgs") <(sort "$tmp_dir/installed-packages.txt"))
            if [ -n "$new_pkgs" ]; then
                log "删除快照后安装的软件包..."
                echo "$new_pkgs" | head -20
                [ $(echo "$new_pkgs" | wc -l) -gt 20 ] && echo "... 等共 $(echo "$new_pkgs" | wc -l) 个包"
                
                if command -v apt-get &>/dev/null; then
                    echo "$new_pkgs" | xargs apt-get purge -y --auto-remove 2>/dev/null || true
                elif command -v yum &>/dev/null; then
                    echo "$new_pkgs" | xargs yum remove -y 2>/dev/null || true
                elif command -v apk &>/dev/null; then
                    echo "$new_pkgs" | xargs apk del 2>/dev/null || true
                fi
            fi
            rm -f "$current_pkgs"
        fi
    fi
    
    # 删除快照后新增的二进制文件和服务
    if [ -f "$tmp_dir/binary-files.txt" ]; then
        log "清理新增的二进制文件..."
        
        # 检查 /usr/local/bin/
        for f in /usr/local/bin/*; do
            [ -e "$f" ] || continue
            local fname=$(basename "$f")
            if ! grep -qx "$fname" "$tmp_dir/binary-files.txt" 2>/dev/null; then
                log "删除: /usr/local/bin/$fname"
                rm -f "$f"
            fi
        done
        
        # 检查 /usr/local/sbin/
        for f in /usr/local/sbin/*; do
            [ -e "$f" ] || continue
            local fname=$(basename "$f")
            if ! grep -qx "$fname" "$tmp_dir/binary-files.txt" 2>/dev/null; then
                log "删除: /usr/local/sbin/$fname"
                rm -f "$f"
            fi
        done
    fi
    
    # 删除快照后新增的 systemd 服务
    if [ -f "$tmp_dir/systemd-services.txt" ]; then
        log "清理新增的 systemd 服务..."
        # 只清理用户安装的服务，不动系统服务
        # 只检查 /etc/systemd/system (用户服务)，不检查 /usr/lib/systemd/system (系统服务)
        for f in /etc/systemd/system/*.service; do
            [ -e "$f" ] || continue
            local fname=$(basename "$f")
            # 跳过系统核心服务和模板服务
            case "$fname" in
                systemd-*|dbus*|ssh*|cron*|rsyslog*|networking*|getty*|serial-getty*) continue ;;
                cloud-*|apt-*|apparmor*|console-*|keyboard-*|grub-*) continue ;;
                docker*|containerd*|snap*|snapd*) continue ;;
                *@*) continue ;;  # 跳过所有模板服务
            esac
            if ! grep -qx "$fname" "$tmp_dir/systemd-services.txt" 2>/dev/null; then
                log "停止并删除服务: $fname"
                systemctl stop "$fname" 2>/dev/null || true
                systemctl disable "$fname" 2>/dev/null || true
                rm -f "$f"
            fi
        done
        systemctl daemon-reload 2>/dev/null || true
    fi
    
    # 清理相关配置和安装目录
    log "清理相关配置..."
    # sing-box 相关
    rm -rf /usr/local/etc/sing-box /etc/sing-box 2>/dev/null || true
    # x-ui / x-panel 相关
    rm -rf /usr/local/x-ui 2>/dev/null || true
    # 其他常见手动安装程序的配置
    rm -rf /usr/local/etc/v2ray /etc/v2ray 2>/dev/null || true
    rm -rf /usr/local/etc/xray /etc/xray 2>/dev/null || true
    rm -rf /usr/local/xray 2>/dev/null || true
    
    # 停止并删除所有Docker容器
    if command -v docker &>/dev/null; then
        log "清理现有 Docker 环境..."
        docker stop $(docker ps -aq) 2>/dev/null || true
        docker rm $(docker ps -aq) 2>/dev/null || true
        docker system prune -af 2>/dev/null || true
    fi
    
    # 导入Docker镜像
    if [ -f "$tmp_dir/docker-images.tar.gz" ]; then
        log "导入 Docker 镜像..."
        gunzip -c "$tmp_dir/docker-images.tar.gz" | docker load
    fi
    
    # 恢复Docker volumes
    if [ -f "$tmp_dir/docker-volumes.tar.gz" ]; then
        log "恢复 Docker Volumes..."
        tar -xzf "$tmp_dir/docker-volumes.tar.gz" -C /var/lib/docker/volumes/ 2>/dev/null || true
    fi
    
    # 恢复docker-compose文件
    if [ -f "$tmp_dir/docker-compose.yml" ]; then
        cp "$tmp_dir/docker-compose.yml" /root/
    fi
    if [ -f "$tmp_dir/.env" ]; then
        cp "$tmp_dir/.env" /root/
    fi
    
    # 恢复应用数据
    if ls "$tmp_dir"/app-data.tar.gz &>/dev/null; then
        log "恢复应用数据..."
        tar -xzf "$tmp_dir"/app-data.tar.gz -C / 2>/dev/null || true
    fi
    
    # 启动Docker容器
    if [ -f /root/docker-compose.yml ]; then
        log "启动 Docker 容器..."
        cd /root && docker compose up -d 2>/dev/null || docker-compose up -d 2>/dev/null || true
    fi
    
    rm -rf "$tmp_dir"
    log "✅ 完整恢复完成"
}

# 仅恢复数据 - 保留现有系统
do_data_restore() {
    local snap_file="$1"
    log "🔄 恢复数据: $(basename $snap_file)"
    
    # 解压到临时目录
    local tmp_dir="/tmp/restore_$$"
    mkdir -p "$tmp_dir"
    if ! tar -xzf "$snap_file" -C "$tmp_dir" 2>/dev/null; then
        error "快照文件损坏或解压失败"
        rm -rf "$tmp_dir"
        return 1
    fi
    
    # 导入Docker镜像
    if [ -f "$tmp_dir/docker-images.tar.gz" ]; then
        log "导入 Docker 镜像..."
        gunzip -c "$tmp_dir/docker-images.tar.gz" | docker load
    fi
    
    # 恢复 Docker Volumes
    if [ -d "$tmp_dir/volumes" ]; then
        log "恢复 Docker Volumes..."
        for vol_file in "$tmp_dir/volumes"/*.tar.gz; do
            [ -f "$vol_file" ] || continue
            local vol_name=$(basename "$vol_file" .tar.gz)
            docker volume create "$vol_name" 2>/dev/null || true
            docker run --rm -v "$vol_name:/data" -v "$tmp_dir/volumes:/backup" \
                alpine sh -c "cd /data && tar -xzf /backup/$(basename $vol_file)"
            log "  恢复 Volume: $vol_name"
        done
    fi
    
    # 恢复应用数据
    if [ -f "$tmp_dir/app-data.tar.gz" ]; then
        log "恢复应用数据..."
        if ! tar -xzf "$tmp_dir/app-data.tar.gz" -C / 2>/dev/null; then
            warn "应用数据恢复部分失败"
        fi
    fi
    
    rm -rf "$tmp_dir"
    log "✅ 数据恢复完成"
}

#===============================================================================
# 从配置的远程恢复
#===============================================================================

do_restore_config_remote() {
    load_config 2>/dev/null || true
    
    if [ -z "$REMOTE_IP" ]; then
        error "未配置远程服务器，请先运行配置"
        return 1
    fi
    
    local remote_path="${REMOTE_DIR:-/backup}/${VPS_NAME:-$(hostname)}"
    
    info "📡 从配置的远程服务器恢复: $REMOTE_IP:$remote_path"
    echo ""
    
    # 获取远程快照列表
    local snap_list=$(_ssh_wrap ssh -o StrictHostKeyChecking=no \
        -p "${REMOTE_PORT:-22}" "${REMOTE_USER:-root}@$REMOTE_IP" \
        "ls -t $remote_path/*.tar.gz 2>/dev/null")
    
    [ -z "$snap_list" ] && { error "无远程快照"; return 1; }
    
    # 显示编号列表
    info "远程快照列表:"
    local i=1
    local snaps=()
    while read -r snap; do
        local name=$(basename "$snap")
        local size=$(_ssh_wrap ssh -o StrictHostKeyChecking=no \
            -p "${REMOTE_PORT:-22}" "${REMOTE_USER:-root}@$REMOTE_IP" \
            "ls -lh '$snap' 2>/dev/null | awk '{print \$5}'" 2>/dev/null)
        echo "  $i) $name ($size)"
        snaps+=("$snap")
        ((i++))
    done <<< "$snap_list"
    
    echo ""
    read -p "请选择 [1-$((i-1))]: " choice
    
    # 验证选择
    if ! [[ "$choice" =~ ^[0-9]+$ ]] || [ "$choice" -lt 1 ] || [ "$choice" -gt $((i-1)) ]; then
        error "无效选择"
        return 1
    fi
    
    local selected="${snaps[$((choice-1))]}"
    
    log "下载快照: $(basename "$selected")"
    local local_file="/tmp/remote_snapshot_$$.tar.gz"
    _ssh_wrap scp -o StrictHostKeyChecking=no \
        -P "${REMOTE_PORT:-22}" "${REMOTE_USER:-root}@$REMOTE_IP:$selected" "$local_file"
    
    [ ! -f "$local_file" ] && { error "下载失败"; return 1; }
    
    log "🔄 恢复快照..."
    local tmp_dir="/tmp/restore_$$"
    mkdir -p "$tmp_dir"
    tar -xzf "$local_file" -C "$tmp_dir"
    
    # 导入Docker
    if [ -f "$tmp_dir/docker-images.tar.gz" ]; then
        log "导入 Docker 镜像..."
        gunzip -c "$tmp_dir/docker-images.tar.gz" | docker load
    fi
    
    # 恢复应用数据
    if ls "$tmp_dir"/app-data.tar.gz &>/dev/null; then
        log "恢复应用数据..."
        tar -xzf "$tmp_dir"/app-data.tar.gz -C / 2>/dev/null || true
    fi
    
    rm -rf "$tmp_dir" "$local_file"
    log "✅ 恢复完成"
}

#===============================================================================
# 远程恢复
#===============================================================================

do_restore_remote() {
    echo ""
    info "📡 从远程服务器拉取快照"
    read -p "远程服务器 IP: " remote_ip
    read -p "远程端口 [22]: " remote_port
    remote_port=${remote_port:-22}
    read -p "远程用户 [root]: " remote_user
    remote_user=${remote_user:-root}
    read -s -p "远程密码: " remote_pass
    echo ""
    read -p "远程快照路径 (如 /var/snapshots/xxx.tar.gz): " remote_path
    
    [ -z "$remote_ip" ] || [ -z "$remote_path" ] && { error "参数不完整"; return 1; }
    
    log "下载快照..."
    local local_file="/tmp/remote_snapshot_$$.tar.gz"
    sshpass -p "$remote_pass" scp -o StrictHostKeyChecking=no \
        -P "$remote_port" "$remote_user@$remote_ip:$remote_path" "$local_file"
    
    [ ! -f "$local_file" ] && { error "下载失败"; return 1; }
    
    log "🔄 恢复快照..."
    local tmp_dir="/tmp/restore_$$"
    mkdir -p "$tmp_dir"
    tar -xzf "$local_file" -C "$tmp_dir"
    
    # 导入Docker
    if [ -f "$tmp_dir/docker-images.tar.gz" ]; then
        log "导入 Docker 镜像..."
        gunzip -c "$tmp_dir/docker-images.tar.gz" | docker load
    fi
    
    # 恢复应用数据
    if ls "$tmp_dir"/app-data.tar.gz &>/dev/null; then
        log "恢复应用数据..."
        tar -xzf "$tmp_dir"/app-data.tar.gz -C / 2>/dev/null || true
    fi
    
    rm -rf "$tmp_dir" "$local_file"
    log "✅ 恢复完成"
}

#===============================================================================
# 同步到远程
#===============================================================================

do_sync_remote() {
    load_config 2>/dev/null || true
    
    if [ -z "$REMOTE_IP" ]; then
        error "未配置远程服务器，请先运行配置"
        return 1
    fi
    
    local snap_dir="${LOCAL_DIR:-/var/snapshots}"
    local latest=$(ls -t "$snap_dir"/*.tar.gz 2>/dev/null | head -1)
    
    [ -z "$latest" ] && { error "无本地快照"; return 1; }
    
    # 远程目录：基础目录/VPS名称
    local remote_base="${REMOTE_DIR:-/backup}"
    local remote_path="${remote_base}/${VPS_NAME:-$(hostname)}"
    
    log "📤 同步到远程: $REMOTE_IP:$remote_path"
    
    # 创建远程目录（以VPS名称命名）
    _ssh_wrap ssh "${REMOTE_USER:-root}@$REMOTE_IP" \
        "mkdir -p $remote_path"
    
    # 同步到VPS专属目录
    _ssh_wrap rsync -avz --progress \
        "$latest" "${REMOTE_USER:-root}@$REMOTE_IP:$remote_path/"
    
    # 清理远程旧快照
    cleanup_remote
    
    log "✅ 同步完成"
    
    # TG通知 - 上传成功
    send_tg "☁️ *快照上传成功*
🖥️ 本机: ${VPS_NAME:-$(hostname)}
📁 远程: $REMOTE_IP:$remote_path
🕒 时间: $(date '+%Y-%m-%d %H:%M:%S')"
}

#===============================================================================
# Telegram 通知
#===============================================================================

send_tg() {
    [ -z "$TG_BOT_TOKEN" ] || [ -z "$TG_CHAT_ID" ] && return
    local msg="$1"
    # 转义 Markdown 特殊字符
    msg=$(echo "$msg" | sed 's/_/\\_/g')
    curl -s -X POST "https://api.telegram.org/bot${TG_BOT_TOKEN}/sendMessage" \
        -d chat_id="$TG_CHAT_ID" -d text="$msg" -d parse_mode="Markdown" > /dev/null 2>&1 || true
}

#===============================================================================
# 定时快照
#===============================================================================

setup_cron() {
    local script_path=$(readlink -f "$0")
    
    echo ""
    info "⏰ 设置定时快照"
    echo ""
    echo "  1) 每天一次"
    echo "  2) 每12小时"
    echo "  3) 每6小时"
    echo "  4) 每小时"
    echo "  5) 自定义cron表达式"
    echo "  6) 查看当前定时任务"
    echo "  7) 删除定时任务"
    echo "  0) 返回"
    echo ""
    read -p "请选择: " cron_choice
    
    local cron_expr=""
    case $cron_choice in
        1) cron_expr="0 2 * * *" ;;
        2) cron_expr="0 */12 * * *" ;;
        3) cron_expr="0 */6 * * *" ;;
        4) cron_expr="0 * * * *" ;;
        5) read -p "输入cron表达式: " cron_expr ;;
        6) 
            echo ""
            crontab -l 2>/dev/null | grep -E "vps-snapshot|快照" || echo "无定时任务"
            return
            ;;
        7)
            crontab -l 2>/dev/null | grep -v "vps-snapshot" | crontab -
            log "定时任务已删除"
            return
            ;;
        0) return ;;
        *) error "无效选项"; return ;;
    esac
    
    [ -z "$cron_expr" ] && return
    
    # 选择快照类型
    echo ""
    echo "  1) 仅本地快照"
    echo "  2) 快照并同步远程"
    read -p "请选择: " snap_type
    
    local cmd=""
    case $snap_type in
        1) cmd="$script_path snapshot" ;;
        2) cmd="$script_path snapshot-sync" ;;
        *) error "无效选项"; return ;;
    esac
    
    # 添加到crontab
    (crontab -l 2>/dev/null | grep -v "vps-snapshot"; echo "$cron_expr $cmd # vps-snapshot") | crontab -
    
    log "✅ 定时任务已设置: $cron_expr"
    send_tg "⏰ *${VPS_NAME}* 定时快照已设置\n$cron_expr"
}

#===============================================================================
# 更新脚本
#===============================================================================

update_script() {
    local script_path=$(readlink -f "$0")
    local url="https://raw.githubusercontent.com/mango082888-bit/vps-snapshot/main/vps-snapshot.sh"
    
    log "🔄 检查更新..."
    
    curl -sL "$url" -o "${script_path}.new"
    
    if [ -f "${script_path}.new" ]; then
        mv "${script_path}.new" "$script_path"
        chmod +x "$script_path"
        log "✅ 更新完成，请重新运行脚本"
        exit 0
    else
        error "更新失败"
    fi
}

#===============================================================================
# 配置管理
#===============================================================================

load_config() {
    [ -f "$CONFIG_FILE" ] && source "$CONFIG_FILE"
}

# SSH/SCP/rsync 前缀：有key用key，有密码用sshpass，都没有走默认
_ssh_wrap() {
    local mode="$1"; shift
    local key="${REMOTE_KEY:-}" pass="${REMOTE_PASS:-}"
    local ssh_opts="-o StrictHostKeyChecking=no -p ${REMOTE_PORT:-22}"
    [ -n "$key" ] && [ -f "$key" ] && ssh_opts="$ssh_opts -i $key"
    local ssh_cmd="ssh $ssh_opts"
    local prefix=""
    [ -z "$key" ] || [ ! -f "$key" ] && [ -n "$pass" ] && prefix="sshpass -p $pass"
    case "$mode" in
        ssh)  $prefix $ssh_cmd "$@" ;;
        scp)  $prefix scp -o StrictHostKeyChecking=no ${key:+-i "$key"} -P "${REMOTE_PORT:-22}" "$@" ;;
        rsync) $prefix rsync -e "$ssh_cmd" "$@" ;;
    esac
}

save_config() {
    cat > "$CONFIG_FILE" << EOF
VPS_NAME="$VPS_NAME"
LOCAL_DIR="$LOCAL_DIR"
LOCAL_KEEP="$LOCAL_KEEP"
REMOTE_IP="$REMOTE_IP"
REMOTE_PORT="$REMOTE_PORT"
REMOTE_USER="$REMOTE_USER"
REMOTE_PASS="$REMOTE_PASS"
REMOTE_KEY="$REMOTE_KEY"
REMOTE_DIR="$REMOTE_DIR"
REMOTE_KEEP_DAYS="$REMOTE_KEEP_DAYS"
TG_BOT_TOKEN="$TG_BOT_TOKEN"
TG_CHAT_ID="$TG_CHAT_ID"
EOF
    chmod 600 "$CONFIG_FILE"
}

do_setup() {
    print_banner
    info "⚙️ 配置向导"
    echo ""
    
    read -p "VPS 名称 [$(hostname)]: " VPS_NAME
    VPS_NAME=${VPS_NAME:-$(hostname)}
    
    read -p "本地快照目录 [/var/snapshots]: " LOCAL_DIR
    LOCAL_DIR=${LOCAL_DIR:-/var/snapshots}
    
    read -p "本地保留快照数量 [3]: " LOCAL_KEEP
    LOCAL_KEEP=${LOCAL_KEEP:-3}
    
    echo ""
    info "远程备份配置 (可选，留空跳过)"
    read -p "远程服务器 IP: " REMOTE_IP
    if [ -n "$REMOTE_IP" ]; then
        read -p "远程端口 [22]: " REMOTE_PORT
        REMOTE_PORT=${REMOTE_PORT:-22}
        read -p "远程用户 [root]: " REMOTE_USER
        REMOTE_USER=${REMOTE_USER:-root}
        read -s -p "远程密码 (key认证留空): " REMOTE_PASS
        echo ""
        read -p "远程密钥路径 (密码认证留空): " REMOTE_KEY
        read -p "远程目录 [/backup]: " REMOTE_DIR
        REMOTE_DIR=${REMOTE_DIR:-/backup}
        read -p "远程保留天数 [30]: " REMOTE_KEEP_DAYS
        REMOTE_KEEP_DAYS=${REMOTE_KEEP_DAYS:-30}
    fi
    
    echo ""
    info "Telegram 通知 (可选，留空跳过)"
    read -p "Bot Token: " TG_BOT_TOKEN
    [ -n "$TG_BOT_TOKEN" ] && read -p "Chat ID: " TG_CHAT_ID
    
    save_config
    log "配置已保存到 $CONFIG_FILE"
}

#===============================================================================
# 主菜单
#===============================================================================

show_menu() {
    # 自动检测并安装依赖
    check_deps
    
    print_banner
    load_config 2>/dev/null || true
    
    echo -e "${CYAN}当前配置:${NC} ${VPS_NAME:-未配置}"
    echo ""
    echo "  1) 首次配置 / 重新配置"
    echo "  2) 扫描已安装应用"
    echo "  3) 创建本地快照"
    echo "  4) 创建快照并同步远程"
    echo "  5) 从本地快照恢复"
    echo "  6) 从配置的远程恢复"
    echo "  7) 自定义远程恢复 (输入任意服务器)"
    echo "  8) 一键迁移到新服务器"
    echo "  9) 导出 Docker 数据"
    echo " 10) 导入 Docker 数据"
    echo " 11) 查看本地快照"
    echo " 12) 同步到远程"
    echo " 13) 设置定时快照"
    echo " 14) 更新脚本"
    echo " 15) 安装依赖"
    echo "  0) 退出"
    echo ""
    read -p "请选择 [0-15]: " choice
    
    case $choice in
        1) do_setup ;;
        2) detect_apps ;;
        3) 
            load_config 2>/dev/null || true
            create_snapshot "${LOCAL_DIR:-/var/snapshots}" "${VPS_NAME:-snapshot}"
            ;;
        4) 
            load_config 2>/dev/null || true
            create_snapshot "${LOCAL_DIR:-/var/snapshots}" "${VPS_NAME:-snapshot}"
            do_sync_remote
            ;;
        5) do_restore_local ;;
        6) do_restore_config_remote ;;
        7) do_restore_remote ;;
        8) do_migrate ;;
        9) 
            read -p "输出目录 [/var/snapshots]: " dir
            docker_export "${dir:-/var/snapshots}"
            ;;
        10)
            read -p "输入目录 [/var/snapshots]: " dir
            docker_import "${dir:-/var/snapshots}"
            ;;
        11)
            load_config 2>/dev/null || true
            echo ""
            ls -lh "${LOCAL_DIR:-/var/snapshots}" 2>/dev/null || echo "无快照"
            ;;
        12) do_sync_remote ;;
        13) setup_cron ;;
        14) update_script ;;
        15) install_deps ;;
        0) exit 0 ;;
        *) error "无效选项" ;;
    esac
    
    echo ""
    read -p "按回车继续..."
    show_menu
}

#===============================================================================
# 入口
#===============================================================================

case "${1:-}" in
    setup) do_setup ;;
    scan) detect_apps ;;
    snapshot) 
        load_config 2>/dev/null || true
        create_snapshot "${LOCAL_DIR:-/var/snapshots}" "${VPS_NAME:-snapshot}" 
        ;;
    snapshot-sync)
        load_config 2>/dev/null || true
        create_snapshot "${LOCAL_DIR:-/var/snapshots}" "${VPS_NAME:-snapshot}"
        do_sync_remote
        ;;
    migrate) do_migrate ;;
    docker-export) docker_export "${2:-/var/snapshots}" ;;
    docker-import) docker_import "${2:-/var/snapshots}" ;;
    restore)
        # 命令行恢复: restore <快照文件> [full|data]
        load_config 2>/dev/null || true
        _snap_file="${2:-}"
        _mode="${3:-data}"
        if [ -z "$_snap_file" ]; then
            error "用法: $0 restore <快照文件> [full|data]"
            echo "  full = 完整恢复 (删除后来安装的软件)"
            echo "  data = 仅恢复数据 (默认)"
            exit 1
        fi
        [ ! -f "$_snap_file" ] && { error "文件不存在: $_snap_file"; exit 1; }
        case "$_mode" in
            full) do_full_restore "$_snap_file" ;;
            data|*) do_data_restore "$_snap_file" ;;
        esac
        ;;
    help|--help|-h)
        echo "用法: $0 [命令]"
        echo ""
        echo "命令:"
        echo "  setup         配置向导"
        echo "  scan          扫描已安装应用"
        echo "  snapshot      创建本地快照"
        echo "  snapshot-sync 创建快照并同步远程"
        echo "  restore       恢复快照 (restore <文件> [full|data])"
        echo "  migrate       一键迁移"
        echo "  docker-export 导出 Docker"
        echo "  docker-import 导入 Docker"
        echo ""
        echo "无参数运行显示交互菜单"
        ;;
    "") show_menu ;;
    *) error "未知命令: $1"; exit 1 ;;
esac
