#!/bin/bash
#
# Gateway Status Monitor Script
# Purpose: Monitor gateway status and publish metrics via MQTT
# Version: 1.0.1
#

set -euo pipefail  # Exit on error, undefined variables, and pipe failures

#==============================================================================
# Configuration
#==============================================================================

#readonly MQTT_SERVER='lns1.thingseye.io'
#readonly MQTT_PORT=8883
#readonly CA_FILE='/usr/local/dragino/monitor/certificates/ca.pem'
readonly MONITOR_VERSION='1.0.1'

readonly MQTT_SERVER='120.78.138.177'
readonly MQTT_PORT=1883
readonly MQTT_USERNAME=preshiptest
readonly MQTT_PASSWORD=preshiptest


#==============================================================================
# Functions
#==============================================================================

# Check if required commands are available
check_dependencies() {
    local missing_deps=()
    
    if ! command -v jq &>/dev/null; then
        missing_deps+=("jq")
    fi
    
    if [[ ${#missing_deps[@]} -gt 0 ]]; then
        echo "Error: Missing required dependencies: ${missing_deps[*]}" >&2
        echo "Please install with: opkg update && opkg install ${missing_deps[*]}" >&2
        exit 1
    fi
}

# Get system owner from configuration
get_owner() {
    local owner
    owner=$(uci -q get remote_management.@general[0].owner 2>/dev/null || echo "")
    echo "${owner:-Dragino}"
}

# Get network interfaces information (optimized - checks specific interfaces only)
get_network_info() {
    local target_interfaces=("eth0" "wlan0" "wwan0")
    local interface_data=""
    
    for interface in "${target_interfaces[@]}"; do
        local status="disabled"
        local ip_address="N/A"
        local mac_address="N/A"
        
        # Check if interface exists
        if [[ -d "/sys/class/net/${interface}" ]]; then
            local operstate
            operstate=$(cat "/sys/class/net/${interface}/operstate" 2>/dev/null || echo "down")
            
            # Get MAC address
            mac_address=$(cat "/sys/class/net/${interface}/address" 2>/dev/null || echo "N/A")
            
            if [[ "$operstate" == "up" ]]; then
                # Get IP address
                ip_address=$(ip addr show "$interface" 2>/dev/null | \
                           grep -oE 'inet [0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(/[0-9]+)?' | \
                           head -n 1 | cut -d' ' -f2 || echo "N/A")
                
                if [[ "$ip_address" != "N/A" ]]; then
                    status="up"
                else
                    status="no-ip"
                fi
            else
                status="down"
            fi
        fi
        
        # Collect data in CSV format for batch processing
        interface_data+="${interface},${status},${ip_address},${mac_address}"$'\n'
    done
    
    # Build JSON array in one jq call (much faster)
    echo "$interface_data" | jq -R -s '
        split("\n") |
        map(select(length > 0)) |
        map(split(",") | {
            interface: .[0],
            status: .[1],
            ip_address: .[2],
            mac_address: .[3]
        })
    '
}

# Get Dragino packages information (optimized for speed)
get_dragino_packages() {
    local package_data
    
    # Get list of dragino packages and convert to JSON in one pass
    package_data=$(dpkg -l 2>/dev/null | grep dragino | awk '{print $2","$3}' || echo "")
    
    if [[ -z "$package_data" ]]; then
        echo "[]"
        return
    fi
    
    # Use jq once to build the entire array (much faster than multiple calls)
    echo "$package_data" | jq -R -s '
        split("\n") |
        map(select(length > 0)) |
        map(split(",") | {name: .[0], version: .[1]})
    '
}

# Calculate uptime in minutes (optimized)
get_uptime_minutes() {
    # Read directly from /proc/uptime (fastest and most reliable on Linux)
    # Format: uptime_seconds idle_seconds
    if [[ -r /proc/uptime ]]; then
        awk '{print int($1/60)}' /proc/uptime 2>/dev/null && return 0
    fi
    
    # Fallback: use uptime -s if available (simpler and more reliable)
    if command -v uptime &>/dev/null; then
        local uptime_since
        uptime_since=$(uptime -s 2>/dev/null)
        if [[ -n "$uptime_since" ]]; then
            local now_epoch boot_epoch
            now_epoch=$(date +%s)
            boot_epoch=$(date -d "$uptime_since" +%s 2>/dev/null)
            if [[ -n "$boot_epoch" ]]; then
                echo $(( (now_epoch - boot_epoch) / 60 ))
                return 0
            fi
        fi
    fi
    
    # Last resort: default to 0
    echo "0"
}

# Generate JSON status payload using jq with nested structure
generate_json_payload() {
    local compact="${1:-false}"
    local jq_opts="-n"
    
    # Use compact output for MQTT transmission
    if [[ "$compact" == "true" ]]; then
        jq_opts="-n -c"
    fi
    
    jq $jq_opts \
        --arg model "$model" \
        --arg hostname "$hostname" \
        --arg firmware "$firmware" \
        --arg owner "$owner" \
        --argjson uptime_minutes "$uptime_minutes" \
        --arg gateway_eui "$gateway_eui" \
        --arg iot_type "$iot_type" \
        --arg freq_plan "$freq_plan" \
        --arg online_status "$online_status" \
        --arg provision_key "$provision_key" \
        --arg cfg_version "$cfg_version" \
        --argjson network_interfaces "$network_interfaces" \
        --arg cpu_load "$cpu_load" \
        --argjson ram_free_mb "$ram_free_mb" \
        --arg disk_used "$disk_used" \
        --arg time_now "$time_now" \
        --arg monitor_version "$MONITOR_VERSION" \
        --argjson dragino_packages "$dragino_packages" \
        '{
            system: {
                model: $model,
                hostname: $hostname,
                firmware: $firmware,
                owner: $owner,
                uptime_minutes: $uptime_minutes
            },
            gateway: {
                gweui: $gateway_eui,
                iot_type: $iot_type,
                frequency_plan: $freq_plan,
                status: $online_status,
                provision_key: $provision_key,
                config_version: $cfg_version
            },
            network: {
                interfaces: $network_interfaces
            },
            resources: {
                cpu: {
                    load_1min: $cpu_load
                },
                memory: {
                    free_mb: $ram_free_mb
                },
                disk: {
                    used: $disk_used
                }
            },
            packages: {
                dragino: $dragino_packages
            },
            metadata: {
                timestamp: $time_now,
                monitor_version: $monitor_version
            }
        }'
}

#==============================================================================
# Main Script
#==============================================================================

main() {
    # Check dependencies
    check_dependencies
    
    # Retrieve system information
    local owner model hostname provision_key firmware
    local time_now gateway_eui iot_type freq_plan cpu_load ram_free_kb ram_free_mb
    local disk_used network_interfaces uptime_minutes
    local online_status cfg_version dragino_packages json_payload
    
    owner=$(get_owner)
    model=$(cat /var/iot/model.txt 2>/dev/null || echo "Unknown")
    hostname=$(hostname)
    provision_key=$(dragino-sid 2>/dev/null | grep OPTION | awk '{print $2}' || echo "N/A")
    
    # Get firmware version
    local fw_version fw_date
    fw_version=$(uci -q get gateway.general.version 2>/dev/null || echo "Unknown")
    fw_date=$(uci -q get gateway.general.version_date 2>/dev/null || echo "")
    firmware="${fw_version}${fw_date:+-}${fw_date}"
    
    time_now=$(date -u +"%Y-%m-%d %H:%M:%S")
    gateway_eui=$((uci -q get gateway.general.GWID 2>/dev/null || echo "N/A") | tr '[:upper:]' '[:lower:]')
    iot_type=$(uci -q get gateway.general.server_type 2>/dev/null || echo "N/A")
    if [[ $iot_type = lorawan ]]; then
        iot_type=semtech_udp
    elif [[ $iot_type = station ]]; then
        iot_type=basic_station
    fi
    freq_plan=$(uci -q get gateway.general.gwcfg 2>/dev/null || echo "N/A")
    
    # Get CPU load
    local uptime_output
    uptime_output=$(uptime)
    cpu_load=$(echo "${uptime_output#*load}" | awk '{print $2}' | tr -d ',' || echo "0.00")
    
    # Get memory usage
    ram_free_kb=$(free | grep Mem | awk '{print $4}' || echo "0")
    ram_free_mb=$((ram_free_kb / 1024))
    
    # Get disk usage
    disk_used=$(df -h / | awk 'NR==2{print $3}' || echo "N/A")
    
    # Get network information (returns JSON directly)
    network_interfaces=$(get_network_info)
    
    # Calculate uptime
    uptime_minutes=$(get_uptime_minutes)
    
    # Get gateway status
    online_status=$(cat /var/iot/status 2>/dev/null || echo "Unknown")
    cfg_version=$(uci -q get system.@system[0].config_ver 2>/dev/null || echo "N/A")
    
    # Get Dragino packages information
    dragino_packages=$(get_dragino_packages)
    
    # Generate JSON payload (pretty format for console output)
    #json_payload=$(generate_json_payload false)
    
    # Output JSON (for debugging or piping)
    #echo "$json_payload"
    
    # Publish via MQTT (uncomment to enable)
    # Use compact format for MQTT transmission to save bandwidth
    # if [[ -f "$CA_FILE" ]]; then
    #     local compact_json
        compact_json=$(generate_json_payload true)
        mosquitto_pub -h "$MQTT_SERVER" -p "$MQTT_PORT" -u "$MQTT_USERNAME" -P "$MQTT_PASSWORD" \
            -t "preshiptest/gateway/monitor/gateway/status/${hostname}" \
            -m "$compact_json" 
    #         --cafile "$CA_FILE"
    # else
    #     echo "Error: CA certificate file not found: $CA_FILE" >&2
    #     exit 1
    # fi
}

# Execute main function
main "$@"
