#!/bin/bash

# Database Backup and Restore Script
# Provides convenient commands for database backup and restore operations

set -e

# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

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

# Change to project root
cd "$PROJECT_ROOT"

# Default backup directory
BACKUP_DIR="backups"
DATE_FORMAT="%Y%m%d_%H%M%S"

# Logging functions
log() {
    echo -e "${GREEN}[INFO]${NC} $1"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

print_header() {
    echo -e "${BLUE}=== $1 ===${NC}"
}

print_usage() {
    echo "Database Backup and Restore Script"
    echo
    echo "Usage: $0 <command> [options]"
    echo
    echo "Commands:"
    echo "  backup               Create database backup"
    echo "  restore              Restore database from backup"
    echo "  list                 List available backups"
    echo "  clean                Clean old backups"
    echo "  export               Export data to JSON/CSV"
    echo "  import               Import data from JSON/CSV"
    echo "  clone                Clone database to different name"
    echo "  compare              Compare two databases"
    echo
    echo "Options:"
    echo "  --env ENV           Environment (dev/prod) [default: dev]"
    echo "  --backup-dir DIR    Backup directory [default: backups]"
    echo "  --file FILE         Backup file path"
    echo "  --format FORMAT     Backup format (sql/custom/tar) [default: sql]"
    echo "  --compress          Compress backup file"
    echo "  --schema-only       Backup schema only (no data)"
    echo "  --data-only         Backup data only (no schema)"
    echo "  --tables TABLES     Comma-separated list of tables to backup"
    echo "  --keep-days DAYS    Keep backups for N days [default: 30]"
    echo "  --force             Skip confirmations"
    echo "  --quiet             Suppress verbose output"
    echo
    echo "Examples:"
    echo "  $0 backup                          # Create full backup"
    echo "  $0 backup --compress               # Create compressed backup"
    echo "  $0 backup --schema-only            # Backup schema only"
    echo "  $0 backup --tables users,content   # Backup specific tables"
    echo "  $0 restore --file backup.sql       # Restore from backup"
    echo "  $0 list                            # List backups"
    echo "  $0 clean --keep-days 7             # Clean old backups"
    echo "  $0 export --format json            # Export to JSON"
    echo "  $0 clone --env prod                # Clone to prod database"
}

# Check if .env file exists and load it
load_env() {
    if [ ! -f ".env" ]; then
        log_error ".env file not found"
        echo "Please run the database setup script first:"
        echo "  ./scripts/db-setup.sh setup"
        exit 1
    fi

    # Load environment variables
    export $(grep -v '^#' .env | xargs)
}

# Parse database URL
parse_database_url() {
    if [[ $DATABASE_URL == postgresql://* ]] || [[ $DATABASE_URL == postgres://* ]]; then
        DB_TYPE="postgresql"
        DB_HOST=$(echo $DATABASE_URL | sed -n 's/.*@\([^:]*\):.*/\1/p')
        DB_PORT=$(echo $DATABASE_URL | sed -n 's/.*:\([0-9]*\)\/.*/\1/p')
        DB_NAME=$(echo $DATABASE_URL | sed -n 's/.*\/\([^?]*\).*/\1/p')
        DB_USER=$(echo $DATABASE_URL | sed -n 's/.*\/\/\([^:]*\):.*/\1/p')
        DB_PASS=$(echo $DATABASE_URL | sed -n 's/.*:\/\/[^:]*:\([^@]*\)@.*/\1/p')
    elif [[ $DATABASE_URL == sqlite://* ]]; then
        DB_TYPE="sqlite"
        DB_FILE=$(echo $DATABASE_URL | sed 's/sqlite:\/\///')
    else
        log_error "Unsupported database URL format: $DATABASE_URL"
        exit 1
    fi
}

# Create backup directory
setup_backup_dir() {
    if [ ! -d "$BACKUP_DIR" ]; then
        log "Creating backup directory: $BACKUP_DIR"
        mkdir -p "$BACKUP_DIR"
    fi
}

# Generate backup filename
generate_backup_filename() {
    local timestamp=$(date +"$DATE_FORMAT")
    local env_suffix=""

    if [ "$ENVIRONMENT" != "dev" ]; then
        env_suffix="_${ENVIRONMENT}"
    fi

    local format_ext=""
    case "$FORMAT" in
        "sql") format_ext=".sql" ;;
        "custom") format_ext=".dump" ;;
        "tar") format_ext=".tar" ;;
    esac

    local compress_ext=""
    if [ "$COMPRESS" = "true" ]; then
        compress_ext=".gz"
    fi

    echo "${BACKUP_DIR}/${DB_NAME}_${timestamp}${env_suffix}${format_ext}${compress_ext}"
}

# Create PostgreSQL backup
backup_postgresql() {
    local backup_file="$1"
    local pg_dump_args=()

    # Add connection parameters
    pg_dump_args+=("-h" "$DB_HOST")
    pg_dump_args+=("-p" "$DB_PORT")
    pg_dump_args+=("-U" "$DB_USER")
    pg_dump_args+=("-d" "$DB_NAME")

    # Add format options
    case "$FORMAT" in
        "sql")
            pg_dump_args+=("--format=plain")
            ;;
        "custom")
            pg_dump_args+=("--format=custom")
            ;;
        "tar")
            pg_dump_args+=("--format=tar")
            ;;
    esac

    # Add backup type options
    if [ "$SCHEMA_ONLY" = "true" ]; then
        pg_dump_args+=("--schema-only")
    elif [ "$DATA_ONLY" = "true" ]; then
        pg_dump_args+=("--data-only")
    fi

    # Add table selection
    if [ -n "$TABLES" ]; then
        IFS=',' read -ra TABLE_ARRAY <<< "$TABLES"
        for table in "${TABLE_ARRAY[@]}"; do
            pg_dump_args+=("--table=$table")
        done
    fi

    # Add other options
    pg_dump_args+=("--verbose")
    pg_dump_args+=("--no-password")

    # Set password environment variable
    export PGPASSWORD="$DB_PASS"

    log "Creating PostgreSQL backup: $backup_file"

    if [ "$COMPRESS" = "true" ]; then
        pg_dump "${pg_dump_args[@]}" | gzip > "$backup_file"
    else
        pg_dump "${pg_dump_args[@]}" > "$backup_file"
    fi

    unset PGPASSWORD
}

