#!/usr/bin/env bash
#
# Daily database backup for AMSOIL DLP
#
# Dumps both databases and config files, copies them to remote backup server
# via rclone, and prunes local backups older than 30 days. Sends email alerts
# on failure via the Resend API.
#
# Usage:
#   bash /home/amsoildlp/scripts/backup-to-remote.sh
#
# Cron (daily at 2:00 AM EST — server is America/Toronto):
#   0 2 * * * bash /home/amsoildlp/scripts/backup-to-remote.sh
#
# Prerequisites:
#   - rclone configured with an SFTP remote named "backupserver"
#   - SSH key access from this server to the backup server
#   - PostgreSQL accessible via sudo -u postgres
#   - RESEND_API_KEY in production .env.local (for failure alerts)
#
# See docs/DATABASE_BACKUP_RESTORE.md for full setup instructions.

set -euo pipefail

# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
BACKUP_ROOT="/backups/amsoil-dlp/daily"
LOG_FILE="/var/log/amsoil-backup.log"
RETENTION_DAYS=30
RCLONE_REMOTE="backupserver:backups/amsoil-dlp/daily"
TODAY=$(date +%Y-%m-%d)
BACKUP_DIR="${BACKUP_ROOT}/${TODAY}"

# App directories on the production server
PRODUCTION_APP_DIR="/home/amsoildlp/public_html/amsoil.aimclear.com"
STAGING_APP_DIR="/home/amsoildlp/public_html/aimclear.biz"

# Databases to back up
PRODUCTION_DB="amsoil_dlp_production"
STAGING_DB="amsoil_dlp_staging"

# Alert configuration
ALERT_TO="team-amsoil-dlp@aimclear.com"
ALERT_FROM="noreply@amsoil.aimclear.com"
HOSTNAME=$(hostname)

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

# ---------------------------------------------------------------------------
# Email alerts via Resend API
# ---------------------------------------------------------------------------
send_alert() {
  local subject="$1"
  local body="$2"

  # Load RESEND_API_KEY from production .env.local
  local api_key=""
  if [[ -f "${PRODUCTION_APP_DIR}/.env.local" ]]; then
    api_key=$(grep -oP '^RESEND_API_KEY=["'"'"']?\K[^"'"'"'\s]+' "${PRODUCTION_APP_DIR}/.env.local" 2>/dev/null || true)
  fi

  if [[ -z "$api_key" ]]; then
    log "WARNING: RESEND_API_KEY not found, skipping email alert"
    return 0
  fi

  # Build JSON payload using jq to handle escaping
  local json
  json=$(jq -n \
    --arg from "$ALERT_FROM" \
    --arg to "$ALERT_TO" \
    --arg subject "$subject" \
    --arg text "$body" \
    '{from: $from, to: [$to], subject: $subject, text: $text}')

  local http_code
  http_code=$(curl -s -o /dev/null -w "%{http_code}" \
    -X POST "https://api.resend.com/emails" \
    -H "Authorization: Bearer ${api_key}" \
    -H "Content-Type: application/json" \
    -d "$json") || true

  if [[ "$http_code" == "200" ]]; then
    log "Alert email sent: ${subject}"
  else
    log "WARNING: Alert email failed (HTTP ${http_code}): ${subject}"
  fi
}

# ---------------------------------------------------------------------------
# Error handling
# ---------------------------------------------------------------------------
on_error() {
  trap - ERR  # Prevent recursive trapping if send_alert fails
  local line="$1"
  log "ERROR: Backup failed at line ${line}"
  # Clean up partial temp files
  rm -f "/tmp/${PRODUCTION_DB}.dump.tmp" "/tmp/${STAGING_DB}.dump.tmp"
  local log_tail
  log_tail=$(tail -20 "$LOG_FILE")
  send_alert \
    "[BACKUP FAILED] AMSOIL DLP - ${TODAY}" \
    "Backup failed on ${HOSTNAME} at line ${line}.

Check log: ${LOG_FILE}

Last 20 lines:
${log_tail}" || true
  exit 1
}
trap 'on_error $LINENO' ERR

# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
# Prevent concurrent runs
LOCK_FILE="/var/lock/amsoil-backup.lock"
exec 9>"$LOCK_FILE"
if ! flock -n 9; then
  echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: Another backup is already running" | tee -a "$LOG_FILE"
  exit 1
fi

# Dependency checks
for cmd in pg_dump rclone jq curl; do
  if ! command -v "$cmd" &>/dev/null; then
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] ERROR: Required command '$cmd' not found" | tee -a "$LOG_FILE"
    exit 1
  fi
done

