#!/usr/bin/env bash
#
# scripts/install.sh — one-liner installer for the cass-tui binary.
#
# Served at https://cass.tools/install and invoked via:
#
#     curl -fsSL https://cass.tools/install | bash
#
# Flow (closed-beta, manifest-driven):
#
#   1. Detect OS + arch, map to a Rust target triple.
#   2. Fetch https://cass.tools/manifest.json — the authoritative release
#      metadata (version, per-target url + sha256).
#   3. Extract the url + sha256 for this machine's target.
#   4. Download the binary to a tempfile.
#   5. Verify SHA-256 matches the manifest entry (primary integrity check).
#   6. Verify ELF / Mach-O magic bytes (secondary check — catches CDN 404
#      HTML pages served with HTTP 200).
#   7. chmod +x, atomic rename into the install dir, print next steps.
#
# DESIGN NOTES (Micheline-grade):
#
# 1. set -euo pipefail — fail fast and loudly. Silent partial success on an
#    install is the worst outcome; a mid-download crash should leave the
#    user with the OLD binary, not a half-written new one.
#
# 2. Platform detection via `uname -s` and `uname -m`. Each supported
#    (os, arch) pair is listed explicitly in the case statement. Anything
#    unmatched errors out with a clear message pointing at cass.tools/issues
#    — better than silently downloading the wrong binary and failing on
#    first run with an opaque "Exec format error".
#
# 3. $HOME/.local/bin is the default install dir because:
#    - It's in the default PATH on modern Linux distros (XDG spec).
#    - No sudo, so we don't need to prompt mid-curl-pipe.
#    - User-scoped install/uninstall doesn't affect other users.
#    Override with CASS_INSTALL_DIR for a different prefix.
#
# 4. Manifest parsing prefers jq (fast, purpose-built) with a Python 3
#    fallback (universally available on modern Linux and macOS). If
#    neither is present the script errors out with an actionable install
#    hint — see parse_manifest(). We deliberately do NOT hand-roll a
#    pure-sh JSON parser; this script's integrity matters too much to trust
#    a 30-line regex hack with arbitrary server-side input.
#
# 5. SHA-256 verification is the PRIMARY integrity check. The manifest is
#    fetched over HTTPS from cass.tools (TLS + CF edge cert), and the
#    sha256 it contains is computed in CI on the same runner that built
#    the binary. A mismatched download or a CDN that swapped the binary
#    underneath us will fail here.
#
#    The ELF / Mach-O magic byte check is a SECONDARY sanity check: it
#    catches the failure mode where a misconfigured CDN serves an HTML
#    error page with HTTP 200. sha256 would also catch that (the hash
#    of an HTML page is not the hash of the binary), but the magic byte
#    check is cheap and gives a clearer error message.
#
# 6. We do NOT automatically modify ~/.bashrc / ~/.zshrc to add the install
#    dir to PATH. If it isn't on PATH we print the exact export line the
#    user can add themselves. Quiet modification of shell config files is
#    invasive and a surprise; a warning is better than a surprise.
#
# 7. The script is idempotent — running it twice produces the same result
#    (same binary at the same path). This doubles as an upgrade command.

set -euo pipefail

BASE_URL="${CASS_BASE_URL:-https://cass.tools}"
INSTALL_DIR="${CASS_INSTALL_DIR:-$HOME/.local/bin}"
MANIFEST_URL="${BASE_URL}/manifest.json"

