#!/bin/sh
# Collect a local hardware report for Clawdie live-USB support and future
# custom ISO builds. This command never uploads unless explicitly requested.

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

usage() {
    cat <<'EOF'
Usage: hw-report [--upload-public-probe] [--no-smart]

Collect a local hardware diagnostics bundle under /home/clawdie/hw-reports
when that home directory exists, otherwise under $HOME/hw-reports.

Options:
  --upload-public-probe  Also run hw-probe -all -upload. This submits an
                         anonymous public report to bsd-hardware.info.
  --no-smart             Skip smartctl disk probes.
  -h, --help             Show this help.

Recommended live-USB invocation:
  mdo -u root hw-report
EOF
}

_upload_public_probe="NO"
_run_smart="YES"
while [ "$#" -gt 0 ]; do
    case "$1" in
        --upload-public-probe)
            _upload_public_probe="YES"
            ;;
        --no-smart)
            _run_smart="NO"
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        *)
            echo "ERROR: unknown option: $1" >&2
            usage >&2
            exit 2
            ;;
    esac
    shift
done

if [ "$(id -u)" -ne 0 ]; then
    echo "WARNING: not running as root; some hardware data will be incomplete." >&2
    echo "Recommended: mdo -u root hw-report" >&2
fi

_stamp="$(date -u '+%Y%m%dT%H%M%SZ')"
_host="$(hostname 2>/dev/null || echo clawdie-live)"
_report_name="hw-report-${_host}-${_stamp}"

if [ -d /home/clawdie ]; then
    _base="${CLAWDIE_HW_REPORT_DIR:-/home/clawdie/hw-reports}"
else
    _base="${CLAWDIE_HW_REPORT_DIR:-${HOME:-/root}/hw-reports}"
fi
_report_dir="${_base}/${_report_name}"
_raw_dir="${_report_dir}/raw"
_logs_dir="${_report_dir}/logs"

mkdir -p "${_raw_dir}" "${_logs_dir}"

_log="${_report_dir}/collection.log"
: > "${_log}"

note() {
    printf '%s %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$*" | tee -a "${_log}"
}

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

run_capture() {
    _name="$1"
    shift
    _out="${_raw_dir}/${_name}.txt"
    note "collect ${_name}: $*"
    {
        printf '# command: %s\n' "$*"
        printf '# collected_utc: %s\n\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
        "$@"
    } > "${_out}" 2>&1
    _rc=$?
    printf '\n# exit_status: %s\n' "${_rc}" >> "${_out}"
    if [ "${_rc}" -ne 0 ]; then
        note "WARN ${_name} exited ${_rc}"
    fi
    return 0
}

run_shell_capture() {
    _name="$1"
    _cmd="$2"
    _out="${_raw_dir}/${_name}.txt"
    note "collect ${_name}: ${_cmd}"
    {
        printf '# command: %s\n' "${_cmd}"
        printf '# collected_utc: %s\n\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')"
        /bin/sh -c "${_cmd}"
    } > "${_out}" 2>&1
    _rc=$?
    printf '\n# exit_status: %s\n' "${_rc}" >> "${_out}"
    if [ "${_rc}" -ne 0 ]; then
        note "WARN ${_name} exited ${_rc}"
    fi
    return 0
}

run_if_present() {
    _cmd="$1"
    _name="$2"
    shift 2
    if have "${_cmd}"; then
        run_capture "${_name}" "${_cmd}" "$@"
    else
        note "skip ${_name}: ${_cmd} not found"
    fi
}

run_shell_if_present() {
    _cmd="$1"
    _name="$2"
    _script="$3"
    if have "${_cmd}"; then
        run_shell_capture "${_name}" "${_script}"
    else
        note "skip ${_name}: ${_cmd} not found"
    fi
}

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

note "start ${_report_name}"

cat > "${_report_dir}/README.txt" <<'EOF'
Clawdie hardware report bundle

