#!/bin/sh
set -eu

umask 077

confirmation_phrase="replace-bowli-production-database"

if [ "${RESTORE_CONFIRM:-}" != "$confirmation_phrase" ]; then
    echo "Database restoration was not confirmed." >&2
    echo "Run it with RESTORE_CONFIRM=$confirmation_phrase" >&2
    exit 1
fi

require_variable() {
    variable_name="$1"
    eval "variable_value=\${$variable_name:-}"

    if [ -z "$variable_value" ]; then
        echo "$variable_name is required." >&2
        exit 1
    fi
}

aws_cli() {
    endpoint="${BACKUP_S3_ENDPOINT:-${AWS_ENDPOINT:-}}"

    if [ -n "$endpoint" ]; then
        aws --endpoint-url "$endpoint" "$@"
    else
        aws "$@"
    fi
}

for variable in DB_DATABASE DB_HOST DB_PASSWORD DB_USERNAME
do
    require_variable "$variable"
done

case "$DB_DATABASE" in
    *[!A-Za-z0-9_-]*)
        echo "DB_DATABASE may only contain letters, numbers, underscores and hyphens for restoration." >&2
        exit 1
        ;;
esac

source_name="${1:-latest-s3}"
backup_directory="${BACKUP_DIRECTORY:-/backups}"
backup_prefix="${BACKUP_S3_PREFIX:-database-backups/postgres}"
backup_prefix="${backup_prefix#/}"
backup_prefix="${backup_prefix%/}"
temporary_directory="$(mktemp -d)"
restore_path=""

cleanup() {
    rm -rf "$temporary_directory"
}

trap cleanup EXIT INT TERM

download_s3_backup() {
    s3_uri="$1"
    file_name="${s3_uri##*/}"
    restore_path="$temporary_directory/$file_name"

    aws_cli s3 cp "$s3_uri" "$restore_path" --only-show-errors
    aws_cli s3 cp "$s3_uri.sha256" "$restore_path.sha256" --only-show-errors
}

case "$source_name" in
    latest-local)
        restore_path="$(find "$backup_directory" -maxdepth 1 -type f -name "*.dump" | sort | tail -n 1)"
        ;;
    latest-s3)
        require_variable AWS_ACCESS_KEY_ID
        require_variable AWS_DEFAULT_REGION
        require_variable AWS_SECRET_ACCESS_KEY
        require_variable BACKUP_S3_BUCKET

        s3_directory="s3://$BACKUP_S3_BUCKET"
        if [ -n "$backup_prefix" ]; then
            s3_directory="$s3_directory/$backup_prefix"
        fi

        latest_key="$(aws_cli s3 ls "$s3_directory/" --recursive \
            | awk '$4 ~ /\.dump$/ { print $4 }' \
            | sort \
            | tail -n 1)"

        if [ -n "$latest_key" ]; then
            download_s3_backup "s3://$BACKUP_S3_BUCKET/$latest_key"
        fi
        ;;
    s3://*)
        require_variable AWS_ACCESS_KEY_ID
        require_variable AWS_DEFAULT_REGION
        require_variable AWS_SECRET_ACCESS_KEY
        download_s3_backup "$source_name"
        ;;
    /*)
        restore_path="$source_name"
        ;;
    *)
        restore_path="$backup_directory/$source_name"
        ;;
esac

if [ -z "$restore_path" ] || [ ! -f "$restore_path" ]; then
    echo "Backup not found: $source_name" >&2
    exit 1
fi

if [ ! -f "$restore_path.sha256" ]; then
    echo "Checksum not found for: $restore_path" >&2
    exit 1
fi

(
    cd "$(dirname "$restore_path")"
    sha256sum -c "$(basename "$restore_path").sha256"
)
pg_restore --list "$restore_path" >/dev/null

if [ "$(dirname "$restore_path")" != "$temporary_directory" ]; then
    cp "$restore_path" "$temporary_directory/"
    cp "$restore_path.sha256" "$temporary_directory/"
    restore_path="$temporary_directory/$(basename "$restore_path")"
fi

export PGPASSWORD="$DB_PASSWORD"
export PGCONNECT_TIMEOUT="${PGCONNECT_TIMEOUT:-10}"

if [ "${RESTORE_SKIP_SAFETY_BACKUP:-false}" != "true" ]; then
    echo "Creating a safety backup before restoration."
    /usr/local/bin/backup-database pre-restore
fi

timestamp="$(date -u +"%Y%m%dT%H%M%SZ")"
database_prefix="$(printf "%s" "$DB_DATABASE" | cut -c1-28)"
restore_database="${database_prefix}_restore_${timestamp}_$$"
previous_database="${database_prefix}_before_${timestamp}_$$"

createdb \
    --host="$DB_HOST" \
    --port="${DB_PORT:-5432}" \
    --username="$DB_USERNAME" \
    --owner="$DB_USERNAME" \
    "$restore_database"

restore_failed=true
current_database_renamed=false

cleanup_restore_database() {
    if [ "$restore_failed" = "true" ]; then
        if [ "$current_database_renamed" = "true" ]; then
            psql \
                --host="$DB_HOST" \
                --port="${DB_PORT:-5432}" \
                --username="$DB_USERNAME" \
                --dbname=postgres \
                --command="ALTER DATABASE \"$previous_database\" RENAME TO \"$DB_DATABASE\";" \
                >/dev/null 2>&1 || true
        fi

        dropdb \
            --host="$DB_HOST" \
            --port="${DB_PORT:-5432}" \
            --username="$DB_USERNAME" \
            --force \
            --if-exists \
            "$restore_database" 2>/dev/null || true
    fi

    cleanup
}

trap cleanup_restore_database EXIT INT TERM

echo "Restoring and validating temporary database $restore_database."
pg_restore \
    --host="$DB_HOST" \
    --port="${DB_PORT:-5432}" \
    --username="$DB_USERNAME" \
    --dbname="$restore_database" \
    --exit-on-error \
    --no-owner \
    --no-acl \
    "$restore_path"
vacuumdb \
    --host="$DB_HOST" \
    --port="${DB_PORT:-5432}" \
    --username="$DB_USERNAME" \
    --dbname="$restore_database" \
    --analyze-in-stages

database_exists="$(psql \
    --host="$DB_HOST" \
    --port="${DB_PORT:-5432}" \
    --username="$DB_USERNAME" \
    --dbname=postgres \
    --tuples-only \
    --no-align \
    --command="SELECT 1 FROM pg_database WHERE datname = '$DB_DATABASE';")"

if [ "$database_exists" = "1" ]; then
    psql \
        --host="$DB_HOST" \
        --port="${DB_PORT:-5432}" \
        --username="$DB_USERNAME" \
        --dbname=postgres \
        --set=ON_ERROR_STOP=1 \
        --command="SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '$DB_DATABASE' AND pid <> pg_backend_pid();" \
        --command="ALTER DATABASE \"$DB_DATABASE\" RENAME TO \"$previous_database\";"
    current_database_renamed=true
fi

psql \
    --host="$DB_HOST" \
    --port="${DB_PORT:-5432}" \
    --username="$DB_USERNAME" \
    --dbname=postgres \
    --set=ON_ERROR_STOP=1 \
    --command="ALTER DATABASE \"$restore_database\" RENAME TO \"$DB_DATABASE\";"

restore_failed=false
trap cleanup EXIT INT TERM

echo "Database restoration completed successfully."
if [ "$database_exists" = "1" ]; then
    echo "Previous database kept for rollback as: $previous_database"
fi
