#!/bin/bash
#
# uninstall_weda.sh — remove every file the WEDA Node installer places on a device.
#
# Mirrors the full install footprint of BOTH stages:
#   1. the platform installer script (install_weda_{ubuntu_x64,jetson,yocto_arm64}.sh):
#      the activator binary, rootfs/opt -> /opt, rootfs/usr -> /usr, docker-compose.yml,
#      weda-images.tar.gz, and the res/board hardware-model cache
#   2. the activator binary (InstallWEDANode): res/{masterkey,authinfo}.bin, .meta/,
#      installation.json, data/, .last_install_error, and a re-run of the embedded
#      rootfs (which also drops /masterKey.bin* at filesystem root)
#   plus the Docker containers, network, volumes, and images from docker-compose.yml,
#   and the per-user desktop icons that setup-desktop-icons.sh copies at login.
#
# Run as root on the device. Idempotent and safe to re-run.
#
# Usage:
#   sudo ./uninstall_weda.sh [--dry-run] [--yes] [--keep-activator] [--keep-identity]
#                            [--install-base DIR]
#
#   --dry-run         List what would be removed; change nothing.
#   --yes             Do not prompt for confirmation.
#   --keep-activator  Preserve the wizard binary + its desktop launchers so the node
#                     can be re-installed from the TUI (removes everything else).
#   --keep-identity   Preserve res/board (the hardware-model cache / device identity).
#   --install-base    Advantech install root (default: /opt/Advantech).
#
set -u

INSTALL_BASE="/opt/Advantech"
DRY_RUN=0
ASSUME_YES=0
KEEP_ACTIVATOR=0
KEEP_IDENTITY=0

# The three WEDA container/image names from docker-compose.template.yml.
CONTAINERS="dmagent system-agent watchtower"
IMAGES="edgesync.azurecr.io/weda-edge/dmagent:v1.1 edgesync.azurecr.io/weda-edge/system-agent:v1.1 edgesync.azurecr.io/weda-edge/watchtower:v1.0"
# docker-compose derives the project name from the compose file's parent directory
# (/opt/Advantech/dmagent -> "dmagent"), so the bridge network is dmagent_weda_network.
COMPOSE_NETWORK="dmagent_weda_network"

usage() { sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//'; exit "${1:-0}"; }

while [ $# -gt 0 ]; do
    case "$1" in
        --dry-run)        DRY_RUN=1 ;;
        --yes|-y)         ASSUME_YES=1 ;;
        --keep-activator) KEEP_ACTIVATOR=1 ;;
        --keep-identity)  KEEP_IDENTITY=1 ;;
        --install-base)   INSTALL_BASE="${2:?--install-base needs a directory}"; shift ;;
        -h|--help)        usage 0 ;;
        *) echo "unknown option: $1" >&2; usage 1 ;;
    esac
    shift
done

if [ "$(id -u)" -ne 0 ]; then
    echo "Error: this uninstaller must be run as root (sudo)." >&2
    exit 1
fi

DMAGENT_DIR="$INSTALL_BASE/dmagent"
SYSTEM_AGENT_DIR="$INSTALL_BASE/system-agent"
SYSTEM_MONITOR_DIR="$INSTALL_BASE/system-monitor"
COMPOSE_FILE="$DMAGENT_DIR/docker-compose.yml"

removed=0
rm_path() {  # rm_path <path>  — remove a file/dir/symlink, honoring --dry-run
    local p="$1"
    if [ ! -e "$p" ] && [ ! -L "$p" ]; then return 0; fi
    if [ "$DRY_RUN" -eq 1 ]; then
        echo "  [dry-run] would remove: $p"
    elif rm -rf -- "$p"; then
        echo "  removed: $p"
    else
        echo "  FAILED : $p" >&2
    fi
    removed=$((removed + 1))
}