This is a local diagnostics bundle for FreeBSD/Clawdie live-USB support and
future custom ISO builds. It is not uploaded automatically.

Privacy notes:
- This bundle may contain hostnames, MAC addresses, disk model/serial strings,
  USB/PCI IDs, kernel messages, local network interface state, Xorg/SDDM log
  details, device names, and monitor EDID or serial-like strings.
- Review before sharing.
- Public hw-probe upload only happens when hw-report is run with
  --upload-public-probe.
EOF

run_capture uname-a uname -a
run_if_present freebsd-version freebsd-version-kru -kru
run_capture hostname hostname
run_capture date-utc date -u '+%Y-%m-%dT%H:%M:%SZ'
run_if_present uptime uptime

run_if_present pciconf pciconf-lv -lv
run_if_present pciconf pciconf-lvbce -lvbce
run_if_present usbconfig usbconfig
run_if_present usbconfig usb-device-desc dump_device_desc
run_if_present usbconfig usb-all-desc dump_all_desc
run_if_present devinfo devinfo-rv -rv
run_if_present dmidecode dmidecode
run_if_present service dbus-status dbus onestatus
run_if_present service sddm-status sddm onestatus

run_if_present kldstat kldstat
run_if_present kldstat kldstat-v -v
run_if_present dmesg dmesg
run_shell_capture dmesg-hardware-filter "dmesg | egrep -i 'drm|kms|amdgpu|i915|radeon|nvidia|firmware|wifi|wlan|iwm|iwlwifi|rtwn|urtwn|run|otus|mt76|usb|uhub|ure|ue[0-9]|realtek|rtl|bluetooth|ubt|bcm|cypress|webcam|video|hid|iichid|hms|hmt|elan|touch|acpi|battery|powerd'"
run_shell_capture dmesg-boot "for f in /var/run/dmesg.boot; do [ -e \"\$f\" ] || continue; echo \"===== \$f =====\"; cat \"\$f\"; echo; done"
run_shell_capture xorg-var-logs "for f in /var/log/Xorg.*.log*; do [ -e \"\$f\" ] || continue; echo \"===== \$f =====\"; cat \"\$f\"; echo; done"
run_shell_capture xorg-user-logs "for f in /home/clawdie/.local/share/xorg/Xorg.*.log*; do [ -e \"\$f\" ] || continue; echo \"===== \$f =====\"; cat \"\$f\"; echo; done"
run_shell_capture xsession-errors "for f in /home/clawdie/.xsession-errors /home/clawdie/.xsession-errors.old; do [ -e \"\$f\" ] || continue; echo \"===== \$f =====\"; cat \"\$f\"; echo; done"
run_shell_capture var-log-messages "for f in /var/log/messages*; do [ -e \"\$f\" ] || continue; echo \"===== \$f =====\"; case \"\$f\" in *.gz) zcat \"\$f\";; *.bz2) bzcat \"\$f\";; *.xz) xzcat \"\$f\";; *.zst) zstdcat \"\$f\";; *) cat \"\$f\";; esac; echo; done"
run_shell_capture sddm-log-files "for f in /var/log/sddm.log; do [ -e \"\$f\" ] || continue; echo \"===== \$f =====\"; cat \"\$f\"; echo; done"
run_shell_capture sddm-config "for f in /usr/local/etc/sddm.conf /usr/local/etc/sddm.conf.d/*; do [ -e \"\$f\" ] || continue; echo \"===== \$f =====\"; cat \"\$f\"; echo; done"
run_shell_capture loader-conf-files "for f in /boot/loader.conf /boot/loader.conf.local; do [ -e \"\$f\" ] || continue; echo \"===== \$f =====\"; cat \"\$f\"; echo; done"
run_if_present kenv kenv
run_shell_capture xorg-config-snippets "for f in /usr/local/etc/X11/xorg.conf.d/* /etc/X11/xorg.conf.d/* /usr/local/share/X11/xorg.conf.d/*; do [ -e \"\$f\" ] || continue; echo \"===== \$f =====\"; cat \"\$f\"; echo; done"

