#!/usr/bin/env bash
#   2026-07-07 yjmoon  TRINITY PRISM 완전 초기화(clean reset) — 최초 설치 전 기존 흔적 박멸.
#     "재설치했는데 옛 버전이 서빙" 사고 근본 차단. 신규 설치분 대상 —
#     컨테이너=trinity-prism-* (이름) · 볼륨/네트워크=prism-cosmos-prod (프로젝트) · 이미지=trinity-prism-*.
#     저번주·오늘 설치 모두 trinity-prism-* 이름이라 이 기준으로 전부 포착. dev(cosmos-*/prism-cosmos-dev)는 제외.
#     지우는 대상 = 컨테이너 · 볼륨(system-store 포함) · 네트워크 · 우리 이미지 · 설치 디렉토리.
#     남기는 것 = 외부 베이스 이미지(postgres/es/redis, --base-images 로만 삭제) · 사용자 백업 tar.
#
#   왜 볼륨·이미지·디렉토리까지 지우나 (재설치가 "새 설치"로 성립하려면):
#     · 볼륨: 데이터 볼륨이 남으면 install.sh 가 "기존 설치 감지"로 재설치를 막고(die), --force 로
#       뚫어도 빈 DB 첫 기동이 아니라 최신 스키마가 안 깔림 → 코드-DB 불일치로 로그인 오류.
#     · 설치 디렉토리(/opt/prism-cosmos): gateway 가 app/frontend 를 호스트에서 직마운트(bind)하고
#       .env(옛 시크릿·base-path)도 여기 있음. 안 지우면 옛 dist·설정이 살아남아 새 설치가 안 먹음.
#     · 이미지: 같은 버전태그 잔존 시 재로드가 꼬일 수 있어 함께 제거 → 번들에서 깨끗이 재로드.
#
#   사용법 (설치담당자):
#     bash uninstall.sh                   # 대화형 - 경고 1회 확인 후 전량 삭제 + 최종 검증
#     bash uninstall.sh --dry-run         # 지울 목록만 출력, 실제 삭제 안 함 (먼저 확인용)
#     bash uninstall.sh -y                # 무인 - 확인 프롬프트 생략 (자동화/재설치 스크립트)
#     bash uninstall.sh --keep-store      # system-store 볼륨(라이선스/설정)만 보존
#     bash uninstall.sh --keep-files      # 호스트 설치 디렉토리 보존 (컨테이너/볼륨/이미지만)
#     bash uninstall.sh --base-images     # postgres/es/redis 등 외부 베이스 이미지도 삭제
#     bash uninstall.sh --engine=podman   # docker/podman 공존 호스트 - 설치 때 쓴 엔진 지정
#
#   ※ 실행 권한은 설치 때와 같아야 합니다.
#     - root 설치(/opt, /var/lib) : sudo bash uninstall.sh
#     - rootless podman 설치($HOME) : sudo 없이 그대로. 붙이면 root 의 podman 을 보게 돼 대상이 안 잡힙니다.
#   ※ 개발 스택(prism-yjmoon, cosmos-*-yjmoon 등)은 대상 아님 — 이름이 안 겹칩니다.

#   -e 는 쓰지 않음: 정리 중 개별 리소스가 이미 없어 실패해도 계속 진행(멱등). pipefail 은 유지.
set -uo pipefail

