#!/bin/bash
# 备份完整性校验脚本
# 用法: ./backup-verify.sh [backup_file] [--min-size BYTES]
# 不传参数时校验最新备份

set -u

show_help() {
  cat <<EOF
用法: $0 [backup_file] [选项]
备份完整性校验脚本

参数:
  backup_file          要校验的备份文件路径(不传时校验最新备份)

选项:
  --min-size BYTES     最小文件大小阈值(字节),默认 1024
  --no-sql-check       跳过 SQL 语法校验(不连接数据库)
  --help, -h           显示帮助信息

环境变量:
  BACKUP_DIR              备份目录(默认 ./backups)
  DATABASE_URL            数据库连接 URL(用于 SQL 语法校验)
  BACKUP_VERIFY_MIN_SIZE  最小文件大小(字节,默认 1024)

退出码:
  0  校验通过
  1  校验失败
EOF
}

# 解析参数
BACKUP_FILE=""
MIN_SIZE="${BACKUP_VERIFY_MIN_SIZE:-1024}"
NO_SQL_CHECK=0

while [ $# -gt 0 ]; do
  case "$1" in
    --help|-h)
      show_help
      exit 0
      ;;
    --min-size)
      if [ $# -lt 2 ]; then
        echo "ERROR: --min-size requires an argument" >&2
        exit 1
      fi
      MIN_SIZE="$2"
      shift 2
      ;;
    --no-sql-check)
      NO_SQL_CHECK=1
      shift
      ;;
    *)
      if [ -z "$BACKUP_FILE" ]; then
        BACKUP_FILE="$1"
      else
        echo "ERROR: Unknown argument: $1" >&2
        exit 1
      fi
      shift
      ;;
  esac
done

BACKUP_DIR="${BACKUP_DIR:-./backups}"

# 如果未指定文件,查找最新备份
if [ -z "$BACKUP_FILE" ]; then
  BACKUP_FILE=$(ls -t "$BACKUP_DIR"/db_backup_*.sql.gz 2>/dev/null | head -1)
  if [ -z "$BACKUP_FILE" ]; then
    echo "ERROR: No backup file found in $BACKUP_DIR" >&2
    echo "Hint: Run scripts/backup-db.sh first or specify a file path" >&2
    exit 1
  fi
fi

echo "=== Backup Verification Report ==="
echo "File: $BACKUP_FILE"
echo "Time: $(date -u +"%Y-%m-%dT%H:%M:%SZ")"
echo ""

ERRORS=0
WARNINGS=0

# 步骤 1: 检查文件存在
echo "[1/4] Checking file existence..."
if [ ! -f "$BACKUP_FILE" ]; then
  echo "  FAIL: File does not exist: $BACKUP_FILE"
  exit 1
fi
echo "  PASS: File exists"

# 步骤 2: 检查文件大小
echo "[2/4] Checking file size..."
FILE_SIZE=$(stat -c%s "$BACKUP_FILE" 2>/dev/null || stat -f%z "$BACKUP_FILE" 2>/dev/null)
if [ -z "$FILE_SIZE" ]; then
  echo "  WARN: Could not determine file size"
  WARNINGS=$((WARNINGS + 1))
elif [ "$FILE_SIZE" -lt "$MIN_SIZE" ]; then
  echo "  FAIL: File size ${FILE_SIZE} bytes is below threshold ${MIN_SIZE} bytes"
  echo "  This may indicate a corrupted or empty backup"
  exit 1
else
  echo "  PASS: File size ${FILE_SIZE} bytes (threshold: ${MIN_SIZE} bytes)"
fi

# 步骤 3: 校验 gzip 完整性
echo "[3/4] Verifying gzip integrity..."
if ! gunzip -t "$BACKUP_FILE" 2>/dev/null; then
  echo "  FAIL: gzip integrity check failed - file may be corrupted"
  exit 1
fi
echo "  PASS: gzip integrity verified"

# 步骤 4: 校验 SQL 内容
echo "[4/4] Verifying SQL content..."
TEMP_SQL=$(mktemp 2>/dev/null || echo "/tmp/backup_verify_$$.sql")
trap "rm -f \"$TEMP_SQL\" /tmp/backup_verify_errors_$$.txt 2>/dev/null" EXIT

if ! gunzip -c "$BACKUP_FILE" > "$TEMP_SQL" 2>/dev/null; then
  echo "  FAIL: Could not decompress backup file"
  exit 1
fi

# 检查文件非空
SQL_SIZE=$(stat -c%s "$TEMP_SQL" 2>/dev/null || stat -f%z "$TEMP_SQL" 2>/dev/null)
if [ -z "$SQL_SIZE" ] || [ "$SQL_SIZE" -eq 0 ]; then
  echo "  FAIL: Decompressed SQL file is empty"
  exit 1
fi
echo "  PASS: Decompressed size: ${SQL_SIZE} bytes"