run_if_present ifconfig ifconfig-a -a
run_if_present netstat netstat-rn -rn
run_shell_capture resolver-state "ls -l /etc/resolv.conf 2>/dev/null || true; readlink /etc/resolv.conf 2>/dev/null || true; cat /etc/resolv.conf 2>/dev/null || true"
run_shell_capture wlan-devices "sysctl net.wlan.devices"
run_shell_capture rc-conf-live "grep -E '^(hostname|kld_list|sshd_enable|tailscaled_enable|avahi_daemon_enable|pf_enable|sddm_enable|webcamd_enable|powerdxx_enable|colibri_daemon_enable|clawdie_live_|performance_cx_lowest|economy_cx_lowest|dumpdev_enable)' /etc/rc.conf || true"
run_if_present service service-enabled -e

run_if_present sysctl sysctl-hw hw
run_if_present sysctl sysctl-machdep machdep
run_shell_capture sysctl-selected "sysctl hw.acpi hw.dri kern.disks kern.geom net.wlan.devices dev.cpu 2>/dev/null || true"
run_shell_capture sysctl-drm-hw-dri "sysctl dev.drm hw.dri 2>/dev/null || true"
run_if_present service powerdxx-status powerdxx status
run_if_present sysctl snd-default-unit hw.snd.default_unit
run_if_present sysctl acpi-acline hw.acpi.acline
run_if_present sysctl acpi-cx-lowest hw.acpi.cpu.cx_lowest
run_if_present sysctl acpi-video-brightness hw.acpi.video.lcd0.brightness
run_shell_capture sysctl-input-stack "sysctl kern.evdev dev.iichid dev.hidbus dev.hms dev.hmt dev.hkbd dev.hconf dev.ig4 dev.psm dev.atkbd dev.uhid hw.psm 2>/dev/null || true"
run_if_present service moused-status moused onestatus

run_if_present gpart gpart-show show
run_if_present geom geom-disk-list disk list
run_if_present camcontrol camcontrol-devlist devlist
run_if_present nvmecontrol nvmecontrol-devlist devlist
run_if_present df df-h -h
run_if_present mount mount
run_if_present zpool zpool-list list
run_if_present zfs zfs-list list

if [ "${_run_smart}" = "YES" ] && have smartctl; then
    _disks="$(sysctl -n kern.disks 2>/dev/null || true)"
    if [ -n "${_disks}" ]; then
        for _disk in ${_disks}; do
            case "${_disk}" in
                md*|cd*)
                    note "skip smartctl-${_disk}: virtual/removable class"
                    ;;
                *)
                    run_capture "smartctl-${_disk}" smartctl -a "/dev/${_disk}"
                    ;;
            esac
        done
    else
        note "skip smartctl: kern.disks empty"
    fi
else
    note "skip smartctl: disabled or smartctl not found"
fi

run_if_present lspci lspci-nn -nn
run_if_present lsusb lsusb
run_if_present lscpu lscpu
run_if_present lsblk lsblk
run_if_present hwstat hwstat
run_shell_if_present usbhid-dump usbhid-dump-version "usbhid-dump --help 2>&1 | head -40"

run_if_present cat clawdie-live-gpu-log /var/log/clawdie-live-gpu.log
run_if_present cat clawdie-noblank-guard-log /home/clawdie/.clawdie-noblank-guard.log
run_if_present cat clawdie-touchpad-guard-log /home/clawdie/.clawdie-touchpad-guard.log
run_if_present cat sndstat /dev/sndstat
run_if_present mixer mixer-s -s
run_if_present v4l2-ctl v4l2-list-devices --list-devices
run_shell_capture video-devices "ls -l /dev/video* 2>/dev/null || true"
run_shell_capture dri-devices "ls -l /dev/dri /dev/dri/* 2>/dev/null || true"
run_shell_capture input-device-nodes "ls -l /dev/input /dev/input/* /dev/uhid* /dev/atp* 2>/dev/null || true"

