#!/bin/bash
#
# NixMon Agent Kurulum Scripti v3.0.143
#   v3.0.143 — Disk retention: 1d=400g/1mo=5y pencere + satır-cap (yüksek-kardinaliteli POP koruması) + Akvorado CH log TTL(7g)
# Tüm dosyalar mon.nixcon.com.tr'den indirilir.
#
# Sürüm notları:
#   v3.0.130 — BGP large community (32-bit ASN) desteği: ASN>65535 community
#             değerleri large-community formatında (ASN:X:Y) anons edilir.
#   v3.0.128 — IPFIX/inline-monitoring sampling ×config fix (önceden sadece sFlow
#             çarpılıyordu, IPFIX 1000× düşük çıkıyordu) + goflow2 GRE/IP-in-IP/TEB
#             recursive tünel-decap (inline-monitoring dataLinkFrameSection iç IP).
#   v3.0.67 — Blok SPLIT mantığı. /24 redirect next step /32 ise CoveredIPs'teki HER
#             IP için ayrı trafik check + üstündeyse ayrı /32 redirect (splitToPerIPRedirect).
#             /24 STAY (MaxStepReached=true SQLite persist). Cluster auto-remove: TÜM IP
#             toplamı rule.MbpsDownloadThreshold altıysa /24 kaldırılır (Berk verbatim
#             "TÜM IP'lere saldırı bitmeden /24 ASLA kaldırılamaz"). BlockMemberWaitSeconds
#             SPLIT'e özel EK bekleme (D-1). edge_active_redirects'a max_step_reached +
#             covered_ips sütunları (ALTER ADD COLUMN IF NOT EXISTS — idempotent).
#   v3.0.65 — DDoS escalation cycle refactor (#04). Eski hysteresis (clear ratio,
#             max hold extensions, window median, pre/post removal monitor,
#             ascending/descending wait) tamamen kaldırıldı. Yeni döngüsel
#             state machine: atak başlar → S1 community apply → panel
#             `escalation_check_interval_seconds` (default 60sn) bekle →
#             LOCAL stat'a bak (backend sorgu YOK, gecikme yok) → trafik
#             düşmediyse S2 → her step yükselişinde auto_remove RESET. Komple
#             kaldır kararı rule default threshold karşılaştırması ile.
#             Auto_remove dolduğunda + trafik yok → cold_watchlist tablosuna
#             UPSERT (süre = auto_remove × 5). Re-attack cold watchlist'te ise
#             S1 değil son step community HEMEN apply (/32→/32 IP, /24→/24
#             blok). Per-step threshold opsiyonel (threshold_mbps + threshold_pps
#             nullable; NULL → rule default; OR mantığı). DB migrations
#             103_cold_watchlist + 104_rule_community_per_step_threshold.
#             Eski hysteresis kolonları DB'de korundu (agent okumaz) — backward
#             compat.
#   v3.0.64 — Manuel cancel MONITORING state (#04 ara plan). Operatör panelden
#             saldırı kuralını manuel iptal ettiğinde escalation cycle hemen
#             durur, IP MONITORING state'ine geçer; trafik tekrar yükselirse
#             yeni cycle başlatılır.
#   v3.0.63 — TTL + peak detection (#04 ara plan). Her escalation state için
#             TTL + son N saniyenin peak değeri saklanır; downscale kararı
#             peak referansı ile alınır (false negative azaltıldı).
#   v3.0.62 — Branding agent-serve worker (#P2.3 Phase 2). Agent custom domain
#             üzerinden mon backend'i reverse proxy eder; 5dk tick'te
#             /agent/brand-serve/config çekip nginx + certbot auto kurar.
#             Mon panel brand fetch ETMEZ (default görür).
#   v3.0.23 — KRİTİK FIX: v3.0.22 Goflow2Record.Proto int olarak tanımlıydı,
#             goflow2 proto'yu STRING ("TCP"/"UDP") gönderiyor → gojson
#             strict decode fail → tüm sFlow record'ları sessizce drop
#             oluyordu → trafik 0.00 in/out, 0 IP. v3.0.23: Proto string,
#             portMonitorObserve "tcp"|"udp" eşleşmesi. ProtoName + normalizeProto
#             helper kaldırıldı (gereksizdi).
#   v3.0.22 — [GERİ ÇEKİLDİ] Port Monitör agent-side aggregator (#26). goflow2 sample
#             pipeline'ında her flow için src/dst port + protokol (TCP/UDP)
#             okunur, panel'den çekilen kurallarla CIDR + direction + protocol
#             + port filtresine göre eşleşirse rolling pencere boyunca per-IP
#             connection sayımı tutulur. window_seconds sonunda toplam eşiği
#             (threshold_conn) aşarsa /api/v1/agent/port-monitor/alert post
#             edilir (flood guard: rule başına 30dk minimum aralık). watch
#             mode: threshold=0 → alarm yok, sadece snapshot. Local API: GET
#             /edge/port-stats/<rule_id> anlık top50 + total döner.
#   v3.0.21 — Anti-crack Ed25519 lisans imza doğrulaması (#22). Backend her
#             lisans cevabını gömülü public key ile imzalar; agent verify
#             yapar (replay koruması: nonce + signed_at ±5dk pencere).
#             DNS spoofing veya sahte lisans sunucusu = licenseOK=false
#             anında, grace YOK. Public key panel'de döner; değiştirilince
#             agent kodu güncellenmeli.
#   v3.0.20 — SQLite uzun vadeli ölçeklenebilirlik (#32). Hiyerarşik aggregator
#             1m → 1h → 1d (3 yıl) → 1mo (sınırsız). Retention: 1m=7gün,
#             1h=90gün, 1d=3yıl, 1mo=15yıl üst limit, edge_attacks=1yıl.
#             auto_vacuum=INCREMENTAL + saatlik incremental_vacuum,
#             haftalık VACUUM + integrity_check + ANALYZE. /diag/snapshot'a
#             db_size_mb alanı. Disk size saatlik log.
#   v3.0.19 — Operasyonel mail agent-side (#31). DDoS/redirect/escalation/remove
#             bildirimleri agent'ın kendisi müşteri SMTP'siyle gönderir; panel
#             offline iken bile mail gider. SMS panel tarafında kalır (NetGSM,
#             kredi modeli — kullanıcı kuralı). syncNotifyConfig 60sn poll,
#             quiet hours + event-bazlı tercih desteği, STARTTLS/SSL/none.
#   v3.0.18 — Carpet Bombing + Subnet Spike kural tipleri (3 saldırı modu).
#             single_ip + opsiyonel % artış / mutlak spike, carpet_bombing
#             (per-IP eşik üzerinde IP sayısı >= required → blok), subnet_spike
#             (block toplam rolling baseline + % veya mutlak fark). Edge-first
#             uyumlu — panel offline iken de çalışır. spikeRing in-memory
#             rolling window per-IP ve per-subnet.
#   v3.0.17 — Lisans 'ulaşamamak' = err VEYA non-200 (nginx 502'de de grace).
#             4 saatten az ulaşamazsa çalış, fazla ise kes (kullanıcı kuralı).
#   v3.0.16 — Lisans grace boot fix (lastLicCheck zero → grace başlat).
#   v3.0.15 — Rules disk cache (/etc/nixmon-agent/rules.cache). Panel offline
#             iken agent restart yapılırsa son sync'ten kalan kurallar yüklenir,
#             edge-first koruma kesilmez.
#   v3.0.14 — Offline queue (SQLite edge_pending_reports). Panel'e ulaşılamayan
#             trigger/escalate/remove rapor POST'ları kuyrukta saklanır,
#             panel açılınca otomatik replay. Inject endpoint race fix.
#   v3.0.13 — EDGE-FIRST: agent kendisi LOKAL gobgp CLI ile BGP push yapar.
#             Backend SSH path yedek (legacy agent uyumluluk).
#   v3.0.12 — Test /diag/inject-traffic endpoint (E2E test akışı).
#   v3.0.11 — Zengin DDoS bildirimi: threshold + announced_prefix payload.
#   v3.0.10 — Kural bazlı IP-block kapsamı + Bare-array decode bug fixi.
#   v3.0.9  — Per-IP rolling 15sn smoothing.
#   v3.0.8  — FastNetMon paritesi.
#   v3.0.7  — Edge-first SQLite + local API 9091.
#
# Binary URL: ${REPO_URL}/agent/nixmon-agent  (latest stable, şu an v3.0.142)
# Bu URL daima en güncel stable symlink'i serve eder; install.sh kullanıcıya
# eski versiyon indirme seçeneği sunmaz (Berk kuralı: stable yaklaşım).
#

