#!/bin/sh
# clawdie-hw-probe — single-shot JSON hardware probe
#
# Outputs one JSON object to stdout. All messages/errors go to stderr.
# Designed to run as root for full hardware access.
# Target: < 3 seconds on cold cache.
#
# Schema: clawdie.hw-probe.v1

set -u
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
export PATH

########################################################################
# helpers
########################################################################

die() {
    printf '{"error":"%s","schema_version":"clawdie.hw-probe.v1"}\n' "$(json_escape "$1")"
    exit 1
}

json_escape() {
    printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g; s/\n/\\n/g; s/\r//g; s/\t/\\t/g'
}

json_bool() {
    if [ "$1" = "yes" ] || [ "$1" = "YES" ] || [ "$1" = "true" ] || [ "$1" = "TRUE" ] || [ "$1" = "1" ]; then
        printf 'true'
    else
        printf 'false'
    fi
}

########################################################################
# field collectors — each function prints to a temp file or variable
########################################################################

collect_hostname() {
    hostname 2>/dev/null || echo "unknown"
}

collect_freebsd_version() {
    # kernel, userland, runtime
    _k="$(freebsd-version -k 2>/dev/null || echo "unknown")"
    _u="$(freebsd-version -u 2>/dev/null || echo "unknown")"
    _r="$(freebsd-version -r 2>/dev/null || echo "unknown")"
    printf '{"kernel":"%s","userland":"%s","runtime":"%s"}' \
        "$(json_escape "$_k")" \
        "$(json_escape "$_u")" \
        "$(json_escape "$_r")"
}

collect_ram_gb() {
    _bytes="$(sysctl -n hw.physmem 2>/dev/null || echo 0)"
    # Round to nearest GB: add 0.5 GB then truncate
    _half_gb=536870912  # 2^29
    _gb=$(( ( _bytes + _half_gb ) / 1073741824 ))
    printf '%d' "$_gb"
}

collect_cpu_model() {
    sysctl -n hw.model 2>/dev/null || echo "unknown"
}

collect_cpu_cores() {
    sysctl -n hw.ncpu 2>/dev/null || echo "0"
}

collect_gpu() {
    # Extract VGA/display devices from pciconf -lv
    # Output: JSON array of {vendor, device, chip}
    _found=0
    printf '['
    pciconf -lv 2>/dev/null | awk '
    BEGIN { in_block=0; vendor=""; device=""; chip=""; first=1 }
    /^[a-z]/ {
        if (in_block && device ~ /VGA|display/) {
            if (!first) printf ",";
            gsub(/"/, "\\\"", vendor);
            gsub(/"/, "\\\"", device);
            gsub(/"/, "\\\"", chip);
            printf "{\"vendor\":\"%s\",\"device\":\"%s\",\"chip\":\"%s\"}", vendor, device, chip;
            first=0;
        }
        in_block=1; vendor=""; device=""; chip=$0;
        sub(/@.*/, "", chip);
    }
    /vendor *=/ { vendor=$0; sub(/^[[:space:]]*vendor[[:space:]]*=[[:space:]]*/, "", vendor); sub(/'\''/, "", vendor); sub(/'\''$/, "", vendor) }
    /device *=/ { device=$0; sub(/^[[:space:]]*device[[:space:]]*=[[:space:]]*/, "", device); sub(/'\''/, "", device); sub(/'\''$/, "", device) }
    END {
        if (in_block && device ~ /VGA|display/) {
            if (!first) printf ",";
            gsub(/"/, "\\\"", vendor);
            gsub(/"/, "\\\"", device);
            gsub(/"/, "\\\"", chip);
            printf "{\"vendor\":\"%s\",\"device\":\"%s\",\"chip\":\"%s\"}", vendor, device, chip;
        }
    }
    '
    printf ']'
}

collect_gpu_driver() {
    # Check which KMS drivers are loaded
    printf '['
    _first=1
    for _drv in amdgpu i915kms nvidia-modeset; do
        if kldstat -q -n "$_drv" 2>/dev/null; then
            if [ "$_first" -ne 1 ]; then printf ','; fi
            printf '"%s"' "$_drv"
            _first=0
        fi
    done
    printf ']'
}

collect_disks() {
    # Disk names + sizes
    printf '{'
    _disks="$(sysctl -n kern.disks 2>/dev/null || true)"
    _first=1
    for _disk in $_disks; do
        _size=""
        # Try geom disk list first (fast, no device open)
        if command -v geom >/dev/null 2>&1; then
            _size="$(geom disk list "$_disk" 2>/dev/null | awk '/Mediasize:/ {print $2}' | head -1)"
        fi
        # Fallback to camcontrol
        if [ -z "$_size" ] && command -v camcontrol >/dev/null 2>&1; then
            _size="$(camcontrol identify "/dev/${_disk}" 2>/dev/null | awk -F'[<>]' '/LBA48 supported|LBA supported/{print $2}' | head -1)"
        fi
        # Fallback to diskinfo
        if [ -z "$_size" ] && command -v diskinfo >/dev/null 2>&1; then
            _size="$(diskinfo "/dev/${_disk}" 2>/dev/null | awk '{print $3}' | head -1)"
        fi
        if [ "$_first" -ne 1 ]; then printf ','; fi
        printf '"%s":%s' "$(json_escape "$_disk")" "${_size:-0}"
        _first=0
    done
    printf '}'
}