rmdir_if_empty() {  # rmdir a directory only if it is empty
    local d="$1"
    [ -d "$d" ] || return 0
    [ -n "$(ls -A "$d" 2>/dev/null)" ] && return 0
    if [ "$DRY_RUN" -eq 1 ]; then
        echo "  [dry-run] would rmdir (empty): $d"
    else
        rmdir "$d" 2>/dev/null && echo "  rmdir  : $d"
    fi
}

detect_compose() {  # echo the docker compose invocation (v2 plugin or v1 binary), or empty
    if docker compose version >/dev/null 2>&1; then
        echo "docker compose"
    elif command -v docker-compose >/dev/null 2>&1; then
        echo "docker-compose"
    fi
}

# ---------------------------------------------------------------------------
echo "=== WEDA Node uninstaller ==="
echo "  install base   : $INSTALL_BASE"
echo "  keep activator : $([ $KEEP_ACTIVATOR -eq 1 ] && echo yes || echo no)"
echo "  keep identity  : $([ $KEEP_IDENTITY -eq 1 ] && echo yes || echo no)"
echo "  mode           : $([ $DRY_RUN -eq 1 ] && echo DRY-RUN || echo EXECUTE)"
echo

if [ "$DRY_RUN" -eq 0 ] && [ "$ASSUME_YES" -eq 0 ]; then
    if [ -t 0 ]; then
        printf "This will remove WEDA Node containers and files. Continue? [y/N] "
        read -r ans
        case "$ans" in y|Y|yes|YES) ;; *) echo "Aborted."; exit 0 ;; esac
    else
        echo "Refusing to run non-interactively without --yes (or use --dry-run)." >&2
        exit 1
    fi
fi

# --- Step 1: tear down Docker containers, network, volumes, and images -------
echo "[1/6] Removing Docker containers, network, volumes, and images..."
COMPOSE="$(detect_compose)"
if [ -n "$COMPOSE" ] && [ -f "$COMPOSE_FILE" ]; then
    echo "  using: $COMPOSE -f $COMPOSE_FILE down --volumes --rmi all --remove-orphans"
    if [ "$DRY_RUN" -eq 0 ]; then
        # shellcheck disable=SC2086
        $COMPOSE -f "$COMPOSE_FILE" down --volumes --rmi all --remove-orphans 2>&1 | sed 's/^/    /' || \
            echo "    (compose down reported errors; continuing with manual cleanup)"
    fi
fi
# Manual fallback / belt-and-suspenders: force-remove by name in case compose is
# gone, the file was already deleted, or containers were started detached.
if command -v docker >/dev/null 2>&1; then
    for c in $CONTAINERS; do
        if docker ps -a --format '{{.Names}}' 2>/dev/null | grep -qx "$c"; then
            if [ "$DRY_RUN" -eq 1 ]; then echo "  [dry-run] would: docker rm -f $c"
            else docker rm -f "$c" >/dev/null 2>&1 && echo "  container removed: $c"; fi
        fi
    done
    if docker network ls --format '{{.Name}}' 2>/dev/null | grep -qx "$COMPOSE_NETWORK"; then
        if [ "$DRY_RUN" -eq 1 ]; then echo "  [dry-run] would: docker network rm $COMPOSE_NETWORK"
        else docker network rm "$COMPOSE_NETWORK" >/dev/null 2>&1 && echo "  network removed: $COMPOSE_NETWORK"; fi
    fi
    for img in $IMAGES; do
        if docker image inspect "$img" >/dev/null 2>&1; then
            if [ "$DRY_RUN" -eq 1 ]; then echo "  [dry-run] would: docker rmi $img"
            else docker rmi "$img" >/dev/null 2>&1 && echo "  image removed: $img"; fi
        fi
    done
else
    echo "  docker not found; skipping container/image cleanup."
fi

# --- Step 2: the dmagent tree ------------------------------------------------
echo "[2/6] Removing $DMAGENT_DIR ..."
if [ $KEEP_ACTIVATOR -eq 0 ] && [ $KEEP_IDENTITY -eq 0 ]; then
    rm_path "$DMAGENT_DIR"