set -e

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

log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[OK]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }

SERVER_ID=""
API_TOKEN=""
BACKEND_URL="https://mon.nixcon.com.tr"
REPO_URL="http://mon.nixcon.com.tr:8080"
SSH_PUBKEY=""

while [[ $# -gt 0 ]]; do
    case $1 in
        --server-id=*) SERVER_ID="${1#*=}"; shift ;;
        --api-token=*) API_TOKEN="${1#*=}"; shift ;;
        --backend-url=*) BACKEND_URL="${1#*=}"; shift ;;
        --repo-url=*) REPO_URL="${1#*=}"; shift ;;
        --ssh-pubkey=*) SSH_PUBKEY="${1#*=}"; shift ;;
        *) shift ;;
    esac
done

if [ -z "$SERVER_ID" ] || [ -z "$API_TOKEN" ]; then
    log_error "Gerekli parametreler eksik!"
    echo "Kullanım: $0 --server-id=UUID --api-token=TOKEN"
    exit 1
fi

echo ""
echo "╔══════════════════════════════════════════════════════════════╗"
echo "║    NixMon Agent Kurulum v3.0.128 (IPFIX sampling + tünel-decap)  ║"
echo "║           DDoS Tespit & Yönlendirme Sistemi                  ║"
echo "╚══════════════════════════════════════════════════════════════╝"
echo ""

