#!/usr/bin/env bash
# Build a browser-downloadable, encrypted archive of irreplaceable Athena data.
set -Eeuo pipefail
umask 077

OUTPUT_DIR=${ATHENA_EXPORT_DIR:-/data/emergency-backups}
RECIPIENT_FILE=${ATHENA_AGE_RECIPIENT_FILE:-/etc/mike-ai/recovery.age-recipient}
STATE=/var/lib/mike-ai-disaster-backup/latest
KEEP=${ATHENA_EXPORT_KEEP:-5}

log() { printf '\n==> %s\n' "$*"; }
die() { printf 'FEHLER: %s\n' "$*" >&2; exit 1; }

[[ $EUID -eq 0 ]] || die "Bitte als root ausführen."
[[ -s $RECIPIENT_FILE ]] || die "Age-Empfänger fehlt: $RECIPIENT_FILE"
[[ $KEEP =~ ^[1-9][0-9]*$ ]] || die "ATHENA_EXPORT_KEEP muss positiv sein."
for command in age zstd tar docker sha256sum flock; do
  command -v "$command" >/dev/null || die "$command fehlt."
done
exec 9>/run/lock/athena-export-backup.lock
flock -n 9 || die "Ein exportierbares Backup läuft bereits."

install -d -m 0755 "$OUTPUT_DIR"
install -d -m 0700 "$STATE"

log "Aktuellen Docker-Zustand sichern"
docker exec mike-ai-backup backup
latest=$(readlink -f /data/docker-backups/athena-latest.tar.gz)
[[ -s $latest ]] || die "Docker-Zustandsbackup fehlt."
gzip -t "$latest" || die "Docker-Zustandsbackup ist beschädigt."
install -m 0600 "$latest" "$STATE/docker-state.tar.gz"

stamp=$(date -u +%Y-%m-%dT%H-%M-%SZ)
name="athena-portable-$stamp.tar.zst.age"
partial="$OUTPUT_DIR/.$name.partial"
target="$OUTPUT_DIR/$name"
list=$(mktemp /tmp/athena-export-list.XXXXXX)
trap 'rm -f "$list" "$partial"' EXIT

# The encrypted export is roughly the size of its predecessor. Keeping all
# generations until after writing the next one requires one extra archive of
# free space and can deadlock a full backup disk. Release exactly one slot
# before writing when the configured retention is already reached. If the new
# export fails, KEEP-1 valid generations remain available.
mapfile -t existing < <(find "$OUTPUT_DIR" -maxdepth 1 -type f \
  -name 'athena-portable-*.tar.zst.age' -printf '%T@ %p\n' \
  | sort -rn | cut -d' ' -f2-)
while ((${#existing[@]} >= KEEP)); do
  last_index=$((${#existing[@]} - 1))
  oldest=${existing[$last_index]}
  rm -f -- "$oldest" "$oldest.sha256"
  unset 'existing[last_index]'
  existing=("${existing[@]}")
done

add_path() {
  local path=${1#/}
  [[ ! -e /$path ]] || printf '%s\0' "$path" >>"$list"
}

# Reproducible model/HF caches are deliberately omitted. Everything below is
# either a host configuration, project source, user input or generated result.
add_path /etc/mike-ai
add_path /opt/mike-ai
add_path /var/lib/mike-ai-disaster-backup/latest
add_path /data/voice/applio/logs
add_path /data/voice/applio/datasets
add_path /data/voice/applio/config.json
# Preserve user-created Applio state while continuing to omit the large,
# reproducible predictor/embedder/base-model caches.
add_path /data/voice/applio/mikes-applio-ui
add_path /data/voice/applio/models/pretraineds/custom
add_path /data/voice/omnivoice/output
add_path /data/voice/xvc/output
add_path /data/voice/studio
add_path /data/music
add_path /data/audio
add_path /data/llama-dashboard
add_path /data/mike-ai-operator
add_path /data/benchmarks
add_path /data/model-benchmarks
add_path /data/image-comparison
add_path /data/backups
add_path /data/deploy-backups

log "Portables, verschlüsseltes Backup erzeugen"
tar --create --numeric-owner --acls --xattrs -C / --null --files-from="$list" \
  | zstd -T0 -3 \
  | age -R "$RECIPIENT_FILE" -o "$partial"
chmod 0644 "$partial"
mv "$partial" "$target"
sha256sum "$target" >"$target.sha256"
chmod 0644 "$target.sha256"

log "Nur die letzten $KEEP Generationen behalten"
mapfile -t old < <(find "$OUTPUT_DIR" -maxdepth 1 -type f \
  -name 'athena-portable-*.tar.zst.age' -printf '%T@ %p\n' | sort -rn | tail -n +$((KEEP + 1)) | cut -d' ' -f2-)
for archive in "${old[@]}"; do
  rm -f -- "$archive" "$archive.sha256"
done

printf 'ATHENA_EXPORT_BACKUP_OK file=%s bytes=%s\n' "$target" "$(stat -c %s "$target")"
