#!/usr/bin/env bash
#
# WolfPanel Agent uninstaller (v1 development).
#
# Usage:
#   sudo ./uninstall.sh                 # interactive scope prompt
#   sudo ./uninstall.sh --scope 1|2|3   # non-interactive
#
# Scopes:
#   1) Remove agent only          (service + /opt/wolfpanel)
#   2) + local runtime data       (also /var/lib/wolfpanel)
#   3) Remove everything          (also /etc/wolfpanel config+secrets, logs)
#
# Safe by design: every step tolerates already-missing files.
#
set -uo pipefail

# --- Settings ---------------------------------------------------------------
# Mirrors install.sh's overrides exactly (same env var names, same production
# defaults), so an install that was sandboxed via these vars is uninstalled
# from the SAME paths it was installed to -- never the production defaults.
# This is security-critical: these were previously hardcoded independently of
# install.sh, so a sandboxed/test install still had the real production
# directories wiped on uninstall.
OPT_DIR="${WOLFPANEL_OPT_DIR:-/opt/wolfpanel}"
ETC_DIR="${WOLFPANEL_ETC_DIR:-/etc/wolfpanel}"
VAR_DIR="${WOLFPANEL_VAR_DIR:-/var/lib/wolfpanel}"
LOG_DIR="${WOLFPANEL_LOG_DIR:-/var/log/wolfpanel}"
UNIT="${WOLFPANEL_SYSTEMD_UNIT:-/etc/systemd/system/wolfpanel-agent.service}"

# True as soon as ANY of the four base-directory env overrides is set, i.e.
# this run targets a non-default (sandboxed/test) install. Used to keep the
# real host's systemd service untouched during a sandboxed uninstall, the
# same way the directory removals below stay confined to the override paths.
SANDBOXED=false
if [[ -n "${WOLFPANEL_OPT_DIR:-}${WOLFPANEL_ETC_DIR:-}${WOLFPANEL_VAR_DIR:-}${WOLFPANEL_LOG_DIR:-}" ]]; then
  SANDBOXED=true
fi

# Absolute paths that must never be an rm -rf target, no matter what
# OPT_DIR/ETC_DIR/VAR_DIR/LOG_DIR resolve to. Guards an empty/unset override,
# a typo, or a resolved path landing on a shared system directory.
FORBIDDEN_REMOVE_TARGETS=(
  "/" "/root" "/home" "${HOME:-/root}" "/opt" "/etc" "/var" "/var/lib" "/var/log"
  "/usr" "/bin" "/sbin" "/lib" "/lib64" "/boot" "/dev" "/proc" "/sys" "/srv"
  "/mnt" "/media" "/tmp"
)

SCOPE=""

log() { echo "[wolfpanel] $*"; }
warn() { echo "[wolfpanel][warning] $*" >&2; }
die() { echo "[wolfpanel][fatal] $*" >&2; exit 1; }

warn_identity_left() {
  warn "$1"
  warn "The agent token and server identity remain on disk under ${ETC_DIR}/secrets."
  warn "This server can still be identified/re-used by the panel. Use '--scope 3' to remove everything."
}

parse_args() {
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --scope) SCOPE="${2:-}"; shift 2 ;;
      --scope=*) SCOPE="${1#*=}"; shift ;;
      *) echo "unknown argument: $1" >&2; exit 1 ;;
    esac
  done
}

require_root() {
  [[ "$(id -u)" -eq 0 ]] || { echo "must run as root (use sudo)" >&2; exit 1; }
}

prompt_scope() {
  [[ -n "${SCOPE}" ]] && return
  cat <<EOF
Select uninstall scope:
  1) Remove agent only
  2) Remove agent + local runtime data
  3) Remove everything including config, secrets and logs
EOF
  read -r -p "Scope [1-3]: " SCOPE
}

stop_service() {
  if [[ "${SANDBOXED}" == "true" ]]; then
    log "sandbox override active; skipping real systemd service management"
    return
  fi
  command -v systemctl >/dev/null 2>&1 || { log "systemd not present; skipping"; return; }
  log "stopping and disabling service"
  systemctl stop wolfpanel-agent.service 2>/dev/null || true
  systemctl disable wolfpanel-agent.service 2>/dev/null || true
  if [[ -f "${UNIT}" ]]; then
    rm -f -- "${UNIT}"
    systemctl daemon-reload 2>/dev/null || true
  fi
}

# Refuses to remove anything that is empty, relative, or resolves (after
# following symlinks / ".." segments via realpath) to one of the protected
# roots above, or to any top-level directory (depth < 2) not covered by that
# explicit list. Aborts the whole uninstall on a violation rather than
# skipping it, so a caller relying on this to clean a real path finds out
# immediately if its target was rejected.
safe_remove_path() {
  local raw="$1" label="$2" resolved forbidden depth=0 seg

  [[ -n "${raw}" ]] || die "refusing to remove ${label}: path is empty"
  [[ "${raw}" == /* ]] || die "refusing to remove ${label}: path '${raw}' is not absolute"

  if command -v realpath >/dev/null 2>&1; then
    resolved="$(realpath -m -- "${raw}" 2>/dev/null || true)"
    [[ -n "${resolved}" ]] || die "refusing to remove ${label}: could not resolve path '${raw}'"
  else
    resolved="${raw}"
  fi

  for forbidden in "${FORBIDDEN_REMOVE_TARGETS[@]}"; do
    if [[ "${resolved}" == "${forbidden}" ]]; then
      die "refusing to remove ${label}: resolved to protected path '${resolved}' (from '${raw}')"
    fi
  done

  IFS='/' read -ra _segs <<< "${resolved}"
  for seg in "${_segs[@]}"; do [[ -n "${seg}" ]] && depth=$((depth + 1)); done
  if (( depth < 2 )); then
    die "refusing to remove ${label}: resolved path '${resolved}' is a top-level directory"
  fi

  if [[ -e "${resolved}" ]]; then
    log "removing ${label}: ${resolved}"
    rm -rf -- "${resolved}"
  fi
}

main() {
  parse_args "$@"
  require_root
  prompt_scope

  stop_service
  safe_remove_path "${OPT_DIR}" "agent"

  case "${SCOPE}" in
    1)
      warn_identity_left "agent binaries removed; runtime state (${VAR_DIR}) and config/secrets (${ETC_DIR}) kept"
      ;;
    2)
      safe_remove_path "${VAR_DIR}" "runtime state"
      warn_identity_left "runtime state removed, but config/secrets in ${ETC_DIR} (incl. agent token + server id) are kept"
      ;;
    3)
      safe_remove_path "${VAR_DIR}" "runtime state"
      safe_remove_path "${ETC_DIR}" "config/secrets"
      safe_remove_path "${LOG_DIR}" "logs"
      log "scope 3: removed config, secrets (agent token), runtime state and logs"
      ;;
    *) echo "invalid scope: ${SCOPE}" >&2; exit 1 ;;
  esac

  log "uninstall complete (scope ${SCOPE})"
}

# Run main only when executed directly; sourcing exposes functions for testing
# (mirrors install.sh's same guard).
if [[ "${BASH_SOURCE[0]:-$0}" == "${0}" ]]; then
  main "$@"
fi
