#!/bin/bash
# The EU server from the Mac. The server's address is in deploy/eu.host (one line, its IPv4);
# the SSH key is ~/.ssh/zone4_eu (root on the server).
#   deploy/eu setup            set the server up (deploy/setup.sh), then push and game
#   deploy/eu push             send the source, build there, swap the logic in (players stay);
#                              a changed gateway restarts it, which drops everyone's connection
#   deploy/eu game             send the game files the server reads
#   deploy/eu invite <who>     a one-time sign-up link for a friend
#   deploy/eu invites          every invite, open or used
#   deploy/eu accounts [...]   z4-accounts on the server (no arguments: every account)
#   deploy/eu copy-account <local> <there>   this Mac's account's characters to a new account there
#   deploy/eu log [n]          the last n lines of the server log (default 100)
#   deploy/eu backups          copy the server's nightly database dumps to backups-eu/
#   deploy/eu status           the services, the open connections, disk and the last backup
#   deploy/eu ssh [command]    a shell on the server, as root
#   deploy/eu panel            the server's dev panel at http://127.0.0.1:39991 (through SSH; Ctrl-C ends it)
set -euo pipefail
ROOT=$(cd "$(dirname "$0")/.." && pwd)
# Z4_EU_HOST overrides it, and Z4_EU_SSH (host:port) reaches a machine whose SSH is elsewhere
# (a rehearsal VM behind a forwarded port).
HOST=${Z4_EU_HOST:-$(cat "$ROOT/deploy/eu.host" 2>/dev/null)} || true
[ -n "$HOST" ] || { echo "no deploy/eu.host: put the server's IPv4 in it"; exit 1; }
TARGET=${Z4_EU_SSH:-$HOST:22}
KEY=~/.ssh/zone4_eu
SSHO="ssh -i $KEY -p ${TARGET##*:} -o StrictHostKeyChecking=accept-new"
SSH=($SSHO "root@${TARGET%:*}")
RSYNC=(rsync -az -e "$SSHO")
R="root@${TARGET%:*}"
as_z4() { "${SSH[@]}" "sudo -u z4 -H bash -lc $(printf %q "set -a; . /srv/zone4/env; set +a; $1")"; }

push() {
  "${RSYNC[@]}" --delete --exclude target "$ROOT/crates" "$ROOT/Cargo.toml" "$ROOT/Cargo.lock" \
    "$ROOT/opcodes-589.json" "$R:/srv/zone4/src/"
  "${SSH[@]}" chown -R z4:z4 /srv/zone4/src
  echo " building on the server..."
  as_z4 'cd /srv/zone4/src && cargo build -q --release -p z4-gateway -p z4-logic'
  # New binaries go in beside the old and are renamed over them: a running process keeps its own.
  "${SSH[@]}" 'set -e; cd /srv/zone4; t=src/target/release
    gw_changed=$(cmp -s $t/z4-gateway bin/z4-gateway && echo no || echo yes)
    for b in z4-gateway z4-logic z4-accounts; do install -o z4 -g z4 -m 755 $t/$b bin/$b.new; mv bin/$b.new bin/$b; done
    if [ "$gw_changed" = yes ]; then systemctl restart zone4-gateway; echo " gateway restarted"; fi
    if systemctl is-active -q zone4-logic; then systemctl reload zone4-logic; else systemctl start zone4-logic; fi'
  sleep 3
  "${SSH[@]}" 'tail -n 3 /srv/zone4/logs/server.log'
}

game() {
  # The server reads the .dat tables, the stage files, the item models' and icons' presence and
  # the icons themselves (dev panel): the scenes, effects, animation and sound stay behind. Stage
  # files the dev panel changed (enemy HP) go as their originals, from .enemy-hp-backup.
  local G="$ROOT/game-589" B="$ROOT/game-589/.enemy-hp-backup" changed
  changed=$(cd "$B" 2>/dev/null && find . -type f ! -name mode | sed 's|^\.||') || true
  echo " sending the game files (the first time is about 4 GB)..."
  "${RSYNC[@]}" --delete \
    --exclude scene --exclude effect --exclude animation --exclude particle --exclude particle_THAI \
    --exclude sound --exclude source --exclude XIGNCODE --exclude Zone4Launcher.exe --exclude Shaders \
    --exclude '*.orig' --exclude Screenshots --exclude .enemy-hp-backup \
    --exclude-from=<(printf '%s\n' "$changed") \
    "$G/" "$R:/srv/zone4/game-589/"
  [ -z "$changed" ] || "${RSYNC[@]}" --exclude mode "$B/" "$R:/srv/zone4/game-589/"
  "${SSH[@]}" chown -R z4:z4 /srv/zone4/game-589
}

