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

export PATH="/usr/sbin:/sbin:/usr/bin:/bin:$PATH"

#################### CONFIG – EDIT THESE ####################

# LUKS2 block device
LUKS_DEVICE="/dev/disk/by-uuid/6659e638-d52a-4c32-9781-9dcedd44db35"

# Name for the mapper device (will appear as /dev/mapper/${LUKS_NAME})
LUKS_NAME="shield"

# Mount point for the decrypted filesystem
LUKS_MOUNTPOINT="/mnt/shield"

# Local folders to back up with rsync → LUKS volume
BACKUP_ITEMS=(
    "$HOME/"
    "/media/.secrets/"
)

# Machine identifier (destination subdir on the LUKS volume)
MACHINE_DIR="T480"

# rsync exclude patterns (passed via stdin to --exclude-from=-)
# Paths are relative to each rsync source root (i.e. $HOME for the home backup).
read -r -d '' RSYNC_EXCLUDES <<'EOF' || true
# === Top-level junk ===
downloads/
Downloads/
tmp/
.tmp/

# === All caches (3.9G in .cache alone) ===
.cache/

# === Browsers (rebuilt on next launch) ===
.mozilla/
.mullvad-browser/

# === Shell / history / runtime state ===
.fasd
.viminfo
.zcompdump*
.lesshst
.sqlite_history
.python_history
.node_repl_history
.wget-hsts
.xsession-errors*
.Xauthority
.ICEauthority
.dbus/
.pki/
.gvfs/

# === Trash ===
.local/share/Trash/
.Trash-*/

# === Claude Code old versions (884M, current one re-fetches) ===
.local/share/claude/versions/

# === Haskell / cabal (1.9G total, fully regenerable) ===
.cache/cabal/
.local/state/cabal/

# === Rust / cargo registry (152M, regenerable from Cargo.lock) ===
.cargo/registry/
.cargo/git/

# === Python: venvs, caches, bytecode (anywhere in tree) ===
__pycache__/
*.pyc
*.pyo
.mypy_cache/
.pytest_cache/
.ruff_cache/
.tox/
.venv/
venv/
.virtualenvs/
.local/share/pipx/

# === Node / JS ===
node_modules/
.npm/
.yarn/cache/
.pnpm-store/

# === Build artifacts in repos ===
build/
dist/
target/
.gradle/
.m2/repository/

# === LaTeX caches ===
.texlive*/texmf-var/
.texlive*/texmf-cache/

# === Java runtime junk ===
.java/

# === Notmuch index (668M, rebuild with `notmuch new`) ===
.local/share/mail/.notmuch/xapian/

# === Whisper.cpp models (754M, redownloadable) ===
git/whisper.cpp/models/
git/whisper.cpp/build/

# === Reinstallable third-party software in ~/opt ===
opt/tor-browser/
opt/monero-gui-*/
opt/Electrum-*/

# === Themes (509M, reinstall from upstream) ===
.themes/gruvbox-gtk-theme/

# === Crypto node chain data (re-syncs) ===
.bitmonero/
.shared-ringdb/
.ethereum/geth/chaindata/
.bitcoin/blocks/
.bitcoin/chainstate/

# === Electron / chat app caches ===
.config/Signal/Cache/
.config/Code/Cache/
.config/Code/CachedData/
.config/Code/logs/

# === Flatpak / Snap caches ===
.var/app/*/cache/
snap/*/common/.cache/

# === OS junk ===
.DS_Store
Thumbs.db
*.swp
*.swo
*~
core
core.*
EOF

#################### INTERNALS – NO NEED TO EDIT ############

need_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "ERROR: missing command: $1" >&2; exit 1; }; }

cleanup() {
    echo
    echo ">>> Cleaning up..."

    if mountpoint -q "${LUKS_MOUNTPOINT}"; then
        echo ">>> Unmounting ${LUKS_MOUNTPOINT}..."
        sudo umount "${LUKS_MOUNTPOINT}" || true
    fi

    if [ -e "/dev/mapper/${LUKS_NAME}" ]; then
        echo ">>> Closing LUKS mapping ${LUKS_NAME}..."
        sudo cryptsetup close "${LUKS_NAME}" || true
    fi
}
trap cleanup EXIT

need_cmd cryptsetup
need_cmd rsync
need_cmd mountpoint

# Cache sudo credentials early
sudo -v

echo ">>> Ensuring mountpoint exists: ${LUKS_MOUNTPOINT}"
sudo mkdir -p "${LUKS_MOUNTPOINT}"

if [ -e "/dev/mapper/${LUKS_NAME}" ]; then
    echo ">>> WARNING: /dev/mapper/${LUKS_NAME} already exists, assuming already open."
else
    echo ">>> Opening LUKS volume (interactive prompt)..."
    sudo cryptsetup open "${LUKS_DEVICE}" "${LUKS_NAME}"
fi

if [ ! -e "/dev/mapper/${LUKS_NAME}" ]; then
    echo "ERROR: /dev/mapper/${LUKS_NAME} not found after cryptsetup open." >&2
    exit 1
fi

if mountpoint -q "${LUKS_MOUNTPOINT}"; then
    echo ">>> WARNING: ${LUKS_MOUNTPOINT} already mounted."
else
    echo ">>> Mounting /dev/mapper/${LUKS_NAME} on ${LUKS_MOUNTPOINT}..."
    sudo mount "/dev/mapper/${LUKS_NAME}" "${LUKS_MOUNTPOINT}"
fi

echo ">>> LUKS volume mounted at ${LUKS_MOUNTPOINT}"

echo ">>> Starting rsync backups of local folders -> ${LUKS_MOUNTPOINT}/${MACHINE_DIR}/..."

sudo mkdir -p "${LUKS_MOUNTPOINT}/${MACHINE_DIR}"

for SRC in "${BACKUP_ITEMS[@]}"; do
    if [ ! -d "$SRC" ]; then
        echo ">>> WARNING: source folder does not exist, skipping: $SRC"
        continue
    fi

    case "$SRC" in
        "$HOME/"|"$HOME")
            RELDEST="${MACHINE_DIR}/HOME"
            ;;
        "/media/.secrets/"|"/media/.secrets")
            RELDEST="${MACHINE_DIR}/secrets"
            ;;
        *)
            NAME="$(basename "${SRC%/}")"
            RELDEST="${MACHINE_DIR}/${NAME}"
            ;;
    esac

    DEST="${LUKS_MOUNTPOINT}/${RELDEST}"
    sudo mkdir -p "${DEST}"

    echo ">>> rsync: ${SRC} -> ${DEST}"
    set +e
    sudo rsync -a --delete --progress \
        --exclude-from=- \
        "${SRC%/}/" \
        "${DEST}/" <<< "${RSYNC_EXCLUDES}"
    RSYNC_RC=$?
    set -e
    case "${RSYNC_RC}" in
        0)  ;;
        23|24)
            echo ">>> rsync finished with code ${RSYNC_RC} (some files skipped/vanished); continuing."
            ;;
        *)
            echo "ERROR: rsync failed with code ${RSYNC_RC} on ${SRC}" >&2
            exit "${RSYNC_RC}"
            ;;
    esac
done

echo ">>> All rsync backups finished."
echo ">>> Unmounting and closing LUKS (via trap)..."
