#!/bin/bash
################################################################################
# Script Name: iptables-blocklist-metrics.sh
# Version: 2.03
# Description: Prometheus exporter for iptables threat feed blocking metrics
# Author: Phil Connor
# Contact: contact@mylinux.work
# Website: https://mylinux.work
# License: MIT
#
# Changes in v2.03:
#   - Fixed blocked_total metric type from counter to gauge (rolling window)
#   - Fixed HTTP response headers (printf with proper \r\n termination)
#   - Fixed rule-stats grep prefix-matching wrong feed (word-boundary match)
#   - Cached iptables -L INPUT output once per scrape instead of per-feed
#   - Removed unused get_iptables_rule_stats helper function
#   - Used FEEDS_ARRAY instead of grep on config file for ipset status check
#   - Removed unnecessary sync call in textfile output path
#
# Changes in v2.02:
#   - Added journal data caching (single journalctl call instead of per-feed)
#   - Added feeds config caching into array
#   - Fixed ipset member counting to use Members: section
#   - Added SCRIPT_VERSION variable for version strings
#   - Added scrape timestamp metric
#   - Fixed hardcoded version in info metric and HTML page
################################################################################

# Ensure PATH includes sbin (for ipset/iptables when run from cron)
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH"
#
# EXPORTED METRICS:
# - iptables_blocklist_info - Exporter metadata
# - iptables_blocklist_enabled_feeds - Count of enabled feeds
# - iptables_blocklist_ipset_size - IPs per feed ipset (IPv4/v6)
# - iptables_blocklist_blocked_total - Block counts per feed (1h, 24h)
# - iptables_blocklist_effectiveness - Blocks per 1000 IPs (24h)
# - iptables_blocklist_last_update_timestamp - Feed cache file mtime
# - iptables_blocklist_cache_age_seconds - Age of feed cache files
# - iptables_blocklist_file_size_bytes - Feed parsed file sizes
# - iptables_blocklist_ip_version_ratio - IPv4 vs IPv6 distribution per feed
# - iptables_blocklist_total_unique_ips - Total unique IPs across all feeds
# - iptables_blocklist_total_rules - Total iptables rules
# - iptables_blocklist_rule_packets - Packet counts from iptables rules
# - iptables_blocklist_rule_bytes - Byte counts from iptables rules
# - iptables_blocklist_conntrack_entries - Current conntrack entries
# - iptables_blocklist_conntrack_max - Maximum conntrack entries
# - iptables_blocklist_conntrack_usage_percent - Conntrack usage percentage
# - iptables_blocklist_whitelist_size - Whitelist ipset sizes
# - iptables_blocklist_exporter_runtime_seconds - Script execution time

CONFIG_DIR="/etc/iptables-threats"
CACHE_DIR="$CONFIG_DIR/cache"
FEEDS_CONFIG="$CONFIG_DIR/feeds.conf"
IPSET_PREFIX="iptables-feed"
WHITELIST_IPSET="iptables-whitelist"
WHITELIST_IPSET_V6="iptables-whitelist-v6"

TEXTFILE_DIR="/var/lib/node_exporter"
OUTPUT_FILE=""
HTTP_MODE=false
HTTP_PORT=9419
LOCK_FILE="/var/run/iptables-blocklist-metrics.lock"
SCRIPT_VERSION="2.03"

# Global cache variables
JOURNAL_1H=""
JOURNAL_24H=""
FEEDS_ARRAY=()

show_usage() {
    cat <<EOF
Usage: $0 [OPTIONS]

Export per-feed iptables threat statistics as Prometheus metrics.

MODES:
    --textfile      Write to node_exporter textfile collector
    --http          Run HTTP server on port $HTTP_PORT

OPTIONS:
    -p, --port      HTTP port (default: 9419)
    -o, --output    Output file
    -h, --help      Show this help

EXAMPLES:
    # Write to textfile collector
    $0 --textfile

    # Run as HTTP server
    $0 --http --port 9419

    # Generate metrics to stdout
    $0

EOF
    exit 0
}

