#!/bin/bash

# Safe Database Migration Script
# Usage: ./safe-migrate.sh [environment]
# Environments: Development, Sandbox, Production, Oda

set -e  # Exit on any error

ENVIRONMENT=${1:-"Development"} # Default to Development for safer initial testing
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR_NAME="backups" # Just the directory name
LOGS_DIR_NAME="logs" # Just the directory name

# Get the directory where the script is located
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"

# Create logs directory first (before LOG_FILE is used)
LOGS_DIR="$SCRIPT_DIR/$LOGS_DIR_NAME"
mkdir -p "$LOGS_DIR" || { echo "Failed to create logs directory: $LOGS_DIR"; exit 1; }

LOG_FILE="$SCRIPT_DIR/logs/migration_${ENVIRONMENT}_${TIMESTAMP}.log"

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

# Logging function
log() {
    echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}" | tee -a "$LOG_FILE"
}

warn() {
    echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}" | tee -a "$LOG_FILE"
}

error() {
    echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}" | tee -a "$LOG_FILE"
    exit 1
}

info() {
    echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')] INFO: $1${NC}" | tee -a "$LOG_FILE"
}

# --- Determine Base Paths ---
# Get the directory where the script is located
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )"
log "Script is located in: $SCRIPT_DIR"

# Define absolute paths for projects and common directories relative to the script
# Assuming the project structure is:
# your_repo/
# ├── scripts/safe-migrate.sh
# └── src/
#     ├── Managing.Api/
#     ├── Managing.Infrastructure.Database/
#     └── Managing.Docker/
PROJECT_ROOT_DIR="$(dirname "$SCRIPT_DIR")" # One level up from scripts/
SRC_DIR="$PROJECT_ROOT_DIR/src"
DB_PROJECT_PATH="$SRC_DIR/Managing.Infrastructure.Database"
API_PROJECT_PATH="$SRC_DIR/Managing.Api"
DOCKER_DIR="$SRC_DIR/Managing.Docker" # Adjust if your docker-compose files are elsewhere

# Define absolute path for backup directory with environment subfolder
BACKUP_DIR="$SCRIPT_DIR/$BACKUP_DIR_NAME/$ENVIRONMENT"

# --- Pre-checks and Setup ---
info "Pre-flight checks..."
command -v dotnet >/dev/null 2>&1 || error ".NET SDK is not installed. Please install .NET SDK to run this script."
command -v docker >/dev/null 2>&1 || warn "Docker is not installed. This is fine if not running Development or Oda environment with Docker."
command -v psql >/dev/null 2>&1 || warn "PostgreSQL CLI (psql) is not installed. Database connectivity checks will be skipped."
command -v pg_dump >/dev/null 2>&1 || warn "PostgreSQL pg_dump is not installed. Will use EF Core migration script for backup instead."

# Create backup directory (with environment subfolder)
mkdir -p "$BACKUP_DIR" || error "Failed to create backup directory: $BACKUP_DIR"
log "Backup directory created/verified: $BACKUP_DIR"

log "🚀 Starting safe migration for environment: $ENVIRONMENT"

# Validate environment
case $ENVIRONMENT in
    "Development"|"Sandbox"|"Production"|"Oda")
        log "✅ Environment '$ENVIRONMENT' is valid"
        ;;
    *)
        error "❌ Invalid environment '$ENVIRONMENT'. Use: Development, Sandbox, Production, or Oda"
        ;;
esac

# Helper function to start PostgreSQL for Development (if still using Docker Compose)
start_postgres_if_needed() {
    if [ "$ENVIRONMENT" = "Development" ] || [ "$ENVIRONMENT" = "Oda" ]; then # Assuming Oda also uses local Docker
        log "🔍 Checking if PostgreSQL is running for $ENVIRONMENT..."
        if ! docker ps --filter "name=postgres" --format "{{.Names}}" | grep -q "postgres"; then
            log "🐳 Starting PostgreSQL container for $ENVIRONMENT from $DOCKER_DIR..."
            # Execute docker-compose from the DOCKER_DIR
            (cd "$DOCKER_DIR" && docker-compose -f docker-compose.yml -f docker-compose.local.yml up -d postgres) || error "Failed to start PostgreSQL container."
            log "⏳ Waiting for PostgreSQL to be ready (15 seconds)..."
            sleep 15
        else
            log "✅ PostgreSQL container is already running."
        fi
    fi
}

