#!/bin/bash
# =============================================================================
# telemetry-cron.sh — 5-Minute Telemetry Collection
# =============================================================================
# Runs every 5 minutes on ALL 8 nodes via cron. Collects:
#   1. Runtime health (CPU, mem, disk, load)
#   2. Security detect (anomaly scan)
#   3. Heartbeat (nodes.last_heartbeat update)
#   4. Network status (WireGuard tunnel health)
#   5. Service status (systemd units)
#
# All data written to Store DB (558201 172.28.50.1) non-blocking.
# =============================================================================
set -euo pipefail

TOOL_DIR="$(dirname "$(readlink -f "$0")")"
[ -f "$TOOL_DIR/common.sh" ] && source "$TOOL_DIR/common.sh" 2>/dev/null || true
[ -f "$TOOL_DIR/log_to_db.sh" ] && source "$TOOL_DIR/log_to_db.sh" 2>/dev/null || true

NODE_ID="${NODE_ID:-$(cat /etc/binary/node_id 2>/dev/null || hostname -s)}"

# ── DB Config ──
DB_HOST="${STORE_DB_HOST:-172.28.50.1}"
DB_PORT="${STORE_DB_PORT:-3306}"
DB_NAME="${STORE_DB_NAME:-shared_engine}"
DB_USER="${STORE_DB_USER:-tooling}"
DB_PASS="${STORE_DB_PASS:-}"

CRED_FILE="/etc/binary/tooling.env"
[ -f "$CRED_FILE" ] && source "$CRED_FILE" 2>/dev/null || true

RUN_TS=$(date -u +%Y-%m-%dT%H:%M:%SZ)
ERRORS=0

sql_escape() { echo "$1" | sed "s/'/''/g"; }

db_write() {
    [[ -z "${DB_PASS:-}" ]] && return 0
    { MYSQL_PWD="$DB_PASS" mysql -h"$DB_HOST" -P"$DB_PORT" -u"$DB_USER" -D"$DB_NAME" \
        --connect-timeout=3 -e "$1" 2>/dev/null; } &
    disown 2>/dev/null
}

# ─────────────────────────────────────────────────────────────
# 1. Runtime Health (TOOL_ID d4c2)
# ─────────────────────────────────────────────────────────────
collect_health() {
    local start_ms=$(($(date +%s%N)/1000000))
    
    local cpu_pct mem_pct disk_pct load_1 load_5 uptime_hrs
    
    # CPU usage (1-second sample)
    cpu_pct=$(top -bn1 2>/dev/null | grep "Cpu(s)" | awk '{print $2}' | cut -d'%' -f1 || echo "0")
    
    # Memory usage
    mem_pct=$(free 2>/dev/null | awk '/^Mem:/{printf "%.1f", $3/$2*100}' || echo "0")
    
    # Disk usage (root partition)
    disk_pct=$(df / 2>/dev/null | awk 'NR==2{gsub(/%/,""); print $5}' || echo "0")
    
    # Load average
    load_1=$(cat /proc/loadavg 2>/dev/null | awk '{print $1}' || echo "0")
    load_5=$(cat /proc/loadavg 2>/dev/null | awk '{print $2}' || echo "0")
    
    # Uptime hours
    uptime_hrs=$(awk '{printf "%.1f", $1/3600}' /proc/uptime 2>/dev/null || echo "0")
    
    local dur_ms=$(( $(date +%s%N)/1000000 - start_ms ))
    
    # Build metrics JSON
    local metrics
    metrics=$(printf '{"cpu":%.1f,"mem":%.1f,"disk":%.1f,"load_1":%s,"load_5":%s,"uptime_hours":%s}' \
        "$cpu_pct" "$mem_pct" "$disk_pct" "$load_1" "$load_5" "$uptime_hrs")
    
    # Update nodes table with fresh metrics
    db_write "UPDATE nodes SET 
        cpu_usage_pct = $cpu_pct, 
        mem_usage_pct = $mem_pct, 
        disk_usage_pct = $disk_pct,
        metrics = '$(sql_escape "$metrics")',
        last_heartbeat = NOW(),
        status = 'online',
        consecutive_misses = 0
        WHERE node_id = '$(sql_escape "$NODE_ID")'"
    
    # Log tool run
    db_write "INSERT INTO sec_tool_runs 
        (node_id, tool_name, tool_function, status, exit_code, duration_ms, 
         stdout_summary, status_code, started_at, finished_at)
        VALUES ('$NODE_ID', 'runtime-health', 'collect_health', 'ok', 0, $dur_ms,
                '$(sql_escape "$metrics")', 1,
                DATE_SUB(NOW(), INTERVAL $dur_ms/1000 SECOND), NOW())"
    
    echo "  health: cpu=${cpu_pct}% mem=${mem_pct}% disk=${disk_pct}% load=${load_1}"
}

