#!/bin/sh

#
# SecuBox Core Daemon
# Main orchestration service for SecuBox framework
#

# Note: Not using set -e as many UCI/service checks legitimately return non-zero

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

SECUBOX_VERSION="0.8.2"
LOG_FILE="/var/log/secubox/core.log"
PID_FILE="/var/run/secubox/core.pid"
STATE_DIR="/var/run/secubox"
WATCHDOG_STATE="/var/run/secubox/watchdog.json"

# LED paths for MochaBin (RGB LEDs: led1, led2, led3 + mmc0)
# led1: Global health status (green=ok, yellow=degraded, red=critical)
# led2: Security threat level (green=safe, red=attack)
# led3: Global capacity meter (CPU + Network)
# mmc0: Classic heartbeat when states are stable
LED_GREEN1="/sys/class/leds/green:led1"
LED_GREEN2="/sys/class/leds/green:led2"
LED_GREEN3="/sys/class/leds/green:led3"
LED_RED1="/sys/class/leds/red:led1"
LED_RED2="/sys/class/leds/red:led2"
LED_RED3="/sys/class/leds/red:led3"
LED_BLUE1="/sys/class/leds/blue:led1"
LED_BLUE2="/sys/class/leds/blue:led2"
LED_BLUE3="/sys/class/leds/blue:led3"
LED_MMC0="/sys/class/leds/mmc0::"
# Legacy aliases for compatibility
LED_GREEN="$LED_GREEN1"
LED_RED="$LED_RED1"
LED_BLUE="$LED_BLUE1"
LED_ENABLED=0
LED_COLOR_CYCLE=0  # Cycles 0-5 for rainbow colors
LED_PREV_STATE=""  # Track previous state for change detection

# Services to monitor (init.d name:check_method:restart_delay)
# check_method: pid, docker, lxc, port:PORT
MONITORED_SERVICES=""

# LED helper functions
led_init() {
    # Check if LEDs are available (MochaBin or compatible)
    if [ -d "$LED_GREEN1" ] && [ -d "$LED_RED1" ]; then
        LED_ENABLED=1
        # Set trigger to none for manual control on RGB LEDs
        for led in "$LED_RED1" "$LED_GREEN1" "$LED_BLUE1" \
                   "$LED_RED2" "$LED_GREEN2" "$LED_BLUE2" \
                   "$LED_RED3" "$LED_GREEN3" "$LED_BLUE3"; do
            echo none > "$led/trigger" 2>/dev/null
            echo 0 > "$led/brightness" 2>/dev/null
        done
        # mmc0 LED: start with heartbeat trigger (stable state)
        if [ -d "$LED_MMC0" ]; then
            echo heartbeat > "$LED_MMC0/trigger" 2>/dev/null
        fi
        log debug "LED enabled: led1=health, led2=security, led3=capacity, mmc0=heartbeat"
    else
        log debug "LED heartbeat disabled (no compatible LEDs)"
    fi
}

# mmc0 classic heartbeat control
# When states are stable: heartbeat trigger (classic pulse)
# When states change: rapid blink to indicate activity
led_mmc0_heartbeat() {
    [ -d "$LED_MMC0" ] || return 0
    local stable="$1"  # 1=stable, 0=changing

    if [ "$stable" = "1" ]; then
        # Classic heartbeat - stable state
        local current=$(cat "$LED_MMC0/trigger" 2>/dev/null)
        [ "$current" != "heartbeat" ] && echo heartbeat > "$LED_MMC0/trigger" 2>/dev/null
    else
        # Rapid blink - state change detected
        echo timer > "$LED_MMC0/trigger" 2>/dev/null
        echo 50 > "$LED_MMC0/delay_on" 2>/dev/null
        echo 50 > "$LED_MMC0/delay_off" 2>/dev/null
    fi
}

# Set all colors for a specific LED (1, 2, or 3)
led_set_rgb() {
    local led_num="$1"  # 1, 2, or 3
    local r="$2" g="$3" b="$4"
    case "$led_num" in
        1)
            echo "$r" > "$LED_RED1/brightness" 2>/dev/null
            echo "$g" > "$LED_GREEN1/brightness" 2>/dev/null
            echo "$b" > "$LED_BLUE1/brightness" 2>/dev/null
            ;;
        2)
            echo "$r" > "$LED_RED2/brightness" 2>/dev/null
            echo "$g" > "$LED_GREEN2/brightness" 2>/dev/null
            echo "$b" > "$LED_BLUE2/brightness" 2>/dev/null
            ;;
        3)
            echo "$r" > "$LED_RED3/brightness" 2>/dev/null
            echo "$g" > "$LED_GREEN3/brightness" 2>/dev/null
            echo "$b" > "$LED_BLUE3/brightness" 2>/dev/null
            ;;
    esac
}