# Helper function to extract connection details from appsettings
extract_connection_details() {
    local appsettings_file="$API_PROJECT_PATH/appsettings.$ENVIRONMENT.json"
    local default_appsettings="$API_PROJECT_PATH/appsettings.json"
    
    # Try environment-specific file first, then default
    if [ -f "$appsettings_file" ]; then
        log "📋 Reading connection string from: appsettings.$ENVIRONMENT.json"
        # Look for PostgreSql.ConnectionString first, then fallback to ConnectionString
        CONNECTION_STRING=$(grep -A 3 '"PostgreSql"' "$appsettings_file" | grep -o '"ConnectionString": *"[^"]*"' | cut -d'"' -f4)
        if [ -z "$CONNECTION_STRING" ]; then
            CONNECTION_STRING=$(grep -o '"ConnectionString": *"[^"]*"' "$appsettings_file" | cut -d'"' -f4)
        fi
    elif [ -f "$default_appsettings" ]; then
        log "📋 Reading connection string from: appsettings.json (default)"
        # Look for PostgreSql.ConnectionString first, then fallback to ConnectionString
        CONNECTION_STRING=$(grep -A 3 '"PostgreSql"' "$default_appsettings" | grep -o '"ConnectionString": *"[^"]*"' | cut -d'"' -f4)
        if [ -z "$CONNECTION_STRING" ]; then
            CONNECTION_STRING=$(grep -o '"ConnectionString": *"[^"]*"' "$default_appsettings" | cut -d'"' -f4)
        fi
    else
        warn "⚠️  Could not find appsettings file for environment $ENVIRONMENT"
        return 1
    fi
    
    if [ -z "$CONNECTION_STRING" ]; then
        error "❌ Could not extract connection string from appsettings file"
        return 1
    fi
    
    log "📋 Found connection string: $CONNECTION_STRING"
    
    # Parse connection string
    DB_HOST=$(echo "$CONNECTION_STRING" | grep -o 'Host=[^;]*' | cut -d'=' -f2)
    DB_PORT=$(echo "$CONNECTION_STRING" | grep -o 'Port=[^;]*' | cut -d'=' -f2)
    DB_NAME=$(echo "$CONNECTION_STRING" | grep -o 'Database=[^;]*' | cut -d'=' -f2)
    DB_USER=$(echo "$CONNECTION_STRING" | grep -o 'Username=[^;]*' | cut -d'=' -f2)
    DB_PASSWORD=$(echo "$CONNECTION_STRING" | grep -o 'Password=[^;]*' | cut -d'=' -f2)
    
    # Set defaults if not found
    DB_HOST=${DB_HOST:-"localhost"}
    DB_PORT=${DB_PORT:-"5432"}
    DB_NAME=${DB_NAME:-"postgres"}
    DB_USER=${DB_USER:-"postgres"}
    DB_PASSWORD=${DB_PASSWORD:-"postgres"}
    
    log "📋 Extracted connection details: $DB_HOST:$DB_PORT/$DB_NAME (user: $DB_USER, password: $DB_PASSWORD)"
}

# Helper function to get the first migration name
get_first_migration() {
    local first_migration=$(cd "$DB_PROJECT_PATH" && dotnet ef migrations list --no-build --startup-project "$API_PROJECT_PATH" | head -1 | awk '{print $1}')
    echo "$first_migration"
}

# Helper function to test PostgreSQL connectivity
test_postgres_connectivity() {
    if ! command -v psql >/dev/null 2>&1; then
        warn "⚠️  psql not available, skipping PostgreSQL connectivity test"
        return 0
    fi
    
    log "🔍 Testing PostgreSQL connectivity with psql..."
    
    # For remote servers or when target database might not exist, test with postgres database first
    local test_database="$DB_NAME"
    if [ "$TARGET_DB_EXISTS" = "false" ]; then
        test_database="postgres"
        log "🔍 Target database doesn't exist, testing connectivity with 'postgres' database..."
    fi
    
    # Test basic connectivity
    if PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$test_database" -c "SELECT version();" >/dev/null 2>&1; then
        log "✅ PostgreSQL connectivity test passed"
        
        # Get database info
        log "📊 Database Information:"
        DB_INFO=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$test_database" -t -c "
            SELECT 
                'Database: ' || current_database() || ' (Size: ' || pg_size_pretty(pg_database_size(current_database())) || ')',
                'PostgreSQL Version: ' || version(),
                'Connection: ' || inet_server_addr() || ':' || inet_server_port()
        " 2>/dev/null | tr '\n' ' ')
        log "   $DB_INFO"
        
        # Only check migrations if we're testing the actual target database
        if [ "$test_database" = "$DB_NAME" ]; then
            # Check if __EFMigrationsHistory table exists
            if PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -c "\dt __EFMigrationsHistory" >/dev/null 2>&1; then
                log "✅ EF Core migrations history table exists"
                
                # Count applied migrations
                MIGRATION_COUNT=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -t -c "SELECT COUNT(*) FROM \"__EFMigrationsHistory\";" 2>/dev/null | tr -d ' ')
                log "📋 Applied migrations count: $MIGRATION_COUNT"
                
                # Show recent migrations
                if [ "$MIGRATION_COUNT" -gt 0 ]; then
                    log "📋 Recent migrations:"
                    RECENT_MIGRATIONS=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -t -c "
                        SELECT \"MigrationId\" FROM \"__EFMigrationsHistory\" 
                        ORDER BY \"MigrationId\" DESC 
                        LIMIT 5;
                    " 2>/dev/null | sed 's/^/   /')
                    echo "$RECENT_MIGRATIONS"
                fi
            else
                warn "⚠️  EF Core migrations history table not found - database may be empty"
            fi
        else
            log "📋 Connectivity test completed using 'postgres' database (target database will be created)"
        fi
        
        return 0
    else
        error "❌ PostgreSQL connectivity test failed"
        error "   Host: $DB_HOST, Port: $DB_PORT, Database: $test_database, User: $DB_USER"
        return 1
    fi
}

