#!/usr/bin/env bash
#
# clavue repair — fix npm global install corruption (ENOTEMPTY, missing bin,
# half-renamed `.clavue-<rand>` staging dirs).
#
# What npm does: `npm install -g <pkg>` renames the existing pkg dir to a
# hidden `.<pkg>-<rand>` staging path, then unpacks the new tarball into a
# fresh `<pkg>` dir. If the previous run was interrupted, or the OS refused
# the rename (APFS/ext4 with open handles, filesystem quirks, "directory
# not empty" on retry), the staging dir survives and every future install
# attempt fails with ENOTEMPTY at the SAME path.
#
# This script:
#   1. Locates the global node_modules dir (works under nvm, brew, asdf,
#      and system installs).
#   2. Removes the live package dir AND every `.<pkg>-*` staging leftover
#      (also cleans legacy `clavue` / `.clavue-*` when migrating to iclavue).
#   3. Removes dangling bin symlinks (`clavue`, `iclavue`) if broken.
#   4. Reinstalls iclavue@latest (or the version passed as $1).
#
# Usage:
#   curl -fsSL https://unpkg.com/iclavue/repair.sh | bash
#   curl -fsSL https://unpkg.com/iclavue/repair.sh | bash -s -- 10.4.3
#
# Environment:
#   CLAVUE_PACKAGE  — override the package name (default: iclavue)
#   CLAVUE_NO_INSTALL=1 — only clean, do not reinstall (useful for
#                          uninstall flows and for power users who want
#                          to install with a custom registry afterwards).
set -euo pipefail

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
}

resolve_node_modules() {
  local prefix
  prefix="$(npm prefix -g 2>/dev/null || true)"
  if [[ -z "$prefix" ]]; then
    printf 'Could not locate npm prefix. Is npm on PATH?\n' >&2
    exit 1
  fi

  if [[ -d "${prefix}/lib/node_modules" ]]; then
    printf '%s' "${prefix}/lib/node_modules"
  elif [[ -d "${prefix}/node_modules" ]]; then
    printf '%s' "${prefix}/node_modules"
  else
    # Fallback: the dir may not exist yet on a fresh prefix; pick the
    # canonical Linux/macOS layout so subsequent install can create it.
    printf '%s' "${prefix}/lib/node_modules"
  fi
}

needs_sudo_for() {
  [[ -d "$1" && ! -w "$1" ]]
}

clean_residue() {
  local node_modules="$1"
  local prefix
  prefix="$(npm prefix -g 2>/dev/null || true)"
  # Current package + legacy npm name (clavue) left after security-hold migration.
  local pkgs=("${PACKAGE}")
  if [[ "$PACKAGE" != "clavue" ]]; then
    pkgs+=("clavue")
  fi

  local found_any=0
  local remove_paths=()
  local pkg pkg_dir staging_glob d bin_link
  for pkg in "${pkgs[@]}"; do
    pkg_dir="${node_modules}/${pkg}"
    staging_glob="${node_modules}/.${pkg}-*"
    if [[ -e "$pkg_dir" ]]; then
      printf '  - removing %s\n' "$pkg_dir"
      remove_paths+=("$pkg_dir")
      found_any=1
    fi
    # shellcheck disable=SC2086
    for d in ${staging_glob}; do
      if [[ -e "$d" ]]; then
        printf '  - removing staging leftover %s\n' "$d"
        remove_paths+=("$d")
        found_any=1
      fi
    done
  done

  for bin_name in clavue iclavue agent ai chat; do
    bin_link="${prefix}/bin/${bin_name}"
    if [[ -L "$bin_link" && ! -e "$bin_link" ]]; then
      printf '  - removing dangling bin symlink %s\n' "$bin_link"
      remove_paths+=("$bin_link")
      found_any=1
    fi
  done

  if [[ "$found_any" -eq 0 ]]; then
    printf 'No leftovers to clean in %s.\n' "$node_modules"
    return 0
  fi

  local sudo_cmd=""
  if needs_sudo_for "$node_modules"; then
    if command -v sudo >/dev/null 2>&1; then
      sudo_cmd="sudo"
      printf '%s is not user-writable; using sudo to clean.\n' "$node_modules"
    else
      printf 'Warning: %s is not user-writable and sudo is unavailable; clean may fail.\n' "$node_modules" >&2
    fi
  fi

  $sudo_cmd rm -rf "${remove_paths[@]}" || true
}

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

main() {
  need_cmd npm

  local node_modules
  node_modules="$(resolve_node_modules)"
  printf 'Repairing %s install in %s...\n' "$PACKAGE" "$node_modules"
  clean_residue "$node_modules"

  if [[ "${CLAVUE_NO_INSTALL:-0}" == "1" ]]; then
    printf '\nCleanup complete. CLAVUE_NO_INSTALL=1 set — skipping reinstall.\n'
    return 0
  fi

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

  printf '\nInstalling %s@%s...\n' "$PACKAGE" "$version"
  if ! npm install -g "${PACKAGE}@${version}"; then
    printf '\nFirst install attempt failed. Cleaning again and retrying...\n' >&2
    clean_residue "$node_modules"
    npm install -g "${PACKAGE}@${version}"
  fi

  printf '\nInstalled %s@%s globally.\n' "$PACKAGE" "$version"
  printf 'CLI commands: clavue · agent · ai · chat  (also: iclavue)\n'
  if command -v clavue >/dev/null 2>&1; then
    clavue --version || true
  elif command -v iclavue >/dev/null 2>&1; then
    iclavue --version || true
  else
    printf 'Warning: clavue/iclavue not on PATH. Reopen shell or check `npm bin -g`.\n' >&2
  fi
}

main "$@"