touch "$LOG_FILE"
chmod 640 "$LOG_FILE"
log "=== Starting daily backup ==="

mkdir -p "$BACKUP_DIR"
chmod 700 "$BACKUP_DIR"
log "Backup directory: $BACKUP_DIR"

# --- Database dumps --------------------------------------------------------
# pg_dump runs as postgres user, which can't write to the root-owned backup dir.
# Dump to /tmp first, then move into place.
log "Dumping ${PRODUCTION_DB}..."
sudo -u postgres pg_dump -Fc "$PRODUCTION_DB" -f "/tmp/${PRODUCTION_DB}.dump.tmp"
mv "/tmp/${PRODUCTION_DB}.dump.tmp" "${BACKUP_DIR}/${PRODUCTION_DB}.dump"
chmod 600 "${BACKUP_DIR}/${PRODUCTION_DB}.dump"
log "  $(du -h "${BACKUP_DIR}/${PRODUCTION_DB}.dump" | awk '{print $1}') written"

log "Dumping ${STAGING_DB}..."
sudo -u postgres pg_dump -Fc "$STAGING_DB" -f "/tmp/${STAGING_DB}.dump.tmp"
mv "/tmp/${STAGING_DB}.dump.tmp" "${BACKUP_DIR}/${STAGING_DB}.dump"
chmod 600 "${BACKUP_DIR}/${STAGING_DB}.dump"
log "  $(du -h "${BACKUP_DIR}/${STAGING_DB}.dump" | awk '{print $1}') written"

# --- Environment and config files -------------------------------------------
log "Copying config files..."

for env in production staging; do
  if [[ "$env" == "production" ]]; then
    APP_DIR="$PRODUCTION_APP_DIR"
  else
    APP_DIR="$STAGING_APP_DIR"
  fi

  for file in .env.local ecosystem.config.js; do
    # Strip leading dot to avoid double-dot filenames (e.g. production.env.local not production..env.local)
    dest_name="${env}.${file#.}"
    if [[ -f "${APP_DIR}/${file}" ]]; then
      cp "${APP_DIR}/${file}" "${BACKUP_DIR}/${dest_name}"
      chmod 600 "${BACKUP_DIR}/${dest_name}"
      log "  Copied ${env} ${file}"
    else
      log "  WARNING: ${APP_DIR}/${file} not found, skipping"
    fi
  done
done

# --- Prune old local backups -----------------------------------------------
log "Pruning local backups older than ${RETENTION_DAYS} days..."
CUTOFF=$(date -d "-${RETENTION_DAYS} days" +%Y-%m-%d)  # GNU date required
PRUNED=0
for dir in "${BACKUP_ROOT}"/????-??-??; do
  [[ -d "$dir" ]] || continue
  dirname=$(basename "$dir")
  if [[ "$dirname" < "$CUTOFF" ]]; then
    rm -rf "$dir"
    log "  Removed $dir"
    PRUNED=$((PRUNED + 1))
  fi
done
if [[ "$PRUNED" -eq 0 ]]; then
  log "  Nothing to prune"
fi

# --- Copy to remote backup server ------------------------------------------
# Uses rclone copy (not sync) so the remote accumulates backups independently.
# Remote retention is managed separately on the backup server.
log "Copying today's backup to remote server..."
rclone copy "$BACKUP_DIR" "${RCLONE_REMOTE}/${TODAY}" --log-level INFO 2>&1 | tee -a "$LOG_FILE"
rclone check "$BACKUP_DIR" "${RCLONE_REMOTE}/${TODAY}" 2>&1 | tee -a "$LOG_FILE"
log "Remote copy verified"

# --- Summary ---------------------------------------------------------------
DUMP_COUNT=$(find "$BACKUP_DIR" -name "*.dump" | wc -l)
TOTAL_SIZE=$(du -sh "$BACKUP_DIR" | awk '{print $1}')
BACKUP_COUNT=$(find "$BACKUP_ROOT" -maxdepth 1 -type d -not -path "$BACKUP_ROOT" | wc -l)

log "=== Backup complete ==="
log "  Today: ${DUMP_COUNT} dumps, ${TOTAL_SIZE} total"
log "  Retained: ${BACKUP_COUNT} days of backups"

# --- Success alert (daily confirmation) ------------------------------------
send_alert \
  "[BACKUP OK] AMSOIL DLP - ${TODAY}" \
  "Daily backup completed successfully on ${HOSTNAME}.

${DUMP_COUNT} dumps, ${TOTAL_SIZE} total.
${BACKUP_COUNT} days of backups retained.
Remote copy verified." || log "WARNING: Could not send success alert email"
