#!/usr/bin/env bash
# docker-clean.sh - Interactive Docker cleanup for the ByteLyst platform prototype.
set -euo pipefail

RED=$'\033[0;31m'
GREEN=$'\033[0;32m'
YELLOW=$'\033[1;33m'
CYAN=$'\033[0;36m'
BOLD=$'\033[1m'
DIM=$'\033[2m'
NC=$'\033[0m'

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"

log() { echo -e "${CYAN}[docker-clean]${NC} $*"; }
ok() { echo -e "${GREEN}OK:${NC} $*"; }
warn() { echo -e "${YELLOW}WARN:${NC} $*"; }
err() { echo -e "${RED}ERROR:${NC} $*" >&2; }

require_docker() {
  if ! command -v docker >/dev/null 2>&1; then
    err "Docker CLI is not installed or not on PATH."
    exit 1
  fi

  if ! docker info >/dev/null 2>&1; then
    err "Docker daemon is not reachable. Start Docker Desktop or your Docker daemon and try again."
    exit 1
  fi

  if ! docker compose version >/dev/null 2>&1; then
    err "Docker Compose plugin is not available. Install/update Docker Desktop or the docker compose plugin."
    exit 1
  fi
}

run_cmd() {
  echo -e "${DIM}$*${NC}"
  "$@"
}

confirm() {
  local prompt="$1"
  read -r -p "$(echo -e "${BOLD}${prompt}${NC} [y/N] ")" answer
  case "${answer:-}" in
    y|Y|yes|YES) return 0 ;;
    * ) return 1 ;;
  esac
}

compose_status() {
  log "Compose services in $REPO_ROOT"
  cd "$REPO_ROOT"
  run_cmd docker compose ps
  echo ""
  log "Docker disk usage"
  run_cmd docker system df
}

stop_compose() {
  log "Stopping compose services"
  cd "$REPO_ROOT"
  run_cmd docker compose stop
}

down_compose() {
  log "Removing compose containers and networks"
  cd "$REPO_ROOT"
  run_cmd docker compose down --remove-orphans
}

down_compose_with_volumes() {
  cd "$REPO_ROOT"
  run_cmd docker compose down --remove-orphans --volumes
}

safe_prune() {
  log "Pruning stopped containers, unused networks, dangling images, and build cache"
  run_cmd docker container prune --force
  run_cmd docker network prune --force
  run_cmd docker image prune --force
  run_cmd docker builder prune --force
}

unused_image_prune() {
  run_cmd docker image prune --all --force
  run_cmd docker builder prune --all --force
}

full_prune() {
  run_cmd docker system prune --all --volumes --force
}

builder_prune() {
  log "Pruning Docker builder cache"
  run_cmd docker builder prune --all --force
}

show_menu() {
  cat <<EOF

${BOLD}Docker Cleanup Menu${NC}

Repo: $REPO_ROOT

  ${BOLD}1${NC}) Status only
     Show compose services and Docker disk usage.

  ${BOLD}2${NC}) Stop compose services
     Keeps containers, networks, volumes, and images.

  ${BOLD}3${NC}) Remove compose containers and networks
     Runs compose down with orphan cleanup. Keeps repo volumes.

  ${BOLD}4${NC}) Remove compose containers, networks, and repo compose volumes
     Deletes prototype state for this repo's compose project.

  ${BOLD}5${NC}) Safe global prune
     Removes stopped containers, unused networks, dangling images, and build cache.

  ${BOLD}6${NC}) Deeper image/cache prune
     Removes all unused images and build cache.

  ${BOLD}7${NC}) Full global Docker prune
     Removes all unused Docker resources, including unused volumes.

  ${BOLD}8${NC}) Builder cache only
     Removes Docker build cache.

  ${BOLD}q${NC}) Quit

EOF
}

describe_level() {
  case "$1" in
    1)
      cat <<EOF

${BOLD}You selected: Status only${NC}

