#!/bin/bash
#
# SYSTEM PREPARATION FOR LARGE DATABASE RESTORES
# ===============================================
# Run as: root
#
# This script handles system-level preparation:
# - Swap creation
# - OOM killer protection
# - Kernel tuning
#
# Usage:
#   sudo ./prepare_system.sh           # Run diagnostics
#   sudo ./prepare_system.sh --fix     # Apply all fixes
#   sudo ./prepare_system.sh --swap    # Create swap only
#

set -euo pipefail

VERSION="1.0.0"

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'

log_info()  { echo -e "${BLUE}ℹ${NC} $1"; }
log_ok()    { echo -e "${GREEN}✓${NC} $1"; }
log_warn()  { echo -e "${YELLOW}⚠${NC} $1"; }
log_error() { echo -e "${RED}✗${NC} $1"; }

#==============================================================================
# CHECK ROOT
#==============================================================================
check_root() {
    if [ "$EUID" -ne 0 ]; then
        log_error "This script must be run as root"
        echo "  Run: sudo $0"
        exit 1
    fi
}

#==============================================================================
# DIAGNOSE
#==============================================================================
diagnose() {
    echo
    echo "╔══════════════════════════════════════════════════════════════════╗"
    echo "║         SYSTEM DIAGNOSIS FOR LARGE RESTORES                      ║"
    echo "╚══════════════════════════════════════════════════════════════════╝"
    echo
    
    # Memory
    echo -e "${CYAN}━━━ MEMORY ━━━${NC}"
    free -h
    echo
    
    # Swap
    echo -e "${CYAN}━━━ SWAP ━━━${NC}"
    swapon --show 2>/dev/null || echo "  No swap configured!"
    echo
    
    # Disk
    echo -e "${CYAN}━━━ DISK SPACE ━━━${NC}"
    df -h / /var/lib/pgsql 2>/dev/null || df -h /
    echo
    
    # OOM
    echo -e "${CYAN}━━━ RECENT OOM KILLS ━━━${NC}"
    dmesg 2>/dev/null | grep -i "out of memory\|oom\|killed process" | tail -5 || echo "  None found"
    echo
    
    # PostgreSQL OOM protection
    echo -e "${CYAN}━━━ POSTGRESQL OOM PROTECTION ━━━${NC}"
    local pg_pid
    pg_pid=$(pgrep -x postgres 2>/dev/null | head -1 || echo "")
    if [ -n "$pg_pid" ] && [ -f "/proc/$pg_pid/oom_score_adj" ]; then
        local score=$(cat "/proc/$pg_pid/oom_score_adj")
        if [ "$score" = "-1000" ]; then
            log_ok "PostgreSQL protected (oom_score_adj = -1000)"
        else
            log_warn "PostgreSQL NOT protected (oom_score_adj = $score)"
        fi
    else
        log_warn "Cannot check PostgreSQL OOM status"
    fi
    echo
    
    # Summary
    echo -e "${CYAN}━━━ RECOMMENDATIONS ━━━${NC}"
    local swap_gb=$(free -g | awk '/^Swap:/ {print $2}')
    local avail_gb=$(df -BG / | tail -1 | awk '{print $4}' | tr -d 'G')
    
    if [ "${swap_gb:-0}" -lt 4 ]; then
        log_warn "Create swap: sudo $0 --swap"
    fi
    
    if [ -n "$pg_pid" ]; then
        local score=$(cat "/proc/$pg_pid/oom_score_adj" 2>/dev/null || echo "0")
        if [ "$score" != "-1000" ]; then
            log_warn "Enable OOM protection: sudo $0 --oom-protect"
        fi
    fi
    
    echo
    echo "To apply all fixes: sudo $0 --fix"
    echo
}