# --- Core Logic ---
# No global 'cd' needed here. All paths are now absolute.
# This makes the script much more robust to where it's executed from.

# Set ASPNETCORE_ENVIRONMENT to load the correct appsettings
export ASPNETCORE_ENVIRONMENT="$ENVIRONMENT"
log "ASPNETCORE_ENVIRONMENT set to: $ASPNETCORE_ENVIRONMENT"

# If Development or Oda, start local PostgreSQL
start_postgres_if_needed

# Extract connection details from appsettings
extract_connection_details

# Step 0: Build the .NET projects to ensure they are up to date
log "🔨 Step 0: Building .NET projects..."
log "🔧 Building Managing.Infrastructure.Database project..."
if (cd "$DB_PROJECT_PATH" && dotnet build); then
    log "✅ Managing.Infrastructure.Database project built successfully"
else
    error "❌ Failed to build Managing.Infrastructure.Database project"
fi

# Step 1: Check Database Connection and Create if Needed
log "🔧 Step 1: Checking database connection and creating database if needed..."

# Log the environment and expected connection details (for user info, still relies on appsettings)
log "🔧 Using environment: $ENVIRONMENT"
log "📋 Connection details: $DB_HOST:$DB_PORT/$DB_NAME (user: $DB_USER)"

# Initial connectivity check - test if we can reach the database server
log "🔍 Step 1a: Testing basic database server connectivity..."
if command -v psql >/dev/null 2>&1; then
    # Test if we can connect to the postgres database (which should always exist)
    log "🔍 Connecting to default 'postgres' database to verify server connectivity..."
    if PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres -c "SELECT 1;" >/dev/null 2>&1; then
        log "✅ Database server connectivity test passed"
        
        # Check if our target database exists
        log "🔍 Checking if target database '$DB_NAME' exists..."
        DB_EXISTS=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres -t -c "SELECT 1 FROM pg_database WHERE datname = '$DB_NAME';" 2>/dev/null | tr -d ' ')
        
        if [ "$DB_EXISTS" = "1" ]; then
            log "✅ Target database '$DB_NAME' exists"
            TARGET_DB_EXISTS=true
        else
            log "⚠️  Target database '$DB_NAME' does not exist - will be created"
            TARGET_DB_EXISTS=false
        fi
        
    else
        error "❌ Database server connectivity test failed"
        error "   Cannot reach PostgreSQL server at $DB_HOST:$DB_PORT with database 'postgres'"
        error "   Please verify:"
        error "   - Database server is running"
        error "   - Network connectivity to $DB_HOST:$DB_PORT"
        error "   - Credentials are correct (user: $DB_USER)"
        error "   - Firewall settings allow connections"
        error "   - The 'postgres' database exists (default PostgreSQL database)"
    fi
else
    # Fallback: try to connect using EF Core to test basic connectivity
    log "🔄 psql not available, testing connectivity via EF Core..."
    if (cd "$DB_PROJECT_PATH" && dotnet ef migrations list --no-build --startup-project "$API_PROJECT_PATH" --connection "$CONNECTION_STRING") >/dev/null 2>&1; then
        log "✅ Database server connectivity test passed (via EF Core)"
        TARGET_DB_EXISTS=true  # Assume it exists if EF Core can connect
    else
        warn "⚠️  Could not verify database server connectivity (psql not available)"
        warn "   Proceeding with caution - connectivity will be tested during migration"
        TARGET_DB_EXISTS=false  # Assume it doesn't exist if EF Core can't connect
    fi
fi

log "🔍 Step 1b: Testing database connection and checking if database exists via EF CLI..."

# Test connection by listing migrations. If it fails, the database likely doesn't exist or is inaccessible.
# Execute dotnet ef from DB_PROJECT_PATH for correct context, but pass API_PROJECT_PATH as startup.
# Since we just built the projects, we can safely use --no-build flag for faster execution
if (cd "$DB_PROJECT_PATH" && dotnet ef migrations list --no-build --startup-project "$API_PROJECT_PATH" --connection "$CONNECTION_STRING") >/dev/null 2>&1; then
    log "✅ EF Core database connection successful and database appears to exist."
    
    # Now test with psql for additional verification (this will use postgres db if target doesn't exist)
    test_postgres_connectivity
    
    # If psql connectivity test fails, stop the migration
    if [ $? -ne 0 ]; then
        error "❌ PostgreSQL connectivity test failed. Migration aborted for safety."
        error "   Please verify your database connection and try again."
    fi
    
