#!/bin/bash
# 数据备份脚本
# 支持 SQLite 数据库和配置文件的备份
#
# 使用方法:
#   ./scripts/backup/backup.sh              # 执行一次备份
#   ./scripts/backup/backup.sh --restore  # 从最新备份恢复
#   ./scripts/backup/backup.sh --list      # 列出所有备份
#   ./scripts/backup/backup.sh --verify     # 验证备份完整性
#
# 自动备份 (crontab):
#   0 2 * * * /path/to/scripts/backup/backup.sh  # 每天凌晨2点

set -e

# 配置
BACKUP_DIR="${BACKUP_DIR:-./backups}"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_NAME="user-management_${TIMESTAMP}"
DB_PATH="${DB_PATH:-./data/user_management.db}"
CONFIG_PATH="${CONFIG_PATH:-./configs/config.yaml}"
RETENTION_DAYS="${RETENTION_DAYS:-30}"

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

log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_warning() { echo -e "${YELLOW}[WARNING]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }

# 创建备份目录
mkdir -p "${BACKUP_DIR}"

# 备份数据库
backup_database() {
    local db_file="$1"
    local backup_file="$2"

    if [ -f "${db_file}" ]; then
        log_info "Backing up database: ${db_file}"
        # 使用 SQLite 的 .backup 命令进行一致性备份
        sqlite3 "${db_file}" ".backup '${backup_file}'"
        log_success "Database backed up to: ${backup_file}"
        return 0
    else
        log_warning "Database file not found: ${db_file}"
        return 1
    fi
}

# 备份配置文件
backup_config() {
    local config_file="$1"
    local backup_file="$2"

    if [ -f "${config_file}" ]; then
        log_info "Backing up config: ${config_file}"
        cp "${config_file}" "${backup_file}"
        log_success "Config backed up to: ${backup_file}"
        return 0
    else
        log_warning "Config file not found: ${config_file}"
        return 1
    fi
}

# 验证备份完整性
verify_backup() {
    local backup_file="$1"
    local file_type="$2"

    log_info "Verifying backup: ${backup_file}"

    if [ ! -f "${backup_file}" ]; then
        log_error "Backup file not found: ${backup_file}"
        return 1
    fi

    case "${file_type}" in
        "sqlite")
            # 验证 SQLite 数据库完整性
            if sqlite3 "${backup_file}" "PRAGMA integrity_check;" 2>/dev/null | grep -q "ok"; then
                log_success "SQLite backup is valid"
                return 0
            else
                log_error "SQLite backup is corrupted"
                return 1
            fi
            ;;
        "config")
            # 验证 YAML 格式
            if grep -q "^server:" "${backup_file}"; then
                log_success "Config backup is valid"
                return 0
            else
                log_error "Config backup is invalid"
                return 1
            fi
            ;;
    esac
}

# 执行完整备份
do_backup() {
    log_info "Starting backup..."

    local backup_subdir="${BACKUP_DIR}/${BACKUP_NAME}"
    mkdir -p "${backup_subdir}"

    local db_backup="${backup_subdir}/database.db"
    local config_backup="${backup_subdir}/config.yaml"
    local metadata_file="${backup_subdir}/metadata.json"

    # 备份数据库
    backup_database "${DB_PATH}" "${db_backup}" || true

    # 备份配置
    backup_config "${CONFIG_PATH}" "${config_backup}" || true

    # 创建元数据
    cat > "${metadata_file}" << EOF
{
    "timestamp": "${TIMESTAMP}",
    "backup_name": "${BACKUP_NAME}",
    "db_path": "${DB_PATH}",
    "config_path": "${CONFIG_PATH}",
    "created_at": "$(date -Iseconds)"
}
EOF

    # 创建压缩包
    local archive_file="${BACKUP_DIR}/${BACKUP_NAME}.tar.gz"
    tar -czf "${archive_file}" -C "${BACKUP_DIR}" "${BACKUP_NAME}"

    # 计算校验和
    local checksum_file="${BACKUP_DIR}/${BACKUP_NAME}.sha256"
    sha256sum "${archive_file}" > "${checksum_file}"

    # 清理未压缩的备份目录
    rm -rf "${backup_subdir}"

    log_success "Backup completed: ${archive_file}"
    log_success "Checksum: $(cat ${checksum_file})"

    # 清理过期备份
    cleanup_old_backups
}

# 清理过期备份
cleanup_old_backups() {
    log_info "Cleaning up backups older than ${RETENTION_DAYS} days..."

    find "${BACKUP_DIR}" -name "*.tar.gz" -mtime +${RETENTION_DAYS} -delete 2>/dev/null || true
    find "${BACKUP_DIR}" -name "*.sha256" -mtime +${RETENTION_DAYS} -delete 2>/dev/null || true

    log_success "Cleanup completed"
}

