#!/usr/bin/env bash
#
# Scratch databases for the legacy Drupal re-import.
#
# Three databases, three roles:
#
#     drupal_src   the legacy Drupal dump — read-only source, the importers only SELECT from it
#     r24_prod     a pristine copy of the production dump — never written, the restore point
#     <app db>     the application's own database (DB_NAME in env.php) — the working copy
#
# The importers run against the application database so ActiveRecord, the caches and the search index all
# behave exactly as they will on production. `reset` puts that database back to the production copy, which
# is what makes an idempotence check ("run it twice, the second run is a no-op") cheap.
#
# Usage:
#
#     bin/import-db drupal [dump.sql]    load the Drupal dump into drupal_src
#     bin/import-db prod   [dump.sql]    load the production dump into r24_prod
#     bin/import-db reset  [--force]     restore the application database from r24_prod
#     bin/import-db restore-dev          put back what reset last overwrote
#     bin/import-db status               table and row counts of all three
#
# Default dump paths are data/import/dumps/drupal.sql and data/import/dumps/prod.sql.
#
# Overridable from the environment: DB_CONTAINER, DB_ROOT_USER, DB_ROOT_PASSWORD, APP_DB, APP_DB_USER,
# DRUPAL_DB, PROD_DB.

set -uo pipefail

cd "$( dirname "${BASH_SOURCE[0]}" )/.." || exit 1

DB_CONTAINER=${DB_CONTAINER:-raktar24-mariadb}
DB_ROOT_USER=${DB_ROOT_USER:-root}
DB_ROOT_PASSWORD=${DB_ROOT_PASSWORD:-raktar24}
APP_DB=${APP_DB:-raktar24}
APP_DB_USER=${APP_DB_USER:-raktar24}
DRUPAL_DB=${DRUPAL_DB:-drupal_src}
PROD_DB=${PROD_DB:-r24_prod}

DUMP_DIR=data/import/dumps

# Run a statement as root. Warnings on stderr are mysql's password notice, not ours.
mysql_root() {
	docker exec -i "$DB_CONTAINER" mysql -u"$DB_ROOT_USER" -p"$DB_ROOT_PASSWORD" "$@" 2> >( grep -v 'Using a password' >&2 )
}

die() {
	echo "$1" >&2
	exit 1
}

require_container() {
	docker exec "$DB_CONTAINER" true > /dev/null 2>&1 ||
		die "Container '$DB_CONTAINER' is not running. Start it with: docker compose up -d"
}

# Create the database, empty it if it already exists, then stream the dump in.
# The dumps are phpMyAdmin exports with no CREATE DATABASE and no USE, so the target is ours to choose.
load_dump() {

	local database=$1 dump=$2 label=$3

	[ -f "$dump" ] || die "Dump not found: $dump"

	local size
	size=$( du -h "$dump" | cut -f1 )
	echo "Loading the $label dump into \`$database\` ($size)…"

	mysql_root -e "DROP DATABASE IF EXISTS \`$database\`; CREATE DATABASE \`$database\` CHARACTER SET utf8mb4;" ||
		die "Could not create \`$database\`."

	docker exec -i "$DB_CONTAINER" mysql -u"$DB_ROOT_USER" -p"$DB_ROOT_PASSWORD" "$database" < "$dump" \
		2> >( grep -v 'Using a password' >&2 ) ||
		die "Import into \`$database\` failed."

	echo "Loaded $( count_tables "$database" ) table(s) into \`$database\`."

}

count_tables() {
	mysql_root -N -B -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = '$1';"
}

# The importers read the Drupal data with cross-schema queries on the application connection, so the
# application user needs SELECT on it. Nothing more — the source is never written.
grant_drupal_read() {
	mysql_root -e "GRANT SELECT ON \`$DRUPAL_DB\`.* TO '$APP_DB_USER'@'%'; FLUSH PRIVILEGES;" ||
		die "Could not grant SELECT on \`$DRUPAL_DB\` to '$APP_DB_USER'."
	echo "Granted SELECT on \`$DRUPAL_DB\` to '$APP_DB_USER'."
}

