#!/usr/bin/env bash
# dep-upgrader: polyglot dependency and workspace maintenance assistant.
# Code comments and user-facing text are English.

set -Eeuo pipefail
IFS=$'\n\t'

VERSION="1.0.0"
ROOT="$(pwd -P)"
MODULE=""
MODE="semver-safe"
AUDIT_MODE="report"
VALIDATE_PROFILE="full"
QUALITY_PROFILE="full"
HARDEN="strict"
ECOSYSTEMS="all"
PROJECT_FILTER=""
DRY_RUN=0
ASSUME_YES=0
KEEP_GOING=1
FIX=0
INSTALL_TOOLS=0
GENERATE_RENOVATE=1
GENERATE_CHANGESETS=1
NON_INTERACTIVE=0
LIST_ONLY=0
ROLLBACK_ARCHIVE=""
RID=""
STASH=0

C_RESET=$'\033[0m'; C_BOLD=$'\033[1m'; C_CYAN=$'\033[36m'; C_GREEN=$'\033[32m'; C_YELLOW=$'\033[33m'; C_RED=$'\033[31m'; C_DIM=$'\033[2m'
[[ -t 1 ]] || C_RESET='' C_BOLD='' C_CYAN='' C_GREEN='' C_YELLOW='' C_RED='' C_DIM=''

NODE_PROJECTS=()
NODE_TYPES=()
NODE_ROOTS=()
DOTNET_PROJECTS=()
DOTNET_TYPES=()
SHARED_PACKAGES=()
FAILED=0
CHANGED=0
LOG_DIR=""

SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
for library in common discovery node dotnet modules; do
  # shellcheck source=/dev/null
  source "$SCRIPT_DIR/lib/$library.sh"
done

usage() {
  cat <<'USAGE'
Dep Upgrader 1.0.0

Usage:
  dep-upgrader.sh [options]

Core:
  --root DIR
  --module all|deps|validate|security|quality|automation|config|dedupe|ui-analysis
  --mode latest|minor|patch|semver-safe|interactive
  --ecosystems all|node|dotnet
  --projects TEXT
  --non-interactive
  --list
  --dry-run
  --yes
  --fail-fast
  --stash

Node and security:
  --audit off|report|fix
  --harden off|balanced|strict
  --fix
  --quality-profile quick|full|deep
  --install-tools

Validation:
  --validate quick|full|build|format|test|run|publish
  --rid RID

Automation:
  --generate-renovate | --no-renovate
  --generate-changesets | --no-changesets

Recovery:
  --rollback ARCHIVE

Mandatory Node policy:
  - recognized .next and node_modules directories are deleted before install;
  - npm workspaces are migrated to pnpm;
  - minimumReleaseAge is set to 10080 minutes (7 days);
  - npm-check-updates is executed once per project directory, never with pnpm -r dlx.
USAGE
}

need_value() { [[ $# -ge 2 && -n "$2" ]] || { printf 'Missing value for %s\n' "$1" >&2; exit 64; }; }
while (($#)); do
  case "$1" in
    --root) need_value "$@"; ROOT="$2"; shift 2 ;;
    --module) need_value "$@"; MODULE="$2"; NON_INTERACTIVE=1; shift 2 ;;
    --mode) need_value "$@"; MODE="$2"; shift 2 ;;
    --audit) need_value "$@"; AUDIT_MODE="$2"; shift 2 ;;
    --validate) need_value "$@"; VALIDATE_PROFILE="$2"; shift 2 ;;
    --quality-profile) need_value "$@"; QUALITY_PROFILE="$2"; shift 2 ;;
    --harden) need_value "$@"; HARDEN="$2"; shift 2 ;;
    --ecosystems) need_value "$@"; ECOSYSTEMS="$2"; shift 2 ;;
    --projects) need_value "$@"; PROJECT_FILTER="$2"; shift 2 ;;
    --rid) need_value "$@"; RID="$2"; shift 2 ;;
    --rollback) need_value "$@"; ROLLBACK_ARCHIVE="$2"; MODULE="rollback"; NON_INTERACTIVE=1; shift 2 ;;
    --non-interactive) NON_INTERACTIVE=1; shift ;;
    --list) LIST_ONLY=1; NON_INTERACTIVE=1; shift ;;
    --dry-run) DRY_RUN=1; shift ;;
    --yes|-y) ASSUME_YES=1; shift ;;
    --fail-fast) KEEP_GOING=0; shift ;;
    --keep-going) KEEP_GOING=1; shift ;;
    --stash) STASH=1; shift ;;
    --fix) FIX=1; shift ;;
    --install-tools) INSTALL_TOOLS=1; shift ;;
    --generate-renovate) GENERATE_RENOVATE=1; shift ;;
    --no-renovate) GENERATE_RENOVATE=0; shift ;;
    --generate-changesets) GENERATE_CHANGESETS=1; shift ;;
    --no-changesets) GENERATE_CHANGESETS=0; shift ;;
    --help|-h) usage; exit 0 ;;
    *) printf 'Unknown option: %s\n' "$1" >&2; usage >&2; exit 64 ;;
  esac
done

ROOT="$(cd "$ROOT" && pwd -P)"
STAMP="$(date -u +%Y%m%dT%H%M%SZ)-$$"
LOG_DIR="$ROOT/.maintenance/logs/$STAMP"
mkdir -p "$LOG_DIR"

main() {
  discover
  if ((LIST_ONLY)); then print_inventory; exit 0; fi
  if ((NON_INTERACTIVE == 0)) && [[ -t 0 && -t 1 ]]; then interactive_menu; fi
  [[ -n "$MODULE" ]] || MODULE=all
  info "Dep Upgrader $VERSION · Module $MODULE · Root $ROOT"
  git_preflight
  require_mutation_consent || return 1

  case "$MODULE" in
    rollback) rollback ;;
    deps) [[ "$ECOSYSTEMS" == dotnet ]] || update_node; [[ "$ECOSYSTEMS" == node ]] || update_dotnet ;;
    security) security ;;
    validate) validate ;;
    quality) quality ;;
    automation) automation ;;
    config) config_analysis ;;
    dedupe) dedupe ;;
    ui-analysis) ui_analysis ;;
    all)
      [[ "$ECOSYSTEMS" == dotnet ]] || update_node
      [[ "$ECOSYSTEMS" == node ]] || update_dotnet
      dedupe; security; validate; quality; config_analysis; ui_analysis
      ;;
    *) err "Unknown module: $MODULE" ;;
  esac

  printf '\n%sSummary%s\n' "$C_BOLD" "$C_RESET"
  printf 'Logs: %s\nChanges: %s\nFailures: %s\n' "$LOG_DIR" "$CHANGED" "$FAILED"
  ((FAILED == 0))
}

main
