#!/bin/sh
# event-forwarder - Forward OpenWrt events to central server
# Copyright (c) 2026 Luis Rosales - MIT License
# Size: ~20KB

set -euo pipefail

# Config
CONFIG_FILE="/etc/config/event-forwarder"
LOG_TAG="event-forwarder"

# Load config
load_config() {
    # Server URL
    SERVER_URL=$(uci get event-forwarder.server.url 2>/dev/null || echo "")
    SERVER_TOKEN=$(uci get event-forwarder.server.token 2>/dev/null || echo "")
    TIMEOUT=$(uci get event-forwarder.server.timeout 2>/dev/null || echo "10")
    RETRY=$(uci get event-forwarder.server.retry 2>/dev/null || echo "3")
    
    # Router ID (use hostname if not set)
    ROUTER_ID=$(uci get event-forwarder.router.id 2>/dev/null || hostname)
    
    # Event flags
    EVT_WIFI_CONN=$(uci get event-forwarder.events.wifi_connect 2>/dev/null || echo "1")
    EVT_WIFI_DISC=$(uci get event-forwarder.events.wifi_disconnect 2>/dev/null || echo "1")
    EVT_DHCP=$(uci get event-forwarder.events.dhcp_lease 2>/dev/null || echo "1")
    
    # Validate
    if [ -z "$SERVER_URL" ]; then
        log_err "Server URL not configured"
        exit 1
    fi
}

# Logger
log_debug() { [ "${DEBUG:-0}" = "1" ] && logger -t "$LOG_TAG" -p user.debug "$@"; }
log_info()  { logger -t "$LOG_TAG" -p user.info "$@"; }
log_err()   { logger -t "$LOG_TAG" -p user.err "$@"; }

# Send event to server
send_event() {
    local event_json="$1"
    local attempt=0
    
    while [ $attempt -lt $RETRY ]; do
        attempt=$((attempt + 1))
        
        response=$(curl -s -w "%{http_code}" \
            --max-time "$TIMEOUT" \
            -X POST "$SERVER_URL" \
            -H "Authorization: Bearer $SERVER_TOKEN" \
            -H "Content-Type: application/json" \
            -d "$event_json" 2>/dev/null) || true
        
        http_code="${response: -3}"
        
        if [ "$http_code" = "200" ] || [ "$http_code" = "201" ]; then
            log_debug "Event sent successfully"
            return 0
        fi
        
        log_debug "Attempt $attempt failed (HTTP $http_code)"
        sleep 1
    done
    
    log_err "Failed to send event after $RETRY attempts"
    return 1
}

# Build JSON payload
build_json() {
    local event_type="$1"
    local mac="$2"
    local ip="$3"
    local hostname="$4"
    local extra="$5"
    
    # Escape special chars
    mac="${mac:-null}"
    ip="${ip:-null}"
    hostname="${hostname:-null}"
    
    cat <<EOF
{
  "router_id": "$ROUTER_ID",
  "hostname": "$(hostname)",
  "event_type": "$event_type",
  "timestamp": "$(date -Iseconds)",
  "payload": {
    "mac": "$mac",
    "ip": "$ip",
    "hostname": "$hostname"$extra
  }
}
EOF
}

# ===== EVENT LISTENERS =====

# 1. WiFi events via hostapd
listen_wifi() {
    local wlan_iface="$1"
    
    # Monitor hostapd control interface
    local ctrl_path="/var/run/hostapd-$wlan_iface"
    
    # We can't use hostapd_cli in monitor mode easily, 
    # so we'll poll the station list as fallback
    while true; do
        # Get associated stations
        if [ -f "/proc/sys/net/netfilter/nf_conntrack" ]; then
            # Check for new WiFi clients via conntrack
            # This is a simplified version
            :
        fi
        
        sleep 10
    done
}

# 2. DHCP events via dnsmasq
listen_dhcp() {
    local lease_file="/var/lib/dnsmasq/dnsmasq.leases"
    local old_leases=""
    
    while true; do
        if [ -f "$lease_file" ]; then
            current_leases=$(cat "$lease_file")
            
            # Compare and detect changes
            if [ "$current_leases" != "$old_leases" ]; then
                # Parse new/changed leases
                echo "$current_leases" | while read line; do
                    [ -z "$line" ] && continue
                    
                    set -- $line
                    local timestamp="$1"
                    local mac="$2"
                    local ip="$3"
                    local hostname="$4"
                    
                    # New lease (timestamp changed = new)
                    if [[ ! "$old_leases" == *"$mac"* ]]; then
                        json=$(build_json "dhcp_lease" "$mac" "$ip" "$hostname" ', "action": "new"')
                        log_info "DHCP lease: $mac -> $ip ($hostname)"
                        send_event "$json"
                    fi
                done
                
                old_leases="$current_leases"
            fi
        fi
        
        sleep 5
    done
}

# 3. Interface events via ubus netifd
listen_interfaces() {
    ubus -m listen network.interface 2>/dev/null | while read event; do
        event_type=$(echo "$event" | jsonfilter -e '@.handler')
        
        [ -z "$event_type" ] && continue
        
        device=$(echo "$event" | jsonfilter -e '@.interface')
        action=$(echo "$event" | jsonfilter -e '@.action')
        
        json=$(build_json "interface_${action}" "null" "null" "null" ", \"device\": \"$device\", \"action\": \"$action\"")
        log_info "Interface $action: $device"
        
        send_event "$json" &
    done
}

# 4. Generic ubus events
listen_ubus() {
    ubus -m listen 2>/dev/null | while read event; do
        # Filter network-related events
        event_json=$(echo "$event" | jsonfilter -e '@')
        
        # Check for wireless events
        if echo "$event" | grep -q "wpa"; then
            log_debug "WiFi event: $event"
        fi
    done
}

# Main loop - runs all listeners concurrently
main() {
    load_config
    
    log_info "Starting event forwarder..."
    log_info "Server: $SERVER_URL"
    
    # Trap signals for graceful shutdown
    trap 'log_info "Shutting down..."; kill 0 2>/dev/null; exit 0' TERM INT
    
    # Start listeners in background
    listen_dhcp &
    local dhcp_pid=$!
    
    listen_interfaces &
    local if_pid=$!
    
    log_info "Event listeners started (DHCP:$dhcp_pid, IFACE:$if_pid)"
    
    # Wait for any signal
    wait
}

# Run main
main "$@"