else
    # Database doesn't exist or connection failed
    if [ "$TARGET_DB_EXISTS" = "false" ]; then
        log "📝 Database '$DB_NAME' does not exist. Creating database and applying migrations..."
        
        # Test connectivity with postgres database first (since target doesn't exist)
        test_postgres_connectivity
        
        # If connectivity test fails, stop the migration
        if [ $? -ne 0 ]; then
            error "❌ PostgreSQL connectivity test failed. Cannot proceed with database creation."
            error "   Please verify your connection settings and try again."
        fi
        
        # Step 1: Create the database first
        log "🔧 Step 1: Creating database '$DB_NAME'..."
        if command -v psql >/dev/null 2>&1; then
            if PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres -c "CREATE DATABASE \"$DB_NAME\";" >/dev/null 2>&1; then
                log "✅ Database '$DB_NAME' created successfully"
            else
                error "❌ Failed to create database '$DB_NAME'"
                error "   Please verify you have sufficient privileges to create databases."
            fi
        else
            warn "⚠️  psql not available, attempting to create database via EF Core..."
            # EF Core will attempt to create the database during update
        fi
        
        # Step 2: Generate migration script for the new database
        log "📝 Step 2: Generating migration script for new database..."
        TEMP_MIGRATION_SCRIPT="$BACKUP_DIR/temp_migration_${ENVIRONMENT}_${TIMESTAMP}.sql"
        
        if (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --idempotent --no-build --startup-project "$API_PROJECT_PATH" --output "$TEMP_MIGRATION_SCRIPT"); then
            log "✅ Migration script generated successfully: $(basename "$TEMP_MIGRATION_SCRIPT")"
            
            # Step 3: Apply the migration script to the new database
            log "🔧 Step 3: Applying migration script to new database..."
            if command -v psql >/dev/null 2>&1; then
                if PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -f "$TEMP_MIGRATION_SCRIPT" >/dev/null 2>&1; then
                    log "✅ Migration script applied successfully to new database"
                else
                    error "❌ Failed to apply migration script to newly created database"
                fi
            else
                # Fallback to EF Core database update
                log "🔄 psql not available, using EF Core to apply migrations..."
                if (cd "$DB_PROJECT_PATH" && dotnet ef database update --no-build --startup-project "$API_PROJECT_PATH" --connection "$CONNECTION_STRING"); then
                    log "✅ Database created and initialized successfully using EF Core"
                else
                    ERROR_OUTPUT=$( (cd "$DB_PROJECT_PATH" && dotnet ef database update --no-build --startup-project "$API_PROJECT_PATH" --connection "$CONNECTION_STRING") 2>&1 || true )
                    error "❌ Failed to create and initialize database using EF Core."
                    error "   EF CLI Output: $ERROR_OUTPUT"
                fi
            fi
            
            # Clean up temporary migration script
            rm -f "$TEMP_MIGRATION_SCRIPT"
            
        else
            ERROR_OUTPUT=$( (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --idempotent --no-build --startup-project "$API_PROJECT_PATH" --output "$TEMP_MIGRATION_SCRIPT") 2>&1 || true )
            error "❌ Failed to generate migration script."
            error "   EF CLI Output: $ERROR_OUTPUT"
        fi
        
    else
        warn "⚠️  Database connection failed but database may exist. Attempting to update existing database..."
        
        # Try to update the existing database
        if (cd "$DB_PROJECT_PATH" && dotnet ef database update --no-build --startup-project "$API_PROJECT_PATH" --connection "$CONNECTION_STRING"); then
            log "✅ Database updated successfully"
        else
            ERROR_OUTPUT=$( (cd "$DB_PROJECT_PATH" && dotnet ef database update --no-build --startup-project "$API_PROJECT_PATH" --connection "$CONNECTION_STRING") 2>&1 || true )
            error "❌ Failed to update database."
            error "   EF CLI Output: $ERROR_OUTPUT"
            error "   This usually means the connection string in your .NET project's appsettings.$ENVIRONMENT.json is incorrect,"
            error "   or the database server is not running/accessible for environment '$ENVIRONMENT'."
        fi
    fi
    
    # Test connectivity after creation/update
    test_postgres_connectivity
    
    # If connectivity test fails after creation, stop the migration
    if [ $? -ne 0 ]; then
        error "❌ PostgreSQL connectivity test failed after database creation. Migration aborted for safety."
        error "   Database may have been created but is not accessible. Please verify your connection settings."
    fi
fi

# Final verification of connection
log "🔍 Verifying database connection post-creation/update..."
if (cd "$DB_PROJECT_PATH" && dotnet ef migrations list --no-build --startup-project "$API_PROJECT_PATH" --connection "$CONNECTION_STRING") >/dev/null 2>&1; then
    log "✅ Database connectivity verification passed."
else
    ERROR_OUTPUT=$( (cd "$DB_PROJECT_PATH" && dotnet ef migrations list --no-build --startup-project "$API_PROJECT_PATH" --connection "$CONNECTION_STRING") 2>&1 || true )
    error "❌ Final database connectivity verification failed."
    error "   EF CLI Output: $ERROR_OUTPUT"
    error "   This is critical. Please review the previous error messages and your connection string for '$ENVIRONMENT'."
fi

# Step 2: Create database backup (only if database exists)
log "📦 Step 2: Checking if database backup is needed..."

# Check if the target database exists
DB_EXISTS=false
if PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "postgres" -c "SELECT 1 FROM pg_database WHERE datname='$DB_NAME';" 2>/dev/null | grep -q "1 row"; then
    DB_EXISTS=true
    log "✅ Target database '$DB_NAME' exists - proceeding with backup"
else
    log "ℹ️  Target database '$DB_NAME' does not exist - skipping backup"
fi