# 列出所有备份
list_backups() {
    log_info "Available backups in ${BACKUP_DIR}:"

    if [ ! -d "${BACKUP_DIR}" ] || [ -z "$(ls -A ${BACKUP_DIR}/*.tar.gz 2>/dev/null)" ]; then
        log_warning "No backups found"
        return
    fi

    printf "\n%-40s %15s %20s\n" "BACKUP FILE" "SIZE" "CREATED"
    printf "%s\n" "------------------------------------------------------------------------"

    for archive in "${BACKUP_DIR}"/*.tar.gz; do
        if [ -f "${archive}" ]; then
            local size=$(du -h "${archive}" | cut -f1)
            local date=$(date -r "${archive}" "+%Y-%m-%d %H:%M:%S")
            printf "%-40s %15s %20s\n" "$(basename ${archive})" "${size}" "${date}"
        fi
    done
}

# 恢复备份
do_restore() {
    local latest_archive=$(ls -t "${BACKUP_DIR}"/*.tar.gz 2>/dev/null | head -1)

    if [ -z "${latest_archive}" ]; then
        log_error "No backup found to restore"
        exit 1
    fi

    log_warning "This will overwrite current data with backup: ${latest_archive}"
    read -p "Are you sure? (yes/no): " confirm

    if [ "${confirm}" != "yes" ]; then
        log_info "Restore cancelled"
        exit 0
    fi

    log_info "Restoring from: ${latest_archive}"

    # 验证备份
    if [ -f "${latest_archive}.sha256" ]; then
        if ! sha256sum --check "${latest_archive}.sha256"; then
            log_error "Backup checksum verification failed!"
            exit 1
        fi
        log_success "Checksum verified"
    fi

    # 解压到临时目录
    local temp_dir="${BACKUP_DIR}/.restore_temp"
    rm -rf "${temp_dir}"
    mkdir -p "${temp_dir}"
    tar -xzf "${latest_archive}" -C "${temp_dir}"

    # 查找解压的目录
    local restored_subdir=$(find "${temp_dir}" -mindepth 1 -maxdepth 1 -type d | head -1)

    # 恢复数据库
    local db_backup="${restored_subdir}/database.db"
    if [ -f "${db_backup}" ]; then
        verify_backup "${db_backup}" "sqlite"
        log_info "Restoring database..."
        cp "${db_backup}" "${DB_PATH}"
        log_success "Database restored"
    fi

    # 恢复配置
    local config_backup="${restored_subdir}/config.yaml"
    if [ -f "${config_backup}" ]; then
        verify_backup "${config_backup}" "config"
        log_info "Restoring config..."
        cp "${config_backup}" "${CONFIG_PATH}"
        log_success "Config restored"
    fi

    # 清理临时目录
    rm -rf "${temp_dir}"

    log_success "Restore completed successfully!"
}

# 验证备份
do_verify() {
    local latest_archive=$(ls -t "${BACKUP_DIR}"/*.tar.gz 2>/dev/null | head -1)

    if [ -z "${latest_archive}" ]; then
        log_error "No backup found to verify"
        exit 1
    fi

    log_info "Verifying latest backup: ${latest_archive}"

    # 验证校验和
    if [ -f "${latest_archive}.sha256" ]; then
        if sha256sum --check "${latest_archive}.sha256"; then
            log_success "Checksum verified"
        else
            log_error "Checksum verification failed"
            exit 1
        fi
    fi

    # 验证压缩包完整性
    if tar -tzf "${latest_archive}" > /dev/null 2>&1; then
        log_success "Archive integrity verified"
    else
        log_error "Archive is corrupted"
        exit 1
    fi

    # 解压并验证内容
    local temp_dir="${BACKUP_DIR}/.verify_temp"
    rm -rf "${temp_dir}"
    mkdir -p "${temp_dir}"
    tar -xzf "${latest_archive}" -C "${temp_dir}"

    local restored_subdir=$(find "${temp_dir}" -mindepth 1 -maxdepth 1 -type d | head -1)

    local db_backup="${restored_subdir}/database.db"
    if [ -f "${db_backup}" ]; then
        verify_backup "${db_backup}" "sqlite"
    fi

    rm -rf "${temp_dir}"
    log_success "Backup verification completed successfully!"
}

# 显示帮助
show_help() {
    echo "Usage: $0 [COMMAND]"
    echo ""
    echo "Commands:"
    echo "  (no args)     Execute a backup"
    echo "  --restore     Restore from the latest backup"
    echo "  --list        List all backups"
    echo "  --verify      Verify the latest backup"
    echo "  --help        Show this help message"
    echo ""
    echo "Environment variables:"
    echo "  BACKUP_DIR       Backup directory (default: ./backups)"
    echo "  DB_PATH          Database path (default: ./data/user_management.db)"
    echo "  CONFIG_PATH      Config path (default: ./configs/config.yaml)"
    echo "  RETENTION_DAYS    Backup retention days (default: 30)"
}

# 主逻辑
case "${1:-}" in
    --restore)
        do_restore
        ;;
    --list)
        list_backups
        ;;
    --verify)
        do_verify
        ;;
    --help)
        show_help
        ;;
    "")
        do_backup
        ;;
    *)
        log_error "Unknown command: $1"
        show_help
        exit 1
        ;;
esac
