#!/bin/sh
set -eu

package_origin='https://packages.limilake.com/'
release_public_key_pem_b64='LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUNvd0JRWURLMlZ3QXlFQVNOelVDSGYzdGlHeXlocjB1YWp4QmtqdTZ0TktHVjBYN0l0REVaQU9LVzA9Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo='
version=''
channel='stable'
channel_set=0
install_dir="${HOME}/.local/bin"
modify_path=1

usage() {
  printf '%s\n' 'Usage: install.sh [--version VERSION | --channel stable|beta|nightly] [--install-dir DIR] [--no-modify-path]'
}

while [ "$#" -gt 0 ]; do
  case "$1" in
    --version)
      [ "$#" -ge 2 ] || { usage >&2; exit 2; }
      version=$2
      shift 2
      ;;
    --channel)
      [ "$#" -ge 2 ] || { usage >&2; exit 2; }
      channel=$2
      channel_set=1
      shift 2
      ;;
    --install-dir)
      [ "$#" -ge 2 ] || { usage >&2; exit 2; }
      install_dir=$2
      shift 2
      ;;
    --no-modify-path)
      modify_path=0
      shift
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      printf 'Unknown option: %s\n' "$1" >&2
      usage >&2
      exit 2
      ;;
  esac
done

case "$channel" in stable|beta|nightly) ;; *) printf 'Unsupported channel: %s\n' "$channel" >&2; exit 2 ;; esac
if [ -n "$version" ] && [ "$channel_set" -eq 1 ]; then
  printf '%s\n' 'Choose either --version or --channel, not both.' >&2
  exit 2
fi

case "$(uname -s)" in
  Linux) release_os=linux ;;
  Darwin) release_os=darwin ;;
  *) printf 'Unsupported operating system: %s\n' "$(uname -s)" >&2; exit 1 ;;
esac
case "$(uname -m)" in
  x86_64|amd64) release_arch=amd64 ;;
  arm64|aarch64) release_arch=arm64 ;;
  *) printf 'Unsupported architecture: %s\n' "$(uname -m)" >&2; exit 1 ;;
esac

temporary=$(mktemp -d "${TMPDIR:-/tmp}/limilake-install.XXXXXX")
trap 'rm -rf "$temporary"' EXIT HUP INT TERM

if [ -z "$version" ]; then
  version=$(curl -fsSL "${package_origin%/}/cli/channels/${channel}")
fi
if ! awk -v value="$version" '
  function numeric(part) { return part ~ /^[0-9]+$/ }
  function canonical_number(part) { return numeric(part) && (part == "0" || part !~ /^0/) }
  BEGIN {
    build_count = split(value, build_parts, "[+]")
    if (build_count > 2 || build_parts[1] == "") exit 1
    if (build_count == 2) {
      count = split(build_parts[2], ids, ".")
      for (i = 1; i <= count; i++) if (ids[i] !~ /^[0-9A-Za-z-]+$/) exit 1
    }
    hyphen = index(build_parts[1], "-")
    core_value = hyphen ? substr(build_parts[1], 1, hyphen - 1) : build_parts[1]
    prerelease_value = hyphen ? substr(build_parts[1], hyphen + 1) : ""
    if (core_value == "" || (hyphen && prerelease_value == "")) exit 1
    count = split(core_value, core, ".")
    if (count != 3) exit 1
    for (i = 1; i <= 3; i++) if (!canonical_number(core[i])) exit 1
    if (hyphen) {
      count = split(prerelease_value, ids, ".")
      for (i = 1; i <= count; i++) {
        if (ids[i] !~ /^[0-9A-Za-z-]+$/) exit 1
        if (numeric(ids[i]) && !canonical_number(ids[i])) exit 1
      }
    }
  }
'; then
  printf 'Invalid semantic version: %s\n' "$version" >&2
  exit 1
fi

manifest_path="cli/v${version}/manifest.json"
manifest="$temporary/manifest.json"
signature_b64="$temporary/manifest.json.sig.b64"
signature="$temporary/manifest.json.sig"
public_key="$temporary/release-public.pem"
curl -fsSL "${package_origin%/}/${manifest_path}" -o "$manifest"
curl -fsSL "${package_origin%/}/${manifest_path}.sig" -o "$signature_b64"

