#!/bin/sh
# install.sh: install the rippit CLI binary and wire up PATH via
# ~/.rippit/env (rustup-style env script + a single source line in
# the user's shell rc files).
#
# Usage:
#   curl -fsSL https://rippit.sh | sh
#   curl -fsSL https://rippit.sh | sh -s -- --version v0.2.0
#   curl -fsSL https://rippit.sh | sh -s -- --prefix /usr/local
#
# To install AND instrument the current project:
#   curl -fsSL https://rippit.sh/instrument | sh
#
# Environment variables:
#   RIPPIT_INSTALL_BASE         download host (default: https://rippit.sh)
#   RIPPIT_INSTALL_PREFIX       install prefix (default: $HOME/.local)
#   RIPPIT_VERSION              tagged version to install (default: latest)
#   RIPPIT_INSTALL_NO_PATH_EDIT skip rc-file edits

set -eu

BIN_NAME="rippit"
DEFAULT_BASE="https://rippit.sh"
DEFAULT_PREFIX="${HOME}/.local"

BASE="${RIPPIT_INSTALL_BASE:-$DEFAULT_BASE}"
PREFIX="${RIPPIT_INSTALL_PREFIX:-$DEFAULT_PREFIX}"
VERSION="${RIPPIT_VERSION:-latest}"

show_help() {
  # Inlined: `curl ... | sh -s -- --help` has $0 = /bin/sh, not the script.
  cat <<'HELP'
install.sh: install the rippit CLI.

Usage:
  curl -fsSL https://rippit.sh | sh
  curl -fsSL https://rippit.sh | sh -s -- --version v0.2.0
  curl -fsSL https://rippit.sh | sh -s -- --prefix /usr/local

To install AND instrument: curl -fsSL https://rippit.sh/instrument | sh

Environment variables:
  RIPPIT_INSTALL_BASE         download host (default: https://rippit.sh)
  RIPPIT_INSTALL_PREFIX       install prefix (default: $HOME/.local)
  RIPPIT_VERSION              tagged version (default: latest)
  RIPPIT_INSTALL_NO_PATH_EDIT skip rc-file edits
HELP
}

while [ $# -gt 0 ]; do
  case "$1" in
    --version) VERSION="$2"; shift 2 ;;
    --prefix)  PREFIX="$2"; shift 2 ;;
    --base)    BASE="$2"; shift 2 ;;
    -h|--help) show_help; exit 0 ;;
    *) echo "unknown argument: $1" >&2; exit 2 ;;
  esac
done

log()  { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
warn() { printf '\033[1;33m==>\033[0m %s\n' "$*" >&2; }
die()  { printf '\033[1;31m==>\033[0m %s\n' "$*" >&2; exit 1; }

need() { command -v "$1" >/dev/null 2>&1 || die "missing required tool: $1"; }
need uname
need tar
need mkdir
if command -v curl >/dev/null 2>&1; then
  fetch() { curl -fsSL "$1" -o "$2"; }
elif command -v wget >/dev/null 2>&1; then
  fetch() { wget -qO "$2" "$1"; }
else
  die "need curl or wget"
fi

detect_os() {
  case "$(uname -s)" in
    Darwin) echo darwin ;;
    Linux)  echo linux ;;
    *) die "unsupported OS: $(uname -s)" ;;
  esac
}

detect_arch() {
  case "$(uname -m)" in
    x86_64|amd64) echo amd64 ;;
    arm64|aarch64) echo arm64 ;;
    *) die "unsupported architecture: $(uname -m)" ;;
  esac
}

resolve_version() {
  if [ "$VERSION" = "latest" ]; then
    tmp="$(mktemp)"
    fetch "${BASE}/cli/latest" "$tmp" || die "failed to resolve latest version from ${BASE}/cli/latest"
    VERSION="$(tr -d '[:space:]' < "$tmp")"
    rm -f "$tmp"
    [ -n "$VERSION" ] || die "latest version response was empty"
  fi
}