# A local account's characters (from this Mac's database) to a new account on the server, with a
# password typed here. Before anyone makes characters there: they keep their ids.
copy_account() {
  local from=${1:?usage: deploy/eu copy-account <local account> <account there>} to=${2:?usage: deploy/eu copy-account <local account> <account there>} pw pw2
  local PSQL=("$(brew --prefix postgresql@17)/bin/psql" -h "$ROOT/pgdata" -U zone4 -d zone4 -v ON_ERROR_STOP=1 -v from="$from" -v to="$to")
  [ "$("${PSQL[@]}" -At <<< "SELECT count(*) FROM accounts WHERE login = :'from'")" = 1 ] || { echo "no local account $from"; exit 1; }
  read -rs -p "password for $to: " pw; echo; read -rs -p "again: " pw2; echo
  [ -n "$pw" ] && [ "$pw" = "$pw2" ] || { echo "the passwords differ (or are empty)"; exit 1; }
  { echo 'BEGIN;'
    echo 'COPY accounts (login) FROM STDIN WITH CSV;'
    "${PSQL[@]}" -q <<< "COPY (SELECT :'to') TO STDOUT WITH CSV"
    echo '\.'
    echo 'COPY characters (id, account, slot, data) FROM STDIN WITH CSV;'
    "${PSQL[@]}" -q <<< "COPY (SELECT id, :'to', slot, data FROM characters WHERE account = :'from' ORDER BY slot) TO STDOUT WITH CSV"
    echo '\.'
    echo "SELECT setval('character_ids', GREATEST((SELECT max(id) FROM characters), (SELECT last_value FROM character_ids)));"
    echo 'COMMIT;'
  } | as_z4 'psql -q -v ON_ERROR_STOP=1 -h /var/run/postgresql zone4 > /dev/null'
  # z4-accounts salts and hashes it as the server keeps passwords.
  as_z4 "/srv/zone4/bin/z4-accounts password $(printf '%q %q' "$to" "$pw") > /dev/null && /srv/zone4/bin/z4-accounts chars $(printf %q "$to")"
}

case "${1:-}" in
  setup)
    "${SSH[@]}" "bash -s $HOST" < "$ROOT/deploy/setup.sh"
    game
    push ;;
  push) push ;;
  game) game ;;
  invite) shift; as_z4 "/srv/zone4/bin/z4-accounts invite $(printf '%q ' "$@")" ;;
  invites) as_z4 '/srv/zone4/bin/z4-accounts invites' ;;
  accounts) shift; as_z4 "/srv/zone4/bin/z4-accounts${*:+ $(printf '%q ' "$@")}" ;;
  log) "${SSH[@]}" "tail -n ${2:-100} /srv/zone4/logs/server.log" ;;
  backups) mkdir -p "$ROOT/backups-eu"; "${RSYNC[@]}" "$R:/srv/zone4/backups/" "$ROOT/backups-eu/"; ls -lh "$ROOT/backups-eu" | tail -5 ;;
  status)
    "${SSH[@]}" 'systemctl is-active zone4-gateway zone4-logic postgresql caddy | paste -sd" " -
      echo "game connections: $(ss -Htn state established "( sport >= :29992 and sport <= :29998 )" | wc -l)"
      df -h /srv | tail -1; ls -t /srv/zone4/backups | head -1' ;;
  ssh) shift; "${SSH[@]}" "$@" ;;
  copy-account) shift; copy_account "$@" ;;
  panel) echo " dev panel on http://127.0.0.1:39991 (Ctrl-C ends it)"; $SSHO -N -L 39991:127.0.0.1:29991 "$R" ;;
  *) sed -n '2,16p' "$0" | sed 's/^# \{0,1\}//'; exit 1 ;;
esac
