#!/usr/bin/env sh

set -euf

REPO=${DISTRI_REPO:-distrihub/distri}
GIT_URL="https://github.com/${REPO}"

log() {
  printf "distri-install: %s\n" "$*" >&2
}

fatal() {
  log "$1"
  exit 1
}

usage() {
  cat >&2 <<'EOF'
distri installer

Usage:
  curl -fsSL https://distri.dev/install.sh | sh
  curl -fsSL https://distri.dev/install.sh | sh -s -- [OPTIONS]

Options:
  -y, --yes               Non-interactive: auto-approve building from source and
                          installing the Rust toolchain (rustup) if it is missing.
      --build-from-source Skip the prebuilt download and build from source.
  -h, --help              Show this help and exit.

Environment:
  DISTRI_VERSION              Release tag to install (default: latest).
  DISTRI_INSTALL_DIR          Directory to install into.
  DISTRI_YES=1                Same as --yes.
  DISTRI_BUILD_FROM_SOURCE=1  Same as --build-from-source.
  DISTRI_REPO                 GitHub repo, owner/name (default: distrihub/distri).
EOF
}

require_cmd() {
  if ! command -v "$1" >/dev/null 2>&1; then
    fatal "missing required dependency: $1"
  fi
}

# prompt_yes_no PROMPT
#   Returns 0 for yes, 1 for an explicit no, 2 when there is no way to ask.
#   Reads from /dev/tty so it works under `curl ... | sh` (where stdin is the
#   pipe, not the terminal). DISTRI_YES / --yes short-circuits to yes.
prompt_yes_no() {
  if [ -n "$ASSUME_YES" ]; then
    return 0
  fi
  # Probe whether an interactive terminal is actually usable. `-r /dev/tty` is
  # not enough: the device node can exist without a controlling terminal (e.g.
  # under setsid or in CI), so try to actually open it before prompting.
  if ! { : </dev/tty; } 2>/dev/null; then
    return 2
  fi
  printf "distri-install: %s [y/N] " "$1" >&2
  ans=""
  read ans </dev/tty 2>/dev/null || return 2
  case "$ans" in
    y | Y | yes | YES | Yes) return 0 ;;
    *) return 1 ;;
  esac
}

require_cmd curl
require_cmd uname
require_cmd mktemp
require_cmd install

ASSUME_YES=${DISTRI_YES:-}
FORCE_SOURCE=${DISTRI_BUILD_FROM_SOURCE:-}

for arg in "$@"; do
  case "$arg" in
    -y | --yes) ASSUME_YES=1 ;;
    --build-from-source) FORCE_SOURCE=1 ;;
    -h | --help) usage; exit 0 ;;
    *) log "ignoring unknown option: $arg" ;;
  esac
done

OS=$(uname -s 2>/dev/null || echo unknown)
case "$OS" in
  Darwin) PLATFORM="darwin" ;;
  Linux) PLATFORM="linux" ;;
  *) PLATFORM=""; log "no prebuilt binaries available for OS: $OS" ;;
esac

ARCH_RAW=$(uname -m 2>/dev/null || echo unknown)
case "$ARCH_RAW" in
  x86_64 | amd64) ARCH="x86_64"; ASSET_ARCH="amd64" ;;
  arm64 | aarch64) ARCH="arm64"; ASSET_ARCH="arm64" ;;
  *) ARCH="$ARCH_RAW"; ASSET_ARCH=""; log "no prebuilt binaries available for architecture: $ARCH_RAW" ;;
esac

NO_PREBUILT=""
if [ -z "$PLATFORM" ] || [ -z "$ASSET_ARCH" ]; then
  NO_PREBUILT=1
fi

