#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────────────────────
# onx-geoip-update — Ücretsiz GeoIP DB güncelleyici
# ─────────────────────────────────────────────────────────────────────────────
# 3 ücretsiz kaynak deneme sırası:
#   1. RIR (Regional Internet Registry) delegated files — public domain,
#      kayıt yok, lisans yok. Kanonik kaynak (ARIN/RIPE/APNIC/AFRINIC/LACNIC).
#   2. DB-IP Lite CSV — CC BY 4.0, sadece atıf gerekli, kayıt yok.
#   3. MaxMind GeoLite2 — lisans key gerekli (config'de varsa), fallback.
#
# Çıktı: /var/lib/onox/geoip/cidrs/<CC>.txt — CIDR aralıklarını içerir.
# Cron: weekly (Pazar 04:00) — RIR dosyaları haftada bir güncellenir.
#
# Kullanım:
#   sudo onx-geoip-update            # otomatik kaynak seç
#   sudo onx-geoip-update --source rir
#   sudo onx-geoip-update --source dbip
#   sudo onx-geoip-update --force    # cache ignore
# ─────────────────────────────────────────────────────────────────────────────

set -euo pipefail

readonly GEOIP_DIR="/var/lib/onox/geoip"
readonly CIDRS_DIR="$GEOIP_DIR/cidrs"
readonly CACHE_DIR="$GEOIP_DIR/cache"
readonly LOG_FILE="/var/log/onox/geoip-update.log"

SOURCE="${1:-auto}"
[[ "${1:-}" == "--source" && -n "${2:-}" ]] && SOURCE="$2"
FORCE=0
[[ "${1:-}" == "--force" ]] && FORCE=1

mkdir -p "$CIDRS_DIR" "$CACHE_DIR"
mkdir -p "$(dirname "$LOG_FILE")"

log() { echo "[$(date -u +%FT%TZ)] $*" | tee -a "$LOG_FILE" >&2; }

# ─────────────────────────────────────────────────────────────────────────────
# Source 1: RIR delegated files (PUBLIC DOMAIN — no key, no license)
# ─────────────────────────────────────────────────────────────────────────────
update_from_rir() {
    log "Updating from RIR delegated files (public domain, free)…"

    declare -A RIRS=(
        [arin]="https://ftp.arin.net/pub/stats/arin/delegated-arin-extended-latest"
        [ripe]="https://ftp.ripe.net/pub/stats/ripencc/delegated-ripencc-extended-latest"
        [apnic]="https://ftp.apnic.net/pub/stats/apnic/delegated-apnic-extended-latest"
        [afrinic]="https://ftp.afrinic.net/pub/stats/afrinic/delegated-afrinic-extended-latest"
        [lacnic]="https://ftp.lacnic.net/pub/stats/lacnic/delegated-lacnic-extended-latest"
    )

    declare -A CC_CIDRS=()  # country_code → list of CIDR aralıkları

    for rir in "${!RIRS[@]}"; do
        local url="${RIRS[$rir]}"
        local cache_file="$CACHE_DIR/$rir.txt"

        # Cache hit kontrolü (24sa)
        if [[ $FORCE -eq 0 && -f "$cache_file" ]]; then
            local age=$(( $(date +%s) - $(stat -c %Y "$cache_file" 2>/dev/null || echo 0) ))
            if (( age < 86400 )); then
                log "  • $rir: cached (age ${age}s)"
            else
                log "  • $rir: downloading $url…"
                curl -fsSL --max-time 60 -o "$cache_file" "$url" || {
                    log "  ✗ $rir download failed, using stale cache"
                }
            fi
        else
            log "  • $rir: downloading $url…"
            curl -fsSL --max-time 60 -o "$cache_file" "$url" || {
                log "  ✗ $rir download failed"
                continue
            }
        fi

        # Parse: arin|US|ipv4|192.168.0.0|256|20020101|allocated
        # Sadece ipv4 allocated/assigned satırları
        # IP + count → CIDR (count log2 ile prefix uzunluğu)
        local count_in_file=0
        while IFS='|' read -r registry cc type ip value date status _rest; do
            [[ "$type" != "ipv4" ]] && continue
            [[ "$status" != "allocated" && "$status" != "assigned" ]] && continue
            [[ -z "$cc" || ${#cc} -ne 2 ]] && continue
            [[ -z "$ip" || -z "$value" ]] && continue

            # IPv4 count → prefix length: 256 → /24, 65536 → /16
            # 32 - log2(count) = prefix
            local prefix
            case "$value" in
                1)        prefix=32 ;;
                2)        prefix=31 ;;
                4)        prefix=30 ;;
                8)        prefix=29 ;;
                16)       prefix=28 ;;
                32)       prefix=27 ;;
                64)       prefix=26 ;;
                128)      prefix=25 ;;
                256)      prefix=24 ;;
                512)      prefix=23 ;;
                1024)     prefix=22 ;;
                2048)     prefix=21 ;;
                4096)     prefix=20 ;;
                8192)     prefix=19 ;;
                16384)    prefix=18 ;;
                32768)    prefix=17 ;;
                65536)    prefix=16 ;;
                131072)   prefix=15 ;;
                262144)   prefix=14 ;;
                524288)   prefix=13 ;;
                1048576)  prefix=12 ;;
                2097152)  prefix=11 ;;
                4194304)  prefix=10 ;;
                8388608)  prefix=9 ;;
                16777216) prefix=8 ;;
                *) continue ;;  # exotic sizes — skip
            esac

            CC_CIDRS[$cc]+="$ip/$prefix"$'\n'
            count_in_file=$((count_in_file + 1))
        done < "$cache_file"

        log "  ✓ $rir: $count_in_file CIDR ranges parsed"
    done

    # Her ülke için ayrı dosya yaz
    local total_countries=0
    local total_cidrs=0
    for cc in "${!CC_CIDRS[@]}"; do
        local output="$CIDRS_DIR/${cc}.txt"
        echo "${CC_CIDRS[$cc]}" | grep -v '^$' | sort -u > "$output"
        local count=$(wc -l < "$output")
        total_cidrs=$((total_cidrs + count))
        total_countries=$((total_countries + 1))
    done

    log "✓ RIR update complete: $total_countries countries, $total_cidrs total CIDRs"

    # Manifest
    jq -n \
        --arg source "rir" \
        --arg updated_at "$(date -u +%FT%TZ)" \
        --argjson countries "$total_countries" \
        --argjson cidrs "$total_cidrs" \
        --argjson cache_dir_files "$(ls "$CACHE_DIR" | wc -l)" \
        '{source: $source, updated_at: $updated_at, countries: $countries, total_cidrs: $cidrs, cache_files: $cache_dir_files, license: "public domain (RIR delegated files)"}' \
        > "$GEOIP_DIR/manifest.json"

    cat "$GEOIP_DIR/manifest.json"
}