install_binary() {
  os="$(detect_os)"
  arch="$(detect_arch)"
  resolve_version

  tarball="${BIN_NAME}_${VERSION}_${os}_${arch}.tar.gz"
  url="${BASE}/cli/${VERSION}/${tarball}"

  tmpdir="$(mktemp -d)"
  trap 'rm -rf "$tmpdir"' EXIT

  log "downloading ${url}"
  fetch "$url" "${tmpdir}/${tarball}" || die "download failed: ${url}"

  log "extracting"
  tar -xzf "${tmpdir}/${tarball}" -C "$tmpdir"

  bin_src="${tmpdir}/${BIN_NAME}"
  [ -x "$bin_src" ] || die "extracted archive did not contain executable ${BIN_NAME}"

  bin_dest="${PREFIX}/bin"
  mkdir -p "$bin_dest"

  install -m 0755 "$bin_src" "${bin_dest}/${BIN_NAME}" 2>/dev/null \
    || cp "$bin_src" "${bin_dest}/${BIN_NAME}" \
    || die "failed to install to ${bin_dest}"
  chmod 0755 "${bin_dest}/${BIN_NAME}"

  log "installed ${BIN_NAME} ${VERSION} to ${bin_dest}/${BIN_NAME}"
  ensure_path "$bin_dest"
}

# Env scripts live at a fixed home location so the source line in rc
# files stays stable across reinstalls with different --prefix values.
RIPPIT_HOME="${HOME}/.rippit"
RIPPIT_ENV_POSIX="${RIPPIT_HOME}/env"
RIPPIT_ENV_FISH="${RIPPIT_HOME}/env.fish"
RIPPIT_ENV_CSH="${RIPPIT_HOME}/env.csh"

# Marker bookends the rc-file edit so reruns can dedupe and uninstall
# can find what to remove.
RIPPIT_RC_MARKER='# added by rippit installer'

manual_path_hint() {
  warn "${1} is not on your PATH."
  warn "Add this to your shell profile (~/.zshrc, ~/.bashrc, etc.):"
  printf '\n    export PATH="%s:$PATH"\n\n' "$1"
}