# Turn off all LEDs on one LED unit
led_off() {
    local led_num="$1"
    led_set_rgb "$led_num" 0 0 0
}

# Global capacity LED on led3 - combines CPU + Network activity
# Color gradient: Green (idle) -> Cyan (light) -> Yellow (moderate) -> Orange (busy) -> Red (max)
# Network state file for delta calculation
NET_PREV_FILE="/tmp/secubox/net_prev"

led_global_capacity() {
    [ "$LED_ENABLED" = "1" ] || return 0

    # === CPU Load (0-100%) ===
    local load=$(cat /proc/loadavg 2>/dev/null | cut -d' ' -f1)
    local ncpu=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || echo 4)
    local load_int=$(echo "$load" | cut -d. -f1)
    local load_dec=$(echo "$load" | cut -d. -f2 | cut -c1-2)
    [ -z "$load_dec" ] && load_dec=0
    local cpu_pct=$(( (load_int * 100 + load_dec) * 100 / ncpu / 100 ))
    [ "$cpu_pct" -gt 100 ] && cpu_pct=100

    # === Network Activity (0-100%) ===
    # Sum all interface bytes
    local net_now=0
    for iface in /sys/class/net/*/statistics/rx_bytes; do
        [ -f "$iface" ] || continue
        local rx=$(cat "$iface" 2>/dev/null || echo 0)
        local tx=$(cat "${iface%rx_bytes}tx_bytes" 2>/dev/null || echo 0)
        net_now=$((net_now + rx + tx))
    done

    # Calculate delta from previous reading
    local net_prev=$(cat "$NET_PREV_FILE" 2>/dev/null || echo "$net_now")
    echo "$net_now" > "$NET_PREV_FILE"
    local net_delta=$((net_now - net_prev))
    [ "$net_delta" -lt 0 ] && net_delta=0

    # Convert to percentage (scale: 10MB/s = 100%)
    # 10MB = 10485760 bytes, measured over ~1.5s interval
    local net_pct=$((net_delta * 100 / 15728640))
    [ "$net_pct" -gt 100 ] && net_pct=100

    # === Combine: weighted average (60% CPU, 40% network) ===
    local pct=$(( (cpu_pct * 60 + net_pct * 40) / 100 ))
    [ "$pct" -gt 100 ] && pct=100

    # === Color gradient with more variety ===
    local r g b
    if [ "$pct" -le 20 ]; then
        # Green (idle) - pure green
        r=0; g=255; b=0
    elif [ "$pct" -le 40 ]; then
        # Green to Cyan (light activity)
        r=0; g=255; b=$(( (pct - 20) * 12 ))
    elif [ "$pct" -le 60 ]; then
        # Cyan to Yellow (moderate)
        r=$(( (pct - 40) * 12 ))
        g=255
        b=$((255 - (pct - 40) * 12))
    elif [ "$pct" -le 80 ]; then
        # Yellow to Orange (busy)
        r=255
        g=$((255 - (pct - 60) * 8))
        b=0
    else
        # Orange to Red (max capacity)
        r=255
        g=$((100 - (pct - 80) * 5))
        [ "$g" -lt 0 ] && g=0
        b=0
    fi

    led_set_rgb 3 "$r" "$g" "$b"
}

led_set() {
    local led="$1"
    local state="$2"  # 0 = off, 1 = on
    [ "$LED_ENABLED" = "1" ] || return 0
    [ -f "${led}/brightness" ] && echo "$state" > "${led}/brightness" 2>/dev/null
}

led_pulse() {
    local led="$1"
    [ "$LED_ENABLED" = "1" ] || return 0
    led_set "$led" 255
    # Brief flash - actual timing handled by background subshell
    ( sleep 1 && led_set "$led" 0 ) &
}

# Security threat level for LED2
# Reads from CrowdSec alerts and mitmproxy threats
THREAT_CACHE_FILE="/tmp/secubox/threat_level"

get_threat_level() {
    # Check cache age (refresh every 10s)
    local cache_age=999
    if [ -f "$THREAT_CACHE_FILE" ]; then
        local now=$(date +%s)
        local mtime=$(stat -c %Y "$THREAT_CACHE_FILE" 2>/dev/null || echo 0)
        cache_age=$((now - mtime))
    fi

    if [ "$cache_age" -lt 10 ]; then
        cat "$THREAT_CACHE_FILE" 2>/dev/null || echo 0
        return
    fi

    # Count recent threats (last 5 minutes)
    local threats=0

    # CrowdSec alerts
    if [ -f "/var/log/crowdsec.log" ]; then
        local cs_alerts=$(tail -100 /var/log/crowdsec.log 2>/dev/null | grep -c "alert" || echo 0)
        threats=$((threats + cs_alerts))
    fi

    # CrowdSec decisions (active bans)
    if command -v cscli >/dev/null 2>&1; then
        local bans=$(cscli decisions list -o raw 2>/dev/null | wc -l || echo 0)
        threats=$((threats + bans / 2))
    fi

    # mitmproxy threats today
    local mitm_threats=$(uci -q get mitmproxy.stats.threats_today 2>/dev/null || echo 0)
    threats=$((threats + mitm_threats / 10))

    # Normalize to 0-100
    [ "$threats" -gt 100 ] && threats=100
    echo "$threats" > "$THREAT_CACHE_FILE"
    echo "$threats"
}

# Get global health score from services
HEALTH_CACHE_FILE="/tmp/secubox/health_score"

get_health_score() {
    # Check cache age (refresh every 15s)
    local cache_age=999
    if [ -f "$HEALTH_CACHE_FILE" ]; then
        local now=$(date +%s)
        local mtime=$(stat -c %Y "$HEALTH_CACHE_FILE" 2>/dev/null || echo 0)
        cache_age=$((now - mtime))
    fi

    if [ "$cache_age" -lt 15 ]; then
        cat "$HEALTH_CACHE_FILE" 2>/dev/null || echo 100
        return
    fi

    local score=100
    local penalty=0

    # Check critical services
    pgrep haproxy >/dev/null 2>&1 || penalty=$((penalty + 20))
    pgrep crowdsec >/dev/null 2>&1 || penalty=$((penalty + 15))
    lxc-info -n haproxy 2>/dev/null | grep -q RUNNING || penalty=$((penalty + 20))

    # Check system resources
    local mem_free=$(grep MemAvailable /proc/meminfo 2>/dev/null | awk '{print $2}')
    local mem_total=$(grep MemTotal /proc/meminfo 2>/dev/null | awk '{print $2}')
    if [ -n "$mem_free" ] && [ -n "$mem_total" ] && [ "$mem_total" -gt 0 ]; then
        local mem_pct=$((mem_free * 100 / mem_total))
        [ "$mem_pct" -lt 10 ] && penalty=$((penalty + 25))
        [ "$mem_pct" -lt 20 ] && penalty=$((penalty + 10))
    fi

    # Check disk space
    local disk_pct=$(df / 2>/dev/null | tail -1 | awk '{print $5}' | tr -d '%')
    [ -n "$disk_pct" ] && [ "$disk_pct" -gt 90 ] && penalty=$((penalty + 20))

    score=$((100 - penalty))
    [ "$score" -lt 0 ] && score=0
    echo "$score" > "$HEALTH_CACHE_FILE"
    echo "$score"
}

# Heartbeat function - 3 dedicated LEDs
# LED1: Global health (green=healthy, yellow=degraded, red=critical) with pulse
# LED2: Security threat meter (green=safe, yellow=activity, orange=elevated, red=attack)
# LED3: Global capacity (CPU+Network) - handled by led_global_capacity()
led_heartbeat() {
    [ "$LED_ENABLED" = "1" ] || return 0
    local status="$1"  # healthy, warning, error

    # LED1: Global health status with rainbow pulse effect
    local health=$(get_health_score)
    local r1 g1 b1

    if [ "$health" -ge 80 ]; then
        # Healthy - Green with subtle color cycling
        case "$((LED_COLOR_CYCLE % 3))" in
            0) r1=0; g1=255; b1=0 ;;      # Pure green
            1) r1=0; g1=255; b1=50 ;;     # Green-cyan
            2) r1=50; g1=255; b1=0 ;;     # Green-yellow
        esac
    elif [ "$health" -ge 50 ]; then
        # Degraded - Yellow/Orange cycling
        case "$((LED_COLOR_CYCLE % 3))" in
            0) r1=255; g1=255; b1=0 ;;    # Yellow
            1) r1=255; g1=200; b1=0 ;;    # Gold
            2) r1=255; g1=150; b1=0 ;;    # Orange
        esac
    else
        # Critical - Red pulsing
        case "$((LED_COLOR_CYCLE % 3))" in
            0) r1=255; g1=0; b1=0 ;;      # Red
            1) r1=255; g1=50; b1=0 ;;     # Red-orange
            2) r1=200; g1=0; b1=50 ;;     # Red-magenta
        esac
    fi
    led_set_rgb 1 "$r1" "$g1" "$b1"
    LED_COLOR_CYCLE=$(( (LED_COLOR_CYCLE + 1) % 6 ))

    # LED2: Security threat level
    local threat=$(get_threat_level)
    local r2 g2 b2

    if [ "$threat" -le 5 ]; then
        # Green - all quiet
        r2=0; g2=255; b2=0
    elif [ "$threat" -le 20 ]; then
        # Green-Yellow - minor activity
        r2=$((threat * 10)); g2=255; b2=0
    elif [ "$threat" -le 50 ]; then
        # Yellow - elevated activity
        r2=255; g2=255; b2=0
    elif [ "$threat" -le 75 ]; then
        # Orange - significant threats
        r2=255; g2=$((180 - (threat - 50) * 4)); b2=0
    else
        # Red - under attack / high threat
        r2=255; g2=$((50 - (threat - 75) * 2))
        [ "$g2" -lt 0 ] && g2=0
        b2=0
    fi

    led_set_rgb 2 "$r2" "$g2" "$b2"
}

# Auto-discover SecuBox services from ctl scripts
discover_secubox_services() {
    local services=""

    # Discover LXC-based services from *ctl scripts
    for ctl in /usr/sbin/*ctl; do
        [ -x "$ctl" ] || continue
        local basename=$(basename "$ctl")

        # Extract service name from xxxctl pattern
        local svc_name=""
        case "$basename" in
            haproxyctl)
                svc_name="haproxy"
                services="$services haproxy:lxc:5"
                ;;
            lyrionctl)
                svc_name="lyrion"
                services="$services lyrion:lxc:10"
                ;;
            mitmproxyctl)
                svc_name="mitmproxy"
                services="$services mitmproxy:lxc:10"
                ;;
            metablogizerctl)
                svc_name="metablogizer"
                services="$services metablogizer:lxc:10"
                ;;
            hexojsctl)
                svc_name="hexojs"
                services="$services hexojs:lxc:10"
                ;;
            adguardhomectl)
                svc_name="adguardhome"
                services="$services adguardhome:docker:10"
                ;;
        esac
    done

    # Add native services (PID-based)
    if [ -x "/etc/init.d/crowdsec" ]; then
        services="$services crowdsec:pid:10"
    fi
    if [ -x "/etc/init.d/tor" ]; then
        services="$services tor:pid:10"
    fi

    echo "$services"
}

# Logging function
log() {
    local level="$1"
    shift
    local message="$*"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo "[$timestamp] [$level] $message" | tee -a "$LOG_FILE"
    # Only log info and above to syslog (skip debug)
    [ "$level" != "debug" ] && logger -t secubox-core -p "user.$level" "$message"
}

# Get system status
get_status() {
    json_init

    # Core info
    json_add_string "version" "$SECUBOX_VERSION"
    json_add_boolean "running" 1
    json_add_string "hostname" "$(uci -q get system.@system[0].hostname)"
    json_add_string "uptime" "$(uptime | awk '{print $3,$4}' | sed 's/,//')"

    # System resources
    json_add_object "resources"
        # CPU load
        local load_1min=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | tr -d ',')
        json_add_string "cpu_load" "$load_1min"

        # Memory
        local mem_total=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
        local mem_free=$(awk '/MemAvailable/ {print $2}' /proc/meminfo)
        local mem_used=$((mem_total - mem_free))
        local mem_percent=$((mem_used * 100 / mem_total))
        json_add_int "memory_total_kb" "$mem_total"
        json_add_int "memory_used_kb" "$mem_used"
        json_add_int "memory_percent" "$mem_percent"

        # Storage
        local storage_info=$(df -k / | tail -1)
        local storage_total=$(echo "$storage_info" | awk '{print $2}')
        local storage_used=$(echo "$storage_info" | awk '{print $3}')
        local storage_percent=$(echo "$storage_info" | awk '{print $5}' | tr -d '%')
        json_add_int "storage_total_kb" "$storage_total"
        json_add_int "storage_used_kb" "$storage_used"
        json_add_int "storage_percent" "$storage_percent"
    json_close_object

    # Network status
    json_add_object "network"
        # WAN status
        local wan_device=$(uci -q get network.wan.device || uci -q get network.wan.ifname || echo "unknown")
        local wan_ip=$(ip -4 addr show dev "$wan_device" 2>/dev/null | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1 | head -1 || echo "none")
        local wan_connected=0
        [ -n "$wan_ip" ] && wan_connected=1

        json_add_object "wan"
            json_add_boolean "connected" "$wan_connected"
            json_add_string "device" "$wan_device"
            json_add_string "ipaddr" "$wan_ip"
        json_close_object

        # LAN status
        local lan_ip=$(uci -q get network.lan.ipaddr || echo "none")
        json_add_object "lan"
            json_add_string "ipaddr" "$lan_ip"
            json_add_string "netmask" "$(uci -q get network.lan.netmask || echo 'none')"
        json_close_object
    json_close_object

    # Installed modules - use cached opkg status for performance
    # Build installed packages cache from opkg status db (fast, no subprocess per pkg)
    local OPKG_STATUS_DB="/usr/lib/opkg/status"
    local installed_cache="/tmp/secubox-installed-status"
    if [ -r "$OPKG_STATUS_DB" ]; then
        # BusyBox-compatible: use grep instead of awk
        grep "^Package: " "$OPKG_STATUS_DB" | cut -d' ' -f2 > "$installed_cache" 2>/dev/null
    fi

    json_add_array "modules"
        if [ -d "/usr/share/secubox/plugins/catalog" ]; then
            for catalog in /usr/share/secubox/plugins/catalog/*.json; do
                [ -f "$catalog" ] || continue
                local module_id=$(jsonfilter -i "$catalog" -e '@.id' 2>/dev/null)
                local module_name=$(jsonfilter -i "$catalog" -e '@.name' 2>/dev/null)

                # Check if module package is installed (try both paths for compatibility)
                local packages=$(jsonfilter -i "$catalog" -e '@.packages.required[0]' 2>/dev/null)
                [ -z "$packages" ] && packages=$(jsonfilter -i "$catalog" -e '@.packages[0]' 2>/dev/null)
                local installed=0
                if [ -n "$packages" ] && [ -f "$installed_cache" ]; then
                    grep -q "^${packages}$" "$installed_cache" && installed=1
                fi

                json_add_object ""
                    json_add_string "id" "$module_id"
                    json_add_string "name" "$module_name"
                    json_add_boolean "installed" "$installed"
                json_close_object
            done
        fi
    json_close_array
    rm -f "$installed_cache"

    json_dump
}

# Health check function
run_health_check() {
    local overall_status="healthy"
    local warnings=0
    local errors=0
    local details=""

    # Check CPU
    local cpu_threshold=$(uci -q get secubox.settings.health_threshold_cpu || echo "80")
    local cpu_load=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | tr -d ',' | cut -d'.' -f1)
    local cpu_load_full=$(uptime | awk -F'load average:' '{print $2}' | awk '{print $1}' | tr -d ',')
    if [ "$cpu_load" -gt "$cpu_threshold" ]; then
        log warn "CPU load high: $cpu_load"
        warnings=$((warnings + 1))
        overall_status="warning"
    fi

    # Check memory
    local mem_threshold=$(uci -q get secubox.settings.health_threshold_memory || echo "90")
    local mem_total=$(awk '/MemTotal/ {print $2}' /proc/meminfo)
    local mem_free=$(awk '/MemAvailable/ {print $2}' /proc/meminfo)
    local mem_percent=$(( (mem_total - mem_free) * 100 / mem_total ))
    if [ "$mem_percent" -gt "$mem_threshold" ]; then
        log warn "Memory usage high: ${mem_percent}%"
        warnings=$((warnings + 1))
        overall_status="warning"
    fi

    # Check storage
    local storage_threshold=$(uci -q get secubox.settings.health_threshold_storage || echo "85")
    local storage_percent=$(df / | tail -1 | awk '{print $5}' | tr -d '%')
    if [ "$storage_percent" -gt "$storage_threshold" ]; then
        log warn "Storage usage high: ${storage_percent}%"
        warnings=$((warnings + 1))
        overall_status="warning"
    fi

    # Check network connectivity
    local network_ok=1
    if ! ping -c 1 -W 2 8.8.8.8 >/dev/null 2>&1; then
        log warn "No internet connectivity"
        warnings=$((warnings + 1))
        network_ok=0
    fi

    # Module health check - use appstore output for accurate data
    local modules_installed=0
    local modules_enabled=0
    local modules_active=0
    local modules_total=0
    local modules_failed=0

    if [ -x /usr/sbin/secubox-appstore ]; then
        local appstore_out=$(/usr/sbin/secubox-appstore list --json 2>/dev/null)
        if [ -n "$appstore_out" ]; then
            modules_total=$(echo "$appstore_out" | jsonfilter -e '@.modules[*].id' 2>/dev/null | wc -l)
            modules_installed=$(echo "$appstore_out" | jsonfilter -e '@.modules[*]' 2>/dev/null | grep '"installed": true' | wc -l)
            modules_enabled=$(echo "$appstore_out" | jsonfilter -e '@.modules[*]' 2>/dev/null | grep '"enabled": true' | wc -l)
            modules_active=$(echo "$appstore_out" | jsonfilter -e '@.modules[*]' 2>/dev/null | grep '"active": true' | wc -l)
            # Count enabled but not active as potentially failed
            modules_failed=$((modules_enabled - modules_active))
            [ "$modules_failed" -lt 0 ] && modules_failed=0

            if [ "$modules_failed" -gt 0 ]; then
                log warn "$modules_failed enabled modules not running"
                warnings=$((warnings + modules_failed))
                [ "$overall_status" = "healthy" ] && overall_status="warning"
            fi
        fi
    fi

    # Output health status
    json_init
    json_add_string "status" "$overall_status"
    json_add_int "warnings" "$warnings"
    json_add_int "errors" "$errors"
    json_add_string "timestamp" "$(date -Iseconds)"

    # Resource details
    json_add_object "resources"
        json_add_string "cpu_load" "$cpu_load_full"
        json_add_int "cpu_threshold" "$cpu_threshold"
        json_add_int "memory_percent" "$mem_percent"
        json_add_int "memory_threshold" "$mem_threshold"
        json_add_int "memory_total_mb" "$((mem_total / 1024))"
        json_add_int "storage_percent" "$storage_percent"
        json_add_int "storage_threshold" "$storage_threshold"
    json_close_object

    # Network status
    json_add_object "network"
        json_add_boolean "internet" "$network_ok"
    json_close_object

    # Module status
    json_add_object "modules"
        json_add_int "total" "$modules_total"
        json_add_int "installed" "$modules_installed"
        json_add_int "enabled" "$modules_enabled"
        json_add_int "active" "$modules_active"
        json_add_int "failed" "$modules_failed"
    json_close_object

    json_dump

    return 0
}

# Service watchdog function
run_watchdog() {
    local watchdog_enabled=$(uci -q get secubox.main.watchdog_enabled || echo "1")
    [ "$watchdog_enabled" != "1" ] && return 0

    # Auto-discover services if none configured
    local services=$(uci -q get secubox.main.watchdog_services)
    [ -z "$services" ] && services=$(discover_secubox_services)

    local restart_count=0
    local checked_count=0
    local running_count=0
    local services_status=""

    log debug "Watchdog: Checking services..."

    for service_entry in $services; do
        local service_name=$(echo "$service_entry" | cut -d: -f1)
        local check_method=$(echo "$service_entry" | cut -d: -f2)
        local restart_delay=$(echo "$service_entry" | cut -d: -f3)
        [ -z "$restart_delay" ] && restart_delay=5
        [ -z "$check_method" ] && check_method="pid"

        # Determine if service is enabled (check ctl script or init.d)
        local ctl_script="/usr/sbin/${service_name}ctl"
        local init_script="/etc/init.d/$service_name"
        local is_enabled=false

        if [ -x "$ctl_script" ]; then
            # Check via UCI for LXC/Docker services
            local uci_enabled=$(uci -q get "$service_name.main.enabled" 2>/dev/null || echo "0")
            [ "$uci_enabled" = "1" ] && is_enabled=true
        elif [ -x "$init_script" ]; then
            $init_script enabled >/dev/null 2>&1 && is_enabled=true
        fi

        # Skip disabled services
        [ "$is_enabled" = "false" ] && continue

        checked_count=$((checked_count + 1))
        local is_running=false
        local status_detail=""

        case "$check_method" in
            pid)
                # Check via pgrep
                if pgrep "$service_name" >/dev/null 2>&1; then
                    is_running=true
                    status_detail="pid=$(pgrep -o "$service_name")"
                fi
                ;;
            docker)
                # Check Docker container (secbx- prefix)
                local container_name="secbx-${service_name}"
                if docker ps --filter "name=$container_name" --format "{{.Names}}" 2>/dev/null | grep -q "$container_name"; then
                    is_running=true
                    status_detail="container=$container_name"
                fi
                ;;
            lxc)
                # Check LXC container
                if lxc-info -n "$service_name" -s 2>/dev/null | grep -q "RUNNING"; then
                    is_running=true
                    # Get container IP if available
                    local lxc_ip=$(lxc-info -n "$service_name" -i 2>/dev/null | awk '{print $2}' | head -1)
                    [ -n "$lxc_ip" ] && status_detail="ip=$lxc_ip"
                fi
                ;;
            port:*)
                # Check if port is listening
                local port=$(echo "$check_method" | cut -d: -f2)
                local port_hex=$(printf '%04X' "$port")
                if grep -q ":$port_hex " /proc/net/tcp /proc/net/tcp6 2>/dev/null; then
                    is_running=true
                    status_detail="port=$port"
                fi
                ;;
        esac

        if [ "$is_running" = "true" ]; then
            running_count=$((running_count + 1))
            services_status="$services_status ${service_name}:ok"
        else
            services_status="$services_status ${service_name}:down"
            log warn "Watchdog: $service_name is down, restarting..."
            sleep "$restart_delay"

            # Double-check before restart (service might have recovered)
            case "$check_method" in
                pid) pgrep "$service_name" >/dev/null 2>&1 && { running_count=$((running_count + 1)); continue; } ;;
                lxc) lxc-info -n "$service_name" -s 2>/dev/null | grep -q "RUNNING" && { running_count=$((running_count + 1)); continue; } ;;
            esac

            # Restart using ctl script if available, otherwise init.d
            if [ -x "$ctl_script" ]; then
                $ctl_script restart >/dev/null 2>&1 &
            elif [ -x "$init_script" ]; then
                $init_script restart >/dev/null 2>&1 &
            fi
            restart_count=$((restart_count + 1))

            log info "Watchdog: Restarted $service_name"
        fi
    done

    # Save detailed watchdog state
    json_init
    json_add_string "last_check" "$(date -Iseconds)"
    json_add_int "restarts" "$restart_count"
    json_add_int "checked" "$checked_count"
    json_add_int "running" "$running_count"

    json_add_object "services"
    for svc_status in $services_status; do
        local svc=$(echo "$svc_status" | cut -d: -f1)
        local status=$(echo "$svc_status" | cut -d: -f2)
        json_add_string "$svc" "$status"
    done
    json_close_object

    json_dump > "$WATCHDOG_STATE" 2>/dev/null

    return 0
}

# Get list of UCI-configured services to watch
get_watchdog_services() {
    # Core services always monitored if enabled
    local core_services="haproxy crowdsec"

    # Scan for secubox apps with watchdog=1
    for conf in $(uci show 2>/dev/null | grep "\.watchdog=" | grep "'1'" | cut -d. -f1-2); do
        local service=$(uci -q get "$conf.service")
        [ -n "$service" ] && core_services="$core_services $service"
    done

    echo "$core_services"
}

# Event pulse - flash led3 with specific color when events happen
# Usage: echo "config|task|alert" > /tmp/secubox/led-event
led_event_pulse() {
    local event_file="/tmp/secubox/led-event"
    [ -f "$event_file" ] || return 1

    local event=$(cat "$event_file" 2>/dev/null)
    rm -f "$event_file"
    [ -z "$event" ] && return 1

    # Flash pattern based on event type
    case "$event" in
        config)
            # White flash for config changes
            led_set_rgb 3 255 255 255
            ;;
        task)
            # Cyan flash for scheduled tasks
            led_set_rgb 3 0 255 255
            ;;
        alert)
            # Magenta flash for alerts
            led_set_rgb 3 255 0 255
            ;;
        network)
            # Blue flash for network events
            led_set_rgb 3 0 100 255
            ;;
        *)
            # Purple for unknown events
            led_set_rgb 3 180 0 255
            ;;
    esac
    return 0
}

# Fast LED heartbeat background loop
# Runs independently at 1.5s intervals for reactive visual feedback
# led1: Global health status
# led2: Security threat level
# led3: Global capacity meter (CPU + Network) with event pulse overlay
# mmc0: Classic heartbeat when states stable, rapid blink on changes
led_heartbeat_loop() {
    local status_file="/tmp/secubox/led-status"
    echo "healthy" > "$status_file"

    local prev_health=0
    local prev_threat=0
    local stable_counter=0

    while true; do
        local status=$(cat "$status_file" 2>/dev/null || echo "healthy")

        # Get current states
        local cur_health=$(get_health_score)
        local cur_threat=$(get_threat_level)

        # Detect state changes
        local health_delta=$((cur_health - prev_health))
        [ "$health_delta" -lt 0 ] && health_delta=$((-health_delta))
        local threat_delta=$((cur_threat - prev_threat))
        [ "$threat_delta" -lt 0 ] && threat_delta=$((-threat_delta))

        # Check if states are stable (delta < 5)
        if [ "$health_delta" -lt 5 ] && [ "$threat_delta" -lt 5 ]; then
            stable_counter=$((stable_counter + 1))
        else
            stable_counter=0
        fi

        # mmc0: classic heartbeat when stable for 3+ cycles
        if [ "$stable_counter" -ge 3 ]; then
            led_mmc0_heartbeat 1  # Stable
        else
            led_mmc0_heartbeat 0  # Changing
        fi

        prev_health=$cur_health
        prev_threat=$cur_threat

        # Update RGB LEDs
        led_heartbeat "$status"

        # Check for event pulse first (overrides capacity momentarily)
        if led_event_pulse; then
            sleep 0.3  # Brief flash
        fi

        # Global capacity on led3
        led_global_capacity
        sleep 1.2
    done
}

# Daemon mode
daemon_mode() {
    log info "SecuBox Core daemon starting (version $SECUBOX_VERSION)"

    # Write PID
    echo $$ > "$PID_FILE"

    # Initialize LED heartbeat
    led_init
    led_heartbeat boot

    # Get health check interval
    local health_interval=$(uci -q get secubox.main.health_check_interval || echo "300")

    # Get watchdog interval (faster than health check)
    local watchdog_interval=$(uci -q get secubox.main.watchdog_interval || echo "60")

    # LED heartbeat setting (enabled by default)
    local led_heartbeat_enabled=$(uci -q get secubox.main.led_heartbeat || echo "1")

    # Start fast LED heartbeat in background
    if [ "$led_heartbeat_enabled" = "1" ]; then
        led_heartbeat_loop &
        LED_HEARTBEAT_PID=$!
        log debug "LED heartbeat loop started (PID: $LED_HEARTBEAT_PID)"
    fi

    # Main daemon loop
    local health_counter=0
    local health_cycles=$((health_interval / watchdog_interval))
    [ "$health_cycles" -lt 1 ] && health_cycles=1
    local last_health_status="healthy"

    while true; do
        # Run watchdog every cycle
        run_watchdog

        # Run health check every N cycles
        health_counter=$((health_counter + 1))
        if [ "$health_counter" -ge "$health_cycles" ]; then
            local health_output=$(run_health_check)
            echo "$health_output" > /tmp/secubox/health-status.json
            # Extract status for LED and update status file
            last_health_status=$(echo "$health_output" | jsonfilter -e '@.status' 2>/dev/null || echo "healthy")
            echo "$last_health_status" > /tmp/secubox/led-status
            health_counter=0
        fi

        # Sleep until next check
        sleep "$watchdog_interval"
    done
}

# Main command router
case "$1" in
    daemon)
        daemon_mode
        ;;
    status)
        get_status
        ;;
    health)
        run_health_check
        ;;
    reload)
        log info "Reloading configuration"
        killall -HUP secubox-core 2>/dev/null || true
        ;;
    watchdog)
        run_watchdog
        ;;
    *)
        echo "Usage: $0 {daemon|status|health|reload|watchdog}"
        exit 1
        ;;
esac
