#!/usr/bin/env bash
# =============================================================================
# TipSharks ELO API - PostgreSQL Backup Script
# =============================================================================
# Creates daily compressed PostgreSQL dumps with 7-day rotation.
# Designed to run as a cron job or systemd timer.
#
# Usage:
#   ./infrastructure/scripts/backup.sh
#
# Environment variables (set in .env or export before running):
#   BACKUP_DIR        - Backup destination directory (default: ./backups)
#   DB_HOST           - Database host (default: localhost)
#   DB_PORT           - Database port (default: 5432)
#   DB_USER           - Database user (default: tipsharks)
#   DB_PASSWORD       - Database password (default: tipsharks)
#   DB_NAME           - Database name (default: tipsharks)
#   BACKUP_RETENTION  - Days to keep backups (default: 7)
#   PGDUMP_PATH       - Path to pg_dump binary (default: auto-detect)
# =============================================================================

set -euo pipefail

# ── Configuration ──────────────────────────────────────────────────────────

BACKUP_DIR="${BACKUP_DIR:-./backups}"
DB_HOST="${DB_HOST:-localhost}"
DB_PORT="${DB_PORT:-5432}"
DB_USER="${DB_USER:-tipsharks}"
DB_PASSWORD="${DB_PASSWORD:-tipsharks}"
DB_NAME="${DB_NAME:-tipsharks}"
BACKUP_RETENTION="${BACKUP_RETENTION:-7}"

# Auto-detect pg_dump from Docker environment if available
PGDUMP_PATH="${PGDUMP_PATH:-}"
if [ -z "$PGDUMP_PATH" ]; then
    if command -v pg_dump &>/dev/null; then
        PGDUMP_PATH="pg_dump"
    elif command -v docker &>/dev/null; then
        # Fall back to docker exec if a postgres container is running
        PGDUMP_CONTAINER="${PGDUMP_CONTAINER:-tipsharks_prod_db}"
        PGDUMP_PATH="docker exec -i ${PGDUMP_CONTAINER} pg_dump"
    else
        echo "[ERROR] pg_dump not found and no Docker environment detected."
        echo "        Install postgresql-client or set PGDUMP_PATH manually."
        exit 1
    fi
fi

TIMESTAMP=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/${DB_NAME}_${TIMESTAMP}.sql.gz"
LATEST_LINK="${BACKUP_DIR}/${DB_NAME}_latest.sql.gz"

# ── Logging ────────────────────────────────────────────────────────────────

log() {
    local level="$1"
    shift
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] [${level}] $*"
}

# ── Prerequisites ──────────────────────────────────────────────────────────

# Create backup directory if it doesn't exist
mkdir -p "${BACKUP_DIR}"

# Export password for pg_dump (suppresses password prompt)
export PGPASSWORD="${DB_PASSWORD}"

# ── Backup ─────────────────────────────────────────────────────────────────

log "INFO" "Starting backup of database '${DB_NAME}' on ${DB_HOST}:${DB_PORT}"
log "INFO" "Output: ${BACKUP_FILE}"

# Run pg_dump with compression
# Flags:
#   --clean           : Drop objects before creating (for restore convenience)
#   --if-exists       : Use IF EXISTS for DROP statements
#   --no-owner        : Skip ownership restoration (avoids UID mismatch)
#   --no-acl          : Skip privilege restoration
#   --format=custom   : Custom format for pg_restore compatibility (not used with gzip)
if [ "${PGDUMP_PATH}" = "docker exec -i tipsharks_prod_db pg_dump" ] || [[ "${PGDUMP_PATH}" == docker* ]]; then
    # Docker-based execution
    ${PGDUMP_PATH} \
        --dbname="${DB_NAME}" \
        --username="${DB_USER}" \
        --clean \
        --if-exists \
        --no-owner \
        --no-acl \
        2>/dev/null \
    | gzip > "${BACKUP_FILE}"
else
    # Direct pg_dump execution
    ${PGDUMP_PATH} \
        --host="${DB_HOST}" \
        --port="${DB_PORT}" \
        --dbname="${DB_NAME}" \
        --username="${DB_USER}" \
        --clean \
        --if-exists \
        --no-owner \
        --no-acl \
        2>/dev/null \
    | gzip > "${BACKUP_FILE}"
fi

# ── Verify Backup ──────────────────────────────────────────────────────────

if [ ! -f "${BACKUP_FILE}" ]; then
    log "ERROR" "Backup file was not created: ${BACKUP_FILE}"
    exit 1
fi

BACKUP_SIZE=$(du -h "${BACKUP_FILE}" | cut -f1)
log "INFO" "Backup completed successfully: ${BACKUP_FILE} (${BACKUP_SIZE})"

# Test integrity by reading the gzip file
if ! gzip -t "${BACKUP_FILE}" 2>/dev/null; then
    log "ERROR" "Backup integrity check failed: ${BACKUP_FILE} is corrupted"
    rm -f "${BACKUP_FILE}"
    exit 1
fi
log "INFO" "Backup integrity check passed"

# Update latest symlink
ln -sf "$(basename "${BACKUP_FILE}")" "${LATEST_LINK}"
log "INFO" "Updated latest backup symlink: ${LATEST_LINK}"

# ── Retention: Remove backups older than BACKUP_RETENTION days ────────────

log "INFO" "Cleaning backups older than ${BACKUP_RETENTION} days..."
DELETED_COUNT=0
while IFS= read -r -d '' old_file; do
    rm -f "${old_file}"
    log "INFO" "Deleted expired backup: ${old_file}"
    DELETED_COUNT=$((DELETED_COUNT + 1))
done < <(find "${BACKUP_DIR}" -maxdepth 1 -type f -name "${DB_NAME}_*.sql.gz" -mtime "+${BACKUP_RETENTION}" -print0)

if [ "${DELETED_COUNT}" -eq 0 ]; then
    log "INFO" "No expired backups to clean"
else
    log "INFO" "Cleaned ${DELETED_COUNT} expired backup(s)"
fi

# ── Summary ────────────────────────────────────────────────────────────────

unset PGPASSWORD

log "INFO" "Backup summary:"
log "INFO" "  Database:      ${DB_NAME}"
log "INFO" "  File:          ${BACKUP_FILE}"
log "INFO" "  Size:          ${BACKUP_SIZE}"
log "INFO" "  Retention:     ${BACKUP_RETENTION} days"
log "INFO" "  Status:        SUCCESS"
