#!/usr/bin/env bash
# shellcheck shell=bash

[ ! -z "$DEBUG" ] && set -x

# Default values
PSQL_VERSION="ALL"
PORTNO="@PGSQL_DEFAULT_PORT@"
PGSQL_SUPERUSER="@PGSQL_SUPERUSER@"
PGPASSWORD="${PGPASSWORD:-postgres}"
PGSQL_USER="postgres"
FLAKE_URL="github:supabase/postgres"
MIGRATIONS_DIR="@MIGRATIONS_DIR@"
CURRENT_SYSTEM="@CURRENT_SYSTEM@"
ANSIBLE_VARS="@ANSIBLE_VARS@"
PGBOUNCER_AUTH_SCHEMA_SQL=@PGBOUNCER_AUTH_SCHEMA_SQL@
STAT_EXTENSION_SQL=@STAT_EXTENSION_SQL@

# Start PostgreSQL using nix
start_postgres() {
    DATDIR=$(mktemp -d)
    echo "Starting PostgreSQL in directory: $DATDIR"  # Create the DATDIR if it doesn't exist
    nix run "$FLAKE_URL#start-server" -- "$PSQL_VERSION" --skip-migrations --daemonize --datdir "$DATDIR" 
    echo "PostgreSQL started."
}

# Cleanup function
cleanup() {
    echo "Cleaning up..."
    
    # Check if PostgreSQL processes exist
    if pgrep -f "postgres" >/dev/null; then
        echo "Stopping PostgreSQL gracefully..."
        
        # Use pg_ctl to stop PostgreSQL
        pg_ctl -D "$DATDIR" stop
        
        # Wait a bit for graceful shutdown
        sleep 5
        
        # Check if processes are still running
        if pgrep -f "postgres" >/dev/null; then
            echo "Warning: Some PostgreSQL processes could not be stopped gracefully."
        fi
    else
        echo "PostgreSQL is not running, skipping stop."
    fi

    # Always exit successfully, log any remaining processes
    if pgrep -f "postgres" >/dev/null; then
        echo "Warning: Some PostgreSQL processes could not be cleaned up:"
        pgrep -f "postgres"
    else
        echo "Cleanup completed successfully"
    fi
}


# Function to display help
print_help() {
    echo "Usage: nix run .#dbmate-tool -- [options]"
    echo
    echo "Options:"
    echo "  -v, --version [15|16|orioledb-17|all]  Specify the PostgreSQL version to use (required defaults to --version all)"
    echo "  -p, --port PORT                    Specify the port number to use (default: 5435)"
    echo "  -h, --help                         Show this help message"
    echo "  -f, --flake-url URL                Specify the flake URL to use (default: github:supabase/postgres)"
    echo "Description:"
    echo "  Runs 'dbmate up' against a locally running the version of database you specify. Or 'all' to run against all versions."
    echo "  NOTE: To create a migration, you must run 'nix develop' and then 'dbmate new <migration_name>' to create a new migration file."
    echo
    echo "Examples:"
    echo "  nix run .#dbmate-tool"
    echo "  nix run .#dbmate-tool -- --version 15"
    echo "  nix run .#dbmate-tool -- --version 16 --port 5433"
    echo "  nix run .#dbmate-tool -- --version 16 --port 5433 --flake-url github:supabase/postgres/<commithash>"
}