# Platform detection: map (uname -s, uname -m) -> Rust target triple.
# Keep in sync with crates/cass-tools/src/update.rs::target_triple() and
# the CI release matrix in .github/workflows/release.yml.
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
ARCH="$(uname -m)"
case "$OS-$ARCH" in
    linux-x86_64)        TARGET="x86_64-unknown-linux-gnu" ;;
    linux-aarch64)       TARGET="aarch64-unknown-linux-gnu" ;;
    linux-arm64)         TARGET="aarch64-unknown-linux-gnu" ;;
    darwin-x86_64)       TARGET="x86_64-apple-darwin" ;;
    darwin-arm64)        TARGET="aarch64-apple-darwin" ;;
    *)
        echo "ERROR: Unsupported platform: $OS-$ARCH" >&2
        echo "Supported: linux x86_64, linux aarch64, macos x86_64, macos arm64" >&2
        echo "If you want a build for $OS-$ARCH, file an issue at https://cass.tools/issues" >&2
        exit 1
        ;;
esac

# parse_manifest <JSON> <target> <field>
# Extracts .binaries[<target>].<field> from the manifest JSON passed on
# stdin. Prints to stdout. Errors and exits the whole script if neither
# jq nor python3 is present, because we refuse to hand-roll JSON parsing
# for a critical integrity path.
parse_manifest() {
    local target="$1" field="$2"
    if command -v jq >/dev/null 2>&1; then
        jq -r --arg t "$target" --arg f "$field" '.binaries[$t][$f] // empty'
    elif command -v python3 >/dev/null 2>&1; then
        python3 -c "
import sys, json
try:
    data = json.load(sys.stdin)
    entry = data['binaries'].get('$target', {})
    value = entry.get('$field', '')
    print(value)
except Exception as e:
    sys.stderr.write(f'manifest parse error: {e}\n')
    sys.exit(1)
"
    else
        echo "ERROR: this installer needs jq or python3 to parse the release manifest" >&2
        echo "Install one of:" >&2
        echo "  Debian/Ubuntu : sudo apt install jq" >&2
        echo "  Fedora/RHEL   : sudo dnf install jq" >&2
        echo "  macOS         : brew install jq" >&2
        echo "  (python3 is also acceptable and usually pre-installed)" >&2
        exit 1
    fi
}

echo "cass-tui installer"
echo "  platform : $OS-$ARCH ($TARGET)"
echo "  manifest : $MANIFEST_URL"
echo

# Fetch manifest.json into a variable so we can parse it multiple times
# (once for version, once for url, once for sha256) without three HTTP
# round-trips. The manifest is tiny (< 2KB) so the memory cost is nil.
MANIFEST="$(curl -fsSL "$MANIFEST_URL" 2>&1)" || {
    echo "ERROR: failed to fetch manifest from $MANIFEST_URL" >&2
    echo "Details: $MANIFEST" >&2
    echo "Check your connection or report at https://cass.tools/issues" >&2
    exit 1
}

VERSION="$(echo "$MANIFEST" | parse_manifest "$TARGET" version || true)"
if [ -z "$VERSION" ]; then
    # Fall back to top-level .version if per-target version isn't set.
    if command -v jq >/dev/null 2>&1; then
        VERSION="$(echo "$MANIFEST" | jq -r '.version // empty')"
    elif command -v python3 >/dev/null 2>&1; then
        VERSION="$(echo "$MANIFEST" | python3 -c "import sys,json; print(json.load(sys.stdin).get('version',''))")"
    fi
fi

URL="$(echo "$MANIFEST" | parse_manifest "$TARGET" url)"
SHA256="$(echo "$MANIFEST" | parse_manifest "$TARGET" sha256)"

if [ -z "$URL" ] || [ -z "$SHA256" ]; then
    echo "ERROR: manifest has no entry for target $TARGET" >&2
    echo "Manifest URL: $MANIFEST_URL" >&2
    echo "This usually means the release hasn't published a binary for your" >&2
    echo "platform yet. File an issue at https://cass.tools/issues." >&2
    exit 1
fi

DEST="${INSTALL_DIR}/cass-tui"

echo "  version  : $VERSION"
echo "  source   : $URL"
echo "  sha256   : $SHA256"
echo "  dest     : $DEST"
echo

mkdir -p "$INSTALL_DIR"