# ─────────────────────────────────────────────────────────────
# 2. Security Detect (TOOL_ID f6a4)
# ─────────────────────────────────────────────────────────────
collect_security() {
    local start_ms=$(($(date +%s%N)/1000000))
    local findings=0
    
    # Check for failed SSH logins (last 5 min)
    local ssh_fails
    ssh_fails=$(journalctl -u sshd --since "5 minutes ago" --no-pager 2>/dev/null | \
        grep -ci "failed\|invalid\|refused" || echo "0")
    
    if [[ "$ssh_fails" -gt 5 ]]; then
        findings=$((findings + 1))
        db_write "INSERT INTO sec_events 
            (node_id, source_tag, event_type, severity, details_enc, status_code, created_at)
            VALUES ('$NODE_ID', 'security-detect', 'ssh_brute_force', 'high',
                    '$ssh_fails failed SSH attempts in 5 minutes', 1, NOW())"
    fi
    
    # Check for unauthorized file changes on critical files
    local shadow_changed=false
    if [[ -f /etc/shadow ]]; then
        local shadow_hash
        shadow_hash=$(sha256sum /etc/shadow 2>/dev/null | awk '{print $1}')
        local stored_hash
        stored_hash=$(cat /var/run/se/shadow.hash 2>/dev/null || echo "")
        
        if [[ -n "$stored_hash" && "$shadow_hash" != "$stored_hash" ]]; then
            shadow_changed=true
            findings=$((findings + 1))
            db_write "INSERT INTO sec_events 
                (node_id, source_tag, event_type, severity, details_enc, status_code, created_at)
                VALUES ('$NODE_ID', 'security-detect', 'shadow_tamper', 'critical',
                        '/etc/shadow hash mismatch', 1, NOW())"
        fi
        # Update stored hash
        echo "$shadow_hash" > /var/run/se/shadow.hash 2>/dev/null || true
    fi
    
    # Check for suspicious processes
    local suspicious
    suspicious=$(ps aux 2>/dev/null | grep -Ei "nc -l|ncat|socat|miner|xmrig|cryptonight" | grep -v grep | wc -l || echo 0)
    
    if [[ "$suspicious" -gt 0 ]]; then
        findings=$((findings + 1))
        local procs
        procs=$(ps aux 2>/dev/null | grep -Ei "nc -l|ncat|socat|miner|xmrig|cryptonight" | grep -v grep | head -3)
        db_write "INSERT INTO sec_events 
            (node_id, source_tag, event_type, severity, details_enc, status_code, created_at)
            VALUES ('$NODE_ID', 'security-detect', 'suspicious_process', 'high',
                    '$(sql_escape "$procs")', 1, NOW())"
    fi
    
    local dur_ms=$(( $(date +%s%N)/1000000 - start_ms ))
    
    db_write "INSERT INTO sec_tool_runs 
        (node_id, tool_name, tool_function, status, exit_code, duration_ms,
         stdout_summary, status_code, started_at, finished_at)
        VALUES ('$NODE_ID', 'security-detect', 'collect_security', 'ok', 0, $dur_ms,
                'findings=$findings ssh_fails=$ssh_fails', 1,
                DATE_SUB(NOW(), INTERVAL $dur_ms/1000 SECOND), NOW())"
    
    echo "  security: findings=$findings ssh_fails=$ssh_fails"
}

# ─────────────────────────────────────────────────────────────
# 3. Heartbeat (TOOL_ID d4c0)
# ─────────────────────────────────────────────────────────────
send_heartbeat() {
    # Already updated in collect_health, but log the tool run
    db_write "INSERT INTO sec_tool_runs 
        (node_id, tool_name, tool_function, status, exit_code, duration_ms,
         stdout_summary, status_code, started_at, finished_at)
        VALUES ('$NODE_ID', 'svcbeat', 'heartbeat', 'ok', 0, 0,
                'heartbeat sent', 1, NOW(), NOW())"
    
    echo "  heartbeat: sent"
}

