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

SCRIPT_NAME="DANGER-COMPLETE-REMOVE-TVHEADEND-BUTLER-KODI-OPENVPN.sh"
CONFIRM_PHRASE="ERASE TVHEADEND BUTLER KODI OPENVPN"
DRY_RUN=0
WARNINGS=0

# Alternate roots are reserved for automated tests. A public removal always
# operates on the real filesystem and still requires interactive confirmation.
ROOT="${TVH_BUTLER_COMPLETE_UNINSTALL_ROOT:-/}"

usage() {
  cat <<EOF
Usage: sudo bash ${SCRIPT_NAME} [--dry-run]

This is the destructive, website-only removal utility. It completely removes:
  * AmigaRob's TVHeadend Butler, configuration, state, logs, and temporary data
  * TVHeadend packages, local/source installations, accounts, and configuration
  * Kodi packages, Snap/Flatpak/local installations, profiles, and add-on data
  * OpenVPN packages, configuration, and every *.ovpn file on the root filesystem

It deliberately preserves:
  * Butler's custom /opt/ffmpeg-8.1 build and separately installed FFmpeg packages
  * TV recordings and other media stored outside application/config directories

There is no --yes option. A real removal requires a terminal, a normal yes/no
confirmation, and the exact confirmation phrase printed by the script.
EOF
}

while (($#)); do
  case "$1" in
    --dry-run)
      DRY_RUN=1
      ;;
    --help|-h)
      usage
      exit 0
      ;;
    *)
      echo "Unknown option: $1" >&2
      usage >&2
      exit 2
      ;;
  esac
  shift
done