collect_network_interfaces() {
    # Simpler, reliable version that doesn't rely on subshell variable leakage
    _ifaces="$(ifconfig -l 2>/dev/null)"
    printf '{'
    _first=1
    for _iface in $_ifaces; do
        _mac="$(ifconfig "$_iface" 2>/dev/null | awk '/ether/{print $2}' | head -1)"
        [ -z "$_mac" ] && continue
        if [ "$_first" -ne 1 ]; then printf ','; fi
        printf '"%s":"%s"' "$(json_escape "$_iface")" "$(json_escape "$_mac")"
        _first=0
    done
    printf '}'
}

collect_wifi() {
    _wlan="$(sysctl -n net.wlan.devices 2>/dev/null || true)"
    if [ -n "$_wlan" ]; then
        printf '['
        _first=1
        for _dev in $_wlan; do
            if [ "$_first" -ne 1 ]; then printf ','; fi
            printf '"%s"' "$(json_escape "$_dev")"
            _first=0
        done
        printf ']'
    else
        printf '[]'
    fi
}

collect_usb_devices() {
    if command -v usbconfig >/dev/null 2>&1; then
        printf '%d' "$(usbconfig 2>/dev/null | grep -c '^ugen' || echo 0)"
    else
        printf '0'
    fi
}

collect_xorg_running() {
    if pgrep -x Xorg >/dev/null 2>&1; then
        printf 'true'
    else
        printf 'false'
    fi
}

collect_vulkan_support() {
    if command -v vulkaninfo >/dev/null 2>&1 && \
       vulkaninfo --summary >/dev/null 2>&1; then
        printf 'true'
    else
        printf 'false'
    fi
}

collect_boot_type() {
    if [ -e /dev/da0 ]; then
        printf '"usb"'
    elif [ -e /dev/nda0 ] || [ -e /dev/nvd0 ]; then
        printf '"nvme"'
    elif [ -e /dev/ada0 ]; then
        printf '"sata"'
    else
        printf '"unknown"'
    fi
}

collect_colibri_running() {
    if command -v service >/dev/null 2>&1; then
        # service status can hang on rc.d scripts; enforce a 1s deadline
        _status="$(timeout 1 service colibri_daemon status 2>/dev/null || echo "not_running")"
        case "$_status" in
            *"is running"*|*"Running"*)
                printf 'true'
                ;;
            *)
                printf 'false'
                ;;
        esac
    else
        printf 'false'
    fi
}

collect_zfs_pools() {
    if command -v zpool >/dev/null 2>&1; then
        _pools="$(zpool list -H -o name 2>/dev/null || true)"
        if [ -n "$_pools" ]; then
            printf '['
            _first=1
            for _pool in $_pools; do
                if [ "$_first" -ne 1 ]; then printf ','; fi
                printf '"%s"' "$(json_escape "$_pool")"
                _first=0
            done
            printf ']'
            return
        fi
    fi
    printf '[]'
}

########################################################################
# assemble JSON
########################################################################

_hostname="$(collect_hostname)"
_freebsd_ver="$(collect_freebsd_version)"
_ram_gb="$(collect_ram_gb)"
_cpu_model="$(collect_cpu_model)"
_cpu_cores="$(collect_cpu_cores)"
_gpu="$(collect_gpu)"
_gpu_driver="$(collect_gpu_driver)"
_disks="$(collect_disks)"
_net="$(collect_network_interfaces)"
_wifi="$(collect_wifi)"
_usb="$(collect_usb_devices)"
_xorg="$(collect_xorg_running)"
_vulkan="$(collect_vulkan_support)"
_boot_type="$(collect_boot_type)"
_colibri="$(collect_colibri_running)"
_zfs_pools="$(collect_zfs_pools)"

printf '{\n'
printf '  "schema_version":"clawdie.hw-probe.v1",\n'
printf '  "hostname":"%s",\n'   "$(json_escape "$_hostname")"
printf '  "freebsd_version":%s,\n' "$_freebsd_ver"
printf '  "ram_gb":%s,\n'         "$_ram_gb"
printf '  "cpu_model":"%s",\n'    "$(json_escape "$_cpu_model")"
printf '  "cpu_cores":%s,\n'      "$_cpu_cores"
printf '  "gpu":%s,\n'            "$_gpu"
printf '  "gpu_driver":%s,\n'     "$_gpu_driver"
printf '  "disks":%s,\n'          "$_disks"
printf '  "network_interfaces":%s,\n' "$_net"
printf '  "wifi":%s,\n'           "$_wifi"
printf '  "usb_devices":%s,\n'    "$_usb"
printf '  "xorg_running":%s,\n'   "$_xorg"
printf '  "vulkan_support":%s,\n' "$_vulkan"
printf '  "boot_type":%s,\n'      "$_boot_type"
printf '  "colibri_running":%s,\n' "$_colibri"
printf '  "zfs_pools":%s\n'       "$_zfs_pools"
printf '}\n'

exit 0