#==============================================================================
# CREATE SWAP
#==============================================================================
create_swap() {
    local SWAP_FILE="/swapfile_dbbackup"
    
    echo -e "${CYAN}━━━ SWAP CHECK ━━━${NC}"
    
    # Check existing swap
    local current_swap_gb=$(free -g | awk '/^Swap:/ {print $2}')
    current_swap_gb=${current_swap_gb:-0}
    
    echo "  Current swap: ${current_swap_gb}GB"
    swapon --show 2>/dev/null || true
    echo
    
    # If already have 4GB+ swap, we're good
    if [ "$current_swap_gb" -ge 4 ]; then
        log_ok "Sufficient swap already configured (${current_swap_gb}GB)"
        return 0
    fi
    
    # Check if our swap file already exists
    if [ -f "$SWAP_FILE" ]; then
        if swapon --show | grep -q "$SWAP_FILE"; then
            log_ok "Our swap file already active: $SWAP_FILE"
            return 0
        else
            # File exists but not active - activate it
            log_info "Activating existing swap file..."
            swapon "$SWAP_FILE" 2>/dev/null && log_ok "Swap activated" && return 0
        fi
    fi
    
    # Need to create swap
    local avail_gb=$(df -BG / | tail -1 | awk '{print $4}' | tr -d 'G')
    
    # Calculate how much MORE swap we need (target: 8GB total)
    local target_swap=8
    local need_swap=$((target_swap - current_swap_gb))
    
    if [ "$need_swap" -le 0 ]; then
        log_ok "Swap is sufficient"
        return 0
    fi
    
    # Auto-detect size based on available disk AND what we need
    local size
    if [ "$avail_gb" -ge 40 ] && [ "$need_swap" -ge 16 ]; then
        size="32G"
    elif [ "$avail_gb" -ge 20 ] && [ "$need_swap" -ge 8 ]; then
        size="16G"
    elif [ "$avail_gb" -ge 12 ] && [ "$need_swap" -ge 4 ]; then
        size="8G"
    elif [ "$avail_gb" -ge 6 ]; then
        size="4G"
    elif [ "$avail_gb" -ge 4 ]; then
        size="3G"
    elif [ "$avail_gb" -ge 3 ]; then
        size="2G"
    elif [ "$avail_gb" -ge 2 ]; then
        size="1G"
    else
        log_error "Not enough disk space (only ${avail_gb}GB available)"
        return 1
    fi
    
    log_info "Creating additional swap: $size (current: ${current_swap_gb}GB, disk: ${avail_gb}GB)"
    
    echo "  Creating ${size} swap file..."
    
    if command -v fallocate &>/dev/null; then
        fallocate -l "$size" "$SWAP_FILE"
    else
        local size_mb=$((${size//[!0-9]/} * 1024))
        dd if=/dev/zero of="$SWAP_FILE" bs=1M count="$size_mb" status=progress
    fi
    
    chmod 600 "$SWAP_FILE"
    mkswap "$SWAP_FILE"
    swapon "$SWAP_FILE"
    
    # Persist
    if ! grep -q "$SWAP_FILE" /etc/fstab 2>/dev/null; then
        echo "$SWAP_FILE none swap sw 0 0" >> /etc/fstab
        log_ok "Added to /etc/fstab"
    fi
    
    # Show result
    local new_swap_gb=$(free -g | awk '/^Swap:/ {print $2}')
    log_ok "Swap now: ${new_swap_gb}GB (was ${current_swap_gb}GB)"
    swapon --show
}

#==============================================================================
# OOM PROTECTION
#==============================================================================
enable_oom_protection() {
    echo -e "${CYAN}━━━ ENABLING OOM PROTECTION ━━━${NC}"
    
    # Protect PostgreSQL
    local pg_pids=$(pgrep -x postgres 2>/dev/null || echo "")
    
    if [ -z "$pg_pids" ]; then
        log_warn "PostgreSQL not running"
    else
        for pid in $pg_pids; do
            if [ -f "/proc/$pid/oom_score_adj" ]; then
                echo -1000 > "/proc/$pid/oom_score_adj" 2>/dev/null || true
            fi
        done
        log_ok "PostgreSQL processes protected"
    fi
    
    # Kernel tuning
    sysctl -w vm.overcommit_memory=2 2>/dev/null && log_ok "vm.overcommit_memory = 2"
    sysctl -w vm.overcommit_ratio=90 2>/dev/null && log_ok "vm.overcommit_ratio = 90"
    
    # Persist
    if ! grep -q "vm.overcommit_memory" /etc/sysctl.conf 2>/dev/null; then
        echo "vm.overcommit_memory = 2" >> /etc/sysctl.conf
        echo "vm.overcommit_ratio = 90" >> /etc/sysctl.conf
        log_ok "Settings persisted to /etc/sysctl.conf"
    fi
}

#==============================================================================
# APPLY ALL FIXES
#==============================================================================
apply_all() {
    echo
    echo "╔══════════════════════════════════════════════════════════════════╗"
    echo "║         APPLYING SYSTEM FIXES                                    ║"
    echo "╚══════════════════════════════════════════════════════════════════╝"
    echo
    
    create_swap
    echo
    enable_oom_protection
    
    echo
    log_ok "System preparation complete!"
    echo
    echo "  Next: Run PostgreSQL tuning as postgres user:"
    echo "    su - postgres -c './prepare_postgres.sh --fix'"
    echo
}

#==============================================================================
# HELP
#==============================================================================
show_help() {
    echo "SYSTEM PREPARATION v$VERSION"
    echo
    echo "Usage: sudo $0 [OPTION]"
    echo
    echo "Options:"
    echo "  (none)        Run diagnostics"
    echo "  --fix         Apply all fixes"
    echo "  --swap        Create swap file only"
    echo "  --oom-protect Enable OOM protection only"
    echo "  --help        Show this help"
    echo
}

#==============================================================================
# MAIN
#==============================================================================
main() {
    check_root
    
    case "${1:-}" in
        --help|-h) show_help ;;
        --fix) apply_all ;;
        --swap) create_swap ;;
        --oom-protect) enable_oom_protection ;;
        "") diagnose ;;
        *) log_error "Unknown option: $1"; show_help; exit 1 ;;
    esac
}

main "$@"