parse_args() {
    while [[ $# -gt 0 ]]; do
        case $1 in
            -h|--help) show_usage ;;
            --textfile) OUTPUT_FILE="$TEXTFILE_DIR/iptables_blocklist.prom"; shift ;;
            --http) HTTP_MODE=true; shift ;;
            -p|--port) HTTP_PORT="$2"; shift 2 ;;
            -o|--output) OUTPUT_FILE="$2"; shift 2 ;;
            *) echo "Unknown: $1"; exit 1 ;;
        esac
    done
}

# Load all journal data ONCE at startup
cache_journal_data() {
    JOURNAL_1H=$(timeout 10 journalctl -k --since "1 hour ago" 2>/dev/null | grep '\[THREAT' || echo "")
    JOURNAL_24H=$(timeout 30 journalctl -k --since "24 hours ago" 2>/dev/null | grep '\[THREAT' || echo "")
}

# Parse feeds config ONCE into array
cache_feeds_config() {
    FEEDS_ARRAY=()
    if [ -f "$FEEDS_CONFIG" ]; then
        while IFS='|' read -r enabled name url type description; do
            [[ "$enabled" =~ ^#.*$ ]] && continue
            [[ -z "$enabled" ]] && continue
            FEEDS_ARRAY+=("$enabled|$name|$url|$type|$description")
        done < "$FEEDS_CONFIG"
    fi
}

get_ipset_size() {
    local ipset_name="$1"
    local count
    count=$(ipset list "$ipset_name" 2>/dev/null | sed -n '/^Members:$/,$p' | tail -n +2 | wc -l)
    echo "${count:-0}"
}

get_feed_blocks() {
    local feed="$1"
    local period="$2"
    local data

    case "$period" in
        "1 hour ago") data="$JOURNAL_1H" ;;
        "24 hours ago") data="$JOURNAL_24H" ;;
        *) echo 0; return ;;
    esac

    if [ -z "$data" ]; then echo 0; return; fi
    local count
    count=$(printf '%s' "$data" | grep -c "\[THREAT:${feed}\]" 2>/dev/null)
    echo "${count:-0}"
}

get_feed_blocks_v6() {
    local feed="$1"
    local period="$2"
    local data

    case "$period" in
        "1 hour ago") data="$JOURNAL_1H" ;;
        "24 hours ago") data="$JOURNAL_24H" ;;
        *) echo 0; return ;;
    esac

    if [ -z "$data" ]; then echo 0; return; fi
    local count
    count=$(printf '%s' "$data" | grep -c "\[THREAT-v6:${feed}\]" 2>/dev/null)
    echo "${count:-0}"
}

get_file_timestamp() {
    [ -f "$1" ] && stat -c %Y "$1" 2>/dev/null || echo "0"
}

get_file_size() {
    [ -f "$1" ] && stat -c %s "$1" 2>/dev/null || echo "0"
}

get_cache_age() {
    if [ -f "$1" ]; then
        echo $(($(date +%s) - $(stat -c %Y "$1" 2>/dev/null || echo 0)))
    else
        echo "0"
    fi
}

get_total_unique_ips() {
    local ip_version="$1"
    local count=0
    
    if [ "$ip_version" = "4" ]; then
        count=$(cat "$CACHE_DIR/"*-v4.parsed 2>/dev/null | sort -u | wc -l 2>/dev/null)
    elif [ "$ip_version" = "6" ]; then
        count=$(cat "$CACHE_DIR/"*-v6.parsed 2>/dev/null | sort -u | wc -l 2>/dev/null)
    fi
    
    echo "${count:-0}"
}

get_conntrack_count() {
    if [ -f /proc/sys/net/netfilter/nf_conntrack_count ]; then
        cat /proc/sys/net/netfilter/nf_conntrack_count
    else
        echo "0"
    fi
}

get_conntrack_max() {
    if [ -f /proc/sys/net/netfilter/nf_conntrack_max ]; then
        cat /proc/sys/net/netfilter/nf_conntrack_max
    else
        echo "0"
    fi
}

get_ipset_memory() {
    local ipset_name="$1"
    local mem
    mem=$(ipset list "$ipset_name" -t 2>/dev/null | grep "Size in memory:" | awk '{print $4}')
    echo "${mem:-0}"
}