# ─────────────────────────────────────────────────────────────
# 4. Network Status (TOOL_ID 1a01)
# ─────────────────────────────────────────────────────────────
collect_network() {
    local start_ms=$(($(date +%s%N)/1000000))
    
    # Check WireGuard interfaces
    local wg_interfaces
    wg_interfaces=$(wg show 2>/dev/null | grep "^interface:" | awk '{print $2}' | tr '\n' ',' | sed 's/,$//')
    [[ -z "$wg_interfaces" ]] && wg_interfaces="none"
    
    # Count WG peers
    local wg_peers
    wg_peers=$(wg show all peers 2>/dev/null | wc -l || echo 0)
    
    # Check latest handshakes (stale = > 5 min)
    local stale_peers=0
    while IFS= read -r line; do
        [[ -z "$line" ]] && continue
        local hs_time
        hs_time=$(echo "$line" | awk '{print $NF}')
        if [[ "$hs_time" == "0" || -z "$hs_time" ]]; then
            stale_peers=$((stale_peers + 1))
        fi
    done < <(wg show all latest-handshakes 2>/dev/null || true)
    
    # Update nodes.wg_peers
    db_write "UPDATE nodes SET wg_peers = $wg_peers WHERE node_id = '$(sql_escape "$NODE_ID")'"
    
    local dur_ms=$(( $(date +%s%N)/1000000 - start_ms ))
    
    db_write "INSERT INTO sec_tool_runs 
        (node_id, tool_name, tool_function, status, exit_code, duration_ms,
         stdout_summary, status_code, started_at, finished_at)
        VALUES ('$NODE_ID', 'network-status', 'collect_network', 'ok', 0, $dur_ms,
                'ifaces=$wg_interfaces peers=$wg_peers stale=$stale_peers', 1,
                DATE_SUB(NOW(), INTERVAL $dur_ms/1000 SECOND), NOW())"
    
    if [[ "$stale_peers" -gt 0 ]]; then
        db_write "INSERT INTO sec_events 
            (node_id, source_tag, event_type, severity, details_enc, status_code, created_at)
            VALUES ('$NODE_ID', 'network-status', 'stale_wg_peers', 'medium',
                    '$stale_peers stale WG peers on $wg_interfaces', 1, NOW())"
    fi
    
    echo "  network: ifaces=$wg_interfaces peers=$wg_peers stale=$stale_peers"
}

# ─────────────────────────────────────────────────────────────
# 5. Service Status
# ─────────────────────────────────────────────────────────────
collect_services() {
    local start_ms=$(($(date +%s%N)/1000000))
    local failed_units
    
    failed_units=$(systemctl --failed --no-pager --no-legend 2>/dev/null | wc -l || echo 0)
    
    if [[ "$failed_units" -gt 0 ]]; then
        local failed_list
        failed_list=$(systemctl --failed --no-pager --no-legend 2>/dev/null | awk '{print $2}' | head -5 | tr '\n' ',')
        db_write "INSERT INTO sec_events 
            (node_id, source_tag, event_type, severity, details_enc, status_code, created_at)
            VALUES ('$NODE_ID', 'service-check', 'failed_units', 'medium',
                    '$failed_units failed: $(sql_escape "$failed_list")', 1, NOW())"
    fi
    
    local dur_ms=$(( $(date +%s%N)/1000000 - start_ms ))
    
    db_write "INSERT INTO sec_tool_runs 
        (node_id, tool_name, tool_function, status, exit_code, duration_ms,
         stdout_summary, status_code, started_at, finished_at)
        VALUES ('$NODE_ID', 'service-check', 'collect_services', 'ok', 0, $dur_ms,
                'failed_units=$failed_units', 1,
                DATE_SUB(NOW(), INTERVAL $dur_ms/1000 SECOND), NOW())"
    
    echo "  services: failed_units=$failed_units"
}

# ── Main ──
main() {
    echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] telemetry-cron start node=$NODE_ID"
    
    # Create state dir if needed
    mkdir -p /var/run/se 2>/dev/null || true
    
    collect_health   || ERRORS=$((ERRORS + 1))
    collect_security || ERRORS=$((ERRORS + 1))
    send_heartbeat   || ERRORS=$((ERRORS + 1))
    collect_network  || ERRORS=$((ERRORS + 1))
    collect_services || ERRORS=$((ERRORS + 1))
    
    echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] telemetry-cron done errors=$ERRORS"
}

main
