#!/bin/sh
#
# AlertaVuln CLI installer - Linux / macOS.
#
#   curl -fsSL https://get.alertavuln.com/cli/install.sh | sh
#
# To pass options when piping, use `sh -s --`:
#
#   curl -fsSL https://get.alertavuln.com/cli/install.sh | sh -s -- --version v0.4.0
#
# Options:
#   --version vX.Y.Z    install a specific version
#                       (default: "latest" from the published version.json)
#   --with-mcp-router   also install the AlertaVuln MCP router binary
#   --dry-run           resolve platform + version and print the URLs that
#                       would be used; downloads and installs NOTHING.
#                       Combined with --version, it performs no network I/O
#                       at all.
#   -h, --help          show this help
#
# On Windows use install.ps1 instead:
#   irm https://get.alertavuln.com/cli/install.ps1 | iex
#
# Every download is verified against the release's SHA256SUMS. The script
# hard-fails rather than install anything unverified.
#
# Binaries are served from Azure Blob Storage; version directories are
# immutable and version.json is the only mutable pointer.

set -u

BASE_URL="https://downloads.alertavuln.com"
INSTALL_DIR="${HOME}/.local/bin"

fail() {
    printf 'install.sh: error: %s\n' "$1" >&2
    exit 1
}

usage() {
    cat <<'EOF'
AlertaVuln CLI installer (Linux / macOS).

  curl -fsSL https://get.alertavuln.com/cli/install.sh | sh
  curl -fsSL https://get.alertavuln.com/cli/install.sh | sh -s -- --version v0.4.0

Options:
  --version vX.Y.Z    install a specific version (default: latest from version.json)
  --with-mcp-router   also install the AlertaVuln MCP router binary
  --dry-run           print resolved URLs; download and install nothing
  -h, --help          show this help

On Windows:  irm https://get.alertavuln.com/cli/install.ps1 | iex
Docs:        https://docs.alertavuln.com
EOF
}

# http_get URL          -> body on stdout
# http_get_to URL FILE  -> body written to FILE
http_get() {
    if command -v curl >/dev/null 2>&1; then
        curl -fsSL "$1"
    elif command -v wget >/dev/null 2>&1; then
        wget -qO- "$1"
    else
        return 127
    fi
}

http_get_to() {
    if command -v curl >/dev/null 2>&1; then
        curl -fsSL -o "$2" "$1"
    elif command -v wget >/dev/null 2>&1; then
        wget -qO "$2" "$1"
    else
        return 127
    fi
}

