#!/usr/bin/env bash
set -euo pipefail

MIN_NODE_MAJOR=22
MIN_NODE_MINOR=19
MIN_NODE_PATCH=0
DEFAULT_GIT_URL="https://github.com/alpacaloop/alpacaloop.git"
DEFAULT_GIT_REF="${CLI_GIT_REF:-main}"
DEFAULT_PACKAGE="alpacaloop"

INSTALL_DEPS=0
RUN_INIT=0
SOURCE="${CLI_INSTALL_SOURCE:-git}"
VERSION="${CLI_INSTALL_VERSION:-latest}"
NO_MODIFY_PATH=0
CLI_CMD=""

usage() {
  cat <<'EOF'
AlpacaLoop installer

Usage:
  install.sh [options]

Options:
  --install-deps       Install missing Node.js tooling with fnm.
  --yes               Alias for --install-deps.
  --init              Run alpaca init after installing the CLI.
  --source <source>   Install source: git, tarball, or npm. Default: git.
  --version <version> Version or tag to install. Default: latest.
  --no-modify-path    Do not let fnm update shell profile files.
  -h, --help          Show this help.

Environment:
  CLI_INSTALL_SOURCE   git, tarball, or npm.
  CLI_INSTALL_VERSION  Version or tag to install.
  CLI_GIT_URL          Git repository URL for --source git.
  CLI_GIT_REF          Git ref used by default for --source git. Default: main.
  CLI_TARBALL_URL      Tarball URL for --source tarball.
  CLI_NPM_PACKAGE      npm package name for --source npm.
EOF
}

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

warn() {
  printf 'alpaca: warning: %s\n' "$*" >&2
}

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

while [ "$#" -gt 0 ]; do
  case "$1" in
    --install-deps)
      INSTALL_DEPS=1
      ;;
    --yes)
      INSTALL_DEPS=1
      ;;
    --init)
      RUN_INIT=1
      ;;
    --source)
      [ "$#" -ge 2 ] || fail "--source requires a value"
      SOURCE="$2"
      shift
      ;;
    --version)
      [ "$#" -ge 2 ] || fail "--version requires a value"
      VERSION="$2"
      shift
      ;;
    --no-modify-path)
      NO_MODIFY_PATH=1
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      fail "unknown option: $1"
      ;;
  esac
  shift
done

check_platform() {
  local os
  os="$(uname -s)"
  case "$os" in
    Darwin)
      command -v launchctl >/dev/null 2>&1 ||
        warn "launchctl was not found; alpaca service enable will not work on this macOS host."
      ;;
    Linux)
      if ! command -v systemctl >/dev/null 2>&1; then
        warn "systemctl was not found; alpaca service enable requires systemd --user."
      elif ! systemctl --user show-environment >/dev/null 2>&1; then
        if grep -qi microsoft /proc/version 2>/dev/null || [ -n "${WSL_DISTRO_NAME:-}" ]; then
          warn "WSL needs systemd user services enabled before alpaca service enable can work."
        else
          warn "systemd --user is not available; alpaca service enable will not work on this host."
        fi
      fi
      ;;
    *)
      fail "unsupported OS: $os. install.sh currently supports macOS and Linux."
      ;;
  esac
}

node_version_ok() {
  command -v node >/dev/null 2>&1 || return 1
  node -e "
const [major, minor, patch] = process.versions.node.split('.').map(Number);
const ok =
  major > $MIN_NODE_MAJOR ||
  (major === $MIN_NODE_MAJOR &&
    (minor > $MIN_NODE_MINOR ||
      (minor === $MIN_NODE_MINOR && patch >= $MIN_NODE_PATCH)));
process.exit(ok ? 0 : 1);
" >/dev/null 2>&1
}

ensure_downloader() {
  if command -v curl >/dev/null 2>&1 || command -v wget >/dev/null 2>&1; then
    return
  fi
  fail "curl or wget is required to install fnm."
}

download_to_stdout() {
  local url="$1"
  if command -v curl >/dev/null 2>&1; then
    curl -fsSL "$url"
  else
    wget -qO- "$url"
  fi
}

add_fnm_to_path() {
  if [ -x "$HOME/.local/share/fnm/fnm" ]; then
    export PATH="$HOME/.local/share/fnm:$PATH"
  fi
  if [ -x "$HOME/.fnm/fnm" ]; then
    export PATH="$HOME/.fnm:$PATH"
  fi
}

install_node_with_fnm() {
  local fnm_args=()
  ensure_downloader
  if ! command -v fnm >/dev/null 2>&1; then
    [ "$NO_MODIFY_PATH" -eq 1 ] && fnm_args+=(--skip-shell)
    log "installing fnm"
    download_to_stdout "https://fnm.vercel.app/install" | bash -s -- "${fnm_args[@]}"
    add_fnm_to_path
  fi

  command -v fnm >/dev/null 2>&1 || fail "fnm installation finished, but fnm is not on PATH."
  eval "$(fnm env --shell bash)"
  log "installing Node.js LTS with fnm"
  fnm install --lts
  fnm use lts-latest
}

