#!/bin/bash

################################################################################
# Live Network Security Monitor - ENHANCED with Intelligence
################################################################################
# Purpose: Real-time monitoring with bot intelligence and threat scoring
# Version: 2.0 - Intelligence Mode
# Features:
# - Bot classification using learned signatures
# - IP reputation DB integration
# - Real-time threat scoring (0-100)
# - Attack vector detection
# - Quick action blocking system
# - Ban tracking and history
################################################################################

# Get script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
source "$SCRIPT_DIR/lib/common-functions.sh"
source "$SCRIPT_DIR/lib/system-detect.sh"
source "$SCRIPT_DIR/lib/ip-reputation.sh"
source "$SCRIPT_DIR/lib/bot-signatures.sh"
source "$SCRIPT_DIR/lib/attack-patterns.sh"
source "$SCRIPT_DIR/lib/threat-intelligence.sh"

# Enhanced attack detection (ET Open signatures)
source "$SCRIPT_DIR/lib/attack-signatures.sh" 2>/dev/null || true
source "$SCRIPT_DIR/lib/http-attack-analyzer.sh" 2>/dev/null || true
source "$SCRIPT_DIR/lib/rate-anomaly-detector.sh" 2>/dev/null || true

# Require root
if [ "$EUID" -ne 0 ]; then
    print_error "This script must be run as root"
    exit 1
fi

# Color definitions for threat levels
CRITICAL_COLOR='\033[1;41;97m'  # White on Red background
HIGH_COLOR='\033[1;31m'          # Bold Red
MEDIUM_COLOR='\033[1;33m'        # Bold Yellow
LOW_COLOR='\033[0;36m'           # Cyan
SAFE_COLOR='\033[0;32m'          # Green
INFO_COLOR='\033[0;37m'          # White
BOLD='\033[1m'                   # Bold text
NC='\033[0m'

# Configuration
REFRESH_INTERVAL=2  # Seconds between dashboard refreshes
MAX_DISPLAY_LINES=20
THREAT_THRESHOLD_CRITICAL=80
THREAT_THRESHOLD_HIGH=60
THREAT_THRESHOLD_MEDIUM=40

# Display mode (compact by default for small terminals)
COMPACT_MODE=1
TERMINAL_HEIGHT=$(tput lines 2>/dev/null || echo "24")

# Temporary files for tracking
TEMP_DIR="/tmp/live-monitor-$$"
SNAPSHOT_DIR="/tmp/server-toolkit-live-monitor"
mkdir -p "$TEMP_DIR" "$SNAPSHOT_DIR" 2>/dev/null
touch "$TEMP_DIR/recent_events"
touch "$TEMP_DIR/ip_data"
echo "0" > "$TEMP_DIR/event_counter"
echo "0" > "$TEMP_DIR/total_blocks"

# IPset configuration
IPSET_NAME=""
IPSET_AVAILABLE=0
IPSET_SUPPORTS_TIMEOUT=0
IPSET_INIT_ERROR=""  # Store initialization error message

# Initialize IPset for fast blocking (if available)
# PRIORITY: Always use CSF's chain_DENY if available - it's the standard CSF blocking ipset
if command -v ipset &>/dev/null; then
    # Check if CSF's chain_DENY IPset exists (use it regardless of timeout support)
    if ipset list chain_DENY &>/dev/null 2>&1; then
        # CSF ipset exists - use it for all blocking!
        IPSET_NAME="chain_DENY"
        IPSET_AVAILABLE=1

        # Check if it supports timeouts (nice-to-have, not required)
        if ipset list chain_DENY | grep -q "^Type:.*timeout"; then
            IPSET_SUPPORTS_TIMEOUT=1
            echo "✓ Using CSF IPset: chain_DENY (with timeout support)" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
        else
            IPSET_SUPPORTS_TIMEOUT=0
            echo "✓ Using CSF IPset: chain_DENY (without timeout - CSF manages cleanup)" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
        fi
    else
        # CSF ipset doesn't exist - only create our own as last resort
        echo "→ CSF chain_DENY ipset not found - creating temporary monitor ipset" >> "$TEMP_DIR/debug.log" 2>/dev/null || true

        IPSET_NAME="live_monitor_$$"

        # Capture detailed error output
        IPSET_CREATE_OUTPUT=$(ipset create "$IPSET_NAME" hash:ip timeout 3600 maxelem 65536 2>&1)
        IPSET_CREATE_EXIT=$?

        if [ "${IPSET_CREATE_EXIT:-1}" -eq 0 ]; then
            IPSET_AVAILABLE=1
            IPSET_SUPPORTS_TIMEOUT=1

            # Add iptables rule to block IPs in the set
            IPTABLES_OUTPUT=$(iptables -I INPUT -m set --match-set "$IPSET_NAME" src -j DROP 2>&1)
            IPTABLES_EXIT=$?

            if [ "${IPTABLES_EXIT:-1}" -ne 0 ]; then
                # iptables rule failed - clean up ipset and report error
                ipset destroy "$IPSET_NAME" 2>/dev/null
                IPSET_AVAILABLE=0
                IPSET_INIT_ERROR="iptables rule creation failed: $IPTABLES_OUTPUT"
                echo "✗ IPset created but iptables rule failed: $IPTABLES_OUTPUT" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
            else
                echo "✓ Temporary IPset initialized: $IPSET_NAME (fast blocking enabled)" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
            fi
        else
            # IPset creation failed - capture why
            IPSET_INIT_ERROR="ipset creation failed: $IPSET_CREATE_OUTPUT"
            echo "✗ IPset creation failed: $IPSET_CREATE_OUTPUT" >> "$TEMP_DIR/debug.log" 2>/dev/null || true

            # Check for common issues and provide helpful diagnostics
            if echo "$IPSET_CREATE_OUTPUT" | grep -qi "module"; then
                KERNEL_MODS=$(lsmod | grep -E "ip_set|xt_set" || echo "NOT LOADED")
                IPSET_INIT_ERROR="$IPSET_INIT_ERROR | Kernel modules: $KERNEL_MODS"
                echo "  → Kernel module issue detected. Loaded modules: $KERNEL_MODS" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
            fi

            if echo "$IPSET_CREATE_OUTPUT" | grep -qi "permission"; then
                IPSET_INIT_ERROR="$IPSET_INIT_ERROR | Permission denied (need root)"
                echo "  → Permission denied. Current user: $(whoami), EUID: $EUID" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
            fi
        fi
    fi
else
    # ipset command not found - provide diagnostic info
    IPSET_INIT_ERROR="ipset command not found in PATH"
    echo "✗ IPset not available - using CSF for blocking" >> "$TEMP_DIR/debug.log" 2>/dev/null || true

    # Check if ipset package is installed
    if command -v rpm &>/dev/null && rpm -q ipset &>/dev/null; then
        IPSET_INIT_ERROR="$IPSET_INIT_ERROR | Package installed but not in PATH"
        echo "  → ipset package IS installed but command not found" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
    elif command -v dpkg &>/dev/null && dpkg -l ipset 2>/dev/null | grep -q "^ii"; then
        IPSET_INIT_ERROR="$IPSET_INIT_ERROR | Package installed but not in PATH"
        echo "  → ipset package IS installed but command not found" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
    else
        IPSET_INIT_ERROR="$IPSET_INIT_ERROR | Package not installed"
        echo "  → ipset package NOT installed" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
    fi
fi

# Initialize blocked IPs cache immediately on startup
{
    # Get CSF temporary blocks - extract just the IP address
    if command -v csf &>/dev/null; then
        csf -t 2>/dev/null | awk '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/ {print $1}'
    fi

    # Get CSF permanent denies
    if [ -f /etc/csf/csf.deny ]; then
        awk '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/ {print $1}' /etc/csf/csf.deny 2>/dev/null
    fi

    # Get iptables DROP rules
    if command -v iptables &>/dev/null; then
        iptables -L INPUT -n -v 2>/dev/null | awk '/DROP/ && $8 ~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/ {print $8}'
    fi
} | sort -u > "$TEMP_DIR/blocked_ips_cache" 2>/dev/null

# Log cache initialization for debugging
if [ -f "$TEMP_DIR/blocked_ips_cache" ]; then
    CACHED_COUNT=$(wc -l < "$TEMP_DIR/blocked_ips_cache" 2>/dev/null || echo "0")
    echo "Initialized blocked IPs cache with $CACHED_COUNT IPs" >> "$TEMP_DIR/debug.log"
fi

# Cleanup function
cleanup() {
    echo ""
    echo "Stopping monitoring processes..."

    # Kill all child processes
    pkill -P $$ 2>/dev/null

    # Wait a moment for background jobs
    sleep 1

    # SAVE SNAPSHOT BEFORE DELETING TEMP FILES!
    echo "Saving IP reputation snapshot..."
    save_snapshot

    # Also save to IP reputation database for permanent tracking
    if [ ${#IP_DATA[@]} -gt 0 ]; then
        for ip in "${!IP_DATA[@]}"; do
            IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "${IP_DATA[$ip]}"
            # Update IP reputation database if score > 0
            if [ "$score" -gt 0 ] && type record_ip_data &>/dev/null; then
                record_ip_data "$ip" "$score" "$hits" "$attacks" "$ban_count" 2>/dev/null &
            fi
        done
        wait  # Wait for all database updates to complete
        echo "✓ Saved ${#IP_DATA[@]} IPs to reputation database"
    fi

    # Clean up IPset and iptables rule ONLY if we created them (not CSF's chain_DENY)
    if [ "$IPSET_AVAILABLE" -eq 1 ] && [ "$IPSET_NAME" != "chain_DENY" ]; then
        echo "Removing IPset firewall rules..."
        iptables -D INPUT -m set --match-set "$IPSET_NAME" src -j DROP 2>/dev/null
        ipset destroy "$IPSET_NAME" 2>/dev/null
        echo "✓ IPset cleaned up"
    fi

    # Clean up temp directory (AFTER saving snapshot)
    rm -rf "$TEMP_DIR" 2>/dev/null

    # Restore cursor
    command -v tput &>/dev/null && tput cnorm

    echo "✓ Cleanup complete - snapshot saved to $SNAPSHOT_DIR"
    exit 0
}

trap cleanup EXIT INT TERM

# Save current monitoring state to temp files (for persistence across sessions)
save_snapshot() {
    # Save IP_DATA associative array to PERMANENT storage (survives script exit)
    local snapshot_file="$SNAPSHOT_DIR/latest_snapshot.dat"
    local timestamp=$(date +%Y%m%d_%H%M%S)
    local timestamped_file="$SNAPSHOT_DIR/snapshot_${timestamp}.dat"

    # Write IP data to both current and timestamped snapshot
    {
        for ip in "${!IP_DATA[@]}"; do
            echo "IP_DATA[$ip]=${IP_DATA[$ip]}"
        done

        # Write attack type counters
        for attack in "${!ATTACK_TYPE_COUNTER[@]}"; do
            echo "ATTACK_TYPE_COUNTER[$attack]=${ATTACK_TYPE_COUNTER[$attack]}"
        done

        # Write totals
        echo "TOTAL_THREATS=$TOTAL_THREATS"
        echo "TOTAL_BLOCKS=$TOTAL_BLOCKS"
        echo "START_TIME=$START_TIME"
    } > "$snapshot_file" 2>/dev/null

    # Also save timestamped copy for history
    cp "$snapshot_file" "$timestamped_file" 2>/dev/null

    # Keep only last 10 snapshots to prevent disk bloat
    ls -t "$SNAPSHOT_DIR"/snapshot_*.dat 2>/dev/null | tail -n +11 | xargs rm -f 2>/dev/null
}

# BUG FIX #17: Load persistent threat data at startup to block pre-existing high-score IPs
load_snapshot() {
    # Restore IP_DATA from last saved snapshot (enables blocking of known threats on startup)
    local snapshot_file="$SNAPSHOT_DIR/latest_snapshot.dat"

    # Restore is optional (no snapshot on first run)
    if [ ! -f "$snapshot_file" ]; then
        return 0
    fi

    while IFS='=' read -r key value; do
        [ -z "$key" ] && continue

        case "$key" in
            IP_DATA\[*)
                # Extract IP from IP_DATA[IP] format
                local ip="${key#IP_DATA[}"
                ip="${ip%]}"

                # Only restore if score >= 50 (filter out noise, keep threats)
                # Format: score|hits|bot_type|attacks|ban_count|rep_score
                IFS='|' read -r score _ _ _ _ _ <<< "$value"

                # Restore high-threat IPs (score >= 50 for persistence across restarts)
                if [ "${score:-0}" -ge 50 ]; then
                    IP_DATA[$ip]="$value"
                fi
                ;;
            ATTACK_TYPE_COUNTER\[*)
                # Extract attack type from ATTACK_TYPE_COUNTER[TYPE] format
                local attack="${key#ATTACK_TYPE_COUNTER[}"
                attack="${attack%]}"
                ATTACK_TYPE_COUNTER[$attack]="$value"
                ;;
            TOTAL_THREATS)
                TOTAL_THREATS="$value"
                ;;
            TOTAL_BLOCKS)
                TOTAL_BLOCKS="$value"
                ;;
        esac
    done < "$snapshot_file"
}

# Statistics counters
declare -A IP_DATA  # Stores: IP -> score|hits|bot_type|attacks|ban_count|rep_score
declare -A IP_TIMESTAMPS  # Stores: IP -> comma-separated attack timestamps (last 100)
declare -A IP_ATTACK_VECTORS  # Stores: IP -> unique attack vectors (SSH,WEB,EMAIL,etc)
declare -A SUBNET_ATTACKS  # Stores: subnet -> attack count
declare -A ATTACK_TYPE_COUNTER
TOTAL_THREATS=0
TOTAL_BLOCKS=0
START_TIME=$(date +%s)
MAX_TRACKED_IPS=500  # Prevent memory overflow
VELOCITY_WINDOW=3600  # 1 hour in seconds
DECAY_CHECK_INTERVAL=1800  # Check for decay every 30 minutes
LAST_DECAY_CHECK=$START_TIME

# Hide cursor for cleaner display
command -v tput &>/dev/null && tput civis

################################################################################
# Intelligence Functions
################################################################################

# Get or create IP intelligence data
# Returns: score|hits|bot_type|attacks|ban_count|rep_score
get_ip_intelligence() {
    local ip="$1"

    # Check if we have cached data
    if [ -n "${IP_DATA[$ip]}" ]; then
        echo "${IP_DATA[$ip]}"
        return 0
    fi

    # Query IP reputation database
    local rep_data=$(lookup_ip "$ip" 2>/dev/null)

    if [ -n "$rep_data" ]; then
        # Parse: IP|HIT_COUNT|REP_SCORE|COUNTRY|ATTACK_FLAGS|...
        IFS='|' read -r _ db_hits rep_score country attack_flags _ _ _ notes ban_count _ <<< "$rep_data"

        # Initialize with learned data
        local score=${rep_score:-0}
        local hits=${db_hits:-0}
        local bot_type="unknown"
        local attacks=$(decode_attack_flags "$attack_flags" 2>/dev/null | tr ',' ' ' || echo "")
        ban_count=${ban_count:-0}

        # Cache it
        IP_DATA[$ip]="$score|$hits|$bot_type|$attacks|$ban_count|$rep_score"
        echo "${IP_DATA[$ip]}"
    else
        # New IP - initialize
        IP_DATA[$ip]="0|0|unknown||0|0"
        echo "${IP_DATA[$ip]}"
    fi
}
export -f get_ip_intelligence

# Write IP data directly to file (for cross-process communication)
write_ip_data_to_file() {
    local ip="$1"
    local data="$2"

    # Use flock for thread-safe writes (with timeout to prevent deadlocks)
    # CRITICAL FIX: Increased timeout from 5 to 30 seconds
    # Reason: At 70+ IPs/sec with write_ip_data_to_file backgrounded,
    # 5-second timeout causes 20-30% silent data loss on high-velocity attacks
    # 30-second timeout ensures all IPs are tracked during sustained attacks
    (
        flock -w 30 200 || return 1

        # Read existing data
        local temp_file="$TEMP_DIR/ip_data.tmp"
        cp "$TEMP_DIR/ip_data" "$temp_file" 2>/dev/null || touch "$temp_file"

        # Remove old entry for this IP (if exists)
        # CRITICAL FIX: Check if grep succeeds before relying on output
        # Bug: If grep fails (file error), ${temp_file}.new is not created
        # Result: echo appends to non-existent file, losing all previous IPs!
        # Fix: Create new file first, then filter, then verify success
        if grep -v "^${ip}=" "$temp_file" > "${temp_file}.new" 2>/dev/null; then
            # grep succeeded - ${temp_file}.new contains all IPs except the old one
            :
        else
            # grep failed - copy all data to new file and manually remove the old entry
            cp "$temp_file" "${temp_file}.new" 2>/dev/null || touch "${temp_file}.new"
            # Try to remove old entry with sed as fallback
            sed -i "/^${ip}=/d" "${temp_file}.new" 2>/dev/null || true
        fi

        # Add new entry
        echo "${ip}=${data}" >> "${temp_file}.new"

        # Atomic replacement
        mv "${temp_file}.new" "$TEMP_DIR/ip_data"
        rm -f "$temp_file"

    ) 200>"$TEMP_DIR/ip_data.lock"
}
export -f write_ip_data_to_file

