#!/bin/sh

set -eu

release_base_url=${AUTOLITH_RELEASE_BASE_URL:-https://sh.lambda-symbolics.com/releases}
release_latest_url=${AUTOLITH_RELEASE_LATEST_URL:-$release_base_url/latest}
home=${HOME:-}
data_home=${XDG_DATA_HOME:-$home/.local/share}
install_root=${AUTOLITH_INSTALL_ROOT:-$data_home/autolith/installation}
bin_directory=${AUTOLITH_BIN_DIR:-$home/.local/bin}
requested_tag=
temporary_root=
publish_command_p=true

fail()
{
  printf 'Autolith installation failed: %s\n' "$1" >&2
  exit 1
}

cleanup()
{
  if [ -n "$temporary_root" ] && [ -d "$temporary_root" ]; then
    chmod -R u+w "$temporary_root" 2>/dev/null || true
    rm -rf -- "$temporary_root"
  fi
}

release_tag_valid_p()
{
  printf '%s\n' "$1" | grep -Eq '^v[0-9]+\.[0-9]+\.[0-9]+$'
}

release_latest_tag()
{
  effective_url=$(curl --fail --silent --show-error --location \
    --max-time 10 --output /dev/null --write-out '%{url_effective}' \
    "$release_latest_url") || return 1
  latest_tag=${effective_url##*/}
  release_tag_valid_p "$latest_tag" || return 1
  printf '%s\n' "$latest_tag"
}

release_installed_p()
{
  release_installed_target=$1
  release_installed_expected_tag=$2

  [ -x "$release_installed_target/bin/autolith" ] || return 1
  [ -r "$release_installed_target/RELEASE" ] || return 1
  grep -Fx "tag=$release_installed_expected_tag" \
    "$release_installed_target/RELEASE" >/dev/null 2>&1
}

publish_links()
{
  publish_target=$1
  publish_tag=$2
  current_temporary=$install_root/.current.$$
  command_temporary=$bin_directory/.autolith.$$

  if [ -d "$install_root/current" ] && [ ! -L "$install_root/current" ]; then
    fail "$install_root/current is a directory, not an installation link."
  fi
  ln -s "releases/$publish_tag" "$current_temporary"
  mv -Tf -- "$current_temporary" "$install_root/current"
  if [ "$publish_command_p" = true ]; then
    mkdir -p -- "$bin_directory"
    ln -s "$install_root/current/bin/autolith" "$command_temporary"
    mv -f -- "$command_temporary" "$bin_directory/autolith"
  fi
}

while [ "$#" -gt 0 ]; do
  case $1 in
    --version)
      [ "$#" -ge 2 ] || fail "--version needs a release tag."
      requested_tag=$2
      shift 2
      ;;
    --without-command-link)
      publish_command_p=false
      shift
      ;;
    --help)
      printf '%s\n' \
        'usage: install [--version vMAJOR.MINOR.PATCH] [--without-command-link]' \
        '' \
        'Install or update the Autolith Linux x86-64 binary release.'
      exit 0
      ;;
    *)
      fail "unknown argument $1."
      ;;
  esac
done

[ -n "$home" ] || fail "HOME is not set."
[ "$(uname -s)" = Linux ] && [ "$(uname -m)" = x86_64 ] ||
  fail "binary releases currently support Linux x86-64 only. On macOS, install Autolith with Nix: nix run github:luciusmagn/autolith."
for command in bash bwrap curl git grep openssl sha256sum tar; do
  command -v "$command" >/dev/null 2>&1 ||
    fail "$command is required. Nix is the recommended installation path when system dependencies are unavailable."
done

if [ -z "$requested_tag" ]; then
  requested_tag=$(release_latest_tag) ||
    fail "the latest release tag could not be discovered."
fi
case $requested_tag in
  v*) ;;
  *) requested_tag=v$requested_tag ;;
esac
release_tag_valid_p "$requested_tag" || fail "the requested release tag is malformed."

release_name=autolith-$requested_tag-x86_64-linux
archive_name=$release_name.tar.gz
checksum_name=$archive_name.sha256
releases_root=$install_root/releases
target=$releases_root/$requested_tag
mkdir -p -- "$releases_root"
chmod 700 -- "$install_root" "$releases_root"

if release_installed_p "$target" "$requested_tag"; then
  publish_links "$target" "$requested_tag"
  printf 'Autolith %s is already installed.\n' "${requested_tag#v}"
else
  temporary_root=$(mktemp -d "$releases_root/.install.XXXXXX")
  archive=$temporary_root/$archive_name
  checksum=$temporary_root/$checksum_name
  extracted=$temporary_root/$release_name
  archive_url=$release_base_url/$requested_tag/$archive_name
  checksum_url=$release_base_url/$requested_tag/$checksum_name

  printf 'Downloading Autolith %s.\n' "${requested_tag#v}"
  curl --fail --location --show-error --retry 3 --progress-bar \
    --proto '=https' --tlsv1.2 --output "$archive" "$archive_url"
  curl --fail --location --show-error --retry 3 --silent \
    --proto '=https' --tlsv1.2 --output "$checksum" "$checksum_url"
  (
    cd -- "$temporary_root"
    sha256sum --check --status "$checksum_name"
  ) || fail "the release archive has the wrong SHA-256 identity."
  tar -xzf "$archive" -C "$temporary_root"
  release_installed_p "$extracted" "$requested_tag" ||
    fail "the release archive has an unexpected layout."
  chmod u+w -- "$extracted" ||
    fail "the verified release directory could not be prepared for publication."

  stale_target=
  if [ -e "$target" ]; then
    stale_target=$releases_root/.stale.$$
    mv -- "$target" "$stale_target"
  fi
  if ! mv -- "$extracted" "$target"; then
    if [ -n "$stale_target" ] && [ -e "$stale_target" ]; then
      mv -- "$stale_target" "$target" || true
    fi
    fail "the verified release could not be published."
  fi
  chmod u-w -- "$target" ||
    fail "the published release permissions could not be restored."
  publish_links "$target" "$requested_tag"
  if [ -n "$stale_target" ] && [ -e "$stale_target" ]; then
    chmod -R u+w "$stale_target" 2>/dev/null || true
    rm -rf -- "$stale_target"
  fi
  printf 'Installed Autolith %s.\n' "${requested_tag#v}"
fi

if [ "$publish_command_p" = true ]; then
  case :${PATH:-}: in
    *:"$bin_directory":*) ;;
    *)
      printf 'Add %s to PATH to run Autolith:\n' "$bin_directory"
      printf '  export PATH="%s:$PATH"\n' "$bin_directory"
      ;;
  esac
fi
if [ "$publish_command_p" = true ]; then
  printf 'Run: autolith\n'
fi