else
    # Selective removal so we can preserve the binary and/or the board cache.
    for child in docker-compose.yml installation.json .last_install_error \
                 weda-images.tar.gz .meta data scripts; do
        rm_path "$DMAGENT_DIR/$child"
    done
    if [ $KEEP_IDENTITY -eq 1 ]; then
        # Remove everything under res/ except the board hardware-model cache.
        if [ -d "$DMAGENT_DIR/res" ]; then
            for entry in "$DMAGENT_DIR"/res/*; do
                [ -e "$entry" ] || continue
                [ "$(basename "$entry")" = "board" ] && continue
                rm_path "$entry"
            done
        fi
    else
        rm_path "$DMAGENT_DIR/res"
    fi
    [ $KEEP_ACTIVATOR -eq 0 ] && rm_path "$DMAGENT_DIR/weda-node-activator"
    rmdir_if_empty "$DMAGENT_DIR"
fi

# --- Step 3: sibling /opt/Advantech install files ----------------------------
echo "[3/6] Removing system-agent, system-monitor, and helper scripts..."
rm_path "$SYSTEM_AGENT_DIR"
rm_path "$SYSTEM_MONITOR_DIR"
rm_path "$INSTALL_BASE/show-weda-versions.sh"
rmdir_if_empty "$INSTALL_BASE"

# --- Step 4: files the embedded rootfs drops outside /opt --------------------
echo "[4/6] Removing rootfs-installed system files (/, /etc, /usr)..."
# Sample master keys the embedded rootfs writes to filesystem root.
rm_path "/masterKey.bin"
rm_path "/masterKey.bin.sandbox02"
# The uninstaller itself (install.sh drops it at /usr/local/bin). Safe to
# self-remove: the running shell keeps its open fd after the file is unlinked.
# Kept when --keep-activator so the node can still be fully removed later.
if [ $KEEP_ACTIVATOR -eq 0 ]; then
    rm_path "/usr/local/bin/uninstall_weda.sh"
fi
# Desktop launchers, autostart entry, skeleton copies, and backgrounds — unless
# --keep-activator, which keeps the launchers so the wizard stays reachable.
if [ $KEEP_ACTIVATOR -eq 0 ]; then
    for f in weda-intro.desktop weda-learninghub.desktop weda-node.desktop; do
        rm_path "/usr/share/applications/$f"
        rm_path "/etc/skel/Desktop/$f"
    done
    rm_path "/etc/xdg/autostart/weda-desktop-setup.desktop"
    rm_path "/usr/share/backgrounds/favicon-advantech.ico"
    rm_path "/usr/share/backgrounds/WEDA_Node_Wizard.ico"
    rmdir_if_empty "/etc/skel/Desktop"
fi

# --- Step 5: per-user desktop icons + state ----------------------------------
echo "[5/6] Removing per-user desktop icons and setup state..."
if [ $KEEP_ACTIVATOR -eq 0 ]; then
    # Every real home: /root plus each /home/* directory.
    homes="/root"
    for h in /home/*; do [ -d "$h" ] && homes="$homes $h"; done
    for h in $homes; do
        for f in weda-node.desktop weda-intro.desktop weda-learninghub.desktop; do
            rm_path "$h/Desktop/$f"
        done
        rm_path "$h/.config/weda-desktop-icons-setup.done"
        rm_path "$h/.cache/weda-desktop-icons-setup.log"
    done
fi

# --- Step 6: summary ---------------------------------------------------------
echo "[6/6] Done."
if [ "$DRY_RUN" -eq 1 ]; then
    echo "Dry-run complete — $removed path(s) would be removed. Re-run without --dry-run to apply."
else
    echo "Uninstall complete — $removed path(s) processed."
    [ $KEEP_IDENTITY -eq 1 ]  && echo "Preserved: $DMAGENT_DIR/res/board (device identity)."
    [ $KEEP_ACTIVATOR -eq 1 ] && echo "Preserved: activator binary + desktop launchers (re-install from the wizard)."
fi
