#!/bin/bash
set -euo pipefail

# System Information Collector
# Comprehensive discovery of hardware, OS, network, and storage configuration
# Part of the Current State Discovery Framework

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DISCOVERY_DIR="${SCRIPT_DIR}/results"
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
REPORT_FILE="${DISCOVERY_DIR}/system_discovery_${TIMESTAMP}.json"

# Create discovery directory
mkdir -p "$DISCOVERY_DIR"

main() {
    echo "🔍 Starting system information collection..."
    
    # Initialize JSON report
    cat > "$REPORT_FILE" << 'EOF'
{
  "discovery_metadata": {
    "timestamp": "",
    "hostname": "",
    "discovery_version": "1.0"
  },
  "hardware": {},
  "operating_system": {},
  "network": {},
  "storage": {},
  "performance": {}
}
EOF
    
    collect_metadata
    collect_hardware_info
    collect_os_info
    collect_network_info
    collect_storage_info
    collect_performance_baseline
    
    echo "✅ System discovery complete: $REPORT_FILE"
    generate_summary
}

collect_metadata() {
    echo "📋 Collecting metadata..."
    
    jq --arg timestamp "$(date -Iseconds)" \
       --arg hostname "$(hostname)" \
       '.discovery_metadata.timestamp = $timestamp | .discovery_metadata.hostname = $hostname' \
       "$REPORT_FILE" > "${REPORT_FILE}.tmp" && mv "${REPORT_FILE}.tmp" "$REPORT_FILE"
}