main() {
    VERSION=""
    WITH_ROUTER=0
    DRY_RUN=0

    while [ $# -gt 0 ]; do
        case "$1" in
            --version)
                [ $# -ge 2 ] || fail "--version needs an argument (e.g. --version v0.4.0)"
                VERSION="$2"
                shift 2
                ;;
            --version=*)
                VERSION="${1#--version=}"
                shift
                ;;
            --with-mcp-router)
                WITH_ROUTER=1
                shift
                ;;
            --dry-run)
                DRY_RUN=1
                shift
                ;;
            -h|--help)
                usage
                exit 0
                ;;
            *)
                fail "unknown option: $1 (try --help)"
                ;;
        esac
    done

    # ---- platform detection -------------------------------------------------
    kernel="$(uname -s 2>/dev/null || echo unknown)"
    case "$kernel" in
        Linux)  OS=linux ;;
        Darwin) OS=darwin ;;
        MINGW*|MSYS*|CYGWIN*|Windows_NT)
            fail "this script targets Linux/macOS. On Windows run:  irm https://get.alertavuln.com/cli/install.ps1 | iex"
            ;;
        *)
            fail "unsupported operating system '$kernel' (supported: Linux, Darwin)"
            ;;
    esac

    machine="$(uname -m 2>/dev/null || echo unknown)"
    case "$machine" in
        x86_64|amd64)  ARCH=amd64 ;;
        aarch64|arm64) ARCH=arm64 ;;
        *)
            fail "unsupported architecture '$machine' (supported: x86_64/amd64, aarch64/arm64)"
            ;;
    esac

    # ---- version resolution -------------------------------------------------
    if [ -z "$VERSION" ]; then
        command -v curl >/dev/null 2>&1 || command -v wget >/dev/null 2>&1 \
            || fail "need curl or wget to resolve the latest version"
        json="$(http_get "${BASE_URL}/version.json")" \
            || fail "could not fetch ${BASE_URL}/version.json - pass --version vX.Y.Z or try again later"
        VERSION="$(printf '%s' "$json" | tr -d ' \t\r\n' | sed -n 's/.*"latest":"\([^"]*\)".*/\1/p')"
        [ -n "$VERSION" ] || fail "could not parse \"latest\" out of version.json"
    fi
    case "$VERSION" in
        v*) : ;;
        *)  VERSION="v${VERSION}" ;;
    esac
    case "$VERSION" in
        v[0-9]*.[0-9]*.[0-9]*) : ;;
        *) fail "invalid version '$VERSION' (expected vX.Y.Z)" ;;
    esac

    BIN="alertavuln-${OS}-${ARCH}"
    ROUTER_BIN="mcp-router-${OS}-${ARCH}"
    BIN_URL="${BASE_URL}/${VERSION}/${BIN}"
    ROUTER_URL="${BASE_URL}/${VERSION}/${ROUTER_BIN}"
    SUMS_URL="${BASE_URL}/${VERSION}/SHA256SUMS"

    # ---- dry run: print what would happen, touch nothing --------------------
    if [ "$DRY_RUN" -eq 1 ]; then
        echo "dry-run: os=${OS} arch=${ARCH} version=${VERSION}"
        echo "dry-run: binary-url=${BIN_URL}"
        echo "dry-run: checksums-url=${SUMS_URL}"
        if [ "$WITH_ROUTER" -eq 1 ]; then
            echo "dry-run: mcp-router-url=${ROUTER_URL}"
        fi
        echo "dry-run: install-dir=${INSTALL_DIR}"
        echo "dry-run: nothing downloaded, nothing installed"
        exit 0
    fi

    # ---- preflight: downloader + checksum tool must exist -------------------
    command -v curl >/dev/null 2>&1 || command -v wget >/dev/null 2>&1 \
        || fail "need curl or wget to download the CLI"

    if command -v sha256sum >/dev/null 2>&1; then
        SHATOOL="sha256sum"
    elif command -v shasum >/dev/null 2>&1; then
        SHATOOL="shasum -a 256"
    else
        fail "no SHA-256 tool found (need sha256sum or shasum) - refusing to install unverified binaries"
    fi

    tmp="$(mktemp -d 2>/dev/null || mktemp -d -t alertavuln)" || fail "could not create a temp directory"
    trap 'rm -rf "$tmp"' EXIT INT TERM

    # ---- download -----------------------------------------------------------
    echo "Downloading ${BIN_URL}"
    http_get_to "$BIN_URL" "${tmp}/${BIN}" \
        || fail "download failed: ${BIN_URL} (does ${VERSION} exist?)"
    echo "Downloading ${SUMS_URL}"
    http_get_to "$SUMS_URL" "${tmp}/SHA256SUMS" \
        || fail "download failed: ${SUMS_URL}"
    if [ "$WITH_ROUTER" -eq 1 ]; then
        echo "Downloading ${ROUTER_URL}"
        http_get_to "$ROUTER_URL" "${tmp}/${ROUTER_BIN}" \
            || fail "download failed: ${ROUTER_URL} (the MCP router ships from the release after v0.3.0)"
    fi

    # ---- verify (hard-fail on any mismatch) ----------------------------------
    verify() {
        # sha256sum format: "<hex64>  <name>". Tolerate an optional binary-mode
        # "*" prefix on the name (some sha256sum builds emit "<hex64> *<name>").
        expected="$(awk -v f="$1" '{ n = $2; sub(/^\*/, "", n); if (n == f) print $1 }' "${tmp}/SHA256SUMS")"
        [ -n "$expected" ] || fail "no entry for $1 in SHA256SUMS - refusing to install"
        actual="$($SHATOOL "${tmp}/$1" | awk '{ print $1 }')"
        if [ "$actual" != "$expected" ]; then
            fail "SHA-256 MISMATCH for $1
  expected: $expected
  actual:   $actual
The download is corrupt or has been tampered with. NOT installing."
        fi
        echo "verified  $1  (sha256 ok)"
    }
    verify "$BIN"
    if [ "$WITH_ROUTER" -eq 1 ]; then
        verify "$ROUTER_BIN"
    fi

    # ---- install ------------------------------------------------------------
    mkdir -p "$INSTALL_DIR" || fail "could not create ${INSTALL_DIR}"

    # Stage next to the target and mv into place: atomic, and safe to replace
    # a binary that is currently running.
    install_bin() {
        cp "${tmp}/$1" "${INSTALL_DIR}/.$2.new" || fail "could not copy $2 into ${INSTALL_DIR}"
        chmod 0755 "${INSTALL_DIR}/.$2.new" || fail "could not chmod ${INSTALL_DIR}/$2"
        mv -f "${INSTALL_DIR}/.$2.new" "${INSTALL_DIR}/$2" || fail "could not install ${INSTALL_DIR}/$2"
    }

    install_bin "$BIN" alertavuln
    # 'av' convenience alias: symlink where possible, plain copy otherwise.
    if ! ln -sf alertavuln "${INSTALL_DIR}/av" 2>/dev/null; then
        cp "${INSTALL_DIR}/alertavuln" "${INSTALL_DIR}/av" || fail "could not create the 'av' alias"
    fi
    if [ "$WITH_ROUTER" -eq 1 ]; then
        install_bin "$ROUTER_BIN" mcp-router
    fi

    # ---- wrap up ------------------------------------------------------------
    echo ""
    echo "AlertaVuln CLI ${VERSION} installed: ${INSTALL_DIR}/alertavuln (alias: av)"
    if [ "$WITH_ROUTER" -eq 1 ]; then
        echo "MCP router installed:              ${INSTALL_DIR}/mcp-router"
    fi

    case ":${PATH}:" in
        *:"${INSTALL_DIR}":*)
            ;;
        *)
            echo ""
            echo "NOTE: ${INSTALL_DIR} is not on your PATH. Add it to your shell profile:"
            echo "  export PATH=\"\$HOME/.local/bin:\$PATH\""
            ;;
    esac

    echo ""
    echo "Next steps:"
    echo "  av login                   # authenticate"
    echo "  av check npm vite 6.0.0    # vet a package before you adopt it"
    echo ""
    echo "Docs: https://docs.alertavuln.com"
}

main "$@"
