#!/usr/bin/env bash
# vim:et:ts=2:sts=2:sw=2

# Get absolute repository root (especially when symlinked)
ROOT="$(realpath "$(dirname "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")")")"

# Load accompanying bash library
source "$ROOT/lib/lib.bash"

BTRFS="${1:-"/var/btrfs"}"

if [ -n "$HELP" ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
  say "
  USAGE:
    $(g "${BASH_SOURCE##*/}")

  DESCRIPTION:
    $(g "${BASH_SOURCE##*/}") Provides a TUI listing and managing
      components hosted in $(g /srv) in a TechLit system

    OPTIONS:
      -h, --help      Show this help message and exit
  "
  exit 1
fi

# Exit now unless run as admin
assert-is-admin

say "This script requires root access"
sudo echo thank you

declare -A SELECTIONS                                                                          # Store versions to be deleted
SRV="$BTRFS/@srv"                                                                              # Components directory
COMPONENTS=("admin" "control" "curriculum" "desktop" "desktop-mini" "help" "recovery" "snapshots" "packages") # Components to manage
ANSWER=""                                                                                      # Dialog answer placeholder
LOCKFILE="/tmp/tl-comp-manage.lock"                                                            # Lockfile

if [ -e "$LOCKFILE" ]; then
  err "Another instance is already running. Exiting."
  exit 1
fi

cleanup() {
  rm -f "$LOCKFILE"
}

# Helper function for displaying dialog menus
# USAGE: menu "Title" --yesno "Message" 10 60
#        menu "Title" --checklist "Message" 20 40 6 "Option 1" "Description 1" "Option 2" "Description 2"
menu() {
  ANSWER="$(
    dialog \
      --clear --no-collapse --output-fd 1 --colors \
      --backtitle "Manage components hosted in $SRV" --title "$@"
  )"
}