# write_env_scripts: emit the source-able env scripts. POSIX always;
# fish/csh only when $SHELL matches. Each script's PATH-guard makes
# double-source a no-op (matters because ~/.profile sources ~/.bashrc
# on Ubuntu, so a single shell startup may source the env twice).
write_env_scripts() {
  bin_dir="$1"
  shell_name="$2"

  # Embed $HOME-relative paths in the env scripts when bin_dir is
  # under $HOME, so the same env script works across machines with
  # different HOME values (chezmoi/stow, NFS mounts). Custom --prefix
  # outside $HOME (e.g. /usr/local) keeps the absolute path since
  # there's no portable form for it.
  case "$bin_dir" in
    "$HOME"/*) bin_literal='$HOME'"${bin_dir#$HOME}" ;;
    *)         bin_literal="$bin_dir" ;;
  esac

  mkdir -p "$RIPPIT_HOME" 2>/dev/null || return 1

  cat > "$RIPPIT_ENV_POSIX" 2>/dev/null <<EOF || return 1
#!/bin/sh
# rippit env script. Re-source or start a new shell after reinstall.
case ":\${PATH}:" in
    *":${bin_literal}:"*) ;;
    *) export PATH="${bin_literal}:\$PATH" ;;
esac
EOF
  log "wrote ${RIPPIT_ENV_POSIX}"

  if [ "$shell_name" = "fish" ]; then
    cat > "$RIPPIT_ENV_FISH" 2>/dev/null <<EOF || return 1
# rippit env (fish).
if not contains "${bin_literal}" \$PATH
    set -gx PATH "${bin_literal}" \$PATH
end
EOF
    log "wrote ${RIPPIT_ENV_FISH}"
  fi

  if [ "$shell_name" = "tcsh" ] || [ "$shell_name" = "csh" ]; then
    cat > "$RIPPIT_ENV_CSH" 2>/dev/null <<EOF || return 1
# rippit env (csh/tcsh). \${PATH} braces are required: a bare \$PATH
# next to a literal colon triggers csh's variable-modifier parser.
# Colon-wrap both sides to avoid prefix collisions (so /x/.local/bin
# doesn't false-match against /x/.local/bin-backup).
if (":\${PATH}:" !~ *":${bin_literal}:"*) then
    setenv PATH "${bin_literal}:\$PATH"
endif
EOF
    log "wrote ${RIPPIT_ENV_CSH}"
  fi

  return 0
}

# append_source_line: idempotent (marker-checked) add of one source
# line to an rc file. Leading newline avoids fusing onto an
# unterminated final line.
append_source_line() {
  rc_path="$1"
  rc_line="$2"

  if [ -f "$rc_path" ] && grep -qF "$RIPPIT_RC_MARKER" "$rc_path" 2>/dev/null; then
    log "rippit source line already present in ${rc_path} (skipping)"
    return 0
  fi

  rc_dir="$(dirname "$rc_path")"
  if [ ! -d "$rc_dir" ]; then
    mkdir -p "$rc_dir" 2>/dev/null || return 1
  fi

  printf '\n%s\n%s\n' "$RIPPIT_RC_MARKER" "$rc_line" >> "$rc_path" 2>/dev/null || return 1

  log "added rippit source line to ${rc_path}"
  return 0
}

# ensure_path: write env scripts, then add a source line to multiple
# rc files. Belt-and-suspenders covers users who switch shells or
# have a misleading $SHELL (cron, IDE terminals).
ensure_path() {
  bin_dir="$1"

  case ":${PATH}:" in
    *":${bin_dir}:"*) return 0 ;;
  esac

  if [ -n "${RIPPIT_INSTALL_NO_PATH_EDIT:-}" ]; then
    manual_path_hint "$bin_dir"
    return 0
  fi

  shell_name=""
  if [ -n "${SHELL:-}" ]; then
    shell_name="$(basename "$SHELL")"
  fi

  if ! write_env_scripts "$bin_dir" "$shell_name"; then
    warn "could not write env script under ${RIPPIT_HOME}; falling back to manual instructions"
    manual_path_hint "$bin_dir"
    return 0
  fi

  # Single-quoted: literal $HOME goes into the rc file so it resolves
  # at shell-startup time (matches rustup's pattern). Hardcoding the
  # expanded path here would break dotfile-sharing across machines
  # with different HOME values (chezmoi/stow, NFS mounts).
  posix_source='. "$HOME/.rippit/env"'
  fish_source='source "$HOME/.rippit/env.fish"'
  csh_source='source "$HOME/.rippit/env.csh"'

  wrote_any=0

  for rc in "${HOME}/.bashrc" "${HOME}/.zshrc" "${HOME}/.profile"; do
    append_source_line "$rc" "$posix_source" && wrote_any=1
  done

  # Darwin login bash reads .bash_profile, not .bashrc.
  if [ "$(uname -s 2>/dev/null)" = "Darwin" ]; then
    append_source_line "${HOME}/.bash_profile" "$posix_source" && wrote_any=1
  fi

  case "$shell_name" in
    fish)
      append_source_line "${HOME}/.config/fish/config.fish" "$fish_source" && wrote_any=1
      ;;
    tcsh|csh)
      # tcsh reads ~/.tcshrc first and falls back to ~/.cshrc;
      # plain csh only reads ~/.cshrc. Write both so either shell
      # picks it up regardless of which file the user actually has.
      append_source_line "${HOME}/.tcshrc" "$csh_source" && wrote_any=1
      append_source_line "${HOME}/.cshrc" "$csh_source" && wrote_any=1
      ;;
  esac

  if [ "$wrote_any" -eq 0 ]; then
    warn "could not write any shell rc file (read-only HOME?); falling back to manual instructions"
    manual_path_hint "$bin_dir"
    return 0
  fi

  case "$shell_name" in
    fish)      log 'to use rippit in this shell, run: source "$HOME/.rippit/env.fish"' ;;
    tcsh|csh)  log 'to use rippit in this shell, run: source "$HOME/.rippit/env.csh"' ;;
    *)         log 'to use rippit in this shell, run: . "$HOME/.rippit/env"' ;;
  esac
}

install_binary
"${PREFIX}/bin/${BIN_NAME}" --version || true

printf '\n'
log "next: run \`rippit instrument\` to wrap your LLM calls with the Rippit SDK,"
log "      or rerun via \`curl -fsSL https://rippit.sh/instrument | sh\` to do both at once."
