#!/bin/bash

# Function to log with timestamp
log() {
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1"
}

# Function to handle errors
handle_error() {
    local exit_code=$?
    local last_command=${BASH_COMMAND}
    log "ERROR: Command '$last_command' failed with exit code $exit_code."
    log "Exiting with error code: $exit_code"
    # Exit with the captured exit code
    exit $exit_code
}

# Set up error handling to call handle_error on ERR
# and also exit immediately if a command exits with a non-zero status.
set -e
trap 'handle_error' ERR

is_enabled() {
    case "${1:-true}" in
        [Ff][Aa][Ll][Ss][Ee]|0|[Nn][Oo]|[Oo][Ff][Ff])
            return 1
            ;;
        *)
            return 0
            ;;
    esac
}

get_admin_password() {
    local password=""

    if [ -f /run/secrets/postgres_password ]; then
        password=$(cat /run/secrets/postgres_password 2>/dev/null)
    fi

    if [ -z "$password" ] && [ -n "${DB_PASSWORD_ADMIN:-}" ]; then
        password="${DB_PASSWORD_ADMIN}"
    fi

    if [ -z "$password" ] && [ -n "${DB_PASSWORD_SUPERUSER:-}" ]; then
        password="${DB_PASSWORD_SUPERUSER}"
    fi

    echo "$password" | tr -d '[:space:]'
}

# Function to check if postgres is ready
wait_for_postgres() {
    log "Waiting for PostgreSQL to be ready..."
    # Use DB_HOST_ADMIN for direct postgres connections (admin operations)
    # Fall back to DB_HOST if not set, then to 'postgres'
    local PG_ADMIN_HOST=${DB_HOST_ADMIN:-${DB_HOST:-postgres}}
    local PG_ADMIN_PORT=${DB_PORT_ADMIN:-${DB_PORT:-5432}}
    # If host points at PgBouncer, prefer talking to Postgres directly for setup
    if [ "$PG_ADMIN_HOST" = "pgbouncer" ]; then
        log "Detected DB_HOST pointing to PgBouncer; using direct Postgres for admin ops"
        PG_ADMIN_HOST=postgres
        PG_ADMIN_PORT=5432
        export DB_HOST_ADMIN=$PG_ADMIN_HOST
        export DB_PORT_ADMIN=$PG_ADMIN_PORT
    fi
    local PG_PASSWORD
    PG_PASSWORD=$(get_admin_password)
    if [ -z "$PG_PASSWORD" ]; then
        log "ERROR: No admin database password available for wait_for_postgres"
        exit 1
    fi
    set +e  # Temporarily disable exit on error for the until loop
    until PGPASSWORD="${PG_PASSWORD}" psql -h ${PG_ADMIN_HOST} -p ${PG_ADMIN_PORT} -U postgres -c '\q' 2>/dev/null; do
        # Try direct Postgres as a smart fallback if PgBouncer is the target and not responding
        if [ "$PG_ADMIN_HOST" != "postgres" ] && PGPASSWORD="${PG_PASSWORD}" pg_isready -h postgres -p 5432 -U postgres >/dev/null 2>&1; then
            log "PgBouncer not ready but Postgres is reachable; switching admin host to Postgres"
            PG_ADMIN_HOST=postgres
            PG_ADMIN_PORT=5432
            export DB_HOST_ADMIN=$PG_ADMIN_HOST
            export DB_PORT_ADMIN=$PG_ADMIN_PORT
            continue
        fi
        log "PostgreSQL is unavailable - sleeping"
        sleep 1
    done
    set -e  # Re-enable exit on error
    log "PostgreSQL is up and running!"
}