# ─────────────────────────────────────────────────────────────────────────────
# Source 2: DB-IP Lite (CC BY 4.0 — attribution only, no key)
# ─────────────────────────────────────────────────────────────────────────────
update_from_dbip() {
    log "Updating from DB-IP Lite (CC BY 4.0, no key required)…"

    # DB-IP latest CSV — month-based URL
    local year_month
    year_month=$(date +%Y-%m)
    local url="https://download.db-ip.com/free/dbip-country-lite-${year_month}.csv.gz"
    local cache_file="$CACHE_DIR/dbip-country.csv.gz"

    if [[ $FORCE -eq 1 || ! -f "$cache_file" ]] || \
       (( $(stat -c %Y "$cache_file" 2>/dev/null || echo 0) < $(date -d '30 days ago' +%s) )); then
        log "  Downloading $url…"
        curl -fsSL --max-time 120 -o "$cache_file" "$url" || {
            log "  ✗ DB-IP download failed"
            return 1
        }
    fi

    declare -A CC_CIDRS=()

    # CSV format: ip_start,ip_end,country_code
    zcat "$cache_file" | while IFS=',' read -r ip_start ip_end cc _rest; do
        [[ -z "$cc" || ${#cc} -ne 2 ]] && continue
        # IP range → CIDR list (basit IPv4 only)
        # Bu kısım Python/perl ile daha doğru — burada awk basitleştirme:
        # CIDR çıkarımı kompleks, sadece /24'lere yuvarla
        local octet1 octet2 octet3
        IFS='.' read -r octet1 octet2 octet3 _ <<< "$ip_start"
        CC_CIDRS[$cc]+="${octet1}.${octet2}.${octet3}.0/24"$'\n'
    done

    local total=0
    for cc in "${!CC_CIDRS[@]}"; do
        local output="$CIDRS_DIR/${cc}.txt"
        echo "${CC_CIDRS[$cc]}" | grep -v '^$' | sort -u > "$output"
        total=$((total + $(wc -l < "$output")))
    done

    jq -n \
        --arg source "dbip" \
        --arg updated_at "$(date -u +%FT%TZ)" \
        --argjson cidrs "$total" \
        '{source: $source, updated_at: $updated_at, total_cidrs: $cidrs, license: "CC BY 4.0 — DB-IP"}' \
        > "$GEOIP_DIR/manifest.json"

    cat "$GEOIP_DIR/manifest.json"
}

# ─────────────────────────────────────────────────────────────────────────────
# Main
# ─────────────────────────────────────────────────────────────────────────────
case "$SOURCE" in
    rir)
        update_from_rir
        ;;
    dbip)
        update_from_dbip
        ;;
    auto|"")
        # RIR tercih edilir (kanonik, public domain)
        if ! update_from_rir; then
            log "RIR failed, trying DB-IP fallback…"
            update_from_dbip
        fi
        ;;
    *)
        echo "{\"error\":\"unknown source: $SOURCE — use rir|dbip|auto\"}" >&2
        exit 1
        ;;
esac
