#!/usr/bin/env bash
# Backup Sherlock -> EllVault (rclone).
# Roots supported:
#   $GROUP_HOME       -> ell_vault:/sherlock/group_home
#   $GROUP_SCRATCH    -> ell_vault:/sherlock/group_scratch
#   $SCRATCH          -> ell_vault:/sherlock/scratch
#
# Modes (choose one of these):
#   --group_home              # backs up $GROUP_HOME/konstantin
#   --group_scratch           # backs up $GROUP_SCRATCH/konstantin
#   --scratch                 # backs up $SCRATCH
#   --folder <abs_path>       # backs up one folder under any supported root
#   --folders-file <file>     # backs up list of absolute paths (one per line)
#
# Optional flags:
#   --sync                    # use rclone sync (WARNING: deletes remote extras)
#   --dry-run                 # rclone dry-run
#   --checksum                # use checksums instead of size-only (slower but more thorough)
#
# Examples:
#   backup_to_ellvault.sh --group_home
#   backup_to_ellvault.sh --group_scratch
#   backup_to_ellvault.sh --scratch
#   backup_to_ellvault.sh --folder "$GROUP_SCRATCH/konstantin/projectA"
#   backup_to_ellvault.sh --folders-file "$GROUP_HOME/konstantin/utils/folders_to_backup.txt"
#   backup_to_ellvault.sh --group_home --sync
#
# Crontab example (stagger times to avoid lock conflicts):
#   0 2 * * * bash -lc 'module load system rclone && bash /path/to/backup_to_ellvault.sh --group_home'
#   5 2 * * * bash -lc 'module load system rclone && bash /path/to/backup_to_ellvault.sh --group_scratch'
#   10 2 * * * bash -lc 'module load system rclone && bash /path/to/backup_to_ellvault.sh --folders-file "$GROUP_HOME/konstantin/utils/folders_to_backup.txt"'

set -euo pipefail

# ----- config -----
REMOTE="ell_vault:/sherlock"   # fixed base per your request
USER_SUBDIR="konstantin"       # your user folder under group roots
RCLONE_CMD="copy"              # default operation
RCLONE_EXTRA=()                # optional args accumulator

MODE=""
SINGLE_FOLDER=""
LIST_FILE=""
USE_CHECKSUM=false

usage() {
  sed -n '1,80p' "$0" | sed -n '1,80p' | grep -v '^set -euo' >/dev/null 2>&1
  cat <<EOF
Usage:
  $0 --group_home [--sync] [--dry-run]
  $0 --group_scratch [--sync] [--dry-run]
  $0 --scratch [--sync] [--dry-run]
  $0 --folder <abs_path> [--sync] [--dry-run]
  $0 --folders-file <file> [--sync] [--dry-run]
EOF
}

# ----- args -----
while [[ $# -gt 0 ]]; do
  case "$1" in
    --group_home)   MODE="group_home"; shift ;;
    --group_scratch)MODE="group_scratch"; shift ;;
    --scratch)      MODE="scratch"; shift ;;
    --folder)       MODE="folder"; SINGLE_FOLDER="$2"; shift 2 ;;
    --folders-file) MODE="list"; LIST_FILE="$2"; shift 2 ;;
    --sync)         RCLONE_CMD="sync"; shift ;;
    --dry-run)      RCLONE_EXTRA+=(--dry-run); shift ;;
    --checksum)     USE_CHECKSUM=true; shift ;;
    -h|--help)      usage; exit 0 ;;
    *) echo "Unknown arg: $1" >&2; usage; exit 1 ;;
  esac
done

[[ -n "${MODE:-}" ]] || { echo "Choose one mode"; usage; exit 1; }

# ----- deps -----
command -v rclone >/dev/null 2>&1 || { echo "rclone not found (module load system rclone)"; exit 2; }

# ----- resolve roots -----
: "${GROUP_HOME:?GROUP_HOME not set}"
: "${GROUP_SCRATCH:?GROUP_SCRATCH not set}"
: "${SCRATCH:?SCRATCH not set}"

ROOTS=(
  "$GROUP_HOME:::group_home"
  "$GROUP_SCRATCH:::group_scratch"
  "$SCRATCH:::scratch"
)

# Map root label -> remote subdir
remote_for_root() {
  case "$1" in
    group_home)     echo "$REMOTE/group_home" ;;
    group_scratch)  echo "$REMOTE/group_scratch" ;;
    scratch)        echo "$REMOTE/scratch" ;;
    *) return 1 ;;
  esac
}