# ── 대상 SSOT (설치기 install.sh / docker-compose.yml 와 동일) ────────────────
#   신규 prod 설치분만 정밀 타겟 (사용자 지시 2026-07-07: 신규 trinity-prism-* 만.
#   legacy cosmos-*/prism-rel-* 및 dev 개발스택(prism-cosmos-dev / cosmos-*)은 대상 제외).
COMPOSE_PROJECT="prism-cosmos-prod"                     # 신규 설치 compose 프로젝트 (라벨·볼륨/네트워크 prefix)
NAME_PREFIX="trinity-prism-"                            # 컨테이너 이름 베이스 (신규 설치 — 이 이름만 잡음)
#   2026-07-30 jypark1385  우리 이미지 수집을 목록에서 접두어 매칭으로 변경. 종전엔
#     gateway/tap/cosmos/tve 4개만 등재해 updater 와 report 가 미삭제(실측: .94 25개 중 11개,
#     .88 14개 중 5개 잔존). 서비스 추가 때마다 여기를 같이 고쳐야 하던 구조가 원인.
IMAGE_PREFIX="trinity-prism-"                           # 우리 앱 이미지 repo 접두어 (compose 의 image: 와 동일)
#   external:true 볼륨 — compose down 으로 안 지워짐(설치기가 docker volume create 로 별도 생성).
#   prod 전용(dev 는 호스트 system_store bind 라 named volume 미사용 → 삭제해도 dev 무관).
STORE_VOLS=(cosmos-system-store cosmos-prod-system-store trinity-prism-system-store)
#   외부 베이스 이미지 — 기본 보존(재다운로드 부담·폐쇄망). --base-images 시에만 삭제.
BASE_IMAGE_REPOS=(postgres elasticsearch redis temporalio/auto-setup temporalio/ui)
#   설치 디렉토리(호스트) — bind 프론트/.env/system_store/app-overlay 잔존 제거. prod 경로만.
#   2026-07-24  rootless podman 은 $HOME 하위 설치라 제거 대상 경로가 다름 — 권한별 분기(비root=$HOME, root=/opt·/var/lib).
#   2026-08-03 jypark1385  안내 문구의 sudo 도 같은 기준으로 갈린다. rootless 설치 계정에 sudo 를
#     권하면 root 의 podman 을 보게 돼 아무것도 안 잡히고, 사용자는 제거가 끝난 줄로 오해한다.
#   2026-08-07 jypark1385  설치 디렉토리는 실행 권한이 아니라 실제로 있는지로 잡는다. 종전엔 EUID 로 목록을
#     갈라(비root=$HOME, root=/opt, /var/lib) docker(root) 설치본을 sudo 없이 제거하면 컨테이너, 볼륨,
#     이미지 22건은 다 지우고 /opt/prism-cosmos 만 남긴 채 "✓✓ CLEAN" 으로 끝났다(실서버 실측).
#     그 폴더에 프론트 dist 와 .env 가 있어 재설치하면 옛 화면이 그대로 뜬다 - 이 스크립트가 막으려던
#     사고 그 자체다. 이제 셋 다 후보로 두고, 권한이 없으면 삭제 실패로 드러나 안내가 나간다.
INSTALL_DIRS=("$HOME/prism-cosmos" /opt/prism-cosmos /var/lib/prism)
if [ "${EUID:-$(id -u)}" -ne 0 ]; then _AS_ROOT=""; else _AS_ROOT="sudo "; fi

# ── 플래그 ────────────────────────────────────────────────────────────────────
AUTO=0; DRY=0; KEEP_STORE=0; KEEP_FILES=0; DEL_BASE=0; ENGINE_ARG=""
for arg in "$@"; do
  case "$arg" in
    -y|--yes)      AUTO=1 ;;
    --dry-run)     DRY=1 ;;
    --keep-store)  KEEP_STORE=1 ;;
    --keep-files)  KEEP_FILES=1 ;;
    --base-images) DEL_BASE=1 ;;
    --engine=*)    ENGINE_ARG="${arg#*=}" ;;
    -h|--help)
      #   2026-08-07 jypark1385  줄 번호 고정(sed '2,30p')은 헤더가 늘어날 때마다 어긋나 본문(set -uo
      #     pipefail)까지 샜다. 사용법 블록은 첫 빈 줄에서 끝나므로 그 경계로 자른다.
      awk 'NR>1 && /^[[:space:]]*$/ {exit} NR>1' "$0"; exit 0 ;;
    *) echo "[ERROR] 알 수 없는 인자: $arg  (--help 참조)" >&2; exit 1 ;;
  esac
done

# ── 색상/헬퍼 ─────────────────────────────────────────────────────────────────
if [ -t 1 ]; then
  RED=$'\033[31m'; GREEN=$'\033[32m'; YEL=$'\033[33m'; CYAN=$'\033[36m'; BOLD=$'\033[1m'; NC=$'\033[0m'
else
  RED=""; GREEN=""; YEL=""; CYAN=""; BOLD=""; NC=""
fi
say()  { printf "%s\n" "$*"; }
head() { printf "\n${CYAN}${BOLD}== %s ==${NC}\n" "$*"; }
die()  { printf "${RED}${BOLD}[ERROR]${NC} %s\n" "$*" >&2; exit 1; }

# ── 컨테이너 엔진 판정 ────────────────────────────────────────────────────────
#   우선순위: --engine= > PRISM_ENGINE(env) > 설치 폴더 .env > 자동판정(설치 엔진/실제 잔존 흔적)
#   2026-07-24  설치 폴더($ROOT)에 함께 복사된 engine.sh 를 source. 폴백: PRISM_ENGINE(.env) 또는 docker.
#   2026-07-30 jypark1385  .env 에 기록된 확정 엔진을 먼저 export - engine.sh 는 docker/podman 공존 시
#     --engine= 을 요구하며 중단한다. 제거는 설치 때 정해진 엔진을 그대로 따라가야 하므로 여기서 읽는다.
#   2026-08-07 jypark1385  --engine= 신설 + 공존 호스트 자동판정. 종전엔 이 인자가 install.sh 에만 있어
#     .env 가 없는 번들 폴더에서 제거를 돌리면 (가) engine.sh 가 중단 → ENGINE 미설정 → "unbound
#     variable" 로 죽고, (나) engine.sh 없이 따로 받은 사본은 조용히 docker 로 고정돼 podman 설치본을
#     "이미 깨끗한 환경" 으로 잘못 보고했다(둘 다 실측). 엔진을 못 고르는 게 아니라 고를 수단이 없던 것이 원인.
_H="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENGINE_SRC=""
if [ -n "$ENGINE_ARG" ]; then
  PRISM_ENGINE="$ENGINE_ARG"; ENGINE_SRC="--engine 인자"