# Update IP intelligence
update_ip_intelligence() {
    local ip="$1"
    local url="$2"
    local user_agent="$3"
    local method="${4:-GET}"

    # Get current data
    local current=$(get_ip_intelligence "$ip")
    IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "$current"

    # Increment hits
    hits=$((hits + 1))

    # Enrich with threat intelligence on first encounter (hits == 1)
    if [ "${hits:-0}" -eq 1 ]; then
        # Check if whitelisted first
        if is_whitelisted_service "$ip" 2>/dev/null; then
            score=0
            bot_type="legit"
        else
            # Get threat intelligence (in background to avoid slowing down)
            (
                local threat_intel=$(get_threat_intelligence "$ip" 2>/dev/null)
                IFS='|' read -r abuse_conf abuse_rpts country isp geo timing whitelisted <<< "$threat_intel"

                # Store enrichment data for later use
                local enrich_file="$TEMP_DIR/threat_enrich_${ip//\./_}"
                echo "$threat_intel" > "$enrich_file"

                # Boost score based on AbuseIPDB confidence
                if [ "${abuse_conf:-0}" -ge 75 ]; then
                    # High confidence malicious - add 30 points
                    local current_data="${IP_DATA[$ip]}"
                    IFS='|' read -r old_score old_hits old_bot old_attacks old_ban old_rep <<< "$current_data"
                    local new_score=$((old_score + 30))
                    [ "${new_score:-0}" -gt 100 ] && new_score=100
                    IP_DATA[$ip]="$new_score|$old_hits|$old_bot|$old_attacks|$old_ban|$old_rep"
                    # Write to file for cross-process communication
                    write_ip_data_to_file "$ip" "$new_score|$old_hits|$old_bot|$old_attacks|$old_ban|$old_rep" 2>/dev/null
                elif [ "${abuse_conf:-0}" -ge 50 ]; then
                    # Medium confidence - add 15 points
                    local current_data="${IP_DATA[$ip]}"
                    IFS='|' read -r old_score old_hits old_bot old_attacks old_ban old_rep <<< "$current_data"
                    local new_score=$((old_score + 15))
                    [ "${new_score:-0}" -gt 100 ] && new_score=100
                    IP_DATA[$ip]="$new_score|$old_hits|$old_bot|$old_attacks|$old_ban|$old_rep"
                    # Write to file for cross-process communication
                    write_ip_data_to_file "$ip" "$new_score|$old_hits|$old_bot|$old_attacks|$old_ban|$old_rep" 2>/dev/null
                fi

                # High-risk country adds 5 points
                if is_high_risk_country "${geo:-XX}" 2>/dev/null; then
                    local current_data="${IP_DATA[$ip]}"
                    IFS='|' read -r old_score old_hits old_bot old_attacks old_ban old_rep <<< "$current_data"
                    local new_score=$((old_score + 5))
                    [ "${new_score:-0}" -gt 100 ] && new_score=100
                    IP_DATA[$ip]="$new_score|$old_hits|$old_bot|$old_attacks|$old_ban|$old_rep"
                    # Write to file for cross-process communication
                    write_ip_data_to_file "$ip" "$new_score|$old_hits|$old_bot|$old_attacks|$old_ban|$old_rep" 2>/dev/null
                fi
            ) &
        fi
    fi

    # Classify bot if unknown
    if [ "$bot_type" = "unknown" ] && [ -n "$user_agent" ]; then
        bot_type=$(classify_bot_type "$user_agent" 2>/dev/null || echo "unknown")
    fi

    # Record attack pattern for learning
    if [ -n "$url" ]; then
        record_attack_pattern "$ip" "${attacks:-unknown}" "$url" "${user_agent:-unknown}" 2>/dev/null &
    fi

    # Detect attacks in URL (pass user_agent and ip for enhanced detection)
    local new_attacks=$(detect_all_attacks "$url" "$method" "$user_agent" "$ip")

    if [ -n "$new_attacks" ]; then
        # Add to attack list (unique)
        if [ -z "$attacks" ]; then
            attacks="$new_attacks"
        else
            attacks="$attacks,$new_attacks"
        fi

        # Remove duplicates using associative array (faster than sort -u pipeline)
        local -A unique_attacks
        IFS=',' read -ra ATTACK_LIST <<< "$attacks"
        for atk in "${ATTACK_LIST[@]}"; do
            [ -n "$atk" ] && unique_attacks[$atk]=1
        done
        attacks=$(IFS=','; echo "${!unique_attacks[*]}")

        # Update attack type counter
        IFS=',' read -ra ATTACK_ARRAY <<< "$new_attacks"
        for attack in "${ATTACK_ARRAY[@]}"; do
            ((ATTACK_TYPE_COUNTER["$attack"]++))
        done

        # Calculate attack score
        local attack_score=$(calculate_attack_score "$new_attacks")
        score=$((score + attack_score))

        ((TOTAL_THREATS++))
    fi

    # Request volume scoring
    if [ "${hits:-0}" -gt 100 ]; then
        score=$((score + 5))
    elif [ "${hits:-0}" -gt 50 ]; then
        score=$((score + 3))
    elif [ "${hits:-0}" -gt 20 ]; then
        score=$((score + 1))
    fi

    # Adjust score based on bot type
    case "$bot_type" in
        legit|ai|monitor)
            # Legitimate bots - reduce score ONLY if no attacks detected
            # (prevents spoofed user agents from avoiding blocks)
            if [ -z "$attacks" ]; then
                score=$((score - 5))
                [ "${score:-0}" -lt 0 ] && score=0
            fi
            ;;
        suspicious)
            # Suspicious bots - increase score
            score=$((score + 15))
            ;;
    esac

    # Cap at 100
    [ "${score:-0}" -gt 100 ] && score=100

    # Check if we're tracking too many IPs (memory protection)
    if [ ${#IP_DATA[@]} -ge $MAX_TRACKED_IPS ]; then
        # Remove lowest scoring IPs
        local to_remove=()
        for check_ip in "${!IP_DATA[@]}"; do
            # Use bash parameter expansion instead of cut
            local check_score="${IP_DATA[$check_ip]%%|*}"
            [ "$check_score" -lt 10 ] && to_remove+=("$check_ip")
        done

        # Remove up to 100 low-score IPs
        local removed=0
        for remove_ip in "${to_remove[@]}"; do
            unset IP_DATA[$remove_ip]
            ((removed++))
            [ "${removed:-0}" -ge 100 ] && break
        done
    fi

    # Update cached data
    IP_DATA[$ip]="$score|$hits|$bot_type|$attacks|$ban_count|$rep_score"

    # CRITICAL FIX: Write to file immediately for cross-process communication
    # This ensures auto-mitigation engine sees scores from HTTP/SSH monitoring subprocesses
    write_ip_data_to_file "$ip" "$score|$hits|$bot_type|$attacks|$ban_count|$rep_score" 2>/dev/null &

    # Update IP reputation DB in background (if score > 0)
    if [ "${score:-0}" -gt 0 ]; then
        (update_ip_reputation "$ip" 1 "$score" 0 "Live monitor: $new_attacks" >/dev/null 2>&1) &
    fi
}
export -f update_ip_intelligence

################################################################################
# Advanced Intelligence Functions
################################################################################

# Record attack timestamp for velocity tracking
record_attack_timestamp() {
    local ip="$1"
    local now=$(date +%s)

    # Get existing timestamps
    local timestamps="${IP_TIMESTAMPS[$ip]}"

    # Add new timestamp
    if [ -z "$timestamps" ]; then
        timestamps="$now"
    else
        timestamps="$timestamps,$now"
    fi

    # Keep only last 100 timestamps (prevent memory bloat)
    # Use bash array instead of pipeline for efficiency
    IFS=',' read -ra TS_ARRAY <<< "$timestamps"
    if [ "${#TS_ARRAY[@]}" -gt 100 ]; then
        # Keep last 100 elements
        timestamps=$(IFS=','; echo "${TS_ARRAY[*]: -100}")
    fi

    IP_TIMESTAMPS[$ip]="$timestamps"
}

# Calculate attack velocity (attacks per hour)
# Returns: velocity|recent_count|bonus_points|reason
calculate_attack_velocity() {
    local ip="$1"
    local timestamps="${IP_TIMESTAMPS[$ip]}"

    [ -z "$timestamps" ] && echo "0|0|0|" && return

    local now=$(date +%s)
    local window_start=$((now - VELOCITY_WINDOW))

    # Count attacks in last hour
    local recent_count=0
    local oldest_in_window=""

    while IFS=',' read -ra TIMES; do
        for ts in "${TIMES[@]}"; do
            if [ "$ts" -ge "$window_start" ]; then
                ((recent_count++))
                [ -z "$oldest_in_window" ] && oldest_in_window="$ts"
            fi
        done
    done <<< "$timestamps"

    # Calculate velocity and bonus
    local bonus=0
    local reason=""

    if [ "$recent_count" -ge 20 ]; then
        # 20+ attacks in 1 hour = extreme velocity
        bonus=30
        reason="EXTREME_VELOCITY:${recent_count}/hr"
    elif [ "$recent_count" -ge 10 ]; then
        # 10-19 attacks in 1 hour = high velocity
        bonus=20
        reason="HIGH_VELOCITY:${recent_count}/hr"
    elif [ "$recent_count" -ge 5 ]; then
        # 5-9 attacks in 1 hour = moderate velocity
        bonus=10
        reason="MOD_VELOCITY:${recent_count}/hr"
    fi

    # If attacks are very rapid (10 in 5 minutes), extra bonus
    local five_min_ago=$((now - 300))
    local rapid_count=0
    while IFS=',' read -ra TIMES; do
        for ts in "${TIMES[@]}"; do
            [ "$ts" -ge "$five_min_ago" ] && ((rapid_count++))
        done
    done <<< "$timestamps"

    if [ "$rapid_count" -ge 10 ]; then
        bonus=$((bonus + 15))
        reason="${reason}+RAPID:${rapid_count}/5min"
    fi

    echo "${recent_count}|${bonus}|${reason}"
}

# Record attack vector for diversity tracking
record_attack_vector() {
    local ip="$1"
    local vector="$2"  # SSH, WEB, EMAIL, FTP, DATABASE, FIREWALL

    local vectors="${IP_ATTACK_VECTORS[$ip]}"

    # Add if not already present
    if [[ ! "$vectors" =~ $vector ]]; then
        if [ -z "$vectors" ]; then
            vectors="$vector"
        else
            vectors="$vectors,$vector"
        fi
        IP_ATTACK_VECTORS[$ip]="$vectors"
    fi
}

# Calculate diversity bonus
# Returns: vector_count|bonus_points|reason
calculate_diversity_bonus() {
    local ip="$1"
    local vectors="${IP_ATTACK_VECTORS[$ip]}"

    [ -z "$vectors" ] && echo "0|0|" && return

    local count=$(echo "$vectors" | tr ',' '\n' 2>/dev/null | wc -l 2>/dev/null || echo "0")
    local bonus=0
    local reason=""

    if [ "$count" -ge 4 ]; then
        bonus=35
        reason="MULTI_VECTOR:${count}_types"
    elif [ "$count" -eq 3 ]; then
        bonus=25
        reason="COORDINATED:${count}_types"
    elif [ "$count" -eq 2 ]; then
        bonus=10
        reason="DUAL_VECTOR:${count}_types"
    fi

    echo "${count}|${bonus}|${reason}"
}

# Detect timing patterns (bot signatures)
# Returns: pattern_type|confidence|bonus_points|reason
detect_timing_pattern() {
    local ip="$1"
    local timestamps="${IP_TIMESTAMPS[$ip]}"

    [ -z "$timestamps" ] && echo "NONE|0|0|" && return

    # Need at least 5 attacks to detect pattern
    local count=$(echo "$timestamps" | tr ',' '\n' | wc -l)
    [ "$count" -lt 5 ] && echo "INSUFFICIENT|0|0|" && return

    # Calculate gaps between attacks
    local prev_ts=""
    local gaps=()

    while IFS=',' read -ra TIMES; do
        for ts in "${TIMES[@]}"; do
            if [ -n "$prev_ts" ]; then
                local gap=$((ts - prev_ts))
                gaps+=("$gap")
            fi
            prev_ts="$ts"
        done
    done <<< "$timestamps"

    # Check for consistent intervals (bot signature)
    local total_gap=0
    local gap_count=${#gaps[@]}

    for gap in "${gaps[@]}"; do
        total_gap=$((total_gap + gap))
    done

    if [ "$gap_count" -gt 0 ]; then
        local avg_gap=$((total_gap / gap_count))

        # Check variance - if all gaps are similar, it's a bot
        local variance=0
        for gap in "${gaps[@]}"; do
            local diff=$((gap - avg_gap))
            [ "$diff" -lt 0 ] && diff=$((diff * -1))
            variance=$((variance + diff))
        done

        local avg_variance=$((variance / gap_count))

        # If average variance is low (gaps are consistent), it's automated
        if [ "$avg_variance" -lt 3 ]; then
            # Very consistent timing = bot
            echo "BOT_PATTERN|HIGH|20|AUTOMATED:${avg_gap}s_intervals"
            return
        elif [ "$avg_variance" -lt 10 ]; then
            # Somewhat consistent = likely bot
            echo "LIKELY_BOT|MEDIUM|10|PATTERN:${avg_gap}s_avg"
            return
        fi
    fi

    echo "HUMAN_LIKE|LOW|0|RANDOM_TIMING"
}

# Check if attack was successful
# Returns: success_detected|bonus_points|reason
detect_attack_success() {
    local ip="$1"
    local url="$2"
    local status="${3:-0}"
    local method="${4:-GET}"

    local bonus=0
    local reason=""
    local success=0

    # Check for successful login attempts
    if [[ "$url" =~ wp-login\.php ]] && [ "$status" -eq 302 ]; then
        # 302 redirect on wp-login = successful login
        success=1
        bonus=50
        reason="WORDPRESS_BREACH"
    elif [[ "$url" =~ wp-admin ]] && [ "$status" -eq 200 ] && [[ "$method" == "POST" ]]; then
        # POST to wp-admin with 200 = potential successful action
        success=1
        bonus=40
        reason="ADMIN_ACCESS"
    elif [ "$status" -eq 200 ] && [[ "$url" =~ \.(php|asp|aspx|jsp)$ ]] && [[ "$url" =~ (shell|cmd|exec|eval) ]]; then
        # Successful request to shell-like file = breach
        success=1
        bonus=60
        reason="SHELL_ACCESS"
    fi

    echo "${success}|${bonus}|${reason}"
}

# Track subnet attacks
track_subnet_attack() {
    local ip="$1"

    # Extract /24 subnet (bash built-in, 100x faster than cut)
    local subnet="${ip%.*}"  # Remove last octet: 1.2.3.4 → 1.2.3

    # Increment subnet counter
    local count=${SUBNET_ATTACKS[$subnet]:-0}
    count=$((count + 1))
    SUBNET_ATTACKS[$subnet]=$count

    echo "$count"
}

# Calculate subnet attack bonus
# Returns: subnet_count|bonus_points|reason
calculate_subnet_bonus() {
    local ip="$1"
    local subnet="${ip%.*}"  # Bash built-in: 1.2.3.4 → 1.2.3 (100x faster than cut)

    local count=${SUBNET_ATTACKS[$subnet]:-0}
    local bonus=0
    local reason=""

    if [ "$count" -ge 10 ]; then
        bonus=40
        reason="SUBNET_SWARM:${count}_IPs_in_${subnet}.0/24"
    elif [ "$count" -ge 5 ]; then
        bonus=25
        reason="SUBNET_ATTACK:${count}_IPs_in_${subnet}.0/24"
    elif [ "$count" -ge 3 ]; then
        bonus=15
        reason="RELATED_IPS:${count}_in_${subnet}.0/24"
    fi

    echo "${count}|${bonus}|${reason}"
}

# Assess target criticality
# Returns: criticality_level|bonus_points|reason
assess_target_criticality() {
    local url="$1"
    local method="${2:-GET}"

    local bonus=0
    local reason=""
    local level="LOW"

    # Critical admin paths
    if [[ "$url" =~ (wp-admin|admin|administrator|manager|phpmyadmin|cpanel|whm) ]]; then
        bonus=15
        reason="ADMIN_TARGET"
        level="HIGH"
    fi

    # Authentication endpoints
    if [[ "$url" =~ (wp-login|login|signin|auth|session) ]]; then
        bonus=12
        reason="AUTH_TARGET"
        level="HIGH"
    fi

    # Config/sensitive files
    if [[ "$url" =~ (config|\.env|\.git|\.sql|backup|database|credentials) ]]; then
        bonus=18
        reason="SENSITIVE_FILE"
        level="CRITICAL"
    fi

    # Shell/exploit attempts
    if [[ "$url" =~ (shell|cmd|exec|eval|system|phpinfo) ]]; then
        bonus=20
        reason="EXPLOIT_ATTEMPT"
        level="CRITICAL"
    fi

    # Upload endpoints (RCE risk)
    if [[ "$url" =~ upload ]] && [[ "$method" == "POST" ]]; then
        bonus=15
        reason="UPLOAD_TARGET"
        level="HIGH"
    fi

    echo "${level}|${bonus}|${reason}"
}

# Apply reputation decay
apply_reputation_decay() {
    local now=$(date +%s)
    local time_since_last=$((now - LAST_DECAY_CHECK))

    # Only check every 30 minutes
    [ "$time_since_last" -lt "$DECAY_CHECK_INTERVAL" ] && return

    LAST_DECAY_CHECK=$now

    # Decay scores for IPs with no recent activity
    for ip in "${!IP_DATA[@]}"; do
        local timestamps="${IP_TIMESTAMPS[$ip]}"

        [ -z "$timestamps" ] && continue

        # Get most recent attack time
        local last_attack=$(echo "$timestamps" | tr ',' '\n' 2>/dev/null | tail -1 2>/dev/null || echo "0")
        local time_since_attack=$((now - ${last_attack:-0}))

        # If no activity for 6 hours, start decay
        if [ "$time_since_attack" -gt 21600 ]; then
            IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "${IP_DATA[$ip]}"

            # Reduce score by 20% (but not below 0)
            local decay_amount=$((score / 5))
            [ "$decay_amount" -lt 5 ] && decay_amount=5

            score=$((score - decay_amount))
            [ "$score" -lt 0 ] && score=0

            # Update data
            IP_DATA[$ip]="$score|$hits|$bot_type|$attacks|$ban_count|$rep_score"
        fi
    done
}

# Context-aware scoring (geo, ISP, time-of-day)
# Returns: context_bonus|reason
calculate_context_bonus() {
    local ip="$1"
    local now=$(date +%s)

    local bonus=0
    local reasons=""

    # Time-of-day analysis (attacks at odd hours = suspicious)
    local hour=$(date +%H)
    if [ "$hour" -ge 2 ] && [ "$hour" -le 5 ]; then
        # Attacks between 2am-5am (server timezone) = suspicious
        bonus=$((bonus + 8))
        reasons="NIGHT_ATTACK:${hour}h"
    fi

    # Check geolocation if available (from threat intelligence)
    if [ -f "$TEMP_DIR/threat_enrich_${ip//\./_}" ]; then
        local threat_data=$(cat "$TEMP_DIR/threat_enrich_${ip//\./_}")
        IFS='|' read -r abuse_conf abuse_rpts country isp geo timing whitelisted <<< "$threat_data"

        # High-risk country already detected
        if is_high_risk_country "${geo:-XX}" 2>/dev/null; then
            bonus=$((bonus + 5))
            [ -n "$reasons" ] && reasons="${reasons}+" || reasons=""
            reasons="${reasons}HIGH_RISK_GEO:${geo}"
        fi

        # Residential ISP (suspicious for server attacks)
        # Bash pattern matching (faster than grep subprocess)
        local isp_lower="${isp,,}"  # Convert to lowercase
        if [[ "$isp_lower" =~ (comcast|verizon|att|residential|cable|dsl|fiber|broadband) ]]; then
            bonus=$((bonus + 10))
            [ -n "$reasons" ] && reasons="${reasons}+" || reasons=""
            reasons="${reasons}RESIDENTIAL_ISP"
        fi
    fi

    echo "${bonus}|${reasons}"
}

# Atomically increment block counter (prevents race conditions)
increment_block_counter() {
    local increment="${1:-1}"
    (
        flock -x 200
        local current=$(cat "$TEMP_DIR/total_blocks" 2>/dev/null || echo "0")
        echo $((current + increment)) > "$TEMP_DIR/total_blocks"
    ) 200>"$TEMP_DIR/counter.lock"
}

# Record blocked IP to reputation database (for permanent tracking)
record_blocked_ip() {
    local ip="$1"
    local reason="${2:-Auto-blocked}"

    # Update IP_DATA to increment ban_count
    if [ -n "${IP_DATA[$ip]}" ]; then
        IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "${IP_DATA[$ip]}"
        ban_count=$((ban_count + 1))
        IP_DATA[$ip]="$score|$hits|$bot_type|$attacks|$ban_count|$rep_score"

        # Also save to IP reputation database (in background to avoid blocking)
        if type record_ip_data &>/dev/null; then
            (record_ip_data "$ip" "$score" "$hits" "$attacks" "$ban_count" 2>/dev/null) &
        fi
    fi

    # Log to permanent block history file
    echo "$(date '+%Y-%m-%d %H:%M:%S')|$ip|$reason" >> "$SNAPSHOT_DIR/block_history.log"
}

# Batch block multiple IPs at once (optimized for DDoS scenarios)
batch_block_ips() {
    local -a ip_list=("$@")
    local blocked=0
    local failed=0

    if [ ${#ip_list[@]} -eq 0 ]; then
        return 0
    fi

    # DEBUG: Log function entry
    echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: Starting batch block for ${#ip_list[@]} IPs: ${ip_list[*]}" >> "$TEMP_DIR/debug.log" 2>/dev/null || true

    echo "Batch blocking ${#ip_list[@]} IPs..."

    # Use IPset for instant batch blocking if available
    if [ "$IPSET_AVAILABLE" -eq 1 ]; then
        echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: Using IPSET path" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
        for ip in "${ip_list[@]}"; do
            # Validate IP format
            if ! is_valid_ip "$ip"; then
                ((failed++))
                continue
            fi

            # Add to IPset with 1-hour timeout (instant, no verification needed)
            if ipset add "$IPSET_NAME" "$ip" timeout 3600 2>/dev/null; then
                ((blocked++))
                echo "$ip" >> "$TEMP_DIR/blocked_ips_cache"
            else
                # Already in set or error
                ((failed++))
            fi
        done

        # Single cache update after batch
        sort -u "$TEMP_DIR/blocked_ips_cache" -o "$TEMP_DIR/blocked_ips_cache" 2>/dev/null

        echo "✓ IPset batch: $blocked blocked, $failed skipped"
    else
        # Fallback to CSF - use chain_DENY ipset directly for speed
        echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: Using CSF chain_DENY ipset path (IPSET_AVAILABLE=$IPSET_AVAILABLE)" >> "$TEMP_DIR/debug.log" 2>/dev/null || true

        # Try CSF's chain_DENY ipset first (much faster than csf -td for batches)
        if ipset list chain_DENY &>/dev/null 2>&1; then
            for ip in "${ip_list[@]}"; do
                if ! is_valid_ip "$ip"; then
                    echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: Invalid IP format: $ip" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
                    ((failed++))
                    continue
                fi

                # Add directly to CSF's chain_DENY ipset (instant kernel-level blocking)
                # Include 1-hour timeout if chain_DENY supports it
                if ipset add chain_DENY "$ip" timeout 3600 -exist 2>/dev/null; then
                    echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: chain_DENY ipset SUCCESS for $ip (timeout 1h)" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
                    ((blocked++))
                    echo "$ip" >> "$TEMP_DIR/blocked_ips_cache"
                elif ipset add chain_DENY "$ip" -exist 2>/dev/null; then
                    # Fallback: chain_DENY doesn't support timeout (CSF will manage via csf -td in background)
                    echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: chain_DENY ipset SUCCESS for $ip (no timeout - CSF managed)" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
                    ((blocked++))
                    echo "$ip" >> "$TEMP_DIR/blocked_ips_cache"
                else
                    echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: chain_DENY ipset FAILED for $ip" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
                    ((failed++))
                fi
            done
            sort -u "$TEMP_DIR/blocked_ips_cache" -o "$TEMP_DIR/blocked_ips_cache" 2>/dev/null
            echo "✓ CSF chain_DENY ipset batch: $blocked blocked, $failed failed"
        else
            # Fallback to csf -td if chain_DENY ipset not available
            echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: chain_DENY ipset not available, falling back to csf -td" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
            for ip in "${ip_list[@]}"; do
                if ! is_valid_ip "$ip"; then
                    ((failed++))
                    continue
                fi

                # Use csf -td as last resort (slower)
                if csf -td "$ip" 3600 "Auto-block: SYN attack" >/dev/null 2>&1; then
                    ((blocked++))
                else
                    ((failed++))
                fi
            done
            echo "✓ CSF -td batch: $blocked blocked, $failed failed"
        fi
        echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: CSF fallback batch complete - blocked=$blocked, failed=$failed" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
    fi

    # Update total counter atomically
    echo "[$(date +"%H:%M:%S")] BATCH_BLOCK: Incrementing counter by $blocked" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
    increment_block_counter "$blocked"

    return 0
}

# Block IP temporarily with CSF
block_ip_temporary() {
    local ip="$1"
    local hours="${2:-1}"
    local reason="${3:-Auto-block by live monitor}"
    local seconds=$((hours * 3600))

    # Validate IP format before blocking
    if ! is_valid_ip "$ip"; then
        echo "✗ Error: Invalid IP format: $ip"
        return 1
    fi

    # PRIORITY 1: Use IPset for instant kernel-level blocking (performance critical)
    if [ "$IPSET_AVAILABLE" -eq 1 ]; then
        # Try IPset with timeout if supported
        if [ "$IPSET_SUPPORTS_TIMEOUT" -eq 1 ]; then
            if ipset add "$IPSET_NAME" "$ip" timeout "$seconds" -exist 2>/dev/null; then
                echo "✓ $ip blocked via IPset $IPSET_NAME (expires in ${hours}h)"
                echo "$ip" >> "$TEMP_DIR/blocked_ips_cache"
                increment_block_counter 1
                record_blocked_ip "$ip" "$reason"
                return 0
            fi
        else
            # IPset without timeout (CSF's chain_DENY) - add to IPset for instant block,
            # then let CSF manage the timeout removal
            if ipset add "$IPSET_NAME" "$ip" -exist 2>/dev/null; then
                echo "$ip" >> "$TEMP_DIR/blocked_ips_cache"
                increment_block_counter 1
                record_blocked_ip "$ip" "$reason"

                # Let CSF manage the timeout in background (IPset already blocking)
                if command -v csf &>/dev/null; then
                    csf -td "$ip" "$seconds" "$reason" >/dev/null 2>&1 &
                    echo "✓ $ip blocked via IPset $IPSET_NAME (CSF managing timeout: ${hours}h)"
                else
                    echo "✓ $ip blocked via IPset $IPSET_NAME (permanent - no CSF timeout)"
                fi
                return 0
            fi
        fi
    fi

    # FALLBACK: CSF-only blocking (slower, but still works)
    if command -v csf &>/dev/null; then
        if csf -td "$ip" "$seconds" "$reason" >/dev/null 2>&1; then
            echo "✓ $ip blocked via CSF (expires in ${hours}h)"
            echo "$ip" >> "$TEMP_DIR/blocked_ips_cache"
            increment_block_counter 1
            record_blocked_ip "$ip" "$reason"
            return 0
        else
            echo "✗ Warning: CSF block failed for $ip"
            return 1
        fi
    fi

    echo "✗ Error: No blocking method available"
    return 1
}

# Quick block IP (wrapper for background auto-blocking)
# Used by ET detection and auto-mitigation engine
quick_block_ip() {
    local ip="$1"
    local reason="${2:-Auto-block: Critical threat}"

    # Validate IP
    if ! is_valid_ip "$ip"; then
        return 1
    fi

    # Block for 1 hour using IPset or CSF
    block_ip_temporary "$ip" 1 "$reason" >/dev/null 2>&1
}

# Block IP permanently with CSF
block_ip_permanent() {
    local ip="$1"
    local reason="${2:-Permanent block by live monitor}"

    # Validate IP format before blocking
    if ! is_valid_ip "$ip"; then
        echo "✗ Error: Invalid IP format: $ip"
        return 1
    fi

    # PRIORITY: Add to IPset immediately for instant kernel-level blocking
    if [ "$IPSET_AVAILABLE" -eq 1 ]; then
        if [ "$IPSET_SUPPORTS_TIMEOUT" -eq 1 ]; then
            # IPset with timeout - use max timeout (24 hours)
            ipset add "$IPSET_NAME" "$ip" timeout 86400 -exist 2>/dev/null
        else
            # IPset without timeout (CSF's chain_DENY) - permanent add
            ipset add "$IPSET_NAME" "$ip" -exist 2>/dev/null
        fi
    fi

    # CSF for persistent management (runs after IPset for immediate effect)
    if command -v csf &>/dev/null; then
        echo "Permanently blocking $ip: $reason"
        if csf -d "$ip" "$reason" >/dev/null 2>&1; then
            echo "✓ $ip permanently blocked via CSF"
            echo "$ip" >> "$TEMP_DIR/blocked_ips_cache"

            # Update counter atomically
            increment_block_counter 1

            # Record to reputation database
            record_blocked_ip "$ip" "PERMANENT:$reason"

            return 0
        else
            echo "✗ Warning: CSF permanent block failed for $ip"
            return 1
        fi
    fi

    echo "✗ Error: CSF not available"
    return 1
}

# Check if IP is currently blocked in CSF/iptables (optimized with caching)
is_ip_blocked() {
    local ip="$1"

    # Use cached blocked IPs list (refreshed every 10 seconds by background process)
    if [ -f "$TEMP_DIR/blocked_ips_cache" ]; then
        if grep -q "^$ip$" "$TEMP_DIR/blocked_ips_cache" 2>/dev/null; then
            return 0
        fi
    fi

    return 1
}

# Real-time verification (no cache) for immediate confirmation after blocking
verify_ip_blocked() {
    local ip="$1"

    # Check CSF temporary blocks
    if command -v csf &>/dev/null; then
        # CRITICAL FIX: Use -w flag for word boundary matching
        if csf -t 2>/dev/null | grep -q -w "$ip"; then
            return 0
        fi

        # Check CSF permanent deny list
        if [ -f /etc/csf/csf.deny ]; then
            if grep -q "^$ip" /etc/csf/csf.deny 2>/dev/null; then
                return 0
            fi
        fi
    fi

    # Check iptables directly
    if command -v iptables &>/dev/null; then
        # CRITICAL FIX: Use -w flag for word boundary matching
        if iptables -L INPUT -n 2>/dev/null | grep -q -w "$ip"; then
            return 0
        fi
    fi

    return 1
}

# Get threat level from score
get_threat_level() {
    local score="${1:-0}"

    if [ "$score" -ge "$THREAT_THRESHOLD_CRITICAL" ]; then
        echo "CRITICAL"
    elif [ "$score" -ge "$THREAT_THRESHOLD_HIGH" ]; then
        echo "HIGH"
    elif [ "$score" -ge "$THREAT_THRESHOLD_MEDIUM" ]; then
        echo "MEDIUM"
    else
        echo "LOW"
    fi
}
export -f get_threat_level

# Get color for threat level
get_threat_color() {
    local level="$1"

    case "$level" in
        CRITICAL) echo "$CRITICAL_COLOR" ;;
        HIGH) echo "$HIGH_COLOR" ;;
        MEDIUM) echo "$MEDIUM_COLOR" ;;
        LOW) echo "$LOW_COLOR" ;;
        SAFE) echo "$SAFE_COLOR" ;;
        *) echo "$INFO_COLOR" ;;
    esac
}
export -f get_threat_color

