#!/bin/bash
# quetoo-update — update installed quetoo-client/quetoo-server and game data packages.
#
# Usage:
#   quetoo-update                  Update immediately and restart all instances.
#                                  The master server is restarted too, if configured.
#   quetoo-update --wait-for-empty Defer until all instances have no human clients.
#
# Recommended crontab (every 15 minutes, restart as soon as server empties):
#   */15 * * * * root /usr/lib/quetoo/bin/quetoo-update --wait-for-empty >> /var/log/quetoo-update.log 2>&1

set -euo pipefail

WAIT_FOR_EMPTY=0

while [[ $# -gt 0 ]]; do
  case "$1" in
    --wait-for-empty) WAIT_FOR_EMPTY=1; shift ;;
    *) echo "Usage: $0 [--wait-for-empty]" >&2; exit 1 ;;
  esac
done

case "$(uname -m)" in
  x86_64)  BUILD="x86_64-pc-linux" ;;
  aarch64) BUILD="aarch64-pc-linux" ;;
  *) echo "error: unsupported architecture: $(uname -m)" >&2; exit 1 ;;
esac

if command -v dpkg &>/dev/null && command -v apt &>/dev/null; then
  PKG_MANAGER=deb

  # A package removed (but not purged) while it still has conffiles lingers in
  # dpkg's database in "config-files" state; dpkg-query -W would still report
  # its old Version, so require the fully-installed status explicitly.
  deb_version() {
    { dpkg-query -W -f='${Status}\t${Version}\n' "$1" 2>/dev/null || true; } \
      | awk -F'\t' '{n = split($1, s, " "); if (s[n] == "installed") print $2}'
  }

  CLIENT_VERSION=$(deb_version quetoo-client)
  SERVER_VERSION=$(deb_version quetoo-server)
  DATA_VERSION=$(deb_version quetoo-data)
elif command -v rpm &>/dev/null && command -v dnf &>/dev/null; then
  PKG_MANAGER=rpm

  # rpm -q --queryformat writes "package X is not installed" to stdout (not
  # stderr) when the package is missing, so 2>/dev/null alone doesn't
  # suppress it; check installation with --quiet first.
  rpm_version() {
    rpm -q --quiet "$1" || return 0
    rpm -q --queryformat '%{VERSION}' "$1" 2>/dev/null
  }

  CLIENT_VERSION=$(rpm_version quetoo-client)
  SERVER_VERSION=$(rpm_version quetoo-server)
  DATA_VERSION=$(rpm_version quetoo-data)
else
  echo "error: no supported package manager found (apt or dnf required)" >&2
  exit 1
fi

if [[ -z "$CLIENT_VERSION" && -z "$SERVER_VERSION" ]]; then
  echo "error: neither quetoo-client nor quetoo-server is installed" >&2
  exit 1
fi

gh_latest() {
  curl -fsSL "https://api.github.com/repos/jdolan/$1/releases/latest" \
    | python3 -c "import sys,json; print(json.load(sys.stdin)['tag_name'].lstrip('v'))"
}

LATEST=$(gh_latest quetoo)
DATA_LATEST=$(gh_latest quetoo-data)

if [[ -z "$LATEST" ]]; then
  echo "error: could not determine latest quetoo release version" >&2
  exit 1
fi
if [[ -z "$DATA_LATEST" ]]; then
  echo "error: could not determine latest quetoo-data release version" >&2
  exit 1
fi

UPDATE_CLIENT=0
UPDATE_SERVER=0
NEED_UPDATE=0

if [[ -n "$CLIENT_VERSION" && "$CLIENT_VERSION" != "$LATEST" ]]; then
  echo "quetoo-client update available: $CLIENT_VERSION -> $LATEST"
  UPDATE_CLIENT=1
  NEED_UPDATE=1
fi
if [[ -n "$SERVER_VERSION" && "$SERVER_VERSION" != "$LATEST" ]]; then
  echo "quetoo-server update available: $SERVER_VERSION -> $LATEST"
  UPDATE_SERVER=1
  NEED_UPDATE=1
fi
if [[ "$DATA_VERSION" != "$DATA_LATEST" ]]; then
  echo "quetoo-data update available: $DATA_VERSION -> $DATA_LATEST"
  NEED_UPDATE=1
fi

if [[ $NEED_UPDATE -eq 0 ]]; then
  echo "quetoo-client ${CLIENT_VERSION:-not installed}, quetoo-server ${SERVER_VERSION:-not installed}, and quetoo-data $DATA_VERSION are already up to date."
  exit 0
fi

