#!/usr/bin/env bash
# Legacy of Naxxramas — client patch updater
# Run this from inside your WoW folder (same folder as Wow.exe / Data/).
# Fetches the current patch files from the server and only downloads what
# actually changed, verifying each download against a checksum before
# replacing anything.
set -euo pipefail

BASE_URL="https://wdgsarge.com/downloads/patch"
MANIFEST_URL="$BASE_URL/manifest.txt"
SELF_NAME="update-patch.sh"
SELF_URL="$BASE_URL/$SELF_NAME"

# Where a manifest filename goes, relative to this script's folder.
# Pattern-based instead of a hardcoded list -- any new patch-N.MPQ (or
# patch-enUS-N.MPQ) the server starts publishing is picked up automatically,
# no need to update this script every time a new patch ships. A stale local
# copy of this script used to silently skip new patches because they weren't
# in a hardcoded map here; that bug class is now gone.
resolve_dest() {
  local fname="$1"
  if [[ "$fname" =~ ^patch-enUS-[0-9]+\.MPQ$ ]]; then
    echo "Data/enUS/$fname"
  elif [[ "$fname" =~ ^patch-[0-9]+\.MPQ$ ]]; then
    echo "Data/$fname"
  elif [[ "$fname" =~ ^patch-[A-Z]\.MPQ$ ]]; then
    # WotLK 3.3.5a only recognizes patch-1.MPQ..patch-9.MPQ, then patch-A.MPQ..patch-Z.MPQ --
    # there is no double-digit "patch-10.MPQ" the client actually looks for (confirmed
    # 2026-07-16 after patch-10 through patch-16 silently never loaded in any client for
    # this whole project). This branch handles the letter-named patches going forward.
    echo "Data/$fname"
  elif [ "$fname" = "realmlist.wtf" ]; then
    echo "Data/enUS/realmlist.wtf"
  else
    echo ""
  fi
}

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SELF_PATH="$SCRIPT_DIR/$(basename "${BASH_SOURCE[0]}")"
cd "$SCRIPT_DIR"

echo "Legacy of Naxxramas — checking for patch updates..."
echo "  Working folder: $SCRIPT_DIR"

if ! command -v curl >/dev/null 2>&1; then
  echo "ERROR: curl is required but not found on this system." >&2
  exit 1
fi

MANIFEST_TMP="$(mktemp)"
trap 'rm -f "$MANIFEST_TMP"' EXIT

if ! curl -fsSL "$MANIFEST_URL" -o "$MANIFEST_TMP"; then
  echo "ERROR: could not reach $MANIFEST_URL (check your internet connection)." >&2
  exit 1
fi

# Self-update: the manifest lists this script's own expected checksum just like
# any patch file. A stale local copy of this script is exactly what caused the
# "no destination mapping" bug for patch-A -- fixing resolve_dest() on the
# server doesn't help a player still running an old local copy. This check
# means players never need to manually re-download the updater again; it heals
# itself on the next run. Same verify-before-apply discipline as every patch
# file below: only replace this script with a downloaded copy whose checksum
# matches what the manifest says it should be.
self_expected_md5="$(awk -v f="$SELF_NAME" '$2==f{print $1}' "$MANIFEST_TMP")"
if [ -n "$self_expected_md5" ]; then
  self_local_md5="$(md5sum "$SELF_PATH" | awk '{print $1}')"
  if [ "$self_local_md5" != "$self_expected_md5" ]; then
    self_tmp="$(mktemp)"
    if curl -fsSL "$SELF_URL" -o "$self_tmp" 2>/dev/null; then
      self_downloaded_md5="$(md5sum "$self_tmp" | awk '{print $1}')"
      if [ "$self_downloaded_md5" = "$self_expected_md5" ]; then
        echo "Updater script itself is out of date -- updating and re-running..."
        cp "$self_tmp" "$SELF_PATH"
        chmod +x "$SELF_PATH"
        rm -f "$self_tmp"
        exec "$SELF_PATH" "$@"
      else
        echo "  ! update-patch.sh: downloaded copy didn't match manifest checksum, not applying (try again later)" >&2
      fi
    fi
    rm -f "$self_tmp" 2>/dev/null || true
  fi
fi

updated=0
skipped=0
failed=0

while read -r expected_md5 fname; do
  [ -z "${fname:-}" ] && continue
  [ "$fname" = "$SELF_NAME" ] && continue
  dest="$(resolve_dest "$fname")"
  if [ -z "$dest" ]; then
    echo "  ! $fname: no destination mapping, skipping (updater may be out of date)"
    continue
  fi

  if [ -f "$dest" ]; then
    local_md5="$(md5sum "$dest" | awk '{print $1}')"
    if [ "$local_md5" = "$expected_md5" ]; then
      echo "  = $fname: already up to date"
      skipped=$((skipped + 1))
      continue
    fi
  fi

  echo "  > $fname: downloading..."
  mkdir -p "$(dirname "$dest")"
  tmp_file="$(mktemp)"
  if ! curl -fsSL "$BASE_URL/$fname" -o "$tmp_file"; then
    echo "  ! $fname: download failed" >&2
    rm -f "$tmp_file"
    failed=$((failed + 1))
    continue
  fi

  downloaded_md5="$(md5sum "$tmp_file" | awk '{print $1}')"
  if [ "$downloaded_md5" != "$expected_md5" ]; then
    echo "  ! $fname: checksum mismatch after download, not applying (try again)" >&2
    rm -f "$tmp_file"
    failed=$((failed + 1))
    continue
  fi

  mv -f "$tmp_file" "$dest"
  echo "  + $fname: updated -> $dest"
  updated=$((updated + 1))
done < "$MANIFEST_TMP"

echo ""
echo "Done: $updated updated, $skipped already current, $failed failed."

if [ "$failed" -gt 0 ]; then
  exit_code=1
else
  exit_code=0
fi

if [ -t 0 ]; then
  read -rp "Press Enter to close..." _ || true
fi

exit $exit_code
