#!/usr/bin/env bash

#
# Linux BenchTools - Client Benchmark Script
# Version: 1.3.0
#
# Collecte les informations hardware, exécute les benchmarks
# puis envoie les résultats au serveur backend (payload JSON).
#

set -e

#=========================================================
#  Version / variables globales
#=========================================================

BENCH_SCRIPT_VERSION="1.3.2"

# Couleurs
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

TOTAL_STEPS=8
CURRENT_STEP=0

# Paramètres réseau / API (peuvent être surchargés par variables d'environnement)
SERVER_URL="${SERVER_URL:-10.0.0.50:8007}"  # ajouter le port ou le schéma dans send_benchmark_payload si besoin
API_TOKEN="${API_TOKEN:-29855796dacf5cfe75ff9b02d6adf3dd0f9c52db5b53e7abfb4c0df7ece1be0a}"
IPERF_SERVER="${IPERF_SERVER:-10.0.0.50}"

# Mode DEBUG
# Mettre à 1 pour afficher le payload JSON complet avant envoi
# Mettre à 0 pour désactiver le debug
DEBUG_PAYLOAD="${DEBUG_PAYLOAD:-0}"  # Par défaut: 0 (désactivé)
                                      # Peut être activé via: DEBUG_PAYLOAD=1 bash bench.sh

# Forcer locale en anglais pour parsing
export LC_ALL=C

# Ajouter /usr/sbin au PATH pour dmidecode, smartctl, ethtool
export PATH="/usr/sbin:/sbin:$PATH"

# Variables JSON globales
SYSTEM_INFO="null"
CPU_INFO="null"
RAM_INFO="null"
GPU_INFO="null"
MOTHERBOARD_INFO="null"
STORAGE_INFO="[]"
PARTITION_INFO="[]"
NETWORK_INFO="[]"
PCI_INFO="[]"
USB_INFO="[]"
NETWORK_SHARES_INFO="[]"
BENCHMARK_RESULTS="null"

SMARTCTL_CMD="sudo smartctl"
if command -v timeout &>/dev/null; then
    SMARTCTL_CMD="timeout 10s sudo smartctl"
fi

#=========================================================
#  Affichage / logs
#=========================================================

log_step() {
    CURRENT_STEP=$((CURRENT_STEP + 1))
    echo -e "${BLUE}[${CURRENT_STEP}/${TOTAL_STEPS}]${NC} $1"
}

log_info() {
    echo -e "  ${GREEN}✓${NC} $1"
}

log_warn() {
    echo -e "  ${YELLOW}⚠${NC} $1"
}

log_error() {
    echo -e "  ${RED}✗${NC} $1"
}

#=========================================================
#  sudo + dépendances
#=========================================================

check_sudo() {
    echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
    echo -e "  Linux BenchTools - Client Benchmark Script"
    echo -e "  Version ${BENCH_SCRIPT_VERSION}"
    echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
    echo ""
    echo "Ce script nécessite les permissions sudo pour :"
    echo "  - dmidecode (infos RAM, carte mère, BIOS)"
    echo "  - smartctl (infos disques)"
    echo "  - ethtool (vitesse réseau)"
    echo ""

    if ! sudo -v; then
        log_error "Permissions sudo requises. Relance le script avec sudo."
        exit 1
    fi

    # Garder sudo actif
    while true; do sudo -n true; sleep 50; kill -0 "$$" || exit; done 2>/dev/null &

    log_info "Permissions sudo OK"
    echo ""
}

