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

# npm package name (product CLI remains `clavue`; package is `iclavue` after
# the public clavue name was security-held on the registry).
PACKAGE="${CLAVUE_PACKAGE:-iclavue}"
VERSION_INPUT="${1:-${CLAVUE_VERSION:-latest}}"

need_cmd() {
  if ! command -v "$1" >/dev/null 2>&1; then
    printf 'Missing required command: %s\n' "$1" >&2
    exit 1
  fi
}

check_node() {
  need_cmd node

  local node_major
  node_major="$(node -p "process.versions.node.split('.')[0]")"
  if [[ -z "$node_major" || "$node_major" -lt 18 ]]; then
    printf 'clavue requires Node.js 18 or newer. Found: %s\n' "$(node -v)" >&2
    exit 1
  fi
}

resolve_version() {
  if [[ "$VERSION_INPUT" != "latest" ]]; then
    printf '%s' "${VERSION_INPUT#v}"
    return 0
  fi

  npm view "$PACKAGE" version
}

# npm's global install does an atomic rename of the existing package
# directory into a `.<name>-<rand>` staging directory before unpacking the
# new version. On macOS APFS AND Linux ext4 this fails with ENOTEMPTY when
# the previous install left over files (interrupted install, leftover
# symlink target, leftover staging dir from a prior failed run). Once a
# staging dir survives, EVERY future `npm install -g clavue` on that host
# fails at the same path until the leftover is removed. Clean leftovers
# first so the next `npm install -g` can rename without conflict.
clean_global_residue() {
  local remove_live_package="${1:-0}"
  local prefix
  prefix="$(npm prefix -g 2>/dev/null || true)"
  if [[ -z "$prefix" ]]; then
    return 0
  fi

  local node_modules="${prefix}/lib/node_modules"
  # Linux/macOS layout uses lib/node_modules; some distros use just
  # node_modules under prefix. Try both.
  if [[ ! -d "$node_modules" ]]; then
    if [[ -d "${prefix}/node_modules" ]]; then
      node_modules="${prefix}/node_modules"
    else
      return 0
    fi
  fi

  local pkg_dir="${node_modules}/${PACKAGE}"
  local bin_link="${prefix}/bin/${PACKAGE}"
  local needs_sudo=0
  local has_residue=0
  if [[ "$remove_live_package" -eq 1 && -e "$pkg_dir" ]]; then
    has_residue=1
  fi
  if [[ -n "$(compgen -G "${node_modules}/.${PACKAGE}-*" || true)" ]]; then
    has_residue=1
  fi
  # Also catch dangling bin symlinks (pointing into a removed staging dir).
  if [[ -L "$bin_link" && ! -e "$bin_link" ]]; then
    has_residue=1
  fi
  if [[ "$has_residue" -eq 0 ]]; then
    return 0
  fi
  if [[ ! -w "$node_modules" ]]; then
    needs_sudo=1
  fi

  printf 'Cleaning leftover %s install in %s...\n' "$PACKAGE" "$node_modules"
  local live_target=()
  if [[ "$remove_live_package" -eq 1 ]]; then
    live_target=("$pkg_dir")
  fi
  if [[ "$needs_sudo" -eq 1 ]]; then
    sudo rm -rf "${live_target[@]}" "${node_modules}"/."${PACKAGE}"-* 2>/dev/null || true
    if [[ -L "$bin_link" && ! -e "$bin_link" ]]; then
      sudo rm -f "$bin_link" 2>/dev/null || true
    fi
  else
    rm -rf "${live_target[@]}" "${node_modules}"/."${PACKAGE}"-* 2>/dev/null || true
    if [[ -L "$bin_link" && ! -e "$bin_link" ]]; then
      rm -f "$bin_link" 2>/dev/null || true
    fi
  fi
}

# Mindshare aliases (agent/ai/chat) may already be other tools. Warn honestly
# before npm overwrites global bins — do not hide conflicts.
warn_alias_conflicts() {
  local name path
  local any=0
  printf 'Checking mindshare launch aliases for existing commands...\n'
  for name in agent ai chat; do
    if command -v "$name" >/dev/null 2>&1; then
      path="$(command -v "$name" 2>/dev/null || true)"
      # Already Clavue/iclavue → fine
      if [[ -n "$path" ]] && printf '%s' "$path" | grep -Eqi 'iclavue|clavue'; then
        printf '  · %s → %s  (Clavue — ok)\n' "$name" "$path"
        continue
      fi
      any=1
      printf '  ! %s already on PATH: %s\n' "$name" "${path:-?}"
      printf '    npm install will point this name at Clavue (mindshare).\n'
      printf '    If you need the old tool, rename it or use a different npm prefix.\n'
    else
      printf '  · %s free — will install as Clavue alias\n' "$name"
    fi
  done
  if [[ "$any" -eq 1 ]]; then
    printf '\nAlias conflict detected (not fatal). Primary command remains: clavue\n'
    printf 'Skip alias worry: always launch with `clavue` or `iclavue`.\n\n'
  fi
}

main() {
  need_cmd npm
  check_node

  local version
  version="$(resolve_version)"
  if [[ -z "$version" ]]; then
    printf 'Unable to determine the latest %s version from npm.\n' "$PACKAGE" >&2
    exit 1
  fi

  printf 'Installing %s@%s globally from npm...\n' "$PACKAGE" "$version"
  printf 'For one-shot use without a global install, run: npx -y %s@%s\n' "$PACKAGE" "$version"
  warn_alias_conflicts
  clean_global_residue
  if ! npm install -g "${PACKAGE}@${version}"; then
    printf '\nnpm install failed. Retrying once after a deeper cleanup...\n' >&2
    clean_global_residue 1
    npm install -g "${PACKAGE}@${version}"
  fi

  printf 'Installed %s@%s globally\n' "$PACKAGE" "$version"
  printf 'Available commands after npm install:\n'
  printf '  clavue   (primary)\n'
  printf '  agent    (mindshare alias → Clavue)\n'
  printf '  ai       (mindshare alias → Clavue)\n'
  printf '  chat     (mindshare alias → Clavue)\n'
  printf '  iclavue  (package alias)\n'
  # Verify at least one launcher responds
  if command -v clavue >/dev/null 2>&1; then
    clavue --version || true
  elif command -v agent >/dev/null 2>&1; then
    agent --version || true
  elif command -v ai >/dev/null 2>&1; then
    ai --version || true
  elif command -v chat >/dev/null 2>&1; then
    chat --version || true
  elif command -v iclavue >/dev/null 2>&1; then
    iclavue --version || true
  else
    printf 'Warning: no clavue/agent/ai/chat on PATH. Check `npm bin -g`.\n' >&2
  fi

  printf '\nNext step:\n'
  printf '  Run any of:  clavue  |  agent  |  ai  |  chat\n'
  printf '  First launch opens API setup. Choose "自定义 API 配置" when you have an API URL and API key or Auth Token ready; then choose a model family and add a profile.\n'
  printf '  After saving, answer "no" at "返回主菜单？" to enter Clavue immediately with the default profile.\n'
  printf '  If setup is wrong or skipped, reopen it any time with: clavue provider\n'
  printf '  Tip: mouse drag selects · double-click copies word · triple-click copies line.\n'
  printf '  Tip: /plan · /todos show structured progress (auto-updates after TodoWrite).\n'
}

main "$@"