# Identify which supported root a path belongs to; print "<root_label>:::<relative>"
classify_path() {
  local p="$1"
  # normalize: remove trailing slash
  p="${p%/}"
  for entry in "${ROOTS[@]}"; do
    local root="${entry%%:::*}"
    local label="${entry##*:::}"
    if [[ "$p" == "$root"* ]]; then
      local rel="${p#${root}/}"   # relative inside that root
      echo "${label}:::${rel}"
      return 0
    fi
  done
  return 1
}

# rclone options (logs go to Slurm)
base_rclone_opts() {
  local opts=(
    --fast-list
    --checkers=32
    --transfers=16
    --stats=30s
    --copy-links
    --one-file-system
    --update
    --use-mmap
  )
  
  if [[ "$USE_CHECKSUM" == "true" ]]; then
    opts+=(--checksum)
  else
    opts+=(--size-only)
  fi
  
  printf '%s\n' "${opts[@]}"
}

transfer_one() {
  local src="${1%/}/"           # ensure trailing slash to copy contents
  # Determine root/rel and the remote dest base
  local cls
  if ! cls=$(classify_path "${src%/}"); then
    echo "Skip (unsupported location): $src" >&2
    echo "Must be under one of: $GROUP_HOME, $GROUP_SCRATCH, or $SCRATCH" >&2
    return 0
  fi
  local root_label="${cls%%:::*}"
  local rel="${cls##*:::}"
  local dest_base
  dest_base="$(remote_for_root "$root_label")" || { echo "Bad root: $root_label"; return 1; }

  local dest="$dest_base/$rel"
  echo ">> ${RCLONE_CMD^}: $src -> $dest"
  # guard optional array with ${RCLONE_EXTRA:+...}
  local rclone_opts
  readarray -t rclone_opts < <(base_rclone_opts)
  rclone "$RCLONE_CMD" "$src" "$dest" \
    "${rclone_opts[@]}" ${RCLONE_EXTRA:+${RCLONE_EXTRA[@]}}
}

# ----- lock -----
LOCK_DIR="$GROUP_HOME/$USER_SUBDIR/logs/ellvault"
LOCK_FILE="$LOCK_DIR/backup.lock"
mkdir -p "$LOCK_DIR"
exec 9>>"$LOCK_FILE"

# default wait up to 6h (can override with LOCK_TIMEOUT=... in env)
LOCK_TIMEOUT="${LOCK_TIMEOUT:-21600}"

if ! flock -w "$LOCK_TIMEOUT" 9; then
  echo "Timed out waiting for lock; exiting."
  exit 0
fi

_cleanup_lock() {
  flock -u 9 || true
  rm -f -- "$LOCK_FILE" || true
}
trap _cleanup_lock EXIT INT TERM

echo "=== Backup start: $(date) ==="
echo "Mode: $MODE  Op: $RCLONE_CMD  Verification: $([ "$USE_CHECKSUM" == "true" ] && echo "checksum" || echo "size-only")"

case "$MODE" in
  group_home)
    src="$GROUP_HOME/$USER_SUBDIR"
    [[ -d "$src" ]] || { echo "Missing: $src"; exit 3; }
    transfer_one "$src"
    ;;
  group_scratch)
    src="$GROUP_SCRATCH/$USER_SUBDIR"
    [[ -d "$src" ]] || { echo "Missing: $src"; exit 3; }
    transfer_one "$src"
    ;;
  scratch)
    src="$SCRATCH"
    [[ -d "$src" ]] || { echo "Missing: $src"; exit 3; }
    transfer_one "$src"
    ;;
  folder)
    [[ -n "$SINGLE_FOLDER" && -d "$SINGLE_FOLDER" ]] || { echo "Bad folder: $SINGLE_FOLDER"; exit 4; }
    transfer_one "$SINGLE_FOLDER"
    ;;
  list)
    [[ -n "$LIST_FILE" && -f "$LIST_FILE" ]] || { echo "List file not found: $LIST_FILE"; exit 5; }
    while IFS= read -r line; do
      [[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue
      [[ -d "$line" ]] || { echo "Skip (missing): $line"; continue; }
      transfer_one "$line"
    done < "$LIST_FILE"
    ;;
esac

echo "=== Backup done: $(date) ==="
