#!/bin/bash
# ─────────────────────────────────────────────────────────────
# TOOL_ID: 9d00 — Security Analyzer (Mistral)
# Node:    Engine (669427) ONLY
# DB Read: Store DB (sec_events, sec_tool_runs) via Origin 172.28.50.1
# DB Write: Engine DB (engine_alerts, engine_suggestions) local
# AI:      Ollama Mistral 7B (localhost:11434 via Podman)
# Audit:   ai-qry, ai-rsp, ai-alr, ai-sug per AUDIT_CODES.md
# ─────────────────────────────────────────────────────────────
set -euo pipefail

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

NODE_ID="${NODE_ID:-669427}"
TOOL_ID="9d00"
RUN_ID="$(date +%s)-$$"

# ── DB Config ──
# Store DB (Origin) — reads sec_events, sec_tool_runs
STORE_HOST="${STORE_DB_HOST:-172.28.50.1}"
STORE_PORT="${STORE_DB_PORT:-3306}"
STORE_DB="${STORE_DB_NAME:-store}"
STORE_USER="${STORE_DB_USER:-agent}"
STORE_PASS="${STORE_DB_PASS:-}"

# Engine DB (local) — writes engine_alerts, engine_suggestions
ENGINE_HOST="${ENGINE_DB_HOST:-127.0.0.1}"
ENGINE_PORT="${ENGINE_DB_PORT:-3306}"
ENGINE_DB="${ENGINE_DB_NAME:-engine_db}"
ENGINE_USER="${ENGINE_DB_USER:-root}"
ENGINE_PASS="${ENGINE_DB_PASS:-}"

# Ollama
OLLAMA_URL="${OLLAMA_URL:-http://localhost:11434}"
OLLAMA_MODEL="${OLLAMA_MODEL:-mistral}"

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

# ── Audit helpers ──
audit_log() {
    local act="$1" res="${2:-1}" extra="${3:-}"
    local ts
    ts=$(date -u "+%Y-%m-%d %H:%M:%S.%3N")
    local phash
    phash=$(echo -n "${act}:${extra}" | sha256sum | cut -d' ' -f1)
    MYSQL_PWD="$ENGINE_PASS" mysql -h"$ENGINE_HOST" -P"$ENGINE_PORT" -u"$ENGINE_USER" -D"$ENGINE_DB" \
        -e "INSERT INTO sec_tool_audit (ts, nid, tid, act, src, res, phash, sid, dur, xc) VALUES ('$ts', '$NODE_ID', '$TOOL_ID', '$act', '$NODE_ID', $res, '$phash', '$RUN_ID', 0, 0)" \
        2>/dev/null || true
}

# ── DB query helpers ──
query_store() {
    MYSQL_PWD="$STORE_PASS" mysql -h"$STORE_HOST" -P"$STORE_PORT" -u"$STORE_USER" -D"$STORE_DB" \
        --batch --skip-column-names -e "$1" 2>/dev/null
}

query_engine() {
    MYSQL_PWD="$ENGINE_PASS" mysql -h"$ENGINE_HOST" -P"$ENGINE_PORT" -u"$ENGINE_USER" -D"$ENGINE_DB" \
        --batch --skip-column-names -e "$1" 2>/dev/null
}

insert_engine() {
    MYSQL_PWD="$ENGINE_PASS" mysql -h"$ENGINE_HOST" -P"$ENGINE_PORT" -u"$ENGINE_USER" -D"$ENGINE_DB" \
        -e "$1" 2>/dev/null
}

# ── Call Ollama Mistral ──
call_mistral() {
    local prompt="$1"
    local escaped
    escaped=$(echo "$prompt" | python3 -c "import sys,json; print(json.dumps(sys.stdin.read()))" 2>/dev/null | sed 's/^"//;s/"$//')
    
    local response
    response=$(curl -sf --max-time 120 -X POST "${OLLAMA_URL}/api/generate" \
        -H "Content-Type: application/json" \
        -d "{\"model\":\"${OLLAMA_MODEL}\",\"prompt\":\"${escaped}\",\"stream\":false,\"options\":{\"temperature\":0.3,\"num_predict\":1024}}" \
        2>/dev/null)
    
    if [ $? -ne 0 ] || [ -z "$response" ]; then
        echo ""
        return 1
    fi
    
    echo "$response" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('response',''))" 2>/dev/null
}