run_if_present xset xset-q q
run_shell_if_present xfconf-query xfce4-power-manager "xfconf-query -c xfce4-power-manager -l -v 2>/dev/null || true"
run_shell_if_present xfconf-query xfce4-panel "xfconf-query -c xfce4-panel -l -v 2>/dev/null || true"
run_shell_if_present xfconf-query xfce4-desktop "xfconf-query -c xfce4-desktop -l -v 2>/dev/null || true"
run_shell_if_present xfconf-query xsettings "xfconf-query -c xsettings -l -v 2>/dev/null || true"
run_shell_if_present xfconf-query xfce4-pointers "DISPLAY=:0 XAUTHORITY=/home/clawdie/.Xauthority xfconf-query -c pointers -l -v 2>/dev/null || xfconf-query -c pointers -l -v 2>/dev/null || true"
run_shell_capture xfce-perchannel-xml "for f in /home/clawdie/.config/xfce4/xfconf/xfce-perchannel-xml/*; do [ -e \"\$f\" ] || continue; echo \"===== \$f =====\"; cat \"\$f\"; echo; done"
run_shell_capture xfce-panel-launchers "find /home/clawdie/.config/xfce4/panel -maxdepth 4 -type f -print -exec sed -n '1,80p' {} \\; 2>/dev/null || true"
run_shell_capture icon-theme-dirs "find /usr/local/share/icons -maxdepth 4 -type f \( -name 'icon-theme.cache' -o -name 'clawdie-start.*' \) -print 2>/dev/null || true"
run_shell_capture clawdie-iso-assets "find /usr/local/share/clawdie-iso -maxdepth 4 -type f -ls 2>/dev/null || true; for f in /usr/local/share/clawdie-iso/build-manifest.json; do [ -e \"\$f\" ] || continue; echo \"===== \$f =====\"; cat \"\$f\"; echo; done"
run_shell_capture colibri-service-state "service colibri_daemon status 2>&1 || true; egrep -n '^(command|command_args|procname)=' /usr/local/etc/rc.d/colibri_daemon 2>/dev/null || true; sysrc -n colibri_daemon_enable 2>/dev/null || true"
if [ -f /home/clawdie/.Xauthority ]; then
    run_shell_if_present xinput xinput-list "DISPLAY=:0 XAUTHORITY=/home/clawdie/.Xauthority xinput list 2>/dev/null || true"
    run_shell_if_present xinput xinput-list-props "DISPLAY=:0 XAUTHORITY=/home/clawdie/.Xauthority sh -c 'for id in \$(xinput list --id-only 2>/dev/null); do echo \"===== device \$id =====\"; xinput list-props \"\$id\"; echo; done' || true"
    run_shell_if_present xrandr xrandr-verbose "DISPLAY=:0 XAUTHORITY=/home/clawdie/.Xauthority xrandr --verbose 2>/dev/null || true"
    run_shell_if_present glxinfo glxinfo-B "DISPLAY=:0 XAUTHORITY=/home/clawdie/.Xauthority glxinfo -B 2>/dev/null || true"
fi
run_shell_if_present libinput libinput-list-devices "libinput list-devices 2>/dev/null || true"

run_if_present vulkaninfo vulkaninfo-summary --summary
if have vulkaninfo && [ -f /home/clawdie/.Xauthority ]; then
    run_shell_capture vulkaninfo-summary-display0 "DISPLAY=:0 XAUTHORITY=/home/clawdie/.Xauthority vulkaninfo --summary"
fi

run_if_present pkg pkg-info info
run_if_present pkg pkg-version version
run_if_present pkg pkg-audit audit
run_if_present hw-probe hw-probe-all -all