# 检查 mysqldump 头部
if grep -q "MySQL dump" "$TEMP_SQL" 2>/dev/null || grep -q "mysqldump" "$TEMP_SQL" 2>/dev/null; then
  echo "  PASS: mysqldump header found"
else
  echo "  WARN: mysqldump header not found (may not be a standard mysqldump file)"
  WARNINGS=$((WARNINGS + 1))
fi

# 检查 SQL 语句数量
STMT_COUNT=$(grep -c ";" "$TEMP_SQL" 2>/dev/null || echo 0)
if [ "$STMT_COUNT" -lt 10 ]; then
  echo "  WARN: Low statement count (${STMT_COUNT} semicolons)"
  WARNINGS=$((WARNINGS + 1))
else
  echo "  PASS: Found ${STMT_COUNT} SQL statements"
fi

# 检查 CREATE TABLE 数量
CREATE_COUNT=$(grep -ci "CREATE TABLE" "$TEMP_SQL" 2>/dev/null || echo 0)
echo "  INFO: Found ${CREATE_COUNT} CREATE TABLE statements"

# 检查明显的语法错误标记
if grep -qi "ERROR at line" "$TEMP_SQL" 2>/dev/null; then
  echo "  FAIL: Found error markers in SQL file"
  exit 1
fi

# SQL 语法校验(可选,需要 DATABASE_URL)
if [ "$NO_SQL_CHECK" -eq 1 ]; then
  echo "  SKIP: SQL syntax check skipped (--no-sql-check)"
elif [ -z "${DATABASE_URL:-}" ]; then
  echo "  SKIP: DATABASE_URL not set, skipping SQL syntax check"
else
  echo "  Performing SQL syntax check via mysql..."
  # 解析 DATABASE_URL
  DB_USER=$(echo "$DATABASE_URL" | sed -n 's/.*:\/\/\([^:]*\):.*/\1/p')
  DB_PASS=$(echo "$DATABASE_URL" | sed -n 's/.*:\/\/[^:]*:\([^@]*\)@.*/\1/p')
  DB_HOST=$(echo "$DATABASE_URL" | sed -n 's/.*@\([^:]*\):.*/\1/p')
  DB_PORT=$(echo "$DATABASE_URL" | sed -n 's/.*:\([0-9]*\)\/.*/\1/p')

  if [ -z "$DB_HOST" ] || [ -z "$DB_USER" ]; then
    echo "  WARN: Could not parse DATABASE_URL, skipping SQL syntax check"
    WARNINGS=$((WARNINGS + 1))
  else
    # 创建临时数据库进行语法校验(不影响生产数据)
    TEMP_DB="verify_$(date +%s)_$$"
    ERROR_FILE="/tmp/backup_verify_errors_$$.txt"

    if mysql -h "$DB_HOST" -P "${DB_PORT:-3306}" -u "$DB_USER" -p"$DB_PASS" \
        -e "CREATE DATABASE \`$TEMP_DB\`;" 2>"$ERROR_FILE"; then
      # 使用 --force 继续执行,捕获所有语法错误
      mysql -h "$DB_HOST" -P "${DB_PORT:-3306}" -u "$DB_USER" -p"$DB_PASS" \
          --force "$TEMP_DB" < "$TEMP_SQL" > /dev/null 2>"$ERROR_FILE" || true

      # 检查是否有语法错误(区分语法错误和执行错误)
      SYNTAX_ERRORS=$(grep -i "You have an error in your SQL syntax" "$ERROR_FILE" 2>/dev/null | wc -l || echo 0)
      if [ "$SYNTAX_ERRORS" -gt 0 ]; then
        echo "  FAIL: Found $SYNTAX_ERRORS SQL syntax errors"
        grep -i "You have an error in your SQL syntax" "$ERROR_FILE" | head -3
        # 清理临时数据库
        mysql -h "$DB_HOST" -P "${DB_PORT:-3306}" -u "$DB_USER" -p"$DB_PASS" \
            -e "DROP DATABASE IF EXISTS \`$TEMP_DB\`;" 2>/dev/null || true
        exit 1
      else
        echo "  PASS: SQL syntax check passed (no syntax errors)"
      fi

      # 清理临时数据库
      mysql -h "$DB_HOST" -P "${DB_PORT:-3306}" -u "$DB_USER" -p"$DB_PASS" \
          -e "DROP DATABASE IF EXISTS \`$TEMP_DB\`;" 2>/dev/null || true
    else
      echo "  WARN: Could not create temp database for syntax check, skipping"
      WARNINGS=$((WARNINGS + 1))
    fi
  fi
fi

echo ""
echo "=== Verification Summary ==="
echo "Errors:   $ERRORS"
echo "Warnings: $WARNINGS"
if [ "$ERRORS" -gt 0 ]; then
  echo "Result:   FAILED"
  exit 1
fi
echo "Result:   PASSED"
exit 0