if [[ "$ROOT" != /* ]]; then
  echo "Refusing a non-absolute removal root: $ROOT" >&2
  exit 2
fi
ROOT="${ROOT%/}"
[[ -n "$ROOT" ]] || ROOT="/"

if [[ "$DRY_RUN" != "1" && ${EUID} -ne 0 ]]; then
  echo "Run this complete removal utility with sudo." >&2
  exit 1
fi

rooted() {
  local absolute="$1"
  if [[ "$absolute" != /* || "$absolute" == "/" ]]; then
    echo "Unsafe removal path requested: $absolute" >&2
    exit 2
  fi
  if [[ "$ROOT" == "/" ]]; then
    printf '%s' "$absolute"
  else
    printf '%s%s' "$ROOT" "$absolute"
  fi
}

safe_target() {
  local path="$1"
  [[ -n "$path" && "$path" != "/" && "$path" != "$ROOT" ]] || return 1
  if [[ "$ROOT" != "/" && "$path" != "$ROOT/"* ]]; then
    return 1
  fi
  return 0
}

remove_path() {
  local path="$1"
  safe_target "$path" || {
    echo "Refusing unsafe removal target: $path" >&2
    exit 2
  }
  if [[ ! -e "$path" && ! -L "$path" ]]; then
    return 0
  fi
  echo "Removing $path"
  if [[ "$DRY_RUN" != "1" ]]; then
    rm -rf --one-file-system -- "$path"
  fi
}

remove_matches() {
  local directory="$1"
  shift
  [[ -d "$directory" ]] || return 0
  local pattern path
  for pattern in "$@"; do
    while IFS= read -r -d '' path; do
      remove_path "$path"
    done < <(find "$directory" -mindepth 1 -maxdepth 1 -name "$pattern" -print0 2>/dev/null)
  done
}

declare -a PACKAGE_NAMES=()
collect_packages() {
  PACKAGE_NAMES=()
  if [[ "$ROOT" != "/" ]] || ! command -v dpkg-query >/dev/null 2>&1; then
    return 0
  fi
  mapfile -t PACKAGE_NAMES < <(
    dpkg-query -W -f='${binary:Package}\n' 2>/dev/null \
      | sed 's/:.*$//' \
      | grep -E '^(tvheadend($|-)|kodi($|-)|libkodi|libkodiplatform|openvpn($|-)|network-manager-openvpn($|-))' \
      | sort -u || true
  )
}

declare -a OVPN_FILES=()
collect_ovpn_files() {
  OVPN_FILES=()
  mapfile -d '' -t OVPN_FILES < <(
    find "$ROOT" -xdev \
      \( -path "$(rooted /proc)" -o -path "$(rooted /sys)" -o -path "$(rooted /dev)" \
         -o -path "$(rooted /run)" -o -path "$(rooted /mnt)" -o -path "$(rooted /media)" \) -prune \
      -o -type f -iname '*.ovpn' -print0 2>/dev/null
  )
}

declare -a KODI_APPIMAGES=()
collect_kodi_appimages() {
  KODI_APPIMAGES=()
  local base
  for base in "$(rooted /home)" "$(rooted /root)" "$(rooted /opt)"; do
    [[ -d "$base" ]] || continue
    while IFS= read -r -d '' path; do
      KODI_APPIMAGES+=("$path")
    done < <(find "$base" -xdev -type f -iname '*kodi*.AppImage' -print0 2>/dev/null)
  done
}

declare -a OPENVPN_NM_PROFILES=()
collect_openvpn_networkmanager_profiles() {
  OPENVPN_NM_PROFILES=()
  local directory="$(rooted /etc/NetworkManager/system-connections)"
  [[ -d "$directory" ]] || return 0
  while IFS= read -r -d '' path; do
    if grep -IiqE 'service-type[[:space:]]*=[[:space:]]*org\.freedesktop\.NetworkManager\.openvpn|vpn\.service-type[[:space:]]*=[[:space:]]*org\.freedesktop\.NetworkManager\.openvpn' "$path"; then
      OPENVPN_NM_PROFILES+=("$path")
    fi
  done < <(find "$directory" -mindepth 1 -maxdepth 1 -type f -print0 2>/dev/null)
}

declare -a KNOWN_PATHS=(
  /opt/tvh-butler
  /opt/tvh-butler-releases
  /etc/tvh-butler
  /var/lib/tvh-butler
  /var/log/tvh-butler
  /run/tvh-butler
  /dev/shm/tvh-butler
  /opt/tvh-vpn-router
  /etc/tvh-vpn-router
  /var/lib/tvh-vpn-router
  /var/log/tvh-vpn-router
  /run/tvh-vpn-router
  /dev/shm/tvh-vpn-router
  /etc/tvheadend
  /var/lib/tvheadend
  /var/cache/tvheadend
  /var/log/tvheadend
  /run/tvheadend
  /home/hts/.hts/tvheadend
  /home/hts/.config/tvheadend
  /home/tvheadend/.hts/tvheadend
  /home/tvheadend/.config/tvheadend
  /opt/tvheadend
  /usr/share/tvheadend
  /usr/local/lib/tvheadend
  /usr/local/share/tvheadend
  /opt/kodi
  /usr/lib/kodi
  /usr/share/kodi
  /usr/include/kodi
  /usr/lib/aarch64-linux-gnu/kodi
  /usr/lib/arm-linux-gnueabihf/kodi
  /usr/lib/x86_64-linux-gnu/kodi
  /usr/lib/i386-linux-gnu/kodi
  /usr/local/lib/kodi
  /usr/local/share/kodi
  /usr/local/include/kodi
  /etc/openvpn
  /usr/lib/openvpn
  /usr/share/openvpn
  /usr/local/etc/openvpn
  /var/lib/openvpn
  /var/cache/openvpn
  /var/log/openvpn
  /run/openvpn
  /opt/openvpn
)

collect_packages
collect_ovpn_files
collect_kodi_appimages
collect_openvpn_networkmanager_profiles

cat <<'EOF'

===============================================================================
 DANGER: COMPLETE TV PLATFORM REMOVAL
===============================================================================
This is not the normal Butler uninstaller and it cannot be undone.

It removes Butler, TVHeadend, Kodi, OpenVPN, their accounts/configuration,
caches and temporary files. It also removes every .ovpn file found on the
local root filesystem, including files not created by Butler.

The custom Butler /opt/ffmpeg-8.1 build and separately installed FFmpeg
packages are left in place. TV recordings/media outside application/config
directories are also left in place, but TVHeadend's DVR database and
configuration are erased.
EOF

echo
echo "Detected package(s) to purge: ${#PACKAGE_NAMES[@]}"
if ((${#PACKAGE_NAMES[@]})); then
  printf '  %s\n' "${PACKAGE_NAMES[@]}"
fi
echo "Detected .ovpn file(s) to erase: ${#OVPN_FILES[@]}"
if ((${#OVPN_FILES[@]})); then
  printf '  %s\n' "${OVPN_FILES[@]:0:25}"
  if ((${#OVPN_FILES[@]} > 25)); then
    echo "  ... and $((${#OVPN_FILES[@]} - 25)) more"
  fi
fi
echo "Detected Kodi AppImage file(s) to erase: ${#KODI_APPIMAGES[@]}"
echo "Detected OpenVPN NetworkManager profile(s) to erase: ${#OPENVPN_NM_PROFILES[@]}"

echo "Known application/configuration paths currently present:"
present=0
for absolute in "${KNOWN_PATHS[@]}"; do
  target="$(rooted "$absolute")"
  if [[ -e "$target" || -L "$target" ]]; then
    echo "  $target"
    present=$((present + 1))
  fi
done
[[ "$present" -gt 0 ]] || echo "  none detected"

if [[ "$DRY_RUN" == "1" ]]; then
  echo
  echo "Dry-run inventory complete. No files, packages, services, or accounts were changed."
  exit 0
fi

if [[ ! -r /dev/tty || ! -w /dev/tty ]]; then
  echo "An interactive terminal is required. Do not pipe this script directly into bash." >&2
  exit 2
fi

printf '\nContinue with complete removal? [y/N] ' >/dev/tty
answer=""
read -r answer </dev/tty || true
case "${answer,,}" in
  y|yes) ;;
  *) echo "Complete removal cancelled."; exit 1 ;;
esac

echo "Type this exact phrase to authorize permanent removal:" >/dev/tty
echo "  $CONFIRM_PHRASE" >/dev/tty
typed=""
read -r typed </dev/tty || true
if [[ "$typed" != "$CONFIRM_PHRASE" ]]; then
  echo "Confirmation phrase did not match. Nothing was removed." >&2
  exit 1
fi

echo
echo "Stopping related services"
if command -v systemctl >/dev/null 2>&1; then
  systemctl disable --now tvh-butler-update.timer tvh-butler-update.service tvh-butler.service >/dev/null 2>&1 || true
  systemctl disable --now tvh-vpn-router.service tvh-router.service >/dev/null 2>&1 || true
  systemctl disable --now tvheadend.service kodi.service kodi-standalone.service >/dev/null 2>&1 || true
  systemctl disable --now openvpn.service openvpn-client.target openvpn-server.target >/dev/null 2>&1 || true
  systemctl stop 'openvpn-client@*.service' 'openvpn-server@*.service' 'kodi@*.service' >/dev/null 2>&1 || true
fi

# Let Butler close routes through its own ownership records before state is erased.
if [[ -x "$(rooted /usr/local/sbin/tvh-butler-uninstall)" ]]; then
  "$(rooted /usr/local/sbin/tvh-butler-uninstall)" --purge --yes || true
elif [[ -x "$(rooted /opt/tvh-butler/uninstall.sh)" ]]; then
  "$(rooted /opt/tvh-butler/uninstall.sh)" --purge --yes || true
fi

# Remove only namespaces carrying Butler's explicit ownership marker.
if command -v ip >/dev/null 2>&1 && [[ -d "$(rooted /etc/netns)" ]]; then
  while IFS= read -r -d '' marker; do
    namespace="$(basename "$(dirname "$marker")")"
    echo "Removing Butler-owned network namespace $namespace"
    while read -r pid; do
      [[ "$pid" =~ ^[0-9]+$ ]] && kill "$pid" >/dev/null 2>&1 || true
    done < <(ip netns pids "$namespace" 2>/dev/null || true)
    ip netns delete "$namespace" >/dev/null 2>&1 || true
    remove_path "$(dirname "$marker")"
  done < <(find "$(rooted /etc/netns)" -mindepth 2 -maxdepth 2 -name '.tvh-butler-owned' -print0 2>/dev/null)
fi

if command -v pkill >/dev/null 2>&1; then
  pkill -TERM -x tvheadend >/dev/null 2>&1 || true
  pkill -TERM -x kodi >/dev/null 2>&1 || true
  pkill -TERM -x kodi.bin >/dev/null 2>&1 || true
  pkill -TERM -x openvpn >/dev/null 2>&1 || true
fi

if ((${#PACKAGE_NAMES[@]})) && command -v apt-get >/dev/null 2>&1; then
  echo "Purging Debian package(s): ${PACKAGE_NAMES[*]}"
  if ! DEBIAN_FRONTEND=noninteractive apt-get purge -y -- "${PACKAGE_NAMES[@]}"; then
    echo "WARNING: one or more Debian packages could not be purged; continuing filesystem cleanup." >&2
    WARNINGS=$((WARNINGS + 1))
  fi
fi

if command -v snap >/dev/null 2>&1 && snap list kodi >/dev/null 2>&1; then
  echo "Removing Kodi Snap"
  if ! snap remove --purge kodi; then
    echo "WARNING: Kodi Snap removal failed; continuing cleanup." >&2
    WARNINGS=$((WARNINGS + 1))
  fi
fi

if command -v flatpak >/dev/null 2>&1; then
  if flatpak list --system --app --columns=application 2>/dev/null | grep -Fxq 'tv.kodi.Kodi'; then
    echo "Removing system Kodi Flatpak"
    if ! flatpak uninstall --system --delete-data -y tv.kodi.Kodi; then
      echo "WARNING: system Kodi Flatpak removal failed; continuing cleanup." >&2
      WARNINGS=$((WARNINGS + 1))
    fi
  fi
  while IFS=: read -r user_name _password _uid _gid _gecos user_home _shell; do
    [[ "$user_home" == /* && -d "$user_home" ]] || continue
    if command -v runuser >/dev/null 2>&1; then
      runuser -u "$user_name" -- flatpak uninstall --user --delete-data -y tv.kodi.Kodi >/dev/null 2>&1 || true
    fi
  done < <(getent passwd 2>/dev/null || true)
fi

echo "Removing application and configuration paths"
for absolute in "${KNOWN_PATHS[@]}"; do
  remove_path "$(rooted "$absolute")"
done

for absolute in \
  /usr/local/sbin/tvh-butler-setup /usr/local/sbin/tvh-butler-uninstall \
  /usr/local/bin/tvh-butler-setup /usr/local/bin/tvh-butler-uninstall \
  /usr/local/bin/tvheadend /usr/local/sbin/tvheadend \
  /usr/bin/tvheadend /usr/share/tvheadend \
  /usr/local/bin/kodi /usr/local/bin/kodi-standalone /usr/local/bin/kodi-send \
  /usr/bin/kodi /usr/bin/kodi-standalone /usr/bin/kodi-send \
  /usr/local/bin/openvpn /usr/local/sbin/openvpn /usr/sbin/openvpn \
  /etc/default/openvpn /etc/default/tvheadend \
  /etc/init.d/openvpn /etc/init.d/tvheadend /etc/init.d/kodi; do
  remove_path "$(rooted "$absolute")"
done

remove_matches "$(rooted /etc/systemd/system)" \
  'tvh-butler*' 'tvh-vpn-router*' 'tvh-router*' 'tvheadend*' 'kodi*' 'openvpn*'
remove_matches "$(rooted /etc/apt/sources.list.d)" \
  'tvheadend*' 'team-xbmc*' 'kodi*' 'openvpn*'
remove_matches "$(rooted /etc/apt/keyrings)" \
  'tvheadend*' 'team-xbmc*' 'kodi*' 'openvpn*'
remove_matches "$(rooted /etc/apt/trusted.gpg.d)" \
  'tvheadend*' 'team-xbmc*' 'kodi*' 'openvpn*'
remove_matches "$(rooted /usr/share/keyrings)" \
  'tvheadend*' 'team-xbmc*' 'kodi*' 'openvpn*'
remove_matches "$(rooted /tmp)" \
  'tvh-butler*' 'tvh-vpn-router*' 'tvheadend*' 'kodi*' 'openvpn*'
remove_matches "$(rooted /var/tmp)" \
  'tvh-butler*' 'tvh-vpn-router*' 'tvheadend*' 'kodi*' 'openvpn*'

echo "Removing Kodi profiles and local user data"
declare -a HOME_DIRS=("$(rooted /root)")
if [[ -d "$(rooted /home)" ]]; then
  while IFS= read -r -d '' home_dir; do
    HOME_DIRS+=("$home_dir")
  done < <(find "$(rooted /home)" -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null)
fi
for home_dir in "${HOME_DIRS[@]}"; do
  for relative in \
    .kodi .config/kodi .cache/kodi .local/share/kodi .var/app/tv.kodi.Kodi \
    snap/kodi .config/autostart/kodi.desktop .config/autostart/kodi-standalone.desktop; do
    remove_path "$home_dir/$relative"
  done
done

for path in "${KODI_APPIMAGES[@]}"; do
  remove_path "$path"
done

echo "Removing all discovered OpenVPN profile files"
for path in "${OVPN_FILES[@]}"; do
  remove_path "$path"
done

echo "Removing OpenVPN NetworkManager profiles"
for path in "${OPENVPN_NM_PROFILES[@]}"; do
  remove_path "$path"
done

# Remove dedicated service identities without recursively deleting their homes;
# this avoids erasing recordings that may have been placed elsewhere under them.
for service_user in hts tvheadend; do
  if getent passwd "$service_user" >/dev/null 2>&1; then
    echo "Removing service account $service_user (home/media files are not recursively deleted)"
    userdel "$service_user" >/dev/null 2>&1 || true
  fi
  if getent group "$service_user" >/dev/null 2>&1; then
    groupdel "$service_user" >/dev/null 2>&1 || true
  fi
done

if command -v systemctl >/dev/null 2>&1; then
  systemctl daemon-reload >/dev/null 2>&1 || true
  systemctl reset-failed >/dev/null 2>&1 || true
fi

cat <<'EOF'

Complete removal finished.

Removed: Butler, TVHeadend, Kodi, OpenVPN, their known configuration/state,
local profiles, temporary files, and root-filesystem .ovpn files.

Preserved: the custom Butler FFmpeg build, packaged FFmpeg, and recordings or
other media outside the removed application/configuration directories.
EOF

if ((WARNINGS)); then
  echo "WARNING: removal completed with $WARNINGS package-manager warning(s). Review the messages above." >&2
  exit 1
fi
