#!/usr/bin/env bash
#
# Migrate a legacy Moodle (SQL dump + moodledata/filedir) into the LOCAL
# dockerized Moodle (docker-compose services `moodle` + `moodle-mariadb`).
#
# This is a NATIVE Moodle "move to a new server" restore — NOT the retired
# `manage.py import_moodle` Django importer (that fed the home-grown REP LMS,
# which was replaced by real Moodle in the 2026-07 pivot). It:
#   1. loads the dump into the moodle-mariadb database (dropping the fresh
#      bitnami install first — prefix `mdl_` matches),
#   2. drops the legacy filedir blob store (+ the `secret/` encryption key) into
#      the moodle_moodledata volume,
#   3. runs admin/cli/upgrade.php (the legacy 5.0.1 DB upgrades to our 5.0.2),
#   4. re-applies theme_iilmp (the legacy theme `mb2nl` isn't installed here),
#   5. resets the primary admin password so you can log in, and purges caches.
#
# Config is read from .env.deploy (MOODLE_DUMP_FILE / MOODLE_FILEDIR_ARCHIVE)
# and .env (MOODLE_DB_* overrides), same as the rest of the tooling. Override any
# value on the command line, e.g.:
#   YES=1 MOODLE_RESET_ADMIN_PASSWORD='S3cret!' bash scripts/migrate_moodle_into_docker.sh
#
# Env knobs:
#   MOODLE_DUMP_FILE              (required) path to the .sql dump
#   MOODLE_MOODLEDATA_DIR         (optional) an already-extracted moodledata dir;
#                                 auto-detected as the sibling `moodledata/` of
#                                 the dump when present (avoids re-extracting 1GB)
#   MOODLE_FILEDIR_ARCHIVE        (optional) .tar.gz of moodledata, used only when
#                                 no extracted dir is found
#   MOODLE_DB_NAME/USER/PASSWORD/ROOT_PASSWORD   (default the compose values)
#   MOODLE_RESET_ADMIN_PASSWORD   (default Admin#12345) primary-admin password to set
#   SKIP_FILES=1                  DB-only (skip the ~1GB filedir copy)
#   YES=1                         skip the destructive-action confirmation
#
set -euo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$ROOT_DIR"

# --- Load config (non-fatal if a file is missing) --------------------------
set -a
[ -f "$ROOT_DIR/.env" ]        && source "$ROOT_DIR/.env"        || true
[ -f "$ROOT_DIR/.env.deploy" ] && source "$ROOT_DIR/.env.deploy" || true
set +a

COMPOSE="docker compose"
DB_NAME="${MOODLE_DB_NAME:-bitnami_moodle}"
DB_USER="${MOODLE_DB_USER:-bn_moodle}"
DB_ROOT_PW="${MOODLE_DB_ROOT_PASSWORD:-moodleroot}"
NEW_ADMIN_PW="${MOODLE_RESET_ADMIN_PASSWORD:-Admin#12345}"
MOODLE_CODE="/opt/bitnami/moodle"
MOODLEDATA="/bitnami/moodledata"

log()  { printf '\033[1;34m>>\033[0m %s\n' "$*"; }
ok()   { printf '\033[1;32m✓\033[0m %s\n'  "$*"; }
warn() { printf '\033[1;33m!!\033[0m %s\n' "$*" >&2; }
die()  { printf '\033[1;31merror:\033[0m %s\n' "$*" >&2; exit 1; }

# --- Validate inputs -------------------------------------------------------
: "${MOODLE_DUMP_FILE:?MOODLE_DUMP_FILE not set (put it in .env.deploy)}"
[ -f "$MOODLE_DUMP_FILE" ] || die "MOODLE_DUMP_FILE not found: $MOODLE_DUMP_FILE"

# Resolve the moodledata/filedir source: prefer an extracted dir, else a tarball.
MOODLEDATA_SRC=""
if [ "${SKIP_FILES:-0}" != "1" ]; then
  if [ -n "${MOODLE_MOODLEDATA_DIR:-}" ] && [ -d "$MOODLE_MOODLEDATA_DIR/filedir" ]; then
    MOODLEDATA_SRC="$MOODLE_MOODLEDATA_DIR"
  else
    # Auto-detect a sibling `moodledata/` next to the dump.
    _sib="$(dirname "$MOODLE_DUMP_FILE")/moodledata"
    if [ -d "$_sib/filedir" ]; then
      MOODLEDATA_SRC="$_sib"
    elif [ -n "${MOODLE_FILEDIR_ARCHIVE:-}" ] && [ -f "$MOODLE_FILEDIR_ARCHIVE" ]; then
      MOODLEDATA_SRC="__ARCHIVE__"
    else
      die "No moodledata source. Set MOODLE_MOODLEDATA_DIR (extracted), place a 'moodledata/' next to the dump, or set MOODLE_FILEDIR_ARCHIVE. (Or run with SKIP_FILES=1.)"
    fi
  fi
fi

# --- Ensure the containers are up ------------------------------------------
if ! $COMPOSE ps --status running --services 2>/dev/null | grep -qx moodle-mariadb; then
  log "Starting moodle-mariadb + moodle..."
  $COMPOSE up -d moodle-mariadb moodle
