#!/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"}" # Path to system BTRFS root

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

  DESCRIPTION:
    $(g "${BASH_SOURCE##*/}") unpacks desktop a archive into
    the TechLit system hosted in BTRFS volume at $(c btrfs-path)
                                        (default: $(c /var/btrfs))

    The lastest local version will be used by default

  NOTE:
    If no desktop archives are available, use $(g tl-comp-pull) to download one
"
  exit 1
fi

# Exit now if run as root user
assert-is-user

say "This script will need root access via $(c sudo)"
sudo echo thank you

IS_MINI_SYSTEM=false
is-mini "$BTRFS" && IS_MINI_SYSTEM=true
if $IS_MINI_SYSTEM; then
  ARCH="/srv/desktop-mini"
  SNAP_EXT="mini.d"
else
  ARCH="${ARCH:-"/srv/desktop"}"
  SNAP_EXT="d"
fi

COMP_DIR_FOR_LATEST="$(dirname "$ARCH")"
PATCH="${2:-"$(COMP_DIR="$COMP_DIR_FOR_LATEST" "$ROOT/bin/tl-comp-latest" "$(basename "$ARCH")")"}"
MINOR="$(echo "$PATCH" | cut -d. -f-2).0"
MAJOR="$(echo "$PATCH" | cut -d. -f-1).0.0"
SNAP="$BTRFS/@srv/snapshots"    # Snapshots directory
TMP="tmp-$(uuidgen | head -c4)" # Temp prefix
[ "$IS_MINI_SYSTEM" = true ] && TMP="mini"

# Early exit if all required snapshots already exist
ALL_EXIST=true
for ver in "$MAJOR" "$MINOR" "$PATCH"; do
  if ! [ -d "$SNAP/v$ver.$SNAP_EXT/@root" ] || ! [ -d "$SNAP/v$ver.$SNAP_EXT/@guest" ]; then
    ALL_EXIST=false
    break
  fi
done

if [ "$ALL_EXIST" = true ]; then
  say "$(g "Version $PATCH is already installed.") Nothing to unpack."
  exit 0
fi

# Syncing
say "Syncing desktop & snapshot versions"
{
  if [ "$ARCH" = "/srv/desktop" ] || [ "$ARCH" = "/srv/desktop-mini" ]; then
    "$ROOT/bin/tl-comp-sync" "$(basename "$ARCH")"
  fi
  COMP_DIR="$BTRFS"/@srv "$ROOT/bin/tl-comp-sync" snapshots
}

#
## Low Storage Handling
#
DISK_SIZE_GB=$(df --output=size -B1G "$BTRFS" | tail -n1)
IS_LOW_STORAGE=false
[[ "$DISK_SIZE_GB" -lt 32 ]] && IS_LOW_STORAGE=true

DEFAULT_STAGE="/srv/stage"
if [ "$IS_LOW_STORAGE" = false ]; then
  DEFAULT_STAGE="$BTRFS/@srv/snapshots"
fi

STAGE="${STAGE:-"$DEFAULT_STAGE"}"
run mkdir -p "$STAGE"

# Check if we actually need to do unpacking
NEEDS_SPACE=false
for ver in "$MAJOR" "$MINOR" "$PATCH"; do
  # A snapshot exists in the staging area if its subvolume files are present
  if ! [ -f "$STAGE/$TMP.$ver.d/@root.btrfs" ] || ! [ -f "$STAGE/$TMP.$ver.d/@guest.btrfs" ]; then
    NEEDS_SPACE=true
    break
  fi
done