check_dependencies() {
    local missing=()
    local bench_missing=()

    echo -e "${BLUE}Vérification des dépendances...${NC}"

    for tool in curl jq lscpu free lsblk ip bc smartctl; do
        command -v "$tool" &>/dev/null || missing+=("$tool")
    done

    for tool in sysbench fio iperf3; do
        command -v "$tool" &>/dev/null || bench_missing+=("$tool")
    done

    if [[ ${#missing[@]} -eq 0 && ${#bench_missing[@]} -eq 0 ]]; then
        log_info "Toutes les dépendances de base sont présentes"
        log_info "Vérification des dépendances terminée"
        echo ""
        return
    fi

    echo ""
    if [[ ${#missing[@]} -gt 0 ]]; then
        log_warn "Outils systèmes manquants: ${missing[*]}"
    fi
    if [[ ${#bench_missing[@]} -gt 0 ]]; then
        log_warn "Outils de benchmark manquants: ${bench_missing[*]}"
    fi

    if ! command -v apt-get &>/dev/null; then
        log_warn "apt-get non disponible, installation automatique impossible."
        log_info "Vérification des dépendances terminée"
        echo ""
        return
    fi

    declare -A pkg_map=(
        [curl]="curl"
        [jq]="jq"
        [lscpu]="util-linux"
        [free]="procps"
        [lsblk]="util-linux"
        [ip]="iproute2"
        [bc]="bc"
        [smartctl]="smartmontools"
        [sysbench]="sysbench"
        [fio]="fio"
        [iperf3]="iperf3"
    )

    local to_install=()
    for t in "${missing[@]}" "${bench_missing[@]}"; do
        [[ -n "${pkg_map[$t]}" ]] && to_install+=("${pkg_map[$t]}")
    done

    if [[ ${#to_install[@]} -gt 0 ]]; then
        mapfile -t to_install < <(printf '%s\n' "${to_install[@]}" | sort -u)
        echo -e "${BLUE}Installation automatique des paquets manquants...${NC}"
        echo "  Paquets: ${to_install[*]}"
        echo ""

        # Pre-configure iperf3 to not install daemon
        if printf '%s\n' "${to_install[@]}" | grep -q "^iperf3$"; then
            echo "► Configuration iperf3 (désactivation du daemon)..."
            echo "iperf3 iperf3/start_daemon boolean false" | sudo debconf-set-selections
        fi

        echo "► apt-get update..."
        if ! sudo apt-get update -qq 2>&1 | grep -v "Policy will reject signature"; then
            log_warn "Warning durant apt-get update (on continue)"
        fi
        echo "► apt-get install..."
        if sudo DEBIAN_FRONTEND=noninteractive apt-get install -y "${to_install[@]}"; then
            log_info "Installation des dépendances OK"
        else
            log_error "Échec de l'installation des dépendances"
            exit 1
        fi
    fi

    log_info "Vérification des dépendances terminée"
    echo ""
}

#=========================================================
#  Utilitaire bc robuste
#=========================================================

safe_bc() {
    local expr="$1"
    local out
    out=$(echo "$expr" | bc 2>/dev/null) || out="0"
    echo "$out"
}

bytes_to_gb() {
    local bytes="$1"
    if [[ -z "$bytes" || "$bytes" == "0" ]]; then
        echo ""
    else
        safe_bc "scale=2; $bytes / (1024*1024*1024)"
    fi
}

# Detect the current graphical session type (wayland/x11/tty)
detect_session_type() {
    local session_type="${XDG_SESSION_TYPE:-}"

    if [[ -z "$session_type" && -n "$USER" && $(command -v loginctl) ]]; then
        local session_id
        session_id=$(loginctl 2>/dev/null | awk -v user="$USER" '$3==user {print $1; exit}')
        if [[ -n "$session_id" ]]; then
            session_type=$(loginctl show-session "$session_id" -p Type 2>/dev/null | awk -F= '/Type=/ {print $2}')
        fi
    fi

    if [[ -z "$session_type" && -n "$WAYLAND_DISPLAY" ]]; then
        session_type="wayland"
    elif [[ -z "$session_type" && -n "$DISPLAY" ]]; then
        session_type="x11"
    fi

    echo "$session_type"
}

# Try to detect the primary screen resolution
detect_screen_resolution() {
    local resolution=""

    if command -v xrandr &>/dev/null && [[ -n "$DISPLAY" ]]; then
        resolution=$(xrandr --current 2>/dev/null | awk '/\*/ {print $1; exit}')
    fi

    if [[ -z "$resolution" && -n "$DISPLAY" ]] && command -v xdpyinfo &>/dev/null; then
        resolution=$(xdpyinfo 2>/dev/null | awk '/dimensions:/ {print $2; exit}')
    fi

    if [[ -z "$resolution" ]] && command -v swaymsg &>/dev/null; then
        resolution=$(swaymsg -t get_outputs 2>/dev/null | jq -r '
            [.[] | select(.active == true) | "\(.current_mode.width)x\(.current_mode.height)"] | .[0]'
        )
        [[ "$resolution" == "null" ]] && resolution=""
    fi

    echo "$resolution"
}

#=========================================================
#  Étape 1 : Système
#=========================================================

collect_system_info() {
    log_step "Collecte des informations système de base"

    local hostname os_name os_version kernel arch
    local session_type screen_resolution last_boot uptime_seconds
    local battery_percentage="" battery_status="" battery_health=""

    hostname=$(hostname)
    os_name=$(grep '^ID=' /etc/os-release | cut -d= -f2 | tr -d '"')
    os_version=$(grep '^VERSION=' /etc/os-release | cut -d= -f2 | tr -d '"')
    kernel=$(uname -r)
    arch=$(uname -m)
    session_type=$(detect_session_type)
    screen_resolution=$(detect_screen_resolution)

    if command -v who &>/dev/null; then
        last_boot=$(who -b 2>/dev/null | awk '{print $3 " " $4}')
    fi

    if [[ -r /proc/uptime ]]; then
        uptime_seconds=$(awk '{print int($1)}' /proc/uptime)
    fi

    for bat in /sys/class/power_supply/BAT*; do
        [[ -d "$bat" ]] || continue
        if [[ -f "$bat/present" ]]; then
            local present
            present=$(cat "$bat/present")
            [[ "$present" != "1" ]] && continue
        fi

        if [[ -z "$battery_percentage" && -f "$bat/capacity" ]]; then
            battery_percentage=$(cat "$bat/capacity" | tr -dc '0-9.')
        fi
        if [[ -z "$battery_status" && -f "$bat/status" ]]; then
            battery_status=$(cat "$bat/status")
        fi
        if [[ -z "$battery_health" ]]; then
            if [[ -f "$bat/health" ]]; then
                battery_health=$(cat "$bat/health")
            elif [[ -f "$bat/uevent" ]]; then
                battery_health=$(grep -m1 'POWER_SUPPLY_HEALTH' "$bat/uevent" 2>/dev/null | cut -d= -f2)
            fi
        fi
        break
    done

    SYSTEM_INFO=$(jq -n \
        --arg hostname "$hostname" \
        --arg os_name "$os_name" \
        --arg os_version "$os_version" \
        --arg kernel "$kernel" \
        --arg arch "$arch" \
        --arg session "$session_type" \
        --arg resolution "$screen_resolution" \
        --arg last_boot "$last_boot" \
        --arg uptime "$uptime_seconds" \
        --arg battery_pct "$battery_percentage" \
        --arg battery_status "$battery_status" \
        --arg battery_health "$battery_health" \
        '{
            hostname: $hostname,
            os: {
                name: $os_name,
                version: $os_version,
                kernel_version: $kernel,
                architecture: $arch,
                session_type: (if $session != "" then $session else null end),
                display_server: (if $session != "" then $session else null end),
                screen_resolution: (if $resolution != "" then $resolution else null end),
                last_boot_time: (if $last_boot != "" then $last_boot else null end),
                uptime_seconds: (if $uptime != "" then ($uptime | tonumber?) else null end),
                battery_percentage: (if $battery_pct != "" then ($battery_pct | tonumber?) else null end),
                battery_status: (if $battery_status != "" then $battery_status else null end),
                battery_health: (if $battery_health != "" then $battery_health else null end)
            }
        }')

    log_info "Hostname: $hostname"
    log_info "OS: $os_name $os_version"
    log_info "Kernel: $kernel"
    [[ -n "$session_type" ]] && log_info "Session: $session_type"
    [[ -n "$screen_resolution" ]] && log_info "Résolution écran: $screen_resolution"
    [[ -n "$last_boot" ]] && log_info "Dernier boot: $last_boot"
    if [[ -n "$battery_percentage" ]]; then
        log_info "Batterie: ${battery_percentage}% ${battery_status:+($battery_status)}"
    fi
    echo ""
}

#=========================================================
#  Étape 2 : CPU
#=========================================================

collect_cpu_info() {
    log_step "Collecte des informations CPU"

    local vendor model cores threads
    vendor=$(lscpu | awk -F: '/Vendor ID/ {gsub(/^[ \t]+/,"",$2); print $2}')
    model=$(lscpu | awk -F: '/Model name/ {gsub(/^[ \t]+/,"",$2); print $2}')

    # Calcul du nombre de cores physiques: Core(s) per socket × Socket(s)
    local cores_per_socket sockets
    cores_per_socket=$(lscpu | awk -F: '/Core\(s\) per socket/ {gsub(/^[ \t]+/,"",$2); gsub(/[^0-9]/,"",$2); print $2}')
    sockets=$(lscpu | awk -F: '/Socket\(s\)/ {gsub(/^[ \t]+/,"",$2); gsub(/[^0-9]/,"",$2); print $2}')

    # S'assurer que les valeurs sont des nombres valides
    [[ -z "$cores_per_socket" || "$cores_per_socket" == "0" ]] && cores_per_socket=1
    [[ -z "$sockets" || "$sockets" == "0" ]] && sockets=1

    cores=$((cores_per_socket * sockets))

    threads=$(nproc)

    local cpu_mhz cpu_max_mhz base_freq_ghz max_freq_ghz
    cpu_mhz=$(lscpu | awk -F: '/CPU MHz/ {gsub(/^[ \t]+/,"",$2); print $2}')
    cpu_max_mhz=$(lscpu | awk -F: '/CPU max MHz/ {gsub(/^[ \t]+/,"",$2); print $2}')

    base_freq_ghz="null"
    max_freq_ghz="null"
    if [[ -n "$cpu_mhz" ]]; then
        base_freq_ghz=$(safe_bc "scale=2; $cpu_mhz / 1000")
    fi
    if [[ -n "$cpu_max_mhz" ]]; then
        max_freq_ghz=$(safe_bc "scale=2; $cpu_max_mhz / 1000")
    fi

    local cache_l1_kb cache_l2_kb cache_l3_kb
    # L1 cache = L1d + L1i
    # Parser format: "384 KiB (12 instances)" ou "6 MiB (12 instances)"
    # Extraire le premier nombre + unité, ignorer "(X instances)"
    # Utilisation de sed pour extraire "nombre unité" puis awk pour convertir MiB en KB
    local cache_l1d cache_l1i
    cache_l1d=$(lscpu | grep 'L1d cache' | sed -n 's/.*:\s*\([0-9]\+\)\s*\(KiB\|MiB\).*/\1 \2/p' | awk '{if ($2 == "MiB") print $1*1024; else print $1}')
    cache_l1i=$(lscpu | grep 'L1i cache' | sed -n 's/.*:\s*\([0-9]\+\)\s*\(KiB\|MiB\).*/\1 \2/p' | awk '{if ($2 == "MiB") print $1*1024; else print $1}')
    cache_l1_kb=$((${cache_l1d:-0} + ${cache_l1i:-0}))

    cache_l2_kb=$(lscpu | grep 'L2 cache' | sed -n 's/.*:\s*\([0-9]\+\)\s*\(KiB\|MiB\).*/\1 \2/p' | awk '{if ($2 == "MiB") print $1*1024; else print $1}')
    cache_l3_kb=$(lscpu | grep 'L3 cache' | sed -n 's/.*:\s*\([0-9]\+\)\s*\(KiB\|MiB\).*/\1 \2/p' | awk '{if ($2 == "MiB") print $1*1024; else print $1}')

    local flags
    flags=$(lscpu | awk -F: '/Flags/ {gsub(/^[ \t]+/,"",$2); print $2}')
    local flags_array
    flags_array=$(printf '%s\n' $flags | jq -R . | jq -s .)

    # Important: on passe les numériques en --arg (string) puis on cast dans jq
    CPU_INFO=$(jq -n \
        --arg vendor "$vendor" \
        --arg model "$model" \
        --arg cores "$cores" \
        --arg threads "$threads" \
        --arg base_freq "$base_freq_ghz" \
        --arg max_freq "$max_freq_ghz" \
        --arg l1 "$cache_l1_kb" \
        --arg l2 "$cache_l2_kb" \
        --arg l3 "$cache_l3_kb" \
        --argjson flags "$flags_array" \
        '{
            vendor: $vendor,
            model: $model,
            cores: ($cores | tonumber? // 0),
            threads: ($threads | tonumber? // 0),
            base_freq_ghz: (if $base_freq == "null" or $base_freq == "" then null else ($base_freq | tonumber?) end),
            max_freq_ghz: (if $max_freq == "null" or $max_freq == "" then null else ($max_freq | tonumber?) end),
            cache_l1_kb: ($l1 | tonumber? // 0),
            cache_l2_kb: ($l2 | tonumber? // 0),
            cache_l3_kb: ($l3 | tonumber? // 0),
            flags: $flags
        }')

    log_info "CPU: $model"
    log_info "Cores: ${cores:-0}, Threads: ${threads:-0}"
    echo ""
}

#=========================================================
#  Étape 3 : RAM
#=========================================================

collect_ram_info() {
    log_step "Collecte des informations RAM"

    local mem_total_kb mem_used_kb mem_free_kb mem_shared_kb
    mem_total_kb=$(free -k | awk '/^Mem:/ {print $2}')
    mem_used_kb=$(free -k | awk '/^Mem:/ {print $3}')
    mem_free_kb=$(free -k | awk '/^Mem:/ {print $4}')
    mem_shared_kb=$(free -k | awk '/^Mem:/ {print $5}')

    local total_mb used_mb free_mb shared_mb
    total_mb=$((mem_total_kb / 1024))
    used_mb=$((mem_used_kb / 1024))
    free_mb=$((mem_free_kb / 1024))
    shared_mb=$((mem_shared_kb / 1024))

    local slots_total=0 slots_used=0 ecc=false
    local dimm_layout="[]"

    if command -v dmidecode &>/dev/null; then
        log_info "[DEBUG] Vérification dmidecode..."
        if sudo dmidecode -t 16 &>/dev/null; then
            log_info "[DEBUG] dmidecode trouvé: $(command -v dmidecode)"
        else
            log_warn "dmidecode accessible mais aucune info DMI"
        fi

        local slots
        slots=$(sudo dmidecode -t 16 2>/dev/null | awk -F: '/Number Of Devices/ {gsub(/^[ \t]+/,"",$2); print $2}' | head -1)
        [[ -n "$slots" ]] && slots_total="$slots"

        if sudo dmidecode -t memory 2>/dev/null | grep -q 'Error Correction Type'; then
            if sudo dmidecode -t memory 2>/dev/null | grep 'Error Correction Type' | grep -q 'None'; then
                ecc=false
            else
                ecc=true
            fi
        fi

        local dimm_data
        dimm_data=$(sudo dmidecode -t 17 | grep -E 'Locator:|Size:|Type:|Speed:|Manufacturer:' | \
            awk '
                /Locator:/ && !/Bank/ { slot=$2; gsub(/_/, "", slot) }
                /Size:/ && /[0-9]+ [GM]B/ {
                    size=$2;
                    if ($3 == "GB") size=size*1024;
                    if ($3 == "MB") size=size;
                }
                /Type:/ && !/Detail/ { type=$2 }
                /Speed:/ && /MHz/ { speed=$2 }
                /Manufacturer:/ {
                    manu=$2;
                    printf "%s;%s;%s;%s;%s\n", slot, size, type, speed, manu
                }')

        if [[ -n "$dimm_data" ]]; then
            while IFS=';' read -r slot size type speed manu; do
                [[ -z "$slot" ]] && continue
                slots_used=$((slots_used + 1))
                local entry
                entry=$(jq -n \
                    --arg slot "$slot" \
                    --arg size_mb "$size" \
                    --arg type "$type" \
                    --arg speed_mhz "$speed" \
                    --arg manu "$manu" \
                    '{
                        slot: $slot,
                        size_mb: ($size_mb | tonumber? // 0),
                        type: $type,
                        speed_mhz: ($speed_mhz | tonumber? // 0),
                        manufacturer: $manu
                    }')
                dimm_layout=$(echo "$dimm_layout" | jq --argjson e "$entry" '. + [$e]')
            done <<< "$dimm_data"
        fi
    else
        log_warn "dmidecode non disponible - layout RAM détaillé ignoré"
    fi

    RAM_INFO=$(jq -n \
        --arg total_mb "$total_mb" \
        --arg used_mb "$used_mb" \
        --arg free_mb "$free_mb" \
        --arg shared_mb "$shared_mb" \
        --arg slots_total "$slots_total" \
        --arg slots_used "$slots_used" \
        --arg ecc_str "$([[ "$ecc" = true ]] && echo true || echo false)" \
        --argjson layout "$dimm_layout" \
        '{
            total_mb: ($total_mb | tonumber? // 0),
            used_mb: ($used_mb | tonumber? // 0),
            free_mb: ($free_mb | tonumber? // 0),
            shared_mb: ($shared_mb | tonumber? // 0),
            slots_total: ($slots_total | tonumber? // 0),
            slots_used: ($slots_used | tonumber? // 0),
            ecc: (if $ecc_str == "true" then true else false end),
            layout: $layout
        }')

    log_info "RAM Total: ${total_mb}MB (Utilisée: ${used_mb}MB, Libre: ${free_mb}MB)"
    log_info "Slots: ${slots_used}/${slots_total}"
    echo ""
}

#=========================================================
#  Étape 4 : GPU / Carte mère
#=========================================================

collect_hardware_info() {
    log_step "Collecte GPU, Carte mère, BIOS"

    local gpu_vendor="null"
    local gpu_model="null"
    local gpu_vram="null"
    local gpu_driver="null"

    if command -v lspci &>/dev/null; then
        local gpu_line
        gpu_line=$(lspci | grep -iE 'vga|3d' | head -1)
        if [[ -n "$gpu_line" ]]; then
            gpu_model=$(echo "$gpu_line" | sed 's/.*: //')
            if echo "$gpu_line" | grep -qi 'nvidia'; then
                gpu_vendor="NVIDIA"
                # Essayer d'obtenir plus d'infos avec nvidia-smi
                if command -v nvidia-smi &>/dev/null; then
                    local nvidia_model nvidia_vram nvidia_driver
                    # Vérifier que nvidia-smi fonctionne avant d'extraire les infos
                    if nvidia-smi &>/dev/null; then
                        nvidia_model=$(nvidia-smi --query-gpu=name --format=csv,noheader 2>/dev/null | head -1 | tr -d '\n')
                        nvidia_vram=$(nvidia-smi --query-gpu=memory.total --format=csv,noheader,nounits 2>/dev/null | head -1 | tr -d '\n')
                        nvidia_driver=$(nvidia-smi --query-gpu=driver_version --format=csv,noheader 2>/dev/null | head -1 | tr -d '\n')

                        # Ne remplacer que si les valeurs sont non-vides et valides
                        if [[ -n "$nvidia_model" && ! "$nvidia_model" =~ (failed|error|Error) ]]; then
                            gpu_model="$nvidia_model"
                        fi
                        if [[ -n "$nvidia_vram" && "$nvidia_vram" =~ ^[0-9]+$ ]]; then
                            gpu_vram="$nvidia_vram"
                        fi
                        if [[ -n "$nvidia_driver" && ! "$nvidia_driver" =~ (failed|error|Error) ]]; then
                            gpu_driver="$nvidia_driver"
                        fi
                    fi
                fi
            elif echo "$gpu_line" | grep -qi 'amd'; then
                gpu_vendor="AMD"
            elif echo "$gpu_line" | grep -qi 'intel'; then
                gpu_vendor="Intel"
            else
                gpu_vendor="Unknown"
            fi
        fi
    fi

    GPU_INFO=$(jq -n \
        --arg vendor "$gpu_vendor" \
        --arg model "$gpu_model" \
        --arg vram "$gpu_vram" \
        --arg driver "$gpu_driver" \
        '{
            vendor: $vendor,
            model: $model,
            memory_dedicated_mb: (if $vram == "null" or $vram == "" then null else ($vram | tonumber?) end),
            driver_version: (if $driver == "null" or $driver == "" then null else $driver end)
        }')

    if [[ "$gpu_model" != "null" ]]; then
        if [[ "$gpu_vram" != "null" ]]; then
            log_info "GPU: $gpu_model (${gpu_vram}MB VRAM)"
        else
            log_info "GPU: $gpu_model"
        fi
    else
        log_warn "GPU non détecté"
    fi

    local mb_manufacturer="Unknown" mb_model="Unknown"
    local bios_vendor="Unknown" bios_version="Unknown" bios_date="Unknown"

    if command -v dmidecode &>/dev/null; then
        mb_manufacturer=$(sudo dmidecode -s baseboard-manufacturer 2>/dev/null || echo "Unknown")
        mb_model=$(sudo dmidecode -s baseboard-product-name 2>/dev/null || echo "Unknown")
        bios_vendor=$(sudo dmidecode -s bios-vendor 2>/dev/null || echo "Unknown")
        bios_version=$(sudo dmidecode -s bios-version 2>/dev/null || echo "Unknown")
        bios_date=$(sudo dmidecode -s bios-release-date 2>/dev/null || echo "Unknown")
    fi

    MOTHERBOARD_INFO=$(jq -n \
        --arg manu "$mb_manufacturer" \
        --arg model "$mb_model" \
        --arg bvend "$bios_vendor" \
        --arg bver "$bios_version" \
        --arg bdate "$bios_date" \
        '{
            manufacturer: $manu,
            model: $model,
            bios_vendor: $bvend,
            bios_version: $bver,
            bios_date: $bdate
        }')

    log_info "Motherboard: $mb_manufacturer $mb_model"
    log_info "BIOS: $bios_version ($bios_date)"
    echo ""

    collect_pci_devices
    collect_usb_devices
}

collect_pci_devices() {
    PCI_INFO="[]"

    if ! command -v lspci &>/dev/null; then
        log_warn "lspci non disponible - périphériques PCI ignorés"
        return
    fi

    local count=0
    while IFS= read -r line; do
        [[ -z "$line" ]] && continue

        local slot class vendor device
        slot=$(echo "$line" | awk '{print $1}')
        class=$(echo "$line" | cut -d'"' -f2)
        vendor=$(echo "$line" | cut -d'"' -f4)
        device=$(echo "$line" | cut -d'"' -f6)

        [[ -z "$slot" ]] && continue

        local entry
        entry=$(jq -n \
            --arg slot "$slot" \
            --arg class "$class" \
            --arg vendor "$vendor" \
            --arg device "$device" \
            '{slot: $slot, class: $class, vendor: $vendor, device: $device}')

        PCI_INFO=$(echo "$PCI_INFO" | jq --argjson e "$entry" '. + [$e]')
        count=$((count + 1))
    done < <(lspci -mm 2>/dev/null || true)

    log_info "Périphériques PCI détectés: $count"
}

collect_usb_devices() {
    USB_INFO="[]"

    if ! command -v lsusb &>/dev/null; then
        log_warn "lsusb non disponible - périphériques USB ignorés"
        return
    fi

    local count=0
    while IFS= read -r line; do
        [[ -z "$line" ]] && continue
        if [[ "$line" =~ Bus[[:space:]]([0-9]+)[[:space:]]Device[[:space:]]([0-9]+):[[:space:]]ID[[:space:]]([0-9a-fA-F]+):([0-9a-fA-F]+)[[:space:]](.*)$ ]]; then
            local bus="${BASH_REMATCH[1]}"
            local dev="${BASH_REMATCH[2]}"
            local vendor_id="${BASH_REMATCH[3]}"
            local product_id="${BASH_REMATCH[4]}"
            local name="${BASH_REMATCH[5]}"

            local entry
            entry=$(jq -n \
                --arg bus "$bus" \
                --arg device "$dev" \
                --arg vendor_id "$vendor_id" \
                --arg product_id "$product_id" \
                --arg name "$name" \
                '{bus: $bus, device: $device, vendor_id: $vendor_id, product_id: $product_id, name: $name}')

            USB_INFO=$(echo "$USB_INFO" | jq --argjson e "$entry" '. + [$e]')
            count=$((count + 1))
        fi
    done < <(lsusb 2>/dev/null || true)

    log_info "Périphériques USB détectés: $count"
}

#=========================================================
#  Étape 5 : Stockage
#=========================================================

collect_storage_info() {
    log_step "Collecte des informations de stockage"

    local storage_array="[]"
    local disks
    disks=$(lsblk -d -n -o NAME,TYPE | awk '$2=="disk"{print $1}')

    for d in $disks; do
        local size_h size_gb rota tran
        size_h=$(lsblk -d -n -o SIZE "/dev/$d")
        rota=$(lsblk -d -n -o ROTA "/dev/$d")
        tran=$(lsblk -d -n -o TRAN "/dev/$d")

        local disk_type="unknown"
        [[ "$rota" = "0" ]] && disk_type="ssd" || disk_type="hdd"

        local interface="unknown"
        [[ -n "$tran" ]] && interface="$tran"

        local model="Unknown" serial="Unknown" temperature="null" smart_health="null"
        if command -v smartctl &>/dev/null; then
            log_info "→ SMART (/dev/$d): collecte en cours (timeout 10s)"
            if $SMARTCTL_CMD -i "/dev/$d" &>/dev/null; then
                local s
                s=$($SMARTCTL_CMD -i "/dev/$d" 2>/dev/null)
                model=$(echo "$s" | awk -F: '/Device Model|Model Number/ {gsub(/^[ \t]+/,"",$2); print $2}' | head -1)
                serial=$(echo "$s" | awk -F: '/Serial Number/ {gsub(/^[ \t]+/,"",$2); print $2}' | head -1)

                # Essayer de récupérer la température et le statut SMART
                local smart_all
                smart_all=$($SMARTCTL_CMD -A "/dev/$d" 2>/dev/null || true)
                # Température (diverses variantes selon le type de disque)
                # SATA/HDD: Temperature_Celsius, Airflow_Temperature_Cel, Current Drive Temperature
                # RAW_VALUE est après le "-" (colonne 8 dans la plupart des cas, mais peut varier)
                # On extrait tout après le "-" puis prend le premier nombre
                # NVMe: Temperature: XX Celsius (colonne 2)
                temperature=$(echo "$smart_all" | awk '/Temperature_Celsius|Airflow_Temperature_Cel|Current Drive Temperature/ {for(i=1;i<=NF;i++) if($i=="-" && i<NF) {print $(i+1); exit}}' | head -1 | grep -oE '^[0-9]+' | head -1)
                [[ -z "$temperature" ]] && temperature=$(echo "$smart_all" | awk '/^Temperature:/ {print $2}' | head -1)
                [[ -z "$temperature" ]] && temperature="null"

                # Statut SMART health
                local health
                health=$($SMARTCTL_CMD -H "/dev/$d" 2>/dev/null | awk '/SMART overall-health|SMART Health Status/ {print $NF}' | head -1)
                [[ -n "$health" ]] && smart_health="$health" || smart_health="null"
            fi
        fi

        size_gb=$(echo "$size_h" | sed 's/[^0-9.]//g')

        local disk_json
        disk_json=$(jq -n \
            --arg device "$d" \
            --arg model "$model" \
            --arg size "$size_gb" \
            --arg type "$disk_type" \
            --arg interface "$interface" \
            --arg serial "$serial" \
            --arg temp "$temperature" \
            --arg health "$smart_health" \
            '{
                device: $device,
                model: $model,
                size_gb: $size,
                type: $type,
                interface: $interface,
                serial: $serial,
                temperature_c: (if $temp == "null" or $temp == "" then null else ($temp | tonumber?) end),
                smart_health: (if $health == "null" or $health == "" then null else $health end)
            }')

        storage_array=$(echo "$storage_array" | jq --argjson disk "$disk_json" '. + [$disk]')

        if [[ "$temperature" != "null" ]]; then
            log_info "Disque: /dev/$d - $model ($size_h, $disk_type, ${temperature}°C)"
        else
            log_info "Disque: /dev/$d - $model ($size_h, $disk_type)"
        fi
    done

    STORAGE_INFO="$storage_array"

    local partitions_json="[]"
    if command -v lsblk &>/dev/null; then
        log_info "→ Récupération des partitions via lsblk"
        local lsblk_payload=""
        local lsblk_fields="NAME,TYPE,MOUNTPOINT,SIZE,FSTYPE,FSUSED,FSAVAIL"
        local lsblk_cmd="lsblk -b -J -o"

        if command -v timeout &>/dev/null; then
            lsblk_cmd="timeout 10s lsblk -b -J -o"
        fi

        if ! lsblk_payload=$(bash -c "$lsblk_cmd \"$lsblk_fields\" 2>/dev/null"); then
            log_warn "lsblk avec colonnes étendues indisponible, tentative en mode simplifié"
            lsblk_fields="NAME,TYPE,MOUNTPOINT,SIZE,FSTYPE"
            lsblk_payload=$(bash -c "$lsblk_cmd \"$lsblk_fields\" 2>/dev/null" || true)
        fi

        if [[ -n "$lsblk_payload" ]]; then
            if ! partitions_json=$(echo "$lsblk_payload" | jq '
                def flatten(node):
                  [node]
                  + ( (node.children // []) | map(flatten(.)) | add );
                (.blockdevices // [])
                | map(flatten(.))
                | add
                | map(select(.type == "part"))
                | map({
                    name: (if .name then "/dev/" + .name else null end),
                    mount_point: (if (.mountpoint // "") == "" then null else .mountpoint end),
                    fs_type: (.fstype // null),
                    total_gb: (if (.size? // null) then (((.size | tonumber) / (1024*1024*1024)) * 100 | round / 100) else null end),
                    used_gb: (if (.fsused? // null) then (((.fsused | tonumber) / (1024*1024*1024)) * 100 | round / 100) else null end),
                    free_gb: (if (.fsavail? // null) then (((.fsavail | tonumber) / (1024*1024*1024)) * 100 | round / 100) else null end)
                })
            ' 2>/dev/null); then
                log_warn "Parsing JSON lsblk échoué"
                partitions_json="[]"
            fi
            [[ -z "$partitions_json" ]] && partitions_json="[]"
        else
            log_warn "Impossible d'obtenir la sortie lsblk (timeout ?)"
        fi
    fi

    PARTITION_INFO="$partitions_json"
    if [[ "$partitions_json" != "[]" ]]; then
        local partition_count
        partition_count=$(echo "$partitions_json" | jq 'length')
        log_info "Partitions détectées: $partition_count"
    fi

    collect_network_shares
    echo ""
}

collect_network_shares() {
    NETWORK_SHARES_INFO="[]"

    if [[ ! -r /proc/mounts ]]; then
        log_warn "Impossible de lire /proc/mounts - partages réseau ignorés"
        return
    fi

    local count=0
    while read -r raw_src raw_target raw_type raw_opts _; do
        local fstype
        fstype=$(printf '%b' "$raw_type" | tr '[:upper:]' '[:lower:]')
        case "$fstype" in
            nfs|nfs4|cifs|smbfs|fuse.cifs|fuse.smbfs|afp|afpfs|fuse.afpfs|sshfs|fuse.sshfs|ftpfs|curlftpfs|fuse.ftpfs|davfs|davfs2|fuse.webdavfs)
                ;;
            *)
                continue
                ;;
        esac

        local protocol="$fstype"
        case "$fstype" in
            cifs|smbfs|fuse.cifs|fuse.smbfs) protocol="smb" ;;
            nfs|nfs4) protocol="nfs" ;;
            afp|afpfs|fuse.afpfs) protocol="afp" ;;
            sshfs|fuse.sshfs) protocol="sshfs" ;;
            ftpfs|curlftpfs|fuse.ftpfs) protocol="ftp" ;;
            davfs|davfs2|fuse.webdavfs) protocol="webdav" ;;
        esac

        local source target options
        source=$(printf '%b' "$raw_src")
        target=$(printf '%b' "$raw_target")
        options=$(printf '%b' "$raw_opts")

        local total_gb="" used_gb="" free_gb=""
        if [[ -d "$target" ]]; then
            local df_line
            df_line=$(df -B1 "$target" 2>/dev/null | tail -1)
            if [[ -n "$df_line" ]]; then
                local total_bytes used_bytes free_bytes
                total_bytes=$(echo "$df_line" | awk '{print $2}')
                used_bytes=$(echo "$df_line" | awk '{print $3}')
                free_bytes=$(echo "$df_line" | awk '{print $4}')
                [[ -n "$total_bytes" ]] && total_gb=$(bytes_to_gb "$total_bytes")
                [[ -n "$used_bytes" ]] && used_gb=$(bytes_to_gb "$used_bytes")
                [[ -n "$free_bytes" ]] && free_gb=$(bytes_to_gb "$free_bytes")
            fi
        fi

        local entry
        entry=$(jq -n \
            --arg protocol "$protocol" \
            --arg source "$source" \
            --arg mount "$target" \
            --arg fs "$fstype" \
            --arg opts "$options" \
            --arg total "${total_gb:-}" \
            --arg used "${used_gb:-}" \
            --arg free "${free_gb:-}" \
            '{
                protocol: ( $protocol | select(. != "") ),
                source: $source,
                mount_point: $mount,
                fs_type: $fs,
                options: ( $opts | select(. != "") ),
                total_gb: ( if $total == "" then null else ($total | tonumber?) end ),
                used_gb: ( if $used == "" then null else ($used | tonumber?) end ),
                free_gb: ( if $free == "" then null else ($free | tonumber?) end )
            }')

        NETWORK_SHARES_INFO=$(echo "$NETWORK_SHARES_INFO" | jq --argjson e "$entry" '. + [$e]')
        count=$((count + 1))
    done < /proc/mounts

    if (( count > 0 )); then
        log_info "Partages réseau détectés: $count"
    else
        log_info "Aucun partage réseau monté"
    fi
}

#=========================================================
#  Étape 6 : Réseau
#=========================================================

collect_network_info() {
    log_step "Collecte des informations réseau"

    echo "  → Début de collect_network_info()"

    local network_array="[]"

    local iface
    echo "  → Début de la boucle sur les interfaces réseau"
    for iface in $(ip -br link show | awk '$2 ~ /UP|UNKNOWN/ {print $1}'); do
        # Filtrer les interfaces virtuelles (loopback, docker, bridges, veth, tap, fw*, vmbr)
        [[ "$iface" =~ ^(lo|docker|br-|veth|tap|fw|vmbr) ]] && continue

        local mac
        mac=$(ip link show "$iface" | awk '/link\/ether/ {print $2}')
        [[ -z "$mac" ]] && continue

        local type="unknown"
        if [[ "$iface" =~ ^(eth|enp|eno) ]]; then
            type="ethernet"
        elif [[ "$iface" =~ ^(wlan|wl) ]]; then
            type="wifi"
        fi

        local ip_addr
        ip_addr=$(ip -4 addr show "$iface" | awk '/inet /{print $2}' | cut -d/ -f1 | head -1)

        local speed="" wol_supported="" driver="" ssid=""
        if [[ "$type" = "ethernet" && -x /usr/sbin/ethtool ]]; then
            local e
            e=$(sudo ethtool "$iface" 2>/dev/null || true)
            local spd
            # Extraire seulement le nombre de la vitesse (enlever "Mb/s")
            spd=$(echo "$e" | awk -F: '/Speed:/ {gsub(/^[ \t]+/,"",$2); print $2}' | grep -oE '[0-9]+' | head -1)
            [[ -n "$spd" ]] && speed="$spd"
            local wol
            # Extraire Wake-on-LAN et nettoyer (enlever retours chariot)
            wol=$(echo "$e" | awk -F: '/Wake-on:/ {gsub(/^[ \t]+/,"",$2); print $2}' | tr -d '\n' | head -1)
            if [[ -n "$wol" && "$wol" != "d" ]]; then
                wol_supported="true"
            elif [[ -n "$wol" ]]; then
                wol_supported="false"
            fi
        fi

        if [[ "$type" = "wifi" ]] && command -v iw &>/dev/null; then
            local iw_info
            iw_info=$(iw dev "$iface" link 2>/dev/null || true)
            if echo "$iw_info" | grep -q "SSID"; then
                ssid=$(echo "$iw_info" | awk -F': ' '/SSID/ {print $2; exit}' | sed 's/[[:space:]]*$//')
            fi

            local bitrate_line
            bitrate_line=$(echo "$iw_info" | awk -F': ' '/tx bitrate/ {print $2; exit}' | xargs)
            if [[ -n "$bitrate_line" ]]; then
                local br_value=$(echo "$bitrate_line" | awk '{print $1}')
                local br_unit=$(echo "$bitrate_line" | awk '{print $2}')
                if [[ "$br_value" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then
                    if [[ "$br_unit" =~ ^MBit/s$ ]]; then
                        speed=$(printf "%.0f" "$br_value")
                    elif [[ "$br_unit" =~ ^GBit/s$ ]]; then
                        speed=$(awk "BEGIN {printf \"%.0f\", $br_value * 1000}")
                    fi
                fi
            fi
        fi

        if command -v ethtool &>/dev/null; then
            local ethtool_info
            ethtool_info=$(sudo ethtool -i "$iface" 2>/dev/null || true)
            if [[ -n "$ethtool_info" ]]; then
                driver=$(echo "$ethtool_info" | awk -F: '/driver:/ {gsub(/^[ \t]+/,"",$2); print $2; exit}')
            fi
        fi
        if [[ -z "$driver" && -L "/sys/class/net/$iface/device/driver" ]]; then
            driver=$(basename "$(readlink -f "/sys/class/net/$iface/device/driver")" 2>/dev/null)
        fi

        # Convertir wol_supported en booléen ou null pour jq
        local wol_json="null"
        if [[ "$wol_supported" == "true" ]]; then
            wol_json="true"
        elif [[ "$wol_supported" == "false" ]]; then
            wol_json="false"
        fi

        # Debug: afficher les variables avant la création du JSON
        echo "  → DEBUG Interface: $iface"
        echo "    type='$type' mac='$mac' ip='${ip_addr:-}' speed='$speed' wol='$wol_json'"

        local net_json jq_error
        net_json=$(jq -n \
            --arg name "$iface" \
            --arg type "$type" \
            --arg mac "$mac" \
            --arg ip "${ip_addr:-}" \
            --arg speed "$speed" \
            --arg driver "$driver" \
            --arg ssid "$ssid" \
            --argjson wol "$wol_json" \
            '{
                name: $name,
                type: $type,
                mac: $mac,
                ip_address: (if $ip != "" then $ip else null end),
                speed_mbps: (if $speed != "" then ($speed | tonumber) else null end),
                driver: (if $driver != "" then $driver else null end),
                ssid: (if $ssid != "" then $ssid else null end),
                wake_on_lan: $wol
            }' 2>&1)

        local jq_exit=$?
        if [[ $jq_exit -ne 0 ]]; then
            echo "    ✗ jq ERREUR (code: $jq_exit): $net_json"
            log_warn "Interface $iface: erreur jq, ignorée"
            continue
        fi

        echo "    net_json result: '$net_json'"

        # Vérifier que net_json est valide avant de l'ajouter
        if echo "$net_json" | jq empty 2>/dev/null; then
            echo "    ✓ JSON valide, ajout à network_array"
            network_array=$(echo "$network_array" | jq --argjson net "$net_json" '. + [$net]')
        else
            echo "    ✗ JSON invalide, interface ignorée"
            log_warn "Interface $iface: JSON invalide, ignorée"
        fi

        local log_line="Interface: $iface ($type) - IP: ${ip_addr:-N/A}"
        if [[ -n "$speed" ]]; then
            log_line+=" - Speed: ${speed}Mbps"
        fi
        if [[ "$type" = "wifi" && -n "$ssid" ]]; then
            log_line+=" - SSID: $ssid"
        fi
        log_info "$log_line"
    done

    NETWORK_INFO="$network_array"
    echo ""
}

#=========================================================
#  Étape 7 : Benchmarks
#=========================================================

run_benchmarks() {
    log_step "Exécution des benchmarks (peut prendre plusieurs minutes)"

    # CPU
    local cpu_bench="null"
    if command -v sysbench &>/dev/null; then
        log_info "Benchmark CPU en cours..."

        # Test single-core (1 thread)
        log_info "→ Test single-core (1 thread)..."
        local cpu_single
        cpu_single=$(sysbench cpu --cpu-max-prime=20000 --threads=1 run 2>&1 || true)
        local eps_single
        eps_single=$(echo "$cpu_single" | awk '/events per second/ {print $4}')
        [[ -z "$eps_single" ]] && eps_single=0
        local cpu_score_single
        cpu_score_single=$(safe_bc "scale=2; $eps_single")
        log_info "   Single-core: ${eps_single} events/sec (score: ${cpu_score_single})"

        # Test multi-core (tous les threads)
        log_info "→ Test multi-core ($(nproc) threads)..."
        local cpu_multi
        cpu_multi=$(sysbench cpu --cpu-max-prime=20000 --threads="$(nproc)" run 2>&1 || true)
        local eps_multi time_s
        eps_multi=$(echo "$cpu_multi" | awk '/events per second/ {print $4}')
        time_s=$(echo "$cpu_multi" | awk '/total time:/ {gsub(/s/,"",$3); print $3}')
        [[ -z "$eps_multi" ]] && eps_multi=0
        [[ -z "$time_s" ]] && time_s=0
        local cpu_score_multi
        cpu_score_multi=$(safe_bc "scale=2; $eps_multi")
        log_info "   Multi-core: ${eps_multi} events/sec (score: ${cpu_score_multi})"

        # Score global = moyenne des deux
        local cpu_score
        cpu_score=$(safe_bc "scale=2; ($cpu_score_single + $cpu_score_multi) / 2")

        cpu_bench=$(jq -n \
            --arg eps "$eps_multi" \
            --arg eps_single "$eps_single" \
            --arg eps_multi "$eps_multi" \
            --arg time "$time_s" \
            --arg score "$cpu_score" \
            --arg score_single "$cpu_score_single" \
            --arg score_multi "$cpu_score_multi" \
            '{
                events_per_sec: ($eps | tonumber? // 0),
                events_per_sec_single: ($eps_single | tonumber? // 0),
                events_per_sec_multi: ($eps_multi | tonumber? // 0),
                duration_s: ($time | tonumber? // 0),
                score: ($score | tonumber? // 0),
                score_single: ($score_single | tonumber? // 0),
                score_multi: ($score_multi | tonumber? // 0)
            }')

        log_info "CPU Global: score ${cpu_score} (single: ${cpu_score_single}, multi: ${cpu_score_multi})"
    else
        log_warn "sysbench non disponible - CPU bench ignoré"
    fi

    # Mémoire
    local mem_bench="null"
    if command -v sysbench &>/dev/null; then
        log_info "Benchmark Mémoire en cours..."
        local mem_res
        mem_res=$(sysbench memory --memory-total-size=1G run 2>&1 || true)
        local thr
        # Extraire le throughput - essayer plusieurs patterns
        thr=$(echo "$mem_res" | grep -oP '\d+\.\d+(?= MiB/sec)' | head -1)
        [[ -z "$thr" ]] && thr=$(echo "$mem_res" | awk '/transferred/ {print $(NF-1)}' | head -1)
        [[ -z "$thr" ]] && thr=0
        local mem_score
        mem_score=$(safe_bc "scale=2; $thr")

        mem_bench=$(jq -n \
            --arg thr "$thr" \
            --arg score "$mem_score" \
            '{
                throughput_mib_s: ($thr | tonumber? // 0),
                score: ($score | tonumber? // 0)
            }')

        log_info "Mémoire: ${thr} MiB/s (score: ${mem_score})"
    else
        log_warn "sysbench non disponible - Memory bench ignoré"
    fi

    # Disque
    local disk_bench="null"
    if command -v fio &>/dev/null; then
        log_info "Benchmark Disque en cours (2–3 minutes)..."
        local tmpdir
        tmpdir=$(mktemp -d /tmp/fio-bench-XXXX)
        local fio_res
        fio_res=$(fio --name=bench --ioengine=libaio --rw=randrw --bs=4k \
            --size=500M --numjobs=1 --direct=1 --runtime=30 --time_based \
            --filename="$tmpdir/testfile" --output-format=json 2>/dev/null || echo '{}')
        rm -rf "$tmpdir"

        if [[ "$fio_res" != "{}" ]]; then
            local rbw wbw riops wiops lat_ns
            rbw=$(echo "$fio_res" | jq '.jobs[0].read.bw // 0')
            wbw=$(echo "$fio_res" | jq '.jobs[0].write.bw // 0')
            riops=$(echo "$fio_res" | jq '.jobs[0].read.iops // 0' | cut -d. -f1)
            wiops=$(echo "$fio_res" | jq '.jobs[0].write.iops // 0' | cut -d. -f1)
            lat_ns=$(echo "$fio_res" | jq '.jobs[0].read.clat_ns.mean // 0')

            local rmb wmb lat_ms
            rmb=$(safe_bc "scale=2; $rbw / 1024")
            wmb=$(safe_bc "scale=2; $wbw / 1024")
            lat_ms=$(safe_bc "scale=3; $lat_ns / 1000000")

            local disk_score
            disk_score=$(safe_bc "scale=2; $rmb + $wmb")

            disk_bench=$(jq -n \
                --arg rmb "$rmb" \
                --arg wmb "$wmb" \
                --arg riops "$riops" \
                --arg wiops "$wiops" \
                --arg lat_ms "$lat_ms" \
                --arg score "$disk_score" \
                '{
                    read_mb_s: ($rmb | tonumber? // 0),
                    write_mb_s: ($wmb | tonumber? // 0),
                    iops_read: ($riops | tonumber? // 0),
                    iops_write: ($wiops | tonumber? // 0),
                    latency_ms: ($lat_ms | tonumber? // 0),
                    score: ($score | tonumber? // 0)
                }')

            log_info "Disque: R=${rmb}MB/s W=${wmb}MB/s (score: ${disk_score})"
        else
            log_warn "Échec du benchmark disque (fio)"
        fi
    else
        log_warn "fio non disponible - Disk bench ignoré"
    fi

    # Réseau (iperf3)
    local net_bench="null"
    if command -v iperf3 &>/dev/null; then
        local target="$IPERF_SERVER"
        [[ -z "$target" ]] && target="10.0.0.50"

        log_info "Benchmark Réseau en cours (vers $target)..."

        if ping -c 1 -W 1 "$target" &>/dev/null; then
            if nc -z "$target" 5201 &>/dev/null; then
                # Test bidirectionnel (upload + download simultanés)
                local bidir_result=$(iperf3 -c "$target" -t 10 --bidir -J 2>/dev/null || echo '{}')

                local upload_mbps="0"
                local download_mbps="0"
                local ping_ms="null"

                if [[ "$bidir_result" != "{}" ]]; then
                    # Extraire upload (end.sum_sent) et download (end.sum_received)
                    local upload_bps=$(echo "$bidir_result" | jq -r '.end.sum_sent.bits_per_second // 0' | tr -d '\n')
                    local download_bps=$(echo "$bidir_result" | jq -r '.end.sum_received.bits_per_second // 0' | tr -d '\n')

                    upload_mbps=$(safe_bc "scale=2; $upload_bps / 1000000" | tr -d '\n')
                    download_mbps=$(safe_bc "scale=2; $download_bps / 1000000" | tr -d '\n')

                    # Mesurer le ping
                    local ping_output=$(ping -c 5 "$target" 2>/dev/null | grep 'avg' || echo "")
                    if [[ -n "$ping_output" ]]; then
                        ping_ms=$(echo "$ping_output" | awk -F'/' '{print $5}' | tr -d '\n')
                    fi

                    # Score réseau
                    local net_score=$(safe_bc "scale=2; $upload_mbps + $download_mbps" | tr -d '\n')

                    # S'assurer que les valeurs sont valides pour jq
                    [[ -z "$upload_mbps" || "$upload_mbps" == "null" ]] && upload_mbps="0"
                    [[ -z "$download_mbps" || "$download_mbps" == "null" ]] && download_mbps="0"
                    [[ -z "$ping_ms" || "$ping_ms" == "null" ]] && ping_ms="0"
                    [[ -z "$net_score" || "$net_score" == "null" ]] && net_score="0"

                    net_bench=$(jq -n \
                        --argjson upload "$upload_mbps" \
                        --argjson download "$download_mbps" \
                        --argjson ping "$ping_ms" \
                        --argjson score "$net_score" \
                        '{upload_mbps: $upload, download_mbps: $download, ping_ms: $ping, score: $score}')

                    log_info "Réseau: ↑${upload_mbps}Mbps ↓${download_mbps}Mbps (ping: ${ping_ms}ms, score: ${net_score})"
                else
                    log_warn "Test iperf3 bidirectionnel échoué - Network bench ignoré"
                fi
            else
                log_warn "Port iperf3 (5201) fermé sur $target - Network bench ignoré"
            fi
        else
            log_warn "Hôte $target non joignable - Network bench ignoré"
        fi
    else
        log_warn "iperf3 non disponible - Network bench ignoré"
    fi

    # GPU bench non implémenté
    local gpu_bench="null"
    log_warn "GPU bench non implémenté - ignoré"

    # Score global selon pondérations :
    # CPU 40% (double du reste), RAM 20%, Disque 20%, Réseau 10%, GPU 10%
    local scores="" total_weight=0

    if [[ "$cpu_bench" != "null" ]]; then
        local cs
        cs=$(echo "$cpu_bench" | jq '.score // 0')
        scores="$scores + $cs * 0.40"
        total_weight=$(safe_bc "$total_weight + 0.40")
    fi

    if [[ "$mem_bench" != "null" ]]; then
        local ms
        ms=$(echo "$mem_bench" | jq '.score // 0')
        scores="$scores + $ms * 0.20"
        total_weight=$(safe_bc "$total_weight + 0.20")
    fi

    if [[ "$disk_bench" != "null" ]]; then
        local ds
        ds=$(echo "$disk_bench" | jq '.score // 0')
        scores="$scores + $ds * 0.20"
        total_weight=$(safe_bc "$total_weight + 0.20")
    fi

    if [[ "$net_bench" != "null" ]]; then
        local ns
        ns=$(echo "$net_bench" | jq '.score // 0')
        scores="$scores + $ns * 0.10"
        total_weight=$(safe_bc "$total_weight + 0.10")
    fi

    if [[ "$gpu_bench" != "null" ]]; then
        local gs
        gs=$(echo "$gpu_bench" | jq '.score // 0')
        scores="$scores + $gs * 0.10"
        total_weight=$(safe_bc "$total_weight + 0.10")
    fi

    scores=$(echo "$scores" | sed -E 's/^[[:space:]]*\+ //')
    local global_score=0
    if [[ -n "$scores" && "$total_weight" != "0" ]]; then
        global_score=$(safe_bc "scale=2; ($scores) / $total_weight")
    fi
    [[ -z "$global_score" ]] && global_score=0

    BENCHMARK_RESULTS=$(jq -n \
        --argjson cpu "$cpu_bench" \
        --argjson mem "$mem_bench" \
        --argjson disk "$disk_bench" \
        --argjson net "$net_bench" \
        --argjson gpu "$gpu_bench" \
        --argjson global "$global_score" \
        '{
            cpu: $cpu,
            memory: $mem,
            disk: $disk,
            network: $net,
            gpu: $gpu,
            global_score: $global
        }')

    log_info "Score global: ${global_score}/100"
    echo ""
}

#=========================================================
#  Envoi du payload JSON
#=========================================================

send_benchmark_payload() {
    log_step "Construction du payload JSON et envoi au serveur"

    # Si SERVER_URL n'a pas de schéma, on préfixe par http://
    local base_url="$SERVER_URL"
    if [[ "$base_url" != http*://* ]]; then
        base_url="http://$base_url"
    fi
    local api_url="${base_url%/}/api/benchmark"

    # Validation des variables JSON avant envoi (éviter les variables vides qui causent des erreurs jq)
    # Si DEBUG_PAYLOAD=1, on affiche les variables pour diagnostic
    if [[ "${DEBUG_PAYLOAD:-0}" == "1" ]]; then
        echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
        echo -e "${YELLOW}DEBUG: Validation des variables JSON${NC}"
        echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
        for var in SYSTEM_INFO CPU_INFO RAM_INFO GPU_INFO MOTHERBOARD_INFO STORAGE_INFO PARTITION_INFO NETWORK_INFO PCI_INFO USB_INFO NETWORK_SHARES_INFO BENCHMARK_RESULTS; do
            local val="${!var}"
            if [[ -z "$val" ]]; then
                echo -e "${RED}✗ $var est VIDE${NC}"
            elif echo "$val" | jq empty 2>/dev/null; then
                echo -e "${GREEN}✓ $var est JSON valide${NC} (${#val} chars)"
            else
                echo -e "${RED}✗ $var est JSON INVALIDE${NC}: ${val:0:100}..."
            fi
        done
        echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
        echo ""
    fi

    [[ -z "$SYSTEM_INFO" ]] && SYSTEM_INFO="null"
    [[ -z "$CPU_INFO" ]] && CPU_INFO="null"
    [[ -z "$RAM_INFO" ]] && RAM_INFO="null"
    [[ -z "$GPU_INFO" ]] && GPU_INFO="null"
    [[ -z "$MOTHERBOARD_INFO" ]] && MOTHERBOARD_INFO="null"
    [[ -z "$STORAGE_INFO" ]] && STORAGE_INFO="[]"
    [[ -z "$PARTITION_INFO" ]] && PARTITION_INFO="[]"
    [[ -z "$NETWORK_INFO" ]] && NETWORK_INFO="[]"
    [[ -z "$PCI_INFO" ]] && PCI_INFO="[]"
    [[ -z "$USB_INFO" ]] && USB_INFO="[]"
    [[ -z "$NETWORK_SHARES_INFO" ]] && NETWORK_SHARES_INFO="[]"
    [[ -z "$BENCHMARK_RESULTS" ]] && BENCHMARK_RESULTS="null"

    local payload
    payload=$(
        jq -n \
            --arg version "$BENCH_SCRIPT_VERSION" \
            --argjson system "$SYSTEM_INFO" \
            --argjson cpu "$CPU_INFO" \
            --argjson ram "$RAM_INFO" \
            --argjson gpu "$GPU_INFO" \
            --argjson mb "$MOTHERBOARD_INFO" \
            --argjson storage "$STORAGE_INFO" \
            --argjson partitions "$PARTITION_INFO" \
            --argjson network "$NETWORK_INFO" \
            --argjson pci "$PCI_INFO" \
            --argjson usb "$USB_INFO" \
            --argjson shares "$NETWORK_SHARES_INFO" \
            --argjson bench "$BENCHMARK_RESULTS" \
            '
{
  device_identifier: $system.hostname,
  bench_script_version: $version,

  hardware: {
    cpu: ($cpu + {
      microarchitecture: null,
      tdp_w: null
    }),

    ram: (
      $ram
      | {
          total_mb: .total_mb,
          used_mb: .used_mb,
          free_mb: .free_mb,
          shared_mb: .shared_mb,
          slots_total: .slots_total,
          slots_used: .slots_used,
          ecc: .ecc,
          layout: [
            .layout[]
            | {
                slot,
                size_mb,
                type,
                speed_mhz,
                vendor: .manufacturer,
                part_number: null
              }
          ]
        }
    ),

    gpu: (
      if $gpu == null then
        {
          vendor: null,
          model: null,
          driver_version: null,
          memory_dedicated_mb: null,
          memory_shared_mb: null,
          api_support: []
        }
      else
        {
          vendor: $gpu.vendor,
          model: $gpu.model,
          driver_version: null,
          memory_dedicated_mb: null,
          memory_shared_mb: null,
          api_support: []
        }
      end
    ),

    storage: {
      devices: [
        $storage[]
        | {
            name: ("/dev/" + .device),
            type: (.type | ascii_upcase),
            interface,
            capacity_gb: (.size_gb | tonumber? // .size_gb),
            vendor: null,
            model,
            smart_health,
            temperature_c
          }
      ],
      partitions: $partitions
    },

    pci_devices: $pci,
    usb_devices: $usb,
    network_shares: $shares,

    network: {
      interfaces: [
        $network[]
        | {
            name,
            type,
            mac,
            ip: .ip_address,
            speed_mbps,
            driver: (.driver // null),
            ssid: (.ssid // null),
            wake_on_lan
          }
      ]
    },

    motherboard: {
      vendor: $mb.manufacturer,
      model: $mb.model,
      bios_vendor: $mb.bios_vendor,
      bios_version: $mb.bios_version,
      bios_date: $mb.bios_date
    },

    os: (
      $system.os
      + {
          hostname: $system.hostname,
          virtualization_type: "none",
          desktop_environment: (.session_type // .display_server // null)
        }
    ),

    sensors: {
      cpu_temp_c: null,
      disk_temps_c: {}
    },

    raw_info: {
      lscpu: null,
      lsblk: null
    }
  },

  results: (
    $bench as $b
    | {
        cpu:    $b.cpu,
        memory: $b.memory,
        disk:   $b.disk,
        network: (
          $b.network
          + {
              jitter_ms: null,
              packet_loss_percent: null
            }
        ),
        gpu: (
          if $b.gpu == null then
            { glmark2_score: null, score: null }
          else
            $b.gpu
          end
        ),
        global_score: $b.global_score
      }
  )
}
'
    )

    # Debug : afficher le payload complet si DEBUG_PAYLOAD=1
    if [[ "${DEBUG_PAYLOAD:-0}" == "1" ]]; then
        echo ""
        echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
        echo -e "${YELLOW}DEBUG: Payload JSON complet${NC}"
        echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
        echo "$payload" | jq '.'
        echo -e "${BLUE}════════════════════════════════════════════════════════${NC}"
        echo ""

        # Sauvegarder le payload dans un fichier avec timestamp
        local debug_file="/tmp/bench_payload_$(date +%Y%m%d_%H%M%S).json"
        echo "$payload" | jq '.' > "$debug_file"
        echo -e "${GREEN}✓${NC} Payload sauvegardé dans: ${debug_file}"
        echo ""

        # Demander confirmation seulement si on a un terminal interactif
        if [[ -t 0 ]]; then
            read -p "Appuyez sur Entrée pour continuer l'envoi ou Ctrl+C pour annuler..."
        else
            log_warn "Mode non-interactif détecté - envoi automatique dans 2 secondes..."
            sleep 2
        fi
    fi

    log_info "Envoi du payload vers: $api_url"

    local tmp_resp http_code
    tmp_resp=$(mktemp /tmp/bench_response_XXXXXX.json)

    # IMPORTANT : on envoie le payload via stdin (pas de @<(...) foireux)
    http_code=$(
        printf '%s\n' "$payload" | curl -sS -o "$tmp_resp" -w "%{http_code}" \
            -X POST "$api_url" \
            -H "Content-Type: application/json" \
            -H "Authorization: Bearer $API_TOKEN" \
            --data-binary @-
    )

    if [[ "$http_code" != "200" && "$http_code" != "201" ]]; then
        log_error "Erreur lors de l'envoi (HTTP $http_code)"
        echo "Réponse serveur :"
        cat "$tmp_resp"
        rm -f "$tmp_resp"
        exit 1
    fi

    log_info "Payload envoyé avec succès (HTTP $http_code)"
    rm -f "$tmp_resp"
    echo ""
}

#=========================================================
#  MAIN
#=========================================================

parse_args() {
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --server)
                SERVER_URL="$2"
                shift 2
                ;;
            --token)
                API_TOKEN="$2"
                shift 2
                ;;
            --iperf-server)
                IPERF_SERVER="$2"
                shift 2
                ;;
            --debug)
                DEBUG_PAYLOAD=1
                shift
                ;;
            --help|-h)
                echo "Usage: $0 [OPTIONS]"
                echo ""
                echo "Options:"
                echo "  --server URL        Backend server URL (default: $SERVER_URL)"
                echo "  --token TOKEN       API authentication token"
                echo "  --iperf-server IP   iperf3 server IP (default: $IPERF_SERVER)"
                echo "  --debug             Enable debug mode (shows full JSON payload)"
                echo "  --help, -h          Show this help message"
                echo ""
                exit 0
                ;;
            *)
                log_warn "Option inconnue: $1 (ignorée)"
                shift
                ;;
        esac
    done
}

main() {
    parse_args "$@"
    check_sudo
    check_dependencies

    echo -e "${BLUE}Début de la collecte et des benchmarks...${NC}"
    echo ""

    collect_system_info
    collect_cpu_info
    collect_ram_info
    collect_hardware_info
    collect_storage_info
    collect_network_info
    run_benchmarks
    send_benchmark_payload

    echo -e "${GREEN}════════════════════════════════════════════════════════${NC}"
    echo -e "${GREEN}  Benchmark terminé avec succès !${NC}"
    echo -e "${GREEN}════════════════════════════════════════════════════════${NC}"
    echo ""
}

main "$@"