choose_install_dir() {
  if [ -n "${DISTRI_INSTALL_DIR:-}" ]; then
    printf "%s\n" "$DISTRI_INSTALL_DIR"
    return
  fi

  if [ "$(id -u 2>/dev/null || echo 1)" -eq 0 ]; then
    printf "/usr/local/bin\n"
    return
  fi

  if [ -d "/usr/local/bin" ] && [ -w "/usr/local/bin" ]; then
    printf "/usr/local/bin\n"
    return
  fi

  if [ ! -d "/usr/local/bin" ]; then
    if mkdir -p "/usr/local/bin" 2>/dev/null; then
      printf "/usr/local/bin\n"
      return
    fi
  fi

  printf "%s\n" "$HOME/.local/bin"
}

INSTALL_DIR=$(choose_install_dir)
mkdir -p "$INSTALL_DIR"

TMPDIR=$(mktemp -d 2>/dev/null || mktemp -d -t distri-install)
trap 'rm -rf "$TMPDIR"' EXIT INT TERM

INSTALLED_SERVER=""

# attempt_prebuilt
#   Downloads and installs the prebuilt binaries. Returns 1 (rather than
#   exiting) when the platform is unsupported or the download/extract fails,
#   so the caller can fall back to a source build.
attempt_prebuilt() {
  for c in tar find; do
    if ! command -v "$c" >/dev/null 2>&1; then
      log "missing '$c'; cannot use prebuilt archive."
      return 1
    fi
  done

  VERSION=${DISTRI_VERSION:-latest}
  ASSET="distri-${PLATFORM}-${ASSET_ARCH}.tar.gz"
  SLUG="${PLATFORM}-${ASSET_ARCH}"

  if [ "$VERSION" = "latest" ]; then
    TAG_LABEL="latest"
    DOWNLOAD_URL="https://github.com/${REPO}/releases/latest/download/${ASSET}"
  else
    case "$VERSION" in
      v*) TAG="$VERSION" ;;
      *) TAG="v$VERSION" ;;
    esac
    TAG_LABEL="$TAG"
    DOWNLOAD_URL="https://github.com/${REPO}/releases/download/${TAG}/${ASSET}"
  fi

  TARBALL="$TMPDIR/$ASSET"

  log "Installing Distri (${TAG_LABEL}) for ${PLATFORM}/${ARCH} into ${INSTALL_DIR}"
  log "Downloading ${DOWNLOAD_URL}"

  if ! curl -fL "$DOWNLOAD_URL" -o "$TARBALL"; then
    log "download failed for ${DOWNLOAD_URL}"
    return 1
  fi

  if ! tar -xzf "$TARBALL" -C "$TMPDIR"; then
    log "failed to extract archive."
    return 1
  fi

  EXTRACT_ROOT="$TMPDIR/$SLUG"
  BIN_PATH="$EXTRACT_ROOT/distri"
  if [ ! -f "$BIN_PATH" ]; then
    BIN_PATH=$(find "$TMPDIR" -type f -name "distri" | head -n 1)
  fi
  if [ -z "$BIN_PATH" ]; then
    fatal "distri binary not found in downloaded archive."
  fi

  install -m 0755 "$BIN_PATH" "$INSTALL_DIR/distri" \
    || fatal "failed to install distri to ${INSTALL_DIR}."

  SERVER_BIN_PATH="$EXTRACT_ROOT/server/distri-server"
  if [ ! -f "$SERVER_BIN_PATH" ]; then
    SERVER_BIN_PATH=$(find "$TMPDIR" -type f -name "distri-server" | head -n 1)
  fi
  if [ -n "$SERVER_BIN_PATH" ]; then
    install -m 0755 "$SERVER_BIN_PATH" "$INSTALL_DIR/distri-server" \
      || fatal "failed to install distri-server to ${INSTALL_DIR}."
    INSTALLED_SERVER=1
  fi

  return 0
}

