#!/usr/bin/env bash
# =============================================================================
# Experiments DB — incremental backup script
#
# Usage:
#   backup.sh daily    – backup only if DB changed since last run; keep 5
#   backup.sh weekly   – unconditional backup; keep 2
#
# Cron (added by setup_cron.sh):
#   0 2 * * *   /home/sam/docker-images/experiments-database/scripts/backup.sh daily
#   0 3 * * 0   /home/sam/docker-images/experiments-database/scripts/backup.sh weekly
# =============================================================================

set -euo pipefail

# ── Config ────────────────────────────────────────────────────────────────────
MODE="${1:-daily}"
BACKUP_ROOT="/home/sam/synology/Backups/Experiments-DB-Backup"
DAILY_DIR="$BACKUP_ROOT/daily"
WEEKLY_DIR="$BACKUP_ROOT/weekly"
STATE_FILE="$BACKUP_ROOT/.last_backup_state"
LOG_FILE="$BACKUP_ROOT/backup.log"
KEEP_DAILY=5
KEEP_WEEKLY=2

PG_CONTAINER="experiments_postgres"
PG_USER="expuser"
PG_DB="experiments_db"

# ── Helpers ───────────────────────────────────────────────────────────────────
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"; }

die() { log "ERROR: $*"; exit 1; }

# Ensure directories exist
mkdir -p "$DAILY_DIR" "$WEEKLY_DIR"

# Check postgres container is running
if ! docker inspect --format '{{.State.Running}}' "$PG_CONTAINER" 2>/dev/null | grep -q true; then
    die "Container $PG_CONTAINER is not running."
fi

# ── DB state fingerprint ──────────────────────────────────────────────────────
# Combines row counts from all key tables + latest audit_log timestamp.
# Changes in any table (insert, update, delete) alter this string.
get_db_state() {
    docker exec "$PG_CONTAINER" psql -U "$PG_USER" -d "$PG_DB" -t -A -c "
        SELECT
            (SELECT COUNT(*) FROM experiments)      || ',' ||
            (SELECT COUNT(*) FROM animals)          || ',' ||
            (SELECT COUNT(*) FROM daily_statuses)   || ',' ||
            (SELECT COUNT(*) FROM daily_analyses)   || ',' ||
            (SELECT COUNT(*) FROM session_files)    || ',' ||
            COALESCE(
                (SELECT MAX(timestamp)::text FROM audit_logs),
                'no-audit'
            )
        AS fingerprint;
    " 2>/dev/null | tr -d '[:space:]'
}

# ── Dump function ─────────────────────────────────────────────────────────────
run_dump() {
    local dest="$1"
    local tmp="${dest}.tmp"
    log "Dumping → $dest"
    docker exec "$PG_CONTAINER" pg_dump \
        -U "$PG_USER" \
        --format=custom \
        --compress=9 \
        "$PG_DB" > "$tmp" \
        && mv "$tmp" "$dest" \
        || { rm -f "$tmp"; die "pg_dump failed."; }
    local size
    size=$(du -sh "$dest" | cut -f1)
    log "Backup written ($size): $(basename "$dest")"
}

# ── Prune old backups ─────────────────────────────────────────────────────────
prune() {
    local dir="$1"
    local keep="$2"
    local count
    count=$(ls -1 "$dir"/*.pgdump 2>/dev/null | wc -l)
    if (( count > keep )); then
        ls -1t "$dir"/*.pgdump | tail -n "+$((keep + 1))" | while read -r f; do
            log "Pruning old backup: $(basename "$f")"
            rm -f "$f"
        done
    fi
}

# ── Main ──────────────────────────────────────────────────────────────────────
TIMESTAMP=$(date '+%Y%m%d_%H%M%S')

if [[ "$MODE" == "daily" ]]; then
    log "--- Daily backup check ---"
    CURRENT_STATE=$(get_db_state)

    if [[ -z "$CURRENT_STATE" ]]; then
        die "Could not read DB state."
    fi

    LAST_STATE=$(cat "$STATE_FILE" 2>/dev/null || echo "")

    if [[ "$CURRENT_STATE" == "$LAST_STATE" ]]; then
        log "No changes detected since last backup. Skipping."
        exit 0
    fi

    DEST="$DAILY_DIR/experiments_db_daily_${TIMESTAMP}.pgdump"
    run_dump "$DEST"

    # Save new state only after successful dump
    echo "$CURRENT_STATE" > "$STATE_FILE"
    log "State fingerprint updated."

    prune "$DAILY_DIR" "$KEEP_DAILY"
    log "Daily backup complete."

elif [[ "$MODE" == "weekly" ]]; then
    log "--- Weekly backup (unconditional) ---"
    DEST="$WEEKLY_DIR/experiments_db_weekly_${TIMESTAMP}.pgdump"
    run_dump "$DEST"
    prune "$WEEKLY_DIR" "$KEEP_WEEKLY"
    log "Weekly backup complete."

else
    die "Unknown mode '$MODE'. Use 'daily' or 'weekly'."
fi
