#!/bin/bash
# ============================================================
# Club Palestino CRM v2.0 — Sistema de Backup
# /opt/clubpalestino/backup/backup.sh
#
# USO:
#   ./backup.sh full        → Backup completo (diario 3 AM)
#   ./backup.sh incremental → Solo cambios (cada hora)
#   ./backup.sh restore     → Restauración interactiva
#   ./backup.sh verify      → Verificar último backup
#
# CRON (agregar con: crontab -e):
#   0  3 * * *   /opt/clubpalestino/backup/backup.sh full
#   0  * * * *   /opt/clubpalestino/backup/backup.sh incremental
#   0  6 * * 1   /opt/clubpalestino/backup/backup.sh verify
# ============================================================

set -euo pipefail

# ── CONFIGURACIÓN ─────────────────────────────────────────────
DB_HOST="localhost"
DB_USER="backup_user"          # Usuario MySQL solo lectura para backups
DB_PASS="CAMBIAR_PASSWORD"
DB_NAME="clubpalestino"

WWW_ROOT="/var/www/clubpalestino"
BACKUP_DIR="/opt/clubpalestino/backups"
LOG_FILE="/var/log/clubpalestino/backup.log"
ENCRYPT_KEY="/opt/clubpalestino/backup/.backup_key"  # Clave GPG o AES

# Retención
KEEP_FULL=30          # días de backups full
KEEP_INCREMENTAL=7    # días de backups incrementales

# Destino remoto (geo-réplica Hostinger)
REMOTE_USER="backup"
REMOTE_HOST="backup.hostinger-server.com"
REMOTE_DIR="/backups/clubpalestino"
REMOTE_SSH_KEY="/root/.ssh/backup_key"

# Notificación
NOTIFY_EMAIL="admin@inteliworks.cl"
NOTIFY_WEBHOOK=""  # URL webhook WhatsApp para alertas críticas

# ── HELPERS ───────────────────────────────────────────────────
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"; }
error() { log "ERROR: $*"; notify_error "$*"; exit 1; }
notify_error() {
    echo "⛔ BACKUP FALLIDO — Club Palestino CRM
Fecha: $(date)
Error: $1
Servidor: $(hostname)" | mail -s "⛔ ALERTA BACKUP CRM" "$NOTIFY_EMAIL" 2>/dev/null || true
    if [ -n "$NOTIFY_WEBHOOK" ]; then
        curl -s -X POST "$NOTIFY_WEBHOOK" -d "{\"message\":\"⛔ Backup fallido CRM: $1\"}" 2>/dev/null || true
    fi
}
notify_ok() {
    echo "✅ Backup completado — Club Palestino CRM
Tipo: $1
Fecha: $(date)
Tamaño: $2
Destino: $3" | mail -s "✅ Backup OK CRM" "$NOTIFY_EMAIL" 2>/dev/null || true
}

mkdir -p "$BACKUP_DIR"/{full,incremental,logs,verify}
mkdir -p /var/log/clubpalestino

