#!/usr/bin/env bash
# Restores one instance's database from a scripts/backup_db.sh dump.
#
#   scripts/restore_db.sh <compose-project-name> <path/to/dump.sql.gz> \
#       [--compose-file <file>] [--into <database>] [--yes]
#
# THIS OVERWRITES THE TARGET DATABASE. It prompts before doing so unless
# --yes is passed (for scripted disaster recovery).
#
# --into <database> restores somewhere OTHER than the live database, creating
# it if needed. That's how you rehearse a restore without an outage: point it
# at a scratch name, compare table and row counts against the live database,
# drop it. Do that at least once per instance — a backup nobody has restored
# is a backup nobody knows works — and again whenever the schema changes
# meaningfully.
#
#   scripts/restore_db.sh axioma_website dump.sql.gz --into axioma_restore_check --yes

set -euo pipefail

PROJECT="${1:?Usage: scripts/restore_db.sh <compose-project-name> <dump.sql.gz> [--compose-file <file>] [--yes]}"
DUMP="${2:?Usage: scripts/restore_db.sh <compose-project-name> <dump.sql.gz> [--compose-file <file>] [--yes]}"
shift 2

REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
COMPOSE_FILE="$REPO_ROOT/docker-compose.yml"
ASSUME_YES=0
TARGET_DB=""

while [ $# -gt 0 ]; do
    case "$1" in
        --compose-file)
            COMPOSE_FILE="$2"
            shift 2
            ;;
        --into)
            TARGET_DB="$2"
            shift 2
            ;;
        --yes)
            ASSUME_YES=1
            shift
            ;;
        *)
            echo "Unknown argument: $1" >&2
            exit 2
            ;;
    esac
done

[ -f "$DUMP" ] || { echo "No such dump: $DUMP" >&2; exit 1; }
gzip -t "$DUMP" 2>/dev/null || { echo "Not a valid gzip archive: $DUMP" >&2; exit 1; }

# shellcheck disable=SC1091
source "$REPO_ROOT/.env"

: "${DB_NAME:?DB_NAME must be set in .env}"
: "${MYSQL_ROOT_PASSWORD:?MYSQL_ROOT_PASSWORD must be set in .env}"

COMPOSE=(docker compose -f "$COMPOSE_FILE" -p "$PROJECT")

RESTORE_INTO="${TARGET_DB:-$DB_NAME}"

if [ "$RESTORE_INTO" = "$DB_NAME" ]; then
    echo "About to OVERWRITE the LIVE database '$DB_NAME' in project '$PROJECT'"
else
    echo "Rehearsal restore into scratch database '$RESTORE_INTO' (live '$DB_NAME' untouched)"
fi
echo "  from: $DUMP"
echo "  dated: $(date -r "$DUMP" '+%F %T' 2>/dev/null || echo unknown)"

if [ "$ASSUME_YES" -ne 1 ]; then
    printf "Type the target database name to confirm: "
    read -r CONFIRM
    if [ "$CONFIRM" != "$RESTORE_INTO" ]; then
        echo "Aborted."
        exit 1
    fi
fi

# Only for --into: the live database always already exists, and this script
# must never be the thing that creates it.
if [ "$RESTORE_INTO" != "$DB_NAME" ]; then
    "${COMPOSE[@]}" exec -T db sh -c \
        'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" -e "CREATE DATABASE IF NOT EXISTS \`'"$RESTORE_INTO"'\` CHARACTER SET utf8mb4"'
fi

echo "[$(date '+%F %T')] Restoring..."

# mysqldump output already contains DROP TABLE IF EXISTS / CREATE TABLE for
# every table it dumped, so this replaces them in place. Tables created AFTER
# the dump was taken are not dropped — if you need a truly clean restore,
# recreate the schema first.
gunzip -c "$DUMP" | "${COMPOSE[@]}" exec -T db sh -c \
    'exec mysql -uroot -p"$MYSQL_ROOT_PASSWORD" --default-character-set=utf8mb4 "$0"' "$RESTORE_INTO"

echo "[$(date '+%F %T')] Restore into '$RESTORE_INTO' complete."
echo "Sanity-check before declaring victory, e.g.:"
echo "  ${COMPOSE[*]} exec -T db sh -c 'mysql -uroot -p\"\$MYSQL_ROOT_PASSWORD\" -e \"SELECT COUNT(*) FROM students; SELECT COUNT(*) FROM payments;\" $RESTORE_INTO'"

if [ "$RESTORE_INTO" != "$DB_NAME" ]; then
    echo "Drop the scratch database when you're done:"
    echo "  ${COMPOSE[*]} exec -T db sh -c 'mysql -uroot -p\"\$MYSQL_ROOT_PASSWORD\" -e \"DROP DATABASE \\\`$RESTORE_INTO\\\`\"'"
fi