elif [ -n "${PRISM_ENGINE:-}" ]; then
  ENGINE_SRC="PRISM_ENGINE 환경변수"
elif [ -f "$_H/.env" ]; then
  #   2026-08-03 jypark1385  head 는 이 파일에서 섹션 제목 함수로 재정의돼 있다. 파이프로 head -1 을
  #     쓰면 값 대신 제목 문자열이 들어가 엔진 판정이 깨지므로, 첫 줄 자르기는 grep -m1 로 한다.
  PRISM_ENGINE="$(grep -m1 '^PRISM_ENGINE=' "$_H/.env" 2>/dev/null | cut -d= -f2- | tr -d '"')"
  [ -n "$PRISM_ENGINE" ] && ENGINE_SRC="설치 폴더 .env"
fi

#   설치 흔적 개수 세기 - 공존 호스트에서 "어느 엔진에 깔려 있나" 를 추측 대신 실측으로 가른다.
#   권한이 없어 조회 자체가 안 되면 0 (그 엔진은 후보에서 빠짐).
_count_traces() {   # $1=engine → 잔존 리소스 수
  local e="$1" n=0
  command -v "$e" >/dev/null 2>&1 || { echo 0; return 0; }
  "$e" ps >/dev/null 2>&1 || { echo 0; return 0; }
  n=$( { "$e" ps -a --format '{{.Names}}'
         "$e" volume ls --format '{{.Name}}'
         "$e" network ls --format '{{.Name}}'
         "$e" images --format '{{.Repository}}:{{.Tag}}'
       } 2>/dev/null | grep -cE "(^|/)${NAME_PREFIX}|^${COMPOSE_PROJECT}_|-system-store$" )
  echo "${n:-0}"
}

if [ -z "${PRISM_ENGINE:-}" ]; then
  _cand=()
  command -v docker >/dev/null 2>&1 && _cand+=(docker)
  command -v podman >/dev/null 2>&1 && _cand+=(podman)
  #   podman-docker 대체 명령: 'docker' 가 실제로는 podman 이면 공존이 아니다(engine.sh 와 같은 기준).
  if [ "${#_cand[@]}" -eq 2 ] && docker --version 2>/dev/null | grep -qiw podman; then _cand=(podman); fi
  if [ "${#_cand[@]}" -eq 2 ]; then
    _nd="$(_count_traces docker)"; _np="$(_count_traces podman)"
    if   [ "$_nd" -gt 0 ] && [ "$_np" -eq 0 ]; then PRISM_ENGINE=docker; ENGINE_SRC="자동판정(흔적 docker ${_nd}건)"
    elif [ "$_np" -gt 0 ] && [ "$_nd" -eq 0 ]; then PRISM_ENGINE=podman; ENGINE_SRC="자동판정(흔적 podman ${_np}건)"
    elif [ "$_nd" -gt 0 ] && [ "$_np" -gt 0 ]; then
      die "docker(${_nd}건), podman(${_np}건) 양쪽에 설치 흔적이 있습니다 - 어느 쪽을 지울지 지정하세요.
       예)  bash uninstall.sh --engine=podman        (설치 때 쓴 엔진과 같아야 합니다)
            bash uninstall.sh --engine=docker"
    else
      #   양쪽 다 흔적 없음 - 컨테이너 자원은 어차피 빈 목록. 디렉토리 정리는 그대로 진행한다.
      PRISM_ENGINE=docker; ENGINE_SRC="자동판정(양쪽 모두 흔적 없음)"
    fi
  elif [ "${#_cand[@]}" -eq 1 ]; then
    PRISM_ENGINE="${_cand[0]}"; ENGINE_SRC="자동판정(설치된 엔진)"
  fi
fi

case "${PRISM_ENGINE:-}" in
  docker|podman) ;;
  "") die "컨테이너 엔진을 찾지 못했습니다 - docker/podman 이 설치돼 있고 실행 권한이 있는지 확인하세요." ;;
  *)  die "알 수 없는 --engine=${PRISM_ENGINE} (docker | podman)" ;;