# ── BACKUP FULL ───────────────────────────────────────────────
backup_full() {
    local DATE=$(date '+%Y%m%d_%H%M%S')
    local DIR="$BACKUP_DIR/full/$DATE"
    mkdir -p "$DIR"
    log "=== INICIANDO BACKUP FULL ($DATE) ==="

    # 1. Dump completo de BD
    log "Dumping base de datos..."
    mysqldump \
        -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" \
        --single-transaction \
        --routines \
        --triggers \
        --events \
        --hex-blob \
        --master-data=2 \
        --flush-logs \
        "$DB_NAME" | gzip -9 > "$DIR/db_full.sql.gz" \
        || error "mysqldump falló"
    log "BD: $(du -sh $DIR/db_full.sql.gz | cut -f1)"

    # 2. Archivos del sitio web
    log "Comprimiendo archivos web..."
    tar -czf "$DIR/www.tar.gz" \
        --exclude="$WWW_ROOT/storage/logs/*" \
        --exclude="$WWW_ROOT/storage/cache/*" \
        "$WWW_ROOT" 2>/dev/null \
        || error "tar www falló"
    log "WWW: $(du -sh $DIR/www.tar.gz | cut -f1)"

    # 3. Archivos DTE (XMLs firmados — MUY importantes)
    if [ -d "/var/www/dte" ]; then
        log "Comprimiendo DTEs..."
        tar -czf "$DIR/dte.tar.gz" /var/www/dte/ 2>/dev/null || log "WARN: No se encontró directorio DTE"
    fi

    # 4. Certificados SII (cifrados)
    if [ -d "/opt/clubpalestino/certs" ]; then
        log "Cifrando certificados SII..."
        tar -czf - /opt/clubpalestino/certs/ | \
            openssl enc -aes-256-cbc -pbkdf2 -pass file:"$ENCRYPT_KEY" \
            > "$DIR/certs.tar.gz.enc" 2>/dev/null \
            || log "WARN: Error cifrando certificados"
    fi

    # 5. Cifrar backup BD
    log "Cifrando backup BD..."
    openssl enc -aes-256-cbc -pbkdf2 -in "$DIR/db_full.sql.gz" \
        -pass file:"$ENCRYPT_KEY" -out "$DIR/db_full.sql.gz.enc" \
        && rm "$DIR/db_full.sql.gz" \
        || log "WARN: No se cifró el backup (key no encontrada)"

    # 6. Crear checksum
    log "Generando checksums..."
    sha256sum "$DIR"/* > "$DIR/CHECKSUMS.sha256"

    # 7. Crear archivo de metadatos
    cat > "$DIR/METADATA.json" << METAEOF
{
  "timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
  "tipo": "full",
  "db_name": "$DB_NAME",
  "hostname": "$(hostname)",
  "mysql_version": "$(mysql --version | awk '{print $5}' | tr -d ',')",
  "www_root": "$WWW_ROOT",
  "archivos": $(ls "$DIR" | wc -l)
}
METAEOF

    local SIZE=$(du -sh "$DIR" | cut -f1)
    log "Backup full completado: $DIR ($SIZE)"

    # 8. Sincronizar al servidor remoto
    sync_remote "$DIR" "full/$DATE"

    # 9. Limpiar backups antiguos
    cleanup_old full $KEEP_FULL

    notify_ok "FULL" "$SIZE" "$DIR"
    log "=== BACKUP FULL COMPLETADO ==="
}

# ── BACKUP INCREMENTAL (binlog MySQL) ─────────────────────────
backup_incremental() {
    local DATE=$(date '+%Y%m%d_%H%M%S')
    local DIR="$BACKUP_DIR/incremental/$DATE"
    mkdir -p "$DIR"
    log "=== BACKUP INCREMENTAL ($DATE) ==="

    # Copiar binary logs desde el último flush
    mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" \
        -e "FLUSH BINARY LOGS;" 2>/dev/null || log "WARN: No se pudo hacer flush de binlogs"

    # Buscar binlogs nuevos desde el último backup incremental
    local LAST_BINLOG=$(cat "$BACKUP_DIR/incremental/.last_binlog" 2>/dev/null || echo "mysql-bin.000001")

    ls /var/lib/mysql/mysql-bin.* 2>/dev/null | while read binlog; do
        local NAME=$(basename "$binlog")
        if [[ "$NAME" > "$LAST_BINLOG" ]]; then
            cp "$binlog" "$DIR/$NAME" && log "Copiado: $NAME"
        fi
    done

    # Guardar referencia del último binlog
    ls /var/lib/mysql/mysql-bin.* 2>/dev/null | sort | tail -1 | xargs basename > "$BACKUP_DIR/incremental/.last_binlog"

    # Backup de archivos modificados en la última hora
    find "$WWW_ROOT" -newer "$BACKUP_DIR/.last_full_ts" -type f 2>/dev/null | \
        tar -czf "$DIR/files_changed.tar.gz" -T - 2>/dev/null || true

    touch "$BACKUP_DIR/.last_incr_ts"
    local SIZE=$(du -sh "$DIR" 2>/dev/null | cut -f1)
    log "Incremental completado: $DIR (${SIZE:-0B})"

    sync_remote "$DIR" "incremental/$DATE"
    cleanup_old incremental $KEEP_INCREMENTAL
}

# ── SINCRONIZACIÓN REMOTA (Hostinger) ─────────────────────────
sync_remote() {
    local SRC="$1"
    local DEST_PATH="$2"
    log "Sincronizando a servidor remoto..."
    rsync -avz --delete \
        -e "ssh -i $REMOTE_SSH_KEY -o StrictHostKeyChecking=no -o ConnectTimeout=30" \
        "$SRC/" \
        "$REMOTE_USER@$REMOTE_HOST:$REMOTE_DIR/$DEST_PATH/" \
        2>/dev/null && log "Sincronización remota: OK" \
        || log "WARN: Sincronización remota falló (seguirá en próximo intento)"
}

# ── VERIFICACIÓN DE BACKUP ────────────────────────────────────
verify_backup() {
    log "=== VERIFICANDO ÚLTIMO BACKUP FULL ==="
    local LAST_FULL=$(ls -dt "$BACKUP_DIR/full"/*/ 2>/dev/null | head -1)

    if [ -z "$LAST_FULL" ]; then
        error "No se encontraron backups full para verificar"
    fi

    log "Verificando: $LAST_FULL"

    # 1. Verificar checksums
    if [ -f "$LAST_FULL/CHECKSUMS.sha256" ]; then
        cd "$LAST_FULL"
        sha256sum -c CHECKSUMS.sha256 2>/dev/null && log "✅ Checksums OK" || error "❌ Checksums fallaron"
    fi

    # 2. Probar restauración en BD temporal
    if [ -f "$LAST_FULL/db_full.sql.gz" ] || [ -f "$LAST_FULL/db_full.sql.gz.enc" ]; then
        local TEST_DB="cp_test_restore_$(date +%s)"
        log "Probando restauración en BD temporal: $TEST_DB"

        # Descifrar si está cifrado
        if [ -f "$LAST_FULL/db_full.sql.gz.enc" ] && [ -f "$ENCRYPT_KEY" ]; then
            openssl enc -d -aes-256-cbc -pbkdf2 \
                -in "$LAST_FULL/db_full.sql.gz.enc" \
                -pass file:"$ENCRYPT_KEY" | \
            gunzip | \
            mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" \
                -e "CREATE DATABASE IF NOT EXISTS $TEST_DB; USE $TEST_DB; SOURCE /dev/stdin" \
                2>/dev/null && log "✅ Restauración de prueba OK" \
                || log "WARN: No se pudo restaurar en BD temporal (verificar manualmente)"

            # Limpiar BD de prueba
            mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" \
                -e "DROP DATABASE IF EXISTS $TEST_DB" 2>/dev/null || true
        fi
    fi

    log "Fecha del backup: $(cat $LAST_FULL/METADATA.json 2>/dev/null | grep timestamp | cut -d'"' -f4)"
    log "=== VERIFICACIÓN COMPLETADA ==="
}