# Deletes all selected components in SELECTIONS
apply_selections() {
  local pre_disk_usage post_disk_usage options

  pre_disk_usage=$(sudo df -h "$BTRFS" --output=used -h | tail -n1)

  if [ ${#SELECTIONS[@]} -gt 0 ]; then
    for comp in "${!SELECTIONS[@]}"; do
      options+="\n$comp: ${SELECTIONS["$comp"]}\n"
    done

    if ! menu "Summary of Selections" --yesno \
      "\Zb\Z1 Warning!!!\Zn You have selected to delete these \n\n       \Zb\Z2 $options\n \Zn\n       \Zb\Z1 This is irreversible. Proceed?" 20 60; then
      return
    fi
  else
    menu "\Z1Nothing selected" --pause "" 7 20 3
    return
  fi

  for component in "${!SELECTIONS[@]}"; do
    for item in ${SELECTIONS["$component"]}; do
      if [ "$component" = "packages" ]; then
        path_to_delete="$SRV/$component/$item"
        [ -z "$path_to_delete" ] && continue
        run sudo rm -rf "$path_to_delete"
      else
        ver="$SRV/$component/$item"
        [ -z "$ver" ] && continue

        # If the component is snapshot, delete the subvolumes first
        for ext in d mini.d; do
          if [ -d "$ver.$ext/@root" ] || [ -d "$ver.$ext/@guest" ]; then
            run sudo btrfs subvol delete "$ver.$ext"/@{root,guest} || continue
          fi
        done
        run sudo rm -rf "$ver".*
      fi
    done
  done

  run sudo sync
  post_disk_usage="$(sudo df -h "$BTRFS" --output=used -h | tail -n1)"

  menu "Components deleted successfully!" \
    --pause "Initial Disk Usage: $pre_disk_usage \n\n Final Disk Usage:   $post_disk_usage" 10 60 5

  SELECTIONS=()
}

purge_components() {
  if ! menu "Purge Components" --yesno \
    "\ZbDelete all temporary files?" 6 60; then
    return
  fi

  # Incomplete unpacks
  for dir in $(sudo find "$SRV"/snapshots -maxdepth 1 -type d -name 'tmp-*'); do
    if [ -d "$dir"/@root ] || [ -d "$dir"/@guest ]; then
      run sudo btrfs subvol delete "$dir"/@{root,guest} || :
    fi
    run sudo rm -rf "$dir" || :
  done

  # aria2 downloads
  run find "$SRV"/desktop "$SRV"/curriculum "$SRV"/recovery "$SRV"/admin \
    -type f -name '*.aria2' -print | while read -r tmp_file; do

    file="${tmp_file%.aria2}"
    run rm -f "$file"*
  done || :

  # Unfinished desktop creation
  run find "$SRV"/desktop -maxdepth 1 -type d -name '*.tmp' -print | while read -r file; do
    run sudo rm -rf "$file" || :
  done

  # incomplete rsync downloads
  for comp in "${COMPONENTS[@]}"; do
    [ -d "$SRV/$comp/.rsync" ] || continue
    run sudo rm -rf "$SRV/$comp"/.rsync/*
  done
}

quit_program() {
  if ! menu "Quit" --yesno "quit?" 6 60; then
    return
  fi
  exit 0
}

# Lists items for a component and saves selections to the SELECTIONS array
list_and_select_items() {
  local component="$1"
  local title="$2"
  local item_name usage is_selected
  local -a options=() all_items=()
  declare -A item_usages

  # Store initial state as a simple string to restore on cancel
  local initial_selections="${SELECTIONS[$component]}"
  # Use a temporary variable for selections within the loop
  local current_selections="${SELECTIONS[$component]}"

  # Define glob based on component type
  local -a item_paths
  if [ "$component" = "packages" ]; then
    item_paths=("$SRV/$component"/*)
  elif [ "$component" = "curriculum" ]; then
    # Curriculum has subjects as subdirectories
    while IFS= read -r -d '' path; do
      item_paths+=("$path")
    done < <(find "$SRV/$component" -mindepth 2 -maxdepth 2 -name "*.tar.gz" -print0)
  else
    item_paths=("$SRV/$component"/*.{tar.gz,iso,d})
  fi

  # Gather all available items and their sizes for this component
  for item_path in "${item_paths[@]}"; do
    [ -e "$item_path" ] || continue
    item_name="$(basename "$item_path")"

    if [ "$component" = "curriculum" ]; then
      local subject="$(basename "$(dirname "$item_path")")"
      item_name="$subject/$item_name"
    fi

    if [ "$component" != "packages" ]; then
      item_name="$(echo "$item_name" | sed -E "s/\.($VERSION_EXTS)$//")"
    fi

    if [[ " ${all_items[*]} " =~ $item_name ]]; then
      continue
    fi
    all_items+=("$item_name")

    if [ "$component" = "snapshots" ]; then
      usage="N/A"
    else
      local item_disk_path="$item_path"
      # For versioned components, calculate size of all related files (e.g., v1.d, v1.iso)
      if [ "$component" != "packages" ]; then
        item_disk_path="$SRV/$component/$item_name.*"
      fi
      usage="$(sudo du -shc $item_disk_path 2>/dev/null | tail -n1 | awk '{print $1}')"
    fi
    item_usages["$item_name"]="$usage"
  done

  if [ ${#all_items[@]} -eq 0 ]; then
    menu "Empty" --msgbox "\Zb\Z1404:\Zn No items found for '$component'." 6 40
    return
  fi

  while true; do
    options=()
    # Add special options first
    options+=("Select all" "Select Everything" "off")
    options+=("Deselect all" "Deselect Everything" "off")

    for item_name in "${all_items[@]}"; do
      is_selected="off"
      # Word splitting is intentional here
      # shellcheck disable=SC2086
      for sel in $current_selections; do
        if [ "$sel" = "$item_name" ]; then
          is_selected="on"
          break
        fi
      done
      options+=("$item_name" "${item_usages[$item_name]}" "$is_selected")
    done

    if ! ANSWER="$(
      dialog \
        --clear --no-collapse --output-fd 1 --colors \
        --backtitle "Manage components hosted in $SRV" --title "$title" \
        --checklist "Select items, an action, then Enter to refresh." \
        20 60 10 "${options[@]}"
    )"; then
      # User pressed Cancel or ESC, restore initial state
      SELECTIONS["$component"]="$initial_selections"
      break
    fi

    # Check for specified action
    local action_taken=false
    if [[ "$ANSWER" == *"Select all"* ]]; then
      current_selections="$(printf '%s ' "${all_items[@]}")"
      action_taken=true
    fi

    # Separate check for Deselect all to allow selecting all then deselecting all in one go
    if [[ "$ANSWER" == *"Deselect all"* ]]; then
      current_selections=""
      action_taken=true
    fi

    # If no action was taken, it's a final selection.
    if ! $action_taken; then
      # Filter out our action tags from the final answer
      ANSWER="${ANSWER//Select all/}"
      ANSWER="${ANSWER//Deselect all/}"
      SELECTIONS["$component"]="$ANSWER"
      break
    fi
  done

  ANSWER=""
}

# The main menu: Loops over components check if it exists and display them as options
# Then run sub_menu with the selected component
main_menu() {
  touch "$LOCKFILE" || {
    err "Failed to create lock file. Exiting."
    exit 1
  }
  local disk_usage version options=() latest usage
  disk_usage=$(sudo df -h "$BTRFS" --output=size,used,avail,pcent \
    | tail -n1 | awk '{printf "Total: %s | Available: %s | Used: %s", $1, $3, $4}')

  while true; do
    options=() # Resetting here to avoid multiple lists
    for component in "${COMPONENTS[@]}"; do
      [ -d "$SRV/$component" ] || continue
      if [ "$component" = "curriculum" ]; then
        version="N/A"
      else
        version="$(tl-comp-latest "$component" 2>/dev/null)"
      fi
      version="${version:-None}"
      latest="Latest: \Zb\Z4 $(printf "%-10s" "$version")\Zn"

      local usage_val
      if [ "$component" = "snapshots" ]; then
        usage_val="N/A"
      else
        usage_val=$(sudo du -sxh "$(readlink -f "$SRV/$component")" 2>/dev/null | awk '{print $1}')
      fi
      usage="Disk: \Zb\Z4 $(printf "%-5s" "$usage_val")\Zn"
      options+=("$component" "$usage  $latest")
    done

    options+=(" " "") # A dummy separator
    options+=("Apply" "\Z1Delete all selected components")
    options+=("Purge" "Temporary files/incomplete downloads")
    options+=("Quit" "Exit without changes")

    menu "Select component or an option" \
      --menu "\Zb\Z4Disk Usage\Zn → $disk_usage" 21 65 10 "${options[@]}" || :

    [ -z "$ANSWER" ] && return

    case "$ANSWER" in
      " ") ;;
      Apply) apply_selections ;;
      Purge) purge_components ;;
      Quit) quit_program ;;
      *) list_and_select_items "$ANSWER" "Selections Menu" ;;
    esac
    ANSWER=""
  done
}

cleanup
[[ "$0" = "${BASH_SOURCE[0]}" ]] && main_menu