esac
export PRISM_ENGINE
if [ -f "$_H/engine.sh" ]; then . "$_H/engine.sh" || true; fi
#   engine.sh 가 없거나 중간에 빠져나가도 여기서 확정 - "ENGINE: unbound variable" 재발 방지.
ENGINE="${ENGINE:-$PRISM_ENGINE}"

# ── 사전 점검 ─────────────────────────────────────────────────────────────────
command -v "$ENGINE" >/dev/null 2>&1 || die "$ENGINE 미설치 - $ENGINE 명령을 찾을 수 없습니다. 설치 때 쓴 엔진이 다르면 --engine= 으로 지정하세요."
"$ENGINE" ps >/dev/null 2>&1 || die "$ENGINE 명령 실패 - 적절한 권한(예: docker 그룹/sudo, 또는 rootless podman 사용자)으로 실행하세요."

#   grep 매칭 0 건이어도 죽지 않게 (pipefail 하에서 grep 실패=exit1 방어).
_grep() { grep "$@" || true; }

# ── 인벤토리 수집 (신규 prod 설치분만 — 컨테이너는 이름, 볼륨/네트워크는 프로젝트) ──
collect_containers() {
  #   컨테이너는 이름(trinity-prism-*)만 잡음 — dev(cosmos-*)·legacy 오삭제 방지.
  "$ENGINE" ps -a --filter "name=${NAME_PREFIX}" --format '{{.Names}}' 2>/dev/null \
    | sort -u | _grep -v '^$'
}
collect_volumes() {
  { "$ENGINE" volume ls --filter "label=com.docker.compose.project=${COMPOSE_PROJECT}" --format '{{.Name}}' 2>/dev/null
    #   라벨 유실(수동생성/부분설치) 대비 — 프로젝트명 prefix 로도 스윕.
    "$ENGINE" volume ls --format '{{.Name}}' 2>/dev/null | _grep -E "^${COMPOSE_PROJECT}_"
    if [ "$KEEP_STORE" -eq 0 ]; then
      for v in "${STORE_VOLS[@]}"; do
        "$ENGINE" volume inspect "$v" >/dev/null 2>&1 && echo "$v"
      done
    fi
  } | sort -u | _grep -v '^$'
}
collect_networks() {
  { "$ENGINE" network ls --filter "label=com.docker.compose.project=${COMPOSE_PROJECT}" --format '{{.Name}}' 2>/dev/null
    "$ENGINE" network ls --format '{{.Name}}' 2>/dev/null | _grep -E "^${COMPOSE_PROJECT}_"
  } | sort -u | _grep -vE '^(bridge|host|none|)$'
}
collect_images() {
  #   podman 은 repo 에 docker.io/library/ 또는 localhost/ 가 붙으므로 경로 뒤까지 매칭.
  #   태그 없는(<none>) 항목은 rmi 대상이 아니라 제외.
  { "$ENGINE" images --format '{{.Repository}}:{{.Tag}}' 2>/dev/null \
      | _grep -E "(^|/)${IMAGE_PREFIX}" | _grep -v ':<none>$'
    if [ "$DEL_BASE" -eq 1 ]; then
      for r in "${BASE_IMAGE_REPOS[@]}"; do
        "$ENGINE" images --format '{{.Repository}}:{{.Tag}}' "$r" 2>/dev/null
      done
    fi
  } | sort -u | _grep -v '^$'
}
#   2026-08-07 jypark1385  한 서버에 설치본이 둘 이상일 수 있다(root 의 docker 설치 + 계정의 rootless
#     podman 설치). 지금 지우는 엔진의 것이 아닌 폴더는 대상에서 빼야 한다. 안 그러면 rootless 담당자가
#     자기 설치본을 지울 때 남의 /opt 폴더가 목록에 끼어 삭제 실패로 끝난다.
#     판단 근거는 두 가지다.
#       1) 폴더의 .env 를 읽을 수 있으면 거기 적힌 PRISM_ENGINE 이 기준이다(확실한 근거).
#       2) 못 읽으면 실행 맥락으로 가른다. rootless podman 설치본은 항상 $HOME 아래라 /opt 와
#          /var/lib 는 남의 것이다. 반대로 docker 는 root 로 설치되므로 /opt 도 내 대상이다.
#     판단이 안 서면 대상에 남긴다. 빼는 쪽으로 기울면 지워야 할 폴더가 조용히 빠지기 때문이다.
_dir_is_mine() {   # $1=설치 폴더 → 0=이번 대상, 1=다른 설치본
  local d="$1" de="" ow=""
  #   root 로 돌 때 다른 계정 소유 폴더는 그 계정의 rootless 설치본이다. 그 컨테이너는 root 에게 안
  #   보이므로 폴더만 지우면 스택이 살아남은 채 설정만 사라진다. 대상에서 빼고 _rootless_note 가
  #   어느 계정으로 다시 실행해야 하는지 안내한다.
  if [ "${EUID:-$(id -u)}" -eq 0 ]; then
    ow="$(stat -c '%U' "$d" 2>/dev/null)"
    [ -n "$ow" ] && [ "$ow" != "root" ] && return 1
  fi
  [ -r "$d/.env" ] && de="$(grep -m1 '^PRISM_ENGINE=' "$d/.env" 2>/dev/null | cut -d= -f2- | tr -d '"')"
  if [ -n "$de" ]; then [ "$de" = "$ENGINE" ]; return $?; fi
  if [ "$ENGINE" = "podman" ] && [ "${EUID:-$(id -u)}" -ne 0 ] && [ "$d" != "$HOME/prism-cosmos" ]; then
    return 1
  fi
  return 0
}
collect_dirs() {
  [ "$KEEP_FILES" -eq 1 ] && return 0
  for d in "${INSTALL_DIRS[@]}"; do
    [ -e "$d" ] && _dir_is_mine "$d" && echo "$d"
  done
}
collect_other_dirs() {   # 이번 대상이 아닌 설치 폴더 - 지우지 않되 화면에는 반드시 남긴다
  for d in "${INSTALL_DIRS[@]}"; do
    [ -e "$d" ] || continue
    _dir_is_mine "$d" || echo "$d"
  done
}

