#!/usr/bin/env bash
# node-version-bridge — CLI entrypoint
# Detects Node.js version from project files and generates commands
# to apply it via the user's preferred version manager.

set -euo pipefail

NVB_VERSION="0.6.3"

# Resolve script location (handles symlinks on macOS/Linux)
_nvb_resolve_root() {
  local source="${BASH_SOURCE[0]}"
  while [[ -L "$source" ]]; do
    local dir
    dir="$(cd -P "$(dirname "$source")" && pwd)"
    source="$(readlink "$source")"
    [[ "$source" != /* ]] && source="${dir}/${source}"
  done
  cd -P "$(dirname "$source")/.." && pwd
}
NVB_ROOT="$(_nvb_resolve_root)"

# Source libraries
source "${NVB_ROOT}/lib/log.sh"
source "${NVB_ROOT}/lib/config.sh"
source "${NVB_ROOT}/lib/detect.sh"
source "${NVB_ROOT}/lib/alias.sh"
source "${NVB_ROOT}/lib/parse.sh"
source "${NVB_ROOT}/lib/resolve.sh"
source "${NVB_ROOT}/lib/cache.sh"
source "${NVB_ROOT}/lib/manager.sh"

# Load configuration file (env vars take precedence)
nvb_config_load

# --- Commands ---

nvb_cmd_refresh() {
  # shellcheck disable=SC2119
  nvb_resolve_version || true
  local resolved_version="${NVB_RESOLVED_VERSION:-}"
  local resolved_source="${NVB_RESOLVED_SOURCE:-}"

  if [[ -z "$resolved_version" ]]; then
    nvb_log debug "No version file found, nothing to do"
    return 0
  fi

  # Check cache — skip if nothing changed (bypass with NVB_FORCE_REFRESH=1)
  if [[ "${NVB_FORCE_REFRESH:-}" != "1" ]] && nvb_cache_is_current "$PWD" "$resolved_version" "$resolved_source"; then
    nvb_log debug "Cache hit: ${resolved_version} from ${resolved_source}"
    return 0
  fi

  local manager
  manager="$(nvb_detect_manager)" || {
    echo "echo '[nvb] No supported version manager found.' >&2"
    echo "echo '[nvb] Install one of: nvm, fnm, mise, asdf, n' >&2"
    echo "echo '[nvb] See: https://github.com/marcosreuquen/node-version-bridge#supported-managers' >&2"
    return 1
  }

  nvb_log info "Switching to Node ${resolved_version} (from ${resolved_source}) via ${manager}"

  # Resolve partial version (e.g. "18" → "18.20.8") for managers that need exact versions
  local apply_version
  apply_version="$(nvb_resolve_installed_version "$manager" "$resolved_version")"

  # Auto-install if enabled
  local auto_install
  auto_install="$(nvb_config_get NVB_AUTO_INSTALL false)"
  if [[ "$auto_install" == "true" ]]; then
    # If version couldn't be resolved to installed, install it first
    if [[ "$apply_version" == "$resolved_version" ]] && ! _nvb_is_full_version "$resolved_version"; then
      nvb_log debug "Auto-install enabled, no installed match for partial version ${resolved_version}"
      nvb_adapter_install_cmd "$manager" "$resolved_version"
      # Re-resolve after install (next hook trigger will pick it up if eval'd install succeeds)
      apply_version="$(nvb_resolve_installed_version "$manager" "$resolved_version")"
    else
      nvb_log debug "Auto-install enabled, emitting install command"
      nvb_adapter_install_cmd "$manager" "$apply_version"
    fi
  fi

  # Output eval-able command for the shell hook
  nvb_adapter_apply_cmd "$manager" "$apply_version"

  # Update cache
  nvb_cache_update "$PWD" "$resolved_version" "$resolved_source"
}

nvb_cmd_current() {
  # shellcheck disable=SC2119
  nvb_resolve_version || true
  local resolved_version="${NVB_RESOLVED_VERSION:-}"
  local resolved_source="${NVB_RESOLVED_SOURCE:-}"

  local active_version
  active_version="$(node --version 2>/dev/null | sed 's/^v//')" || active_version="(not found)"

  local manager
  manager="$(nvb_detect_manager 2>/dev/null)" || manager="(none detected)"

  # Resolve partial version to show what would actually be applied
  local apply_version="${resolved_version}"
  if [[ -n "$resolved_version" && "$manager" != "(none detected)" ]]; then
    apply_version="$(nvb_resolve_installed_version "$manager" "$resolved_version" 2>/dev/null)" || apply_version="$resolved_version"
  fi

  echo "node-version-bridge v${NVB_VERSION}"
  echo ""
  echo "  Active Node:    ${active_version}"
  if [[ -n "$resolved_version" && "$apply_version" != "$resolved_version" ]]; then
    echo "  Resolved:       ${resolved_version} → ${apply_version}"
  else
    echo "  Resolved:       ${resolved_version:-(none)}"
  fi
  echo "  Source:         ${resolved_source:-(none)}"
  echo "  Manager:        ${manager}"
}

nvb_cmd_doctor() {
  echo "node-version-bridge v${NVB_VERSION} — diagnostics"
  echo ""

  # Check version managers
  echo "Version managers:"
  local managers=("nvm" "fnm" "mise" "asdf" "n")
  local active_manager
  active_manager="$(nvb_detect_manager 2>/dev/null)" || active_manager=""

  for m in "${managers[@]}"; do
    if nvb_adapter_available "$m"; then
      if [[ "$m" == "$active_manager" ]]; then
        echo "  ● ${m} (active)"
      else
        echo "  ✓ ${m}"
      fi
    else
      echo "  ✗ ${m}"
    fi
  done

  echo ""

  # Check version files
  echo "Version files (from $PWD):"
  local priority_str="${NVB_PRIORITY:-.nvmrc,.node-version,.tool-versions,package.json}"
  IFS=',' read -ra priorities <<< "$priority_str"

  for f in "${priorities[@]}"; do
    f="$(echo "$f" | xargs)"
    local found
    found="$(nvb_detect_file "$f" 2>/dev/null)" || found=""
    if [[ -n "$found" ]]; then
      local ver
      ver="$(nvb_parse_file "$f" "$found" 2>/dev/null)" || ver="(parse error)"
      echo "  ✓ ${found} → ${ver}"
    else
      echo "  ✗ ${f} (not found)"
    fi
  done

  echo ""

  # Configuration
  echo "Configuration:"
  echo "  NVB_MANAGER:         ${NVB_MANAGER:-(auto-detect)}"
  echo "  NVB_LOG_LEVEL:       ${NVB_LOG_LEVEL:-error}"
  echo "  NVB_PRIORITY:        ${NVB_PRIORITY:-.nvmrc,.node-version,.tool-versions,package.json}"
  echo "  NVB_CACHE_DIR:       ${NVB_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/node-version-bridge}"
  echo "  NVB_AUTO_INSTALL:    $(nvb_config_get NVB_AUTO_INSTALL false)"
  echo "  NVB_ALIAS_CACHE_TTL: ${NVB_ALIAS_CACHE_TTL:-3600}"
  echo "  Config file:         $(nvb_config_file)"

  echo ""
  local node_ver
  node_ver="$(node --version 2>/dev/null)" || node_ver="(not found)"
  echo "Active Node: ${node_ver}"
}

nvb_cmd_config() {
  local subcmd="${1:-list}"
  shift 2>/dev/null || true

  case "$subcmd" in
    list|ls)
      nvb_config_list
      ;;
    get)
      local key="${1:-}"
      if [[ -z "$key" ]]; then
        echo "Usage: nvb config get <KEY>" >&2
        return 1
      fi
      nvb_config_get "$key"
      ;;
    set)
      local key="${1:-}"
      local value="${2:-}"
      if [[ -z "$key" || -z "$value" ]]; then
        echo "Usage: nvb config set <KEY> <VALUE>" >&2
        return 1
      fi
      nvb_config_set "$key" "$value"
      echo "Set ${key}=${value} in $(nvb_config_file)"
      ;;
    unset)
      local key="${1:-}"
      if [[ -z "$key" ]]; then
        echo "Usage: nvb config unset <KEY>" >&2
        return 1
      fi
      nvb_config_unset "$key"
      echo "Removed ${key} from $(nvb_config_file)"
      ;;
    path)
      nvb_config_file
      ;;
    *)
      echo "Usage: nvb config <list|get|set|unset|path>" >&2
      echo "" >&2
      echo "  list          Show all config values and their sources" >&2
      echo "  get <KEY>     Get a config value" >&2
      echo "  set <KEY> <VALUE>  Set a config value" >&2
      echo "  unset <KEY>   Remove a config value" >&2
      echo "  path          Show config file path" >&2
      return 1
      ;;
  esac
}

nvb_cmd_init() {
  local shell_name="${1:-}"

  if [[ -z "$shell_name" ]]; then
    # Auto-detect from parent shell
    shell_name="$(basename "${SHELL:-}")"
  fi

  case "$shell_name" in
    zsh)
      echo "_NVB_BIN='${NVB_ROOT}/bin/nvb'"
      cat <<'HOOK'
nvb() { "${_NVB_BIN}" "$@"; }
_nvb_hook() {
  [[ "${_NVB_LAST_DIR:-}" == "$PWD" ]] && return
  _NVB_LAST_DIR="$PWD"
  eval "$("${_NVB_BIN}" refresh 2>/dev/null)"
}
autoload -U add-zsh-hook
add-zsh-hook chpwd _nvb_hook
eval "$(NVB_FORCE_REFRESH=1 "${_NVB_BIN}" refresh 2>/dev/null)"
_NVB_LAST_DIR="$PWD"
HOOK
      ;;
    bash)
      echo "_NVB_BIN='${NVB_ROOT}/bin/nvb'"
      cat <<'HOOK'
nvb() { "${_NVB_BIN}" "$@"; }
_nvb_hook() {
  [[ "${_NVB_LAST_DIR:-}" == "$PWD" ]] && return
  _NVB_LAST_DIR="$PWD"
  eval "$("${_NVB_BIN}" refresh 2>/dev/null)"
}
if [[ ";${PROMPT_COMMAND:-};" != *";_nvb_hook;"* ]]; then
  PROMPT_COMMAND="_nvb_hook;${PROMPT_COMMAND:-}"
fi
eval "$(NVB_FORCE_REFRESH=1 "${_NVB_BIN}" refresh 2>/dev/null)"
_NVB_LAST_DIR="$PWD"
HOOK
      ;;
    *)
      echo "Unknown shell: ${shell_name}. Supported: zsh, bash" >&2
      return 1
      ;;
  esac
}

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

  local nvb_bin="${NVB_ROOT}/bin/nvb"

  if [[ "$shell_name" != "zsh" && "$shell_name" != "bash" ]]; then
    echo "[nvb] Could not detect shell (got: ${shell_name}). Supported: zsh, bash" >&2
    echo "[nvb] Add manually to your shell config:" >&2
    echo "  eval \"\$(${nvb_bin} init zsh)\"   # for zsh" >&2
    echo "  eval \"\$(${nvb_bin} init bash)\"  # for bash" >&2
    return 1
  fi

  local rc_file
  case "$shell_name" in
    zsh)  rc_file="${ZDOTDIR:-$HOME}/.zshrc" ;;
    bash) rc_file="$HOME/.bashrc" ;;
  esac

  local hook_line="eval \"\$(${nvb_bin} init ${shell_name})\""
  local marker="# node-version-bridge:hook"

  # Check if already configured (new or legacy format)
  if [[ -f "$rc_file" ]]; then
    if grep -qF "nvb init" "$rc_file" || grep -qF "$marker" "$rc_file"; then
      echo "[nvb] Shell hook already configured in ${rc_file}"
      return 0
    fi
  fi

  echo "[nvb] Adding shell hook to ${rc_file}..."
  {
    echo ""
    echo "$marker"
    echo "$hook_line"
  } >> "$rc_file"

  echo "[nvb] Done! Restart your shell or run:"
  echo ""
  echo "  source ${rc_file}"
  echo ""
}

nvb_cmd_help() {
  cat <<EOF
node-version-bridge v${NVB_VERSION}

Automatically applies the Node.js version declared in your project
using your preferred version manager.

Usage: nvb <command> [options]

Commands:
  setup       Auto-configure shell hook (run once after install)
  init        Output shell hook code for eval (used in shell config)
  refresh     Detect version and output shell commands to apply it (for eval)
  current     Show resolved version, source, and active Node version
  doctor      Check available managers and configuration
  config      Manage configuration (list, get, set, unset, path)
  version     Show nvb version
  help        Show this help

Config examples:
  nvb config list                          Show all configuration
  nvb config set NVB_AUTO_INSTALL true     Enable auto-install
  nvb config set NVB_MANAGER fnm           Force fnm as manager
  nvb config set NVB_LOG_LEVEL debug       Enable debug logging
  nvb config unset NVB_MANAGER             Remove manager override

Supported version files: .nvmrc, .node-version, .tool-versions, package.json (engines.node)
Supported managers: nvm, fnm, mise, asdf, n

Shell integration:
  nvb setup                    Auto-configure (recommended, run once)
  eval "\$(nvb init zsh)"       Manual — add to ~/.zshrc
  eval "\$(nvb init bash)"      Manual — add to ~/.bashrc

Environment variables:
  NVB_MANAGER          Force a specific version manager (nvm, fnm, mise, asdf, n)
  NVB_LOG_LEVEL        Log verbosity: error, warn, info, debug (default: error)
  NVB_PRIORITY         Comma-separated file priority (default: .nvmrc,.node-version,.tool-versions,package.json)
  NVB_CACHE_DIR        Override cache directory
  NVB_AUTO_INSTALL     Auto-install missing versions: true/false (default: false)
  NVB_ALIAS_CACHE_TTL  Alias cache TTL in seconds (default: 3600)

Configuration file: $(nvb_config_file)
EOF
}

# --- Main dispatch ---

case "${1:-help}" in
  setup)       nvb_cmd_setup ;;
  init)        shift; nvb_cmd_init "$@" ;;
  refresh)     nvb_cmd_refresh ;;
  current)     nvb_cmd_current ;;
  doctor)      nvb_cmd_doctor ;;
  config)      shift; nvb_cmd_config "$@" ;;
  version|-v)  echo "nvb ${NVB_VERSION}" ;;
  help|--help|-h) nvb_cmd_help ;;
  *)
    nvb_log error "Unknown command: $1"
    nvb_cmd_help >&2
    exit 1
    ;;
esac