# Download to a temp file first so a partial/failed download doesn't leave
# a corrupt binary at the destination. The tempfile lives in the same
# directory as the destination so the final mv is an atomic rename —
# a cross-filesystem mv would fall back to copy+delete which is NOT atomic
# and leaves a window where the binary is half-written.
TMP="$(mktemp "${INSTALL_DIR}/cass-tui.XXXXXX")"
trap 'rm -f "$TMP"' EXIT

if ! curl -fsSL "$URL" -o "$TMP"; then
    echo "ERROR: download failed from $URL" >&2
    echo "Check your connection or report at https://cass.tools/issues" >&2
    exit 1
fi

# PRIMARY integrity check: SHA-256 against the manifest entry.
# The sha256 was computed in CI on the same runner that built this binary.
# If this check fails, EITHER the CDN served a different file than what
# was uploaded (mitm / cache poisoning) OR the manifest is out of sync
# with the binary (broken release). Either way, abort.
if command -v sha256sum >/dev/null 2>&1; then
    ACTUAL_SHA="$(sha256sum "$TMP" | awk '{print $1}')"
elif command -v shasum >/dev/null 2>&1; then
    # macOS ships shasum (perl) but not sha256sum in the default path.
    ACTUAL_SHA="$(shasum -a 256 "$TMP" | awk '{print $1}')"
else
    echo "ERROR: neither sha256sum nor shasum is available - cannot verify binary integrity" >&2
    echo "Refusing to install an unverified binary." >&2
    exit 1
fi

if [ "$ACTUAL_SHA" != "$SHA256" ]; then
    echo "ERROR: sha256 mismatch - refusing to install" >&2
    echo "  expected: $SHA256" >&2
    echo "  actual  : $ACTUAL_SHA" >&2
    echo "This means the downloaded binary does not match the release manifest." >&2
    echo "Your network or CDN may be serving a tampered or stale file." >&2
    exit 1
fi

# SECONDARY sanity check: magic bytes. The primary check (sha256) already
# caught any wrong-file condition, but this produces a clearer error if
# the CDN is serving an HTML error page with HTTP 200 — "expected ELF,
# got HTML" is much more actionable than a bare hash mismatch.
MAGIC="$(head -c 4 "$TMP" | od -An -tx1 | tr -d ' \n')"
case "$OS-$MAGIC" in
    linux-7f454c46)
        # ELF magic - looks valid
        ;;
    darwin-cffaedfe|darwin-cefaedfe|darwin-feedface|darwin-feedfacf)
        # Mach-O magic in either endianness - looks valid
        ;;
    *)
        echo "ERROR: downloaded file is not a valid binary for $OS" >&2
        echo "Got magic bytes: $MAGIC (expected ELF or Mach-O)" >&2
        echo "The CDN may have served a 404 page with the wrong content type," >&2
        echo "but the sha256 check above also passed - this is inconsistent." >&2
        echo "Report at https://cass.tools/issues with this output." >&2
        exit 1
        ;;
esac

chmod +x "$TMP"
mv "$TMP" "$DEST"
trap - EXIT

echo "Installed cass-tui $VERSION to $DEST"
echo

# PATH check: warn (don't auto-modify) if the install dir isn't in PATH.
# Adding to ~/.bashrc / ~/.zshrc automatically is invasive; the user can
# decide whether to do it themselves with the exact line we print.
case ":$PATH:" in
    *":$INSTALL_DIR:"*)
        echo "Run 'cass-tui --help' to verify."
        ;;
    *)
        echo "NOTE: $INSTALL_DIR is not currently in your PATH."
        echo
        echo "To run cass-tui RIGHT NOW, use the full path:"
        echo
        echo "    $DEST"
        echo
        echo "To run it from anywhere going forward, add this line to ~/.bashrc"
        echo "(or ~/.zshrc if you use zsh):"
        echo
        echo "    export PATH=\"$INSTALL_DIR:\$PATH\""
        echo
        echo "Then open a new terminal and run:   cass-tui"
        ;;
esac
