#!/bin/sh
# Pyre CLI installer for Linux and macOS.
#
#   curl -fsSL https://www.pyredata.io/dl/cli/install.sh | sh
#
# Options (environment):
#   PYRE_VERSION=1.2.3        install a specific version (default: latest)
#   PYRE_INSTALL_DIR=~/bin    where to put the binary (default: /usr/local/bin,
#                             falling back to ~/.local/bin without root)
#   PYRE_DL_BASE=https://...  a self-hosted manager's download base
#   PYRE_NO_COMPLETION=1      skip shell completions and man page
#
# What it does: detects OS and CPU, downloads the archive for it plus the
# signed SHA256SUMS, verifies the checksum (and the minisign signature
# when `minisign` is installed), installs the binary, completions and
# man page, and prints what it did. Nothing else is touched; `pyre
# uninstall` reverses it.
set -eu

DL_BASE=${PYRE_DL_BASE:-https://www.pyredata.io/dl}
VERSION=${PYRE_VERSION:-}
PUBKEY="RWRMW5U71VNmVoootBTNKqeKpK1n09FT+1++27/I06LirKMzpKyeQMPR"

say() { printf '%s\n' "$*" >&2; }
die() { say "install.sh: $*"; exit 1; }
have() { command -v "$1" >/dev/null 2>&1; }

fetch() {
    if have curl; then curl -fsSL "$1" -o "$2"
    elif have wget; then wget -q "$1" -O "$2"
    else die "need curl or wget"; fi
}

os=$(uname -s | tr '[:upper:]' '[:lower:]')
case $os in
    linux|darwin) ;;
    *) die "unsupported OS: $os (Linux and macOS only; Windows uses install.ps1)" ;;
esac
arch=$(uname -m)
case $arch in
    x86_64|amd64) arch=amd64 ;;
    arm64|aarch64) arch=arm64 ;;
    *) die "unsupported CPU: $arch" ;;
esac
[ "$os/$arch" = "linux/arm64" ] && die "no Linux arm64 build yet; ask support@pyredata.io"

tmp=$(mktemp -d 2>/dev/null || mktemp -d -t pyre)
trap 'rm -rf "$tmp"' EXIT

if [ -z "$VERSION" ]; then
    fetch "$DL_BASE/cli/latest.json" "$tmp/latest.json"
    VERSION=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$tmp/latest.json" | head -1)
    [ -n "$VERSION" ] || die "could not read the latest version from $DL_BASE/cli/latest.json"
fi
base="$DL_BASE/cli/$VERSION"
archive="pyre_${VERSION}_${os}_${arch}.tar.gz"
say "Installing pyre $VERSION for $os/$arch"

fetch "$base/$archive" "$tmp/$archive"
fetch "$base/SHA256SUMS" "$tmp/SHA256SUMS"
if have minisign; then
    fetch "$base/SHA256SUMS.minisig" "$tmp/SHA256SUMS.minisig"
    minisign -Vq -P "$PUBKEY" -m "$tmp/SHA256SUMS" -x "$tmp/SHA256SUMS.minisig" || die "SHA256SUMS signature does not verify; not installing"
    say "signature ok (minisign)"
else
    say "note: minisign not installed; checksum verified, signature not (install minisign to verify releases)"
fi
want=$(grep " \*\{0,1\}$archive\$" "$tmp/SHA256SUMS" | cut -d' ' -f1)
[ -n "$want" ] || die "$archive is not listed in SHA256SUMS"
if have sha256sum; then got=$(sha256sum "$tmp/$archive" | cut -d' ' -f1)
else got=$(shasum -a 256 "$tmp/$archive" | cut -d' ' -f1); fi
[ "$got" = "$want" ] || die "checksum mismatch for $archive; not installing"
say "checksum ok"

mkdir -p "$tmp/x" && tar -xzf "$tmp/$archive" -C "$tmp/x"

# Where to install: an explicit dir, else /usr/local/bin. On a stock Mac
# (and most Linux boxes) /usr/local/bin needs root, so sudo is tried
# when there is a terminal to ask on -- sudo prompts on /dev/tty, which
# works under `curl | sh` too. Only without sudo, or without a terminal,
# does the install fall back to ~/.local/bin, and then the shell rc is
# taught about it (below) so `pyre` works in the next terminal.
SUDO=""
if [ -n "${PYRE_INSTALL_DIR:-}" ]; then
    bindir=$PYRE_INSTALL_DIR
elif [ -w /usr/local/bin ] || [ "$(id -u)" = 0 ]; then
    bindir=/usr/local/bin
elif have sudo && [ -c /dev/tty ] && [ -t 2 ]; then
    bindir=/usr/local/bin; SUDO=sudo
    say "installing into /usr/local/bin needs your password for sudo (set PYRE_INSTALL_DIR=~/.local/bin to avoid it)"
    if ! sudo -v </dev/tty; then
        say "sudo declined; installing into ~/.local/bin instead"
        bindir=$HOME/.local/bin; SUDO=""
    fi