# ── RESTAURACIÓN INTERACTIVA ──────────────────────────────────
restore_backup() {
    echo ""
    echo "╔══════════════════════════════════════════╗"
    echo "║  RESTAURACIÓN — Club Palestino CRM       ║"
    echo "║  ⚠️  ESTO SOBREESCRIBIRÁ LA BD ACTUAL  ⚠️  ║"
    echo "╚══════════════════════════════════════════╝"
    echo ""

    # Listar backups disponibles
    echo "Backups full disponibles:"
    ls -dt "$BACKUP_DIR/full"/*/ 2>/dev/null | head -10 | while read d; do
        local META="$d/METADATA.json"
        local TS=$(cat "$META" 2>/dev/null | grep timestamp | cut -d'"' -f4 || echo "desconocido")
        local SIZE=$(du -sh "$d" 2>/dev/null | cut -f1)
        echo "  $(basename $d) — $TS — $SIZE"
    done

    echo ""
    read -p "¿Fecha del backup a restaurar (YYYYMMDD_HHMMSS)? " BACKUP_DATE
    local RESTORE_DIR="$BACKUP_DIR/full/$BACKUP_DATE"

    if [ ! -d "$RESTORE_DIR" ]; then
        error "Backup no encontrado: $RESTORE_DIR"
    fi

    echo ""
    echo "BACKUP A RESTAURAR: $RESTORE_DIR"
    read -p "¿Confirmar restauración? Escriba 'SI' para continuar: " CONFIRM
    if [ "$CONFIRM" != "SI" ]; then
        echo "Restauración cancelada."; exit 0
    fi

    log "=== INICIANDO RESTAURACIÓN: $BACKUP_DATE ==="

    # Backup de emergencia de la BD actual antes de restaurar
    log "Haciendo backup de emergencia de BD actual..."
    mysqldump -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" \
        --single-transaction "$DB_NAME" | gzip > \
        "$BACKUP_DIR/emergency_$(date +%Y%m%d_%H%M%S).sql.gz" 2>/dev/null || true

    # Restaurar BD
    log "Restaurando base de datos..."
    if [ -f "$RESTORE_DIR/db_full.sql.gz.enc" ] && [ -f "$ENCRYPT_KEY" ]; then
        openssl enc -d -aes-256-cbc -pbkdf2 \
            -in "$RESTORE_DIR/db_full.sql.gz.enc" \
            -pass file:"$ENCRYPT_KEY" | \
        gunzip | \
        mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" \
            && log "✅ BD restaurada" || error "❌ Error restaurando BD"
    elif [ -f "$RESTORE_DIR/db_full.sql.gz" ]; then
        gunzip -c "$RESTORE_DIR/db_full.sql.gz" | \
        mysql -h "$DB_HOST" -u "$DB_USER" -p"$DB_PASS" "$DB_NAME" \
            && log "✅ BD restaurada" || error "❌ Error restaurando BD"
    fi

    # Restaurar archivos web
    if [ -f "$RESTORE_DIR/www.tar.gz" ]; then
        log "Restaurando archivos web..."
        tar -xzf "$RESTORE_DIR/www.tar.gz" -C / \
            && log "✅ Archivos web restaurados" \
            || log "WARN: Error restaurando archivos web"
    fi

    log "=== RESTAURACIÓN COMPLETADA ==="
    echo ""
    echo "✅ Restauración completada. Verifica que el sistema funcione correctamente."
    echo "   Backup de emergencia guardado en: $BACKUP_DIR/emergency_*.sql.gz"
}

# ── LIMPIAR BACKUPS ANTIGUOS ──────────────────────────────────
cleanup_old() {
    local TYPE="$1"
    local DAYS="$2"
    local COUNT=$(find "$BACKUP_DIR/$TYPE" -maxdepth 1 -type d -mtime +$DAYS 2>/dev/null | wc -l)
    if [ "$COUNT" -gt 0 ]; then
        find "$BACKUP_DIR/$TYPE" -maxdepth 1 -type d -mtime +$DAYS -exec rm -rf {} + 2>/dev/null
        log "Limpiados $COUNT backups $TYPE con más de $DAYS días"
    fi
}

# ── GENERAR CLAVE DE CIFRADO ──────────────────────────────────
generate_key() {
    if [ -f "$ENCRYPT_KEY" ]; then
        echo "La clave ya existe: $ENCRYPT_KEY"
        return
    fi
    mkdir -p "$(dirname $ENCRYPT_KEY)"
    openssl rand -base64 32 > "$ENCRYPT_KEY"
    chmod 600 "$ENCRYPT_KEY"
    echo "✅ Clave generada: $ENCRYPT_KEY"
    echo "⚠️  GUARDA ESTA CLAVE EN UN LUGAR SEGURO FUERA DEL SERVIDOR:"
    cat "$ENCRYPT_KEY"
}

# ── MAIN ──────────────────────────────────────────────────────
case "${1:-help}" in
    full)          backup_full        ;;
    incremental)   backup_incremental ;;
    restore)       restore_backup     ;;
    verify)        verify_backup      ;;
    genkey)        generate_key       ;;
    *)
        echo "Uso: $0 {full|incremental|restore|verify|genkey}"
        echo ""
        echo "  full         Backup completo (diario)"
        echo "  incremental  Backup incremental (por hora)"
        echo "  restore      Restauración interactiva"
        echo "  verify       Verificar integridad del último backup"
        echo "  genkey       Generar clave de cifrado"
        ;;
esac
