#!/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" "help" "recovery" "snapshots") # Components to manage
LOG_FILE="/tmp/$(basename "$0").log"                                                # Log file
ANSWER=""                                                                           # Dialog answer placeholder
MOUNTPOINT="$(mktemp -d)"                                                           # USB mountpoint

# Clear the log file
: >"$LOG_FILE"

# 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 "$@"
  )"
}

# Outputs a message to the log file
# USAGE: log "Doing something"
log() {
  local message="$1"
  echo "$(date '+%Y-%m-%d %H:%M:%S') -> $message" >>"$LOG_FILE"
}

# 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\Z1Warning!!!\Zn You have selected to delete these: \n
        \Z5$options\n
        \Zn \Zb\Z1This is irreversible. Proceed?" 20 60; then
      return
    fi
  else
    menu "\Z1Nothing selected" --pause "" 7 20 3
    return
  fi

  for component in "${!SELECTIONS[@]}"; do
    for version in ${SELECTIONS["$component"]}; do
      ver="$SRV/$component/$version"

      # If the component is snapshot, delete the subvolumes first
      if [ -d "$ver".d/@root ] || [ -d "$ver".d/@guest ]; then
        log "Deleting snapshot $ver"
        run sudo btrfs subvol delete "$ver".d/@{root,guest} >"$LOG_FILE" || :
        run sudo rm -rf "$ver".d >"$LOG_FILE"
      else
        [ -z "$ver" ] && return
        log "Deleting component $ver"
        run sudo rm -rf "$ver".* >"$LOG_FILE"
      fi
    done
  done

  log "Syncing disk"
  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
Final Disk Usage:   $post_disk_usage" 10 60 5

  SELECTIONS=()
}

