#!/bin/sh

have() { command -v "$1" >/dev/null 2>&1; }

# Product/model name from SMBIOS via kenv, e.g. "HP ProBook 430 G7" - falls
# back to vendor+board, then hostname, if kenv has no usable product name.
get_model() {
    model=""
    if have kenv; then
        model=$(kenv smbios.system.product 2>/dev/null)
    fi
    case "$model" in
        ""|*"O.E.M"*|*"To Be Filled"*|"System Product Name")
            vendor=$(have kenv && kenv smbios.system.maker 2>/dev/null)
            board=$(have kenv && kenv smbios.planar.product 2>/dev/null)
            model="${vendor:+$vendor }${board}"
            ;;
    esac
    model=$(echo "$model" | tr '/' '-' | sed 's/^ *//;s/ *$//')
    [ -z "$model" ] && model=$(hostname)
    echo "$model"
}

OUTPUT_FILE="./$(get_model)_$(date +%F).txt"
# Tool ships as a pkg - tell the user what to search for. Also echoed to the
# real terminal (fd 3) since the whole block below is redirected to the
# output file - otherwise missing-tool messages stay invisible until the
# script finishes and you open the file.
missing() {
    pkg="${2:-$1}"
    msg="[MISSING] '$1' not found - try: pkg search $pkg"
    echo "$msg"
    echo "$msg" >&3
}
# Tool is expected to be part of the FreeBSD base system - a missing one
# means PATH/install issue, not something to pkg-install.
missing_base() {
    msg="[MISSING] '$1' not found - this is normally part of the FreeBSD base system, check \$PATH / base install"
    echo "$msg"
    echo "$msg" >&3
}