#   2026-08-07 jypark1385  root 로 돌렸는데 한 건도 안 잡힌 경우 - rootless podman 설치본을 못 본 것일 수
#     있다. root 에서는 다른 계정의 rootless 컨테이너, 볼륨, 이미지가 통째로 안 보이는데, 화면은 "이미
#     깨끗한 환경" 으로 끝나 담당자가 제거된 줄 알고 재설치한다(실서버 실측 - 엔진을 podman 으로 맞게
#     골라도 root 의 podman 저장소가 비어 있어 0건이 된다). 7/9 부터 8/3 까지의 번들은 설치 완료 안내가
#     sudo 를 권했으므로, 시키는 대로 하면 그대로 겪는다.
#     추측이 아니라 실제로 있는 폴더로 판단한다 - $HOME/prism-cosmos 는 rootless 설치본만 만든다.
#     홈 경로는 /home 고정이 아니다(/export/home 등) - getent 으로 실제 로그인 계정의 홈을 훑는다.
#     실패해도 /home/* 글롭으로 한 번 더 본다(getent 없는 최소 이미지 대비).
_rootless_dirs() {   # root 실행 + 다른 계정의 설치 폴더 존재 → 경로 출력(0), 아니면 1
  [ "${EUID:-$(id -u)}" -eq 0 ] || return 1
  local h out="" homes
  #   while 을 파이프 뒤에 두면 서브셸에 갇혀 out 이 안 남는다 - here-string 으로 현재 셸에서 돈다.
  homes="$( { getent passwd 2>/dev/null | awk -F: '$3>=1000 && $3<65534 {print $6}'
              ls -d /home/* 2>/dev/null; } | sort -u )"
  while IFS= read -r h; do
    [ -n "$h" ] || continue
    [ -d "$h/prism-cosmos" ] || continue
    case " $out " in *" $h/prism-cosmos "*) continue ;; esac
    out="$out $h/prism-cosmos"
  done <<< "$homes"
  [ -n "$out" ] || return 1
  echo "$out"
}
_rootless_note() {   # 경고를 냈으면 0 (= "깨끗하다" 고 말하면 안 되는 상태), 아니면 1
  local dirs d owner
  dirs="$(_rootless_dirs)" || return 1
  echo
  say "${RED}${BOLD}⚠  rootless podman 설치본이 남아 있습니다 - root 권한에서는 보이지 않습니다.${NC}"
  say "${YEL}   아래 설치 폴더가 그 흔적입니다. 이 상태는 '깨끗한 환경' 이 아닙니다.${NC}"
  for d in $dirs; do
    owner="$(stat -c '%U' "$d" 2>/dev/null)"
    say "     - $d   (소유자 ${owner:-?})"
  done
  say "${YEL}   설치 계정으로 sudo 없이 다시 실행하세요:${NC}"
  for d in $dirs; do
    owner="$(stat -c '%U' "$d" 2>/dev/null)"
    say "     ${BOLD}su - ${owner:-<설치계정>} -c 'bash $d/uninstall.sh'${NC}"
  done
  return 0
}

#   다른 설치본 안내 - 조사는 엔진 이름 받침에 따라 갈린다(podman 으로 / docker 로). 값이 둘뿐이다.
_other_dirs_note() {   # 안내를 냈으면 0, 낼 게 없으면 1
  local d de jo
  [ "${#O_LIST[@]}" -gt 0 ] || return 1
  say "${YEL}· 아래 폴더는 다른 엔진으로 설치한 것이라 이번 대상이 아닙니다.${NC}"
  for d in "${O_LIST[@]}"; do
    de="$(grep -m1 '^PRISM_ENGINE=' "$d/.env" 2>/dev/null | cut -d= -f2- | tr -d '"')"
    jo="로"; [ "$de" = "podman" ] && jo="으로"
    if [ -n "$de" ]; then say "${YEL}    - $d  (${de} ${jo} 설치)  지우려면 --engine=${de}${NC}"
    else                  say "${YEL}    - $d  (다른 권한으로 설치)${NC}"; fi
  done
  return 0
}

# ── 목록 출력 ─────────────────────────────────────────────────────────────────
mapfile -t C_LIST < <(collect_containers)
mapfile -t V_LIST < <(collect_volumes)
mapfile -t N_LIST < <(collect_networks)
mapfile -t I_LIST < <(collect_images)
mapfile -t D_LIST < <(collect_dirs)
mapfile -t O_LIST < <(collect_other_dirs)

print_section() {  # $1=제목  나머지=항목들
  local title="$1"; shift
  printf "${BOLD}%s${NC} (%d)\n" "$title" "$#"
  if [ "$#" -eq 0 ]; then printf "    ${GREEN}(없음)${NC}\n"; else printf "    - %s\n" "$@"; fi
}

clear 2>/dev/null || true
cat <<'BANNER'

 ╔═══════════════════════════════════════════════════════════════╗
 ║        TRINITY PRISM — 완전 초기화 (Clean Reset)              ║
 ║        기존 설치 흔적을 전부 삭제하고 새 설치를 위한 빈 환경으로 ║
 ╚═══════════════════════════════════════════════════════════════╝
BANNER

head "삭제 대상 (아래 항목이 영구 삭제됩니다)"
print_section "컨테이너" "${C_LIST[@]}"
print_section "볼륨(데이터 — DB/인덱스/캐시/라이선스·설정)" "${V_LIST[@]}"
print_section "네트워크" "${N_LIST[@]}"
print_section "이미지(우리 앱 — 재설치 시 번들에서 다시 로드)" "${I_LIST[@]}"
print_section "설치 디렉토리(.env·프론트·system_store 포함)" "${D_LIST[@]}"

TOTAL=$(( ${#C_LIST[@]} + ${#V_LIST[@]} + ${#N_LIST[@]} + ${#I_LIST[@]} + ${#D_LIST[@]} ))
echo
say "${CYAN}· 대상 엔진: ${ENGINE}${ENGINE_SRC:+  (${ENGINE_SRC})}${NC}"
if [ "$KEEP_STORE" -eq 1 ]; then say "${YEL}· --keep-store: system-store 볼륨(라이선스/설정)은 보존합니다.${NC}"; fi
if [ "$KEEP_FILES" -eq 1 ]; then say "${YEL}· --keep-files: 호스트 설치 디렉토리는 보존합니다.${NC}"; fi
if [ "$DEL_BASE"   -eq 0 ]; then say "${YEL}· 외부 베이스 이미지(postgres/es/redis)는 보존합니다 (--base-images 로 삭제).${NC}"; fi
_other_dirs_note || true

#   2026-08-07 jypark1385  못 지우는 폴더는 yes 를 치기 전에 알린다. 종전엔 컨테이너를 다 지운 뒤
#     마지막 단계에서야 권한 부족이 드러나, 담당자가 sudo 로 다시 도는 수밖에 없었다. 판단은 이름이
#     아니라 실제 권한 검사다 - 폴더를 지우려면 상위 폴더에 쓰기 권한이 있어야 한다.
NEED_SUDO=0
for _d in "${D_LIST[@]}"; do
  [ -w "$(dirname "$_d")" ] && continue
  NEED_SUDO=1
  say "${YEL}· $_d 는 지금 권한으로 못 지웁니다(소유자 $(stat -c '%U' "$_d" 2>/dev/null)).${NC}"
done
if [ "$NEED_SUDO" -eq 1 ]; then
  say "${YEL}  ${BOLD}sudo bash $0${NC}${YEL} 로 실행하면 한 번에 끝납니다. 이대로 진행하면 그 폴더만 남습니다.${NC}"
fi

if [ "$TOTAL" -eq 0 ]; then
  echo
  #   "0건" 은 깨끗해서일 수도, 안 보여서일 수도 있다. 후자면 성공으로 끝내지 않는다(exit 2).
  if _rootless_note; then exit 2; fi
  if [ "${#O_LIST[@]}" -gt 0 ]; then
    say "${YEL}${BOLD}지울 것이 없습니다. 다만 위 다른 설치본이 남아 있어 깨끗한 환경은 아닙니다.${NC}"
    exit 2
  fi
  say "${GREEN}${BOLD}✓ 삭제할 TRINITY PRISM 흔적이 없습니다 — 이미 깨끗한 환경입니다.${NC}"
  say "  바로 설치를 진행하세요:  ${_AS_ROOT}./install.sh"
  exit 0
fi

if [ "$DRY" -eq 1 ]; then
  echo
  say "${CYAN}[--dry-run] 실제로는 아무것도 삭제하지 않았습니다. 위 목록만 확인용으로 출력.${NC}"
  exit 0
fi

# ── 경고 알럿 1회 확인 ────────────────────────────────────────────────────────
echo
say "${RED}${BOLD}⚠  경고: 위 ${TOTAL}개 항목이 영구 삭제됩니다. 되돌릴 수 없습니다.${NC}"
say "${RED}   DB·인덱스·라이선스·설정을 포함한 모든 데이터가 사라집니다.${NC}"
if [ "$AUTO" -ne 1 ]; then
  echo
  read -r -p "정말 전부 삭제하려면 ${BOLD}yes${NC} 를 입력하세요 (그 외 = 취소): " ANS
  if [ "$ANS" != "yes" ]; then
    say "${YEL}취소됨 — 아무것도 삭제하지 않았습니다.${NC}"
    exit 0
  fi
fi

# ── 삭제 실행 ─────────────────────────────────────────────────────────────────
head "삭제 진행"

if [ "${#C_LIST[@]}" -gt 0 ]; then
  say "[1/5] 컨테이너 중지·제거..."
  #   -t 3: graceful stop 짧게 → 바로 rm -f (volume 은 뒤에서 별도 삭제).
  "$ENGINE" rm -f "${C_LIST[@]}" >/dev/null 2>&1 || true
  say "  ✓ 컨테이너 ${#C_LIST[@]}개 제거"
else
  say "[1/5] 컨테이너 — 없음"
fi

if [ "${#V_LIST[@]}" -gt 0 ]; then
  say "[2/5] 볼륨 제거..."
  for v in "${V_LIST[@]}"; do "$ENGINE" volume rm -f "$v" >/dev/null 2>&1 || true; done
  say "  ✓ 볼륨 ${#V_LIST[@]}개 제거"
else
  say "[2/5] 볼륨 — 없음"
fi

if [ "${#N_LIST[@]}" -gt 0 ]; then
  say "[3/5] 네트워크 제거..."
  for n in "${N_LIST[@]}"; do "$ENGINE" network rm "$n" >/dev/null 2>&1 || true; done
  say "  ✓ 네트워크 ${#N_LIST[@]}개 제거"
else
  say "[3/5] 네트워크 — 없음"
fi

if [ "${#I_LIST[@]}" -gt 0 ]; then
  say "[4/5] 이미지 제거 (재설치 시 번들에서 새로 로드)..."
  for img in "${I_LIST[@]}"; do "$ENGINE" rmi -f "$img" >/dev/null 2>&1 || true; done
  say "  ✓ 이미지 ${#I_LIST[@]}개 제거"
else
  say "[4/5] 이미지 — 없음"
fi

DIR_FAIL=0; DIR_FAIL_HINT=""
if [ "${#D_LIST[@]}" -gt 0 ]; then
  say "[5/5] 설치 디렉토리 제거..."
  for d in "${D_LIST[@]}"; do
    #   2026-08-07 jypark1385  지울 수 없는 폴더는 아예 손대지 않는다. rm -rf 는 상위 폴더에 쓰기
    #     권한이 없어도 "안쪽" 은 지운다 - /opt/prism-cosmos(root 소유) 를 일반계정이 지우면 최상위만
    #     남고 그 안의 trinity 소유 ops, db, system_store 는 삭제된다(모의 실측). 운영 compose 와
    #     라이선스가 날아간 반쪽 설치본이 되므로, 지울 수 없으면 시도조차 하지 않고 안내만 한다.
    _parent="$(dirname "$d")"
    if [ ! -w "$_parent" ]; then
      DIR_FAIL=1
      _own="$(stat -c '%U' "$d" 2>/dev/null)"
      say "  ${RED}✗ $d - 삭제 불가(상위 $_parent 에 쓰기 권한 없음). 일부만 지워지는 것을 막기 위해 건드리지 않았습니다.${NC}"
      if [ "$_own" = "root" ]; then DIR_FAIL_HINT="sudo bash $0"
      elif [ -n "$_own" ];  then DIR_FAIL_HINT="su - $_own -c 'bash $0'"; fi
      continue
    fi
    if rm -rf "$d" 2>/dev/null; then
      say "  ✓ $d"
    else
      DIR_FAIL=1
      #   2026-08-03 jypark1385  rootless 는 sudo 로 다시 돌려도 소용없다. 이 경우 남는 원인은
      #     디렉토리 소유자가 설치 계정이 아닌 것이므로 소유자를 그대로 보여준다.
      #   2026-08-07 jypark1385  반대로 비root 가 root 소유(/opt) 설치본을 만나면 sudo 로 다시 실행하는 것이 맞다.
      #     실패 원인을 실행 권한이 아니라 그 폴더의 소유자로 갈라야 안내가 맞는다.
      _own="$(stat -c '%U' "$d" 2>/dev/null)"
      if [ -n "$_AS_ROOT" ]; then
        say "  ${RED}✗ $d - 삭제 실패. root 인데도 실패하면 사용 중(마운트)이거나 잠긴 경로입니다.${NC}"
      elif [ "$_own" = "root" ]; then
        say "  ${RED}✗ $d - 삭제 실패(권한). root 소유 설치본입니다.${NC}"
        DIR_FAIL_HINT="sudo bash $0"
      else
        say "  ${RED}✗ $d - 삭제 실패(권한). 소유자 $(stat -c '%U:%G' "$d" 2>/dev/null) / 현재 $(id -un) 입니다.${NC}"
        [ -n "$_own" ] && DIR_FAIL_HINT="su - $_own -c 'bash $0'"
      fi
    fi
  done
else
  say "[5/5] 설치 디렉토리 — 없음"
fi

# ── 최종 검증 (다 지워졌나 재조회) ───────────────────────────────────────────
head "최종 검증"
mapfile -t C2 < <(collect_containers)
mapfile -t V2 < <(collect_volumes)
mapfile -t N2 < <(collect_networks)
mapfile -t I2 < <(collect_images)
mapfile -t D2 < <(collect_dirs)
LEFT=$(( ${#C2[@]} + ${#V2[@]} + ${#N2[@]} + ${#I2[@]} + ${#D2[@]} ))

chk() {  # $1=라벨 나머지=잔여목록
  local label="$1"; shift
  if [ "$#" -eq 0 ]; then printf "  ${GREEN}✓${NC} %s\n" "$label"
  else printf "  ${RED}✗ %s — 잔여 %d건:${NC}\n" "$label" "$#"; printf "      - %s\n" "$@"; fi
}
chk "컨테이너" "${C2[@]}"
chk "볼륨"     "${V2[@]}"
chk "네트워크" "${N2[@]}"
chk "이미지"   "${I2[@]}"
chk "설치 디렉토리" "${D2[@]}"

echo
if [ "$LEFT" -eq 0 ]; then
  #   여기서 지운 건 root 가 볼 수 있던 범위뿐이다 - 다른 계정의 rootless 설치본은 그대로 남는다.
  if _rootless_note; then
    say "${YEL}  위 설치본까지 지워야 완전 초기화입니다.${NC}"
    exit 2
  fi
  say "${GREEN}${BOLD}✓✓ CLEAN — 이번 대상은 모두 삭제됐습니다.${NC}"
  #   내 대상만 지웠다는 뜻이다. 다른 설치본이 남아 있으면 "서버가 깨끗하다" 로 읽히지 않게 덧붙인다.
  if [ "${#O_LIST[@]}" -gt 0 ]; then
    say "${YEL}  다만 다른 설치본이 남아 있습니다(위 안내 참고). 서버 전체가 빈 상태는 아닙니다.${NC}"
  else
    say "  이제 그대로 설치하세요:  ${BOLD}${_AS_ROOT}./install.sh${NC}"
  fi
  exit 0
else
  say "${RED}${BOLD}✗ 잔여 ${LEFT}건 — 완전히 지워지지 않았습니다.${NC}"
  #   재실행 명령은 실패한 폴더의 소유자에서 뽑는다(root 소유면 sudo, 다른 계정이면 그 계정).
  if [ "$DIR_FAIL" -eq 1 ]; then
    say "${YEL}  디렉토리 삭제 실패가 있습니다 - ${BOLD}${DIR_FAIL_HINT:-${_AS_ROOT}bash $0}${NC}${YEL} 로 다시 실행하세요.${NC}"
  fi
  say "${YEL}  위 잔여 항목을 확인하고 스크립트를 한 번 더 실행하거나, 해당 리소스를 수동 삭제 후 재설치하세요.${NC}"
  exit 1
fi