else
    bindir=$HOME/.local/bin
fi
$SUDO mkdir -p "$bindir"
$SUDO install -m 0755 "$tmp/x/pyre" "$bindir/pyre"
say "installed $bindir/pyre"
# A previous macOS download by browser may carry the quarantine flag; a
# curl download does not, but clear it in case.
[ "$os" = darwin ] && have xattr && $SUDO xattr -d com.apple.quarantine "$bindir/pyre" 2>/dev/null || true

if [ -z "${PYRE_NO_COMPLETION:-}" ]; then
    if [ -n "$SUDO" ] || [ "$bindir" = /usr/local/bin ]; then
        $SUDO mkdir -p /usr/local/etc/bash_completion.d /usr/local/share/zsh/site-functions /usr/local/share/fish/vendor_completions.d /usr/local/share/man/man1 2>/dev/null || true
        $SUDO install -m 0644 "$tmp/x/completions/pyre.bash" /usr/local/etc/bash_completion.d/pyre 2>/dev/null || true
        $SUDO install -m 0644 "$tmp/x/completions/_pyre" /usr/local/share/zsh/site-functions/_pyre 2>/dev/null || true
        $SUDO install -m 0644 "$tmp/x/completions/pyre.fish" /usr/local/share/fish/vendor_completions.d/pyre.fish 2>/dev/null || true
        $SUDO install -m 0644 "$tmp"/x/man/*.1.gz /usr/local/share/man/man1/ 2>/dev/null || true
        say "installed completions (bash, zsh, fish) and man pages under /usr/local"
    else
        mkdir -p "$HOME/.local/share/bash-completion/completions" "$HOME/.zsh/completions" "$HOME/.config/fish/completions" "$HOME/.local/share/man/man1"
        install -m 0644 "$tmp/x/completions/pyre.bash" "$HOME/.local/share/bash-completion/completions/pyre"
        install -m 0644 "$tmp/x/completions/_pyre" "$HOME/.zsh/completions/_pyre"
        install -m 0644 "$tmp/x/completions/pyre.fish" "$HOME/.config/fish/completions/pyre.fish"
        install -m 0644 "$tmp"/x/man/*.1.gz "$HOME/.local/share/man/man1/"
        say "installed completions under ~/.local and ~/.zsh/completions"
    fi
fi

# A bindir outside PATH (the ~/.local/bin fallback, or a custom dir)
# gets one commented line in the user's shell rc, once; the user
# completions dir gets its fpath line the same way. Nothing else in the
# rc is touched.
rc_add() { # <rc file> <line>
    [ -f "$1" ] || touch "$1"
    grep -qF -- "$2" "$1" 2>/dev/null || printf '\n# added by the Pyre CLI installer\n%s\n' "$2" >> "$1"
}
shell_rc() {
    case "${SHELL:-}" in
        */zsh) echo "$HOME/.zshrc" ;;
        */fish) echo "" ;;
        *) if [ "$os" = darwin ]; then echo "$HOME/.zshrc"; else echo "$HOME/.bashrc"; fi ;;
    esac
}
reopen=""
case ":$PATH:" in
    *":$bindir:"*) ;;
    *)
        rc=$(shell_rc)
        # Written with $HOME, not the expanded path, so the line survives
        # a renamed home directory and reads the same for everyone.
        case $bindir in
            "$HOME"/*) bindir_rc="\$HOME${bindir#"$HOME"}" ;;
            *) bindir_rc=$bindir ;;
        esac
        if [ -n "$rc" ]; then
            rc_add "$rc" "export PATH=\"$bindir_rc:\$PATH\""
            say "added $bindir_rc to PATH in ~/$(basename "$rc")"
        else
            say "note: add $bindir to your PATH (fish: fish_add_path $bindir)"
        fi
        reopen=1 ;;
esac
if [ -z "${PYRE_NO_COMPLETION:-}" ] && [ -f "$HOME/.zsh/completions/_pyre" ] && [ -n "$(shell_rc)" ] && [ "$(basename "$(shell_rc)")" = .zshrc ]; then
    rc_add "$HOME/.zshrc" 'fpath=("$HOME/.zsh/completions" $fpath); autoload -Uz compinit && compinit'
    say "added ~/.zsh/completions to fpath in ~/.zshrc"
    reopen=1
fi
say ""
say "pyre $VERSION installed. Next:"
say "  pyre auth login      # paste an API token from the dashboard"
say "  pyre --help          # commands with examples"
say "  pyre update          # later, to update;  pyre uninstall  to remove"
if [ -n "$reopen" ]; then
    say ""
    printf '\033[1mOpen a new terminal (or run: source ~/%s) before using pyre.\033[0m\n' "$(basename "$(shell_rc)")" >&2
fi