exec 3>&1
{
    echo "===== SUMMARY (fastfetch) ====="
    have fastfetch && fastfetch --logo none --structure-disabled colors || missing fastfetch

    echo; echo "===== uname / version ====="
    uname -a
    have freebsd-version && freebsd-version -kru 2>/dev/null || missing_base freebsd-version

    echo; echo "===== CPU ====="
    have sysctl && sysctl hw.model hw.ncpu hw.machine hw.machine_arch hw.clockrate 2>/dev/null || missing_base sysctl

    echo; echo "===== CPU virtualization support ====="
    if [ -r /var/run/dmesg.boot ]; then
        grep -iE 'Features|VT-x|AMD-V|POPCNT' /var/run/dmesg.boot
    else
        dmesg 2>/dev/null | grep -iE 'Features|VT-x|AMD-V|POPCNT'
    fi
    sysctl kern.vm_guest 2>/dev/null
    sysctl hw.vmm 2>/dev/null || echo "hw.vmm not present - try: kldload vmm (not a pkg; bhyve support built into kernel/module)"
    have kldstat && (kldstat | grep -i vmm || echo "vmm.ko not loaded - try: kldload vmm") || missing_base kldstat

    echo; echo "===== IOMMU (VT-d / AMD-Vi, needed for PCI passthrough in bhyve) ====="
    sysctl hw.vmm.iommu 2>/dev/null || echo "hw.vmm.iommu not present (load vmm.ko via 'kldload vmm', or unsupported by CPU/BIOS)"
    if [ -r /var/run/dmesg.boot ]; then
        grep -iE 'DMAR|IOMMU|remap' /var/run/dmesg.boot
    else
        dmesg 2>/dev/null | grep -iE 'DMAR|IOMMU|remap'
    fi

    echo; echo "===== TPM ====="
    if [ -e /dev/tpm0 ]; then
        echo "/dev/tpm0 present"
    else
        echo "No /dev/tpm0 found (not a pkg - try: kldload tpm, else hardware/firmware dependent)"
    fi
    have kldstat && (kldstat | grep -i tpm || echo "tpm.ko not loaded - try: kldload tpm")
    sysctl -a 2>/dev/null | grep -i tpm

    echo; echo "===== SPI / I2C / ACPI peripherals (covers sensors that don't show up on PCI/USB, e.g. some fingerprint readers) ====="
    if have pnpinfo; then
        pnpinfo 2>/dev/null
    else
        missing_base pnpinfo
    fi
    if have devinfo; then
        devinfo -v 2>/dev/null
    else
        missing_base devinfo
    fi
    echo "--- kernel log hints ---"
    if [ -r /var/run/dmesg.boot ]; then
        grep -iE 'fingerprint|fpc1[0-9]{3}|goodix|validity|elan.*fp|synaptics.*(spi|fp)' /var/run/dmesg.boot || echo "No fingerprint-related kernel log hints found"
    else
        dmesg 2>/dev/null | grep -iE 'fingerprint|fpc1[0-9]{3}|goodix|validity|elan.*fp|synaptics.*(spi|fp)' || echo "No fingerprint-related kernel log hints found"
    fi

    echo; echo "===== Memory ====="
    sysctl hw.physmem hw.realmem hw.usermem 2>/dev/null
    have swapinfo && swapinfo -h || missing_base swapinfo
    have dmidecode && doas dmidecode -t memory || missing dmidecode

    echo; echo "===== Motherboard / BIOS / Chassis / System ====="
    have kenv && kenv | grep -Ei 'smbios|bios' || missing_base kenv
    have dmidecode && doas dmidecode -t baseboard -t bios -t chassis -t system || missing dmidecode

    echo; echo "===== Expansion slots ====="
    have dmidecode && doas dmidecode -t slot || missing dmidecode

    echo; echo "===== PCI devices ====="
    have pciconf && pciconf -lv || missing_base pciconf

    echo; echo "===== USB devices ====="
    # usbconfig needs root to query device descriptors, hence doas. Only
    # call doas once for the list (reused below) - a second call with
    # stderr suppressed was silently eating the password prompt.
    if have usbconfig; then
        usb_list=$(doas usbconfig list)
        echo "$usb_list"
        # usbconfig list has no numeric idVendor/idProduct - dump each
        # device's descriptor so IDs are captured too (parity with lsusb).
        for u in $(echo "$usb_list" | awk -F: '{print $1}'); do
            echo "--- $u ---"
            doas usbconfig -d "$u" dump_device_desc 2>/dev/null
        done
    else
        missing_base usbconfig
    fi

    echo; echo "===== Disks / storage ====="
    have geom && geom disk list || missing_base geom
    have camcontrol && doas camcontrol devlist || missing_base camcontrol
    have gpart && gpart show || missing_base gpart
    for d in $(sysctl -n kern.disks 2>/dev/null); do
        echo "--- diskinfo -v $d ---"
        have diskinfo && doas diskinfo -v "/dev/$d" 2>/dev/null || missing_base diskinfo
        echo "--- camcontrol identify $d (model/serial) ---"
        have camcontrol && doas camcontrol identify "$d" 2>/dev/null
    done

    echo; echo "===== ZFS ====="
    if have zpool; then
        zpool list -v
        zpool status
        zfs list
    else
        msg="[MISSING] 'zpool' not found - not a pkg, it's a kernel module: try 'kldload zfs' (add zfs_enable=\"YES\" to /etc/rc.conf to persist)"
        echo "$msg"
        echo "$msg" >&3
    fi

    echo; echo "===== Disk health (SMART) ====="
    if have smartctl; then
        for d in $(sysctl -n kern.disks 2>/dev/null); do
            echo "--- /dev/$d ---"
            # nda*/nvd* are NVMe drives on the CAM/nvme(4) stack - smartctl's
            # default ATA/SCSI probe sends an INQUIRY, which NVMe controllers
            # reject ("INQUIRY failed"). smartctl's NVMe support further
            # requires the actual /dev/nvmeN controller path, not /dev/ndaN
            # with a flag ("ids must begin with '/dev/nvme'").
            case "$d" in
                nda*) doas smartctl -a "/dev/nvme${d#nda}" 2>&1 ;;
                nvd*) doas smartctl -a "/dev/nvme${d#nvd}" 2>&1 ;;
                *)    doas smartctl -a "/dev/$d" 2>&1 ;;
            esac
        done
    else
        missing smartctl smartmontools
    fi

    echo; echo "===== Network interfaces ====="
    have ifconfig && ifconfig -a || missing_base ifconfig

    echo; echo "===== Supported link media per interface ====="
    if have ifconfig; then
        for i in $(ifconfig -l 2>/dev/null); do
            echo "--- $i ---"
            ifconfig -m "$i" 2>/dev/null
        done
    fi

    echo; echo "===== Sensors / temperature ====="
    sysctl -a 2>/dev/null | grep -Ei 'temperature|coretemp'
    sysctl hw.acpi.thermal 2>/dev/null

    echo; echo "===== Battery ====="
    if have acpiconf && acpiconf -i 0 2>/dev/null; then
        :
    else
        msg="[MISSING or N/A] 'acpiconf' not found or no battery - acpiconf is part of the base system"
        echo "$msg"
        echo "$msg" >&3
    fi

} > "$OUTPUT_FILE" 2>&1

echo "Saved to $OUTPUT_FILE"