fi
log "Waiting for moodle-mariadb..."
for i in $(seq 1 60); do
  if $COMPOSE exec -T -e PW="$DB_ROOT_PW" moodle-mariadb sh -c 'mariadb -uroot -p"$PW" -e "SELECT 1" >/dev/null 2>&1'; then break; fi
  sleep 2
  [ "$i" = 60 ] && die "moodle-mariadb never became ready."
done
ok "Database reachable."

# --- Confirm (destructive) -------------------------------------------------
cat <<EOF

  This REPLACES the current dockerized Moodle with the legacy dump.
    dump      : $MOODLE_DUMP_FILE
    filedir   : ${MOODLEDATA_SRC:-<skipped>}
    target DB : $DB_NAME  (dropped & recreated)
    moodledata: $MOODLEDATA/filedir  (replaced)
  The fresh bitnami install (its admin + demo data) will be discarded.

EOF
if [ -z "${CI:-}" ] && [ -z "${YES:-}" ]; then
  printf "Proceed? [y/N] "
  read -r ans
  case "$ans" in y|Y|yes|YES) ;; *) echo "Aborted."; exit 1 ;; esac
fi

# --- 1. Load the SQL dump --------------------------------------------------
log "Dropping & recreating \`$DB_NAME\` and re-granting $DB_USER..."
printf 'DROP DATABASE IF EXISTS `%s`;\nCREATE DATABASE `%s` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;\nGRANT ALL PRIVILEGES ON `%s`.* TO `%s`@`%%`;\nFLUSH PRIVILEGES;\n' \
  "$DB_NAME" "$DB_NAME" "$DB_NAME" "$DB_USER" \
  | $COMPOSE exec -T -e PW="$DB_ROOT_PW" moodle-mariadb sh -c 'mariadb -uroot -p"$PW"'

log "Loading dump into \`$DB_NAME\` (~$(du -h "$MOODLE_DUMP_FILE" | cut -f1))... this takes a few minutes."
$COMPOSE exec -T -e PW="$DB_ROOT_PW" -e DB="$DB_NAME" moodle-mariadb \
  sh -c 'mariadb -uroot -p"$PW" "$DB"' < "$MOODLE_DUMP_FILE"
ok "Dump loaded."

# --- 2. Restore the filedir blob store (+ secret key) ----------------------
if [ "${SKIP_FILES:-0}" = "1" ]; then
  warn "SKIP_FILES=1 — leaving moodledata untouched (uploaded files will 404)."
else
  # Materialise an extracted moodledata dir if we were only given a tarball.
  _cleanup_extract=""
  if [ "$MOODLEDATA_SRC" = "__ARCHIVE__" ]; then
    log "Extracting $MOODLE_FILEDIR_ARCHIVE (one-off)..."
    _tmp="$(mktemp -d)"; _cleanup_extract="$_tmp"
    tar xzf "$MOODLE_FILEDIR_ARCHIVE" -C "$_tmp"
    MOODLEDATA_SRC="$(dirname "$(find "$_tmp" -maxdepth 3 -type d -name filedir | head -n1)")"
    [ -d "$MOODLEDATA_SRC/filedir" ] || die "No filedir/ inside $MOODLE_FILEDIR_ARCHIVE."
  fi

  log "Streaming filedir ($(du -sh "$MOODLEDATA_SRC/filedir" | cut -f1)) into the volume..."
  $COMPOSE exec -T moodle sh -c "rm -rf $MOODLEDATA/filedir"
  tar -C "$MOODLEDATA_SRC" -cf - filedir \
    | $COMPOSE exec -T moodle sh -c "tar -xf - -C $MOODLEDATA"

  # Preserve the site's sodium encryption key so any encrypted DB values decrypt.
  if [ -d "$MOODLEDATA_SRC/secret" ]; then
    log "Restoring moodledata/secret (encryption key)..."
    $COMPOSE exec -T moodle sh -c "rm -rf $MOODLEDATA/secret"
    tar -C "$MOODLEDATA_SRC" -cf - secret \
      | $COMPOSE exec -T moodle sh -c "tar -xf - -C $MOODLEDATA"
  fi

  log "Fixing moodledata ownership (daemon)..."
  $COMPOSE exec -T moodle chown -R daemon:daemon "$MOODLEDATA"
  [ -n "$_cleanup_extract" ] && rm -rf "$_cleanup_extract"
  ok "Filedir restored."
fi

# --- 3. Upgrade the DB to our Moodle version -------------------------------
# Clear any stray upgrade-lock left in the dump before invoking the upgrader.
printf "UPDATE \`mdl_config\` SET value='0' WHERE name IN ('upgraderunning','maintenance_enabled');\n" \
  | $COMPOSE exec -T -e PW="$DB_ROOT_PW" -e DB="$DB_NAME" moodle-mariadb sh -c 'mariadb -uroot -p"$PW" "$DB"' 2>/dev/null || true