if [ "$DB_EXISTS" = "true" ]; then
    # Define the actual backup file path (absolute)
    BACKUP_FILE="$BACKUP_DIR/managing_${ENVIRONMENT}_backup_${TIMESTAMP}.sql"
    # Backup file display path (relative to script execution)
    BACKUP_FILE_DISPLAY="$BACKUP_DIR_NAME/$ENVIRONMENT/managing_${ENVIRONMENT}_backup_${TIMESTAMP}.sql"

# Create backup with retry logic
BACKUP_SUCCESS=false
for attempt in 1 2 3; do
    log "Backup attempt $attempt/3..."
    
    # Create real database backup using pg_dump
    if command -v pg_dump >/dev/null 2>&1; then
        if PGPASSWORD="$DB_PASSWORD" pg_dump -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" --no-password --verbose --clean --if-exists --create --format=plain > "$BACKUP_FILE" 2>/dev/null; then
            log "✅ Database backup created using pg_dump: $BACKUP_FILE_DISPLAY"
            BACKUP_SUCCESS=true
            break
        else
            # If pg_dump fails, fall back to EF Core migration script
            warn "⚠️  pg_dump failed, falling back to EF Core migration script..."
            
            # Get the first migration name to generate complete script
            FIRST_MIGRATION=$(get_first_migration)
            
            if [ -n "$FIRST_MIGRATION" ]; then
                log "📋 Generating complete backup script from initial migration: $FIRST_MIGRATION"
                if (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --from "$FIRST_MIGRATION" --idempotent --no-build --startup-project "$API_PROJECT_PATH" --output "$BACKUP_FILE"); then
                    log "✅ Complete EF Core Migration SQL Script generated: $BACKUP_FILE_DISPLAY"
                    BACKUP_SUCCESS=true
                    break
                else
                    # Try fallback without specifying from migration
                    ERROR_OUTPUT=$( (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --idempotent --no-build --startup-project "$API_PROJECT_PATH" --output "$BACKUP_FILE") 2>&1 || true)
                    if [ $attempt -lt 3 ]; then
                        warn "⚠️  Backup attempt $attempt failed. Retrying in 5 seconds..."
                        warn "   EF CLI Output: $ERROR_OUTPUT"
                        sleep 5
                    else
                        error "❌ Database backup failed after 3 attempts."
                        error "   EF CLI Output: $ERROR_OUTPUT"
                        error "   Migration aborted for safety reasons."
                    fi
                fi
            else
                # Fallback: generate script without specifying from migration
                if (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --idempotent --no-build --startup-project "$API_PROJECT_PATH" --output "$BACKUP_FILE"); then
                    log "✅ EF Core Migration SQL Script generated: $BACKUP_FILE_DISPLAY"
                    BACKUP_SUCCESS=true
                    break
                else
                    # Try fallback without specifying from migration
                    ERROR_OUTPUT=$( (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --idempotent --no-build --startup-project "$API_PROJECT_PATH" --output "$BACKUP_FILE") 2>&1 || true)
                    if [ $attempt -lt 3 ]; then
                        warn "⚠️  Backup attempt $attempt failed. Retrying in 5 seconds..."
                        warn "   EF CLI Output: $ERROR_OUTPUT"
                        sleep 5
                    else
                        error "❌ Database backup failed after 3 attempts."
                        error "   EF CLI Output: $ERROR_OUTPUT"
                        error "   Migration aborted for safety reasons."
                    fi
                fi
            fi
        fi
    else
        # If pg_dump is not available, use EF Core migration script
        warn "⚠️  pg_dump not available, using EF Core migration script for backup..."
        
        # Get the first migration name to generate complete script
        FIRST_MIGRATION=$(get_first_migration)
        
        if [ -n "$FIRST_MIGRATION" ]; then
            log "📋 Generating complete backup script from initial migration: $FIRST_MIGRATION"
            if (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --from "$FIRST_MIGRATION" --idempotent --no-build --startup-project "$API_PROJECT_PATH" --output "$BACKUP_FILE"); then
                log "✅ Complete EF Core Migration SQL Script generated: $BACKUP_FILE_DISPLAY"
                BACKUP_SUCCESS=true
                break
            else
                # Try fallback without specifying from migration
                ERROR_OUTPUT=$( (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --idempotent --no-build --startup-project "$API_PROJECT_PATH" --output "$BACKUP_FILE") 2>&1 || true)
                if [ $attempt -lt 3 ]; then
                    warn "⚠️  Backup attempt $attempt failed. Retrying in 5 seconds..."
                    warn "   EF CLI Output: $ERROR_OUTPUT"
                    sleep 5
                else
                    error "❌ Database backup failed after 3 attempts."
                    error "   EF CLI Output: $ERROR_OUTPUT"
                    error "   Migration aborted for safety reasons."
                fi
            fi
        else
            # Fallback: generate script without specifying from migration
            if (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --idempotent --no-build --startup-project "$API_PROJECT_PATH" --output "$BACKUP_FILE"); then
                log "✅ EF Core Migration SQL Script generated: $BACKUP_FILE_DISPLAY"
                BACKUP_SUCCESS=true
                break
            else
                # Try fallback without specifying from migration
                ERROR_OUTPUT=$( (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --idempotent --no-build --startup-project "$API_PROJECT_PATH" --output "$BACKUP_FILE") 2>&1 || true)
                if [ $attempt -lt 3 ]; then
                    warn "⚠️  Backup attempt $attempt failed. Retrying in 5 seconds..."
                    warn "   EF CLI Output: $ERROR_OUTPUT"
                    sleep 5
                else
                    error "❌ Database backup failed after 3 attempts."
                    error "   EF CLI Output: $ERROR_OUTPUT"
                    error "   Migration aborted for safety reasons."
                fi
            fi
        fi
    fi
