#!/usr/bin/env sh
set -eu

BASE_URL="${BMCLI_BASE_URL:-https://download.busmust.com/bmcli}"
INSTALL_ROOT="${BMCLI_INSTALL_ROOT:-$HOME/.local/share/bmcli}"
BIN_ROOT="${BMCLI_BIN_ROOT:-$HOME/.local/bin}"
MAN_ROOT="${BMCLI_MAN_ROOT:-$HOME/.local/share/man/man1}"
VERSION="${BMCLI_VERSION:-}"
PLATFORM="${BMCLI_PLATFORM:-}"
PACKAGE_PATH=""
PAYLOAD_PATH=""
NO_AI=0
INSTALL_UDEV=0

usage() {
    cat <<'EOF'
Usage: install.sh [options]
  --package FILE       Install a local official BMCLI tar.gz package
  --payload DIRECTORY  Install an already extracted BMCLI package
  --version VERSION    Install a specific online release (X.Y.Z)
  --platform PLATFORM  Override the detected Linux platform
  --install-root DIR   Versioned installation root
  --bin-root DIR       Directory receiving the bmcli symlink
  --man-root DIR       Directory receiving the bmcli(1) symlink
  --no-ai              Do not run 'bmcli ai setup'
  --install-udev       Install the bundled BUSMUST USB udev rule with sudo
EOF
}

while [ "$#" -gt 0 ]; do
    case "$1" in
        --package) PACKAGE_PATH="$2"; shift 2 ;;
        --payload) PAYLOAD_PATH="$2"; shift 2 ;;
        --version) VERSION="$2"; shift 2 ;;
        --platform) PLATFORM="$2"; shift 2 ;;
        --install-root) INSTALL_ROOT="$2"; shift 2 ;;
        --bin-root) BIN_ROOT="$2"; shift 2 ;;
        --man-root) MAN_ROOT="$2"; shift 2 ;;
        --no-ai) NO_AI=1; shift ;;
        --install-udev) INSTALL_UDEV=1; shift ;;
        --help|-h) usage; exit 0 ;;
        *) echo "Unknown option: $1" >&2; usage >&2; exit 1 ;;
    esac
done

valid_version() {
    printf '%s\n' "$1" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'
}

detect_platform() {
    machine=$(uname -m)
    case "$machine" in
        x86_64|amd64) echo linux-x86_64 ;;
        aarch64|arm64) echo linux-aarch64 ;;
        armv7l|armv6l) echo linux-armhf ;;
        *) echo "Unsupported Linux architecture: $machine" >&2; exit 1 ;;
    esac
}

verify_tar_paths() {
    archive=$1
    listing=$2
    tar -tzf "$archive" > "$listing"
    if awk '/^\// || /(^|\/)\.\.($|\/)/ { bad=1 } END { exit bad ? 0 : 1 }' "$listing"; then
        echo "Package contains an unsafe archive path" >&2
        exit 1
    fi
}

[ -n "$PLATFORM" ] || PLATFORM=$(detect_platform)
case "$PLATFORM" in
    linux-x86_64|linux-aarch64|linux-armhf) ;;
    *) echo "Unsupported Linux platform: $PLATFORM" >&2; exit 1 ;;
esac
[ -z "$VERSION" ] || valid_version "$VERSION" || {
    echo "Invalid BMCLI version '$VERSION'; expected X.Y.Z" >&2; exit 1;
}

SCRIPT_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
if [ -z "$PAYLOAD_PATH" ] && [ -x "$SCRIPT_DIR/bin/bmcli" ] && [ -f "$SCRIPT_DIR/VERSION" ]; then
    PAYLOAD_PATH="$SCRIPT_DIR"
fi

tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT HUP INT TERM

if [ -n "$PAYLOAD_PATH" ]; then
    payload=$(CDPATH= cd -- "$PAYLOAD_PATH" && pwd)
elif [ -n "$PACKAGE_PATH" ]; then
    command -v tar >/dev/null 2>&1 || { echo "tar is required" >&2; exit 1; }
    package=$(CDPATH= cd -- "$(dirname -- "$PACKAGE_PATH")" && pwd)/$(basename -- "$PACKAGE_PATH")
    [ -f "$package" ] || { echo "Package not found: $package" >&2; exit 1; }
    mkdir -p "$tmp/extract"
    verify_tar_paths "$package" "$tmp/archive.list"
    tar -xzf "$package" -C "$tmp/extract"
    roots=$(find "$tmp/extract" -mindepth 1 -maxdepth 1 -type d -name 'bmcli-*' -print)
    [ "$(printf '%s\n' "$roots" | sed '/^$/d' | wc -l)" -eq 1 ] || {
        echo "Invalid BMCLI package layout" >&2; exit 1;
    }
    payload=$roots
else
    command -v curl >/dev/null 2>&1 || { echo "curl is required" >&2; exit 1; }
    command -v sha256sum >/dev/null 2>&1 || { echo "sha256sum is required" >&2; exit 1; }
    command -v tar >/dev/null 2>&1 || { echo "tar is required" >&2; exit 1; }
    if command -v python3 >/dev/null 2>&1; then python_cmd=python3
    elif command -v python >/dev/null 2>&1; then python_cmd=python
    else echo "python3 is required to parse the release manifest" >&2; exit 1
    fi

    manifest="$tmp/manifest.json"
    if [ -n "$VERSION" ]; then
        curl -fsSL "$BASE_URL/releases/$VERSION/manifest.json" -o "$manifest"
    else
        curl -fsSL "$BASE_URL/releases/latest/manifest.json" -o "$manifest"
    fi
    requested_version=$VERSION
    eval "$($python_cmd - "$manifest" "$PLATFORM" <<'PY'