# ── Main analysis ──
main() {
    local start_ts
    start_ts=$(date +%s%3N)
    
    audit_log "ai-qry" 1 "analysis_start"
    
    # 1. Fetch recent security events (last 5 minutes)
    local events
    events=$(query_store "
        SELECT node_id, source_tag, event_type, severity, created_at
        FROM sec_events
        WHERE status_code = 1
          AND created_at >= NOW() - INTERVAL 5 MINUTE
        ORDER BY created_at DESC
        LIMIT 50
    ") || events=""
    
    # 2. Fetch recent tool runs (last 5 minutes)
    local tool_runs
    tool_runs=$(query_store "
        SELECT node_id, tool_name, exit_code, duration_ms, status_code, finished_at
        FROM sec_tool_runs
        WHERE status_code = 1
          AND finished_at >= NOW() - INTERVAL 5 MINUTE
        ORDER BY finished_at DESC
        LIMIT 50
    ") || tool_runs=""
    
    # 3. Fetch heartbeat data
    local heartbeats
    heartbeats=$(query_store "
        SELECT node_id, status, last_heartbeat,
               TIMESTAMPDIFF(SECOND, last_heartbeat, NOW()) AS age_seconds
        FROM nodes
        WHERE status != 'decommissioned'
        ORDER BY node_id
    ") || heartbeats=""
    
    # 4. Fetch recent lockdowns
    local lockdowns
    lockdowns=$(query_store "
        SELECT node_id, level, activated_at, activated_by
        FROM sec_lockdown_state
        WHERE status_code = 1 AND deactivated_at IS NULL
    ") || lockdowns=""
    
    # 5. Count recent canary triggers
    local canary_count
    canary_count=$(query_store "
        SELECT COUNT(*)
        FROM sec_canary_triggers
        WHERE status_code = 1
          AND detected_at >= NOW() - INTERVAL 5 MINUTE
    ") || canary_count="0"
    
    # If no data, log and exit
    local total_events
    total_events=$(echo "$events" | grep -c '.' 2>/dev/null || echo 0)
    local total_runs
    total_runs=$(echo "$tool_runs" | grep -c '.' 2>/dev/null || echo 0)
    
    if [ "$total_events" -eq 0 ] && [ "$total_runs" -eq 0 ] && [ "${canary_count:-0}" -eq 0 ]; then
        audit_log "ai-rsp" 1 "no_data_quiet_period"
        return 0
    fi
    
    # 6. Build Mistral prompt
    local prompt
    prompt="You are a security operations analyst for a distributed mesh network with 8 nodes.
Analyze the following telemetry data from the last 5 minutes and identify:
1. Authentication anomalies (unusual login patterns, failed auth)
2. Infrastructure posture issues (service failures, heartbeat gaps)
3. Network anomalies (direct IP connections, WG handshake failures)
4. Abuse indicators (brute force, scanning, suspicious processes)
5. Operational irregularities (tool failures, unusual execution patterns)

SECURITY EVENTS ($total_events events):
$events

TOOL EXECUTIONS ($total_runs runs):
$tool_runs

HEARTBEAT STATUS:
$heartbeats

ACTIVE LOCKDOWNS:
$lockdowns

CANARY TRIGGERS (5 min): $canary_count

Respond ONLY with a JSON object:
{
  \"status\": \"clean|anomaly|threat|critical\",
  \"findings\": [
    {
      \"type\": \"auth|network|abuse|integrity|posture\",
      \"severity\": \"info|low|medium|high|critical\",
      \"title\": \"short title\",
      \"description\": \"detailed description\",
      \"source_node\": \"node_id or null\",
      \"confidence\": 0.0-1.0,
      \"suggested_action\": \"description of recommended action\"
    }
  ]
}
If everything looks normal, return {\"status\":\"clean\",\"findings\":[]}"

    # 7. Call Mistral
    local ai_response
    ai_response=$(call_mistral "$prompt")
    
    if [ -z "$ai_response" ]; then
        audit_log "ai-rsp" 0 "ollama_unreachable"
        return 1
    fi
    
    audit_log "ai-rsp" 1 "response_received"
    
    # 8. Parse response and insert alerts via Python
    export AI_RESPONSE="$ai_response"
    python3 << 'PYEOF'
import json, sys, uuid, subprocess, os, re

response_text = os.environ.get('AI_RESPONSE', '')
engine_host = os.environ.get('ENGINE_HOST', '127.0.0.1')
engine_port = os.environ.get('ENGINE_PORT', '3306')
engine_user = os.environ.get('ENGINE_USER', 'root')
engine_pass = os.environ.get('ENGINE_PASS', '')
engine_db = os.environ.get('ENGINE_DB', 'engine_db')

# Parse JSON from response
try:
    data = json.loads(response_text)
except:
    match = re.search(r'\{.*\}', response_text, re.DOTALL)
    if match:
        try:
            data = json.loads(match.group())
        except:
            sys.exit(0)
    else:
        sys.exit(0)

status = data.get('status', 'clean')
findings = data.get('findings', [])

if status == 'clean' or not findings:
    print('CLEAN')
    sys.exit(0)

env = os.environ.copy()
env['MYSQL_PWD'] = engine_pass

def mysql_exec(sql):
    subprocess.run(
        ['mysql', f'-h{engine_host}', f'-P{engine_port}', f'-u{engine_user}', f'-D{engine_db}', '-e', sql],
        env=env, capture_output=True, timeout=10
    )

for f in findings:
    alert_uuid = str(uuid.uuid4())
    alert_type = f.get('type', 'unknown')[:50]
    severity = f.get('severity', 'info')
    if severity not in ('info','low','medium','high','critical'):
        severity = 'info'
    source_nid = str(f.get('source_node', '') or '')[:10]
    title = f.get('title', 'Untitled')[:255].replace("'", "''")
    description = f.get('description', '').replace("'", "''")
    confidence = min(max(float(f.get('confidence', 0.5)), 0.0), 1.0)
    evidence = json.dumps(f, default=str).replace("'", "''")
    suggested = f.get('suggested_action', '').replace("'", "''")

    alert_sql = (
        f"INSERT INTO engine_alerts "
        f"(alert_uuid, alert_type, severity, source_nid, source_tid, title, description, "
        f"evidence_json, model_version, confidence, acknowledged, resolved, created_at) "
        f"VALUES ('{alert_uuid}', '{alert_type}', '{severity}', '{source_nid}', '9d00', "
        f"'{title}', '{description}', '{evidence}', 'mistral-7b', {confidence}, 0, 0, NOW())"
    )
    mysql_exec(alert_sql)
    print(f'ALERT:{severity}:{alert_uuid}:{f.get("title","")}')

    # High-confidence high-severity -> create suggestion
    if confidence >= 0.8 and severity in ('high', 'critical') and suggested:
        sug_uuid = str(uuid.uuid4())
        sug_sql = (
            f"INSERT INTO engine_suggestions "
            f"(suggestion_uuid, alert_id, category, action_type, target_nid, description, "
            f"risk_level, model_version, confidence, status, created_at, expires_at) "
            f"VALUES ('{sug_uuid}', "
            f"(SELECT id FROM engine_alerts WHERE alert_uuid = '{alert_uuid}' LIMIT 1), "
            f"'{alert_type}', 'remediation', '{source_nid}', '{suggested}', "
            f"'{severity}', 'mistral-7b', {confidence}, 'pending', NOW(), "
            f"DATE_ADD(NOW(), INTERVAL 1 HOUR))"
        )
        mysql_exec(sug_sql)
        print(f'SUGGESTION:{sug_uuid}:{f.get("suggested_action","")}')

print(f'ANALYSIS_COMPLETE:status={status}:findings={len(findings)}')
PYEOF
    
    local end_ts
    end_ts=$(date +%s%3N)
    local dur=$(( end_ts - start_ts ))
    
    audit_log "ai-alr" 1 "analysis_complete:dur=${dur}ms"
}

# Export DB vars for Python heredoc
export ENGINE_HOST ENGINE_PORT ENGINE_USER ENGINE_PASS ENGINE_DB

# ── Entrypoint ──
case "${1:-run}" in
    run|analyze)
        # Set AI_RESPONSE before calling python
        main
        ;;
    status)
        if curl -sf --max-time 5 "${OLLAMA_URL}/api/tags" >/dev/null 2>&1; then
            echo '{"tool":"security-analyzer","node":"'"$NODE_ID"'","status":"healthy","ollama":"active","model":"'"$OLLAMA_MODEL"'","checked_at":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'"}'
        else
            echo '{"tool":"security-analyzer","node":"'"$NODE_ID"'","status":"degraded","ollama":"unreachable","checked_at":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'"}'
        fi
        ;;
    *)
        echo "Usage: $0 {run|analyze|status}"
        exit 1
        ;;
esac
