#!/bin/bash
# Вторая копия /mnt/backup на 1 ТБ диск в роутере Huawei (SFTP :8022, remote `router:` в
# ~rus/.config/rclone/rclone.conf). Диск питается от роутера — не нагружает USB малины.
#
#   router-backup run     — ежедневно (таймер 06:00)
#   router-backup verify  — по воскресеньям после run: размеры + перечитать свежие архивы серверов по sha256
#   router-backup sample  — ежемесячно: случайные файлы OneDrive (~2 ГБ) перечитать с роутера и сверить sha256
#
# Пишем прямо в remote, а не через FUSE /mnt/router: так надёжнее и есть коды ошибок.
# Роутер не умеет хэши (shell_type=none) → сравнение по размеру+mtime, целостность — перечиткой.
set -uo pipefail
C=/home/rus/.config/rclone/rclone.conf
R=router:rpi-backup
SRC=/mnt/backup
TS=$(date +%F_%H%M)
LOGDIR=$SRC/router-logs; mkdir -p "$LOGDIR"
LOG="$LOGDIR/router-$TS.log"
rc() { rclone --config "$C" --log-file "$LOG" --log-level INFO --stats 10m --stats-log-level NOTICE \
              --transfers 2 --checkers 4 --sftp-concurrency 8 --retries 5 --low-level-retries 20 "$@"; }

mountpoint -q "$SRC" || { echo "$SRC не смонтирован — пропуск"; exit 1; }
rclone --config "$C" lsd router: >/dev/null 2>&1 || { echo "роутер недоступен по SFTP"; exit 1; }

run() {
  local r=0
  # 1) то, что никогда не удаляется: архив версий, манифесты, логи — только copy
  for d in archive manifests logs; do
    [ -d "$SRC/onedrive/$d" ] && { rc copy "$SRC/onedrive/$d" "$R/onedrive/$d" || r=1; }
  done
  # 2) зеркало current. Удалённое из current локально уже лежит в archive (скопированном выше).
  #    --max-delete: если локально «пропало» много файлов (сбой диска/монтирования) — стоп, а не зачистка копии
  rc sync "$SRC/onedrive/current" "$R/onedrive/current" --exclude '*.partial' --max-delete 1000 || r=1
  # 3) зашифрованные бэкапы серверов — зеркало (ротация 14/8/12 → удаляется пара файлов в день)
  rc sync "$SRC/servers" "$R/servers" --max-delete 50 || r=1
  echo "run: $([ $r = 0 ] && echo OK || echo 'ЕСТЬ ОШИБКИ') — лог $LOG"
  return $r
}

verify() {
  local r=0 s rel want got
  # размеры всего дерева (только листинг, без чтения данных)
  rc check "$SRC/onedrive/current" "$R/onedrive/current" --size-only --one-way --exclude '*.partial' || r=1
  rc check "$SRC/servers" "$R/servers" --size-only --one-way || r=1
  # перечитать с роутера последний daily каждого сервера и сверить с sha256
  for s in $(ls -1t "$SRC"/servers/*/daily/*.age.sha256 2>/dev/null | awk -F/ '!seen[$(NF-2)]++'); do
    rel=${s#"$SRC/servers/"}; rel=${rel%.sha256}
    want=$(cut -d' ' -f1 "$s")
    got=$(rclone --config "$C" cat "$R/servers/$rel" 2>>"$LOG" | sha256sum | cut -d' ' -f1)
    if [ "$want" = "$got" ]; then echo "read-back OK  $rel"; else echo "read-back BAD $rel"; r=1; fi
  done
  echo "verify: $([ $r = 0 ] && echo OK || echo 'ЕСТЬ РАСХОЖДЕНИЯ')"
  return $r
}

sample() {
  # Роутер хэшей не считает, verify сверяет OneDrive только по размеру — тихую порчу внутри
  # файла того же размера ловит только перечитка. Берём файлы старше суток (точно должны
  # быть скопированы), до SAMPLE_BYTES суммарно и не больше SAMPLE_MAX штук.
  local budget=${SAMPLE_BYTES:-2147483648} max=${SAMPLE_MAX:-300} total=0 n=0 bad=0 miss=0 size rel want got
  while IFS=' ' read -r size rel; do
    [ $n -ge $max ] && break
    [ $total -gt 0 ] && [ $((total + size)) -gt $budget ] && continue
    want=$(sha256sum "$SRC/onedrive/$rel" | cut -d' ' -f1)
    if ! got=$(rclone --config "$C" cat "$R/onedrive/$rel" 2>>"$LOG" | sha256sum | cut -d' ' -f1) \
       || [ "$got" = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" -a "$size" != 0 ]; then
      echo "MISSING $rel"; miss=$((miss + 1))
    elif [ "$want" != "$got" ]; then
      echo "BAD     $rel"; bad=$((bad + 1))
    fi
    n=$((n + 1)); total=$((total + size))
  done < <(cd "$SRC/onedrive" && find current archive -type f ! -name '*.partial' -mmin +1440 -printf '%s %p\n' | shuf)
  echo "sample: проверено $n файлов, $((total / 1048576)) МиБ; повреждено $bad, нет на роутере $miss"
  [ $bad = 0 ] && { [ $miss = 0 ] || [ -n "${SAMPLE_ALLOW_MISSING:-}" ]; }
}

case "${1:-run}" in
  run)    run; r=$?; [ "$(date +%u)" = 7 ] && { verify || r=1; }; exit $r ;;
  verify) verify ;;
  sample) sample ;;
  *) echo "usage: router-backup run|verify|sample" >&2; exit 2 ;;
esac
