#!/usr/bin/env bash
#
# onx-fail2ban-stats — Aggregate stats fail2ban SQLite DB'sinden.
#
# Output: hourly chart (24h), bans_by_jail, top_attackers_24h, today/week/month totals
#
# DB: /var/lib/fail2ban/fail2ban.sqlite3
# Tables:
#   bans  (jail, ip, timeofban, data, bantime)
#   bips  (ip, jail, timeofban, bancount, lastban, bandata)

set -euo pipefail

readonly DB="/var/lib/fail2ban/fail2ban.sqlite3"

if [[ ! -r "$DB" ]]; then
    jq -nc '{ok:false,error:"fail2ban DB not readable",path:"/var/lib/fail2ban/fail2ban.sqlite3"}' >&2
    exit 2
fi

if ! command -v sqlite3 &>/dev/null; then
    jq -nc '{ok:false,error:"sqlite3 not installed"}' >&2
    exit 2
fi

# ── Today / week / month counts ───────────────────────────────────────────
today_bans=$(sqlite3 "$DB" "SELECT COUNT(*) FROM bans WHERE timeofban >= strftime('%s','now','start of day')" 2>/dev/null || echo 0)
week_bans=$(sqlite3 "$DB" "SELECT COUNT(*) FROM bans WHERE timeofban >= strftime('%s','now','-7 days')" 2>/dev/null || echo 0)
month_bans=$(sqlite3 "$DB" "SELECT COUNT(*) FROM bans WHERE timeofban >= strftime('%s','now','-30 days')" 2>/dev/null || echo 0)
total_failed_today=$(sqlite3 "$DB" "SELECT COALESCE(SUM(json_extract(data,'$.matches.length()')),0) FROM bans WHERE timeofban >= strftime('%s','now','start of day')" 2>/dev/null || echo 0)

# ── Currently banned (sum of bips with active ban) ────────────────────────
currently_banned=$(sqlite3 "$DB" "SELECT COUNT(DISTINCT ip) FROM bans WHERE timeofban + bantime > strftime('%s','now')" 2>/dev/null || echo 0)

# ── Hourly chart (last 24h) ───────────────────────────────────────────────
hourly_json="["
first=1
for h in {23..0}; do
    start_ts=$(date -d "$h hours ago" +%s 2>/dev/null || date -v "-${h}H" +%s)
    end_ts=$(date -d "$((h-1)) hours ago" +%s 2>/dev/null || date -v "-$((h-1))H" +%s)
    [[ $h -eq 0 ]] && end_ts=$(date +%s)
    label=$(date -d "@$start_ts" +%H:00 2>/dev/null || date -r "$start_ts" +%H:00)

    count=$(sqlite3 "$DB" "SELECT COUNT(*) FROM bans WHERE timeofban >= $start_ts AND timeofban < $end_ts" 2>/dev/null || echo 0)
    failed=$((count * 5))  # tahmini: her ban ~5 failed attempt

    [[ $first -eq 0 ]] && hourly_json+=","
    first=0
    hourly_json+=$(jq -nc --arg hour "$label" --argjson bans "$count" --argjson failed "$failed" \
        '{hour:$hour,bans:$bans,failed:$failed}')
done
hourly_json+="]"

# ── Bans by jail (last 7 days) ────────────────────────────────────────────
bans_by_jail_json="["
first=1
sqlite3 -separator '|' "$DB" \
    "SELECT jail, COUNT(*) as cnt FROM bans
     WHERE timeofban >= strftime('%s','now','-7 days')
     GROUP BY jail ORDER BY cnt DESC LIMIT 20" 2>/dev/null | \
while IFS='|' read -r jail cnt; do
    [[ -z "$jail" ]] && continue
    [[ $first -eq 0 ]] && bans_by_jail_json+=","
    first=0
    bans_by_jail_json+=$(jq -nc --arg jail "$jail" --argjson count "$cnt" '{jail:$jail,count:$count}')
done
# pipe subshell issue — write to temp instead
bans_by_jail_tmp="$(mktemp)"
sqlite3 -separator '|' "$DB" \
    "SELECT jail, COUNT(*) as cnt FROM bans
     WHERE timeofban >= strftime('%s','now','-7 days')
     GROUP BY jail ORDER BY cnt DESC LIMIT 20" 2>/dev/null > "$bans_by_jail_tmp" || true

bans_by_jail_json="$(awk -F'|' 'BEGIN{print "["} NR==1{sep=""}{printf "%s{\"jail\":\"%s\",\"count\":%d}",sep,$1,$2; sep=","} END{print "]"}' "$bans_by_jail_tmp")"
rm -f "$bans_by_jail_tmp"

# ── Top attackers (24h) ────────────────────────────────────────────────────
top_attackers_tmp="$(mktemp)"
sqlite3 -separator '|' "$DB" \
    "SELECT ip, bancount, lastban,
            (CASE WHEN (lastban > strftime('%s','now') - 3600) THEN 1 ELSE 0 END) as recent
     FROM bips
     ORDER BY bancount DESC LIMIT 10" 2>/dev/null > "$top_attackers_tmp" || true

# Build JSON with optional country lookup
top_attackers_json="["
first=1
while IFS='|' read -r ip bancount lastban recent; do
    [[ -z "$ip" ]] && continue

    cc=""
    if [[ -d /var/lib/onox/geoip/cidrs ]] && command -v grepcidr &>/dev/null && [[ "$ip" != *:* ]]; then
        for f in /var/lib/onox/geoip/cidrs/*.txt; do
            [[ -f "$f" ]] || continue
            if grepcidr "$ip" "$f" &>/dev/null; then
                cc="$(basename "$f" .txt | tr '[:lower:]' '[:upper:]')"
                break
            fi
        done
    fi

    # banned currently?
    banned=$(sqlite3 "$DB" "SELECT COUNT(*) FROM bans WHERE ip='$ip' AND timeofban+bantime > strftime('%s','now')" 2>/dev/null || echo 0)
    banned_bool="false"
    [[ "$banned" -gt 0 ]] && banned_bool="true"

    [[ $first -eq 0 ]] && top_attackers_json+=","
    first=0
    top_attackers_json+=$(jq -nc \
        --arg ip "$ip" --arg cc "$cc" \
        --argjson attempts "$bancount" \
        --argjson banned "$banned_bool" \
        '{ip:$ip,country_code:$cc,attempts:$attempts,banned:$banned}')
done < "$top_attackers_tmp"
rm -f "$top_attackers_tmp"
top_attackers_json+="]"

# ── Final output ──────────────────────────────────────────────────────────
jq -nc \
    --argjson today_bans "$today_bans" \
    --argjson today_failed "$total_failed_today" \
    --argjson week_bans "$week_bans" \
    --argjson month_bans "$month_bans" \
    --argjson currently_banned "$currently_banned" \
    --argjson top_attackers "$top_attackers_json" \
    --argjson bans_by_jail "$bans_by_jail_json" \
    --argjson hourly "$hourly_json" \
    '{
        ok: true,
        today_bans: $today_bans,
        today_failed: $today_failed,
        week_bans: $week_bans,
        month_bans: $month_bans,
        currently_banned: $currently_banned,
        top_attackers_24h: $top_attackers,
        bans_by_jail: $bans_by_jail,
        hourly: $hourly
    }'