# Syncs the selected components from a USB drive
# TODO: Refine this
update_from_usb() {
  local usb components selected options=() rsync_log
  is-mounted() { grep -q "$1" /proc/mounts && echo "Mounted" || echo "Not Mounted"; }

  rsync_log="$(mktemp)"
  : >"$rsync_log"

  for block in $(lsblk -o NAME,TRAN | grep usb | awk '{print $1}'); do
    options+=("/dev/$block" "$(is-mounted "$block")")
  done

  if ! menu "Select USB Drive" \
    --menu "Block devices" 10 40 20 "${options[@]}"; then
    return
  else
    usb="$ANSWER"
  fi

  if [ ! -b "$usb"4 ] || [ "$(is-mounted "$usb")" == "Mounted" ]; then
    menu "USB" --msgbox "Invalid or mounted USB" 6 20
    return
  fi

  cleanup() {
    log "Unmounting USB drive, deleting $MOUNTPOINT"
    run sudo umount -q "$MOUNTPOINT"
  }

  if ! sudo mount "$usb"4 "$MOUNTPOINT"; then
    menu "Mount" --msgbox "Failed to mount USB drive." 6 40
    return
  fi

  for component in $(ls -d "$MOUNTPOINT"/*/ 2>/dev/null); do
    comp=$(basename "$component")
    [[ " ${COMPONENTS[*]} " =~ $comp ]] || continue
    components+=("$comp")
  done

  if [ ${#components[@]} -eq 0 ]; then
    menu "Comp selection" --msgbox \
      "No components found on the USB drive." 6 40
    return
  fi

  local options=() latest
  for component in "${components[@]}"; do
    log "Selecting components to sync"
    log "USB: Components ${components[*]}"
    latest="$(COMP_DIR="$MOUNTPOINT" tl-comp-latest "$component")"
    options+=("$component" "$latest" "off")
  done

  if menu "Components Menu" \
    --checklist "Select components to sync:" 20 40 6 "${options[@]}"; then
    selected="$ANSWER"
  fi

  if [ -z "$selected" ]; then
    dialog --msgbox "No components selected." 6 40
    return
  fi

  for component in $selected; do
    latest="$(COMP_DIR="$MOUNTPOINT" tl-comp-latest "$component")"
    log "Syncing Components rsync log: $rsync_log"

    if [ "$component" = "desktop" ]; then
      PATCH="$latest"
      MINOR="$(echo "$PATCH" | cut -d. -f-2).0"
      MAJOR="$(echo "$PATCH" | cut -d. -f-1).0.0"

      {
        rsync -avP "$MOUNTPOINT/$component/v$MAJOR"* "$SRV/$component/"
        rsync -avP "$MOUNTPOINT/$component/v$MINOR"* "$SRV/$component/"
        rsync -avP "$MOUNTPOINT/$component/v$PATCH"* "$SRV/$component/"
      } >>"$rsync_log" 2>&1 | dialog --gauge "Syncing components... Please wait." 7 50 10

    else
      log "Syncing $component -> version $latest"
      rsync -avP "$MOUNTPOINT/$component/v$latest"* "$SRV/$component/" >>"$rsync_log" 2>&1 \
        | dialog --gauge "Syncing components... Please wait." 7 50 10
    fi

    menu "Syncing Logs" --textbox "$rsync_log" 20 60
    tl-comp-sync "$component"

    rm -f "$rsync_log"
  done

  cleanup
}

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
    log "Deleting incomplete unpacks"
    if [ -d "$dir/@root" ]; then
      run sudo btrfs subvol delete "$dir/@{root,guest}" || :
    fi
    run sudo rm -rf "$dir"
  done

  # Downloaded using aria2
  run find "$SRV" \
    \( -path "$SRV"/snapshots -o -path "$SRV"/secure -o -path "$SRV"/public \) -prune -o \
    -type f -name '*.aria2' -print | while read -r tmp_file; do

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

  # incomplete rsync downloads
  for comp in "${COMPONENTS[@]}"; do
    log "Deleting incomplete rsync downloads"
    [ -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 a component and its versions as options and save them to SELECTIONS
sub_menu() {
  local component version_name usage options=() selected_versions=()
  component="$1"

  if [ -n "${SELECTIONS[$component]}" ]; then
    selected_versions=("${SELECTIONS["$component"]}")
  fi

  for version in "$SRV/$component"/*.{tar.gz,iso,d}; do
    [ -r "$version" ] || continue
    version_name="$(basename "$(echo "$version" | sed -E 's/\.(tar\.gz|iso|d)$//')")"
    usage="$(sudo du -sxh "$version" | awk '{print $1}')"

    if [[ " ${selected_versions[*]} " == *" $version_name "* ]]; then
      options+=("$version_name" "$usage" on)
    else
      options+=("$version_name" "$usage" off)
    fi
  done

  if [ ${#options[@]} -eq 0 ]; then
    menu "Selected" --msgbox "\Zb\Z1404:\Zn No component found" 6 30
    main_menu
    return
  fi

  menu "Versions Menu" \
    --checklist "Select versions to delete:" \
    20 40 6 "${options[@]}" || :

  if [ -n "$ANSWER" ]; then
    SELECTIONS["$component"]="$ANSWER"
  fi
  ANSWER=""

  main_menu
}

# 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() {
  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
      version="$(tl-comp-latest "$component" 2>/dev/null)"   # Check the latest version
      version="${version:-None}"                             # Replace empty version with "0.0.0"
      latest="Latest \Zb\Z6 $(printf "%-10s" "$version")\Zn" # printf allows consistent format
      usage="Disk \Zb\Z3$(sudo du -sxh "$(readlink -f "$SRV/$component")" 2>/dev/null | awk '{printf "%-5s", $1}')\Zn"
      log "Adding component $component from main menu"
      options+=("$component" "$usage  $latest")
    done

    options+=("Apply" "\Z1Delete all selected components")
    options+=(" " "") # A dummy separator
    options+=("Others" "\Z4\ZbOther utilities")
    options+=("USB" "Update components (from USB)")
    options+=("Purge" "Temporary files/incomplete downloads")
    options+=("Quit" "Exit without changes")

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

    [ -z "$ANSWER" ] && return

    case "$ANSWER" in
      " ") ;;
      "Others") ;;
      Apply) apply_selections ;;
      USB) update_from_usb ;;
      Purge) purge_components ;;
      Quit) quit_program ;;
      *) sub_menu "$ANSWER" ;;
    esac
    ANSWER=""
  done
}

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