#!/bin/sh
#
# paperwork: the CloudRaker Paperwork API CLI. https://docs.cloudraker.com
#
# Installer served at https://paperwork.sh. POSIX shell, needs only
# curl (or wget) and tar. Downloads one archive from release.paperwork.sh,
# unpacks it, puts one `paperwork` binary in your PATH. Touches nothing else:
# no shell rc files, no system dirs, no telemetry, no sudo.
#
# INSTALL
#
#   curl -fsSL https://paperwork.sh | sh
#
# Or review it first and run the copy you read:
#
#   curl -fsSL https://paperwork.sh -o install-paperwork.sh
#   less install-paperwork.sh
#   sh install-paperwork.sh
#
# OPTIONS
#
# Flags go after `-s --`, or use the env var:
#
#   curl -fsSL https://paperwork.sh | sh -s -- --dir /usr/local/bin
#
#   --dir <path>       Install location. Default ~/.local/bin.
#                      Env: PAPERWORK_INSTALL_DIR
#   --version <ver>    `latest` (default), `dev` for the newest development
#                      build, or a release like 0.0.6.
#                      Env: PAPERWORK_VERSION
#   --dev              Same as --version dev.
#   --help             Print help and exit.
#
# PLATFORMS
#
#   macOS      arm64, x86_64
#   Linux      arm64, x86_64. Static musl builds: any distro, and containers
#              with no libc.
#   Windows    use the PowerShell installer at the same URL:
#                irm https://paperwork.sh | iex
#
# UNINSTALL
#
#   paperwork auth logout              # drops credentials from your keychain
#   rm ~/.local/bin/paperwork          # or wherever you installed it
#
set -eu

# Overridable for testing against a local server. Normal installs leave it.
BASE_URL="${PAPERWORK_BASE_URL:-https://release.paperwork.sh}"

version="${PAPERWORK_VERSION:-latest}"
install_dir="${PAPERWORK_INSTALL_DIR:-$HOME/.local/bin}"

say() { printf 'paperwork: %s\n' "$1"; }
die() { printf 'paperwork: %s\n' "$1" >&2; exit 1; }

# Spelled out, not sed'd out of the header: piped into `sh` there is no script
# file to read back.
usage() {
  cat <<'EOF'
Installs the paperwork CLI, the CloudRaker Paperwork API command line.

  curl -fsSL https://paperwork.sh | sh
  curl -fsSL https://paperwork.sh | sh -s -- [options]

Options:
  --dir <path>      Install location (default: ~/.local/bin).
                    Env: PAPERWORK_INSTALL_DIR
  --version <ver>   `latest` (default), `dev`, or a release like 0.0.6.
                    Env: PAPERWORK_VERSION
  --dev             Same as --version dev.
  --help            Print help and exit.

macOS and Linux, arm64 and x86_64. On Windows use the PowerShell installer:
  irm https://paperwork.sh | iex
EOF
}

while [ $# -gt 0 ]; do
  case "$1" in
    --dir) [ $# -ge 2 ] || die "--dir needs a path"; install_dir="$2"; shift 2 ;;
    --dir=*) install_dir="${1#--dir=}"; shift ;;
    --version) [ $# -ge 2 ] || die "--version needs a value"; version="$2"; shift 2 ;;
    --version=*) version="${1#--version=}"; shift ;;
    --dev) version=dev; shift ;;
    -h | --help) usage; exit 0 ;;
    *) die "unknown option '$1'. Run with --help." ;;
  esac
done

case "$version" in
  latest | dev) ;;
  # Releases are published under the bare version (0.0.6), the same one
  # `paperwork --version` prints. A leading v is an easy slip: drop it.
  v[0-9]*) version="${version#v}" ;;
  [0-9]*) ;;
  *) die "--version must be 'latest', 'dev', or a release like 0.0.6 (got '$version')" ;;
esac

# --- platform -------------------------------------------------------------

os="$(uname -s)"
arch="$(uname -m)"

