#!/bin/sh
# HulyNet for testers, from https://4e6yphet.nothing.md/ (published there as install.sh by
# scripts/testers/server-setup.sh page): downloads the release for this computer, checks
# it, installs it with this server's configuration, which the archive carries, and, given
# the wave's invite code, registers the computer and joins its network (ADR 0022).
#
#   curl -fsSL https://4e6yphet.nothing.md/install.sh | sudo sh -s -- <invite code> [--name <name>]
#   curl -fsSL https://4e6yphet.nothing.md/install.sh | sh -s -- <invite code>   # a Mac without sudo:
#                                                                    # proxy mode
#   curl -fsSL https://4e6yphet.nothing.md/install.sh | sudo sh -s -- --uninstall [--purge]
#
# Without a code it only installs. Running it again is safe: an installed computer is
# upgraded, and a registered one gets its own network back.
#
# Everything is in a function that runs on the last line: `curl | sh` reads the script
# as it goes, and a cut-off download must not run half of it.
set -eu

main() {
  invite="" name="" pass=""
  while [ $# -gt 0 ]; do
    case $1 in
      --name)
        [ $# -ge 2 ] || die "--name needs a name"
        name=$2
        shift 2
        ;;
      --uninstall | --purge) pass="$pass $1" && shift ;;
      -*) die "unknown option $1" ;;
      *) invite=$1 && shift ;;
    esac
  done

  base=https://4e6yphet.nothing.md/downloads
  case $(uname -s) in
    Linux)
      case $(uname -m) in
        x86_64 | amd64) file=hulyscale-linux-x86_64.tar.gz ;;
        aarch64 | arm64) file=hulyscale-linux-aarch64.tar.gz ;;
        *) die "HulyNet runs on 64-bit PCs (x86_64 or ARM64), not on $(uname -m)" ;;
      esac
      [ "$(id -u)" = 0 ] || die "it needs administrator rights: curl -fsSL https://4e6yphet.nothing.md/install.sh | sudo sh -s -- $invite"
      mode="" cli=/usr/bin/hulyscale
      ;;
    Darwin)
      file=hulyscale-macos.tar.gz
      # With sudo, for the whole Mac; without, proxy mode for this user.
      if [ "$(id -u)" = 0 ]; then
        mode=--system cli=/usr/local/bin/hulyscale
      else
        mode="" cli=$HOME/.local/bin/hulyscale
      fi
      ;;
    *) die "HulyNet runs on Linux and macOS" ;;
  esac
  command -v curl >/dev/null 2>&1 || die "curl is missing"

  tmp=$(mktemp -d)
  trap 'rm -rf "$tmp"' EXIT
  echo "Downloading HulyNet ($file)..."
  curl -fL --progress-bar -o "$tmp/$file" "$base/$file" || die "the download failed: check the connection and try again"
  curl -fsSL -o "$tmp/SHA256SUMS" "$base/SHA256SUMS" || die "the download failed: check the connection and try again"
  expected=$(sed -n "s/^\([0-9a-f]*\)  $file\$/\1/p" "$tmp/SHA256SUMS")
  if command -v sha256sum >/dev/null 2>&1; then
    actual=$(sha256sum "$tmp/$file" | cut -d' ' -f1)
  else
    actual=$(shasum -a 256 "$tmp/$file" | cut -d' ' -f1)
  fi
  [ -n "$expected" ] && [ "$expected" = "$actual" ] || die "the download is damaged: try again"
  tar -C "$tmp" -xzf "$tmp/$file"
  if [ -n "$invite" ] && [ -z "$pass" ]; then
    # The installer need not ask the tester to send us their ID: we join below.
    HULYSCALE_JOINS=1
    export HULYSCALE_JOINS
  fi
  # shellcheck disable=SC2086 # $mode and $pass are words or nothing.
  "$tmp/hulyscale/install.sh" $mode $pass </dev/null
  if [ -n "$pass" ] || [ -z "$invite" ]; then
    return
  fi
  join
}

# Registers this computer with the invite code, and joins the network it gets.
join() {
  echo
  endpoint=""
  for _ in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
    endpoint=$("$cli" status 2>/dev/null | sed -n 's/^endpoint  *//p')
    [ -z "$endpoint" ] || break
    sleep 1
  done
  [ -n "$endpoint" ] || die "HulyNet did not start; please send us its log (see the page)"
  case $("$cli" status 2>/dev/null | sed -n 's/^network  *//p') in
    none* | "") ;;
    *)
      echo "This computer is in its network already: nothing more to do."
      return
      ;;
  esac
  echo "Registering this computer..."
  if [ -n "$name" ]; then
    status=$(curl -sS -o "$tmp/reply" -w '%{http_code}' --data-urlencode "invite=$invite" \
      --data-urlencode "endpoint=$endpoint" --data-urlencode "name=$name" \
      https://4e6yphet.nothing.md/register) || die "the server cannot be reached: try again"
  else
    status=$(curl -sS -o "$tmp/reply" -w '%{http_code}' --data-urlencode "invite=$invite" \
      --data-urlencode "endpoint=$endpoint" https://4e6yphet.nothing.md/register) ||
      die "the server cannot be reached: try again"
  fi
  [ "$status" = 200 ] || die "$(cat "$tmp/reply")"
  ticket=$(sed -n 's/^ticket //p' "$tmp/reply")
  joined=$(sed -n 's/^name //p' "$tmp/reply")
  # A new exit can take a few seconds to be found.
  for attempt in 1 2 3 4; do
    if "$cli" join "$ticket" </dev/null >"$tmp/join" 2>&1; then
      break
    fi
    [ "$attempt" -lt 4 ] || die "joining the network failed: $(cat "$tmp/join"); run the same line again"
    echo "Waiting for the exit..."
    sleep 5
  done
  echo
  echo "Done: this computer is \"$joined\" in its own network, and its traffic goes through"
  echo "the exit. To check: curl https://ifconfig.me; echo"
}

die() {
  echo "HulyNet: $*" >&2
  exit 1
}

main "$@"