get_cache_disk_usage() {
    if [ -d "$CACHE_DIR" ]; then
        df -B1 "$CACHE_DIR" 2>/dev/null | tail -1 | awk '{print $3"|"$4"|"$5}'
    else
        echo "0|0|0%"
    fi
}

get_total_cache_size() {
    if [ -d "$CACHE_DIR" ]; then
        du -sb "$CACHE_DIR" 2>/dev/null | awk '{print $1}'
    else
        echo "0"
    fi
}

acquire_lock() {
    if [ -f "$LOCK_FILE" ]; then
        local pid
        pid=$(cat "$LOCK_FILE" 2>/dev/null)
        if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
            echo "ERROR: Another instance is already running (PID: $pid)" >&2
            exit 1
        else
            echo "Removing stale lock file" >&2
            rm -f "$LOCK_FILE"
        fi
    fi
    echo $$ > "$LOCK_FILE"
    trap cleanup EXIT INT TERM
}

cleanup() {
    rm -f "$LOCK_FILE"
}

generate_metrics() {
    local start_time
    start_time=$(date +%s)

    local iptables_input_stats
    iptables_input_stats=$(iptables -L INPUT -v -n -x 2>/dev/null)
    
    cat <<EOF
# HELP iptables_blocklist_info Per-feed iptables threat blocking info
# TYPE iptables_blocklist_info gauge
iptables_blocklist_info{mode="per-feed",version="${SCRIPT_VERSION}"} 1

# HELP iptables_blocklist_enabled_feeds Total enabled feeds
# TYPE iptables_blocklist_enabled_feeds gauge
iptables_blocklist_enabled_feeds $(printf '%s\n' "${FEEDS_ARRAY[@]}" | grep -c '^1|')

# HELP iptables_blocklist_ipset_size Number of IPs per feed ipset
# TYPE iptables_blocklist_ipset_size gauge
EOF

    # Only export metrics for ipsets that actually exist
    for ipset_name in $(ipset list -n 2>/dev/null | grep "^${IPSET_PREFIX}-"); do
        # Extract feed name and IP version
        local feed_name="${ipset_name#"${IPSET_PREFIX}"-}"
        local ip_version="4"
        
        if [[ "$feed_name" =~ -v6$ ]]; then
            feed_name="${feed_name%-v6}"
            ip_version="6"
        fi
        
        # Get status from config
        local status="disabled"
        if printf '%s\n' "${FEEDS_ARRAY[@]}" | grep -q "^1|${feed_name}|" 2>/dev/null; then
            status="enabled"
        fi
        
        local size
        size=$(get_ipset_size "$ipset_name")
        echo "iptables_blocklist_ipset_size{feed=\"$feed_name\",ip_version=\"$ip_version\",status=\"$status\"} $size"
    done

    cat <<EOF

# HELP iptables_blocklist_blocked_total Blocked attempts per feed
# TYPE iptables_blocklist_blocked_total gauge
EOF

    # Per-feed block counts (IPv4 and IPv6)
    for feed_line in "${FEEDS_ARRAY[@]}"; do
        IFS='|' read -r enabled name url type description <<< "$feed_line"
        [ "$enabled" != "1" ] && continue
        
        local blocks_1h_v4 blocks_24h_v4 blocks_1h_v6 blocks_24h_v6
        blocks_1h_v4=$(get_feed_blocks "$name" "1 hour ago")
        blocks_24h_v4=$(get_feed_blocks "$name" "24 hours ago")
        blocks_1h_v6=$(get_feed_blocks_v6 "$name" "1 hour ago")
        blocks_24h_v6=$(get_feed_blocks_v6 "$name" "24 hours ago")
        
        echo "iptables_blocklist_blocked_total{feed=\"$name\",ip_version=\"4\",period=\"1h\"} $blocks_1h_v4"
        echo "iptables_blocklist_blocked_total{feed=\"$name\",ip_version=\"4\",period=\"24h\"} $blocks_24h_v4"
        echo "iptables_blocklist_blocked_total{feed=\"$name\",ip_version=\"6\",period=\"1h\"} $blocks_1h_v6"
        echo "iptables_blocklist_blocked_total{feed=\"$name\",ip_version=\"6\",period=\"24h\"} $blocks_24h_v6"
    done

    # Feed effectiveness (blocks per 1000 IPs)
    cat <<EOF

# HELP iptables_blocklist_effectiveness Blocks per 1000 IPs in feed (24h)
# TYPE iptables_blocklist_effectiveness gauge
EOF

    for feed_line in "${FEEDS_ARRAY[@]}"; do
        IFS='|' read -r enabled name url type description <<< "$feed_line"
        [ "$enabled" != "1" ] && continue
        
        local ipset_size blocks_v4 blocks_v6 effectiveness_v4 effectiveness_v6
        ipset_size=$(get_ipset_size "${IPSET_PREFIX}-${name}")
        blocks_v4=$(get_feed_blocks "$name" "24 hours ago")
        blocks_v6=$(get_feed_blocks_v6 "$name" "24 hours ago")
        
        # Strip whitespace and ensure integers
        ipset_size=$(echo "$ipset_size" | tr -d '\n' | tr -d ' ')
        blocks_v4=$(echo "$blocks_v4" | tr -d '\n' | tr -d ' ')
        blocks_v6=$(echo "$blocks_v6" | tr -d '\n' | tr -d ' ')
        ipset_size=${ipset_size:-0}
        blocks_v4=${blocks_v4:-0}
        blocks_v6=${blocks_v6:-0}
        
        if [ "$ipset_size" -gt 0 ] 2>/dev/null; then
            effectiveness_v4=$(awk "BEGIN {printf \"%.2f\", ($blocks_v4 / $ipset_size) * 1000}" 2>/dev/null || echo "0")
            effectiveness_v6=$(awk "BEGIN {printf \"%.2f\", ($blocks_v6 / $ipset_size) * 1000}" 2>/dev/null || echo "0")
        else
            effectiveness_v4="0"
            effectiveness_v6="0"
        fi
        
        echo "iptables_blocklist_effectiveness{feed=\"$name\",ip_version=\"4\"} $effectiveness_v4"
        echo "iptables_blocklist_effectiveness{feed=\"$name\",ip_version=\"6\"} $effectiveness_v6"
    done

    # Feed update/cache metrics
    cat <<EOF

# HELP iptables_blocklist_last_update_timestamp Feed cache file last modified timestamp
# TYPE iptables_blocklist_last_update_timestamp gauge
EOF

    for feed_line in "${FEEDS_ARRAY[@]}"; do
        IFS='|' read -r enabled name url type description <<< "$feed_line"
        
        local v4_file="${CACHE_DIR}/${name}-v4.parsed"
        local v6_file="${CACHE_DIR}/${name}-v6.parsed"
        local v4_ts v6_ts
        v4_ts=$(get_file_timestamp "$v4_file")
        v6_ts=$(get_file_timestamp "$v6_file")
        
        echo "iptables_blocklist_last_update_timestamp{feed=\"$name\",ip_version=\"4\"} $v4_ts"
        echo "iptables_blocklist_last_update_timestamp{feed=\"$name\",ip_version=\"6\"} $v6_ts"
    done

    cat <<EOF

# HELP iptables_blocklist_cache_age_seconds Age of feed cache files
# TYPE iptables_blocklist_cache_age_seconds gauge
EOF

    for feed_line in "${FEEDS_ARRAY[@]}"; do
        IFS='|' read -r enabled name url type description <<< "$feed_line"
        
        local v4_file="${CACHE_DIR}/${name}-v4.parsed"
        local v6_file="${CACHE_DIR}/${name}-v6.parsed"
        local v4_age v6_age
        v4_age=$(get_cache_age "$v4_file")
        v6_age=$(get_cache_age "$v6_file")
        
        echo "iptables_blocklist_cache_age_seconds{feed=\"$name\",ip_version=\"4\"} $v4_age"
        echo "iptables_blocklist_cache_age_seconds{feed=\"$name\",ip_version=\"6\"} $v6_age"
    done

    cat <<EOF

# HELP iptables_blocklist_file_size_bytes Feed parsed file sizes
# TYPE iptables_blocklist_file_size_bytes gauge
EOF

    for feed_line in "${FEEDS_ARRAY[@]}"; do
        IFS='|' read -r enabled name url type description <<< "$feed_line"
        
        local v4_file="${CACHE_DIR}/${name}-v4.parsed"
        local v6_file="${CACHE_DIR}/${name}-v6.parsed"
        local v4_size v6_size
        v4_size=$(get_file_size "$v4_file")
        v6_size=$(get_file_size "$v6_file")
        
        echo "iptables_blocklist_file_size_bytes{feed=\"$name\",ip_version=\"4\",type=\"parsed\"} $v4_size"
        echo "iptables_blocklist_file_size_bytes{feed=\"$name\",ip_version=\"6\",type=\"parsed\"} $v6_size"
    done

    # IP version distribution ratio
    cat <<EOF

# HELP iptables_blocklist_ip_version_ratio Ratio of IPv4 to IPv6 addresses per feed
# TYPE iptables_blocklist_ip_version_ratio gauge
EOF

    for feed_line in "${FEEDS_ARRAY[@]}"; do
        IFS='|' read -r enabled name url type description <<< "$feed_line"
        
        local v4_size v6_size total ratio_v4 ratio_v6
        v4_size=$(get_ipset_size "${IPSET_PREFIX}-${name}")
        v6_size=$(get_ipset_size "${IPSET_PREFIX}-${name}-v6")
        
        v4_size=${v4_size:-0}
        v6_size=${v6_size:-0}
        total=$((v4_size + v6_size))
        
        if [ "$total" -gt 0 ] 2>/dev/null; then
            ratio_v4=$(awk "BEGIN {printf \"%.4f\", $v4_size / $total}" 2>/dev/null || echo "0")
            ratio_v6=$(awk "BEGIN {printf \"%.4f\", $v6_size / $total}" 2>/dev/null || echo "0")
        else
            ratio_v4="0"
            ratio_v6="0"
        fi
        
        echo "iptables_blocklist_ip_version_ratio{feed=\"$name\",version=\"4\"} $ratio_v4"
        echo "iptables_blocklist_ip_version_ratio{feed=\"$name\",version=\"6\"} $ratio_v6"
    done

    # Total metrics
    cat <<EOF

# HELP iptables_blocklist_total_unique_ips Total unique IPs across all feeds
# TYPE iptables_blocklist_total_unique_ips gauge
iptables_blocklist_total_unique_ips{ip_version="4"} $(get_total_unique_ips "4")
iptables_blocklist_total_unique_ips{ip_version="6"} $(get_total_unique_ips "6")

# HELP iptables_blocklist_total_rules Total iptables rules
# TYPE iptables_blocklist_total_rules gauge
iptables_blocklist_total_rules $(iptables -S 2>/dev/null | wc -l)

# HELP iptables_blocklist_rule_packets Packet counts from iptables rules
# TYPE iptables_blocklist_rule_packets counter
EOF

    for feed_line in "${FEEDS_ARRAY[@]}"; do
        IFS='|' read -r enabled name url type description <<< "$feed_line"
        [ "$enabled" != "1" ] && continue
        
        local stats_log stats_drop packets_log bytes_log packets_drop bytes_drop
        
        stats_log=$(echo "$iptables_input_stats" | grep " ${IPSET_PREFIX}-${name} " | grep LOG | head -1 | awk '{print $1"|"$2}')
        stats_drop=$(echo "$iptables_input_stats" | grep " ${IPSET_PREFIX}-${name} " | grep DROP | head -1 | awk '{print $1"|"$2}')
        
        if [ -n "$stats_log" ]; then
            packets_log=$(echo "$stats_log" | cut -d'|' -f1)
            bytes_log=$(echo "$stats_log" | cut -d'|' -f2)
            echo "iptables_blocklist_rule_packets{feed=\"$name\",ip_version=\"4\",action=\"log\"} ${packets_log:-0}"
        fi
        
        if [ -n "$stats_drop" ]; then
            packets_drop=$(echo "$stats_drop" | cut -d'|' -f1)
            bytes_drop=$(echo "$stats_drop" | cut -d'|' -f2)
            echo "iptables_blocklist_rule_packets{feed=\"$name\",ip_version=\"4\",action=\"drop\"} ${packets_drop:-0}"
        fi
    done

    cat <<EOF

# HELP iptables_blocklist_rule_bytes Byte counts from iptables rules
# TYPE iptables_blocklist_rule_bytes counter
EOF

    for feed_line in "${FEEDS_ARRAY[@]}"; do
        IFS='|' read -r enabled name url type description <<< "$feed_line"
        [ "$enabled" != "1" ] && continue
        
        local stats_log stats_drop packets_log bytes_log packets_drop bytes_drop
        
        stats_log=$(echo "$iptables_input_stats" | grep " ${IPSET_PREFIX}-${name} " | grep LOG | head -1 | awk '{print $1"|"$2}')
        stats_drop=$(echo "$iptables_input_stats" | grep " ${IPSET_PREFIX}-${name} " | grep DROP | head -1 | awk '{print $1"|"$2}')
        
        if [ -n "$stats_log" ]; then
            packets_log=$(echo "$stats_log" | cut -d'|' -f1)
            bytes_log=$(echo "$stats_log" | cut -d'|' -f2)
            echo "iptables_blocklist_rule_bytes{feed=\"$name\",ip_version=\"4\",action=\"log\"} ${bytes_log:-0}"
        fi
        
        if [ -n "$stats_drop" ]; then
            packets_drop=$(echo "$stats_drop" | cut -d'|' -f1)
            bytes_drop=$(echo "$stats_drop" | cut -d'|' -f2)
            echo "iptables_blocklist_rule_bytes{feed=\"$name\",ip_version=\"4\",action=\"drop\"} ${bytes_drop:-0}"
        fi
    done

    cat <<EOF

# HELP iptables_blocklist_ipset_memory_bytes Memory used by each ipset
# TYPE iptables_blocklist_ipset_memory_bytes gauge
EOF

    for feed_line in "${FEEDS_ARRAY[@]}"; do
        IFS='|' read -r enabled name url type description <<< "$feed_line"
        
        mem_v4=$(get_ipset_memory "${IPSET_PREFIX}-${name}")
        mem_v6=$(get_ipset_memory "${IPSET_PREFIX}-${name}-v6")
        
        echo "iptables_blocklist_ipset_memory_bytes{feed=\"$name\",ip_version=\"4\"} $mem_v4"
        echo "iptables_blocklist_ipset_memory_bytes{feed=\"$name\",ip_version=\"6\"} $mem_v6"
    done

    # Conntrack metrics
    local conntrack_count conntrack_max conntrack_usage
    conntrack_count=$(get_conntrack_count)
    conntrack_max=$(get_conntrack_max)
    
    if [ "$conntrack_max" -gt 0 ] 2>/dev/null; then
        conntrack_usage=$(awk "BEGIN {printf \"%.2f\", ($conntrack_count / $conntrack_max) * 100}" 2>/dev/null || echo "0")
    else
        conntrack_usage="0"
    fi

    # Cache disk metrics
    local disk_info cache_size disk_used disk_avail disk_pct
    disk_info=$(get_cache_disk_usage)
    cache_size=$(get_total_cache_size)
    disk_used=$(echo "$disk_info" | cut -d'|' -f1)
    disk_avail=$(echo "$disk_info" | cut -d'|' -f2)
    disk_pct=$(echo "$disk_info" | cut -d'|' -f3 | tr -d '%')

    cat <<EOF

# HELP iptables_blocklist_conntrack_entries Current conntrack entries
# TYPE iptables_blocklist_conntrack_entries gauge
iptables_blocklist_conntrack_entries $conntrack_count

# HELP iptables_blocklist_conntrack_max Maximum conntrack entries
# TYPE iptables_blocklist_conntrack_max gauge
iptables_blocklist_conntrack_max $conntrack_max

# HELP iptables_blocklist_conntrack_usage_percent Conntrack usage percentage
# TYPE iptables_blocklist_conntrack_usage_percent gauge
iptables_blocklist_conntrack_usage_percent $conntrack_usage

# HELP iptables_blocklist_cache_disk_used_bytes Disk space used by cache partition
# TYPE iptables_blocklist_cache_disk_used_bytes gauge
iptables_blocklist_cache_disk_used_bytes $disk_used

# HELP iptables_blocklist_cache_disk_available_bytes Disk space available on cache partition
# TYPE iptables_blocklist_cache_disk_available_bytes gauge
iptables_blocklist_cache_disk_available_bytes $disk_avail

# HELP iptables_blocklist_cache_disk_usage_percent Cache partition disk usage percentage
# TYPE iptables_blocklist_cache_disk_usage_percent gauge
iptables_blocklist_cache_disk_usage_percent ${disk_pct:-0}

# HELP iptables_blocklist_cache_total_size_bytes Total size of cache directory
# TYPE iptables_blocklist_cache_total_size_bytes gauge
iptables_blocklist_cache_total_size_bytes $cache_size

# HELP iptables_blocklist_whitelist_size Whitelist ipset size
# TYPE iptables_blocklist_whitelist_size gauge
iptables_blocklist_whitelist_size{ip_version="4"} $(get_ipset_size "$WHITELIST_IPSET")
iptables_blocklist_whitelist_size{ip_version="6"} $(get_ipset_size "$WHITELIST_IPSET_V6")

# HELP iptables_blocklist_scrape_timestamp_seconds Unix timestamp of metric generation
# TYPE iptables_blocklist_scrape_timestamp_seconds gauge
iptables_blocklist_scrape_timestamp_seconds $(date +%s)

# HELP iptables_blocklist_exporter_runtime_seconds Exporter runtime in seconds
# TYPE iptables_blocklist_exporter_runtime_seconds gauge
iptables_blocklist_exporter_runtime_seconds $(($(date +%s) - start_time))
EOF
    echo ""
}