case "$os" in
  Darwin) os_part=apple-darwin ;;
  # Linux ships musl only. Static, so host libc and distro do not matter.
  Linux) os_part=unknown-linux-musl ;;
  MINGW* | MSYS* | CYGWIN*)
    die "on Windows run the PowerShell installer: irm https://paperwork.sh | iex" ;;
  *) die "unsupported OS '$os'" ;;
esac

case "$arch" in
  arm64 | aarch64) arch_part=aarch64 ;;
  x86_64 | amd64) arch_part=x86_64 ;;
  *) die "unsupported architecture '$arch'. Only arm64 and x86_64 are published." ;;
esac

name="paperwork-$arch_part-$os_part"
url="$BASE_URL/cli/$version/$name.tar.gz"

# --- download -------------------------------------------------------------

command -v tar >/dev/null 2>&1 || die "tar not found"

if command -v curl >/dev/null 2>&1; then
  download() { curl -fsSL --retry 3 --retry-connrefused -o "$2" -- "$1"; }
elif command -v wget >/dev/null 2>&1; then
  download() { wget -q --tries=3 -O "$2" -- "$1"; }
else
  die "needs curl or wget, found neither"
fi

# A Homebrew copy is updated by brew. A second copy here would shadow it or be
# shadowed by it, and the two would drift apart.
if command -v brew >/dev/null 2>&1 && [ -n "$(brew list --versions paperwork 2>/dev/null)" ]; then
  [ "$version" = latest ] || die "paperwork is installed with Homebrew. Run 'brew uninstall paperwork' first to install $version here."
  say "already installed with Homebrew. Update it with: brew upgrade paperwork"
  exit 0
fi

tmp="$(mktemp -d)" || die "could not create a temp dir"
# Cleans up on success, failure and Ctrl-C.
trap 'rm -rf "$tmp"' EXIT
trap 'rm -rf "$tmp"; exit 130' INT
trap 'rm -rf "$tmp"; exit 143' TERM

say "downloading $url"
download "$url" "$tmp/archive.tar.gz" || die "download failed. Is '$version' published?"

# No checksum verification yet: the release build does not publish a .sha256
# per archive. Verify the archive here once it does.

tar -xzf "$tmp/archive.tar.gz" -C "$tmp" || die "archive could not be unpacked"

# Archive layout: paperwork-<target>/{paperwork,README.md,LICENSE}.
binary="$tmp/$name/paperwork"
[ -f "$binary" ] || die "archive did not contain $name/paperwork"
chmod +x "$binary"

# Catches a wrong-platform or truncated download here, not after it overwrites
# a working install.
"$binary" --version >/dev/null 2>&1 || die "the downloaded binary does not run on this machine"
new_version="$("$binary" --version 2>/dev/null | head -n 1)"

# --- install --------------------------------------------------------------

mkdir -p "$install_dir" 2>/dev/null \
  || die "cannot create $install_dir. Pass --dir <path>."
[ -w "$install_dir" ] \
  || die "$install_dir is not writable. Pass --dir <path>, or re-run with sudo."

target="$install_dir/paperwork"
if [ -e "$target" ]; then
  say "replacing $("$target" --version 2>/dev/null | head -n 1 || echo 'an existing install')"
fi

# Staged beside the target: the copy can cross filesystems, the mv is atomic,
# so a running paperwork never sees a half-written file.
staged="$install_dir/.paperwork.$$"
trap 'rm -rf "$tmp" "$staged"' EXIT
cp "$binary" "$staged" || die "could not write to $install_dir"
chmod 755 "$staged"
mv -f "$staged" "$target" || die "could not install to $target"

say "installed $new_version to $target"

# --- PATH -----------------------------------------------------------------

case ":${PATH:-}:" in
  *":$install_dir:"*)
    say "run 'paperwork --help' to get started" ;;
  *)
    say "$install_dir is not on your PATH. Add to your shell profile:"
    # shellcheck disable=SC2016  # literal $PATH: the user pastes this line.
    printf '\n  export PATH="%s:$PATH"\n\n' "$install_dir"
    say "or run it directly: $target --help" ;;
esac