# ensure_cargo
#   Guarantees `cargo` is available, installing the Rust toolchain via rustup
#   if needed (after confirmation, unless --yes was given).
ensure_cargo() {
  if command -v cargo >/dev/null 2>&1; then
    return 0
  fi

  # cargo may be installed but not yet on PATH (e.g. a prior rustup install).
  if [ -r "$HOME/.cargo/env" ]; then
    # shellcheck disable=SC1091
    . "$HOME/.cargo/env"
    if command -v cargo >/dev/null 2>&1; then
      return 0
    fi
  fi

  log "Rust toolchain (cargo) is required to build from source."
  if prompt_yes_no "Install the Rust toolchain via rustup now?"; then
    :
  else
    rc=$?
    if [ "$rc" -eq 2 ]; then
      fatal "cargo not found and no terminal to confirm. Install Rust from https://rustup.rs and re-run, or pass '-y' to auto-install."
    fi
    fatal "cargo is required to build from source. Install Rust from https://rustup.rs and re-run."
  fi

  log "Installing Rust toolchain via rustup..."
  if ! curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y; then
    fatal "rustup installation failed."
  fi
  if [ -r "$HOME/.cargo/env" ]; then
    # shellcheck disable=SC1091
    . "$HOME/.cargo/env"
  fi
  if ! command -v cargo >/dev/null 2>&1; then
    fatal "cargo still not found after installing rustup. Ensure \$HOME/.cargo/bin is on your PATH and re-run."
  fi
}

build_from_source() {
  ensure_cargo

  CARGO_ROOT="$TMPDIR/cargo"
  log "Building distri from source with cargo (this can take several minutes)..."
  log "Running: cargo install --git ${GIT_URL} distri-cli distri-server-cli"
  if ! cargo install --git "$GIT_URL" --root "$CARGO_ROOT" distri-cli distri-server-cli; then
    fatal "cargo build failed."
  fi

  if [ -f "$CARGO_ROOT/bin/distri" ]; then
    install -m 0755 "$CARGO_ROOT/bin/distri" "$INSTALL_DIR/distri" \
      || fatal "failed to install distri to ${INSTALL_DIR}."
  else
    fatal "built distri binary not found."
  fi

  if [ -f "$CARGO_ROOT/bin/distri-server" ]; then
    install -m 0755 "$CARGO_ROOT/bin/distri-server" "$INSTALL_DIR/distri-server" \
      || fatal "failed to install distri-server to ${INSTALL_DIR}."
    INSTALLED_SERVER=1
  fi
}

PREBUILT_OK=""
if [ -n "$FORCE_SOURCE" ]; then
  log "Skipping prebuilt download (--build-from-source)."
elif [ -n "$NO_PREBUILT" ]; then
  log "No prebuilt binary is available for this platform."
else
  if attempt_prebuilt; then
    PREBUILT_OK=1
  fi
fi

if [ -z "$PREBUILT_OK" ]; then
  # Reached when forced, when the platform has no prebuilt binary, or when the
  # download failed. Confirm before building from source (unless --yes/forced).
  if [ -z "$FORCE_SOURCE" ]; then
    if prompt_yes_no "Prebuilt binary unavailable. Build distri from source with cargo?"; then
      :
    else
      rc=$?
      if [ "$rc" -eq 2 ]; then
        fatal "prebuilt binary unavailable and no terminal to confirm a source build. Re-run with '-y' (e.g. 'curl -fsSL https://distri.dev/install.sh | sh -s -- -y') or set DISTRI_YES=1."
      fi
      fatal "aborted: prebuilt binary unavailable and source build declined."
    fi
  fi
  build_from_source
fi

if ! printf "%s" "$PATH" | tr ":" "\n" | grep -qx "$INSTALL_DIR"; then
  log "added distri to ${INSTALL_DIR}, but that directory is not on your PATH."
  log "Add the following line to your shell profile:"
  log "  export PATH=\"$INSTALL_DIR:\$PATH\""
fi

if [ -n "$INSTALLED_SERVER" ]; then
  log "Distri and distri-server installed successfully. Run 'distri --version' to verify."
else
  log "Distri installed successfully. Run 'distri --version' to verify."
fi