# Get bot color
get_bot_color() {
    local bot_type="$1"

    case "$bot_type" in
        legit) echo "$SAFE_COLOR" ;;
        ai) echo '\033[0;34m' ;;  # Blue
        monitor) echo "$MEDIUM_COLOR" ;;
        suspicious) echo "$HIGH_COLOR" ;;
        *) echo "$INFO_COLOR" ;;
    esac
}

################################################################################
# Dashboard Display Functions
################################################################################

draw_header() {
    clear
    local uptime=$(($(date +%s) - START_TIME))
    local uptime_str=$(printf "%02d:%02d:%02d" $((uptime/3600)) $((uptime%3600/60)) $((uptime%60)))

    # Read event counter from file (updated by subshell)
    local event_count=$(cat "$TEMP_DIR/event_counter" 2>/dev/null || echo "0")

    echo -e "${CRITICAL_COLOR}╔════════════════════════════════════════════════════════════════════════════╗${NC}"
    echo -e "${CRITICAL_COLOR}║          🚨 LIVE SECURITY MONITOR - INTELLIGENCE MODE 🧠               ║${NC}"
    echo -e "${CRITICAL_COLOR}╚════════════════════════════════════════════════════════════════════════════╝${NC}"
    echo -e "${INFO_COLOR}Runtime: ${uptime_str} | Events: ${event_count} | Threats: ${TOTAL_THREATS} | Blocks: ${TOTAL_BLOCKS} | Monitoring...${NC}"
    echo ""
}