printf '%s' "$release_public_key_pem_b64" | openssl base64 -d -A -out "$public_key"
openssl base64 -d -A -in "$signature_b64" -out "$signature"
openssl pkeyutl -verify -pubin -inkey "$public_key" -rawin -in "$manifest" -sigfile "$signature" >/dev/null

artifact_path="cli/v${version}/limilake_${version}_${release_os}_${release_arch}.tar.gz"
checksum=$(awk -v target="$artifact_path" '
  index($0, "\"path\": \"" target "\"") { selected=1; next }
  selected && /"sha256":/ { line=$0; sub(/^.*"sha256": "/, "", line); sub(/".*$/, "", line); print line; exit }
' "$manifest")
[ -n "$checksum" ] || { printf 'Signed manifest does not contain %s\n' "$artifact_path" >&2; exit 1; }

archive="$temporary/release.tar.gz"
curl -fsSL "${package_origin%/}/${artifact_path}" -o "$archive"
if command -v sha256sum >/dev/null 2>&1; then
  actual=$(sha256sum "$archive" | awk '{print $1}')
else
  actual=$(shasum -a 256 "$archive" | awk '{print $1}')
fi
[ "$actual" = "$checksum" ] || { printf '%s\n' 'Release archive checksum verification failed.' >&2; exit 1; }

mkdir "$temporary/archive"
entries="$temporary/archive-entries"
tar -tzf "$archive" > "$entries"
if ! awk '
  /^\// || /\\/ || /(^|\/)\.\.($|\/)/ { exit 1 }
  $0 == "limilake" { binaries++ }
  $0 == "LICENSE" { licenses++ }
  $0 == "NOTICE" { notices++ }
  $0 == "THIRD_PARTY_NOTICES" { third_party++ }
  /^licenses\/[^/]+\/.+$/ { dependency_licenses++; next }
  $0 != "limilake" && $0 != "LICENSE" && $0 != "NOTICE" && $0 != "THIRD_PARTY_NOTICES" { exit 1 }
  END { if (binaries != 1 || licenses != 1 || notices != 1 || third_party != 1 || dependency_licenses < 1) exit 1 }
' "$entries"; then
  printf '%s\n' 'Release archive contains unsafe or unexpected entries.' >&2
  exit 1
fi
if ! tar -tvzf "$archive" | awk 'substr($0, 1, 1) != "-" { exit 1 }'; then
  printf '%s\n' 'Release archive contains a non-regular entry.' >&2
  exit 1
fi
tar -xzf "$archive" -C "$temporary/archive"
[ -f "$temporary/archive/limilake" ] || { printf '%s\n' 'Release archive does not contain limilake.' >&2; exit 1; }
[ -f "$temporary/archive/LICENSE" ] && [ -f "$temporary/archive/NOTICE" ] && [ -f "$temporary/archive/THIRD_PARTY_NOTICES" ] || { printf '%s\n' 'Release archive is missing license notices.' >&2; exit 1; }

mkdir -p "$install_dir"
replacement=$(mktemp "${install_dir}/.limilake-install.XXXXXX")
trap 'rm -rf "$temporary"; rm -f "$replacement"' EXIT HUP INT TERM
cp "$temporary/archive/limilake" "$replacement"
chmod 755 "$replacement"
mv -f "$replacement" "${install_dir}/limilake"

if [ "$modify_path" -eq 1 ] && [ "$install_dir" = "${HOME}/.local/bin" ]; then
  case ":${PATH}:" in
    *":${install_dir}:"*) ;;
    *)
      case "${SHELL##*/}" in
        zsh) profile="${HOME}/.zprofile" ;;
        *) profile="${HOME}/.profile" ;;
      esac
      path_line='export PATH="$HOME/.local/bin:$PATH"'
      if [ ! -f "$profile" ] || ! grep -Fqx "$path_line" "$profile"; then
        printf '\n%s\n' "$path_line" >> "$profile"
      fi
      printf 'Added %s to PATH in %s. Start a new shell to use it.\n' "$install_dir" "$profile"
      ;;
  esac
fi

printf 'Installed limilake %s to %s\n' "$version" "${install_dir}/limilake"
if [ "$modify_path" -eq 0 ] || [ "$install_dir" != "${HOME}/.local/bin" ]; then
  printf 'PATH was not modified. Add %s to PATH if needed.\n' "$install_dir"
fi
