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

ASR_REPO="${ASR_REPO:-https://github.com/AxiomOrient/ASR}"
ASR_PACKAGE="${ASR_PACKAGE:-agent-source-repository}"
ASR_RELEASE_TAG="${ASR_RELEASE_TAG:-v0.1.0}"
ASR_RELEASE_BASE_URL="${ASR_RELEASE_BASE_URL:-${ASR_REPO}/releases/download/${ASR_RELEASE_TAG}}"
ASR_INSTALL_METHOD="${ASR_INSTALL_METHOD:-auto}"
ASR_INSTALL_ROOT="${ASR_INSTALL_ROOT:-}"
ASR_BIN_DIR="${ASR_BIN_DIR:-}"
ASR_SOURCE_PATH="${ASR_SOURCE_PATH:-}"
ASR_SKIP_RUSTUP="${ASR_SKIP_RUSTUP:-0}"
ASR_SKIP_COMPILER_CHECK="${ASR_SKIP_COMPILER_CHECK:-0}"
ASR_TMP_ROOT=""

SCRIPT_DIR="$(CDPATH= cd "$(dirname "$0")" && pwd -P)"
DEFAULT_SOURCE_PATH="$(CDPATH= cd "$SCRIPT_DIR/.." && pwd -P)"
if [ -z "$ASR_SOURCE_PATH" ] && [ -f "$DEFAULT_SOURCE_PATH/Cargo.toml" ] && [ -f "$DEFAULT_SOURCE_PATH/src/bin/asr.rs" ]; then
  ASR_SOURCE_PATH="$DEFAULT_SOURCE_PATH"
fi

log() {
  printf '%s\n' "$*"
}

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

has_cmd() {
  command -v "$1" >/dev/null 2>&1
}

need_cmd() {
  has_cmd "$1" || fail "missing required command: $1"
}

cleanup() {
  if [ -n "$ASR_TMP_ROOT" ]; then
    rm -rf "$ASR_TMP_ROOT"
  fi
}

make_tmp_root() {
  if [ -z "$ASR_TMP_ROOT" ]; then
    ASR_TMP_ROOT="${TMPDIR:-/tmp}/asr-install.$$"
    mkdir -p "$ASR_TMP_ROOT"
    trap cleanup EXIT HUP INT TERM
  fi
}

target_triple() {
  os="$(uname -s 2>/dev/null || printf unknown)"
  arch="$(uname -m 2>/dev/null || printf unknown)"

  case "$os-$arch" in
    Darwin-arm64) printf '%s\n' "aarch64-apple-darwin" ;;
    Darwin-x86_64) printf '%s\n' "x86_64-apple-darwin" ;;
    Linux-x86_64) printf '%s\n' "x86_64-unknown-linux-gnu" ;;
    Linux-aarch64) printf '%s\n' "aarch64-unknown-linux-gnu" ;;
    *) return 1 ;;
  esac
}

install_dir() {
  if [ -n "$ASR_INSTALL_ROOT" ]; then
    printf '%s\n' "$ASR_INSTALL_ROOT/bin"
  elif [ -n "$ASR_BIN_DIR" ]; then
    printf '%s\n' "$ASR_BIN_DIR"
  else
    printf '%s\n' "$HOME/.cargo/bin"
  fi
}

ensure_compiler() {
  if [ "$ASR_SKIP_COMPILER_CHECK" = "1" ]; then
    return
  fi

  if has_cmd cc || has_cmd clang || has_cmd gcc; then
    return
  fi

  fail "ASR dependencies compile native code; install a C compiler first"
}

ensure_cargo() {
  if has_cmd cargo; then
    return
  fi

  if [ "$ASR_SKIP_RUSTUP" = "1" ]; then
    fail "cargo was not found and ASR_SKIP_RUSTUP=1 is set"
  fi

  need_cmd curl
  log "Installing Rust stable toolchain with rustup..."
  curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
    | sh -s -- -y --profile minimal --default-toolchain stable

  if [ -f "$HOME/.cargo/env" ]; then
    # shellcheck disable=SC1090
    . "$HOME/.cargo/env"
  fi

  has_cmd cargo || fail "cargo was not found after rustup installation"
}

try_install_release_binary() {
  triple="$(target_triple)" || return 1
  asset="asr-${ASR_RELEASE_TAG}-${triple}.tar.gz"
  url="${ASR_RELEASE_BASE_URL}/${asset}"

  make_tmp_root
  archive="$ASR_TMP_ROOT/$asset"
  extract_dir="$ASR_TMP_ROOT/extract"
  mkdir -p "$extract_dir"

  log "Trying ASR release binary: $url"
  if ! curl -fL -o "$archive" "$url"; then
    return 1
  fi

  tar -xzf "$archive" -C "$extract_dir"
  [ -x "$extract_dir/asr" ] || fail "release archive did not contain an executable asr binary"

  bin_dir="$(install_dir)"
  mkdir -p "$bin_dir"
  cp "$extract_dir/asr" "$bin_dir/asr"
  chmod +x "$bin_dir/asr"
  ASR_BIN="$bin_dir/asr"
}

