#!/usr/bin/env bash
set -euo pipefail

#################### CONFIG ####################

LUKS_SOURCE="/dev/disk/by-uuid/bdd2a016-b6db-493d-ad0a-e8dd8a93dfbd"
MAPPER_NAME="usb_safe"
MOUNTPOINT="/mnt/usb_safe"
RAW_DEVICE=""

# Source vault (docs/vault)
SOURCE_VAULT_MOUNT="$HOME/docs/vault"
SOURCE_VAULT_IMAGE="$HOME/secure/containers/work-vault.img"
SOURCE_VAULT_MAPPER="vault"

# Tomb
SECRETS_TOMB_FILE="$HOME/.secrets.tomb"
SECRETS_TOMB_KEY="$HOME/.secrets.tomb.key"
SECRETS_TOMB_MOUNT="/media/.secrets"

BACKUP_ITEMS=(
  "$HOME/.abook"
  "$HOME/.config"
  "$HOME/docs"
  "$HOME/git"
  "$HOME/.password-store"
  "$HOME/secure"
  "$HOME/.password.tomb"
  "$HOME/.password.tomb.key"
  "$HOME/.secrets.tomb"
  "$HOME/.secrets.tomb.key"
  "/media/.secrets"
  "$HOME/.bitmonero"
  "$HOME/.electrum"
  "$HOME/.zotero-data"
)

RSYNC_OPTS=(
  -a
  --delete
  --no-perms
  --no-owner
  --no-group
  --no-xattrs
  --omit-dir-times
  --progress
)

LOGFILE="$HOME/backup_flash_rsync.log"

#################### HELPERS ####################

die() { echo "ERROR: $*" >&2; exit 1; }

is_mapper_open() { [[ -e "/dev/mapper/${MAPPER_NAME}" ]]; }
is_mounted()     { mountpoint -q "${MOUNTPOINT}"; }

require_mount() {
  local path="$1"
  local label="$2"
  [[ -e "$path" ]] || die "${label} path missing: ${path}"
  mountpoint -q "$path" || die "${label} is not mounted: ${path}"
}

OPENED_SOURCE_VAULT=0
OPENED_SECRETS_TOMB=0

ensure_source_vault_open() {
  if mountpoint -q "${SOURCE_VAULT_MOUNT}"; then
    echo ">>> Source vault already mounted"
    return
  fi

  echo ">>> Opening source vault..."
  mkdir -p "${SOURCE_VAULT_MOUNT}"

  if [[ ! -e "/dev/mapper/${SOURCE_VAULT_MAPPER}" ]]; then
    sudo cryptsetup open "${SOURCE_VAULT_IMAGE}" "${SOURCE_VAULT_MAPPER}"
  fi

  sudo mount "/dev/mapper/${SOURCE_VAULT_MAPPER}" "${SOURCE_VAULT_MOUNT}"
  OPENED_SOURCE_VAULT=1
}

ensure_secrets_tomb_open() {
  if mountpoint -q "${SECRETS_TOMB_MOUNT}"; then
    echo ">>> Secrets tomb already mounted"
    return
  fi

  echo ">>> Opening secrets tomb..."
  tomb open "${SECRETS_TOMB_FILE}" -k "${SECRETS_TOMB_KEY}" "${SECRETS_TOMB_MOUNT}"
  OPENED_SECRETS_TOMB=1
}

cleanup() {
  set +e

  if is_mounted; then
    echo ">>> Unmounting backup drive..."
    sudo umount "${MOUNTPOINT}" || true
  fi

  if is_mapper_open; then
    echo ">>> Closing backup mapper..."
    sudo cryptsetup close "${MAPPER_NAME}" || true
  fi

  if [[ "${OPENED_SECRETS_TOMB}" -eq 1 ]]; then
    echo ">>> Closing secrets tomb..."
    tomb close "${SECRETS_TOMB_MOUNT}" || true
  fi

  if [[ "${OPENED_SOURCE_VAULT}" -eq 1 ]]; then
    echo ">>> Closing source vault..."
    sudo umount "${SOURCE_VAULT_MOUNT}" || true
    sudo cryptsetup close "${SOURCE_VAULT_MAPPER}" || true
  fi

  if [[ -n "${RAW_DEVICE}" ]]; then
    udisksctl power-off -b "${RAW_DEVICE}" >/dev/null 2>&1 || true
  fi
}

trap cleanup EXIT

#################### OPEN DESTINATION ####################

echo ">>> Ensuring backup mountpoint exists..."
sudo mkdir -p "${MOUNTPOINT}"

if is_mounted; then
  echo ">>> Backup drive already mounted at ${MOUNTPOINT}"
elif is_mapper_open; then
  echo ">>> Backup mapper already open, mounting..."
  sudo mount "/dev/mapper/${MAPPER_NAME}" "${MOUNTPOINT}"
else
  echo ">>> Opening backup LUKS..."
  sudo cryptsetup open "${LUKS_SOURCE}" "${MAPPER_NAME}"
  echo ">>> Mounting backup filesystem..."
  sudo mount "/dev/mapper/${MAPPER_NAME}" "${MOUNTPOINT}"
fi

#################### OPEN SOURCES ####################

ensure_source_vault_open
ensure_secrets_tomb_open

require_mount "${SOURCE_VAULT_MOUNT}" "Vault"
require_mount "${SECRETS_TOMB_MOUNT}" "Secrets"

#################### BACKUP ####################

echo "[$(date)] Backup start" >> "$LOGFILE"

ANY_WARNING=0

for SRC in "${BACKUP_ITEMS[@]}"; do
  [[ -e "${SRC}" ]] || die "Missing: ${SRC}"

  NAME="$(basename "${SRC}")"
  DEST="${MOUNTPOINT}/${NAME}"

  echo ">>> ${SRC} -> ${DEST}"
  echo "[$(date)] rsync: ${SRC} -> ${DEST}" >> "$LOGFILE"

  set +e
  if [[ -d "${SRC}" ]]; then
    rsync "${RSYNC_OPTS[@]}" "${SRC}/" "${DEST}/" 2>>"$LOGFILE"
  else
    rsync "${RSYNC_OPTS[@]}" "${SRC}" "${DEST}" 2>>"$LOGFILE"
  fi
  RS=$?
  set -e

  if [[ "${RS}" -eq 0 ]]; then
    echo ">>> rsync OK: ${SRC}"
    echo "[$(date)] rsync OK: ${SRC}" >> "$LOGFILE"
  elif [[ "${RS}" -eq 23 || "${RS}" -eq 24 ]]; then
    echo ">>> WARNING: rsync non-critical issues (code ${RS}) for: ${SRC}"
    echo "[$(date)] WARNING: rsync code ${RS} for: ${SRC}" >> "$LOGFILE"
    ANY_WARNING=1
  else
    echo "[$(date)] ERROR: rsync failed code ${RS} for: ${SRC}" >> "$LOGFILE"
    exit "${RS}"
  fi
done

if [[ "${ANY_WARNING}" -eq 1 ]]; then
  echo ">>> Backup completed WITH WARNINGS"
  echo "[$(date)] Backup completed WITH WARNINGS" >> "$LOGFILE"
else
  echo ">>> Backup completed OK"
  echo "[$(date)] Backup completed OK" >> "$LOGFILE"
fi

echo ">>> Done"
sync