collect_hardware_info() {
    echo "🖥️  Collecting hardware information..."
    
    local cpu_info memory_info gpu_info storage_devices
    
    # CPU Information
    cpu_info=$(cat << 'EOF'
{
  "model": "",
  "cores": 0,
  "threads": 0,
  "architecture": "",
  "flags": []
}
EOF
    )
    
    if [[ -f /proc/cpuinfo ]]; then
        local cpu_model=$(grep "model name" /proc/cpuinfo | head -1 | cut -d: -f2 | xargs)
        local cpu_cores=$(grep "cpu cores" /proc/cpuinfo | head -1 | cut -d: -f2 | xargs)
        local cpu_threads=$(grep "processor" /proc/cpuinfo | wc -l)
        local cpu_arch=$(uname -m)
        local cpu_flags=$(grep "flags" /proc/cpuinfo | head -1 | cut -d: -f2 | xargs | tr ' ' '\n' | jq -R . | jq -s .)
        
        cpu_info=$(echo "$cpu_info" | jq --arg model "$cpu_model" \
                                         --argjson cores "${cpu_cores:-1}" \
                                         --argjson threads "$cpu_threads" \
                                         --arg arch "$cpu_arch" \
                                         --argjson flags "$cpu_flags" \
                   '.model = $model | .cores = $cores | .threads = $threads | .architecture = $arch | .flags = $flags')
    fi
    
    # Memory Information
    memory_info=$(cat << 'EOF'
{
  "total_gb": 0,
  "available_gb": 0,
  "swap_gb": 0,
  "details": {}
}
EOF
    )
    
    if [[ -f /proc/meminfo ]]; then
        local mem_total=$(grep "MemTotal" /proc/meminfo | awk '{print int($2/1024/1024)}')
        local mem_available=$(grep "MemAvailable" /proc/meminfo | awk '{print int($2/1024/1024)}')
        local swap_total=$(grep "SwapTotal" /proc/meminfo | awk '{print int($2/1024/1024)}')
        local mem_details=$(grep -E "(MemTotal|MemAvailable|MemFree|Buffers|Cached|SwapTotal)" /proc/meminfo | 
                           awk '{print "\"" tolower($1) "\":" int($2)}' | tr '\n' ',' | sed 's/,$//' | sed 's/://g')
        
        memory_info=$(echo "$memory_info" | jq --argjson total "$mem_total" \
                                              --argjson available "$mem_available" \
                                              --argjson swap "$swap_total" \
                                              --argjson details "{$mem_details}" \
                     '.total_gb = $total | .available_gb = $available | .swap_gb = $swap | .details = $details')
    fi
    
    # GPU Information
    gpu_info='[]'
    
    # Check for NVIDIA GPUs
    if command -v nvidia-smi &>/dev/null; then
        local nvidia_gpus=$(nvidia-smi --query-gpu=name,memory.total --format=csv,noheader,nounits 2>/dev/null | 
                           awk -F',' '{print "{\"name\":\"" $1 "\",\"memory_mb\":" $2 ",\"vendor\":\"NVIDIA\"}"}' | 
                           jq -s .)
        gpu_info=$(echo "$gpu_info" | jq ". + $nvidia_gpus")
    fi
    
    # Check for AMD/Intel GPUs via lspci
    local other_gpus=$(lspci | grep -i vga | 
                      awk '{print "{\"name\":\"" substr($0, index($0,$5)) "\",\"vendor\":\"" $4 "\",\"detected_via\":\"lspci\"}"}' | 
                      jq -s .)
    gpu_info=$(echo "$gpu_info" | jq ". + $other_gpus")
    
    # Storage Devices
    storage_devices='[]'
    
    if command -v lsblk &>/dev/null; then
        # Get block devices with detailed info
        while IFS= read -r line; do
            if [[ "$line" =~ ^([^[:space:]]+)[[:space:]]+([^[:space:]]+)[[:space:]]+([^[:space:]]+)[[:space:]]+([^[:space:]]*)[[:space:]]+([^[:space:]]*) ]]; then
                local device="${BASH_REMATCH[1]}"
                local size="${BASH_REMATCH[2]}"
                local type="${BASH_REMATCH[3]}"
                local mountpoint="${BASH_REMATCH[4]}"
                local fstype="${BASH_REMATCH[5]}"
                
                # Check if it's rotational (HDD vs SSD)
                local rotational="unknown"
                if [[ -f "/sys/block/$device/queue/rotational" ]]; then
                    if [[ $(cat "/sys/block/$device/queue/rotational" 2>/dev/null) == "0" ]]; then
                        rotational="ssd"
                    else
                        rotational="hdd"
                    fi
                fi
                
                local device_info=$(jq -n --arg name "$device" \
                                         --arg size "$size" \
                                         --arg type "$type" \
                                         --arg mount "$mountpoint" \
                                         --arg fs "$fstype" \
                                         --arg rotation "$rotational" \
                   '{name: $name, size: $size, type: $type, mountpoint: $mount, filesystem: $fs, storage_type: $rotation}')
                
                storage_devices=$(echo "$storage_devices" | jq ". + [$device_info]")
            fi
        done < <(lsblk -o NAME,SIZE,TYPE,MOUNTPOINT,FSTYPE --noheadings | grep -E "^[a-z]")
    fi
    
    # Combine hardware info
    local hardware_data=$(jq -n --argjson cpu "$cpu_info" \
                                --argjson memory "$memory_info" \
                                --argjson gpu "$gpu_info" \
                                --argjson storage "$storage_devices" \
                         '{cpu: $cpu, memory: $memory, gpu: $gpu, storage: $storage}')
    
    jq --argjson hardware "$hardware_data" '.hardware = $hardware' "$REPORT_FILE" > "${REPORT_FILE}.tmp" && mv "${REPORT_FILE}.tmp" "$REPORT_FILE"
}