log "Running Moodle upgrade (legacy 5.0.1 → our 5.0.2)..."
# Run as the web user (daemon), never root — root-owned cache dirs break the web
# process ("invaliddatarootpermissions"). See setup/moodle_bootstrap.md.
$COMPOSE exec -T -u daemon moodle php "$MOODLE_CODE/admin/cli/upgrade.php" --non-interactive
ok "Upgrade complete."

# --- 4. Re-apply theme_iilmp (legacy theme mb2nl is not installed here) -----
log "Setting theme to iilmp..."
$COMPOSE exec -T -u daemon moodle php "$MOODLE_CODE/admin/cli/cfg.php" --name=theme --set=iilmp

# Require login site-wide: this is an internal learning platform, not a public
# catalog — so logging out lands on the (branded) login page instead of an empty
# front page. Override with MOODLE_FORCELOGIN=0 to keep a public front page.
$COMPOSE exec -T -u daemon moodle php "$MOODLE_CODE/admin/cli/cfg.php" --name=forcelogin --set="${MOODLE_FORCELOGIN:-1}"

# Rename the site (course id 1): the navbar wordmark is the site *shortname*,
# which the legacy dump leaves as "REP". Rebrand to IILMP. Override with
# MOODLE_SITE_SHORTNAME / MOODLE_SITE_FULLNAME.
SITE_SHORTNAME="${MOODLE_SITE_SHORTNAME:-REP}"
SITE_FULLNAME="${MOODLE_SITE_FULLNAME:-REP}"
log "Rebranding the site name ($SITE_SHORTNAME)..."
$COMPOSE exec -T -u daemon -e SN="$SITE_SHORTNAME" -e FN="$SITE_FULLNAME" moodle php -r '
define("CLI_SCRIPT", true);
require("'"$MOODLE_CODE"'/config.php");
$DB->set_field("course", "shortname", getenv("SN"), ["id" => 1]);
$DB->set_field("course", "fullname", getenv("FN"), ["id" => 1]);
'

# Replace the legacy custom nav menu (old external Library/RIMS/HAECI links) with
# links back to the IILMP platform modules, so Moodle's top bar cross-navigates to
# the rest of the platform. Override the base with IILMP_BASE_URL for prod.
IILMP_BASE="${IILMP_BASE_URL:-http://localhost:8000}"
log "Wiring the Moodle nav to IILMP modules ($IILMP_BASE)..."
MENU="$(printf '%s\n' \
  "Repository|$IILMP_BASE/repository/" \
  "Funding|$IILMP_BASE/rims/grants/calls/" \
  "Marketplace|$IILMP_BASE/smehub/marketplace/hub/" \
  "Alumni|$IILMP_BASE/alumni/" \
  "MEL|$IILMP_BASE/mel/events/dashboard/")"
$COMPOSE exec -T -u daemon moodle php "$MOODLE_CODE/admin/cli/cfg.php" --name=custommenuitems --set="$MENU"

log "Purging caches..."
$COMPOSE exec -T -u daemon moodle php "$MOODLE_CODE/admin/cli/purge_caches.php"

# --- 5. Reset the primary admin password so you can log in -----------------
log "Resetting the primary site-admin password..."
ADMIN_USER="$($COMPOSE exec -T -u daemon -e NEWPASS="$NEW_ADMIN_PW" moodle php -r '
define("CLI_SCRIPT", true);
require("'"$MOODLE_CODE"'/config.php");
require_once($CFG->libdir."/moodlelib.php");
$admins = get_admins();
$admin = reset($admins);
if (!$admin) { fwrite(STDERR, "no site admin found\n"); exit(1); }
$DB->set_field("user", "auth", "manual", ["id" => $admin->id]);
$admin->auth = "manual";
update_internal_user_password($admin, getenv("NEWPASS"));
echo $admin->username;
' 2>/dev/null)" || die "Could not reset admin password."

# --- 6. Verify -------------------------------------------------------------
USERS="$($COMPOSE exec -T -e PW="$DB_ROOT_PW" -e DB="$DB_NAME" moodle-mariadb sh -c 'mariadb -uroot -p"$PW" "$DB" -N -e "SELECT COUNT(*) FROM mdl_user WHERE deleted=0"' 2>/dev/null | tr -d "[:space:]")"
COURSES="$($COMPOSE exec -T -e PW="$DB_ROOT_PW" -e DB="$DB_NAME" moodle-mariadb sh -c 'mariadb -uroot -p"$PW" "$DB" -N -e "SELECT COUNT(*)-1 FROM mdl_course"' 2>/dev/null | tr -d "[:space:]")"
HTTP="$(curl -s -o /dev/null -w '%{http_code}' http://localhost:${MOODLE_HOST_PORT:-8081}/login/index.php || echo '???')"

cat <<EOF

$(ok "Legacy Moodle migrated into the dockerized Moodle.")
    users    : ${USERS:-?}
    courses  : ${COURSES:-?}
    login page HTTP: $HTTP   (http://localhost:${MOODLE_HOST_PORT:-8081})
    admin login    : ${ADMIN_USER:-admin} / ${NEW_ADMIN_PW}

  Note: plugins present in the legacy site but not in this Moodle build (e.g.
  theme_mb2nl, local_obf) show as "missing from disk" in Site admin — harmless.
EOF