import json, re, shlex, sys
with open(sys.argv[1], encoding="utf-8-sig") as stream:
    manifest = json.load(stream)
if manifest.get("schema_version") != 1:
    raise SystemExit("unsupported manifest schema")
version = str(manifest.get("version", ""))
if not re.fullmatch(r"[0-9]+\.[0-9]+\.[0-9]+", version):
    raise SystemExit("manifest contains an invalid version")
artifact = manifest.get("artifacts", {}).get(sys.argv[2])
if not artifact:
    raise SystemExit("release has no artifact for " + sys.argv[2])
sha256 = str(artifact.get("sha256", ""))
if not re.fullmatch(r"[0-9a-fA-F]{64}", sha256):
    raise SystemExit("manifest contains an invalid SHA-256")
for key, value in (("VERSION", version), ("URL", artifact["url"]), ("SHA256", sha256)):
    print(key + "=" + shlex.quote(str(value)))
PY
)"
    [ -z "$requested_version" ] || [ "$requested_version" = "$VERSION" ] || {
        echo "Manifest version '$VERSION' does not match requested version '$requested_version'" >&2; exit 1;
    }

    archive="$tmp/bmcli.tar.gz"
    curl -fsSL "$URL" -o "$archive"
    printf '%s  %s\n' "$SHA256" "$archive" | sha256sum -c - >/dev/null
    mkdir -p "$tmp/extract"
    verify_tar_paths "$archive" "$tmp/archive.list"
    tar -xzf "$archive" -C "$tmp/extract"
    payload="$tmp/extract/bmcli-$VERSION-$PLATFORM"
fi

[ -x "$payload/bin/bmcli" ] || { echo "Invalid BMCLI package: bin/bmcli is not executable" >&2; exit 1; }
for required in VERSION BUILD_INFO.json share/bmcli; do
    [ -e "$payload/$required" ] || { echo "Invalid BMCLI package: $required is missing" >&2; exit 1; }
done
payload_version=$(sed -n '1p' "$payload/VERSION" | tr -d '\r')
valid_version "$payload_version" || { echo "Package contains an invalid VERSION" >&2; exit 1; }
[ -n "$VERSION" ] || VERSION=$payload_version
[ "$payload_version" = "$VERSION" ] || {
    echo "Package VERSION '$payload_version' does not match requested version '$VERSION'" >&2; exit 1;
}
build_version=$(sed -n 's/.*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$payload/BUILD_INFO.json")
build_number=$(sed -n 's/.*"build_number"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$payload/BUILD_INFO.json")
git_commit=$(sed -n 's/.*"git_commit"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' "$payload/BUILD_INFO.json")
[ "$build_version" = "$VERSION" ] && [ -n "$build_number" ] && [ -n "$git_commit" ] || {
    echo "Invalid or inconsistent BUILD_INFO.json" >&2; exit 1;
}
binary_info=$(LD_LIBRARY_PATH="$payload/bin${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" "$payload/bin/bmcli" version --format=json)
printf '%s' "$binary_info" | grep -Fq "\"version\":\"$VERSION\"" &&
printf '%s' "$binary_info" | grep -Fq "\"build_number\":\"$build_number\"" &&
printf '%s' "$binary_info" | grep -Fq "\"git_commit\":\"$git_commit\"" || {
    echo "BMCLI binary metadata does not match VERSION and BUILD_INFO" >&2; exit 1;
}

mkdir -p "$INSTALL_ROOT/versions" "$BIN_ROOT"
version_root="$INSTALL_ROOT/versions/$VERSION"
staging_root="$INSTALL_ROOT/versions/.$VERSION.new.$$"
old_root="$INSTALL_ROOT/versions/.$VERSION.old.$$"
rm -rf "$staging_root" "$old_root"
cp -R "$payload" "$staging_root"
if [ -e "$version_root" ]; then mv "$version_root" "$old_root"; fi
if ! mv "$staging_root" "$version_root"; then
    [ ! -e "$old_root" ] || mv "$old_root" "$version_root"
    exit 1
fi
ln -sfn "$version_root" "$INSTALL_ROOT/current.new"
mv -Tf "$INSTALL_ROOT/current.new" "$INSTALL_ROOT/current"
ln -sfn "$INSTALL_ROOT/current/bin/bmcli" "$BIN_ROOT/bmcli"
if [ -f "$INSTALL_ROOT/current/share/man/man1/bmcli.1" ]; then
    mkdir -p "$MAN_ROOT"
    ln -sfn "$INSTALL_ROOT/current/share/man/man1/bmcli.1" "$MAN_ROOT/bmcli.1"
fi
rm -rf "$old_root"

if [ "$INSTALL_UDEV" -eq 1 ]; then
    rule="$version_root/share/bmcli/udev/99-busmust.rules"
    [ -f "$rule" ] || { echo "Bundled udev rule is missing" >&2; exit 1; }
    command -v sudo >/dev/null 2>&1 || { echo "sudo is required for --install-udev" >&2; exit 1; }
    sudo install -m 0644 "$rule" /etc/udev/rules.d/99-busmust.rules
    sudo udevadm control --reload-rules
    sudo udevadm trigger --subsystem-match=usb
    echo "BUSMUST udev rule installed; reconnect the device before use."
fi

if [ "$NO_AI" -eq 0 ]; then "$BIN_ROOT/bmcli" ai setup; fi
printf '%s\n' "$VERSION" > "$INSTALL_ROOT/.install-complete"
printf 'BMCLI %s installed. Run: bmcli doctor\n' "$VERSION"