# Parse arguments
while [[ "$#" -gt 0 ]]; do
    case "$1" in
        -v|--version)
            if [[ -n "$2" && ! "$2" =~ ^- ]]; then
                PSQL_VERSION="$2"
                shift 2
            else
                echo "Error: --version requires an argument (15, 16, or orioledb-17)"
                exit 1
            fi
            ;;
        -u|--user)
            if [[ -n "$2" && ! "$2" =~ ^- ]]; then
                PGSQL_USER="$2"
                shift 2
            else
                echo "Error: --user requires an argument"
                exit 1
            fi
            ;;
        -f|--flake-url)
            if [[ -n "$2" && ! "$2" =~ ^- ]]; then
                FLAKE_URL="$2"
                shift 2
            else
                echo "Error: --flake-url requires an argument"
                exit 1
            fi
            ;;
        -p|--port)
            if [[ -n "$2" && ! "$2" =~ ^- ]]; then
                PORTNO="$2"
                shift 2
            else
                echo "Error: --port requires an argument"
                exit 1
            fi
            ;;
        -h|--help)
            print_help
            exit 0
            ;;
        *)
            echo "Unknown option: $1"
            print_help
            exit 1
            ;;
    esac
done

# Function to wait for PostgreSQL to be ready
wait_for_postgres() {
    local max_attempts=30  # Increased significantly
    local attempt=1
    
    # Give PostgreSQL a moment to actually start the process
    sleep 2
    
    while [ $attempt -le $max_attempts ]; do
        "${PSQLBIN}/pg_isready" -h localhost -p "$PORTNO" -U "$PGSQL_SUPERUSER" -d postgres
        local status=$?
        
        if [ $status -eq 0 ]; then
            echo "PostgreSQL is ready!"
            return 0
        fi
        echo "Waiting for PostgreSQL to start (attempt $attempt/$max_attempts)..."
        sleep 2
        attempt=$((attempt + 1))
    done
    
    echo "PostgreSQL failed to start after $max_attempts attempts"
    return 1
}

check_orioledb_ready() {
    local max_attempts=30
    local attempt=1
    
    while [ $attempt -le $max_attempts ]; do
        if "${PSQLBIN}/psql" -v ON_ERROR_STOP=1 -U "$PGSQL_SUPERUSER" -p "$PORTNO" -h localhost -d postgres -c "SELECT * FROM pg_am WHERE amname = 'orioledb'" | grep -q orioledb; then
            echo "Orioledb extension is ready!"
            return 0
        fi
        echo "Waiting for orioledb to be ready (attempt $attempt/$max_attempts)..."
        sleep 2
        attempt=$((attempt + 1))
    done
    
    echo "Orioledb failed to initialize after $max_attempts attempts"
    return 1
}

trim_schema() {
    case "$CURRENT_SYSTEM" in
    "x86_64-darwin"|"aarch64-darwin")
        sed -i '' '/INSERT INTO public.schema_migrations/,$d' "./db/schema.sql"
        echo "Matched: $CURRENT_SYSTEM"
        ;;
    *)
        sed -i '/INSERT INTO public.schema_migrations/,$d' "./db/schema.sql"
        ;;
    esac
}

perform_dump() {
    local max_attempts=3
    local attempt=1
    
    while [ $attempt -le $max_attempts ]; do
        echo "Attempting dbmate dump (attempt $attempt/$max_attempts)"
        
        if dbmate dump; then
            return 0
        fi
        
        echo "Dump attempt $attempt failed, waiting before retry..."
        sleep 5
        attempt=$((attempt + 1))
    done
    
    echo "All dump attempts failed"
    return 1
}