# ==================== SİSTEM KONTROLÜ ====================
log_info "Sistem kontrol ediliyor..."

if [ "$EUID" -ne 0 ]; then
    log_error "Bu script root olarak çalıştırılmalı!"
    exit 1
fi

if [ -f /etc/debian_version ]; then
    OS="debian"
elif [ -f /etc/redhat-release ]; then
    OS="redhat"
else
    log_error "Desteklenmeyen işletim sistemi!"
    exit 1
fi

log_success "OS: $OS"

# ==================== BAĞIMLILIKLAR ====================
log_info "Bağımlılıklar kuruluyor..."

if [ "$OS" = "debian" ]; then
    apt-get update -qq
    apt-get install -y -qq curl wget jq coreutils >/dev/null 2>&1 || true
else
    yum install -y -q curl wget jq coreutils >/dev/null 2>&1 || true
fi

log_success "Bağımlılıklar hazır"

# ==================== DİZİNLER ====================
log_info "Dizinler oluşturuluyor..."

mkdir -p /etc/nixmon-agent
mkdir -p /var/log/nixmon-agent

log_success "Dizinler oluşturuldu"

# ==================== SSH KEY KURULUMU ====================
mkdir -p /root/.ssh
chmod 700 /root/.ssh
touch /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys

# 2026-07-05 Berk fix: mon sunucusundan otomatik key çek (panel'den agent'a
# passwordless SSH için). Idempotent — zaten varsa yeniden eklemez.
log_info "Mon sunucusu SSH pubkey indiriliyor..."
MON_PUBKEY=$(curl -sSL --max-time 5 "${REPO_URL}/agent/mon-key.pub" 2>/dev/null)
if [ -n "$MON_PUBKEY" ] && echo "$MON_PUBKEY" | grep -qE '^ssh-'; then
    if ! grep -qF "$MON_PUBKEY" /root/.ssh/authorized_keys 2>/dev/null; then
        echo "$MON_PUBKEY" >> /root/.ssh/authorized_keys
        log_success "Mon SSH pubkey eklendi (mon → bu agent passwordless SSH aktif)"
    else
        log_success "Mon SSH pubkey zaten kayıtlı"
    fi
else
    log_error "Mon SSH pubkey indirilemedi! (${REPO_URL}/agent/mon-key.pub)"
fi

# Ek pubkey (opsiyonel, --ssh-pubkey= ile)
if [ -n "$SSH_PUBKEY" ]; then
    log_info "Ek SSH key kuruluyor..."
    if ! grep -qF "$SSH_PUBKEY" /root/.ssh/authorized_keys 2>/dev/null; then
        echo "$SSH_PUBKEY" >> /root/.ssh/authorized_keys
    fi
    log_success "Ek SSH key kuruldu"