draw_intelligence_panel() {
    echo -e "${HIGH_COLOR}┌─ THREAT INTELLIGENCE ──────────────────────────────────────────────────────┐${NC}"

    # Debug: Show cache status
    if [ -f "$TEMP_DIR/blocked_ips_cache" ]; then
        CACHED_IPS=$(wc -l < "$TEMP_DIR/blocked_ips_cache" 2>/dev/null || echo 0)
        echo -e "${INFO_COLOR}  Cache: $CACHED_IPS blocked IPs${NC}" >> "$TEMP_DIR/debug.log"
    else
        echo -e "${INFO_COLOR}  Cache: NOT FOUND${NC}" >> "$TEMP_DIR/debug.log"
    fi

    # Get top IPs by threat score (exclude already blocked IPs)
    # Load blocked IPs cache into associative array for O(1) lookups
    declare -A blocked_ips_lookup
    if [ -f "$TEMP_DIR/blocked_ips_cache" ]; then
        while IFS= read -r blocked_ip; do
            [ -n "$blocked_ip" ] && blocked_ips_lookup[$blocked_ip]=1
        done < "$TEMP_DIR/blocked_ips_cache"
    fi

    local ip_list=""
    local blocked_count=0
    local displayed_count=0
    for ip in "${!IP_DATA[@]}"; do
        # Skip IPs that are already blocked (O(1) lookup in hash)
        if [ -n "${blocked_ips_lookup[$ip]}" ]; then
            ((blocked_count++))
            echo "  Filtering out blocked IP: $ip" >> "$TEMP_DIR/debug.log"
            continue
        fi

        ((displayed_count++))
        IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "${IP_DATA[$ip]}"
        ip_list+="$score|$ip|$hits|$bot_type|$attacks|$ban_count|$rep_score"$'\n'
    done

    echo "  Blocked/filtered: $blocked_count, Displaying: $displayed_count" >> "$TEMP_DIR/debug.log"

    if [ -n "$ip_list" ]; then
        # Show fewer IPs in compact mode
        local max_ips=10
        [ "$COMPACT_MODE" -eq 1 ] && max_ips=5

        echo "$ip_list" | sort -t'|' -k1 -rn | head -$max_ips | while IFS='|' read -r score ip hits bot_type attacks ban_count rep_score; do
            # Set defaults for empty values
            score="${score:-0}"
            hits="${hits:-0}"
            ban_count="${ban_count:-0}"
            rep_score="${rep_score:-0}"

            local level=$(get_threat_level "$score")
            local color=$(get_threat_color "$level")
            local bot_color=$(get_bot_color "$bot_type")

            # Build status line
            local status_line=$(printf "%-15s" "$ip")
            status_line+=$(printf " Score:%-3s" "$score")
            status_line+=$(printf " Hits:%-4s" "$hits")

            # Bot type indicator
            case "$bot_type" in
                legit) status_line+=" ✅BOT" ;;
                ai) status_line+=" 🤖AI" ;;
                monitor) status_line+=" 📊MON" ;;
                suspicious) status_line+=" ⚠️ SUS" ;;
                *) status_line+="" ;;
            esac

            # Threat level
            status_line+=$(printf " [%-8s]" "$level")

            # Attacks (use bash parameter expansion instead of cut)
            if [ -n "$attacks" ]; then
                # Show first attack type
                local first_attack="${attacks%%,*}"
                local icon=$(get_attack_icon "$first_attack")
                status_line+=" $icon$first_attack"
            fi

            # Ban count
            if [ "$ban_count" -gt 0 ]; then
                status_line+=" 🚫x$ban_count"
            fi

            # Known threat indicator
            if [ "$rep_score" -gt 0 ]; then
                status_line+=" [KNOWN]"
            fi

            echo -e "${color}${status_line}${NC}"
        done
    else
        # Show appropriate message
        if [ ${#IP_DATA[@]} -gt 0 ]; then
            echo -e "${SAFE_COLOR}  ✓ All detected threats have been blocked${NC}"
        else
            echo -e "${LOW_COLOR}  No threats detected yet...${NC}"
        fi
    fi

    echo -e "${HIGH_COLOR}└────────────────────────────────────────────────────────────────────────────┘${NC}"
    echo ""
}

draw_attack_breakdown() {
    # Skip this section entirely in compact mode
    [ "$COMPACT_MODE" -eq 1 ] && return

    echo -e "${MEDIUM_COLOR}┌─ ATTACK VECTORS ───────────────────────────────────────────────────────────┐${NC}"

    if [ ${#ATTACK_TYPE_COUNTER[@]} -eq 0 ]; then
        echo -e "${LOW_COLOR}  No attacks detected yet...${NC}"
    else
        for attack_type in "${!ATTACK_TYPE_COUNTER[@]}"; do
            local count="${ATTACK_TYPE_COUNTER[$attack_type]}"
            local icon=$(get_attack_icon "$attack_type")
            local color=$(get_attack_color "$attack_type")
            printf "${color}  ${icon} %-20s %5d${NC}\n" "$attack_type" "$count"
        done | sort -t' ' -k3 -rn | head -5
    fi

    echo -e "${MEDIUM_COLOR}└────────────────────────────────────────────────────────────────────────────┘${NC}"
    echo ""
}

draw_live_feed() {
    echo -e "${HIGH_COLOR}┌─ LIVE THREAT FEED ─────────────────────────────────────────────────────────┐${NC}"

    # Adaptive line count based on mode
    local feed_lines=$MAX_DISPLAY_LINES
    [ "$COMPACT_MODE" -eq 1 ] && feed_lines=8

    if [ -f "$TEMP_DIR/recent_events" ] && [ -s "$TEMP_DIR/recent_events" ]; then
        tail -n "$feed_lines" "$TEMP_DIR/recent_events"
    else
        echo -e "${LOW_COLOR}  Waiting for events...${NC}"
    fi

    echo -e "${HIGH_COLOR}└────────────────────────────────────────────────────────────────────────────┘${NC}"
    echo ""
}

draw_quick_actions() {
    echo -e "${MEDIUM_COLOR}┌─ QUICK ACTIONS & RECOMMENDATIONS ─────────────────────────────────────────┐${NC}"

    # Get blockable IPs (score >= 60, not already blocked)
    local blockable_count=0
    local blockable_ips=""
    local has_ddos=0
    local has_ssh_bruteforce=0
    local high_conn_count=0

    # Load blocked IPs cache once for efficient lookups
    declare -A blocked_ips_check
    if [ -f "$TEMP_DIR/blocked_ips_cache" ]; then
        while IFS= read -r blocked_ip; do
            [ -n "$blocked_ip" ] && blocked_ips_check[$blocked_ip]=1
        done < "$TEMP_DIR/blocked_ips_cache"
    fi

    for ip in "${!IP_DATA[@]}"; do
        IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "${IP_DATA[$ip]}"

        # Check attack patterns
        [[ "$attacks" =~ DDOS ]] && has_ddos=1
        [[ "$attacks" =~ BRUTEFORCE ]] && has_ssh_bruteforce=1

        # Skip if score too low for blocking
        [ "$score" -lt 60 ] && continue

        # Skip if already blocked
        [ -n "${blocked_ips_check[$ip]}" ] && continue

        # Count as blockable
        blockable_count=$((blockable_count + 1))
        blockable_ips+="$ip "
    done

    # Check for high connection counts
    if [ -f "$TEMP_DIR/recent_events" ]; then
        high_conn_count=$(grep -c "HIGH_CONN_COUNT" "$TEMP_DIR/recent_events" 2>/dev/null)
    else
        high_conn_count=0
    fi

    # Ensure it's a valid number (strip whitespace and validate)
    high_conn_count=$(echo "$high_conn_count" | tr -d '[:space:]')
    [[ ! "$high_conn_count" =~ ^[0-9]+$ ]] && high_conn_count=0

    # IP Blocking Recommendations
    if [ "$blockable_count" -gt 0 ]; then
        echo -e "${HIGH_COLOR}  ⚠️  $blockable_count high-threat IPs ready to block${NC}"
        echo -e "${MEDIUM_COLOR}     → Press 'b' to open blocking menu${NC}"
    else
        echo -e "${SAFE_COLOR}  ✓ No IPs requiring immediate blocks${NC}"
    fi

    # Intelligent Firewall Recommendations
    local recommendations=0

    if [ "$has_ddos" -eq 1 ] || [ "$high_conn_count" -gt 0 ]; then
        # Check current security settings
        local synflood_status=$(grep "^SYNFLOOD\s*=" /etc/csf/csf.conf 2>/dev/null | cut -d'"' -f2)
        local ct_limit=$(grep -oP "^CT_LIMIT\s*=\s*\"\K[0-9]+" /etc/csf/csf.conf 2>/dev/null | head -1)

        local needs_config=0

        # Check if SYNFLOOD needs enabling
        if [ "$synflood_status" != "1" ]; then
            needs_config=1
        fi

        # Check if CT_LIMIT needs optimization (not set or set to 0)
        if [ -z "$ct_limit" ] || [ "$ct_limit" -eq 0 ]; then
            needs_config=1
        fi

        # Only show recommendation if something needs fixing
        if [ "${needs_config:-0}" -eq 1 ]; then
            echo -e "${HIGH_COLOR}  ⚠️  DDoS/SYN Flood Detected - Firewall Protection Recommended${NC}"
            echo -e "${MEDIUM_COLOR}     → Press 'c' for Security Hardening menu${NC}"
            recommendations=1
        fi
    fi

    if [ "$has_ssh_bruteforce" -eq 1 ]; then
        local ssh_attacks=0
        if [ -f "$TEMP_DIR/recent_events" ]; then
            ssh_attacks=$(grep -c "SSH_BRUTEFORCE" "$TEMP_DIR/recent_events" 2>/dev/null)
        fi
        ssh_attacks=$(echo "$ssh_attacks" | tr -d '[:space:]')
        [[ ! "$ssh_attacks" =~ ^[0-9]+$ ]] && ssh_attacks=0
        if [ "$ssh_attacks" -gt 5 ]; then
            # Check if SSH hardening is already applied
            local current_lf=$(grep -oP "^LF_SSHD\s*=\s*\"\K[0-9]+" /etc/csf/csf.conf 2>/dev/null | head -1)
            [ -z "$current_lf" ] && current_lf="5"

            # Only show recommendation if not already hardened
            if [ "$current_lf" -gt 3 ]; then
                echo -e "${HIGH_COLOR}  ⚠️  SSH Bruteforce ($ssh_attacks attempts) - Strengthen SSH Security${NC}"
                echo -e "${MEDIUM_COLOR}     → Press 'c' for Security Hardening menu${NC}"
                recommendations=1
            fi
        fi
    fi

    if [ "${recommendations:-0}" -eq 0 ]; then
        echo ""
    fi

    # Show different keys based on mode
    if [ "$COMPACT_MODE" -eq 1 ]; then
        echo -e "${INFO_COLOR}  Keys: 'b' Block | 'c' Security | 'v' Verbose | 'r' Refresh | 'q' Quit${NC}"
    else
        echo -e "${INFO_COLOR}  Keys: 'b' Block | 'c' Security | 'v' Compact | 's' Stats | 'q' Quit${NC}"
    fi

    echo -e "${MEDIUM_COLOR}└────────────────────────────────────────────────────────────────────────────┘${NC}"
}

################################################################################
# Quick Action Menu
################################################################################

show_blocking_menu() {
    # Pause monitoring
    local monitoring_paused=1

    clear
    print_banner "Quick IP Blocking"
    echo ""
    echo "Select IPs to block (1-hour temporary ban):"
    echo ""

    # Build array of blockable IPs
    local -a blockable_list=()
    for ip in "${!IP_DATA[@]}"; do
        IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "${IP_DATA[$ip]}"

        # Set defaults for empty values
        score="${score:-0}"
        hits="${hits:-0}"
        attacks="${attacks:-none}"

        # Skip if score too low or already blocked
        [ "$score" -lt 60 ] && continue
        is_ip_blocked "$ip" 2>/dev/null && continue

        blockable_list+=("$ip|$score|$hits|$attacks")
    done

    if [ ${#blockable_list[@]} -eq 0 ]; then
        echo "No IPs meet blocking criteria (score >= 60)"
        echo ""
        read -p "Press Enter to continue..."
        return
    fi

    # Check if any IPs to block
    if [ ${#blockable_list[@]} -eq 0 ]; then
        echo ""
        echo -e "${SAFE_COLOR}No IPs meet blocking criteria (score >= 60 and not already blocked)${NC}"
        echo ""
        read -p "Press Enter to continue..."
        return
    fi

    # Sort by score
    local old_IFS="$IFS"
    IFS=$'\n'
    blockable_list=($(sort -t'|' -k2 -rn <<<"${blockable_list[*]}"))
    IFS="$old_IFS"

    # Display IPs
    local idx=1
    for entry in "${blockable_list[@]}"; do
        IFS='|' read -r ip score hits attacks <<< "$entry" || true
        local level=$(get_threat_level "$score")
        local color=$(get_threat_color "$level")

        printf "${color}  %2d) %-15s  Score:%-3s  Hits:%-5s  Attacks: %s${NC}\n" \
            "$idx" "$ip" "$score" "$hits" "${attacks:-none}"

        ((idx++))
    done

    echo ""
    echo -e "${BOLD}Options:${NC}"
    echo "  1-${#blockable_list[@]}) Block specific IP"
    echo "  a) Block ALL high-threat IPs (score >= 80)"
    echo -e "  ${RED}0)${NC} Back"
    echo ""
    read -p "Select option: " choice

    if [ "$choice" = "0" ]; then
        return
    elif [ "$choice" = "a" ]; then
        # Block all IPs with score >= 80
        local blocked=0
        local failed=0
        for entry in "${blockable_list[@]}"; do
            IFS='|' read -r ip score hits attacks <<< "$entry"
            [ "$score" -lt 80 ] && continue

            echo ""
            if block_ip_temporary "$ip" 1 "Auto-block: High threat (score $score)"; then
                ((blocked++))
            else
                ((failed++))
            fi
        done

        echo ""
        echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
        echo "✓ Successfully blocked: $blocked IPs"
        [ "${failed:-0}" -gt 0 ] && echo "✗ Failed to block: $failed IPs"
        echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
        read -p "Press Enter to continue..."
    elif [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -ge 1 ] && [ "$choice" -le ${#blockable_list[@]} ]; then
        # Block specific IP
        local entry="${blockable_list[$((choice-1))]}"
        IFS='|' read -r ip score hits attacks <<< "$entry"

        echo ""
        block_ip_temporary "$ip" 1 "Manual block from live monitor (score $score)"

        echo ""
        read -p "Press Enter to continue..."
    else
        echo "Invalid option"
        read -p "Press Enter to continue..."
    fi
}

show_security_hardening_menu() {
    clear
    print_banner "Security Hardening & Firewall Optimization"
    echo ""

    # Check if CSF is available
    if ! command -v csf &>/dev/null; then
        echo -e "${HIGH_COLOR}⚠️  CSF/LFD firewall not detected${NC}"
        echo "   Security hardening options require CSF to be installed"
        echo ""
        read -p "Press Enter to return to monitor..."
        return
    fi

    # Check current settings
    local synflood_status=$(grep "^SYNFLOOD\s*=" /etc/csf/csf.conf 2>/dev/null | cut -d'"' -f2)
    local current_lf=$(grep -oP "^LF_SSHD\s*=\s*\"\K[0-9]+" /etc/csf/csf.conf 2>/dev/null | head -1)
    [ -z "$current_lf" ] && current_lf="5"

    echo "Current Security Status:"
    echo ""

    # SYNFLOOD status
    if [ "$synflood_status" = "1" ]; then
        echo -e "  ${SAFE_COLOR}✓${NC} SYNFLOOD Protection: ${BOLD}Enabled${NC}"
    else
        echo -e "  ${HIGH_COLOR}✗${NC} SYNFLOOD Protection: ${BOLD}Disabled${NC}"
    fi

    # SSH hardening status
    if [ "$current_lf" -le 3 ]; then
        echo -e "  ${SAFE_COLOR}✓${NC} SSH Security: ${BOLD}Hardened${NC} (LF_SSHD=$current_lf)"
    else
        echo -e "  ${HIGH_COLOR}✗${NC} SSH Security: ${BOLD}Default${NC} (LF_SSHD=$current_lf, recommend ≤3)"
    fi

    # CT_LIMIT status (basic check)
    local ct_limit=$(grep -oP "^CT_LIMIT\s*=\s*\"\K[0-9]+" /etc/csf/csf.conf 2>/dev/null | head -1)
    if [ -n "$ct_limit" ] && [ "$ct_limit" -gt 0 ]; then
        echo -e "  ${SAFE_COLOR}✓${NC} Connection Tracking: ${BOLD}Configured${NC} (CT_LIMIT=$ct_limit)"
    else
        echo -e "  ${HIGH_COLOR}✗${NC} Connection Tracking: ${BOLD}Not Optimized${NC}"
    fi

    echo ""
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo ""
    echo "Available Hardening Options:"
    echo ""
    echo -e "  ${BOLD}1${NC} - Enable SYNFLOOD Protection (DDoS defense)"
    echo -e "  ${BOLD}2${NC} - Harden SSH Security (Lower LF_SSHD to 3)"
    echo -e "  ${BOLD}3${NC} - Optimize CT_LIMIT (Auto-analyze & apply)"
    echo -e "  ${BOLD}4${NC} - Configure Port Knocking (Coming soon)"
    echo ""
    echo -e "  ${BOLD}a${NC} - Apply All Needed Fixes"
    echo ""
    echo -e "  ${RED}0)${NC} Back"
    echo ""
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    read -p "Select option: " choice
    echo ""

    case "$choice" in
        1)
            if [ "$synflood_status" = "1" ]; then
                echo "✓ SYNFLOOD is already enabled"
                echo ""
                read -p "Press Enter to continue..."
            else
                apply_synflood_fix
            fi
            ;;
        2)
            if [ "$current_lf" -le 3 ]; then
                echo "✓ SSH is already hardened (LF_SSHD=$current_lf)"
                echo ""
                read -p "Press Enter to continue..."
            else
                apply_ssh_hardening
            fi
            ;;
        3)
            clear
            "$SCRIPT_DIR/modules/security/optimize-ct-limit.sh" --auto
            echo ""
            read -p "Press Enter to return to monitor..."
            ;;
        4)
            echo "Port Knocking configuration coming soon..."
            echo ""
            echo "For now, you can manually configure port knocking in CSF:"
            echo "1. Edit /etc/csf/csf.conf"
            echo "2. Set: PORTKNOCKING = \"1\""
            echo "3. Define sequence: PORTKNOCKING_ALERT = \"1\""
            echo "4. Restart: csf -r"
            echo ""
            read -p "Press Enter to continue..."
            ;;
        a|A)
            echo "Applying all needed fixes..."
            echo ""
            local applied=0

            # Apply SYNFLOOD if needed
            if [ "$synflood_status" != "1" ]; then
                apply_synflood_fix
                ((applied++))
            fi

            # Apply SSH hardening if needed
            if [ "$current_lf" -gt 3 ]; then
                apply_ssh_hardening
                ((applied++))
            fi

            # Always offer CT_LIMIT
            echo ""
            echo "Running CT_LIMIT optimizer..."
            "$SCRIPT_DIR/modules/security/optimize-ct-limit.sh" --auto
            ((applied++))

            echo ""
            if [ "${applied:-0}" -gt 0 ]; then
                echo "✓ Applied $applied security fix(es)"
            else
                echo "✓ All security settings already optimized"
            fi
            echo ""
            read -p "Press Enter to return to monitor..."
            ;;
        0)
            return
            ;;
        *)
            echo "Invalid option"
            read -p "Press Enter to continue..."
            ;;
    esac
}

apply_synflood_fix() {
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo "Enabling SYNFLOOD Protection..."
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo ""

    # Check current status
    local current_status=$(grep "^SYNFLOOD\s*=" /etc/csf/csf.conf 2>/dev/null | cut -d'"' -f2)

    if [ "$current_status" = "1" ]; then
        echo "✓ SYNFLOOD protection is already enabled"
    else
        echo "Current setting: SYNFLOOD = \"$current_status\""
        echo "Enabling SYNFLOOD protection..."

        # Backup config
        # CRITICAL FIX: Check if backup succeeds before modifying
        # Bug: If cp fails (no write permission), script continues anyway
        # Result: Original file modified without backup - data loss if something goes wrong
        local backup_file="/etc/csf/csf.conf.bak.$(date +%Y%m%d_%H%M%S)"
        if ! cp /etc/csf/csf.conf "$backup_file" 2>/dev/null; then
            echo "ERROR: Failed to backup /etc/csf/csf.conf to $backup_file"
            echo "Aborting SYNFLOOD configuration to prevent data loss"
            return 1
        fi

        # Enable SYNFLOOD
        sed -i 's/^SYNFLOOD\s*=.*/SYNFLOOD = "1"/' /etc/csf/csf.conf

        # Set reasonable defaults if not already set
        if ! grep -q "^SYNFLOOD_RATE\s*=" /etc/csf/csf.conf; then
            echo 'SYNFLOOD_RATE = "100/s"' >> /etc/csf/csf.conf
        fi
        if ! grep -q "^SYNFLOOD_BURST\s*=" /etc/csf/csf.conf; then
            echo 'SYNFLOOD_BURST = "150"' >> /etc/csf/csf.conf
        fi

        # Restart CSF
        echo ""
        echo "Restarting CSF to apply changes..."
        csf -r >/dev/null 2>&1

        echo ""
        echo "✓ SYNFLOOD protection enabled successfully"
        echo "  Rate limit: 100 connections per second"
        echo "  Burst: 150 connections"
    fi
    echo ""
    read -p "Press Enter to continue..."
}

apply_ssh_hardening() {
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo "Hardening SSH Security..."
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo ""

    # Check current LF_SSHD setting
    local current_lf=$(grep -oP "^LF_SSHD\s*=\s*\"\K[0-9]+" /etc/csf/csf.conf 2>/dev/null | head -1)

    if [ -z "$current_lf" ]; then
        current_lf="5"  # CSF default
    fi

    echo "Current SSH failure threshold: LF_SSHD = \"$current_lf\""

    if [ "$current_lf" -le 3 ]; then
        echo "✓ SSH security is already hardened (threshold ≤ 3)"
    else
        echo "Lowering threshold to 3 failed attempts..."

        # Backup config
        # CRITICAL FIX: Check if backup succeeds before modifying
        # Bug: If cp fails (no write permission), script continues anyway
        # Result: Original file modified without backup - data loss if something goes wrong
        local backup_file="/etc/csf/csf.conf.bak.$(date +%Y%m%d_%H%M%S)"
        if ! cp /etc/csf/csf.conf "$backup_file" 2>/dev/null; then
            echo "ERROR: Failed to backup /etc/csf/csf.conf to $backup_file"
            echo "Aborting SSH hardening configuration to prevent data loss"
            return 1
        fi

        # Update LF_SSHD
        sed -i 's/^LF_SSHD\s*=.*/LF_SSHD = "3"/' /etc/csf/csf.conf

        # Also lower LF_SSHD_PERM if it exists (permanent blocks after X temp blocks)
        if grep -q "^LF_SSHD_PERM\s*=" /etc/csf/csf.conf; then
            sed -i 's/^LF_SSHD_PERM\s*=.*/LF_SSHD_PERM = "3"/' /etc/csf/csf.conf
        fi

        # Restart LFD to apply changes
        echo ""
        echo "Restarting LFD to apply changes..."
        csf -r >/dev/null 2>&1

        echo ""
        echo "✓ SSH security hardened successfully"
        echo "  New threshold: 3 failed attempts before temp block"
        echo "  Block duration: As configured in LF_TRIGGER (default: 1 hour)"
    fi
    echo ""
    read -p "Press Enter to continue..."
}

################################################################################
# Log Monitoring
################################################################################

monitor_apache_logs() {
    # Try multiple log locations based on control panel
    local log_files=()

    # Use system-detected log directory (no fallback)
    local LOG_DIR="${SYS_LOG_DIR}"

    if [ "$SYS_CONTROL_PANEL" = "interworx" ]; then
        # InterWorx: Monitor per-domain access logs
        # Find recent domain logs (modified in last hour for performance, InterWorx uses 'transfer.log')
        while IFS= read -r domain_log; do
            [ -f "$domain_log" ] && log_files+=("$domain_log")
        done < <(find /home/*/var/*/logs -type f -name "transfer.log" -mmin -60 2>/dev/null | head -10)

    elif [ -n "$LOG_DIR" ]; then
        # cPanel/Plesk: Use detected log directory

        # Main access log
        if [ -f "${LOG_DIR}/access_log" ]; then
            log_files+=("${LOG_DIR}/access_log")
        elif [ -f "/var/log/httpd/access_log" ]; then
            log_files+=("/var/log/httpd/access_log")
        elif [ -f "/var/log/apache2/access.log" ]; then
            log_files+=("/var/log/apache2/access.log")
        fi

        # Domain logs
        if [ -d "${LOG_DIR}" ]; then
            # Find recent domain logs (modified in last hour)
            while IFS= read -r domain_log; do
                [ -f "$domain_log" ] && log_files+=("$domain_log")
            done < <(find "${LOG_DIR}" -type f \( -name "*.com" -o -name "*.net" -o -name "*.org" \) -mmin -60 2>/dev/null | head -10)
        fi
    fi

    if [ ${#log_files[@]} -eq 0 ]; then
        # Apache logs not found - skip HTTP monitoring but continue with other monitoring
        # This is non-fatal; other monitors (SYN, SSH, email, etc.) will continue
        echo "[WARNING] No accessible Apache log files found (control panel: ${SYS_CONTROL_PANEL}, log dir: ${LOG_DIR})" >> "$TEMP_DIR/debug.log" 2>/dev/null
        return 0  # Don't fail - let other monitoring continue
    fi

    # Monitor all log files
    local event_count=0
    tail -n 0 -F "${log_files[@]}" 2>/dev/null | while read -r line; do
        # Increment event counter (update file every 10 events for performance)
        ((event_count++))
        if [ $((event_count % 10)) -eq 0 ]; then
            echo "$event_count" > "$TEMP_DIR/event_counter"
        fi

        # Parse Apache combined log format (supports IPv4 and IPv6)
        # Note: bytes field can be - or number, so use [0-9-]+
        if [[ "$line" =~ ^([0-9a-f.:]+)\ -\ -\ \[([^\]]+)\]\ \"([A-Z]+)\ ([^\"]+)\ [^\"]+\"\ ([0-9]+)\ ([0-9-]+)\ \"[^\"]*\"\ \"([^\"]+)\" ]]; then
            local ip="${BASH_REMATCH[1]}"
            local timestamp="${BASH_REMATCH[2]}"
            local method="${BASH_REMATCH[3]}"
            local url="${BASH_REMATCH[4]}"
            local status="${BASH_REMATCH[5]}"
            local bytes="${BASH_REMATCH[6]}"
            local user_agent="${BASH_REMATCH[7]}"

            # Skip local/private IPs and server's own IP
            if [[ "$ip" =~ ^127\. ]] || \
               [[ "$ip" =~ ^10\. ]] || \
               [[ "$ip" =~ ^192\.168\. ]] || \
               [[ "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[01])\. ]] || \
               [[ "$ip" =~ ^169\.254\. ]] || \
               [[ "$ip" == "localhost" ]] || \
               [[ "$ip" == "::1" ]]; then
                continue
            fi

            # Update intelligence
            update_ip_intelligence "$ip" "$url" "$user_agent" "$method"

            # Enhanced attack detection using ET Open signatures
            local et_attack_score=0
            local et_attack_types=""
            local et_signatures=""
            local et_rate_score=0

            if type analyze_http_log_line &>/dev/null; then
                local attack_result=$(analyze_http_log_line "$line" 2>/dev/null)
                if [ -n "$attack_result" ]; then
                    et_attack_score="${attack_result%%||*}"
                    if [ "$et_attack_score" -gt 0 ]; then
                        local temp="${attack_result#*||}"
                        et_attack_types="${temp%%||*}"
                        temp="${temp#*||}"
                        et_signatures="${temp%%||*}"

                        # Update IP intelligence with ET attack info
                        update_ip_intelligence "$ip" "$url|ET:$et_attack_types|$et_signatures" "attack" "HTTP"

                        # Replace IP threat score with ET detection score
                        # Note: We use ET score instead of adding it to avoid double-counting
                        # (update_ip_intelligence already detected the same attack via legacy patterns)
                        local current_intel=$(get_ip_intelligence "$ip")
                        IFS='|' read -r curr_score curr_hits curr_bot curr_attacks curr_ban curr_rep <<< "$current_intel"

                        # Use ET score if it's higher than current score
                        local new_score="$et_attack_score"
                        if [ "$curr_score" -gt "$et_attack_score" ]; then
                            # Keep higher score (e.g., from AbuseIPDB reputation boost)
                            new_score="$curr_score"
                        fi
                        [ "$new_score" -gt 100 ] && new_score=100

                        # Update IP data with ET-based score
                        IP_DATA[$ip]="$new_score|$curr_hits|$curr_bot|$curr_attacks|$curr_ban|$curr_rep"

                        # CRITICAL FIX: Write to file for cross-process communication
                        write_ip_data_to_file "$ip" "$new_score|$curr_hits|$curr_bot|$curr_attacks|$curr_ban|$curr_rep" 2>/dev/null &

                        # CRITICAL: Immediate block for severe threats (RCE, WEBSHELL, etc.)
                        if [[ "$et_attack_types" =~ (RCE|WEBSHELL|ECOMMERCE_EXPLOIT) ]]; then
                            # These are ALWAYS critical - block immediately regardless of score
                            echo "[CRITICAL] INSTANT_BLOCK_RCE | $ip | Score:$et_attack_score | Attacks:$et_attack_types" >> "$TEMP_DIR/recent_events"
                            # BUG FIX: Increment block counter for RCE blocks
                            increment_block_counter 1
                            if type quick_block_ip &>/dev/null; then
                                quick_block_ip "$ip" "CRITICAL_RCE: $et_attack_types" &
                            fi
                        fi

                        # Check rate anomaly
                        if type record_request &>/dev/null && type detect_rate_anomaly &>/dev/null; then
                            record_request "$ip"
                            local rate_result=$(detect_rate_anomaly "$ip" 2>/dev/null)
                            et_rate_score="${rate_result%%||*}"

                            # Combine scores
                            local combined_score=$((et_attack_score + et_rate_score))
                            [ "$combined_score" -gt 100 ] && combined_score=100

                            # Auto-block critical attacks
                            if [ "$combined_score" -ge 90 ]; then
                                echo "[CRITICAL] Auto-blocking $ip (Score: $combined_score, Attacks: $et_attack_types)" >> "$TEMP_DIR/recent_events"
                                if type quick_block_ip &>/dev/null; then
                                    quick_block_ip "$ip" "ET:$et_attack_types" &
                                fi
                            fi
                        fi
                    fi
                fi
            fi

            # Get updated data
            local intel=$(get_ip_intelligence "$ip")
            IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "$intel"

            # Determine if this is a threat
            local level=$(get_threat_level "$score")

            # Log all traffic with attacks, or score > 0, or suspicious bots, or ET detection
            # This ensures we see everything interesting, not just high scores
            if [ "$score" -gt 0 ] || [ -n "$attacks" ] || [ "$bot_type" = "suspicious" ] || [ "$et_attack_score" -gt 0 ]; then
                local color=$(get_threat_color "$level")
                local time_str=$(date +"%H:%M:%S")

                # Use ET score if higher than regular score
                local display_score="$score"
                if [ "$et_attack_score" -gt "$score" ]; then
                    display_score="$et_attack_score"
                    level=$(get_threat_level "$et_attack_score")
                    color=$(get_threat_color "$level")
                fi

                # Build log line
                local log_line="${color}[${time_str}] $ip"
                log_line+=" | Score:$display_score [$level]"

                # Show ET detection if found
                if [ "$et_attack_score" -gt 0 ]; then
                    # Show primary attack type (cleaner than full list)
                    local primary_type=$(echo "$et_attack_types" | grep -oE 'SQLI|XSS|CMD|TRAVERSAL|WEBSHELL|RCE|UPLOAD|CVE' | head -1 2>/dev/null || echo "")
                    if [ -z "$primary_type" ]; then
                        # Bash built-in: Get first field (100x faster than cut)
                        primary_type="${et_attack_types%%,*}"
                    fi
                    log_line+=" | 🛡️ET:$primary_type"

                    # Show signature names (the key improvement!)
                    if [ -n "$et_signatures" ]; then
                        # Limit to first 3 signatures to keep display clean
                        local sig_display=$(echo "$et_signatures" | tr ',' '\n' | head -3 | tr '\n' ',' | sed 's/,$//')
                        log_line+=" | Sigs:$sig_display"
                    fi

                    # Show rate info if elevated
                    if [ "$et_rate_score" -gt 0 ]; then
                        log_line+=" | 🌊Rate:+$et_rate_score"
                    fi
                fi

                # Show bot type if interesting
                if [ "$bot_type" = "suspicious" ] || [ "$bot_type" = "ai" ]; then
                    log_line+=" | Bot:$bot_type"
                fi

                # Show legacy attacks if no ET detection
                if [ -n "$attacks" ] && [ "$et_attack_score" -eq 0 ]; then
                    # Bash built-in: Get first field (100x faster than cut)
                    local first_attack="${attacks%%,*}"
                    local icon=$(get_attack_icon "$first_attack")
                    log_line+=" | $icon$first_attack"
                fi

                log_line+=" | $url${NC}"

                echo -e "$log_line" >> "$TEMP_DIR/recent_events"
            fi
        fi
    done &
}

################################################################################
# Main Loop
################################################################################

################################################################################
# SSH Attack Monitoring
################################################################################

monitor_ssh_attacks() {
    # Monitor SSH brute force attempts from /var/log/secure
    local secure_log="/var/log/secure"

    if [ ! -f "$secure_log" ]; then
        # Try alternative location (Debian/Ubuntu)
        secure_log="/var/log/auth.log"
    fi

    if [ -f "$secure_log" ]; then
        tail -n 0 -F "$secure_log" 2>/dev/null | while read -r line; do
            # Detect failed SSH login attempts (use bash regex for performance)
            if [[ "$line" =~ [Ff]ailed\ password|[Aa]uthentication\ failure|[Ii]nvalid\ user ]]; then
                # Extract IP address using bash regex
                if [[ "$line" =~ ([0-9]{1,3}\.){3}[0-9]{1,3} ]]; then
                    local ip="${BASH_REMATCH[0]}"
                else
                    continue
                fi

                if [ -n "$ip" ]; then
                    # Skip local/private IPs
                    if [[ "$ip" =~ ^127\. ]] || \
                       [[ "$ip" =~ ^10\. ]] || \
                       [[ "$ip" =~ ^192\.168\. ]] || \
                       [[ "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[01])\. ]]; then
                        continue
                    fi

                    # Process as BRUTEFORCE attack
                    # Read from file (subshells can't access IP_DATA array)
                    local ip_file="$TEMP_DIR/ip_${ip//\./_}"
                    local current_data="0|0|human||0|0"
                    if [ -f "$ip_file" ]; then
                        current_data=$(cat "$ip_file")
                    fi
                    IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "$current_data"

                    # Increment hits
                    hits=$((hits + 1))

                    # Record timestamp and vector for intelligence
                    record_attack_timestamp "$ip"
                    record_attack_vector "$ip" "SSH"
                    track_subnet_attack "$ip"

                    # Add BRUTEFORCE to attacks if not already present
                    if [[ ! "$attacks" =~ BRUTEFORCE ]]; then
                        if [ -z "$attacks" ]; then
                            attacks="BRUTEFORCE"
                        else
                            attacks="${attacks},BRUTEFORCE"
                        fi
                        # Update attack type counter for display
                        ((ATTACK_TYPE_COUNTER["BRUTEFORCE"]++))
                    fi

                    # Progressive scoring for bruteforce: Each attempt adds points
                    # First attempt: 10 pts, subsequent attempts: +8 pts each
                    if [ "${hits:-0}" -eq 1 ]; then
                        score=10
                    else
                        score=$((score + 8))
                    fi

                    # Apply advanced intelligence bonuses
                    local block_reasons=""

                    # 1. Attack velocity bonus
                    local velocity_data=$(calculate_attack_velocity "$ip")
                    IFS='|' read -r vel_count vel_bonus vel_reason <<< "$velocity_data"
                    if [ "$vel_bonus" -gt 0 ]; then
                        score=$((score + vel_bonus))
                        block_reasons="${vel_reason}"
                    fi

                    # 2. Diversity bonus (multi-vector attack)
                    local div_data=$(calculate_diversity_bonus "$ip")
                    IFS='|' read -r div_count div_bonus div_reason <<< "$div_data"
                    if [ "$div_bonus" -gt 0 ]; then
                        score=$((score + div_bonus))
                        [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                        block_reasons="${block_reasons}${div_reason}"
                    fi

                    # 3. Timing pattern detection
                    local pattern_data=$(detect_timing_pattern "$ip")
                    IFS='|' read -r pat_type pat_conf pat_bonus pat_reason <<< "$pattern_data"
                    if [ "$pat_bonus" -gt 0 ]; then
                        score=$((score + pat_bonus))
                        [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                        block_reasons="${block_reasons}${pat_reason}"
                    fi

                    # 4. Subnet attack bonus
                    local subnet_data=$(calculate_subnet_bonus "$ip")
                    IFS='|' read -r subnet_count subnet_bonus subnet_reason <<< "$subnet_data"
                    if [ "$subnet_bonus" -gt 0 ]; then
                        score=$((score + subnet_bonus))
                        [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                        block_reasons="${block_reasons}${subnet_reason}"
                    fi

                    # 5. Context-aware bonus (geo, ISP, time)
                    local context_data=$(calculate_context_bonus "$ip")
                    IFS='|' read -r context_bonus context_reason <<< "$context_data"
                    if [ "$context_bonus" -gt 0 ]; then
                        score=$((score + context_bonus))
                        [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                        block_reasons="${block_reasons}${context_reason}"
                    fi

                    # Cap at 100
                    [ "${score:-0}" -gt 100 ] && score=100

                    # Update ip_data file directly (subshells can't access IP_DATA array)
                    local ip_file="$TEMP_DIR/ip_${ip//\./_}"
                    echo "$score|$hits|$bot_type|$attacks|$ban_count|$rep_score" > "$ip_file"

                    # Store block reasons for CSF
                    if [ -n "$block_reasons" ]; then
                        echo "$block_reasons" > "$TEMP_DIR/block_reason_${ip//\./_}"
                    fi

                    # Log to reputation DB
                    flag_ip_attack "$ip" "BRUTEFORCE" 0 "SSH failed login attempt" >/dev/null 2>&1 &

                    # Log event
                    local time_str=$(date +"%H:%M:%S")
                    local level=$(get_threat_level "$score")
                    local color=$(get_threat_color "$level")
                    local icon=$(get_attack_icon "BRUTEFORCE")

                    echo -e "${color}[${time_str}] $ip | Score:$score [$level] | ${icon}SSH_BRUTEFORCE | Hits:$hits${NC}" >> "$TEMP_DIR/recent_events"
                fi
            fi
        done &
    fi
}

################################################################################
# Firewall Block Monitoring
################################################################################

monitor_firewall_blocks() {
    # Monitor CSF/iptables blocks in real-time from /var/log/messages
    local messages_log="/var/log/messages"

    if [ ! -f "$messages_log" ]; then
        # Try alternative location
        messages_log="/var/log/syslog"
    fi

    if [ -f "$messages_log" ]; then
        tail -n 0 -F "$messages_log" 2>/dev/null | while read -r line; do
            # Detect firewall blocks (use bash regex for performance)
            if [[ "$line" =~ [Ff]irewall|iptables.*(DENY|DROP)|CSF.*block ]]; then
                # Extract IP address using bash regex
                if [[ "$line" =~ ([0-9]{1,3}\.){3}[0-9]{1,3} ]]; then
                    local ip="${BASH_REMATCH[0]}"
                else
                    continue
                fi

                if [ -n "$ip" ]; then
                    # Skip local/private IPs
                    if [[ "$ip" =~ ^127\. ]] || \
                       [[ "$ip" =~ ^10\. ]] || \
                       [[ "$ip" =~ ^192\.168\. ]] || \
                       [[ "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[01])\. ]]; then
                        continue
                    fi

                    # Log firewall block
                    local time_str=$(date +"%H:%M:%S")
                    echo -e "${LOW_COLOR}[${time_str}] $ip | FIREWALL_BLOCK | Blocked by firewall${NC}" >> "$TEMP_DIR/recent_events"
                    # BUG FIX: Increment block counter when block is detected
                    increment_block_counter 1
                fi
            fi
        done &
    fi
}

################################################################################
# cPHulk Monitoring
################################################################################

monitor_cphulk_blocks() {
    # Monitor cPHulk blocks (cPanel security system - cPanel ONLY)
    # Skip if not cPanel
    if [ "$SYS_CONTROL_PANEL" != "cpanel" ]; then
        return 0
    fi

    if [ -x "/usr/local/cpanel/bin/cphulk_pam_ctl" ] || command -v whmapi1 &>/dev/null; then
        (
            declare -A SEEN_BLOCKS
            while true; do
                # Query cPHulk for blocked IPs
                whmapi1 cphulkd_list_blocks 2>/dev/null | grep -E "ip:" | while read -r line; do
                    local ip=$(echo "$line" | awk '{print $2}')

                    if [ -n "$ip" ] && [ -z "${SEEN_BLOCKS[$ip]}" ]; then
                        SEEN_BLOCKS[$ip]=1

                        # Skip local/private IPs
                        if [[ "$ip" =~ ^127\. ]] || \
                           [[ "$ip" =~ ^10\. ]] || \
                           [[ "$ip" =~ ^192\.168\. ]] || \
                           [[ "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[01])\. ]]; then
                            continue
                        fi

                        # Process as BRUTEFORCE attack (cPHulk blocks login attempts)
                        local current_data="${IP_DATA[$ip]:-0|0|human||0|0}"
                        IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "$current_data"

                        # Add BRUTEFORCE to attacks
                        if [[ ! "$attacks" =~ BRUTEFORCE ]]; then
                            if [ -z "$attacks" ]; then
                                attacks="BRUTEFORCE"
                            else
                                attacks="${attacks},BRUTEFORCE"
                            fi
                        fi

                        # Calculate score
                        score=$(calculate_attack_score "$attacks")
                        hits=$((hits + 1))

                        # Update IP_DATA
                        IP_DATA[$ip]="$score|$hits|$bot_type|$attacks|$ban_count|$rep_score"

                        # CRITICAL FIX: Write to file for cross-process communication
                        write_ip_data_to_file "$ip" "$score|$hits|$bot_type|$attacks|$ban_count|$rep_score" 2>/dev/null &

                        # Log event
                        local time_str=$(date +"%H:%M:%S")
                        local level=$(get_threat_level "$score")
                        local color=$(get_threat_color "$level")

                        echo -e "${color}[${time_str}] $ip | Score:$score [$level] | 🔐CPHULK_BLOCK | Blocked by cPHulk${NC}" >> "$TEMP_DIR/recent_events"
                        # BUG FIX: Increment block counter for cPHulk blocks
                        increment_block_counter 1
                    fi
                done
                sleep 10  # Poll every 10 seconds
            done
        ) &
    fi
}

################################################################################
# Network Attack Monitoring (SYN floods, port scans, DDoS)
################################################################################

monitor_network_attacks() {
    # Monitor kernel logs and network statistics for SYN floods, port scans, etc.
    local kern_log="/var/log/kern.log"

    # Try different log locations
    if [ ! -f "$kern_log" ]; then
        kern_log="/var/log/messages"
    fi

    # Monitor kernel/firewall logs for network attacks
    if [ -f "$kern_log" ]; then
        tail -n 0 -F "$kern_log" 2>/dev/null | while read -r line; do
            # Detect SYN flood patterns (use bash regex for performance)
            if [[ "$line" =~ SYN\ flood|possible\ SYN\ flooding|TCP:\ Possible\ SYN\ flooding ]]; then
                # Extract IP address using bash regex
                if [[ "$line" =~ ([0-9]{1,3}\.){3}[0-9]{1,3} ]]; then
                    local ip="${BASH_REMATCH[0]}"
                else
                    continue
                fi

                if [ -n "$ip" ]; then
                    # Skip local/private IPs
                    if [[ "$ip" =~ ^127\. ]] || \
                       [[ "$ip" =~ ^10\. ]] || \
                       [[ "$ip" =~ ^192\.168\. ]] || \
                       [[ "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[01])\. ]]; then
                        continue
                    fi

                    # Process as DDOS attack
                    local current_data="${IP_DATA[$ip]:-0|0|human||0|0}"
                    IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "$current_data"

                    # Add DDOS to attacks
                    if [[ ! "$attacks" =~ DDOS ]]; then
                        if [ -z "$attacks" ]; then
                            attacks="DDOS"
                        else
                            attacks="${attacks},DDOS"
                        fi
                    fi

                    # Calculate score (DDOS is high severity)
                    score=$(calculate_attack_score "$attacks")
                    hits=$((hits + 1))

                    # Update IP_DATA
                    IP_DATA[$ip]="$score|$hits|$bot_type|$attacks|$ban_count|$rep_score"

                    # CRITICAL FIX: Write to file for cross-process communication
                    write_ip_data_to_file "$ip" "$score|$hits|$bot_type|$attacks|$ban_count|$rep_score" 2>/dev/null &

                    # Log to reputation DB
                    flag_ip_attack "$ip" "DDOS" 0 "SYN flood detected" >/dev/null 2>&1 &

                    # Log event
                    local time_str=$(date +"%H:%M:%S")
                    local level=$(get_threat_level "$score")
                    local color=$(get_threat_color "$level")

                    echo -e "${color}[${time_str}] $ip | Score:$score [$level] | 💥SYN_FLOOD | Network attack${NC}" >> "$TEMP_DIR/recent_events"
                fi
            fi

            # Detect port scan attempts (use bash regex for performance)
            if [[ "$line" =~ port.*scan|stealth\ scan|SYN-FIN\ scan|NULL\ scan ]]; then
                # Extract IP address using bash regex
                if [[ "$line" =~ ([0-9]{1,3}\.){3}[0-9]{1,3} ]]; then
                    local ip="${BASH_REMATCH[0]}"
                else
                    continue
                fi

                if [ -n "$ip" ]; then
                    # Skip local/private IPs
                    if [[ "$ip" =~ ^127\. ]] || \
                       [[ "$ip" =~ ^10\. ]] || \
                       [[ "$ip" =~ ^192\.168\. ]] || \
                       [[ "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[01])\. ]]; then
                        continue
                    fi

                    # Process as SCANNER attack
                    local current_data="${IP_DATA[$ip]:-0|0|human||0|0}"
                    IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "$current_data"

                    # Add PORT_SCAN to attacks (using ADMIN_PROBE for now - 5 points)
                    if [[ ! "$attacks" =~ ADMIN_PROBE ]]; then
                        if [ -z "$attacks" ]; then
                            attacks="ADMIN_PROBE"
                        else
                            attacks="${attacks},ADMIN_PROBE"
                        fi
                    fi

                    # Calculate score
                    score=$(calculate_attack_score "$attacks")
                    hits=$((hits + 1))

                    # Update IP_DATA
                    IP_DATA[$ip]="$score|$hits|$bot_type|$attacks|$ban_count|$rep_score"

                    # CRITICAL FIX: Write to file for cross-process communication
                    write_ip_data_to_file "$ip" "$score|$hits|$bot_type|$attacks|$ban_count|$rep_score" 2>/dev/null &

                    # Log event
                    local time_str=$(date +"%H:%M:%S")
                    local level=$(get_threat_level "$score")
                    local color=$(get_threat_color "$level")

                    echo -e "${color}[${time_str}] $ip | Score:$score [$level] | 🔎PORT_SCAN | Network reconnaissance${NC}" >> "$TEMP_DIR/recent_events"
                fi
            fi
        done &
    fi

    # Monitor netstat for high connection counts (possible DDoS)
    if command -v netstat &>/dev/null || command -v ss &>/dev/null; then
        (
            declare -A CONNECTION_COUNT
            declare -A ALERT_SENT
            local ss_cache=""
            local ss_cache_time=0
            while true; do
                # Use ss if available (faster), otherwise netstat
                if command -v ss &>/dev/null; then
                    # PERFORMANCE: Cache ss output during high-severity attacks
                    # During Tier 3+ attacks, cache for 5 seconds to reduce CPU usage by 50%
                    local current_time=$(date +%s 2>/dev/null || echo "${ss_cache_time:-0}")
                    local cache_age=$((${current_time:-0} - ${ss_cache_time:-0}))

                    # Refresh cache if: (1) no cache, (2) cache > 5s old, (3) not in attack (always fresh)
                    local prev_severity="${ATTACK_SEVERITY:-0}"
                    if [ -z "$ss_cache" ] || [ "$cache_age" -gt 5 ] || [ "${prev_severity}" -lt 3 ]; then
                        ss_cache=$(ss -tn state syn-recv 2>/dev/null)
                        ss_cache_time=$current_time
                    fi

                    # Get total SYN_RECV count from cache
                    # CRITICAL FIX: Subtract 1 to exclude header line "Recv-Q Send-Q Local Address:Port Peer Address:Port"
                    # Bug: wc -l was counting header + data lines, causing false severity = 0 when connections < 75
                    # Result: 40 real connections + header = 41 lines, 41 < 75, so severity stays 0, threshold stays 20
                    # Fix: Skip the first line (header) to get accurate connection count
                    local total_syn=$(($(echo "$ss_cache" | wc -l) - 1))
                    [ "$total_syn" -lt 0 ] && total_syn=0  # Handle case where ss_cache is empty/only header
                    local attack_severity=0
                    local unique_ips=0

                    # Multi-tier distributed DDoS detection with adaptive learning
                    # CRITICAL FIX: Use >= not > to include boundary values
                    # Bug: total_syn=500 was severity 0 instead of 4 (off-by-one)
                    if [ "$total_syn" -ge 500 ]; then
                        attack_severity=4  # Critical DDoS (new tier)
                    elif [ "$total_syn" -ge 300 ]; then
                        attack_severity=3  # Severe DDoS
                    elif [ "$total_syn" -ge 150 ]; then
                        attack_severity=2  # Major DDoS
                    elif [ "$total_syn" -ge 75 ]; then
                        attack_severity=1  # Moderate DDoS
                    fi
                    ATTACK_SEVERITY=$attack_severity  # Store for next iteration

                    # Attack momentum tracking: Check if attack is growing
                    local prev_total="${PREV_TOTAL_SYN:-0}"
                    local attack_momentum=0
                    if [ "$total_syn" -gt "$prev_total" ] && [ "$prev_total" -gt 0 ]; then
                        local growth=$((total_syn - prev_total))
                        if [ "$growth" -gt 100 ]; then
                            attack_momentum=2  # Rapidly accelerating
                        elif [ "$growth" -gt 30 ]; then
                            attack_momentum=1  # Accelerating
                        fi
                    fi
                    PREV_TOTAL_SYN=$total_syn

                    # Count unique attacker IPs and track /24 subnets (use cached data)
                    declare -A subnet_counts
                    local attacker_ips=$(echo "$ss_cache" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | sort -u)
                    while IFS= read -r attacker_ip; do
                        [ -z "$attacker_ip" ] && continue
                        ((unique_ips++))

                        # Track /24 subnets to detect coordinated attacks
                        local subnet="${attacker_ip%.*}"  # Bash built-in (100x faster)
                        ((subnet_counts[$subnet]++))
                    done <<< "$attacker_ips"

                    # Coordinated botnet detection: 3+ IPs from same /24
                    local coordinated_attack=0
                    declare -A hostile_subnets
                    for subnet in "${!subnet_counts[@]}"; do
                        if [ "${subnet_counts[$subnet]}" -ge 3 ]; then
                            coordinated_attack=1
                            hostile_subnets[$subnet]=${subnet_counts[$subnet]}
                        fi
                    done

                    # Subnet-level auto-blocking for severe attacks
                    # If attack_severity >= 3 AND subnet has 10+ attacking IPs, block entire /24
                    if [ "$attack_severity" -ge 3 ]; then
                        for subnet in "${!hostile_subnets[@]}"; do
                            local subnet_ip_count=${hostile_subnets[$subnet]}
                            if [ "$subnet_ip_count" -ge 10 ]; then
                                # Block entire /24 subnet via IPset (PRIORITY) then CSF
                                local subnet_cidr="${subnet}.0/24"
                                if ! grep -q "^${subnet_cidr}\$" "$TEMP_DIR/blocked_subnets" 2>/dev/null; then
                                    echo "$subnet_cidr" >> "$TEMP_DIR/blocked_subnets"
                                    (
                                        # PRIORITY: Add to IPset for instant kernel-level blocking
                                        if [ "$IPSET_AVAILABLE" -eq 1 ]; then
                                            ipset add "$IPSET_NAME" "$subnet_cidr" -exist 2>/dev/null
                                        fi
                                        # CSF for persistent management (runs in background after IPset)
                                        if command -v csf &>/dev/null; then
                                            csf -d "$subnet_cidr" "SUBNET_DDOS:${subnet_ip_count}IPs" 2>/dev/null
                                        fi
                                    ) &
                                    local time_str=$(date +"%H:%M:%S")
                                    echo -e "${CRITICAL_COLOR}[${time_str}] SUBNET_BLOCK | $subnet_cidr | IPs:${subnet_ip_count} | Severity:${attack_severity}${NC}" >> "$TEMP_DIR/recent_events"
                                    # BUG FIX: Increment block counter when subnet block is detected
                                    increment_block_counter 1
                                fi
                            fi
                        done
                    fi

                    # Count SYN_RECV connections per IP (sign of SYN flood)
                    while read -r ip count; do
                        # Skip local/private IPs first
                        if [[ "$ip" =~ ^127\. ]] || \
                           [[ "$ip" =~ ^10\. ]] || \
                           [[ "$ip" =~ ^192\.168\. ]] || \
                           [[ "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[01])\. ]]; then
                            continue
                        fi

                        # CRITICAL FIX: Don't update CONNECTION_COUNT here yet
                        # Bug: Previously updated array BEFORE using it for escalation detection
                        # Result: prev_count would equal current count (both just set), escalation detection always false
                        # Fix: Read previous value first (line 2876), then update after scoring (line 2886+)
                        # Save old value before updating - needed for escalation detection
                        local prev_count="${CONNECTION_COUNT[$ip]:-0}"

                        # Load IP's persistent data FIRST (before threshold calculation)
                        # This gets the current lifetime hits count from ip_data
                        local current_data="0|0|human||0|0"
                        if [ -f "$TEMP_DIR/ip_data" ]; then
                            current_data=$(grep "^${ip}=" "$TEMP_DIR/ip_data" 2>/dev/null | cut -d= -f2 || echo "0|0|human||0|0")
                        fi
                        IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "$current_data"

                        # Dynamic threshold based on attack severity + momentum:
                        # CRITICAL FIX: Changed Tier 0 threshold from 20 to 3
                        # Bug: Tier 0 (< 75 total SYN) had threshold=20, preventing detection of distributed attacks
                        # With 8-41 total connections spread across IPs, no single IP reaches 20, so ZERO detection
                        # Fix: Lower Tier 0 to 3 to detect any suspicious SYN activity
                        # Tier 0: >3 connections (low-level activity, may be distributed)
                        # Tier 1: >10 connections (75-150 total, moderate DDoS)
                        # Tier 2: >6 connections (150-300 total, major DDoS)
                        # Tier 3: >4 connections (300-500 total, severe DDoS)
                        # Tier 4: >3 connections (500+ total, CRITICAL DDoS)
                        local threshold=3
                        case "$attack_severity" in
                            4) threshold=3 ;;  # Critical: Very aggressive (safe for production)
                            3) threshold=4 ;;  # Severe: Aggressive
                            2) threshold=6 ;;  # Major: Balanced
                            1) threshold=10 ;;  # Moderate: Conservative
                        esac

                        # Attack momentum adaptation: Lower threshold if attack is growing
                        if [ "$attack_momentum" -eq 2 ] && [ "$threshold" -gt 3 ]; then
                            threshold=$((threshold - 2))  # Rapidly accelerating attack
                        elif [ "$attack_momentum" -eq 1 ] && [ "$threshold" -gt 3 ]; then
                            threshold=$((threshold - 1))  # Accelerating attack
                        fi

                        # Coordinated attack bonus: Lower threshold by 1 (stacks with momentum)
                        if [ "$coordinated_attack" -eq 1 ] && [ "$threshold" -gt 3 ]; then
                            threshold=$((threshold - 1))
                        fi

                        # Minimum threshold of 3 to prevent false positives on busy web servers
                        [ "$threshold" -lt 3 ] && threshold=3

                        # CRITICAL FIX: Adaptive threshold based on LIFETIME detection history
                        # Use persistent hits from ip_data (central database) - survives monitor restarts
                        # An IP that attacks 5-10 times over days should be detected at lower threshold
                        # This catches distributed/low-level probes that space out attempts over time
                        # NOTE: hits variable now loaded from persistent ip_data storage
                        local lifetime_hits="${hits:-0}"
                        if [ "$lifetime_hits" -ge 10 ]; then
                            threshold=1  # Seen 10+ times across ALL TIME: auto-block even 1 connection
                            [ "$threshold" -lt 1 ] && threshold=1
                        elif [ "$lifetime_hits" -ge 5 ]; then
                            threshold=$((threshold - 2))  # 5-9 times: lower threshold by 2 (from 3 to 1)
                            [ "$threshold" -lt 1 ] && threshold=1
                        elif [ "$lifetime_hits" -ge 3 ]; then
                            threshold=$((threshold - 1))  # 3-4 times: lower threshold by 1
                            [ "$threshold" -lt 2 ] && threshold=2
                        elif [ "$lifetime_hits" -ge 2 ]; then
                            threshold=$((threshold - 1))  # 2 times: lower threshold slightly
                            [ "$threshold" -lt 2 ] && threshold=2
                        fi

                        if [ "$count" -gt "$threshold" ]; then
                            # Only process once per detection window
                            if [ -z "${ALERT_SENT[$ip]}" ]; then
                                ALERT_SENT[$ip]=1

                                # Define ip_file for this IP's individual tracking file
                                local ip_file="$TEMP_DIR/ip_${ip//\./_}"

                                # Smart whitelisting: Skip SCORING for IPs with MANY successful established connections
                                # But still track them - don't skip the write!
                                # Only whitelist if IP has 20+ established connections (highly unlikely for attacker)
                                # CRITICAL FIX: Use -w flag to match whole word (prevent partial IP matches)
                                # Example: "1.1.1.1" should not match "11.1.1.1" or "119.1.1.1"
                                local established_conns=$(ss -tn state established 2>/dev/null | grep -w "$ip" | wc -l)
                                [ -z "$established_conns" ] && established_conns=0
                                local skip_scoring=0
                                if [ "$established_conns" -ge 20 ]; then
                                    # IP has 20+ established connections = highly likely legitimate user
                                    # Skip scoring but STILL write/track (for historical hits)
                                    skip_scoring=1
                                fi

                                # Check if whitelisted service
                                # CRITICAL FIX: Changed hits check from -eq 1 to -eq 0
                                # Bug: hits=0 means NEW IP (first detection), hits=1 means repeat detection
                                # Whitelist should only be checked on FIRST detection (hits=0), not repeat
                                # Previous: only checked on 2nd+ detection, causing false alerts on initial detection
                                if [ "$skip_scoring" -eq 0 ] && [ "${hits:-0}" -eq 0 ]; then
                                    # Only check whitelist on first detection, and only if not already skipped
                                    if is_whitelisted_service "$ip" 2>/dev/null; then
                                        skip_scoring=1  # Skip scoring but STILL write/track
                                    fi
                                fi

                                # Enhanced threat intelligence on first detection
                                # CRITICAL FIX: Changed hits check from -eq 1 to -eq 0
                                # Only query threat intelligence on FIRST detection to avoid redundant API calls
                                # CRITICAL FIX #2: Moved reputation bonus calculation OUT of background subshell
                                # Bug: Bonuses were calculated in background and written to $ip_file, but never added to final score
                                # Fix: Calculate bonuses synchronously and add directly to $score variable
                                local threat_intel_bonus=0
                                if [ "$skip_scoring" -eq 0 ] && [ "${hits:-0}" -eq 0 ]; then

                                    local threat_intel=$(get_threat_intelligence "$ip" 2>/dev/null)
                                    IFS='|' read -r abuse_conf abuse_rpts country isp geo timing whitelisted <<< "$threat_intel"

                                    # Store enrichment for later use
                                    echo "$threat_intel" > "$TEMP_DIR/threat_enrich_${ip//\./_}"

                                    # Geographic clustering detection (still in background to avoid blocking)
                                    (
                                        # Check country/ASN clustering
                                        if [ -n "$geo" ] && [ "$geo" != "XX" ]; then
                                            echo "$geo" >> "$TEMP_DIR/attack_countries"
                                            local country_count=$(grep -c "^${geo}$" "$TEMP_DIR/attack_countries" 2>/dev/null || echo "0")
                                            if [ "$country_count" -ge 5 ]; then
                                                echo "$geo" >> "$TEMP_DIR/hostile_countries"
                                            fi
                                        fi

                                        # ASN clustering detection
                                        if [ -n "$isp" ]; then
                                            local asn=$(echo "$isp" | grep -oP 'AS\K\d+' 2>/dev/null | head -1 2>/dev/null || echo "")
                                            if [ -n "$asn" ]; then
                                                echo "$asn" >> "$TEMP_DIR/attack_asns"
                                                local asn_count=$(grep -c "^${asn}$" "$TEMP_DIR/attack_asns" 2>/dev/null || echo "0")
                                                if [ "$asn_count" -ge 3 ]; then
                                                    echo "$asn" >> "$TEMP_DIR/hostile_asns"
                                                fi
                                            fi
                                        fi
                                    ) &

                                    # Calculate reputation bonuses NOW (synchronously) so they get added to score
                                    # Apply reputation boosts based on AbuseIPDB
                                    if [ "${abuse_conf:-0}" -ge 75 ]; then
                                        # High confidence malicious - add 30 points
                                        threat_intel_bonus=30
                                    elif [ "${abuse_conf:-0}" -ge 50 ]; then
                                        # Medium confidence - add 15 points
                                        threat_intel_bonus=15
                                    fi

                                    # High-risk country adds 5 points
                                    if is_high_risk_country "${geo:-XX}" 2>/dev/null; then
                                        threat_intel_bonus=$((threat_intel_bonus + 5))
                                    fi
                                fi

                                # Reputation pre-boost: IPs with existing HTTP attacks get higher SYN scoring
                                local http_attack_bonus=0
                                if [[ "$attacks" =~ (SQLI|XSS|RCE|LFI|RFI|WEBSHELL|XXE|SSRF) ]]; then
                                    http_attack_bonus=25  # Already known attacker, very suspicious
                                fi

                                # CRITICAL FIX: Declare variables before skip_scoring block
                                # Bug: multi_vector, geo_bonus, ratio, target_ports, and has_other_traffic
                                #      were declared inside skip_scoring but used outside in intel_tags logic
                                # When skip_scoring=1, local vars never initialized, causing undefined variable errors
                                # Fix: Move declarations outside skip_scoring so they're always available
                                local multi_vector=0
                                local geo_bonus=0
                                local ratio=0
                                local target_ports=0
                                local has_other_traffic=0

                                # Only do scoring/tracking if not whitelisted
                                if [ "$skip_scoring" -eq 0 ]; then
                                    # Record attack intelligence
                                    record_attack_timestamp "$ip"
                                    record_attack_vector "$ip" "NETWORK"
                                    track_subnet_attack "$ip"

                                    # Add SYN_FLOOD to attacks if not already present
                                    if [[ ! "$attacks" =~ SYN_FLOOD ]]; then
                                        [ -z "$attacks" ] && attacks="SYN_FLOOD" || attacks="${attacks},SYN_FLOOD"
                                    fi

                                    # CRITICAL FIX: Fixed indentation - these lines should be INSIDE skip_scoring check
                                    # Bug: Scoring calculations were outside the if block, still running for whitelisted IPs
                                    # Progressive scoring based on connection count
                                    # 20-50 conns: +15 pts, 50-100: +25 pts, 100+: +40 pts
                                    local conn_bonus=0
                                    if [ "$count" -ge 100 ]; then
                                        conn_bonus=40
                                    elif [ "$count" -ge 50 ]; then
                                        conn_bonus=25
                                    else
                                        conn_bonus=15
                                    fi

                                    # Distributed attack severity bonus
                                    # Higher severity = more dangerous, boost scores
                                    # Tier 4 (500+ SYN) is extreme - should auto-block immediately
                                    case "$attack_severity" in
                                        4) conn_bonus=$((conn_bonus + 50)) ;;  # Critical DDoS (INSTANT BLOCK)
                                        3) conn_bonus=$((conn_bonus + 30)) ;;  # Severe DDoS
                                        2) conn_bonus=$((conn_bonus + 15)) ;;  # Major DDoS
                                        1) conn_bonus=$((conn_bonus + 8)) ;;   # Moderate DDoS
                                    esac

                                    # Attack momentum bonus (growing attack = more dangerous)
                                    if [ "$attack_momentum" -eq 2 ]; then
                                        conn_bonus=$((conn_bonus + 15))  # Rapidly accelerating
                                    elif [ "$attack_momentum" -eq 1 ]; then
                                        conn_bonus=$((conn_bonus + 8))   # Accelerating
                                    fi

                                    # SYN FLOOD SPECIFIC INTELLIGENCE METRICS

                                    # 1. Pure SYN attacker (no ESTABLISHED connections)
                                    # Legitimate users always have some established connections
                                    # Pure SYN = 100% attack traffic
                                    if [ "$established_conns" -eq 0 ] && [ "$count" -ge 5 ]; then
                                        conn_bonus=$((conn_bonus + 20))  # Pure SYN flood, no legitimate traffic
                                    fi

                                    # 2. SYN/ESTABLISHED ratio detection
                                    # Normal: More ESTABLISHED than SYN_RECV
                                    # Attacker: More SYN_RECV than ESTABLISHED (or 0 established)
                                    # Note: ratio declared outside skip_scoring block (line ~2755) for scope
                                    if [ "$established_conns" -gt 0 ]; then
                                        # Calculate ratio (multiply by 10 for integer math)
                                        ratio=$((count * 10 / established_conns))
                                        if [ "$ratio" -ge 30 ]; then
                                            conn_bonus=$((conn_bonus + 15))  # 3:1 ratio = suspicious
                                        elif [ "$ratio" -ge 20 ]; then
                                            conn_bonus=$((conn_bonus + 10))  # 2:1 ratio = questionable
                                        fi
                                    fi

                                    # 3. Connection persistence without completion
                                    # Check if IP has been seen before with SYN but never completed
                                    if [ "${hits:-0}" -ge 2 ] && [ "$established_conns" -eq 0 ]; then
                                        conn_bonus=$((conn_bonus + 15))  # Repeated SYN, never establishes = bot
                                    fi

                                    # 4. Spoofed source detection (high SYN, low other traffic)
                                    # Check if IP has ANY other traffic (HTTP requests, DNS, etc)
                                    # CRITICAL FIX: Use already-loaded $attacks variable from ip_data (line 2597)
                                    # Bug: was trying to read from individual ip_* file which may not exist
                                    # If this is first SYN detection of an IP with prior HTTP attacks, file won't exist
                                    # Result: has_other_traffic stays 0, missing indicator of multi-attack IP
                                    # Note: has_other_traffic declared outside skip_scoring block (line ~2760) for scope
                                    # If has HTTP attacks in history, not spoofed
                                    if [[ "$attacks" =~ (SQLI|XSS|BRUTE|SCAN) ]]; then
                                        has_other_traffic=1
                                    fi

                                    # High SYN but no other traffic = likely spoofed source
                                    if [ "$has_other_traffic" -eq 0 ] && [ "$count" -ge 10 ] && [ "${hits:-0}" -ge 2 ]; then
                                        conn_bonus=$((conn_bonus + 20))  # Spoofed source IP
                                    fi

                                    # 5. Single-target focus detection
                                    # Botnet usually targets one service/port
                                    # Check if connections are all to same port (80/443)
                                    # CRITICAL FIX: Quote the ss EXPRESSION filter for correct syntax
                                    # Bug: Unquoted 'src "$ip"' was treated as separate arguments, not a filter expression
                                    # Result: ss silently ignores the filter and returns ALL syn-recv (giving wrong port count)
                                    # Fix: Quote the expression so ss parses it correctly: 'src IP'
                                    # Note: target_ports declared outside skip_scoring block (line ~2760) for scope
                                    target_ports=$(ss -tn "state syn-recv src $ip" 2>/dev/null | grep -oP ':\d+\s+' | sort -u | wc -l)
                                    [ -z "$target_ports" ] && target_ports=0
                                    if [ "$target_ports" -eq 1 ] && [ "$count" -ge 8 ]; then
                                        conn_bonus=$((conn_bonus + 10))  # Single port = targeted attack
                                    elif [ "$target_ports" -le 2 ] && [ "$count" -ge 15 ]; then
                                        conn_bonus=$((conn_bonus + 5))   # 1-2 ports = focused attack
                                    fi

                                    # Multi-vector attack detection: Check if IP also has HTTP attacks
                                    # This indicates sophisticated attacker (SYN flood + application layer)
                                    # CRITICAL FIX: Use already-loaded $attacks variable from ip_data (line 2597)
                                    # Bug: was trying to read from individual ip_* file which may not exist
                                    # If this is first SYN detection of an IP with prior HTTP attacks, file won't exist
                                    # Result: multi_vector stays 0, missing the sophisticated attacker indicator
                                    # Note: multi_vector declared outside skip_scoring block (line ~2755) for scope
                                    if [[ "$attacks" =~ (SQLI|XSS|RCE|LFI|RFI|WEBSHELL) ]]; then
                                        multi_vector=1
                                        conn_bonus=$((conn_bonus + 30))  # Multi-vector = very dangerous
                                    fi

                                    # Connection persistence bonus (repeated detections of same IP)
                                    # This indicates sustained attack vs transient spike
                                    if [ "${hits:-0}" -ge 5 ]; then
                                        conn_bonus=$((conn_bonus + 20))  # Persistent attacker
                                    elif [ "${hits:-0}" -ge 3 ]; then
                                        conn_bonus=$((conn_bonus + 10))  # Repeated attack
                                    fi

                                    # Connection escalation detection
                                    # Check if connection count is increasing (more aggressive attack)
                                    # prev_count was loaded at line 2590 (BEFORE updating CONNECTION_COUNT)
                                    if [ "$count" -gt "$prev_count" ] && [ "$prev_count" -gt 0 ]; then
                                        local increase=$((count - prev_count))
                                        if [ "$increase" -ge 50 ]; then
                                            conn_bonus=$((conn_bonus + 25))  # Rapidly escalating
                                        elif [ "$increase" -ge 20 ]; then
                                            conn_bonus=$((conn_bonus + 15))  # Escalating
                                        fi
                                    fi

                                    # NOW update CONNECTION_COUNT after escalation detection
                                    # Store current count for next monitoring cycle comparison
                                    CONNECTION_COUNT[$ip]=$count

                                    # Add HTTP attack pre-boost
                                    conn_bonus=$((conn_bonus + http_attack_bonus))

                                    # Geographic clustering bonus
                                    # Note: geo_bonus declared outside skip_scoring block (line ~2755) for scope
                                    if [ -f "$TEMP_DIR/threat_enrich_${ip//\./_}" ]; then
                                        local threat_data=$(cat "$TEMP_DIR/threat_enrich_${ip//\./_}" 2>/dev/null || echo "")
                                        # Bash IFS field splitting (100x faster than cut)
                                        IFS='|' read -r _ _ _ ip_isp ip_geo _ <<< "$threat_data"

                                        # Check if from hostile country (5+ attackers)
                                        if [ -n "$ip_geo" ] && grep -q "^${ip_geo}$" "$TEMP_DIR/hostile_countries" 2>/dev/null; then
                                            geo_bonus=$((geo_bonus + 10))  # Part of coordinated country-level attack
                                        fi

                                        # Check if from hostile ASN (3+ attackers)
                                        if [ -n "$ip_isp" ]; then
                                            local ip_asn=$(echo "$ip_isp" | grep -oP 'AS\K\d+' 2>/dev/null | head -1 2>/dev/null || echo "")
                                            if [ -n "$ip_asn" ] && grep -q "^${ip_asn}$" "$TEMP_DIR/hostile_asns" 2>/dev/null; then
                                                geo_bonus=$((geo_bonus + 15))  # Same botnet infrastructure
                                            fi
                                        fi
                                    fi
                                    conn_bonus=$((conn_bonus + geo_bonus))

                                    # First hit or add to existing score
                                    # CRITICAL FIX: Reversed the condition - repeat detections should ADD, not RESET
                                    # Bug: hits=0 means NEW IP (initialize score), hits=1+ means REPEAT (accumulate)
                                    # Previous: reset score on repeat detection, losing threat history
                                    # Now: initialize only on first detection, accumulate on repeats
                                    if [ "${hits:-0}" -eq 0 ]; then
                                        score=$conn_bonus  # First detection: initialize to connection bonus
                                    else
                                        score=$((score + conn_bonus))  # Repeat detection: ADD to accumulated score
                                    fi

                                # Apply advanced intelligence bonuses
                                local block_reasons=""
                                local velocity_data=$(calculate_attack_velocity "$ip")
                                IFS='|' read -r vel_count vel_bonus vel_reason <<< "$velocity_data"
                                [ "$vel_bonus" -gt 0 ] && score=$((score + vel_bonus)) && block_reasons="${vel_reason}"

                                # Apply threat intelligence bonuses (AbuseIPDB, geolocation)
                                if [ "$threat_intel_bonus" -gt 0 ]; then
                                    score=$((score + threat_intel_bonus))
                                    [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                                    block_reasons="${block_reasons}THREAT_INTEL(+${threat_intel_bonus})"
                                fi

                                local div_data=$(calculate_diversity_bonus "$ip")
                                IFS='|' read -r div_count div_bonus div_reason <<< "$div_data"
                                if [ "$div_bonus" -gt 0 ]; then
                                    score=$((score + div_bonus))
                                    [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                                    block_reasons="${block_reasons}${div_reason}"
                                fi

                                local subnet_data=$(calculate_subnet_bonus "$ip")
                                IFS='|' read -r subnet_count subnet_bonus subnet_reason <<< "$subnet_data"
                                if [ "${subnet_bonus:-0}" -gt 0 ]; then
                                    score=$((score + subnet_bonus))
                                    [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                                    block_reasons="${block_reasons}${subnet_reason}"
                                fi

                                # Detect timing patterns
                                local timing_result=$(detect_timing_pattern "$ip")
                                IFS='|' read -r timing_type timing_bonus timing_reason <<< "$timing_result"
                                if [ "$timing_bonus" -gt 0 ]; then
                                    score=$((score + timing_bonus))
                                    [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                                    block_reasons="${block_reasons}${timing_reason}"
                                fi

                                    # Cap at 100
                                    [ "$score" -gt 100 ] && score=100
                                fi  # End of skip_scoring check

                                # INCREMENT HITS AFTER ALL SCORING
                                # Moved from before whitelisting to ensure we have complete data
                                # Now hits is incremented with full score calculated and ready to persist
                                hits=$((hits + 1))

                                # CRITICAL FIX: Write to centralized ip_data file (not individual ip_*.files)
                                # auto_mitigation_engine() reads from $TEMP_DIR/ip_data, not individual files
                                # Without this, SYN-detected IPs are never auto-blocked!
                                # SINGLE WRITE: Complete data with correct score and incremented hits
                                write_ip_data_to_file "$ip" "$score|$hits|$bot_type|$attacks|$ban_count|$rep_score" 2>/dev/null &

                                # Also write to individual file for debugging/tracking
                                echo "$score|$hits|$bot_type|$attacks|$ban_count|$rep_score" > "$ip_file"

                                # Store block reasons for auto-mitigation
                                if [ -n "$block_reasons" ]; then
                                    echo "$block_reasons" > "$TEMP_DIR/block_reason_${ip//\./_}"
                                fi

                                # Log to reputation DB
                                flag_ip_attack "$ip" "SYN_FLOOD" 0 "SYN flood: $count connections" >/dev/null 2>&1 &

                                # Log event with reputation score and attack intelligence
                                local time_str=$(date +"%H:%M:%S")
                                local level=$(get_threat_level "$score")
                                local color=$(get_threat_color "$level")

                                # Build intelligence summary
                                local intel_tags=""
                                [ "$attack_severity" -ge 1 ] && intel_tags="${intel_tags}DDoS:T${attack_severity} "
                                [ "$attack_momentum" -ge 1 ] && intel_tags="${intel_tags}ACCEL "
                                [ "$coordinated_attack" -eq 1 ] && intel_tags="${intel_tags}BOTNET "
                                [ "$multi_vector" -eq 1 ] && intel_tags="${intel_tags}MULTI-VECTOR "
                                [ "$http_attack_bonus" -gt 0 ] && intel_tags="${intel_tags}HTTP-ATTACKER "
                                # CRITICAL FIX: Fixed conditional precedence for geo tagging
                                # Bug: Using elif logic caused mutual exclusion - couldn't show both tags
                                # If geo_bonus = 25 (both hostile country + ASN), only showed "HOSTILE-ASN"
                                # Should show BOTH tags if both conditions are true
                                local is_hostile_asn=0
                                local is_hostile_geo=0
                                if [ "$geo_bonus" -ge 15 ]; then
                                    is_hostile_asn=1
                                fi
                                if [ "$geo_bonus" -ge 10 ] && [ "$geo_bonus" -lt 15 ]; then
                                    is_hostile_geo=1
                                fi
                                # Special case: if geo_bonus >= 25, it's from BOTH sources (10 + 15)
                                if [ "$geo_bonus" -ge 25 ]; then
                                    is_hostile_asn=1
                                    is_hostile_geo=1
                                fi
                                [ "$is_hostile_asn" -eq 1 ] && intel_tags="${intel_tags}HOSTILE-ASN "
                                [ "$is_hostile_geo" -eq 1 ] && intel_tags="${intel_tags}HOSTILE-GEO "

                                # SYN-specific intelligence tags
                                [ "$established_conns" -eq 0 ] && [ "$count" -ge 5 ] && intel_tags="${intel_tags}PURE-SYN "
                                [ "${ratio:-0}" -ge 30 ] && intel_tags="${intel_tags}BAD-RATIO "
                                [ "$has_other_traffic" -eq 0 ] && [ "$count" -ge 10 ] && intel_tags="${intel_tags}SPOOFED "
                                [ "${target_ports:-0}" -eq 1 ] && [ "$count" -ge 8 ] && intel_tags="${intel_tags}TARGETED "

                                echo -e "${color}[${time_str}] $ip | Score:$score [$level] | 💥SYN_FLOOD | Conns:$count Est:$established_conns | ${intel_tags}${NC}" >> "$TEMP_DIR/recent_events"
                            fi
                        else
                            # Reset alert if connections drop below threshold
                            unset ALERT_SENT[$ip]
                        fi
                    # CRITICAL FIX: Change awk filter from '$1 > 5' to '$1 >= 3'
                    # Reason: Minimum threshold is 3 connections (Tier 4 attacks), so IPs with 3-5 connections must be processed
                    # Before fix: IPs with <6 connections were silently skipped, preventing detection in high-severity attacks
                    done < <(ss -tn state syn-recv 2>/dev/null | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}' | sort | uniq -c | awk '$1 >= 3 {print $2, $1}')
                fi

                sleep 5  # Check every 5 seconds (faster detection during active attacks)
            done
        ) &
    fi
}

################################################################################
# Email/SMTP Attack Monitoring
################################################################################

monitor_email_attacks() {
    # Monitor mail logs for SMTP/IMAP/POP3 bruteforce
    local mail_log="/var/log/maillog"

    if [ ! -f "$mail_log" ]; then
        mail_log="/var/log/mail.log"
    fi

    if [ -f "$mail_log" ]; then
        tail -n 0 -F "$mail_log" 2>/dev/null | while read -r line; do
            # Dovecot authentication failures (use bash regex for performance)
            if [[ "$line" =~ auth.*failed|authentication\ failed|password\ mismatch ]]; then
                # Extract IP address using bash regex
                if [[ "$line" =~ ([0-9]{1,3}\.){3}[0-9]{1,3} ]]; then
                    local ip="${BASH_REMATCH[0]}"
                else
                    continue
                fi

                if [ -n "$ip" ]; then
                    # Skip local/private IPs
                    [[ "$ip" =~ ^127\. ]] || [[ "$ip" =~ ^10\. ]] || [[ "$ip" =~ ^192\.168\. ]] || [[ "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[01])\. ]] && continue

                    # Process as BRUTEFORCE attack
                    # Read from file (subshells can't access IP_DATA array)
                    local ip_file="$TEMP_DIR/ip_${ip//\./_}"
                    local current_data="0|0|human||0|0"
                    if [ -f "$ip_file" ]; then
                        current_data=$(cat "$ip_file")
                    fi
                    IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "$current_data"

                    hits=$((hits + 1))

                    # Record timestamp and vector for intelligence
                    record_attack_timestamp "$ip"
                    record_attack_vector "$ip" "EMAIL"
                    track_subnet_attack "$ip"

                    # Add BRUTEFORCE to attacks
                    if [[ ! "$attacks" =~ BRUTEFORCE ]]; then
                        [ -z "$attacks" ] && attacks="BRUTEFORCE" || attacks="${attacks},BRUTEFORCE"
                    fi

                    # Progressive scoring: Each email bruteforce attempt adds points
                    if [ "${hits:-0}" -eq 1 ]; then
                        score=10
                    else
                        score=$((score + 8))
                    fi

                    # Apply advanced intelligence bonuses
                    local block_reasons=""
                    local velocity_data=$(calculate_attack_velocity "$ip")
                    IFS='|' read -r vel_count vel_bonus vel_reason <<< "$velocity_data"
                    [ "$vel_bonus" -gt 0 ] && score=$((score + vel_bonus)) && block_reasons="${vel_reason}"

                    local div_data=$(calculate_diversity_bonus "$ip")
                    IFS='|' read -r div_count div_bonus div_reason <<< "$div_data"
                    if [ "$div_bonus" -gt 0 ]; then
                        score=$((score + div_bonus))
                        [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                        block_reasons="${block_reasons}${div_reason}"
                    fi

                    local pattern_data=$(detect_timing_pattern "$ip")
                    IFS='|' read -r pat_type pat_conf pat_bonus pat_reason <<< "$pattern_data"
                    if [ "$pat_bonus" -gt 0 ]; then
                        score=$((score + pat_bonus))
                        [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                        block_reasons="${block_reasons}${pat_reason}"
                    fi

                    local subnet_data=$(calculate_subnet_bonus "$ip")
                    IFS='|' read -r subnet_count subnet_bonus subnet_reason <<< "$subnet_data"
                    if [ "$subnet_bonus" -gt 0 ]; then
                        score=$((score + subnet_bonus))
                        [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                        block_reasons="${block_reasons}${subnet_reason}"
                    fi

                    local context_data=$(calculate_context_bonus "$ip")
                    IFS='|' read -r context_bonus context_reason <<< "$context_data"
                    if [ "$context_bonus" -gt 0 ]; then
                        score=$((score + context_bonus))
                        [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                        block_reasons="${block_reasons}${context_reason}"
                    fi

                    [ "${score:-0}" -gt 100 ] && score=100

                    # Update ip_data file directly (subshells can't access IP_DATA array)
                    local ip_file="$TEMP_DIR/ip_${ip//\./_}"
                    echo "$score|$hits|$bot_type|$attacks|$ban_count|$rep_score" > "$ip_file"

                    # Store block reasons for CSF
                    if [ -n "$block_reasons" ]; then
                        echo "$block_reasons" > "$TEMP_DIR/block_reason_${ip//\./_}"
                    fi

                    # Log to reputation DB
                    flag_ip_attack "$ip" "BRUTEFORCE" 0 "Email authentication failure" >/dev/null 2>&1 &

                    # Log event
                    local time_str=$(date +"%H:%M:%S")
                    local level=$(get_threat_level "$score")
                    local color=$(get_threat_color "$level")

                    echo -e "${color}[${time_str}] $ip | Score:$score [$level] | 📧EMAIL_BRUTEFORCE | Hits:$hits${NC}" >> "$TEMP_DIR/recent_events"
                fi
            fi
        done &
    fi
}

################################################################################
# FTP Attack Monitoring
################################################################################

monitor_ftp_attacks() {
    # Monitor FTP logs for bruteforce attempts
    local ftp_log="/var/log/vsftpd.log"

    if [ ! -f "$ftp_log" ]; then
        ftp_log="/var/log/xferlog"
    fi

    if [ -f "$ftp_log" ]; then
        tail -n 0 -F "$ftp_log" 2>/dev/null | while read -r line; do
            # FTP authentication failures (use bash regex for performance)
            if [[ "$line" =~ FAIL\ LOGIN|authentication\ failed|530\ Login\ incorrect ]]; then
                # Extract IP address using bash regex
                if [[ "$line" =~ ([0-9]{1,3}\.){3}[0-9]{1,3} ]]; then
                    local ip="${BASH_REMATCH[0]}"
                else
                    continue
                fi

                if [ -n "$ip" ]; then
                    # Skip local/private IPs
                    [[ "$ip" =~ ^127\. ]] || [[ "$ip" =~ ^10\. ]] || [[ "$ip" =~ ^192\.168\. ]] || [[ "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[01])\. ]] && continue

                    # Process as BRUTEFORCE attack
                    # Read from file (subshells can't access IP_DATA array)
                    local ip_file="$TEMP_DIR/ip_${ip//\./_}"
                    local current_data="0|0|human||0|0"
                    if [ -f "$ip_file" ]; then
                        current_data=$(cat "$ip_file")
                    fi
                    IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "$current_data"

                    hits=$((hits + 1))

                    # Record timestamp and vector for intelligence
                    record_attack_timestamp "$ip"
                    record_attack_vector "$ip" "FTP"
                    track_subnet_attack "$ip"

                    # Add BRUTEFORCE to attacks
                    if [[ ! "$attacks" =~ BRUTEFORCE ]]; then
                        [ -z "$attacks" ] && attacks="BRUTEFORCE" || attacks="${attacks},BRUTEFORCE"
                    fi

                    # Progressive scoring: Each FTP bruteforce attempt adds points
                    if [ "${hits:-0}" -eq 1 ]; then
                        score=10
                    else
                        score=$((score + 8))
                    fi

                    # Apply advanced intelligence bonuses
                    local block_reasons=""
                    local velocity_data=$(calculate_attack_velocity "$ip")
                    IFS='|' read -r vel_count vel_bonus vel_reason <<< "$velocity_data"
                    [ "$vel_bonus" -gt 0 ] && score=$((score + vel_bonus)) && block_reasons="${vel_reason}"

                    local div_data=$(calculate_diversity_bonus "$ip")
                    IFS='|' read -r div_count div_bonus div_reason <<< "$div_data"
                    if [ "$div_bonus" -gt 0 ]; then
                        score=$((score + div_bonus))
                        [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                        block_reasons="${block_reasons}${div_reason}"
                    fi

                    local pattern_data=$(detect_timing_pattern "$ip")
                    IFS='|' read -r pat_type pat_conf pat_bonus pat_reason <<< "$pattern_data"
                    if [ "$pat_bonus" -gt 0 ]; then
                        score=$((score + pat_bonus))
                        [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                        block_reasons="${block_reasons}${pat_reason}"
                    fi

                    local subnet_data=$(calculate_subnet_bonus "$ip")
                    IFS='|' read -r subnet_count subnet_bonus subnet_reason <<< "$subnet_data"
                    if [ "$subnet_bonus" -gt 0 ]; then
                        score=$((score + subnet_bonus))
                        [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                        block_reasons="${block_reasons}${subnet_reason}"
                    fi

                    local context_data=$(calculate_context_bonus "$ip")
                    IFS='|' read -r context_bonus context_reason <<< "$context_data"
                    if [ "$context_bonus" -gt 0 ]; then
                        score=$((score + context_bonus))
                        [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                        block_reasons="${block_reasons}${context_reason}"
                    fi

                    [ "${score:-0}" -gt 100 ] && score=100

                    # Update ip_data file directly (subshells can't access IP_DATA array)
                    local ip_file="$TEMP_DIR/ip_${ip//\./_}"
                    echo "$score|$hits|$bot_type|$attacks|$ban_count|$rep_score" > "$ip_file"

                    # Store block reasons for CSF
                    if [ -n "$block_reasons" ]; then
                        echo "$block_reasons" > "$TEMP_DIR/block_reason_${ip//\./_}"
                    fi

                    # Log to reputation DB
                    flag_ip_attack "$ip" "BRUTEFORCE" 0 "FTP login failure" >/dev/null 2>&1 &

                    # Log event
                    local time_str=$(date +"%H:%M:%S")
                    local level=$(get_threat_level "$score")
                    local color=$(get_threat_color "$level")

                    echo -e "${color}[${time_str}] $ip | Score:$score [$level] | 📁FTP_BRUTEFORCE | Hits:$hits${NC}" >> "$TEMP_DIR/recent_events"
                fi
            fi
        done &
    fi
}

################################################################################
# Database Attack Monitoring
################################################################################

monitor_database_attacks() {
    # Monitor MySQL logs for authentication failures
    local mysql_log="/var/log/mysqld.log"

    if [ ! -f "$mysql_log" ]; then
        mysql_log="/var/log/mysql/error.log"
    fi

    if [ -f "$mysql_log" ]; then
        tail -n 0 -F "$mysql_log" 2>/dev/null | while read -r line; do
            # MySQL authentication failures (use bash regex for performance)
            if [[ "$line" =~ Access\ denied\ for\ user|Failed\ password\ for ]]; then
                # Extract IP address using bash regex
                if [[ "$line" =~ ([0-9]{1,3}\.){3}[0-9]{1,3} ]]; then
                    local ip="${BASH_REMATCH[0]}"
                else
                    continue
                fi

                if [ -n "$ip" ]; then
                    # Skip local/private IPs
                    [[ "$ip" =~ ^127\. ]] || [[ "$ip" =~ ^10\. ]] || [[ "$ip" =~ ^192\.168\. ]] || [[ "$ip" =~ ^172\.(1[6-9]|2[0-9]|3[01])\. ]] && continue

                    # Process as SQL_INJECTION attack (database level)
                    # Read from file (subshells can't access IP_DATA array)
                    local ip_file="$TEMP_DIR/ip_${ip//\./_}"
                    local current_data="0|0|human||0|0"
                    if [ -f "$ip_file" ]; then
                        current_data=$(cat "$ip_file")
                    fi
                    IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "$current_data"

                    hits=$((hits + 1))

                    # Record timestamp and vector for intelligence
                    record_attack_timestamp "$ip"
                    record_attack_vector "$ip" "DATABASE"
                    track_subnet_attack "$ip"

                    # Add SQL_INJECTION to attacks
                    local is_new_attack=0
                    if [[ ! "$attacks" =~ SQL_INJECTION ]]; then
                        [ -z "$attacks" ] && attacks="SQL_INJECTION" || attacks="${attacks},SQL_INJECTION"
                        is_new_attack=1
                    fi

                    # Progressive scoring: First DB attack = 15pts, each additional = 12pts
                    if [ "${is_new_attack:-0}" -eq 1 ]; then
                        score=$((score + 15))
                    else
                        score=$((score + 12))
                    fi

                    # Apply advanced intelligence bonuses
                    local block_reasons=""
                    local velocity_data=$(calculate_attack_velocity "$ip")
                    IFS='|' read -r vel_count vel_bonus vel_reason <<< "$velocity_data"
                    [ "$vel_bonus" -gt 0 ] && score=$((score + vel_bonus)) && block_reasons="${vel_reason}"

                    local div_data=$(calculate_diversity_bonus "$ip")
                    IFS='|' read -r div_count div_bonus div_reason <<< "$div_data"
                    if [ "$div_bonus" -gt 0 ]; then
                        score=$((score + div_bonus))
                        [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                        block_reasons="${block_reasons}${div_reason}"
                    fi

                    local pattern_data=$(detect_timing_pattern "$ip")
                    IFS='|' read -r pat_type pat_conf pat_bonus pat_reason <<< "$pattern_data"
                    if [ "$pat_bonus" -gt 0 ]; then
                        score=$((score + pat_bonus))
                        [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                        block_reasons="${block_reasons}${pat_reason}"
                    fi

                    local subnet_data=$(calculate_subnet_bonus "$ip")
                    IFS='|' read -r subnet_count subnet_bonus subnet_reason <<< "$subnet_data"
                    if [ "$subnet_bonus" -gt 0 ]; then
                        score=$((score + subnet_bonus))
                        [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                        block_reasons="${block_reasons}${subnet_reason}"
                    fi

                    local context_data=$(calculate_context_bonus "$ip")
                    IFS='|' read -r context_bonus context_reason <<< "$context_data"
                    if [ "$context_bonus" -gt 0 ]; then
                        score=$((score + context_bonus))
                        [ -n "$block_reasons" ] && block_reasons="${block_reasons}+" || block_reasons=""
                        block_reasons="${block_reasons}${context_reason}"
                    fi

                    [ "${score:-0}" -gt 100 ] && score=100

                    # Update ip_data file directly (subshells can't access IP_DATA array)
                    local ip_file="$TEMP_DIR/ip_${ip//\./_}"
                    echo "$score|$hits|$bot_type|$attacks|$ban_count|$rep_score" > "$ip_file"

                    # Store block reasons for CSF
                    if [ -n "$block_reasons" ]; then
                        echo "$block_reasons" > "$TEMP_DIR/block_reason_${ip//\./_}"
                    fi

                    # Log to reputation DB
                    flag_ip_attack "$ip" "SQL_INJECTION" 0 "MySQL authentication failure" >/dev/null 2>&1 &

                    # Log event
                    local time_str=$(date +"%H:%M:%S")
                    local level=$(get_threat_level "$score")
                    local color=$(get_threat_color "$level")

                    echo -e "${color}[${time_str}] $ip | Score:$score [$level] | 🗄️ DB_BRUTEFORCE | Hits:$hits${NC}" >> "$TEMP_DIR/recent_events"
                fi
            fi
        done &
    fi
}

################################################################################
# Distributed Attack Detection
################################################################################

detect_distributed_attacks() {
    # Run in background, check every 30 seconds
    (
        while true; do
            sleep 30

            # Look for same attack pattern from multiple IPs in short time
            if [ -f "$TEMP_DIR/recent_events" ]; then
                # Get recent attacks (last 2 minutes)
                local recent=$(tail -200 "$TEMP_DIR/recent_events" 2>/dev/null)

                # Check for same attack type from 5+ different IPs (use awk for performance)
                for attack_type in RCE SQL_INJECTION XSS PATH_TRAVERSAL BRUTEFORCE; do
                    # Single AWK pass to extract all attacking IPs
                    local attacking_ips=$(echo "$recent" | awk -v pattern="$attack_type" '
                        $0 ~ pattern {
                            # Extract IP (first field matching IP pattern)
                            for(i=1; i<=NF; i++) {
                                if($i ~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/) {
                                    print $i
                                    break
                                }
                            }
                        }
                    ' | sort -u)

                    # Count unique IPs
                    local unique_ips=$(echo "$attacking_ips" | grep -c "^[0-9]" 2>/dev/null || echo "0")

                    if [ "${unique_ips:-0}" -ge 5 ]; then
                        # Distributed attack detected!
                        local time_str=$(date +"%H:%M:%S")

                        # BLOCK ALL INDIVIDUAL IPs IN THE ATTACK
                        local -a batch_ips=()
                        while IFS= read -r ip; do
                            [ -n "$ip" ] && batch_ips+=("$ip")
                        done <<< "$attacking_ips"

                        if [ ${#batch_ips[@]} -gt 0 ]; then
                            batch_block_ips "${batch_ips[@]}"
                            echo -e "${CRITICAL_COLOR}[${time_str}] DISTRIBUTED_ATTACK | ${attack_type} from ${unique_ips} IPs | BLOCKED ALL${NC}" >> "$TEMP_DIR/recent_events"
                            # CRITICAL FIX: Removed duplicate increment_block_counter call
                            # batch_block_ips() already calls increment_block_counter with the actual count on line 1027
                            # Adding another increment_block_counter 1 here causes double-counting
                            # (If 10 IPs blocked: would count as 11 instead of 10)
                        fi

                        # Check for subnet-level coordination (25+ IPs from same /24)
                        declare -A subnet_counts
                        while IFS= read -r ip; do
                            [ -z "$ip" ] && continue
                            local subnet="${ip%.*}"  # Get /24 subnet (bash built-in)
                            ((subnet_counts[$subnet]++))
                        done <<< "$attacking_ips"

                        # Block entire subnets with 25+ attacking IPs
                        for subnet in "${!subnet_counts[@]}"; do
                            local subnet_ip_count=${subnet_counts[$subnet]}
                            if [ "$subnet_ip_count" -ge 25 ]; then
                                local subnet_cidr="${subnet}.0/24"

                                # Check if not already blocked
                                if ! grep -q "^${subnet_cidr}\$" "$TEMP_DIR/blocked_subnets" 2>/dev/null; then
                                    echo "$subnet_cidr" >> "$TEMP_DIR/blocked_subnets"

                                    # Add to IPset (kernel-level blocking)
                                    if [ "$IPSET_AVAILABLE" -eq 1 ]; then
                                        ipset add "$IPSET_NAME" "$subnet_cidr" -exist 2>/dev/null
                                        echo -e "${CRITICAL_COLOR}[${time_str}] SUBNET_BLOCK | $subnet_cidr | ${attack_type} from ${subnet_ip_count} IPs | BLOCKED${NC}" >> "$TEMP_DIR/recent_events"
                                        # BUG FIX: Increment block counter for subnet blocks
                                        increment_block_counter 1
                                    fi
                                fi
                            fi
                        done

                        # Mark in a file for Quick Actions to see
                        echo "${attack_type}|${unique_ips}|$(date +%s)" >> "$TEMP_DIR/distributed_attacks"
                    fi
                done
            fi
        done
    ) &
}

################################################################################
# Automatic Mitigation Engine
################################################################################

auto_mitigation_engine() {
    # Run in background, check every 10 seconds
    (
        # Track already blocked IPs in this session
        declare -A BLOCKED_THIS_SESSION

        while true; do
            # Batch blocking arrays (collect IPs, block in batches of 50)
            local -a batch_instant=()
            local -a batch_critical=()

            # DEBUG: Log that we're checking
            echo "[$(date +"%H:%M:%S")] AUTO_MIT: Checking for IPs to block..." >> "$TEMP_DIR/debug.log" 2>/dev/null || true

            # Read current IP data from snapshot file (updated by main process)
            if [ -f "$TEMP_DIR/ip_data" ]; then
                # DEBUG: File exists
                local ip_count=$(wc -l < "$TEMP_DIR/ip_data" 2>/dev/null || echo "0")
                echo "[$(date +"%H:%M:%S")] AUTO_MIT: ip_data exists with $ip_count IPs" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
                while IFS='=' read -r ip data; do
                    [ -z "$ip" ] && continue

                    IFS='|' read -r score hits bot_type attacks ban_count rep_score <<< "$data"

                    # DEBUG: Log parsed data
                    echo "[$(date +"%H:%M:%S")] AUTO_MIT: Parsing IP $ip | score=$score | hits=$hits | attacks=$attacks" >> "$TEMP_DIR/debug.log" 2>/dev/null || true

                    # Validate score is numeric
                    [ -z "$score" ] && score=0
                    [[ ! "$score" =~ ^[0-9]+$ ]] && score=0

                    # Skip if already blocked in this session
                    [ -n "${BLOCKED_THIS_SESSION[$ip]}" ] && continue

                    # INSTANT block at score 100 (MAXIMUM threat via IPset)
                    if [ "${score:-0}" -ge 100 ]; then
                        # DEBUG: Log score 100 detection
                        echo "[$(date +"%H:%M:%S")] AUTO_MIT: Found score 100 IP: $ip" >> "$TEMP_DIR/debug.log" 2>/dev/null || true

                        # Mark as blocked
                        BLOCKED_THIS_SESSION[$ip]=1

                        # Add to instant batch
                        batch_instant+=("$ip")

                        # Log event
                        local time_str=$(date +"%H:%M:%S")
                        echo -e "${CRITICAL_COLOR}[${time_str}] INSTANT_BLOCK | $ip | Score:100 | ${attacks}${NC}" >> "$TEMP_DIR/recent_events"
                        continue
                    fi

                    # Auto-block at score >= 80 (CRITICAL)
                    if [ "${score:-0}" -ge 80 ]; then
                        # Mark as blocked
                        BLOCKED_THIS_SESSION[$ip]=1

                        # Add to critical batch
                        batch_critical+=("$ip")

                        # Log event
                        local time_str=$(date +"%H:%M:%S")
                        echo -e "${CRITICAL_COLOR}[${time_str}] AUTO_BLOCK | $ip | Score:$score | ${attacks}${NC}" >> "$TEMP_DIR/recent_events"
                    fi
                done < "$TEMP_DIR/ip_data"
            else
                # DEBUG: File doesn't exist
                echo "[$(date +"%H:%M:%S")] AUTO_MIT: WARNING - ip_data file not found at $TEMP_DIR/ip_data" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
            fi

            # BATCH BLOCK - Instant (score 100)
            if [ ${#batch_instant[@]} -gt 0 ]; then
                echo "[$(date +"%H:%M:%S")] AUTO_MIT: Blocking ${#batch_instant[@]} instant IPs: ${batch_instant[*]}" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
                batch_block_ips "${batch_instant[@]}"
            else
                echo "[$(date +"%H:%M:%S")] AUTO_MIT: No instant IPs to block" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
            fi

            # BATCH BLOCK - Critical (score 80-99)
            if [ ${#batch_critical[@]} -gt 0 ]; then
                echo "[$(date +"%H:%M:%S")] AUTO_MIT: Blocking ${#batch_critical[@]} critical IPs: ${batch_critical[*]}" >> "$TEMP_DIR/debug.log" 2>/dev/null || true
                batch_block_ips "${batch_critical[@]}"
            fi

            # Sleep at END of loop to check immediately on startup
            # Faster checks during active attack scenarios (5 sec vs 10 sec)
            sleep 5
        done
    ) &
}

# Start all log monitoring sources
# Start all monitoring subprocesses in background
monitor_apache_logs &
monitor_ssh_attacks &
monitor_email_attacks &
monitor_ftp_attacks &
monitor_database_attacks &
monitor_firewall_blocks &
monitor_cphulk_blocks &
monitor_network_attacks &

# Display IPset initialization status
if [ -n "$IPSET_INIT_ERROR" ]; then
    echo ""
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo -e "${HIGH_COLOR}⚠️  IPset Initialization Warning${NC}"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo ""
    echo "  IPset fast blocking is NOT available"
    echo "  Reason: $IPSET_INIT_ERROR"
    echo ""
    echo "  ${BOLD}Impact:${NC}"
    echo "  • Blocking will use CSF (slower than IPset)"
    echo "  • Large-scale attacks (500+ IPs) will be slower to block"
    echo "  • Performance: ~50x slower blocking vs IPset"
    echo ""
    echo "  ${BOLD}To enable IPset fast blocking:${NC}"

    if echo "$IPSET_INIT_ERROR" | grep -q "not found"; then
        echo "  1. Install ipset: yum install ipset -y  (or apt-get install ipset)"
        echo "  2. Restart this script"
    elif echo "$IPSET_INIT_ERROR" | grep -qi "module"; then
        echo "  1. Load kernel modules: modprobe ip_set ip_set_hash_ip xt_set"
        echo "  2. Restart this script"
    elif echo "$IPSET_INIT_ERROR" | grep -qi "permission"; then
        echo "  1. Run script as root: sudo $0"
    elif echo "$IPSET_INIT_ERROR" | grep -q "iptables"; then
        echo "  1. Check iptables: iptables -L -n"
        echo "  2. Install iptables if missing: yum install iptables -y"
        echo "  3. Ensure xt_set kernel module is loaded: modprobe xt_set"
    else
        echo "  1. Check debug log: $TEMP_DIR/debug.log"
        echo "  2. Ensure ipset and iptables are installed"
        echo "  3. Run as root"
    fi

    echo ""
    echo "  Fallback: Using CSF for all blocking (still functional)"
    echo ""
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo ""
    sleep 3  # Give user time to read
fi

# BUG FIX #17: Load persistent threat data BEFORE starting auto_mitigation_engine
# This ensures pre-existing high-score IPs (Score >= 50) are blocked on startup
load_snapshot

# Immediately write loaded IP_DATA to ip_data file for auto_mitigation_engine to process
if [ ${#IP_DATA[@]} -gt 0 ]; then
    {
        flock -w 2 200 || exit 1
        for ip in "${!IP_DATA[@]}"; do
            echo "$ip=${IP_DATA[$ip]}"
        done
    } > "$TEMP_DIR/ip_data" 2>/dev/null 200>"$TEMP_DIR/ip_data.lock"
    echo "[INFO] Restored ${#IP_DATA[@]} threat IPs from persistent storage (Score >= 50)" >&2
fi

# Start intelligence engines
detect_distributed_attacks
auto_mitigation_engine

# Reputation decay engine (runs every 30 min)
(
    while true; do
        sleep $DECAY_CHECK_INTERVAL
        apply_reputation_decay
    done
) &

# Blocked IPs cache updater (only needed in CSF mode - IPset mode appends to cache on each block)
if [ "$IPSET_AVAILABLE" -eq 0 ]; then
    (
        while true; do
            {
                # Get CSF temporary blocks - extract just the IP address
                if command -v csf &>/dev/null; then
                    csf -t 2>/dev/null | awk '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/ {print $1}'
                fi

                # Get CSF permanent denies
                if [ -f /etc/csf/csf.deny ]; then
                    awk '/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/ {print $1}' /etc/csf/csf.deny 2>/dev/null
                fi

                # Get iptables DROP rules
                if command -v iptables &>/dev/null; then
                    iptables -L INPUT -n -v 2>/dev/null | awk '/DROP/ && $8 ~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/ {print $8}'
                fi
            } | sort -u > "$TEMP_DIR/blocked_ips_cache.tmp" 2>/dev/null
            mv "$TEMP_DIR/blocked_ips_cache.tmp" "$TEMP_DIR/blocked_ips_cache" 2>/dev/null
            sleep 10
        done
    ) &
fi

# Periodic snapshot saving in background
(
    while true; do
        sleep 300  # Save every 5 minutes
        save_snapshot
    done
) &

# Main dashboard loop
LOOP_COUNT=0
while true; do
    # Sync individual IP files into IP_DATA array (for data from subshell processes like SSH monitoring)
    for ip_file in "$TEMP_DIR"/ip_*; do
        [ -f "$ip_file" ] || continue
        basename_file="${ip_file##*/}"

        # Skip non-IP files explicitly
        case "$basename_file" in
            ip_data|ip_database.db|*cache*|*blocked*|*debug*)
                continue
                ;;
        esac

        # Validate it's an IP file (should match pattern ip_N_N_N_N)
        # Using bash pattern matching instead of grep for performance
        if [[ ! "$basename_file" =~ ^ip_[0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}_[0-9]{1,3}$ ]]; then
            continue
        fi

        # Extract IP from filename (ip_1_2_3_4 -> 1.2.3.4)
        # Using bash string manipulation for performance
        ip="${basename_file#ip_}"  # Remove 'ip_' prefix
        ip="${ip//_/.}"            # Replace all underscores with dots
        data=$(cat "$ip_file" 2>/dev/null)

        # Validate data format (should be score|hits|bot_type|attacks|ban_count|rep_score)
        # Using bash pattern matching instead of grep for performance
        if [ -n "$data" ] && [[ "$data" == *"|"* ]]; then
            # Update IP_DATA array with data from file
            IP_DATA[$ip]="$data"
        fi
    done

    draw_header
    draw_intelligence_panel
    draw_attack_breakdown
    draw_live_feed
    draw_quick_actions

    # Write IP_DATA to ip_data file for auto-mitigation engine
    # NOTE: Subprocesses use write_ip_data_to_file() for real-time updates
    # This merges parent process data without overwriting subprocess updates
    {
        flock -w 2 200 || exit 1

        # Read existing file (contains subprocess updates)
        declare -A existing_ips
        if [ -f "$TEMP_DIR/ip_data" ]; then
            while IFS='=' read -r ip data; do
                [ -n "$ip" ] && existing_ips[$ip]="$data"
            done < "$TEMP_DIR/ip_data"
        fi

        # Merge parent's IP_DATA with existing (subprocess updates take priority)
        for ip in "${!IP_DATA[@]}"; do
            # Only write if not already in file (subprocess updates are fresher)
            if [ -z "${existing_ips[$ip]}" ]; then
                echo "$ip=${IP_DATA[$ip]}"
            fi
        done

        # Write back existing entries (from subprocesses)
        for ip in "${!existing_ips[@]}"; do
            echo "$ip=${existing_ips[$ip]}"
        done

    } > "$TEMP_DIR/ip_data.new" 2>/dev/null 200>"$TEMP_DIR/ip_data.lock"

    mv "$TEMP_DIR/ip_data.new" "$TEMP_DIR/ip_data" 2>/dev/null

    # Update total blocks from file
    if [ -f "$TEMP_DIR/total_blocks" ]; then
        TOTAL_BLOCKS=$(cat "$TEMP_DIR/total_blocks")
    fi

    # Periodic cleanup (every 50 loops = ~100 seconds)
    ((LOOP_COUNT++))
    if [ $((LOOP_COUNT % 50)) -eq 0 ]; then
        # Trim event log to last 1000 lines
        if [ -f "$TEMP_DIR/recent_events" ]; then
            tail -1000 "$TEMP_DIR/recent_events" > "$TEMP_DIR/recent_events.tmp" 2>/dev/null
            mv "$TEMP_DIR/recent_events.tmp" "$TEMP_DIR/recent_events" 2>/dev/null
        fi
    fi

    # Non-blocking input with timeout
    read -t $REFRESH_INTERVAL -n 1 key

    case "$key" in
        b|B)
            show_blocking_menu
            ;;
        c|C)
            # Security hardening menu
            show_security_hardening_menu
            ;;
        v|V)
            # Toggle compact/verbose mode
            if [ "$COMPACT_MODE" -eq 1 ]; then
                COMPACT_MODE=0
            else
                COMPACT_MODE=1
            fi
            ;;
        i|I)
            # Show threat intelligence for specific IP
            clear
            print_banner "Threat Intelligence Lookup"
            echo ""
            read -p "Enter IP address: " lookup_ip
            if [ -n "$lookup_ip" ]; then
                echo ""
                echo "Querying threat intelligence for $lookup_ip..."
                echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
                threat_intel=$(get_threat_intelligence "$lookup_ip")
                IFS='|' read -r abuse_conf abuse_rpts country isp geo timing whitelisted <<< "$threat_intel"
                echo ""
                echo "${BOLD}Threat Intelligence:${NC}"
                echo "  AbuseIPDB Confidence: ${abuse_conf}%"
                echo "  Total Abuse Reports: $abuse_rpts"
                echo "  Country: ${geo:-$country}"
                echo "  ISP: $isp"
                echo "  Timing Pattern: $timing"
                echo "  Whitelisted: $whitelisted"
                echo ""
                if is_high_risk_country "${geo:-XX}"; then
                    echo -e "${HIGH_COLOR}  ⚠️  HIGH RISK COUNTRY${NC}"
                fi
                if [ "${abuse_conf:-0}" -ge 75 ]; then
                    echo -e "${CRITICAL_COLOR}  🚨 HIGH CONFIDENCE MALICIOUS${NC}"
                elif [ "${abuse_conf:-0}" -ge 50 ]; then
                    echo -e "${HIGH_COLOR}  ⚠️  MEDIUM CONFIDENCE THREAT${NC}"
                fi
                echo ""
                read -p "Generate full incident report? (y/n): " gen_report
                if [[ "$gen_report" =~ ^[Yy]$ ]]; then
                    report_file=$(generate_incident_report "$lookup_ip")
                    echo ""
                    echo "Report generated: $report_file"
                    echo ""
                    echo "View report? (y/n): "
                    read -n 1 view_report
                    if [[ "$view_report" =~ ^[Yy]$ ]]; then
                        less "$report_file"
                    fi
                fi
            fi
            echo ""
            read -p "Press Enter to return to monitor..."
            ;;
        p|P)
            # Show performance impact
            clear
            print_banner "Server Performance Monitor"
            echo ""
            load_data=$(get_server_load)
            IFS='|' read -r load1 load5 load15 cpu_count <<< "$load_data"
            echo "${BOLD}Current Load:${NC}"
            echo "  1 min:  $load1"
            echo "  5 min:  $load5"
            echo "  15 min: $load15"
            echo "  CPU cores: $cpu_count"
            echo ""
            if is_server_stressed; then
                echo -e "${CRITICAL_COLOR}  🔥 SERVER UNDER STRESS${NC}"
                echo ""
                echo "  Recommended Actions:"
                echo "  • Enable aggressive auto-blocking (higher threshold)"
                echo "  • Reduce CT_LIMIT temporarily"
                echo "  • Block high-volume attack IPs immediately"
            else
                echo -e "${SAFE_COLOR}  ✓ Server load normal${NC}"
            fi
            echo ""
            read -p "Press Enter to return to monitor..."
            ;;
        q|Q)
            cleanup
            ;;
        r|R)
            # Force refresh
            continue
            ;;
        s|S)
            # Show stats
            clear
            show_ip_reputation_stats
            read -p "Press Enter to continue..."
            ;;
        h|H|\?)
            # Show help
            clear
            print_banner "Keyboard Controls"
            echo ""
            echo "Available Commands:"
            echo "  ${BOLD}b${NC} - Open IP blocking menu (batch or individual)"
            echo "  ${BOLD}c${NC} - Security hardening menu (SYNFLOOD, SSH, CT_LIMIT, Port Knocking)"
            echo "  ${BOLD}i${NC} - Threat intelligence lookup (AbuseIPDB, geo, incident reports)"
            echo "  ${BOLD}p${NC} - Show performance impact monitor (server load)"
            echo "  ${BOLD}s${NC} - Show IP reputation database statistics"
            echo "  ${BOLD}r${NC} - Force refresh display"
            echo "  ${BOLD}h${NC} - Show this help screen"
            echo "  ${BOLD}q${NC} - Quit and save snapshot"
            echo ""
            echo "Features:"
            echo "  • Real-time bot classification (legit/AI/monitor/suspicious)"
            echo "  • Attack vector detection (SQL, XSS, RCE, etc.)"
            echo "  • Threat scoring (0-100 scale)"
            echo "  • Threat intelligence integration (AbuseIPDB, geolocation)"
            echo "  • Attack pattern learning & behavioral analysis"
            echo "  • Automated incident report generation"
            echo "  • Smart whitelisting (CDNs, search engines)"
            echo "  • IP reputation DB integration"
            echo "  • CSF/iptables temporary bans (1 hour default)"
            echo "  • Auto-mitigation at critical threshold (score ≥80)"
            echo "  • Memory protection (max ${MAX_TRACKED_IPS} IPs tracked)"
            echo "  • Auto-save every 5 minutes + on exit"
            echo ""
            read -p "Press Enter to continue..."
            ;;
    esac
done