run_http_server() {
    echo "Starting iptables blocklist exporter on port $HTTP_PORT..."
    
    if ! command -v nc >/dev/null 2>&1; then
        echo "ERROR: netcat (nc) is required for HTTP mode"
        echo "Install with: yum install nmap-ncat  (RHEL/CentOS)"
        echo "          or: apt install netcat     (Debian/Ubuntu)"
        exit 1
    fi
    
    while true; do
        {
            read -r request
            if [[ "$request" =~ ^GET\ /metrics ]]; then
                printf "HTTP/1.1 200 OK\r\nContent-Type: text/plain; version=0.0.4; charset=utf-8\r\n\r\n"
                cache_journal_data
                cache_feeds_config
                generate_metrics
            else
                printf "HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n"
                echo "<h1>iptables Blocklist Metrics Exporter v${SCRIPT_VERSION}</h1>"
                echo "<p>Per-feed threat blocking statistics</p>"
                echo "<p><a href='/metrics'>Metrics</a></p>"
            fi
        } | nc -l -p "$HTTP_PORT" -q 1 2>/dev/null
    done
}

main() {
    parse_args "$@"
    
    [ ! -d "$CONFIG_DIR" ] && { echo "ERROR: $CONFIG_DIR not found. Run iptables-blocklists.sh first" >&2; exit 1; }
    
    # Prevent multiple instances (skip for HTTP mode as it should run continuously)
    [ "$HTTP_MODE" != true ] && acquire_lock
    
    if [ "$HTTP_MODE" = true ]; then
        run_http_server
    elif [ -n "$OUTPUT_FILE" ]; then
        # Cache data before generating metrics
        cache_journal_data
        cache_feeds_config
        
        # Ensure output directory exists
        mkdir -p "$(dirname "$OUTPUT_FILE")"
        
        # Create temp file in /tmp (not in node_exporter directory!)
        # This prevents node_exporter from seeing partial writes
        local temp_file
        temp_file=$(mktemp /tmp/iptables_metrics.XXXXXX)
        
        # Generate metrics to temp file
        generate_metrics > "$temp_file"
        
        # FORCE NEW INODE: Delete old file first, then move
        # Some node_exporter versions cache file descriptors
        rm -f "$OUTPUT_FILE"
        
        # Move temp file to final location
        mv "$temp_file" "$OUTPUT_FILE"
        
        # Ensure node_exporter user can read it
        chmod 644 "$OUTPUT_FILE"
    else
        cache_journal_data
        cache_feeds_config
        generate_metrics
    fi
}

main "$@"
