#!/usr/bin/env bash
#
# update-microreticulum.sh — download the latest microReticulum firmware release.
#
# Pulls the rnode_firmware_*.zip assets from the microReticulum_Firmware GitHub
# releases (github.com/attermann/microReticulum_Firmware). These zips use the
# stock RNode firmware layout the web flasher already understands, so they're
# stored as-is (no repackaging), verified against the sha256 hashes in the
# release's release.json asset, dropped in a version directory next to this
# script, and the latest/ symlinks are repointed.
#
# The release tag is coarse (e.g. "1.86"); the granular version comes from the
# release *name* (e.g. "1.86.4"), which is what the version directory is named.
#
# Usage:
#   ./update-microreticulum.sh                 # latest stable release
#   ./update-microreticulum.sh --pre           # newest release, incl. pre-releases
#   ./update-microreticulum.sh --tag 1.86      # a specific tag
#   ./update-microreticulum.sh --force         # re-download even if the version dir exists
#
# Env:
#   GITHUB_TOKEN   optional; raises the GitHub API rate limit if set.
#
set -euo pipefail

REPO="attermann/microReticulum_Firmware"
API="https://api.github.com/repos/${REPO}"

# Board firmware zips the web flasher offers for microReticulum. Each is
# downloaded as-is from the release (name is both the asset and the stored
# file). Keep this in sync with the MR+'...' references in htdocs/index.html.
BOARDS=(
  "rnode_firmware_heltec32v2.zip"
  "rnode_firmware_heltec32v3.zip"
  "rnode_firmware_heltec32v4pa.zip"
  "rnode_firmware_lora32v10.zip"
  "rnode_firmware_lora32v20.zip"
  "rnode_firmware_lora32v21.zip"
  "rnode_firmware_ng20.zip"
  "rnode_firmware_ng21.zip"
  "rnode_firmware_rak4631.zip"
  "rnode_firmware_t3s3.zip"
  "rnode_firmware_t3s3_sx127x.zip"
  "rnode_firmware_t3s3_sx1280_pa.zip"
  "rnode_firmware_tbeam.zip"
  "rnode_firmware_tbeam_supreme.zip"
  "rnode_firmware_tbeam_sx1262.zip"
  "rnode_firmware_tdeck.zip"
  "rnode_firmware_xiao_esp32s3.zip"
)

# Root = directory this script lives in.
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"

TAG=""
INCLUDE_PRE=0
FORCE=0

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

# --- args -------------------------------------------------------------------
while [[ $# -gt 0 ]]; do
  case "$1" in
    --tag)   TAG="${2:?--tag needs a value}"; shift 2 ;;
    --pre)   INCLUDE_PRE=1; shift ;;
    --force) FORCE=1; shift ;;
    -h|--help)
      sed -n '2,23p' "${BASH_SOURCE[0]}" | sed 's/^# \?//'
      exit 0 ;;
    *) die "unknown argument: $1" ;;
  esac
done

for bin in curl jq sha256sum; do
  command -v "$bin" >/dev/null 2>&1 || die "'$bin' is required but not installed"
done

# --- curl helper (optional auth) -------------------------------------------
gh_curl() {
  local -a auth=()
  [[ -n "${GITHUB_TOKEN:-}" ]] && auth=(-H "Authorization: Bearer ${GITHUB_TOKEN}")
  curl -fsSL "${auth[@]}" -H "Accept: application/vnd.github+json" "$@"
}

# --- resolve the release ----------------------------------------------------
if [[ -n "$TAG" ]]; then
  log "Fetching release $TAG"
  RELEASE="$(gh_curl "${API}/releases/tags/${TAG}")" \
    || die "no release found for tag '$TAG'"
elif [[ "$INCLUDE_PRE" -eq 1 ]]; then
  log "Fetching newest release (pre-releases included)"
  RELEASE="$(gh_curl "${API}/releases?per_page=1" | jq -e '.[0]')" \
    || die "could not list releases"
else
  log "Fetching latest stable release"
  RELEASE="$(gh_curl "${API}/releases/latest")" \
    || die "could not fetch latest release"
fi

TAG="$(jq -er '.tag_name' <<<"$RELEASE")" || die "release has no tag_name"
# The granular version lives in the release name (e.g. "1.86.4"); the tag
# itself is coarse (e.g. "1.86"). Fall back to the tag if the name is blank.
VERSION="$(jq -r '.name // ""' <<<"$RELEASE")"
VERSION="${VERSION#v}"
[[ -n "$VERSION" ]] || VERSION="${TAG#v}"
[[ -n "$VERSION" ]] || die "empty version from release '$TAG'"

DEST="${ROOT}/${VERSION}"
log "Release $TAG (name: $VERSION) -> version directory $DEST"

if [[ -d "$DEST" && "$FORCE" -ne 1 ]]; then
  warn "$DEST already exists; use --force to re-download. Refreshing symlinks only."
else
  TMP="$(mktemp -d)"
  trap 'rm -rf "$TMP"' EXIT
  mkdir -p "$DEST"

  # release.json carries a sha256 hash per asset; use it to verify downloads.
  HASHES="{}"
  if hurl="$(jq -er '.assets[] | select(.name == "release.json") | .browser_download_url' <<<"$RELEASE")"; then
    log "Fetching release.json for hash verification"
    HASHES="$(curl -fsSL "$hurl")" || die "could not download release.json"
  else
    warn "release has no release.json asset; skipping hash verification"
  fi

  for asset in "${BOARDS[@]}"; do
    url="$(jq -er --arg n "$asset" \
      '.assets[] | select(.name == $n) | .browser_download_url' <<<"$RELEASE")" \
      || die "release $TAG has no asset named '$asset'"

    log "Downloading $asset"
    curl -fsSL -o "${TMP}/${asset}" "$url" || die "download failed: $url"

    want_hash="$(jq -r --arg n "$asset" '.[$n].hash // ""' <<<"$HASHES")"
    if [[ -n "$want_hash" ]]; then
      got_hash="$(sha256sum "${TMP}/${asset}" | cut -d' ' -f1)"
      [[ "$got_hash" == "$want_hash" ]] \
        || die "$asset sha256 mismatch (got $got_hash, expected $want_hash)"
    else
      warn "no hash for $asset in release.json; stored without verification"
    fi

    mv -f "${TMP}/${asset}" "${DEST}/${asset}"
  done

  # Record which release this directory came from.
  jq -n --arg tag "$TAG" --arg ver "$VERSION" \
        --arg name "$(jq -r '.name // .tag_name' <<<"$RELEASE")" \
        --arg pub "$(jq -r '.published_at' <<<"$RELEASE")" \
        --argjson pre "$(jq '.prerelease' <<<"$RELEASE")" \
        '{tag:$tag, version:$ver, name:$name, published_at:$pub, prerelease:$pre}' \
        > "${DEST}/release.json"
fi

# --- repoint latest/ symlinks ----------------------------------------------
LATEST="${ROOT}/latest"
mkdir -p "$LATEST"
for asset in "${BOARDS[@]}"; do
  [[ -f "${DEST}/${asset}" ]] || die "missing ${DEST}/${asset}; cannot link"
  ln -sfn "../${VERSION}/${asset}" "${LATEST}/${asset}"
  log "latest/${asset} -> ../${VERSION}/${asset}"
done

log "Done. microReticulum firmware at version ${VERSION}."