collect_os_info() {
    echo "🐧 Collecting operating system information..."
    
    local os_info=$(cat << 'EOF'
{
  "distribution": "",
  "version": "",
  "kernel": "",
  "architecture": "",
  "installed_packages": [],
  "running_services": [],
  "firewall_status": ""
}
EOF
    )
    
    # OS Distribution and Version
    local distro="Unknown"
    local version="Unknown"
    
    if [[ -f /etc/os-release ]]; then
        distro=$(grep "^NAME=" /etc/os-release | cut -d'"' -f2)
        version=$(grep "^VERSION=" /etc/os-release | cut -d'"' -f2)
    fi
    
    local kernel=$(uname -r)
    local arch=$(uname -m)
    
    # Installed packages (limit to essential packages to avoid huge lists)
    local packages='[]'
    if command -v dpkg &>/dev/null; then
        packages=$(dpkg -l | grep "^ii" | awk '{print $2}' | grep -E "(docker|nginx|apache|mysql|postgresql|redis|nodejs|python)" | jq -R . | jq -s .)
    elif command -v rpm &>/dev/null; then
        packages=$(rpm -qa | grep -E "(docker|nginx|apache|mysql|postgresql|redis|nodejs|python)" | jq -R . | jq -s .)
    fi
    
    # Running services
    local services='[]'
    if command -v systemctl &>/dev/null; then
        services=$(systemctl list-units --type=service --state=active --no-pager --no-legend | 
                  awk '{print $1}' | sed 's/.service$//' | head -20 | jq -R . | jq -s .)
    fi
    
    # Firewall status
    local firewall_status="unknown"
    if command -v ufw &>/dev/null; then
        if ufw status | grep -q "Status: active"; then
            firewall_status="ufw_active"
        else
            firewall_status="ufw_inactive"
        fi
    elif command -v firewall-cmd &>/dev/null; then
        if firewall-cmd --state 2>/dev/null | grep -q "running"; then
            firewall_status="firewalld_active"
        else
            firewall_status="firewalld_inactive"
        fi
    fi
    
    os_info=$(echo "$os_info" | jq --arg distro "$distro" \
                                  --arg version "$version" \
                                  --arg kernel "$kernel" \
                                  --arg arch "$arch" \
                                  --argjson packages "$packages" \
                                  --argjson services "$services" \
                                  --arg firewall "$firewall_status" \
             '.distribution = $distro | .version = $version | .kernel = $kernel | .architecture = $arch | .installed_packages = $packages | .running_services = $services | .firewall_status = $firewall')
    
    jq --argjson os "$os_info" '.operating_system = $os' "$REPORT_FILE" > "${REPORT_FILE}.tmp" && mv "${REPORT_FILE}.tmp" "$REPORT_FILE"
}

collect_network_info() {
    echo "🌐 Collecting network configuration..."
    
    local network_info=$(cat << 'EOF'
{
  "interfaces": [],
  "routing": [],
  "dns_config": {},
  "open_ports": [],
  "docker_networks": []
}
EOF
    )
    
    # Network interfaces
    local interfaces='[]'
    if command -v ip &>/dev/null; then
        while IFS= read -r line; do
            if [[ "$line" =~ ^[0-9]+:[[:space:]]+([^:]+): ]]; then
                local iface="${BASH_REMATCH[1]}"
                local ip_addr=$(ip addr show "$iface" 2>/dev/null | grep "inet " | head -1 | awk '{print $2}' || echo "")
                local state=$(ip link show "$iface" 2>/dev/null | head -1 | grep -o "state [A-Z]*" | cut -d' ' -f2 || echo "UNKNOWN")
                
                local iface_info=$(jq -n --arg name "$iface" --arg ip "$ip_addr" --arg state "$state" \
                                  '{name: $name, ip_address: $ip, state: $state}')
                interfaces=$(echo "$interfaces" | jq ". + [$iface_info]")
            fi
        done < <(ip link show)
    fi
    
    # Routing table
    local routing='[]'
    if command -v ip &>/dev/null; then
        while IFS= read -r line; do
            local route_info=$(echo "$line" | jq -R .)
            routing=$(echo "$routing" | jq ". + [$route_info]")
        done < <(ip route show | head -10)
    fi
    
    # DNS configuration
    local dns_config='{}'
    if [[ -f /etc/resolv.conf ]]; then
        local nameservers=$(grep "nameserver" /etc/resolv.conf | awk '{print $2}' | jq -R . | jq -s .)
        local domain=$(grep "domain\|search" /etc/resolv.conf | head -1 | awk '{print $2}' || echo "")
        dns_config=$(jq -n --argjson nameservers "$nameservers" --arg domain "$domain" \
                    '{nameservers: $nameservers, domain: $domain}')
    fi
    
    # Open ports
    local open_ports='[]'
    if command -v ss &>/dev/null; then
        while IFS= read -r line; do
            local port_info=$(echo "$line" | jq -R .)
            open_ports=$(echo "$open_ports" | jq ". + [$port_info]")
        done < <(ss -tuln | grep LISTEN | head -20)
    elif command -v netstat &>/dev/null; then
        while IFS= read -r line; do
            local port_info=$(echo "$line" | jq -R .)
            open_ports=$(echo "$open_ports" | jq ". + [$port_info]")
        done < <(netstat -tuln | grep LISTEN | head -20)
    fi
    
    # Docker networks
    local docker_networks='[]'
    if command -v docker &>/dev/null && docker info &>/dev/null; then
        while IFS= read -r line; do
            local network_info=$(echo "$line" | jq -R . | jq 'split(" ") | {name: .[0], id: .[1], driver: .[2], scope: .[3]}')
            docker_networks=$(echo "$docker_networks" | jq ". + [$network_info]")
        done < <(docker network ls --format "{{.Name}} {{.ID}} {{.Driver}} {{.Scope}}" 2>/dev/null || echo "")
    fi
    
    network_info=$(echo "$network_info" | jq --argjson interfaces "$interfaces" \
                                            --argjson routing "$routing" \
                                            --argjson dns "$dns_config" \
                                            --argjson ports "$open_ports" \
                                            --argjson docker_nets "$docker_networks" \
                  '.interfaces = $interfaces | .routing = $routing | .dns_config = $dns | .open_ports = $ports | .docker_networks = $docker_nets')
    
    jq --argjson network "$network_info" '.network = $network' "$REPORT_FILE" > "${REPORT_FILE}.tmp" && mv "${REPORT_FILE}.tmp" "$REPORT_FILE"
}