fi

# ==================== GOFLOW2 KURULUMU ====================
# Phase 12 (2026-07-05 Berk): use_akvorado_consumer:true → goflow2 kullanılmıyor.
# Yine de fallback için binary indir (config'te use_goflow2 aktif edilirse gerek).
log_info "goflow2 indiriliyor (fallback binary)..."

if [ ! -f /usr/bin/goflow2 ]; then
    curl -sSL -o /usr/bin/goflow2 "${REPO_URL}/agent/goflow2/goflow2" || {
        log_error "goflow2 indirilemedi!"
        exit 1
    }
    chmod +x /usr/bin/goflow2
    log_success "goflow2 kuruldu"
else
    log_success "goflow2 zaten kurulu"
fi

# ==================== DOCKER KURULUMU (OFFLINE mon-only) ====================
# Phase 12 (2026-07-05 Berk kural: hicbir sey disaridan cekilmez, mon-only).
# Docker static binary + compose plugin mon'da hazir.
log_info "Docker kontrol ediliyor..."

install_docker_offline() {
    log_info "Docker mon.nixcon.com.tr'den (offline) kuruluyor..."

    # Docker static binary tarball
    curl -# -o /tmp/docker.tgz "${REPO_URL}/agent/docker/docker-27.3.1.tgz" || {
        log_error "Docker tarball indirilemedi!"
        return 1
    }
    tar xzf /tmp/docker.tgz -C /tmp
    install -m 0755 /tmp/docker/* /usr/local/bin/
    rm -rf /tmp/docker /tmp/docker.tgz

    # docker-compose plugin (v2 subcommand olarak /usr/local/lib/docker/cli-plugins)
    mkdir -p /usr/local/lib/docker/cli-plugins
    curl -sL -o /usr/local/lib/docker/cli-plugins/docker-compose \
        "${REPO_URL}/agent/docker/docker-compose" || {
        log_error "docker-compose plugin indirilemedi!"
        return 1
    }
    chmod +x /usr/local/lib/docker/cli-plugins/docker-compose

    # Systemd unit — Docker static binary sistem service olarak calisir
    cat > /etc/systemd/system/docker.service << 'DOCKEREOF'
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target
Wants=network-online.target
Requires=docker.socket
StartLimitBurst=3
StartLimitIntervalSec=60

[Service]
Type=notify
ExecStart=/usr/local/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutStartSec=0
RestartSec=2
Restart=always
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity
TasksMax=infinity
Delegate=yes
KillMode=process
OOMScoreAdjust=-500

[Install]
WantedBy=multi-user.target
DOCKEREOF

    cat > /etc/systemd/system/docker.socket << 'SOCKEOF'
[Unit]
Description=Docker Socket for the API

[Socket]
ListenStream=/var/run/docker.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker

[Install]
WantedBy=sockets.target
SOCKEOF

    # containerd systemd unit (bundled in docker tarball)
    cat > /etc/systemd/system/containerd.service << 'CONTAINEREOF'
[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target local-fs.target

[Service]
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/local/bin/containerd
Restart=always
RestartSec=5
Delegate=yes
KillMode=process
OOMScoreAdjust=-999
LimitNOFILE=infinity
LimitNPROC=infinity
LimitCORE=infinity

[Install]
WantedBy=multi-user.target
CONTAINEREOF

    # docker group (için socket ownership)
    groupadd -f docker

    systemctl daemon-reload
    systemctl enable containerd docker.socket docker
    systemctl start containerd
    systemctl start docker.socket
    systemctl start docker

    sleep 3
    if docker info >/dev/null 2>&1; then
        log_success "Docker kuruldu ve calisiyor (offline mon-only)"
        return 0
    fi

    log_error "Docker kuruldu ama calismiyor! docker info hata verdi"
    return 1
}

if ! command -v docker &> /dev/null; then
    install_docker_offline || exit 1
else
    log_success "Docker zaten kurulu ($(docker --version | head -1))"
    systemctl enable docker >/dev/null 2>&1
fi

# Docker Compose plugin kontrol
if ! docker compose version &> /dev/null 2>&1; then
    log_error "docker compose plugin yok!"
    exit 1
fi
log_success "docker compose plugin: $(docker compose version --short 2>&1 | head -1)"

# ==================== AKVORADO DOCKER STACK ====================
log_info "Akvorado Docker imajları mon.nixcon.com.tr'den indiriliyor..."

mkdir -p /opt/akvorado
cd /opt/akvorado

# Docker image tar (mon'da hazır — ~872 MB, tüm Akvorado stack dahil)
if [ ! -f /tmp/akvorado-images.tar ] || [ "$(du -b /tmp/akvorado-images.tar 2>/dev/null | cut -f1)" -lt 800000000 ]; then
    curl -# -o /tmp/akvorado-images.tar "${REPO_URL}/agent/docker-images/akvorado-images.tar" || {
        log_error "Akvorado docker images indirilemedi!"
        exit 1
    }
fi

log_info "Docker imajları yükleniyor..."
docker load -i /tmp/akvorado-images.tar 2>&1 | tail -5

# Akvorado config template (mon'dan)
log_info "Akvorado config template indiriliyor..."
curl -sSL -o /tmp/akvorado-config.tar.gz "${REPO_URL}/agent/akvorado/../akvorado-config.tar.gz" 2>/dev/null || \
curl -sSL "${REPO_URL}/agent/akvorado/" -o /tmp/akvorado-index.html >/dev/null 2>&1

# Config dosyaları için ayrı ayrı çek (nginx autoindex off — indirmek için doğrudan dosya yolları)
mkdir -p /opt/akvorado/config /opt/akvorado/clickhouse
for f in docker-compose.yml versions.yml prometheus.yml; do
    curl -sSL -o "/opt/akvorado/$f" "${REPO_URL}/agent/akvorado/$f" 2>/dev/null || true
done
for f in akvorado.yaml outlet.yaml console.yaml inlet.yaml; do
    curl -sSL -o "/opt/akvorado/config/$f" "${REPO_URL}/agent/akvorado/config/$f" 2>/dev/null || true
done

# GeoIP MMDB (mon /agent/geoip/)
log_info "GeoIP MMDB indiriliyor..."
mkdir -p /var/lib/docker/volumes/akvorado_akvorado-geoip/_data
for f in dbip-asn-lite.mmdb dbip-country-lite.mmdb dbip-city-lite.mmdb; do
    curl -sSL -o "/tmp/$f" "${REPO_URL}/agent/geoip/$f" 2>/dev/null
done
# Akvorado bekliyor: asn.mmdb / country.mmdb / city.mmdb isimlerle
if [ -f /tmp/dbip-asn-lite.mmdb ]; then
    mv /tmp/dbip-asn-lite.mmdb /var/lib/docker/volumes/akvorado_akvorado-geoip/_data/asn.mmdb
    mv /tmp/dbip-country-lite.mmdb /var/lib/docker/volumes/akvorado_akvorado-geoip/_data/country.mmdb
    mv /tmp/dbip-city-lite.mmdb /var/lib/docker/volumes/akvorado_akvorado-geoip/_data/city.mmdb
    log_success "GeoIP MMDB /var/lib/docker/volumes/akvorado_akvorado-geoip/ altına yerleşti"
fi

# Akvorado stack up (restart policy config'te always/unless-stopped olmalı)
log_info "Akvorado stack başlatılıyor..."
cd /opt/akvorado
docker compose up -d 2>&1 | tail -10

sleep 8
UP_COUNT=$(docker ps --filter "name=akvorado" --format "{{.Names}}" | wc -l)
if [ "$UP_COUNT" -ge 6 ]; then
    log_success "Akvorado stack aktif — $UP_COUNT container up"
else
    log_error "Akvorado stack sadece $UP_COUNT container up. Detay: docker compose logs"
fi

# Boot'ta docker daemon otomatik + Akvorado container'lar restart policy sayesinde otomatik
log_info "Akvorado container restart policies:"
docker inspect --format='{{.Name}}: {{.HostConfig.RestartPolicy.Name}}' $(docker ps --filter "name=akvorado" -q) 2>/dev/null | head -10

# ==================== CONFIG DOSYASI ====================
log_info "Config dosyası oluşturuluyor..."

cat > /etc/nixmon-agent/config.yaml << CONFIGEOF
# NixMon Agent Configuration
# Otomatik oluşturuldu: $(date)

server:
  backend_url: "${BACKEND_URL}"
  api_token: "${API_TOKEN}"
  server_id: "${SERVER_ID}"

agent:
  check_interval: 30
  flow_interval: 1
  rule_sync_interval: 30

license:
  check_interval: 300
  offline_timeout: 14400

automation:
  enabled: true
  max_concurrent: 100
  # NOT (v3.0.10+): kural bazlı IP-block kapsamı aktif. Otomasyon kuralları
  # yalnızca panel üzerinden manuel atanan IP blokları üzerinde tetiklenir;
  # kapsam dışı IP'ler için hiçbir BGP aksiyonu alınmaz. Kapsam panel'den
  # düzenlenir: Yönlendirme Otomasyonu > kural düzenle > IP blokları seç.

flow:
  sflow_port: 6343
  netflow_port: 2055
  ipfix_port: 4739
  sampling_rate: 1000
  # Phase 12 (2026-07-05 Berk mimari): Akvorado CH consumer + custom per-IP daily tablo.
  # use_goflow2: false → goflow2 subprocess çalışmaz, agent Akvorado ClickHouse'undan
  # periyodik SELECT ile ipTrafficIn/Out doldurur (edge-first pattern).
  use_goflow2: false
  use_akvorado_consumer: true
  akvorado_ch_url: "clickhouse://default:@127.0.0.1:9000/default?dial_timeout=5s"
  # exporter_ip + local_networks panel'den dinamik sync edilir (getAgentSettings).
  # Startup config için opsiyonel — boş bırakılırsa panel senkronize edilene kadar boş.
  exporter_ip: ""
  local_networks: []
  # Phase 12.C — Trafik geçmişi retention (gün). Kullanıcı panel'den değiştirir.
  # 30/90/180/365/730/1095/1825 seçenekleri. Default 90 (3 ay).
  traffic_history_retention_days: 90

logging:
  file: "/var/log/nixmon-agent/agent.log"
  level: "info"
CONFIGEOF

chmod 600 /etc/nixmon-agent/config.yaml
log_success "Config dosyası oluşturuldu"

# ==================== AGENT BINARY ====================
log_info "Agent binary indiriliyor..."

AGENT_PATH="/usr/bin/nixmon-agent"

if [ -f "$AGENT_PATH" ]; then
    cp "$AGENT_PATH" "${AGENT_PATH}.backup"
fi

curl -sSL -o "$AGENT_PATH" "${REPO_URL}/agent/nixmon-agent" || {
    log_error "Agent binary indirilemedi!"
    exit 1
}

# 2026-08-23 supply-chain butunluk (#12): binary HTTP (${REPO_URL}:8080) uzerinden gelir.
# Beklenen sha256'yi GUVENILIR HTTPS check-update endpointinden al, indirilen binary ile
# karsilastir. HTTP indirmesi MITM ile degistirilse bile checksum tutmaz -> kurulum durur.
API_BASE=$(echo "$REPO_URL" | sed "s|^http://|https://|; s|:8080||")
EXPECT_SHA=$(curl -sSL --max-time 10 "${API_BASE}/api/v1/agent/check-update?version=0.0.0&channel=stable" 2>/dev/null | jq -r ".checksum // empty" 2>/dev/null)
if [ -n "$EXPECT_SHA" ]; then
    GOT_SHA=$(sha256sum "$AGENT_PATH" | awk "{print \$1}")
    if [ "$GOT_SHA" != "$EXPECT_SHA" ]; then
        log_error "Agent binary checksum uyusmuyor (beklenen=$EXPECT_SHA alinan=$GOT_SHA) — indirme bozuk veya mudahale edilmis olabilir. Kurulum durduruldu."
        [ -f "${AGENT_PATH}.backup" ] && mv -f "${AGENT_PATH}.backup" "$AGENT_PATH"
        exit 1
    fi
    log_success "Agent binary checksum dogrulandi (sha256)"
else
    log_warn "Checksum dogrulanamadi (check-update erisilemedi) — kuruluma devam ediliyor."
fi

chmod +x "$AGENT_PATH"
log_success "Agent binary kuruldu"

# ==================== SYSTEMD SERVICE ====================
log_info "Systemd service oluşturuluyor..."

cat > /etc/systemd/system/nixmon-agent.service << 'SERVICEEOF'
[Unit]
Description=NixMon Monitoring Agent
After=network.target network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=/usr/bin/nixmon-agent
Restart=always
RestartSec=5
# 2026-08-22 boot-resilience: agent OOM-killer'dan korunur (kaynak
# baskisinda once Akvorado container'lari feda edilir, kutu ayakta kalir).
OOMScoreAdjust=-900
StandardOutput=append:/var/log/nixmon-agent/agent.log
StandardError=append:/var/log/nixmon-agent/agent.log
LimitNOFILE=65535
LimitNPROC=4096

[Install]
WantedBy=multi-user.target
SERVICEEOF

systemctl daemon-reload
systemctl enable nixmon-agent

# 2026-08-22 BOOT-RESILIENCE FIX (Berk): reboot sonrasi kutunun erisilemez
# kalmasini onler. Kaynak-kisitli collector'da boot'ta Akvorado docker stack
# (ClickHouse/Kafka) RAM'i yiyip OOM-killer SSHD/agent'i olduruyordu -> kutu
# 'acilmiyor'. Fix: SSHD ve agent OOM-korumasi + docker'a baskin OOM skoru.
mkdir -p /etc/systemd/system/ssh.service.d
printf '[Service]\nOOMScoreAdjust=-1000\n' > /etc/systemd/system/ssh.service.d/10-oom.conf
mkdir -p /etc/systemd/system/docker.service.d
printf '[Service]\nOOMScoreAdjust=500\n' > /etc/systemd/system/docker.service.d/10-oom.conf
systemctl daemon-reload
systemctl enable docker >/dev/null 2>&1

log_success "Systemd service oluşturuldu + docker daemon enable"

# ==================== FIREWALL ====================
log_info "Firewall kuralları ekleniyor..."

# Edge-First Phase 02: Panel sunucu IP'si (mon.nixcon.com.tr) — port 9091 sadece bu IP'ye açılır
PANEL_IP="185.150.130.28"
EDGE_API_PORT="9091"

if command -v ufw &> /dev/null; then
    # Flow toplama portları (sFlow/NetFlow/IPFIX) — tüm dünyaya UDP
    ufw allow 6343/udp comment "NixMon sFlow" 2>/dev/null || true
    ufw allow 2055/udp comment "NixMon NetFlow" 2>/dev/null || true
    ufw allow 4739/udp comment "NixMon IPFIX" 2>/dev/null || true
    # Edge-First lokal API — SADECE panel sunucudan
    ufw allow from "$PANEL_IP" to any port "$EDGE_API_PORT" proto tcp comment "NixMon Edge API (panel only)" 2>/dev/null || true
fi

if command -v firewall-cmd &> /dev/null; then
    firewall-cmd --permanent --add-port=6343/udp 2>/dev/null || true
    firewall-cmd --permanent --add-port=2055/udp 2>/dev/null || true
    firewall-cmd --permanent --add-port=4739/udp 2>/dev/null || true
    # firewalld rich rule — panel IP'sinden 9091
    firewall-cmd --permanent --add-rich-rule="rule family='ipv4' source address='$PANEL_IP' port port='$EDGE_API_PORT' protocol='tcp' accept" 2>/dev/null || true
    firewall-cmd --reload 2>/dev/null || true
fi

log_success "Firewall kuralları eklendi (sFlow/NetFlow/IPFIX + Edge API 9091 panel-only)"

# ==================== SERVİSİ BAŞLAT ====================
log_info "Agent başlatılıyor..."

systemctl restart nixmon-agent
sleep 3

if systemctl is-active --quiet nixmon-agent; then
    log_success "Agent başarıyla başlatıldı!"
else
    log_error "Agent başlatılamadı!"
    journalctl -u nixmon-agent -n 20 --no-pager
    exit 1
fi

# ==================== FINAL HEALTH CHECK ====================
log_info "Servis sağlık kontrolü..."

# Docker daemon enable
if systemctl is-enabled docker >/dev/null 2>&1; then
    log_success "Docker daemon boot'ta otomatik başlar"
else
    log_error "Docker daemon boot'ta otomatik başlamıyor! systemctl enable docker"
fi

# Agent enable
if systemctl is-enabled nixmon-agent >/dev/null 2>&1; then
    log_success "nixmon-agent boot'ta otomatik başlar"
else
    log_error "nixmon-agent boot'ta otomatik başlamıyor!"
fi

# Akvorado container'ları restart policy
UNSTOPPED=$(docker ps --filter "name=akvorado" --format "{{.Names}}" | while read c; do
    RP=$(docker inspect --format='{{.HostConfig.RestartPolicy.Name}}' "$c" 2>/dev/null)
    if [ "$RP" = "no" ] || [ -z "$RP" ]; then echo "$c"; fi
done)
if [ -z "$UNSTOPPED" ]; then
    log_success "Akvorado container'ları restart policy tanımlı (crash sonrası otomatik başlar)"
else
    log_error "Bu container'lar restart policy YOK: $UNSTOPPED"
fi

# Custom CH tablo migration (agent boot'ta hazır olur — ilk backfill 30sn sonra)
log_info "Phase 12.C custom CH tablosu bir dakika içinde oluşacak (agent CH-CUSTOM cron)"
log_info "İlk backfill: 30sn boot delay + hourly refresh (30dk)"

echo ""
echo "═══════════════════════════════════════════════════════════════"
log_success "NixMon Agent v3.0.94 kurulumu tamamlandı!"
echo "═══════════════════════════════════════════════════════════════"
echo ""
echo "  ✓ nixmon-agent — /usr/bin/nixmon-agent"
echo "  ✓ Akvorado stack — docker compose /opt/akvorado (6 container)"
echo "  ✓ GeoIP MMDB — asn/country/city"
echo "  ✓ Config — /etc/nixmon-agent/config.yaml (use_akvorado_consumer:true)"
echo "  ✓ Phase 12.C — Trafik geçmişi retention (kullanıcı panelden değiştirir)"
echo ""
echo "  Kontrol:"
echo "    systemctl status nixmon-agent"
echo "    docker ps --filter name=akvorado"
echo "    tail -f /var/log/nixmon-agent/agent.log"
echo ""

# ==================== BACKEND COMPAT SOFT-CHECK ====================
# v3.0.65 cold_watchlist (mig 103) + per-step threshold (mig 104) çağrıları
# yapar. Backend bu migration'ları uygulamadıysa endpointler 404 döner;
# agent graceful handle eder (warning log, crash YOK). Burada sadece
# operatör'e bilgi amaçlı uyarı basıyoruz.
log_info "Backend uyumluluk kontrolü..."
HEALTH_CODE=$(curl -s -o /dev/null -w "%{http_code}" --max-time 5 "${BACKEND_URL}/api/v1/agent/cold-watchlist?server_id=${SERVER_ID}" \
    -H "X-Agent-Token: ${API_TOKEN}" 2>/dev/null || echo "000")

case "$HEALTH_CODE" in
    200|204)
        log_success "Backend v3.0.65 endpoint'lerini destekliyor (cold_watchlist OK)"
        ;;
    401|403)
        log_success "Backend yanıt veriyor (auth gate aktif — normal)"
        ;;
    404)
        log_warn "Backend henüz migration 103/104 uygulamamış. Cold watchlist + per-step threshold ÇALIŞMAYACAK."
        log_warn "Agent crash etmez; mon admin migration uyguladığında otomatik aktive olur."
        ;;
    000|5*)
        log_warn "Backend health check başarısız (HTTP $HEALTH_CODE). Agent çalışıyor ama panel iletişimini doğrulayın."
        ;;
    *)
        log_info "Backend HTTP $HEALTH_CODE — beklenmedik, panel log'a bakın"
        ;;
esac

# ==================== ÖZET ====================
echo ""
echo "╔══════════════════════════════════════════════════════════════╗"
echo "║              KURULUM TAMAMLANDI!                             ║"
echo "╚══════════════════════════════════════════════════════════════╝"
echo ""
echo "  Config          : /etc/nixmon-agent/config.yaml"
echo "  Log Dosyası     : /var/log/nixmon-agent/agent.log"
echo "  Service         : nixmon-agent"
echo ""
echo "  Flow Portları:"
echo "  ─────────────"
echo "  sFlow   : 6343/UDP"
echo "  NetFlow : 2055/UDP"
echo "  IPFIX   : 4739/UDP"
echo ""
echo "  Switch/Router'ınızda flow'u bu sunucuya yönlendirin!"
echo ""

exit 0