if $IS_LOW_STORAGE && $NEEDS_SPACE; then
  # Check USB staging space
  if [[ "$STAGE" == *"/srv/"* ]]; then
    SRV_AVAIL_GB=$(df --output=avail -B1G "$STAGE" | tail -n 1)
    if [[ "$SRV_AVAIL_GB" -lt 14 ]]; then
      err "Insufficient space on USB staging area ($SRV_AVAIL_GB GB < 14 GB). Aborting."
      exit 1
    fi
  fi

  #
  # Cleanup
  #
  delete_old_snapshots() {
    say "Deleting old snapshots to save space."
    find "$SNAP" -mindepth 1 -maxdepth 1 -type d | while read -r dir; do
      local keep=false
      dir=$(basename "$dir")
      if [[ "$dir" == *.mini.d ]]; then keep=true; else
        for ver in "$MAJOR" "$MINOR" "$PATCH"; do
          [[ "$dir" == v"$ver.$SNAP_EXT" ]] && keep=true && break
        done
      fi
      if ! $keep; then
        sudo btrfs subvolume delete "$SNAP/$dir"/@root/home/guest || true
        sudo btrfs subvolume delete "$SNAP/$dir"/@{root,guest} || true
        [ -d "$SNAP/$dir" ] && run sudo rm -rf "$SNAP/$dir"
      fi
    done
  }
  delete_old_desktop() {
    say "Deleting old desktop components to save space."
    find "$ARCH" -name "v*.tar.gz" | while read -r file; do
      local keep=false
      for ver in "$MAJOR" "$MINOR" "$PATCH"; do
        [[ "$(basename "$file")" == "v$ver.tar.gz" ]] && keep=true && break
      done
      if ! $keep; then
        run sudo rm -f "$file" "${file%.tar.gz}.sha256" "${file%.tar.gz}.changelog"
      fi
    done
  }

  # Destructive update logic for major version change...
  CURRENT_PATCH="$(COMP_DIR="$BTRFS/@srv" "$ROOT/bin/tl-comp-latest" snapshots || echo '0.0.0')"
  [ "$(echo "$CURRENT_PATCH" | cut -d. -f1)" != "$(echo "$PATCH" | cut -d. -f1)" ] \
    && {
      run sudo btrfs subvolume delete "$BTRFS"/@root{,.prev}/home/guest "$BTRFS"/@{root,guest}{,.prev} || :
      run sudo rm -rf "$BTRFS"/@root "$BTRFS"/@guest || :
    } \
    || run sudo btrfs subvolume delete "$BTRFS"/@{root,guest}.prev || :

  run sudo rm -rf "$STAGE/"* || :
  run mkdir -p "$STAGE"
  [[ "$SNAP" == "$BTRFS"* ]] && delete_old_snapshots
  [[ "$ARCH" == "$BTRFS"* ]] && delete_old_desktop
fi

# Unpacking loop
for ver in "$MAJOR" "$MINOR" "$PATCH"; do
  if [ -d "$SNAP/v$ver.$SNAP_EXT/@root" ] && [ -d "$SNAP/v$ver.$SNAP_EXT/@guest" ]; then
    continue
  fi

  if ! [ -f "$ARCH/v$ver.tar.gz" ]; then
    err ""
    err "Snapshot v$c$ver$r must be installed, but"
    err "  desktop archive v$c$ver$r is not available locally."
    err ""
    err "Download it with ${g}tl-comp-pull$r to continue."
    err ""
    exit 1
  fi

  run sudo mkdir -p "$SNAP/$TMP.$ver.d"
  if ! [ -f "$STAGE/$TMP.$ver.d/@root.btrfs" ] || ! [ -f "$STAGE/$TMP.$ver.d/@guest.btrfs" ]; then
    say "Verifying checksums"
    if ! (cd "$ARCH" && sha256sum -c "v$ver.sha256"); then
      err ""
      err "Desktop ${c}v$ver$r is corrupt or incmoplete!"
      err "  Could not verify ${c}$ver.sha256$r validity."
      err ""
      err "Download it with ${g}tl-comp-pull$r to try again."
      err ""
      exit 1
    fi

    run sudo mkdir -p "$STAGE/$TMP.$ver.d"
    run sudo tar -C "$STAGE/$TMP.$ver.d" -xzf "$ARCH/v$ver.tar.gz"
  fi

  for subvol in @root @guest; do
    run sudo btrfs receive -f "$STAGE/$TMP.$ver.d/$subvol.btrfs" "$SNAP/$TMP.$ver.d"
    # For devices with less than 20GB (desktop-mini), we want to keep the unpacked snapshots;
    # reduce I/O from unpacking again later.
    # For standard devices, delete the btrfs send file to save space.
    if [ "$IS_MINI_SYSTEM" = false ]; then
      run sudo rm "$STAGE/$TMP.$ver.d/$subvol.btrfs"
    fi
  done

  run mv "$SNAP/$TMP.$ver.d" "$SNAP/v$ver.$SNAP_EXT"
done

say "Syncing snapshot versions"
COMP_DIR="$BTRFS/@srv" "$ROOT/bin/tl-comp-sync" snapshots
say "Done."