done

    # Check if backup was successful before proceeding
    if [ "$BACKUP_SUCCESS" != "true" ]; then
        error "❌ Database backup failed. Migration aborted for safety."
        error "   Cannot proceed with migration without a valid backup."
        error "   Please resolve backup issues and try again."
    fi
fi

# Step 2.5: Check for pending model changes and create migrations if needed
log "🔍 Step 2.5: Checking for pending model changes..."

# Check if there are any pending model changes that need migrations
PENDING_CHANGES_OUTPUT=$( (cd "$DB_PROJECT_PATH" && dotnet ef migrations add --dry-run --startup-project "$API_PROJECT_PATH" --name "PendingChanges_${TIMESTAMP}") 2>&1 || true )

if echo "$PENDING_CHANGES_OUTPUT" | grep -q "No pending model changes"; then
    log "✅ No pending model changes detected - existing migrations are up to date"
else
    log "⚠️  Pending model changes detected that require new migrations"
    echo ""
    echo "=========================================="
    echo "📋 PENDING MODEL CHANGES DETECTED"
    echo "=========================================="
    echo "The following changes require new migrations:"
    echo "$PENDING_CHANGES_OUTPUT"
    echo ""
    echo "Would you like to create a new migration now?"
    echo "=========================================="
    echo ""
    
    read -p "🔧 Create new migration? (y/n): " create_migration
    
    if [[ "$create_migration" =~ ^[Yy]$ ]]; then
        log "📝 Creating new migration..."
        
        # Get migration name from user
        read -p "📝 Enter migration name (or press Enter for auto-generated name): " migration_name
        if [ -z "$migration_name" ]; then
            migration_name="Migration_${TIMESTAMP}"
        fi
        
        # Create the migration
        if (cd "$DB_PROJECT_PATH" && dotnet ef migrations add "$migration_name" --startup-project "$API_PROJECT_PATH"); then
            log "✅ Migration '$migration_name' created successfully"
            
            # Show the created migration file
            LATEST_MIGRATION=$(find "$DB_PROJECT_PATH/Migrations" -name "*${migration_name}.cs" | head -1)
            if [ -n "$LATEST_MIGRATION" ]; then
                log "📄 Migration file created: $(basename "$LATEST_MIGRATION")"
                log "   Location: $LATEST_MIGRATION"
            fi
        else
            ERROR_OUTPUT=$( (cd "$DB_PROJECT_PATH" && dotnet ef migrations add "$migration_name" --startup-project "$API_PROJECT_PATH") 2>&1 || true )
            error "❌ Failed to create migration '$migration_name'"
            error "   EF CLI Output: $ERROR_OUTPUT"
            error "   Please resolve the model issues and try again."
        fi
    else
        log "⚠️  Skipping migration creation. Proceeding with existing migrations only."
        log "   Note: If there are pending changes, the migration may fail."
    fi
fi

# Step 3: Run Migration (This effectively is a retry if previous "update" failed, or a final apply)
log "🔄 Step 3: Running database migration (final application of pending migrations)..."

# Check if database exists and create it if needed before applying migrations
log "🔍 Step 3a: Ensuring target database exists..."
if [ "$TARGET_DB_EXISTS" = "false" ]; then
    log "🔧 Creating database '$DB_NAME'..."
    if command -v psql >/dev/null 2>&1; then
        if PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d postgres -c "CREATE DATABASE \"$DB_NAME\";" >/dev/null 2>&1; then
            log "✅ Database '$DB_NAME' created successfully"
        else
            error "❌ Failed to create database '$DB_NAME'"
            error "   Please verify you have sufficient privileges to create databases."
        fi
    else
        error "❌ psql not available, cannot create database. Please create database '$DB_NAME' manually."
    fi
fi

# Generate migration script first (Microsoft recommended approach)
MIGRATION_SCRIPT="$BACKUP_DIR/migration_${ENVIRONMENT}_${TIMESTAMP}.sql"
log "📝 Step 3b: Generating migration script for pending migrations..."

# Check if database is empty (no tables) to determine the best approach
log "🔍 Checking if database has existing tables..."
DB_HAS_TABLES=false
if command -v psql >/dev/null 2>&1; then
    TABLE_COUNT=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -t -c "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public';" 2>/dev/null | tr -d ' ' || echo "0")
    if [ "$TABLE_COUNT" -gt 0 ]; then
        DB_HAS_TABLES=true
        log "✅ Database has $TABLE_COUNT existing tables - using idempotent script generation"
    else
        log "⚠️  Database appears to be empty - using full migration script generation"
    fi
else
    log "⚠️  psql not available - assuming database has tables and using idempotent script generation"
    DB_HAS_TABLES=true
