#!/bin/sh
# SPDX-License-Identifier: Apache-2.0
# SecuBox Security Intelligence - RPCD backend
# Copyright (C) 2026 CyberMind.fr - Gandalf
#
# Combines mitmproxy threat detection, CrowdSec blocking,
# and P2P mesh threat intelligence sharing

. /lib/functions.sh
. /usr/share/libubox/jshn.sh

CSCLI="/usr/bin/cscli"
THREAT_INTEL="/usr/lib/secubox/threat-intel.sh"

# ==============================================================================
# HELPERS
# ==============================================================================

execute_block() {
	local ip="$1" duration="$2" reason="$3"
	[ -x "$CSCLI" ] || return 1
	$CSCLI decisions add --ip "$ip" --duration "$duration" --reason "$reason" >/dev/null 2>&1
}

# ==============================================================================
# SECURITY STATS (firewall counters)
# ==============================================================================

get_security_stats() {
	local wan_drops=0 fw_rejects=0 cs_bans=0 cs_alerts_24h=0 haproxy_conns=0 invalid_conns=0

	# WAN dropped packets from nftables
	if command -v nft >/dev/null 2>&1; then
		wan_drops=$(nft list chain inet fw4 input_wan 2>/dev/null | \
			grep -oE 'packets [0-9]+' | awk '{sum+=$2} END {print sum+0}')
		local reject_drops=$(nft list chain inet fw4 reject_from_wan 2>/dev/null | \
			grep -oE 'packets [0-9]+' | awk '{sum+=$2} END {print sum+0}')
		[ -n "$reject_drops" ] && wan_drops=$((${wan_drops:-0} + reject_drops))
	fi

	# Firewall rejects
	if command -v nft >/dev/null 2>&1; then
		fw_rejects=$(nft list chain inet fw4 handle_reject 2>/dev/null | \
			grep -oE 'packets [0-9]+' | awk '{sum+=$2} END {print sum+0}')
		if [ "${fw_rejects:-0}" = "0" ]; then
			fw_rejects=$(nft -a list ruleset 2>/dev/null | \
				grep -E "reject|drop" | grep "counter" | \
				grep -oE 'packets [0-9]+' | awk '{sum+=$2} END {print sum+0}')
		fi
	fi

	# CrowdSec bans and alerts
	if [ -x "$CSCLI" ]; then
		if command -v jq >/dev/null 2>&1; then
			cs_bans=$($CSCLI decisions list -o json 2>/dev/null | jq 'length' 2>/dev/null)
			cs_alerts_24h=$($CSCLI alerts list -o json --since 24h 2>/dev/null | jq 'length' 2>/dev/null)
		else
			local cs_json=$($CSCLI decisions list -o json 2>/dev/null)
			[ -n "$cs_json" ] && [ "$cs_json" != "null" ] && [ "$cs_json" != "[]" ] && \
				cs_bans=$(echo "$cs_json" | jsonfilter -e '@[*]' 2>/dev/null | wc -l)
			local alerts_json=$($CSCLI alerts list -o json --since 24h 2>/dev/null)
			[ -n "$alerts_json" ] && [ "$alerts_json" != "null" ] && [ "$alerts_json" != "[]" ] && \
				cs_alerts_24h=$(echo "$alerts_json" | jsonfilter -e '@[*]' 2>/dev/null | wc -l)
		fi
	fi

	# Invalid connections
	if [ -f /proc/net/nf_conntrack ]; then
		invalid_conns=$(grep -c "\[INVALID\]" /proc/net/nf_conntrack 2>/dev/null)
	fi

	# HAProxy connections
	if [ -S /var/run/haproxy/admin.sock ]; then
		haproxy_conns=$(echo "show stat" | socat stdio /var/run/haproxy/admin.sock 2>/dev/null | \
			tail -n+2 | awk -F, '{sum+=$5} END {print sum+0}')
	elif [ -S /var/lib/haproxy/stats ]; then
		haproxy_conns=$(echo "show stat" | socat stdio /var/lib/haproxy/stats 2>/dev/null | \
			tail -n+2 | awk -F, '{sum+=$5} END {print sum+0}')
	elif command -v lxc-info >/dev/null 2>&1 && lxc-info -n haproxy -s 2>/dev/null | grep -q "RUNNING"; then
		haproxy_conns=$(lxc-attach -n haproxy -- sh -c '
			for sock in /stats /run/haproxy.sock /var/lib/haproxy/stats /var/run/haproxy/admin.sock; do
				[ -S "$sock" ] && { echo "show stat" | socat stdio "$sock" 2>/dev/null; break; }
			done' 2>/dev/null | tail -n+2 | awk -F, '{sum+=$5} END {print sum+0}')
	fi

	# Sanitize to numeric
	wan_drops=$(printf '%s' "${wan_drops:-0}" | tr -cd '0-9'); : ${wan_drops:=0}
	fw_rejects=$(printf '%s' "${fw_rejects:-0}" | tr -cd '0-9'); : ${fw_rejects:=0}
	cs_bans=$(printf '%s' "${cs_bans:-0}" | tr -cd '0-9'); : ${cs_bans:=0}
	cs_alerts_24h=$(printf '%s' "${cs_alerts_24h:-0}" | tr -cd '0-9'); : ${cs_alerts_24h:=0}
	invalid_conns=$(printf '%s' "${invalid_conns:-0}" | tr -cd '0-9'); : ${invalid_conns:=0}
	haproxy_conns=$(printf '%s' "${haproxy_conns:-0}" | tr -cd '0-9'); : ${haproxy_conns:=0}

	cat << EOF
{
	"wan_dropped": $wan_drops,
	"firewall_rejects": $fw_rejects,
	"crowdsec_bans": $cs_bans,
	"crowdsec_alerts_24h": $cs_alerts_24h,
	"invalid_connections": $invalid_conns,
	"haproxy_connections": $haproxy_conns
}
EOF
}

# ==============================================================================
# UBUS INTERFACE
# ==============================================================================

case "$1" in
	list)
		json_init
		json_add_object "status"; json_close_object
		json_add_object "get_security_stats"; json_close_object
		json_add_object "get_active_threats"; json_close_object
		json_add_object "get_blocked_ips"; json_close_object
		json_add_object "block_threat"
			json_add_string "ip" "string"
			json_add_string "duration" "string"
			json_add_string "reason" "string"
		json_close_object
		json_add_object "whitelist_host"
			json_add_string "ip" "string"
			json_add_string "reason" "string"
		json_close_object
		json_add_object "remove_whitelist"
			json_add_string "ip" "string"
		json_close_object
		json_add_object "get_threat_intel"; json_close_object
		json_add_object "get_mesh_iocs"; json_close_object
		json_add_object "get_mesh_peers"; json_close_object
		json_add_object "publish_intel"; json_close_object
		json_add_object "apply_intel"; json_close_object
		json_dump
		;;

	call)
		case "$2" in
			status)
				json_init
				json_add_boolean "netifyd_running" $(pgrep netifyd >/dev/null 2>&1 && echo 1 || echo 0)
				json_add_boolean "crowdsec_running" $(pgrep crowdsec >/dev/null 2>&1 && echo 1 || echo 0)
				json_add_boolean "mitmproxy_running" $(pgrep -f mitmweb >/dev/null 2>&1 && echo 1 || echo 0)
				json_add_boolean "cscli_available" $([ -x "$CSCLI" ] && echo 1 || echo 0)
				json_add_boolean "threat_intel_available" $([ -x "$THREAT_INTEL" ] && echo 1 || echo 0)
				json_dump
				;;

			get_security_stats)
				get_security_stats
				;;

			get_active_threats)
				_log_file="/srv/mitmproxy/threats.log"
				if [ -f "$_log_file" ]; then
					_threats=$(tail -50 "$_log_file" 2>/dev/null | jq -sc '
						map({
							ip: .source_ip,
							severity: .severity,
							score: (if .severity == "critical" then 90
								elif .severity == "high" then 70
								elif .severity == "medium" then 50
								else 30 end),
							type: .type,
							pattern: .pattern,
							host: .host,
							country: (.country // "??"),
							timestamp: .timestamp,
							request: (.request // "-"),
							is_bot: (.is_bot // false),
							bot_type: (.bot_type // null),
							cve: (.cve // null)
						}) | unique_by(.ip) | sort_by(.score) | reverse
					' 2>/dev/null || echo "[]")
					printf '{"threats":%s}\n' "$_threats"
				else
					echo '{"threats":[]}'
				fi
				;;

			get_blocked_ips)
				if [ -x "$CSCLI" ]; then
					_decisions=$($CSCLI decisions list -o json 2>/dev/null || echo "[]")
					printf '{"blocked":%s}\n' "$_decisions"
				else
					echo '{"blocked":[]}'
				fi
				;;

			block_threat)
				read -r input
				json_load "$input"
				json_get_var ip ip
				json_get_var duration duration
				json_get_var reason reason
				duration=${duration:-4h}
				reason=${reason:-"Manual block from Security Dashboard"}

				if [ -z "$ip" ]; then
					json_init
					json_add_boolean "success" 0
					json_add_string "error" "IP address required"
					json_dump
				elif execute_block "$ip" "$duration" "$reason"; then
					json_init
					json_add_boolean "success" 1
					json_add_string "message" "IP $ip blocked for $duration"
					json_dump
				else
					json_init
					json_add_boolean "success" 0
					json_add_string "error" "Failed to block IP"
					json_dump
				fi
				;;

			whitelist_host)
				read -r input
				json_load "$input"
				json_get_var ip ip
				json_get_var reason reason
				reason=${reason:-"Whitelisted from Security Dashboard"}

				if [ -z "$ip" ]; then
					json_init
					json_add_boolean "success" 0
					json_add_string "error" "IP address required"
					json_dump
				else
					local section="whitelist_${ip//./_}"
					uci set "secubox_security_threats.${section}=whitelist"
					uci set "secubox_security_threats.${section}.ip=$ip"
					uci set "secubox_security_threats.${section}.reason=$reason"
					uci set "secubox_security_threats.${section}.added_at=$(date -Iseconds)"
					uci commit secubox_security_threats
					json_init
					json_add_boolean "success" 1
					json_add_string "message" "IP $ip whitelisted"
					json_dump
				fi
				;;

			remove_whitelist)
				read -r input
				json_load "$input"
				json_get_var ip ip

				if [ -z "$ip" ]; then
					json_init
					json_add_boolean "success" 0
					json_add_string "error" "IP address required"
					json_dump
				else
					local section="whitelist_${ip//./_}"
					uci delete "secubox_security_threats.${section}" 2>/dev/null
					uci commit secubox_security_threats
					json_init
					json_add_boolean "success" 1
					json_add_string "message" "IP $ip removed from whitelist"
					json_dump
				fi
				;;

			get_threat_intel)
				if [ -x "$THREAT_INTEL" ]; then
					"$THREAT_INTEL" status 2>/dev/null
				else
					echo '{"enabled":false,"error":"threat-intel not available"}'
				fi
				;;

			get_mesh_iocs)
				if [ -x "$THREAT_INTEL" ]; then
					_iocs=$("$THREAT_INTEL" list received 2>/dev/null)
					printf '{"iocs":%s}\n' "${_iocs:-[]}"
				else
					echo '{"iocs":[]}'
				fi
				;;

			get_mesh_peers)
				if [ -x "$THREAT_INTEL" ]; then
					_peers=$("$THREAT_INTEL" peers 2>/dev/null)
					printf '{"peers":%s}\n' "${_peers:-[]}"
				else
					echo '{"peers":[]}'
				fi
				;;

			publish_intel)
				if [ -x "$THREAT_INTEL" ]; then
					"$THREAT_INTEL" collect-and-publish >/dev/null 2>&1 &
					json_init
					json_add_boolean "started" 1
					json_add_string "message" "Publish started in background"
					json_dump
				else
					echo '{"error":"threat-intel not available"}'
				fi
				;;

			apply_intel)
				if [ -x "$THREAT_INTEL" ]; then
					"$THREAT_INTEL" apply-pending 2>/dev/null
					"$THREAT_INTEL" status 2>/dev/null
				else
					echo '{"error":"threat-intel not available"}'
				fi
				;;

			*)
				json_init
				json_add_string "error" "Unknown method: $2"
				json_dump
				;;
		esac
		;;
esac