# Create SQLite backup
backup_sqlite() {
    local backup_file="$1"

    if [ ! -f "$DB_FILE" ]; then
        log_error "SQLite database file not found: $DB_FILE"
        exit 1
    fi

    log "Creating SQLite backup: $backup_file"

    if [ "$COMPRESS" = "true" ]; then
        sqlite3 "$DB_FILE" ".dump" | gzip > "$backup_file"
    else
        sqlite3 "$DB_FILE" ".dump" > "$backup_file"
    fi
}

# Restore PostgreSQL backup
restore_postgresql() {
    local backup_file="$1"

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

    if [ "$FORCE" != "true" ]; then
        echo -n "This will restore the database '$DB_NAME'. Continue? (y/N): "
        read -r confirm
        if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
            log "Restore cancelled"
            exit 0
        fi
    fi

    export PGPASSWORD="$DB_PASS"

    log "Restoring PostgreSQL backup: $backup_file"

    if [[ "$backup_file" == *.gz ]]; then
        gunzip -c "$backup_file" | psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME"
    else
        psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" < "$backup_file"
    fi

    unset PGPASSWORD
}

# Restore SQLite backup
restore_sqlite() {
    local backup_file="$1"

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

    if [ "$FORCE" != "true" ]; then
        echo -n "This will restore the database '$DB_FILE'. Continue? (y/N): "
        read -r confirm
        if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
            log "Restore cancelled"
            exit 0
        fi
    fi

    log "Restoring SQLite backup: $backup_file"

    # Create backup of existing database
    if [ -f "$DB_FILE" ]; then
        local existing_backup="${DB_FILE}.backup.$(date +"$DATE_FORMAT")"
        cp "$DB_FILE" "$existing_backup"
        log "Created backup of existing database: $existing_backup"
    fi

    if [[ "$backup_file" == *.gz ]]; then
        gunzip -c "$backup_file" | sqlite3 "$DB_FILE"
    else
        sqlite3 "$DB_FILE" < "$backup_file"
    fi
}