fi

# Generate migration script based on database state
if [ "$DB_HAS_TABLES" = "true" ]; then
    # For databases with existing tables, we need to generate a complete script
    # that includes all migrations from the beginning
    log "📝 Generating complete migration script from initial migration..."
    
    # Get the first migration name to generate script from the beginning
    FIRST_MIGRATION=$(get_first_migration)
    
    if [ -n "$FIRST_MIGRATION" ]; then
        log "📋 Generating complete script for all migrations (idempotent)..."
        if (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --idempotent --no-build --startup-project "$API_PROJECT_PATH" --output "$MIGRATION_SCRIPT"); then
            log "✅ Complete migration script generated (all migrations, idempotent): $(basename "$MIGRATION_SCRIPT")"
        else
            ERROR_OUTPUT=$( (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --idempotent --no-build --startup-project "$API_PROJECT_PATH" --output "$MIGRATION_SCRIPT") 2>&1 || true )
            error "❌ Failed to generate complete migration script."
            error "   EF CLI Output: $ERROR_OUTPUT"
            error "   Check the .NET project logs for detailed errors."
            error "   Backup script available at: $BACKUP_FILE_DISPLAY"
        fi
    else
        # Fallback: generate script without specifying from migration
        log "📝 Fallback: Generating migration script without specifying from migration..."
        if (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --idempotent --no-build --startup-project "$API_PROJECT_PATH" --output "$MIGRATION_SCRIPT"); then
            log "✅ Migration script generated (idempotent): $(basename "$MIGRATION_SCRIPT")"
        else
            ERROR_OUTPUT=$( (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --idempotent --no-build --startup-project "$API_PROJECT_PATH" --output "$MIGRATION_SCRIPT") 2>&1 || true )
            error "❌ Failed to generate idempotent migration script."
            error "   EF CLI Output: $ERROR_OUTPUT"
            error "   Check the .NET project logs for detailed errors."
            error "   Backup script available at: $BACKUP_FILE_DISPLAY"
        fi
    fi
else
    # Use full script generation for empty databases (generate script from the very beginning)
    log "📝 Generating full migration script for empty database..."
    
    # Get the first migration name to generate script from the beginning
    FIRST_MIGRATION=$(get_first_migration)
    
    if [ -n "$FIRST_MIGRATION" ]; then
        log "📋 Generating complete script for all migrations..."
        if (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --no-build --startup-project "$API_PROJECT_PATH" --output "$MIGRATION_SCRIPT"); then
            log "✅ Complete migration script generated (all migrations): $(basename "$MIGRATION_SCRIPT")"
        else
            ERROR_OUTPUT=$( (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --no-build --startup-project "$API_PROJECT_PATH" --output "$MIGRATION_SCRIPT") 2>&1 || true )
            error "❌ Failed to generate complete migration script."
            error "   EF CLI Output: $ERROR_OUTPUT"
            error "   Check the .NET project logs for detailed errors."
            error "   Backup script available at: $BACKUP_FILE_DISPLAY"
        fi
    else
        # Fallback: generate script without specifying from migration
        log "📝 Fallback: Generating migration script without specifying from migration..."
        if (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --no-build --startup-project "$API_PROJECT_PATH" --output "$MIGRATION_SCRIPT"); then
            log "✅ Migration script generated (fallback): $(basename "$MIGRATION_SCRIPT")"
        else
            ERROR_OUTPUT=$( (cd "$DB_PROJECT_PATH" && ASPNETCORE_ENVIRONMENT="$ENVIRONMENT" dotnet ef migrations script --no-build --startup-project "$API_PROJECT_PATH" --output "$MIGRATION_SCRIPT") 2>&1 || true )
            error "❌ Failed to generate fallback migration script."
            error "   EF CLI Output: $ERROR_OUTPUT"
            error "   Check the .NET project logs for detailed errors."
            error "   Backup script available at: $BACKUP_FILE_DISPLAY"
        fi
    fi