install_from_local_source() {
  [ -n "$ASR_SOURCE_PATH" ] || return 1
  [ -f "$ASR_SOURCE_PATH/Cargo.toml" ] || return 1
  [ -f "$ASR_SOURCE_PATH/src/bin/asr.rs" ] || return 1

  ensure_compiler
  ensure_cargo

  log "Installing ASR from local checkout: $ASR_SOURCE_PATH"
  if [ -n "$ASR_INSTALL_ROOT" ]; then
    cargo install --path "$ASR_SOURCE_PATH" --locked --force --root "$ASR_INSTALL_ROOT" --bin asr
    ASR_BIN="$ASR_INSTALL_ROOT/bin/asr"
  else
    cargo install --path "$ASR_SOURCE_PATH" --locked --force --bin asr
    ASR_BIN="$HOME/.cargo/bin/asr"
  fi
}

install_from_crates() {
  ensure_compiler
  ensure_cargo

  log "Installing ASR from crates.io package: $ASR_PACKAGE"
  if [ -n "$ASR_INSTALL_ROOT" ]; then
    cargo install "$ASR_PACKAGE" --locked --force --root "$ASR_INSTALL_ROOT" --bin asr
    ASR_BIN="$ASR_INSTALL_ROOT/bin/asr"
  else
    cargo install "$ASR_PACKAGE" --locked --force --bin asr
    if [ -x "$HOME/.cargo/bin/asr" ]; then
      ASR_BIN="$HOME/.cargo/bin/asr"
    else
      ASR_BIN="$(command -v asr || true)"
    fi
  fi
}

install_from_git() {
  need_cmd git
  ensure_compiler
  ensure_cargo

  log "Installing ASR from Git repository: $ASR_REPO"
  if [ -n "$ASR_INSTALL_ROOT" ]; then
    cargo install --git "$ASR_REPO" --locked --force --root "$ASR_INSTALL_ROOT" --bin asr
    ASR_BIN="$ASR_INSTALL_ROOT/bin/asr"
  else
    cargo install --git "$ASR_REPO" --locked --force --bin asr
    if [ -x "$HOME/.cargo/bin/asr" ]; then
      ASR_BIN="$HOME/.cargo/bin/asr"
    else
      ASR_BIN="$(command -v asr || true)"
    fi
  fi
}

install_asr() {
  case "$ASR_INSTALL_METHOD" in
    auto)
      if try_install_release_binary; then
        return
      fi
      log "ASR release binary unavailable for this platform."
      if install_from_local_source; then
        return
      fi
      log "Local ASR checkout unavailable; falling back to crates.io install."
      ;;
    binary)
      try_install_release_binary || fail "ASR release binary install failed for this platform"
      return
      ;;
    cargo)
      install_from_crates
      return
      ;;
    git)
      ;;
    path)
      install_from_local_source || fail "ASR local source install failed; set ASR_SOURCE_PATH to a checkout"
      return
      ;;
    *)
      fail "unknown ASR_INSTALL_METHOD: $ASR_INSTALL_METHOD"
      ;;
  esac

  install_from_crates

  [ -n "${ASR_BIN:-}" ] || fail "asr was installed but was not found on PATH"
  [ -x "$ASR_BIN" ] || fail "asr binary is not executable: $ASR_BIN"
}

smoke_asr() {
  need_cmd git
  smoke_root="${TMPDIR:-/tmp}/asr-smoke.$$"
  asr_home="$smoke_root/asr-home"
  repo="$smoke_root/repo"
  mkdir -p "$repo/src"
  trap 'rm -rf "$smoke_root"; cleanup' EXIT HUP INT TERM

  git -C "$repo" init -q
  git -C "$repo" config user.email "asr@example.invalid"
  git -C "$repo" config user.name "ASR Smoke"
  printf '%s\n' 'pub fn retry_backoff() -> u64 { 100 }' >"$repo/src/retry.rs"
  git -C "$repo" add .
  git -C "$repo" commit -q -m "initial"

  ASR_HOME="$asr_home" "$ASR_BIN" init --json >/dev/null
  ASR_HOME="$asr_home" "$ASR_BIN" repo add app "$repo" --json >/dev/null
  ASR_HOME="$asr_home" "$ASR_BIN" repo index app --json >/dev/null
  ASR_HOME="$asr_home" "$ASR_BIN" search retry_backoff --repo app --json >/dev/null
  ASR_HOME="$asr_home" "$ASR_BIN" verify app --json >/dev/null
}

main() {
  need_cmd uname
  need_cmd curl
  need_cmd tar
  install_asr
  "$ASR_BIN" --help >/dev/null
  smoke_asr

  log "ASR installed: $ASR_BIN"
  log "Try: ASR_HOME=\$(mktemp -d) $ASR_BIN init --json"
}

main "$@"