collect_storage_info() {
    echo "💾 Collecting storage information..."
    
    local storage_info=$(cat << 'EOF'
{
  "filesystems": [],
  "disk_usage": [],
  "mount_points": [],
  "docker_volumes": []
}
EOF
    )
    
    # Filesystem information
    local filesystems='[]'
    while IFS= read -r line; do
        local fs_info=$(echo "$line" | awk '{print "{\"filesystem\":\"" $1 "\",\"size\":\"" $2 "\",\"used\":\"" $3 "\",\"available\":\"" $4 "\",\"use_percent\":\"" $5 "\",\"mount\":\"" $6 "\"}"}' | jq .)
        filesystems=$(echo "$filesystems" | jq ". + [$fs_info]")
    done < <(df -h | grep -E "^/dev")
    
    # Disk usage for important directories
    local disk_usage='[]'
    local important_dirs=("/home" "/var" "/opt" "/usr" "/etc")
    for dir in "${important_dirs[@]}"; do
        if [[ -d "$dir" ]]; then
            local usage=$(du -sh "$dir" 2>/dev/null | awk '{print $1}' || echo "unknown")
            local usage_info=$(jq -n --arg dir "$dir" --arg size "$usage" '{directory: $dir, size: $size}')
            disk_usage=$(echo "$disk_usage" | jq ". + [$usage_info]")
        fi
    done
    
    # Mount points with options
    local mount_points='[]'
    while IFS= read -r line; do
        if [[ "$line" =~ ^([^[:space:]]+)[[:space:]]+([^[:space:]]+)[[:space:]]+([^[:space:]]+)[[:space:]]+([^[:space:]]+) ]]; then
            local device="${BASH_REMATCH[1]}"
            local mount="${BASH_REMATCH[2]}"
            local fstype="${BASH_REMATCH[3]}"
            local options="${BASH_REMATCH[4]}"
            
            local mount_info=$(jq -n --arg device "$device" --arg mount "$mount" --arg fstype "$fstype" --arg opts "$options" \
                              '{device: $device, mountpoint: $mount, filesystem: $fstype, options: $opts}')
            mount_points=$(echo "$mount_points" | jq ". + [$mount_info]")
        fi
    done < <(cat /proc/mounts | grep -E "^/dev")
    
    # Docker volumes
    local docker_volumes='[]'
    if command -v docker &>/dev/null && docker info &>/dev/null; then
        while IFS= read -r line; do
            local vol_name=$(echo "$line" | awk '{print $1}')
            local vol_driver=$(echo "$line" | awk '{print $2}')
            local vol_mountpoint=$(docker volume inspect "$vol_name" --format '{{.Mountpoint}}' 2>/dev/null || echo "unknown")
            local vol_size=$(du -sh "$vol_mountpoint" 2>/dev/null | awk '{print $1}' || echo "unknown")
            
            local vol_info=$(jq -n --arg name "$vol_name" --arg driver "$vol_driver" --arg mount "$vol_mountpoint" --arg size "$vol_size" \
                            '{name: $name, driver: $driver, mountpoint: $mount, size: $size}')
            docker_volumes=$(echo "$docker_volumes" | jq ". + [$vol_info]")
        done < <(docker volume ls --format "{{.Name}} {{.Driver}}" 2>/dev/null || echo "")
    fi
    
    storage_info=$(echo "$storage_info" | jq --argjson filesystems "$filesystems" \
                                           --argjson disk_usage "$disk_usage" \
                                           --argjson mount_points "$mount_points" \
                                           --argjson docker_volumes "$docker_volumes" \
                  '.filesystems = $filesystems | .disk_usage = $disk_usage | .mount_points = $mount_points | .docker_volumes = $docker_volumes')
    
    jq --argjson storage "$storage_info" '.storage = $storage' "$REPORT_FILE" > "${REPORT_FILE}.tmp" && mv "${REPORT_FILE}.tmp" "$REPORT_FILE"
}