fi
    
    # Show the migration script path to the user for review
    echo ""
    echo "=========================================="
    echo "📋 MIGRATION SCRIPT READY FOR REVIEW"
    echo "=========================================="
    echo "Generated script: $MIGRATION_SCRIPT"
    echo "Environment: $ENVIRONMENT"
    echo "Database: $DB_HOST:$DB_PORT/$DB_NAME"
    echo ""
    
    # Show a preview of the migration script content
    if [ -f "$MIGRATION_SCRIPT" ]; then
        SCRIPT_SIZE=$(wc -l < "$MIGRATION_SCRIPT")
        echo "📄 Migration script contains $SCRIPT_SIZE lines"
        
        # Show first 20 lines as preview
        echo ""
        echo "📋 PREVIEW (first 20 lines):"
        echo "----------------------------------------"
        head -20 "$MIGRATION_SCRIPT" | sed 's/^/   /'
        if [ "$SCRIPT_SIZE" -gt 20 ]; then
            echo "   ... (showing first 20 lines of $SCRIPT_SIZE total)"
        fi
        echo "----------------------------------------"
        echo ""
    fi
    
    echo "⚠️  IMPORTANT: Please review the migration script before proceeding!"
    echo "   You can examine the full script with: cat $MIGRATION_SCRIPT"
    echo "   Or open it in your editor to review the changes."
    echo ""
    echo "=========================================="
    echo ""
    
    # Ask for user confirmation
    read -p "🔍 Have you reviewed the migration script and want to proceed? Type 'yes' to continue: " user_confirmation
    
    if [ "$user_confirmation" != "yes" ]; then
        log "❌ Migration cancelled by user."
        log "   Migration script is available at: $(basename "$MIGRATION_SCRIPT")"
        log "   You can apply it manually later with:"
        log "   PGPASSWORD=\"$DB_PASSWORD\" psql -h \"$DB_HOST\" -p \"$DB_PORT\" -U \"$DB_USER\" -d \"$DB_NAME\" -f \"$MIGRATION_SCRIPT\""
        exit 0
    fi
    
    log "✅ User confirmed migration. Proceeding with database update..."
    
    # Apply the migration script using psql (recommended approach)
    log "🔧 Step 3c: Applying migration script to database..."
    if command -v psql >/dev/null 2>&1; then
        if PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -f "$MIGRATION_SCRIPT" >/dev/null 2>&1; then
            log "✅ Migration script applied successfully to database"
        else
            ERROR_OUTPUT=$( (PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -f "$MIGRATION_SCRIPT") 2>&1 || true )
            error "❌ Failed to apply migration script to database"
            error "   PSQL Output: $ERROR_OUTPUT"
            error "   Migration script available at: $(basename "$MIGRATION_SCRIPT")"
        fi
    else
        # Fallback to EF Core database update if psql is not available
        log "🔄 psql not available, falling back to EF Core database update..."
        if (cd "$DB_PROJECT_PATH" && dotnet ef database update --no-build --startup-project "$API_PROJECT_PATH" --connection "$CONNECTION_STRING"); then
            log "✅ Database migration completed successfully using EF Core."
        else
            ERROR_OUTPUT=$( (cd "$DB_PROJECT_PATH" && dotnet ef database update --no-build --startup-project "$API_PROJECT_PATH" --connection "$CONNECTION_STRING") 2>&1 || true )
            error "❌ Database migration failed during final update."
            error "   EF CLI Output: $ERROR_OUTPUT"
            error "   Check the .NET project logs for detailed errors."
            error "   Backup script available at: $BACKUP_FILE_DISPLAY"
        fi
    fi
    
    # Save a copy of the migration script for reference before cleaning up
    MIGRATION_SCRIPT_COPY="$BACKUP_DIR/migration_${ENVIRONMENT}_${TIMESTAMP}_applied.sql"
    if [ -f "$MIGRATION_SCRIPT" ]; then
        cp "$MIGRATION_SCRIPT" "$MIGRATION_SCRIPT_COPY"
        log "📝 Migration script saved for reference: $(basename "$MIGRATION_SCRIPT_COPY")"
    fi
    
    # Clean up temporary migration script after successful application
    rm -f "$MIGRATION_SCRIPT"

# Step 4: Verify Migration
log "🔍 Step 4: Verifying migration status..."

# List migrations to check applied status
MIGRATION_LIST_OUTPUT=$( (cd "$DB_PROJECT_PATH" && dotnet ef migrations list --no-build --startup-project "$API_PROJECT_PATH" --connection "$CONNECTION_STRING") 2>&1 )
log "📋 Current migration status:\n$MIGRATION_LIST_OUTPUT"

# Check if there are any pending migrations after update
PENDING_MIGRATIONS=$(echo "$MIGRATION_LIST_OUTPUT" | grep -c "\[ \]" || echo "0")
PENDING_MIGRATIONS=$(echo "$PENDING_MIGRATIONS" | tr -d '\n')  # Remove any newlines
if [ "$PENDING_MIGRATIONS" -gt 0 ]; then
    warn "⚠️  WARNING: $PENDING_MIGRATIONS pending migration(s) found after update."
    warn "   This indicates the 'dotnet ef database update' command may not have fully completed."
else
    log "✅ All migrations appear to be applied successfully."
fi

# --- Step 5: Cleanup Backups (keep only 5 dumps max) ---
log "🧹 Step 5: Cleaning up old backups..."

# Keep only the last 5 backups for this environment (in the environment-specific subfolder)
ls -t "$BACKUP_DIR"/managing_${ENVIRONMENT}_backup_*.sql 2>/dev/null | tail -n +6 | xargs -r rm -f || true # Added -f for force removal

log "✅ Kept last 5 backups for $ENVIRONMENT environment in $BACKUP_DIR_NAME/$ENVIRONMENT/"

# Success Summary
log "🎉 Migration completed successfully for environment: $ENVIRONMENT!"
log "📁 EF Core Migration SQL Script: $BACKUP_FILE_DISPLAY"
log "📝 Full Log file: $LOG_FILE"

echo ""
echo "=========================================="
echo "📋 MIGRATION SUMMARY"
echo "=========================================="
echo "Environment: $ENVIRONMENT"
echo "Timestamp: $TIMESTAMP"
echo "Status: ✅ SUCCESS"
echo "EF Core SQL Backup: $BACKUP_FILE_DISPLAY"
echo "Log: $LOG_FILE"
echo "=========================================="