#!/usr/bin/env bash
# restore-verify.sh — restore the newest snapshot and prove it matches.
# Tested on Debian 12 / bash 5.2, restic 0.18.0, sqlite 3.40.1.
#
# Usage: RESTIC_REPOSITORY=... RESTIC_PASSWORD_FILE=... \
#        restore-verify.sh <appdata-root> <dump-root> [target-dir]
#
# Two rules this script encodes, both learned the hard way:
#   1. Never compare a live database file. The app keeps writing, so the
#      checksum drifts the moment the backup finishes. Compare the dump.
#   2. Never compare transient sidecar files (-journal, -wal, -shm).
set -euo pipefail

SRC="${1:?usage: restore-verify.sh <appdata-root> <dump-root> [target]}"
DUMPS="${2:?usage: restore-verify.sh <appdata-root> <dump-root> [target]}"
TARGET="${3:-/tmp/restore-verify}"
export RESTIC_REPOSITORY="${RESTIC_REPOSITORY:?set RESTIC_REPOSITORY}"
export RESTIC_PASSWORD_FILE="${RESTIC_PASSWORD_FILE:?set RESTIC_PASSWORD_FILE}"

rm -rf "$TARGET"; mkdir -p "$TARGET"

echo "==> newest snapshot"
restic snapshots --latest 1 --compact

echo "==> restoring into $TARGET"
restic restore latest --target "$TARGET" --verify

manifest() {
  ( cd "$1" && find . -type f \
      -not -path '*/thumbs/*' \
      -not -path '*/cache/*' \
      -not -name '*.tmp' \
      -not -name '*-journal' -not -name '*-wal' -not -name '*-shm' \
      -not -path '*/vaultwarden/data/db.sqlite3' \
      -not -path '*/mealie/data/mealie.sqlite' \
      -print0 | sort -z | xargs -0 sha256sum )
}

check_tree() {
  local label="$1" src="$2" restored="$3"
  echo "==> $label"
  if [ ! -d "$restored" ]; then echo "    FAIL: missing $restored"; return 1; fi
  manifest "$src" > /tmp/src.manifest
  manifest "$restored" > /tmp/restored.manifest
  echo "    source files:   $(wc -l < /tmp/src.manifest)"
  echo "    restored files: $(wc -l < /tmp/restored.manifest)"
  if diff -u /tmp/src.manifest /tmp/restored.manifest > /tmp/manifest.diff; then
    echo "    checksums: identical"
  else
    echo "    checksums: DIFFERENCES"
    head -40 /tmp/manifest.diff
    return 1
  fi
}

check_tree "appdata" "$SRC" "$TARGET$SRC"
check_tree "database dumps" "$DUMPS" "$TARGET$DUMPS"

# Bytes matching is not the same as a database that opens. Ask SQLite.
db="$TARGET$DUMPS/vaultwarden/db.sqlite3"
if [ -f "$db" ]; then
  echo "==> sqlite integrity of restored dump"
  result="$(sqlite3 "$db" 'PRAGMA integrity_check;')"
  echo "    integrity_check: $result"
  echo "    rows: $(sqlite3 "$db" 'select count(*) from ciphers;')"
  [ "$result" = "ok" ] || { echo "FAIL: restored dump is corrupt"; exit 1; }
fi

echo "PASS"