migrate_version() {
    echo "PSQL_VERSION: $PSQL_VERSION"
    #pkill -f "postgres" || true  # Ensure PostgreSQL is stopped before starting
    PSQLBIN=$(nix build --no-link "$FLAKE_URL#psql_$PSQL_VERSION/bin" --json | jq -r '.[].outputs.out + "/bin"')
    echo "Using PostgreSQL version $PSQL_VERSION from $PSQLBIN"
    
    # Start PostgreSQL
    start_postgres
    echo "Waiting for PostgreSQL to be ready..."

    # Wait for PostgreSQL to be ready to accept connections
    if ! wait_for_postgres; then
        echo "Failed to connect to PostgreSQL server"
        exit 1
    fi
    
    if [ "$PSQL_VERSION" = "orioledb-17" ]; then
        if ! check_orioledb_ready; then
            echo "Failed to initialize orioledb extension"
            exit 1
        fi
    fi

    echo "PostgreSQL server is ready"

    # Configure PostgreSQL roles and permissions
    if ! "${PSQLBIN}/psql" -v ON_ERROR_STOP=1 --no-password --no-psqlrc -U "$PGSQL_SUPERUSER" -p "$PORTNO" -h localhost -d postgres <<-EOSQL
create role postgres superuser login password '$PGPASSWORD';
alter database postgres owner to postgres;
EOSQL
    then
        echo "Failed to configure PostgreSQL roles and permissions"
        exit 1
    fi
    "${PSQLBIN}/psql" -v ON_ERROR_STOP=1 --no-password --no-psqlrc -U postgres -p "$PORTNO" -h localhost -d postgres -f "$PGBOUNCER_AUTH_SCHEMA_SQL"
    "${PSQLBIN}/psql" -v ON_ERROR_STOP=1 --no-password --no-psqlrc -U postgres -p "$PORTNO" -h localhost -d postgres -f "$STAT_EXTENSION_SQL"

    # Set db url to run dbmate
    export DATABASE_URL="postgres://$PGSQL_USER:$PGPASSWORD@localhost:$PORTNO/postgres?sslmode=disable"
    # Export path so dbmate can find correct psql and pg_dump
    export PATH="$PSQLBIN:$PATH"
    # Run init scripts
    if ! dbmate --migrations-dir "$MIGRATIONS_DIR/init-scripts" up; then
        echo "Error: Initial migration failed"
        exit 1
    fi

    # Password update command
    if ! "${PSQLBIN}/psql" -v ON_ERROR_STOP=1 --no-password --no-psqlrc -U postgres -p "$PORTNO" -h localhost -c "ALTER USER supabase_admin WITH PASSWORD '$PGPASSWORD'"; then
        echo "Error: Failed to update supabase_admin password"
        exit 1
    fi

    # Set up database URL
    export DATABASE_URL="postgres://$PGSQL_SUPERUSER:$PGPASSWORD@localhost:$PORTNO/postgres?sslmode=disable"
    # Run migrations
    if ! dbmate --migrations-dir "$MIGRATIONS_DIR/migrations" up; then
        echo "Error: Final migration failed"
        exit 1
    fi

    echo "Running dbmate dump with $PSQLBIN"
    perform_dump

    echo "CURRENT_SYSTEM: $CURRENT_SYSTEM"
    if [ -f "./db/schema.sql" ]; then
        trim_schema
        cp "./db/schema.sql" "./migrations/schema-$PSQL_VERSION.sql"
        echo "Schema file moved to ./migrations/schema-$PSQL_VERSION.sql"
        echo "PSQLBIN is $PSQLBIN"
    else
        echo "Warning: schema.sql file not found in ./db directory"
        exit 1
    fi

    # If we get here, all commands succeeded
    echo "PostgreSQL migration completed successfully"
    echo "Check migrations are idempotent"
    for sql in ./migrations/db/migrations/*.sql; do
        echo "$0: running $sql"
        "${PSQLBIN}/psql" -v ON_ERROR_STOP=1 --no-password --no-psqlrc -U "$PGSQL_SUPERUSER" -p "$PORTNO" -h localhost -d postgres -f "$sql" || {
            echo "Failed to execute $sql"
            exit 1
        }
    done
}

if [ "$PSQL_VERSION" == "all" ]; then
    VERSIONS=$(yq '.postgres_major[]' "$ANSIBLE_VARS" | tr -d '"')
    echo "$VERSIONS" | while read -r version; do
        PSQL_VERSION="$version"
        echo "Migrating to PostgreSQL version $PSQL_VERSION"
        migrate_version
        cleanup
    done
else
    echo "Migrating to PostgreSQL version $PSQL_VERSION"
    migrate_version
    cleanup
fi
