#!/bin/bash
set -euo pipefail
umask 077
fail() { printf 'ERROR: %s\n' "$*" >&2; exit 1; }
ask() { local a; printf '%s [y/N] ' "$*" >/dev/tty; IFS= read -r a </dev/tty; case "$a" in y|Y|yes|YES) return 0;; *) return 1;; esac; }
if [ "${1:-}" = --help ] || [ "$#" -ne 2 ]; then
    echo 'Usage: /bin/bash restore-migration-databases.sh PACKAGE --plan|--apply|--rehearse'
    echo '--apply restores only into an initialized, stopped, empty PostgreSQL 14 cluster.'
    echo '--rehearse creates a separate temporary cluster on the backup volume; never touches production.'
    exit 0
fi
mode="$2"
case "$mode" in --plan|--apply|--rehearse) ;; *) fail 'Invalid mode';; esac
[ "$EUID" -ne 0 ] || fail 'Run as the normal macOS user.'
[ "$(id -un)" = kinsleypetit-homme ] || fail 'Use the kinsleypetit-homme account.'
package=$(cd "$1" && pwd -P)
db="$package/databases"
script_dir=$(cd "$(dirname "$0")" && pwd -P)
bin=/usr/local/opt/postgresql@14/bin
for name in appsmith.dump misper.dump postgres-globals.sql roles-fingerprint.txt appsmith-counts.sql misper-counts.sql SHA256SUMS; do
    [ -s "$db/$name" ] || fail "Missing database backup file: $name"
done
echo 'Verifying database backup checksums...'
(cd "$db" && /usr/bin/shasum -a 256 -c SHA256SUMS)
if [ "$mode" = --plan ]; then
    echo 'Plan: restore saved roles/password hashes/memberships, both databases with owners and grants, then compare role fingerprints and every table count.'
    exit 0
fi
[ -x "$bin/pg_ctl" ] || fail 'Install PostgreSQL 14 first.'
"$bin/pg_ctl" --version | /usr/bin/grep -q ' 14\.' || fail 'PostgreSQL 14 is required.'
[ -f "$script_dir/migration-role-fingerprint.sql" ] || fail 'Missing verification SQL.'
if [ "$mode" = --apply ]; then
    [ -t 0 ] || fail 'Apply requires an interactive Terminal.'
    data=/usr/local/var/postgresql@14
    [ -f "$data/PG_VERSION" ] && [ "$(cat "$data/PG_VERSION")" = 14 ] || fail 'Initialize PostgreSQL 14 before restoring.'
    [ ! -f "$data/postmaster.pid" ] || fail 'Target cluster is running, or has a stale PID file. Investigate before restore.'
    if /bin/launchctl print "gui/$UID/homebrew.mxcl.postgresql@14" >/dev/null 2>&1; then fail 'Unload the PostgreSQL LaunchAgent first.'; fi
    ask 'Restore roles and both databases into the empty local PostgreSQL 14 cluster?' || exit 1
    work=$(/usr/bin/mktemp -d "$HOME/megatron-db-restore.XXXXXX")
else
    work=$(/usr/bin/mktemp -d "$package/database-rehearsal.XXXXXX")
    data="$work/cluster"
    "$bin/initdb" -D "$data" --username=kinsleypetit-homme --encoding=UTF8 --locale=en_CA.UTF-8 --auth=trust >"$work/initdb.log" 2>&1
fi
# Short private socket directory avoids macOS UNIX socket path limits.
socket=$(/usr/bin/mktemp -d /tmp/megatron-db.XXXXXX)
started=no
cleanup() {
    local status=$?
    trap - EXIT
    if [ "$started" = yes ]; then
        if ! "$bin/pg_ctl" -D "$data" -m fast -w stop >>"$work/server-control.log" 2>&1; then
            echo "ERROR: could not stop restore cluster $data" >&2; status=1
        fi
    fi
    /bin/rmdir "$socket" 2>/dev/null || true
    if [ "$status" -ne 0 ]; then echo "Restore failed. Private logs: $work. Partial target data is retained; do not rerun against it blindly." >&2; fi
    exit "$status"
}
trap cleanup EXIT
"$bin/pg_ctl" -D "$data" -l "$work/server.log" -o "-c listen_addresses='' -c unix_socket_directories='$socket' -p 55439" -w start >"$work/server-control.log" 2>&1
started=yes
export PGHOST="$socket" PGPORT=55439 PGUSER=kinsleypetit-homme PGDATABASE=postgres
unset PGSERVICE PGOPTIONS PGPASSWORD PGHOSTADDR
psql=("$bin/psql" -X -qAt -v ON_ERROR_STOP=1 --no-password)
extra=$("${psql[@]}" -c "SELECT count(*) FROM pg_database WHERE NOT datistemplate AND datname <> 'postgres';")
[ "$extra" = 0 ] || fail 'Target contains databases. No existing database will be overwritten.'
extra=$("${psql[@]}" -c "SELECT count(*) FROM pg_roles WHERE rolname NOT LIKE 'pg_%' AND rolname <> 'kinsleypetit-homme';")
[ "$extra" = 0 ] || fail 'Target contains additional roles. Refusing to overwrite them.'
extra=$("${psql[@]}" -c "SELECT count(*) FROM pg_class c JOIN pg_namespace n ON n.oid=c.relnamespace WHERE n.nspname NOT IN ('pg_catalog','information_schema') AND n.nspname NOT LIKE 'pg_toast%' AND c.relkind IN ('r','p','v','m','S','f');")
[ "$extra" = 0 ] || fail 'Target postgres database contains user objects.'
# The fresh cluster's bootstrap role already exists. Keep every ALTER ROLE,
# password hash and membership statement; remove only that one CREATE ROLE.
count=$(/usr/bin/grep -c '^CREATE ROLE "kinsleypetit-homme";$' "$db/postgres-globals.sql")
[ "$count" = 1 ] || fail 'Unexpected globals format for the bootstrap role.'
/usr/bin/awk '$0 != "CREATE ROLE \"kinsleypetit-homme\";"' "$db/postgres-globals.sql" >"$work/globals-restore.sql"
echo 'Restoring roles, password hashes and memberships...'
"${psql[@]}" -f "$work/globals-restore.sql" >"$work/globals.log" 2>&1
for name in appsmith misper; do
    echo "Restoring $name (all schemas, owners, grants and sequences)..."
    "$bin/pg_restore" --exit-on-error --create --dbname=postgres "$db/$name.dump" >"$work/$name-restore.log" 2>&1
    echo "Checking exact table counts for $name..."
    "${psql[@]}" --dbname="$name" -f "$db/$name-counts.sql" >"$work/$name-counts.log" 2>&1
done
"${psql[@]}" -f "$script_dir/migration-role-fingerprint.sql" >"$work/roles-fingerprint.txt"
/usr/bin/cmp -s "$db/roles-fingerprint.txt" "$work/roles-fingerprint.txt" || fail 'Role attributes, password hashes or memberships differ.'
printf 'PASS: both databases restored; all recorded table counts and role/password/membership fingerprints match.\n' | /usr/bin/tee "$work/RESULT.txt"
echo "Private verification logs: $work"
echo 'The restore server will stop now. Production services and email jobs have not been enabled.'