# Function to check if seeds have been run
check_seeds_status() {
    local has_seeds
    # Use DB_HOST_ADMIN for direct postgres connections (admin operations)
    local PG_ADMIN_HOST=${DB_HOST_ADMIN:-${DB_HOST:-postgres}}
    local PG_ADMIN_PORT=${DB_PORT_ADMIN:-${DB_PORT:-5432}}
    local PG_PASSWORD
    PG_PASSWORD=$(get_admin_password)

    has_seeds=$(PGPASSWORD="${PG_PASSWORD}" psql -h ${PG_ADMIN_HOST} -p ${PG_ADMIN_PORT} -U postgres -d ${DB_NAME_SERVER:-server} -tAc "SELECT EXISTS (SELECT 1 FROM users LIMIT 1);" 2>/dev/null)
    if [ "$has_seeds" = "t" ]; then
        return 0  # Seeds have been run
    else
        return 1  # Seeds haven't been run
    fi
}

# Main setup process
main() {
    wait_for_postgres

    log "Creating database..."
    log "Running create_database.js with timeout protection..."
    timeout 120 node /app/server/setup/create_database.js || {
        local exit_code=$?
        if [ $exit_code -eq 124 ]; then
            log "ERROR: Database creation timed out after 120 seconds"
        else
            log "ERROR: Database creation failed with exit code $exit_code"
        fi
        exit 1
    }
    log "Database creation completed!"

    # Use DB_HOST_ADMIN for direct postgres connections (admin operations)
    # pgbouncer doesn't support certain admin commands
    local PG_ADMIN_HOST=${DB_HOST_ADMIN:-${DB_HOST:-postgres}}
    local PG_ADMIN_PORT=${DB_PORT_ADMIN:-${DB_PORT:-5432}}
    # Read and trim the postgres password (be extra careful with whitespace)
    local PG_PASSWORD
    PG_PASSWORD=$(get_admin_password)

    log "DEBUG: Connecting to ${PG_ADMIN_HOST}:${PG_ADMIN_PORT} with user postgres"
    log "DEBUG: Password available: $([ -n "$PG_PASSWORD" ] && echo 'yes' || echo 'no')"
    log "DEBUG: Password length: ${#PG_PASSWORD}"

    if is_enabled "${SETUP_RUN_MIGRATIONS:-true}"; then
        log "Creating pgboss schema..."
        # Add retry logic for pgboss schema creation in case of temporary connection issues
        local RETRY_COUNT=0
        local MAX_RETRIES=3
        local RETRY_DELAY=2

        until PGPASSWORD="${PG_PASSWORD}" psql -h ${PG_ADMIN_HOST} -p ${PG_ADMIN_PORT} -U postgres -d ${DB_NAME_SERVER:-server} -c 'CREATE SCHEMA IF NOT EXISTS pgboss;' 2>&1; do
            RETRY_COUNT=$((RETRY_COUNT + 1))
            if [ $RETRY_COUNT -ge $MAX_RETRIES ]; then
                log "ERROR: Failed to create pgboss schema after $MAX_RETRIES attempts"
                log "Testing connection with psql..."
                PGPASSWORD="${PG_PASSWORD}" psql -h ${PG_ADMIN_HOST} -p ${PG_ADMIN_PORT} -U postgres -d ${DB_NAME_SERVER:-server} -c 'SELECT version();' 2>&1 || log "Connection test failed"
                log "Checking if Postgres is still accessible..."
                pg_isready -h ${PG_ADMIN_HOST} -p ${PG_ADMIN_PORT} -U postgres || log "Postgres not ready"
                exit 1
            fi
            log "Retry $RETRY_COUNT/$MAX_RETRIES: pgboss schema creation failed, retrying in ${RETRY_DELAY}s..."
            sleep $RETRY_DELAY
        done
        log "pgboss schema created successfully"

        log "Granting necessary permissions..."
        PGPASSWORD="${PG_PASSWORD}" psql -h ${PG_ADMIN_HOST} -p ${PG_ADMIN_PORT} -U postgres -d ${DB_NAME_SERVER:-server} -c 'GRANT ALL ON SCHEMA public TO postgres;'

        log "Running migrations..."

        # For Enterprise Edition, we need to run migrations from a combined directory
        if [ "${EDITION}" = "enterprise" ] || [ "${EDITION}" = "ee" ]; then
            log "Setting up EE migrations..."

            # Create a combined migrations directory within /app/server
            mkdir -p /app/server/combined-migrations

            # Copy base migrations first (these should run first)
            log "Copying base migrations..."
            cp /app/server/migrations/*.cjs /app/server/combined-migrations/ 2>/dev/null || true

            # Copy EE migrations (these run after base migrations)
            log "Copying EE migrations..."
            cp /app/ee/server/migrations/*.cjs /app/server/combined-migrations/ 2>/dev/null || true

            # Create a temporary knexfile in /app/server where node_modules exist
            log "Creating temporary knexfile for EE..."
            cat > /app/server/knexfile-ee.cjs << 'EOF'
const fs = require('fs');
const path = require('path');

const DOCKER_SECRETS_PATH = '/run/secrets';
const SECRETS_PATH = DOCKER_SECRETS_PATH;

function getSecret(secretName, envVar, defaultValue = '') {
  const secretPath = path.join(SECRETS_PATH, secretName);
  try {
    return fs.readFileSync(secretPath, 'utf8').trim();
  } catch (error) {
    if (process.env[envVar]) {
      return process.env[envVar] || defaultValue;
    }
    return defaultValue;
  }
}

module.exports = {
  migration: {
    client: 'pg',
    connection: {
      host: process.env.DB_HOST || 'postgres',
      port: process.env.DB_PORT || '5432',
      user: process.env.DB_USER_ADMIN || 'postgres',
      password: getSecret('postgres_password', 'DB_PASSWORD_ADMIN'),
      database: process.env.DB_NAME_SERVER || 'server',
    },
    pool: {
      min: 2,
      max: 20,
    },
    migrations: {
      directory: './combined-migrations'
    }
  }
};
EOF
        
            log "Running combined migrations for EE..."
            log "Current directory: $(pwd)"
            log "Migration directory contents:"
            ls -la /app/server/combined-migrations/ || log "Could not list migration directory"

            cd /app/server && NODE_ENV=migration timeout 300 npx knex migrate:latest --knexfile knexfile-ee.cjs --verbose || {
                local exit_code=$?
                if [ $exit_code -eq 124 ]; then
                    log "ERROR: EE migrations timed out after 300 seconds"
                else
                    log "ERROR: EE migrations failed with exit code $exit_code"
                fi
                exit 1
            }
            log "EE migrations completed!"

            # Clean up
            rm -rf /app/server/combined-migrations
            rm -f /app/server/knexfile-ee.cjs
        else
            # For CE, just run the base migrations
            log "Running base migrations for CE..."
            log "Current directory: $(pwd)"
            log "Migration directory: /app/server/migrations"
            ls -la /app/server/migrations/ || log "Could not list migration directory"

            NODE_ENV=migration timeout 300 npx knex migrate:latest --knexfile /app/server/knexfile.cjs --verbose || {
                local exit_code=$?
                if [ $exit_code -eq 124 ]; then
                    log "ERROR: Migrations timed out after 300 seconds"
                else
                    log "ERROR: Migrations failed with exit code $exit_code"
                fi
                exit 1
            }
            log "Migrations completed!"
        fi
    else
        log "SETUP_RUN_MIGRATIONS is disabled; skipping pgboss schema creation and migrations."
    fi

    # Check if seeds need to be run
    if is_enabled "${SETUP_RUN_SEEDS:-true}"; then
        if ! check_seeds_status; then
            log "Running seeds..."
            log "Seed directory: /app/server/seeds"
            ls -la /app/server/seeds/ || log "Could not list seed directory"

            NODE_ENV=migration timeout 300 npx knex seed:run --knexfile /app/server/knexfile.cjs --verbose || {
                local exit_code=$?
                if [ $exit_code -eq 124 ]; then
                    log "ERROR: Seeds timed out after 300 seconds"
                else
                    log "ERROR: Seeds failed with exit code $exit_code"
                fi
                exit $exit_code
            }
            log "Seeds completed!"
        else
            log "Seeds have already been run, skipping..."
        fi
    else
        log "SETUP_RUN_SEEDS is disabled; skipping seeds."
    fi

    log "Setup completed!"
    log "Exiting with success code: 0"
    exit 0
}

# Execute main function
main