command=${1:-}
shift 2> /dev/null

case "$command" in

	drupal)
		require_container
		load_dump "$DRUPAL_DB" "${1:-$DUMP_DIR/drupal.sql}" 'Drupal'
		grant_drupal_read
		;;

	prod)
		require_container
		load_dump "$PROD_DB" "${1:-$DUMP_DIR/prod.sql}" 'production'
		;;

	reset)
		require_container

		[ "$( count_tables "$PROD_DB" )" -gt 0 ] ||
			die "\`$PROD_DB\` is empty. Load the production dump first: bin/import-db prod"

		if [ "${1:-}" != '--force' ]; then
			echo "This REPLACES the application database \`$APP_DB\` with the copy in \`$PROD_DB\`."
			read -r -p 'Type the database name to confirm: ' answer
			[ "$answer" = "$APP_DB" ] || die 'Aborted.'
		fi

		# Always keep a way back to whatever was in the application database a moment ago.
		backup=$DUMP_DIR/app-before-reset.sql
		echo "Backing \`$APP_DB\` up to $backup…"
		docker exec "$DB_CONTAINER" mysqldump -u"$DB_ROOT_USER" -p"$DB_ROOT_PASSWORD" --routines --triggers "$APP_DB" \
			2> >( grep -v 'Using a password' >&2 ) > "$backup" ||
			die 'Backup failed — refusing to reset.'

		echo "Restoring \`$APP_DB\` from \`$PROD_DB\`…"

		mysql_root -e "DROP DATABASE IF EXISTS \`$APP_DB\`; CREATE DATABASE \`$APP_DB\` CHARACTER SET utf8mb4;" ||
			die "Could not recreate \`$APP_DB\`."

		docker exec "$DB_CONTAINER" bash -c \
			"mysqldump -u$DB_ROOT_USER -p$DB_ROOT_PASSWORD --routines --triggers $PROD_DB | mysql -u$DB_ROOT_USER -p$DB_ROOT_PASSWORD $APP_DB" \
			2> >( grep -v 'Using a password' >&2 ) ||
			die "Restore failed."

		echo "Restored $( count_tables "$APP_DB" ) table(s). Flush the cache: docker exec -ti raktar24-php-fpm ./yii deploy/flush-cache"
		;;

	restore-dev)
		require_container

		backup=$DUMP_DIR/app-before-reset.sql
		[ -s "$backup" ] || die "No backup at $backup — nothing to restore."

		echo "Restoring \`$APP_DB\` from $backup…"

		mysql_root -e "DROP DATABASE IF EXISTS \`$APP_DB\`; CREATE DATABASE \`$APP_DB\` CHARACTER SET utf8mb4;" ||
			die "Could not recreate \`$APP_DB\`."

		docker exec -i "$DB_CONTAINER" mysql -u"$DB_ROOT_USER" -p"$DB_ROOT_PASSWORD" "$APP_DB" < "$backup" \
			2> >( grep -v 'Using a password' >&2 ) ||
			die 'Restore failed.'

		echo "Restored $( count_tables "$APP_DB" ) table(s). Flush the cache: docker exec -ti raktar24-php-fpm ./yii deploy/flush-cache"
		;;

	status)
		require_container
		printf '%-14s %8s  %s\n' 'DATABASE' 'TABLES' 'ROLE'
		printf '%-14s %8s  %s\n' "$DRUPAL_DB" "$( count_tables "$DRUPAL_DB" )" 'legacy Drupal source (read-only)'
		printf '%-14s %8s  %s\n' "$PROD_DB" "$( count_tables "$PROD_DB" )" 'production copy (restore point)'
		printf '%-14s %8s  %s\n' "$APP_DB" "$( count_tables "$APP_DB" )" 'application database (working copy)'
		;;

	*)
		sed -n '3,26p' "$0" | sed 's/^# \{0,1\}//'
		exit 1
		;;

esac