if [[ -n "$SERVER_VERSION" && $WAIT_FOR_EMPTY -eq 1 ]]; then
  for cfg in /etc/quetoo-dedicated/*.cfg; do
    instance=$(basename "$cfg" .cfg)
    port=$(grep -E '^set net_port[[:space:]]' "$cfg" 2>/dev/null | head -1 | awk '{print $3}' | tr -d '"' || true)
    port="${port:-1998}"

    humans=$(quetoo-status --num-humans "127.0.0.1:$port")
    if [[ "$humans" -gt 0 ]]; then
      echo "Instance '$instance' (port $port) has $humans human client(s). Deferring update."
      exit 0
    fi
  done
fi

RESTART_MASTER=0
INSTANCES=()
ENABLED_INSTANCES=()

if [[ -n "$SERVER_VERSION" ]]; then
  if systemctl is-active --quiet quetoo-master.service ||
     systemctl is-enabled --quiet quetoo-master.service; then
    RESTART_MASTER=1
  fi

  # quetoo-server's preremove stops and disables quetoo-dedicated@* on every
  # upgrade (dpkg/rpm run it as part of the upgrade transaction, not just on
  # removal), so capture which instances were enabled now and re-enable them
  # after install — otherwise they come back up today but silently fail to
  # start on the box's next reboot.
  while IFS= read -r unit; do
    ENABLED_INSTANCES+=("${unit}")
  done < <(
    systemctl list-unit-files --plain --no-legend 'quetoo-dedicated@*.service' \
      | awk '$1 ~ /^quetoo-dedicated@.+\.service$/ && $2 == "enabled" {print $1}'
  )

  while IFS= read -r unit; do
    INSTANCES+=("${unit}")
  done < <(
    {
      if [[ ${#ENABLED_INSTANCES[@]} -gt 0 ]]; then
        printf '%s\n' "${ENABLED_INSTANCES[@]}"
      fi
      systemctl list-units --all --plain --no-legend 'quetoo-dedicated@*.service' \
        | awk '$1 ~ /^quetoo-dedicated@.+\.service$/ {print $1}'
    } | sort -u
  )
fi

TMPDIR=$(mktemp -d -p /var/tmp)
trap 'rm -rf "$TMPDIR"' EXIT

QUETOO_BASE="https://github.com/jdolan/quetoo/releases/download/v${LATEST}"
DATA_BASE="https://github.com/jdolan/quetoo-data/releases/download/v${DATA_LATEST}"

echo "Downloading updates..."

PACKAGES=()

if [[ "$PKG_MANAGER" == "deb" ]]; then
  if [[ $UPDATE_CLIENT -eq 1 ]]; then
    curl -fsSL -o "$TMPDIR/quetoo-client.deb" "$QUETOO_BASE/quetoo-client-${BUILD}.deb"
    PACKAGES+=("$TMPDIR/quetoo-client.deb")
  fi
  if [[ $UPDATE_SERVER -eq 1 ]]; then
    curl -fsSL -o "$TMPDIR/quetoo-server.deb" "$QUETOO_BASE/quetoo-server-${BUILD}.deb"
    PACKAGES+=("$TMPDIR/quetoo-server.deb")
  fi
  if [[ "$DATA_VERSION" != "$DATA_LATEST" ]]; then
    curl -fsSL -o "$TMPDIR/quetoo-data.deb" "$DATA_BASE/quetoo-data_${DATA_LATEST}_all.deb"
    PACKAGES+=("$TMPDIR/quetoo-data.deb")
  fi
  apt install -y "${PACKAGES[@]}"
else
  if [[ $UPDATE_CLIENT -eq 1 ]]; then
    curl -fsSL -o "$TMPDIR/quetoo-client.rpm" "$QUETOO_BASE/quetoo-client-${BUILD}.rpm"
    PACKAGES+=("$TMPDIR/quetoo-client.rpm")
  fi
  if [[ $UPDATE_SERVER -eq 1 ]]; then
    curl -fsSL -o "$TMPDIR/quetoo-server.rpm" "$QUETOO_BASE/quetoo-server-${BUILD}.rpm"
    PACKAGES+=("$TMPDIR/quetoo-server.rpm")
  fi
  if [[ "$DATA_VERSION" != "$DATA_LATEST" ]]; then
    curl -fsSL -o "$TMPDIR/quetoo-data.rpm" "$DATA_BASE/quetoo-data-${DATA_LATEST}-1.noarch.rpm"
    PACKAGES+=("$TMPDIR/quetoo-data.rpm")
  fi
  dnf install -y "${PACKAGES[@]}"
fi

if [[ $RESTART_MASTER -eq 1 ]]; then
  echo "Restarting quetoo-master..."
  systemctl reset-failed quetoo-master.service 2>/dev/null || true
  systemctl restart quetoo-master.service || true
fi

for unit in "${INSTANCES[@]}"; do
  instance="${unit#quetoo-dedicated@}"
  instance="${instance%.service}"
  echo "Restarting quetoo-dedicated@${instance}..."
  systemctl reset-failed "quetoo-dedicated@${instance}" 2>/dev/null || true
  systemctl restart "quetoo-dedicated@${instance}" || true
done

for unit in "${ENABLED_INSTANCES[@]}"; do
  systemctl enable "${unit}" 2>/dev/null || true
done

echo "Quetoo updated to $LATEST (data: $DATA_LATEST)."
