#!/bin/sh
# Install the ix CLI.
# Usage: curl https://ix.dev | sh
#
# Respects IX_INSTALL_DIR to override the install location.
# Supports bash, zsh, fish, and nushell.
set -eu

BASE_URL="https://ix.dev/cli"
INSTALL_DIR="${IX_INSTALL_DIR:-$HOME/.local/bin}"

main() {
  platform="$(detect_platform)"
  download "$platform"
  install_man_pages
  configure_path
  echo ""
  echo "ix installed successfully. Run 'ix' to get started."
}

# `man ix` out of the box: the binary generates its own pages, from the same
# text as --help. man derives its search path from PATH (<dir>/../share/man),
# so pages land next to the install dir. Docs never fail an install.
install_man_pages() {
  man_dir="$(dirname "$INSTALL_DIR")/share/man"
  if "$INSTALL_DIR/ix" system man "$man_dir" >/dev/null 2>&1; then
    echo "Installed man pages to ${man_dir} (try 'man ix')."
  fi
}

detect_platform() {
  os="$(uname -s)"
  arch="$(uname -m)"

  case "$os" in
  Linux) os="linux" ;;
  Darwin) os="darwin" ;;
  *) error "unsupported OS: $os (expected Linux or macOS)" ;;
  esac

  case "$arch" in
  x86_64 | amd64) arch="x86_64" ;;
  arm64 | aarch64) arch="arm64" ;;
  *) error "unsupported architecture: $arch (expected x86_64 or arm64)" ;;
  esac

  platform="${os}-${arch}"

  case "$platform" in
  linux-x86_64 | darwin-arm64) ;;
  linux-arm64) error "linux-arm64 is not yet supported" ;;
  darwin-x86_64) error "Intel Macs (darwin-x86_64) are not supported" ;;
  *) error "unsupported platform: $platform" ;;
  esac

  echo "$platform"
}

download() {
  platform="$1"
  manifest="$(mktemp)"
  tmp="$(mktemp)"
  trap 'rm -f "$manifest" "$tmp"' 0

  # publish-cli writes manifest.json ({ "<platform>": "<sha256>" }) alongside
  # immutable <platform>/sha256/<digest>/ix keys. Downloading by digest and
  # verifying it closes the gap where the mutable <platform>/ix key could be
  # swapped (or caught mid-publish) between release and install.
  fetch "${BASE_URL}/manifest.json" "$manifest"
  digest="$(digest_for_platform "$platform" <"$manifest")"
  if [ -z "$digest" ]; then
    error "no sha256 digest for ${platform} in ${BASE_URL}/manifest.json"
  fi

  echo "Downloading ix for ${platform} to ${INSTALL_DIR}..."
  fetch "${BASE_URL}/${platform}/sha256/${digest}/ix" "$tmp"

  actual="$(sha256_of "$tmp")"
  if [ "$actual" != "$digest" ]; then
    error "sha256 mismatch for ix (${platform}): expected ${digest}, got ${actual}"
  fi

  mkdir -p "$INSTALL_DIR"
  chmod +x "$tmp"
  mv -f "$tmp" "$INSTALL_DIR/ix"
  trap - 0
}

fetch() {
  url="$1"
  out="$2"
  if command -v curl >/dev/null 2>&1; then
    curl -fsSL "$url" -o "$out"
  elif command -v wget >/dev/null 2>&1; then
    wget -qO "$out" "$url"
  else
    error "curl or wget is required"
  fi
}

# Extract this platform's digest from manifest.json on stdin. POSIX sh with no
# jq at install time: strip whitespace so pretty-printed and compact JSON look
# the same, then match the "<platform>":"<64 hex>" pair wherever it sits.
digest_for_platform() {
  tr -d ' \t\n\r' | sed -n "s/.*\"$1\":\"\([0-9a-f]\{64\}\)\".*/\1/p"
}

sha256_of() {
  # sha256sum is coreutils; macOS ships shasum instead.
  if command -v sha256sum >/dev/null 2>&1; then
    sha256sum "$1" | cut -d' ' -f1
  elif command -v shasum >/dev/null 2>&1; then
    shasum -a 256 "$1" | cut -d' ' -f1
  else
    error "sha256sum or shasum is required"
  fi
}

configure_path() {
  case ":$PATH:" in
  *":$INSTALL_DIR:"*) return ;;
  esac

  shell_name="$(basename "${SHELL:-bash}")"

  case "$shell_name" in
  bash)
    rc_file="$HOME/.bashrc"
    if [ ! -f "$rc_file" ]; then
      rc_file="$HOME/.bash_profile"
    fi
    append_path_line "$rc_file" "export PATH=\"$INSTALL_DIR:\$PATH\""
    ;;
  zsh)
    rc_file="$HOME/.zshrc"
    append_path_line "$rc_file" "export PATH=\"$INSTALL_DIR:\$PATH\""
    ;;
  fish)
    rc_file="$HOME/.config/fish/config.fish"
    mkdir -p "$(dirname "$rc_file")"
    append_path_line "$rc_file" "fish_add_path $INSTALL_DIR"
    ;;
  nu | nushell)
    rc_file="$HOME/.config/nushell/env.nu"
    if [ -f "$rc_file" ]; then
      append_path_line "$rc_file" "\$env.PATH = (\$env.PATH | prepend \"$INSTALL_DIR\")"
    else
      echo "Add to your nushell config: \$env.PATH = (\$env.PATH | prepend \"$INSTALL_DIR\")"
      return
    fi
    ;;
  *)
    echo "Add $INSTALL_DIR to your PATH."
    return
    ;;
  esac

  echo "Restart your shell or run:"
  case "$shell_name" in
  fish) echo "  fish_add_path $INSTALL_DIR" ;;
  nu | nushell) echo "  \$env.PATH = (\$env.PATH | prepend \"$INSTALL_DIR\")" ;;
  *) echo "  export PATH=\"$INSTALL_DIR:\$PATH\"" ;;
  esac
}

append_path_line() {
  file="$1"
  line="$2"
  if [ -f "$file" ] && grep -qF "$INSTALL_DIR" "$file" 2>/dev/null; then
    return
  fi
  printf '\n# ix CLI\n%s\n' "$line" >>"$file"
  echo "Updated $file"
}

error() {
  echo "Error: $1" >&2
  exit 1
}

main