ensure_node() {
  if node_version_ok; then
    log "using Node.js $(node -v)"
    return
  fi

  if [ "$INSTALL_DEPS" -ne 1 ]; then
    fail "Node.js >= ${MIN_NODE_MAJOR}.${MIN_NODE_MINOR}.${MIN_NODE_PATCH} is required. Re-run with --install-deps or install Node.js manually."
  fi

  install_node_with_fnm
  node_version_ok ||
    fail "Node.js >= ${MIN_NODE_MAJOR}.${MIN_NODE_MINOR}.${MIN_NODE_PATCH} is still unavailable after fnm setup."
  log "using Node.js $(node -v)"
}

ensure_npm() {
  if command -v npm >/dev/null 2>&1; then
    return
  fi

  if [ "$INSTALL_DEPS" -eq 1 ]; then
    install_node_with_fnm
    command -v npm >/dev/null 2>&1 && return
  fi

  fail "npm is required to install AlpacaLoop. Some Node.js installations include node without npm; install npm manually or re-run with --install-deps to use fnm-managed Node.js."
}

build_install_target() {
  local package_name="${CLI_NPM_PACKAGE:-$DEFAULT_PACKAGE}"
  local git_url="${CLI_GIT_URL:-$DEFAULT_GIT_URL}"
  case "$SOURCE" in
    npm)
      if [ "$VERSION" = "latest" ]; then
        printf '%s' "$package_name"
      else
        printf '%s@%s' "$package_name" "$VERSION"
      fi
      ;;
    git)
      if ! command -v git >/dev/null 2>&1; then
        fail "git is required for --source git. Use --source tarball or install git first."
      fi
      if [ "$VERSION" = "latest" ]; then
        printf 'git+%s#%s' "$git_url" "$DEFAULT_GIT_REF"
      elif [[ "$VERSION" == \#* ]]; then
        printf 'git+%s%s' "$git_url" "$VERSION"
      elif [[ "$VERSION" == v* ]]; then
        printf 'git+%s#%s' "$git_url" "$VERSION"
      elif [[ "$VERSION" =~ ^[0-9] ]]; then
        printf 'git+%s#v%s' "$git_url" "$VERSION"
      else
        printf 'git+%s#%s' "$git_url" "$VERSION"
      fi
      ;;
    tarball)
      if [ -n "${CLI_TARBALL_URL:-}" ]; then
        printf '%s' "$CLI_TARBALL_URL"
      elif [ "$VERSION" != "latest" ]; then
        local release_version="${VERSION#v}"
        printf 'https://github.com/alpacaloop/alpacaloop/releases/download/v%s/alpacaloop-%s.tgz' "$release_version" "$release_version"
      else
        fail "--source tarball requires --version or CLI_TARBALL_URL."
      fi
      ;;
    *)
      fail "unknown source: $SOURCE"
      ;;
  esac
}

install_alpaca() {
  ensure_npm
  local target
  target="$(build_install_target)"
  log "installing AlpacaLoop from $SOURCE"
  install_alpaca_package "$target"
}

install_alpaca_package() {
  local target="$1"
  if npm install -g --foreground-scripts "$target"; then
    return
  fi

  fail "npm install -g failed. If this was an EACCES or permission error on Linux/WSL2, use a user-writable npm prefix such as fnm/nvm-managed Node.js, then retry. Do not run this installer with sudo unless you intentionally want a root-owned install."
}

print_gitea_note() {
  if [ -n "${ALPACA_GITEA_USERNAME:-}" ] && [ -n "${ALPACA_GITEA_PASSWORD:-}" ]; then
    log "Gitea credentials found in ALPACA_GITEA_USERNAME/ALPACA_GITEA_PASSWORD."
  else
    log "Gitea server installation is not managed by this script."
    log "For Gitea, export ALPACA_GITEA_USERNAME and ALPACA_GITEA_PASSWORD before alpaca init, or configure git credentials."
  fi
}

resolve_alpaca_command() {
  local prefix
  prefix="$(npm prefix -g 2>/dev/null || true)"
  if [ -n "$prefix" ] && [ -x "$prefix/bin/alpaca" ]; then
    printf '%s\n' "$prefix/bin/alpaca"
    return
  fi

  if command -v alpaca >/dev/null 2>&1; then
    command -v alpaca
    return
  fi

  if [ -n "$prefix" ] && [ -x "$prefix/bin/alpacaloop" ]; then
    printf '%s\n' "$prefix/bin/alpacaloop"
    return
  fi

  if command -v alpacaloop >/dev/null 2>&1; then
    command -v alpacaloop
    return
  fi

  return 1
}

check_platform
ensure_node
install_alpaca
print_gitea_note

if CLI_CMD="$(resolve_alpaca_command)"; then
  log "installed alpaca"
else
  warn "alpaca is installed, but neither alpaca nor alpacaloop is on PATH in this shell."
fi

if [ "$RUN_INIT" -eq 1 ]; then
  [ -n "$CLI_CMD" ] || fail "neither alpaca nor alpacaloop is on PATH; open a new shell or update PATH before running alpaca init."
  "$CLI_CMD" init
else
  log "next: run alpaca init in the repository you want to manage."
fi