# List available backups
list_backups() {
    print_header "Available Backups"

    if [ ! -d "$BACKUP_DIR" ]; then
        log_warn "No backup directory found: $BACKUP_DIR"
        return
    fi

    if [ ! "$(ls -A "$BACKUP_DIR")" ]; then
        log_warn "No backups found in $BACKUP_DIR"
        return
    fi

    echo "Format: filename | size | date"
    echo "----------------------------------------"

    for backup in "$BACKUP_DIR"/*; do
        if [ -f "$backup" ]; then
            local filename=$(basename "$backup")
            local size=$(du -h "$backup" | cut -f1)
            local date=$(date -r "$backup" '+%Y-%m-%d %H:%M:%S')
            echo "$filename | $size | $date"
        fi
    done
}

# Clean old backups
clean_backups() {
    print_header "Cleaning Old Backups"

    if [ ! -d "$BACKUP_DIR" ]; then
        log_warn "No backup directory found: $BACKUP_DIR"
        return
    fi

    log "Removing backups older than $KEEP_DAYS days..."

    local deleted=0
    while IFS= read -r -d '' backup; do
        if [ -f "$backup" ]; then
            local filename=$(basename "$backup")
            rm "$backup"
            log "Deleted: $filename"
            ((deleted++))
        fi
    done < <(find "$BACKUP_DIR" -name "*.sql*" -o -name "*.dump*" -o -name "*.tar*" -type f -mtime +$KEEP_DAYS -print0)

    log "Deleted $deleted old backup files"
}

# Export data to JSON/CSV
export_data() {
    print_header "Exporting Data"

    local export_file="${BACKUP_DIR}/export_$(date +"$DATE_FORMAT").json"

    if [ "$DB_TYPE" = "postgresql" ]; then
        log "Exporting PostgreSQL data to JSON..."
        # This would require a custom script or tool
        log_warn "JSON export for PostgreSQL not yet implemented"
        log "Consider using pg_dump with --data-only and custom processing"
    elif [ "$DB_TYPE" = "sqlite" ]; then
        log "Exporting SQLite data to JSON..."
        # This would require a custom script or tool
        log_warn "JSON export for SQLite not yet implemented"
        log "Consider using sqlite3 with custom queries"
    fi
}

# Clone database
clone_database() {
    print_header "Cloning Database"

    local timestamp=$(date +"$DATE_FORMAT")
    local temp_backup="${BACKUP_DIR}/temp_clone_${timestamp}.sql"

    # Create temporary backup
    log "Creating temporary backup for cloning..."
    COMPRESS="false"
    FORMAT="sql"

    if [ "$DB_TYPE" = "postgresql" ]; then
        backup_postgresql "$temp_backup"
    elif [ "$DB_TYPE" = "sqlite" ]; then
        backup_sqlite "$temp_backup"
    fi

    # TODO: Implement actual cloning logic
    # This would involve creating a new database and restoring the backup
    log_warn "Database cloning not yet fully implemented"
    log "Temporary backup created: $temp_backup"
    log "Manual steps required to complete cloning"
}

# Parse command line arguments
COMMAND=""
ENVIRONMENT="dev"
FORMAT="sql"
COMPRESS="false"
SCHEMA_ONLY="false"
DATA_ONLY="false"
TABLES=""
BACKUP_FILE=""
KEEP_DAYS=30
FORCE="false"
QUIET="false"

while [[ $# -gt 0 ]]; do
    case $1 in
        --env)
            ENVIRONMENT="$2"
            shift 2
            ;;
        --backup-dir)
            BACKUP_DIR="$2"
            shift 2
            ;;
        --file)
            BACKUP_FILE="$2"
            shift 2
            ;;
        --format)
            FORMAT="$2"
            shift 2
            ;;
        --compress)
            COMPRESS="true"
            shift
            ;;
        --schema-only)
            SCHEMA_ONLY="true"
            shift
            ;;
        --data-only)
            DATA_ONLY="true"
            shift
            ;;
        --tables)
            TABLES="$2"
            shift 2
            ;;
        --keep-days)
            KEEP_DAYS="$2"
            shift 2
            ;;
        --force)
            FORCE="true"
            shift
            ;;
        --quiet)
            QUIET="true"
            shift
            ;;
        -h|--help)
            print_usage
            exit 0
            ;;
        *)
            if [ -z "$COMMAND" ]; then
                COMMAND="$1"
            else
                log_error "Unknown option: $1"
                print_usage
                exit 1
            fi
            shift
            ;;
    esac
done

# Set environment variable
export ENVIRONMENT="$ENVIRONMENT"

# Validate command
if [ -z "$COMMAND" ]; then
    print_usage
    exit 1
fi

# Check if we're in the right directory
if [ ! -f "Cargo.toml" ]; then
    log_error "Please run this script from the project root directory"
    exit 1
fi

# Load environment and parse database URL
load_env
parse_database_url

# Setup backup directory
setup_backup_dir

# Execute command
case "$COMMAND" in
    "backup")
        print_header "Creating Database Backup"

        if [ -z "$BACKUP_FILE" ]; then
            BACKUP_FILE=$(generate_backup_filename)
        fi

        if [ "$DB_TYPE" = "postgresql" ]; then
            backup_postgresql "$BACKUP_FILE"
        elif [ "$DB_TYPE" = "sqlite" ]; then
            backup_sqlite "$BACKUP_FILE"
        fi

        local file_size=$(du -h "$BACKUP_FILE" | cut -f1)
        log "Backup created successfully: $BACKUP_FILE ($file_size)"
        ;;
    "restore")
        print_header "Restoring Database"

        if [ -z "$BACKUP_FILE" ]; then
            log_error "Please specify backup file with --file option"
            exit 1
        fi

        if [ "$DB_TYPE" = "postgresql" ]; then
            restore_postgresql "$BACKUP_FILE"
        elif [ "$DB_TYPE" = "sqlite" ]; then
            restore_sqlite "$BACKUP_FILE"
        fi

        log "Database restored successfully"
        ;;
    "list")
        list_backups
        ;;
    "clean")
        clean_backups
        ;;
    "export")
        export_data
        ;;
    "import")
        log_warn "Import functionality not yet implemented"
        ;;
    "clone")
        clone_database
        ;;
    "compare")
        log_warn "Database comparison not yet implemented"
        ;;
    *)
        log_error "Unknown command: $COMMAND"
        print_usage
        exit 1
        ;;
esac

log "Operation completed successfully"
