#!/bin/bash
# Backup eorganize: postgres dump + uploads tar, rsynced to the home backup
# target. Runs from cron as root on the VPS. Mirrors the gcmanager pattern
# (file names, remote paths, ssh key) so all webapp backups land in the
# same layout on the home Mac.
#
# Retention: 14 most recent dailies + 8 weekly snapshots beyond that. Pruning
# is driven from this script via ssh after the upload succeeds.
set -euo pipefail

APP="eorganize"
BACKUP_DIR="/home/eorganize/backups"
DB_CONTAINER="eorganize-db"
DB_USER="eorg"
DB_NAME="eorgdb"
UPLOADS_VOLUME="eorganize_prod_uploads"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)

DB_FILE="${APP}_prod_${TIMESTAMP}.sql.gz"
UPLOADS_FILE="${APP}_uploads_${TIMESTAMP}.tar.gz"

REMOTE_USER="erictran"
REMOTE_HOST="67.182.44.118"
REMOTE_PORT="2222"
REMOTE_DB_PATH="/Volumes/ExtraSSD/webapp_backup_dbs/"
REMOTE_ATTACH_PATH="/Volumes/ExtraSSD/webapp_backup_attachments/${APP}/"
SSH_KEY="/root/.ssh/backup_to_home"
SSH_BASE_OPTS=(-i "$SSH_KEY" -p "$REMOTE_PORT" -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null)

mkdir -p "$BACKUP_DIR"

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }

log "Starting ${APP} backup"

# 1. Postgres dump
log "Dumping database from ${DB_CONTAINER}…"
docker exec "$DB_CONTAINER" pg_dump -U "$DB_USER" "$DB_NAME" | gzip > "${BACKUP_DIR}/${DB_FILE}"
if [ ! -s "${BACKUP_DIR}/${DB_FILE}" ]; then
  log "ERROR: DB dump is empty"
  rm -f "${BACKUP_DIR}/${DB_FILE}"
  exit 1
fi
log "DB backup: ${DB_FILE} ($(du -h "${BACKUP_DIR}/${DB_FILE}" | cut -f1))"

# 2. Uploads tarball — item photos live in the docker volume
log "Archiving uploads volume ${UPLOADS_VOLUME}…"
docker run --rm -v "${UPLOADS_VOLUME}:/data:ro" alpine tar -czf - -C /data . > "${BACKUP_DIR}/${UPLOADS_FILE}"
if [ ! -s "${BACKUP_DIR}/${UPLOADS_FILE}" ]; then
  log "WARN: uploads tarball is empty (no photos yet) — keeping a 0-byte placeholder"
fi
log "Uploads backup: ${UPLOADS_FILE} ($(du -h "${BACKUP_DIR}/${UPLOADS_FILE}" | cut -f1))"

# 3. Ensure attachments subfolder exists on the remote
log "Ensuring remote attachments folder exists…"
ssh "${SSH_BASE_OPTS[@]}" "${REMOTE_USER}@${REMOTE_HOST}" "mkdir -p '${REMOTE_ATTACH_PATH}'"

# 4. Transfer DB and attachments to their respective folders
log "Transferring DB to ${REMOTE_DB_PATH}"
rsync -avz -e "ssh ${SSH_BASE_OPTS[*]}" \
  "${BACKUP_DIR}/${DB_FILE}" \
  "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_DB_PATH}" || { log "ERROR: db transfer failed"; exit 1; }

log "Transferring uploads to ${REMOTE_ATTACH_PATH}"
rsync -avz -e "ssh ${SSH_BASE_OPTS[*]}" \
  "${BACKUP_DIR}/${UPLOADS_FILE}" \
  "${REMOTE_USER}@${REMOTE_HOST}:${REMOTE_ATTACH_PATH}" || { log "ERROR: uploads transfer failed"; exit 1; }

log "Transfers OK — removing local copies"
rm -f "${BACKUP_DIR}/${DB_FILE}" "${BACKUP_DIR}/${UPLOADS_FILE}"

# 5. Prune the remote: keep 14 newest + 8 weekly snapshots beyond that.
# Decision logic runs locally over the file list returned by the Mac; deletes
# are batched in a single ssh round-trip.
prune_remote() {
  local remote_path="$1"
  local pattern="$2"

  local listing
  listing=$(ssh "${SSH_BASE_OPTS[@]}" "${REMOTE_USER}@${REMOTE_HOST}" \
    "cd '${remote_path}' 2>/dev/null && ls -1 ${pattern} 2>/dev/null" || true)
  [ -z "$listing" ] && return 0

  local to_delete
  to_delete=$(printf '%s\n' "$listing" | python3 - <<'PYEOF'
import sys, re, os, datetime as dt
KEEP_DAILY = 14
KEEP_WEEKLY = 8
files = [l.strip() for l in sys.stdin if l.strip()]
def stamp(f):
    m = re.search(r"_(\d{8})_(\d{6})", os.path.basename(f))
    if not m: return None
    try:
        return dt.datetime.strptime(m.group(1)+m.group(2), "%Y%m%d%H%M%S")
    except ValueError:
        return None
parsed = sorted(((stamp(f), f) for f in files if stamp(f)), reverse=True)
keep = set()
for _, f in parsed[:KEEP_DAILY]:
    keep.add(f)
weeks_seen = set()
for s, f in parsed[KEEP_DAILY:]:
    iso = s.isocalendar()
    key = (iso[0], iso[1])
    if key in weeks_seen:
        continue
    if len(weeks_seen) >= KEEP_WEEKLY:
        continue
    weeks_seen.add(key)
    keep.add(f)
for _, f in parsed:
    if f not in keep:
        print(f)
PYEOF
)

  if [ -z "$to_delete" ]; then
    log "  no files to prune in ${remote_path}"
    return 0
  fi

  log "  pruning $(printf '%s\n' "$to_delete" | wc -l | tr -d ' ') file(s) from ${remote_path}:"
  printf '%s\n' "$to_delete" | sed 's/^/    /'
  printf '%s\n' "$to_delete" \
    | tr '\n' '\0' \
    | ssh "${SSH_BASE_OPTS[@]}" "${REMOTE_USER}@${REMOTE_HOST}" \
        "cd '${remote_path}' && xargs -0 rm -f"
}

log "Pruning remote (keep ${KEEP_DAILY:-14} daily + ${KEEP_WEEKLY:-8} weekly)…"
prune_remote "$REMOTE_DB_PATH"     "${APP}_prod_*.sql.gz"
prune_remote "$REMOTE_ATTACH_PATH" "${APP}_uploads_*.tar.gz"

log "Done."