This will:
  - Show this repo's Docker Compose services.
  - Show Docker disk usage.
  - Not stop, remove, or prune anything.

After this, everything will remain exactly as it is.
EOF
      ;;
    2)
      cat <<EOF

${BOLD}You selected: Stop compose services${NC}

This will:
  - Stop containers from this repo's Docker Compose stack.
  - Keep the containers, networks, images, and volumes.
  - Preserve prototype data in Cosmos, Azurite, and Mailpit volumes.

After this, the stack will be stopped but can be resumed with:
  ./scripts/prototype-up.sh
EOF
      ;;
    3)
      cat <<EOF

${BOLD}You selected: Remove compose containers and networks${NC}

This will:
  - Stop and remove this repo's compose containers.
  - Remove compose networks and orphan containers.
  - Keep repo compose volumes.
  - Preserve prototype data in Cosmos, Azurite, and Mailpit volumes.

After this, containers will be recreated the next time you start the prototype.
EOF
      ;;
    4)
      cat <<EOF

${BOLD}You selected: Remove compose containers, networks, and repo compose volumes${NC}

This will:
  - Stop and remove this repo's compose containers.
  - Remove compose networks and orphan containers.
  - Delete this repo's compose volumes.
  - Delete prototype state stored in local Cosmos, Azurite, and Mailpit volumes.

After this, the next prototype start will recreate empty local backing services.
EOF
      ;;
    5)
      cat <<EOF

${BOLD}You selected: Safe global prune${NC}

This will:
  - Remove all stopped Docker containers.
  - Remove unused Docker networks.
  - Remove dangling image layers.
  - Remove unused Docker build cache.
  - Keep named volumes.
  - Keep images that are still tagged or used by containers.

After this, Docker should use less disk space while preserving volumes.
EOF
      ;;
    6)
      cat <<EOF

${BOLD}You selected: Deeper image/cache prune${NC}

This will:
  - Remove all unused Docker images, not just dangling layers.
  - Remove all unused Docker build cache.
  - Keep images used by existing containers.
  - Keep named volumes.

After this, future builds and starts may need to pull or rebuild images.
EOF
      ;;
    7)
      cat <<EOF

${BOLD}You selected: Full global Docker prune${NC}

This will:
  - Remove stopped containers across Docker.
  - Remove unused networks across Docker.
  - Remove all unused images.
  - Remove build cache.
  - Remove all unused volumes, including volumes from other projects if Docker considers them unused.

After this, Docker disk usage should drop the most, but other local projects may need to recreate data volumes.
EOF
      ;;
    8)
      cat <<EOF

${BOLD}You selected: Builder cache only${NC}

This will:
  - Remove Docker build cache.
  - Keep containers, images, networks, and volumes.

After this, future Docker builds may be slower until the cache is rebuilt.
EOF
      ;;
    q|Q)
      exit 0
      ;;
    *)
      err "Unknown cleanup level: $1"
      return 1
      ;;
  esac
}

run_level() {
  case "$1" in
    1) compose_status ;;
    2) stop_compose ;;
    3) down_compose ;;
    4) down_compose_with_volumes ;;
    5) safe_prune ;;
    6) unused_image_prune ;;
    7) full_prune ;;
    8) builder_prune ;;
    q|Q) exit 0 ;;
    *) err "Unknown cleanup level: $1"; exit 1 ;;
  esac
}

if [[ $# -gt 0 ]]; then
  warn "This script is interactive and does not accept CLI parameters. Ignoring: $*"
fi

show_menu
read -r -p "Select cleanup level: " LEVEL
describe_level "${LEVEL:-q}"

if [[ "${LEVEL:-q}" == "1" ]]; then
  echo ""
  require_docker
  run_level "$LEVEL"
  ok "Done."
  exit 0
fi

echo ""
if ! confirm "Proceed with this cleanup?"; then
  warn "Cancelled. No cleanup was performed."
  exit 0
fi

echo ""
require_docker
run_level "${LEVEL:-q}"
ok "Done."