collect_performance_baseline() {
    echo "📊 Collecting performance baseline..."
    
    local performance_info=$(cat << 'EOF'
{
  "load_average": {},
  "cpu_usage": {},
  "memory_usage": {},
  "disk_io": {},
  "network_stats": {}
}
EOF
    )
    
    # Load average
    local load_avg='{}' 
    if [[ -f /proc/loadavg ]]; then
        local load_data=$(cat /proc/loadavg)
        local load_1min=$(echo "$load_data" | awk '{print $1}')
        local load_5min=$(echo "$load_data" | awk '{print $2}')
        local load_15min=$(echo "$load_data" | awk '{print $3}')
        load_avg=$(jq -n --arg l1 "$load_1min" --arg l5 "$load_5min" --arg l15 "$load_15min" \
                  '{one_minute: $l1, five_minute: $l5, fifteen_minute: $l15}')
    fi
    
    # CPU usage snapshot
    local cpu_usage='{}'
    if [[ -f /proc/stat ]]; then
        local cpu_line=$(grep "^cpu " /proc/stat)
        cpu_usage=$(echo "$cpu_line" | awk '{print "{\"user\":" $2 ",\"nice\":" $3 ",\"system\":" $4 ",\"idle\":" $5 ",\"iowait\":" $6 "}"}' | jq .)
    fi
    
    # Memory usage
    local memory_usage='{}'
    if [[ -f /proc/meminfo ]]; then
        local mem_total=$(grep "MemTotal" /proc/meminfo | awk '{print $2}')
        local mem_free=$(grep "MemFree" /proc/meminfo | awk '{print $2}')
        local mem_available=$(grep "MemAvailable" /proc/meminfo | awk '{print $2}')
        local mem_used=$((mem_total - mem_free))
        memory_usage=$(jq -n --argjson total "$mem_total" --argjson free "$mem_free" --argjson available "$mem_available" --argjson used "$mem_used" \
                      '{total_kb: $total, free_kb: $free, available_kb: $available, used_kb: $used}')
    fi
    
    # Disk I/O stats
    local disk_io='[]'
    if [[ -f /proc/diskstats ]]; then
        while IFS= read -r line; do
            if [[ "$line" =~ ^[[:space:]]*[0-9]+[[:space:]]+[0-9]+[[:space:]]+([a-z]+)[[:space:]]+([0-9]+)[[:space:]]+[0-9]+[[:space:]]+([0-9]+)[[:space:]]+[0-9]+[[:space:]]+([0-9]+)[[:space:]]+[0-9]+[[:space:]]+([0-9]+) ]]; then
                local device="${BASH_REMATCH[1]}"
                local reads="${BASH_REMATCH[2]}"
                local read_sectors="${BASH_REMATCH[3]}"
                local writes="${BASH_REMATCH[4]}"
                local write_sectors="${BASH_REMATCH[5]}"
                
                # Only include main devices, not partitions
                if [[ ! "$device" =~ [0-9]+$ ]]; then
                    local io_info=$(jq -n --arg dev "$device" --arg reads "$reads" --arg read_sectors "$read_sectors" --arg writes "$writes" --arg write_sectors "$write_sectors" \
                                   '{device: $dev, reads: $reads, read_sectors: $read_sectors, writes: $writes, write_sectors: $write_sectors}')
                    disk_io=$(echo "$disk_io" | jq ". + [$io_info]")
                fi
            fi
        done < <(cat /proc/diskstats)
    fi
    
    # Network stats
    local network_stats='[]'
    if [[ -f /proc/net/dev ]]; then
        while IFS= read -r line; do
            if [[ "$line" =~ ^[[:space:]]*([^:]+):[[:space:]]*([0-9]+)[[:space:]]+[0-9]+[[:space:]]+[0-9]+[[:space:]]+[0-9]+[[:space:]]+[0-9]+[[:space:]]+[0-9]+[[:space:]]+[0-9]+[[:space:]]+[0-9]+[[:space:]]+([0-9]+) ]]; then
                local interface="${BASH_REMATCH[1]}"
                local rx_bytes="${BASH_REMATCH[2]}"
                local tx_bytes="${BASH_REMATCH[3]}"
                
                # Skip loopback
                if [[ "$interface" != "lo" ]]; then
                    local net_info=$(jq -n --arg iface "$interface" --arg rx "$rx_bytes" --arg tx "$tx_bytes" \
                                    '{interface: $iface, rx_bytes: $rx, tx_bytes: $tx}')
                    network_stats=$(echo "$network_stats" | jq ". + [$net_info]")
                fi
            fi
        done < <(tail -n +3 /proc/net/dev)
    fi
    
    performance_info=$(echo "$performance_info" | jq --argjson load "$load_avg" \
                                                    --argjson cpu "$cpu_usage" \
                                                    --argjson memory "$memory_usage" \
                                                    --argjson disk_io "$disk_io" \
                                                    --argjson network "$network_stats" \
                      '.load_average = $load | .cpu_usage = $cpu | .memory_usage = $memory | .disk_io = $disk_io | .network_stats = $network')
    
    jq --argjson performance "$performance_info" '.performance = $performance' "$REPORT_FILE" > "${REPORT_FILE}.tmp" && mv "${REPORT_FILE}.tmp" "$REPORT_FILE"
}

generate_summary() {
    echo ""
    echo "📋 SYSTEM DISCOVERY SUMMARY"
    echo "=========================="
    
    # Extract key information for summary
    local hostname=$(jq -r '.discovery_metadata.hostname' "$REPORT_FILE")
    local cpu_model=$(jq -r '.hardware.cpu.model' "$REPORT_FILE")
    local memory_gb=$(jq -r '.hardware.memory.total_gb' "$REPORT_FILE")
    local os_distro=$(jq -r '.operating_system.distribution' "$REPORT_FILE")
    local storage_count=$(jq '.hardware.storage | length' "$REPORT_FILE")
    local network_interfaces=$(jq '.network.interfaces | length' "$REPORT_FILE")
    local docker_containers=$(jq '.network.docker_networks | length' "$REPORT_FILE")
    
    echo "Hostname: $hostname"
    echo "CPU: $cpu_model"
    echo "Memory: ${memory_gb}GB"
    echo "OS: $os_distro"
    echo "Storage Devices: $storage_count"
    echo "Network Interfaces: $network_interfaces"
    echo "Docker Networks: $docker_containers"
    echo ""
    echo "Full report: $REPORT_FILE"
    echo "Next: Run service_inventory_collector.sh"
}

# Execute main function
main "$@"