if [ "${_upload_public_probe}" = "YES" ]; then
    if have hw-probe; then
        run_capture hw-probe-all-upload hw-probe -all -upload
    else
        note "skip hw-probe public upload: hw-probe not found"
    fi
else
    note "public hw-probe upload not requested"
fi

_public_probe_urls=""
_public_probe_url=""
_upload_output="${_raw_dir}/hw-probe-all-upload.txt"
if [ -f "${_upload_output}" ]; then
    _public_probe_urls="$(grep -Eo 'https?://[^[:space:]<>"'"'"']+' "${_upload_output}" | grep -E 'bsd-hardware\.info|linux-hardware\.org' | sort -u || true)"
    _public_probe_url="$(printf '%s\n' "${_public_probe_urls}" | sed -n '1p')"
fi

_freebsd="$(freebsd-version -kru 2>/dev/null | tr '\n' ' ' | sed 's/[[:space:]]*$//')"
_gpu="$(pciconf -lv 2>/dev/null | grep -B3 -A4 -E 'VGA|display' | sed -n '1,12p')"
_wlan="$(sysctl -n net.wlan.devices 2>/dev/null || true)"
_disks="$(sysctl -n kern.disks 2>/dev/null || true)"

cat > "${_report_dir}/summary.txt" <<EOF
Clawdie hardware report
=======================

Report: ${_report_name}
Collected UTC: ${_stamp}
Host: ${_host}
FreeBSD: ${_freebsd}
Output directory: ${_report_dir}
Public hardware probe URL: ${_public_probe_url:-not uploaded or not reported by hw-probe}

Wireless parent devices:
${_wlan:-none reported}

Kernel disks:
${_disks:-none reported}

Display devices, first lines:
${_gpu:-none reported}

Share only after review. The bundle may include MAC addresses, serial strings,
local hostnames, and kernel/network state.
EOF

cat > "${_report_dir}/summary.json" <<EOF
{
  "schema": "clawdie.hardware-report.v1",
  "report_name": "$(json_escape "${_report_name}")",
  "collected_utc": "$(json_escape "${_stamp}")",
  "host": "$(json_escape "${_host}")",
  "freebsd": "$(json_escape "${_freebsd}")",
  "wlan_devices": "$(json_escape "${_wlan}")",
  "kernel_disks": "$(json_escape "${_disks}")",
  "public_probe_upload_requested": "${_upload_public_probe}",
  "public_probe_url": "$(json_escape "${_public_probe_url}")",
  "smart_probe_requested": "${_run_smart}",
  "privacy_note": "local report; review before sharing"
}
EOF

_archive="${_base}/${_report_name}.tar.gz"
run_shell_capture report-file-list "find '${_report_dir}' -type f | sort"
if tar -czf "${_archive}" -C "${_base}" "${_report_name}" >> "${_log}" 2>&1; then
    note "archive ${_archive}"
else
    note "WARN archive failed: ${_archive}"
fi

if [ -d /home/clawdie ] && id clawdie >/dev/null 2>&1; then
    chown -R clawdie:clawdie "${_base}" 2>/dev/null || true
fi

cat <<EOF

Clawdie hardware report complete.

Summary:
  ${_report_dir}/summary.txt
JSON:
  ${_report_dir}/summary.json
Archive:
  ${_archive}
EOF

if [ "${_upload_public_probe}" = "YES" ]; then
    if [ -n "${_public_probe_url}" ]; then
        cat <<EOF
Public hardware probe:
  ${_public_probe_url}
EOF
    else
        cat <<EOF
Public hardware probe upload was requested, but no public URL was detected.
Inspect:
  ${_upload_output}
EOF
    fi
else
    cat <<EOF

Review before sharing. To include a public bsd-hardware.info probe URL, rerun:
  mdo -u root hw-report --upload-public-probe
EOF
fi

exit 0
