# Nomarchy hardware database — sourced by nomarchy-install.
#
# Flat table mapping detected DMI fields to nixos-hardware module names
# (the strings nomarchy.lib.mkFlake's hardwareProfile accepts). Ported
# from the previous iteration (installer/hardware-db.sh at 3bdfc35).
#
# Format (pipe-separated):
#   sys_vendor_regex | product_regex | nixos_hardware_module
#
# - Matching is bash `[[ =~ ]]` regex, case-insensitive.
# - First match wins — put more specific entries (e.g. exact model years)
#   above broader fallbacks.
#
# To add a new device: grab `/sys/class/dmi/id/product_name` on the
# target machine and add a line. Module names must exist in
# nixos-hardware.nixosModules (mkFlake fails with suggestions if not).

HARDWARE_DB=(
  # Framework ---------------------------------------------------------------
  # Module names follow nixos-hardware's actual attrs — for Framework 13
  # the per-generation modules dropped the "13-" prefix.
  "Framework|Laptop 16.*AMD|framework-16-7040-amd"
  "Framework|Laptop 16.*Ryzen AI 300|framework-16-amd-ai-300-series"
  "Framework|Laptop 13.*Ryzen AI 300|framework-amd-ai-300-series"
  "Framework|Laptop 13.*Ryzen 7040|framework-13-7040-amd"
  "Framework|Laptop 13.*Core Ultra|framework-intel-core-ultra-series1"
  "Framework|Laptop 13.*13th Gen Intel|framework-13th-gen-intel"
  "Framework|Laptop 13.*12th Gen Intel|framework-12th-gen-intel"
  "Framework|Laptop 13.*11th Gen Intel|framework-11th-gen-intel"
  "Framework|Laptop \(13.*|framework"

  # Dell XPS / Precision / Latitude ----------------------------------------
  "Dell|XPS 15 9500|dell-xps-15-9500"
  "Dell|XPS 15 9510|dell-xps-15-9510"
  "Dell|XPS 15 9520|dell-xps-15-9520"
  "Dell|XPS 13 9310|dell-xps-13-9310"
  "Dell|XPS 13 9370|dell-xps-13-9370"
  "Dell|XPS 13 9380|dell-xps-13-9380"
  "Dell|XPS 13 7390|dell-xps-13-7390"
  "Dell|Precision 5530|dell-precision-5530"
  "Dell|Latitude 7490|dell-latitude-7490"
  "Dell|Latitude 7430|dell-latitude-7430"
  "Dell|Latitude 7420|dell-latitude-7420"

  # Lenovo ThinkPad --------------------------------------------------------
  # X1 Carbon: the per-gen modules are named "x1-Nth-gen", not
  # "x1-carbon-genN" — the X1 series IS the Carbon line.
  "LENOVO|ThinkPad X1 Carbon Gen 11|lenovo-thinkpad-x1-11th-gen"
  "LENOVO|ThinkPad X1 Carbon Gen 10|lenovo-thinkpad-x1-10th-gen"
  "LENOVO|ThinkPad X1 Carbon Gen 9|lenovo-thinkpad-x1-9th-gen"
  "LENOVO|ThinkPad X1 Carbon Gen 7|lenovo-thinkpad-x1-7th-gen"
  "LENOVO|ThinkPad X1 Carbon Gen 6|lenovo-thinkpad-x1-6th-gen"
  "LENOVO|ThinkPad X1 Extreme|lenovo-thinkpad-x1-extreme"
  "LENOVO|ThinkPad X1 Nano|lenovo-thinkpad-x1-nano-gen1"
  "LENOVO|ThinkPad T14 Gen 3|lenovo-thinkpad-t14-amd-gen3"
  "LENOVO|ThinkPad T14 Gen 2|lenovo-thinkpad-t14-amd-gen2"
  "LENOVO|ThinkPad T14 Gen 1|lenovo-thinkpad-t14-amd-gen1"
  "LENOVO|ThinkPad T480|lenovo-thinkpad-t480"
  "LENOVO|ThinkPad L13|lenovo-thinkpad-l13"
  "LENOVO|ThinkPad P14s.*Gen 5|lenovo-thinkpad-p14s-amd-gen5"
  "LENOVO|ThinkPad P14s.*Gen 4|lenovo-thinkpad-p14s-amd-gen4"
  "LENOVO|ThinkPad P14s.*Gen 3|lenovo-thinkpad-p14s-amd-gen3"

  # Microsoft Surface ------------------------------------------------------
  # nixos-hardware ships per-chip modules, not per-revision.
  "Microsoft|Surface Pro 10|microsoft-surface-pro-intel"
  "Microsoft|Surface Pro 9|microsoft-surface-pro-9"
  "Microsoft|Surface Pro 8|microsoft-surface-pro-intel"
  "Microsoft|Surface Pro 7|microsoft-surface-pro-intel"
  "Microsoft|Surface Pro 6|microsoft-surface-pro-intel"
  "Microsoft|Surface Pro 3|microsoft-surface-pro-3"
  "Microsoft|Surface Laptop.*Ryzen|microsoft-surface-laptop-amd"
  "Microsoft|Surface Go|microsoft-surface-go"

  # ASUS ROG Ally / Zephyrus / Strix ---------------------------------------
  "ASUS.*|RC71L|asus-ally-rc71l"
  "ASUS.*|RC72LA|asus-ally-rc71l"
  "ASUS.*|ROG Ally|asus-ally-rc71l"
  "ASUS.*|ROG Zephyrus G14.*GA402X|asus-zephyrus-ga402x"
  "ASUS.*|ROG Zephyrus G14.*GA402|asus-zephyrus-ga402"
  "ASUS.*|ROG Zephyrus G14.*GA401|asus-zephyrus-ga401"
  "ASUS.*|ROG Zephyrus G15|asus-zephyrus-ga503"
  "ASUS.*|ROG Zephyrus.*GU603|asus-zephyrus-gu603h"
  "ASUS.*|ROG Strix G513|asus-rog-strix-g513im"
  "ASUS.*|ROG Strix G533|asus-rog-strix-g533zw"

  # Apple (T2 Intel; M-series is aarch64 — out of scope) -------------------
  "Apple.*|MacBookPro15|apple-t2"
  "Apple.*|MacBookPro16|apple-t2"
  "Apple.*|MacBookAir8|apple-t2"
  "Apple.*|MacBookAir9|apple-t2"
  "Apple.*|iMac19|apple-t2"
  "Apple.*|iMacPro1|apple-t2"

  # System76 ---------------------------------------------------------------
  "System76|Oryx Pro.*|system76"
  "System76|Lemur Pro.*|system76"
  "System76|Darter Pro.*|system76"
  "System76|Galago Pro.*|system76"
  "System76|Pangolin.*|system76"

  # Known-unsupported (so contributors know they're not missing):
  #   - Valve Steam Deck: Jovian-NixOS territory, not nixos-hardware.
  #   - Snapdragon X laptops / Raspberry Pi: aarch64; installer is x86_64.
)

# ----------------------------------------------------------------------------
# nomarchy_detect_hw — inspects DMI + lspci + /sys, emits one line per hit:
#   MODULE  <nixos-hardware module name>
#   DETAIL  <human-readable explanation>
# Returns 0 if at least one module was detected.
# ----------------------------------------------------------------------------
nomarchy_detect_hw() {
    local sys_vendor product_name cpu_vendor
    local nvidia=0 amdgpu=0 intelgpu=0
    sys_vendor=$(cat /sys/class/dmi/id/sys_vendor 2>/dev/null || echo "")
    product_name=$(cat /sys/class/dmi/id/product_name 2>/dev/null || echo "")
    cpu_vendor=$(lscpu 2>/dev/null | awk -F: '/Vendor ID/{gsub(/ /,"",$2); print $2; exit}')

    local detected=0

    # CPU
    case "$cpu_vendor" in
        AuthenticAMD) echo "MODULE common-cpu-amd"; echo "DETAIL cpu: AMD"; detected=1 ;;
        GenuineIntel) echo "MODULE common-cpu-intel"; echo "DETAIL cpu: Intel"; detected=1 ;;
    esac

    # GPU (lspci may list several; report all)
    if command -v lspci >/dev/null 2>&1; then
        local gpu_line
        while IFS= read -r gpu_line; do
            case "$gpu_line" in
                *"[10de:"*|*"NVIDIA"*) nvidia=1 ;;
                *"[1002:"*|*"AMD/ATI"*|*"Advanced Micro Devices"*) amdgpu=1 ;;
                *"[8086:"*|*"Intel Corporation"*) intelgpu=1 ;;
            esac
        done < <(lspci -nn 2>/dev/null | grep -iE 'vga|3d|display')

        (( nvidia ))   && { echo "MODULE common-gpu-nvidia"; echo "DETAIL gpu: NVIDIA"; detected=1; }
        (( amdgpu ))   && { echo "MODULE common-gpu-amd";    echo "DETAIL gpu: AMD";    detected=1; }
        (( intelgpu )) && { echo "MODULE common-gpu-intel";  echo "DETAIL gpu: Intel";  detected=1; }
    fi

    # ── nomarchy.hardware.* enablement — the gap ABOVE the nixos-hardware
    # commons (GuC/HuC, amd-pstate, the AMD VA-API env, GPU-compute runtimes,
    # fprintd, the NPU driver). Emitted as NOMARCHY lines the installer turns
    # into nomarchy.hardware.* in system.nix; safe bits active, heavy opt-ins
    # commented.
    if [[ "$cpu_vendor" == "GenuineIntel" || $intelgpu -eq 1 ]]; then
        echo "NOMARCHY hardware.intel.enable=true"
        echo "DETAIL nomarchy.hardware.intel: GuC/HuC on; GPU-compute runtime opt-in"
        # i915.enable_guc applies to the i915 driver only. The newer `xe` driver
        # (Lunar Lake / Battlemage / Panther Lake, recent Xe GPUs) enables GuC by
        # default and ignores the param — so turn the toggle off when xe is bound.
        if lspci -k 2>/dev/null | grep -qiE 'driver in use: xe\b'; then
            echo "NOMARCHY hardware.intel.guc=false"
            echo "DETAIL Intel GPU on the xe driver → GuC default-on (i915 param skipped)"
        fi
    fi
    if [[ "$cpu_vendor" == "AuthenticAMD" || $amdgpu -eq 1 ]]; then
        echo "NOMARCHY hardware.amd.enable=true"
        echo "DETAIL nomarchy.hardware.amd: amd-pstate + VA-API on; ROCm opt-in"
    fi

    # Fingerprint reader — libfprint's common USB vendor IDs (Goodix,
    # Synaptics/Validity, Elan, EgisTec, Upek, AuthenTec, FocalTech, NB).
    local have_fp=0
    if command -v lsusb >/dev/null 2>&1; then
        local vid
        for vid in 27c6 06cb 138a 04f3 1c7a 147e 08ff 2808 1fae; do
            lsusb 2>/dev/null | grep -qiE "ID ${vid}:" && { have_fp=1; break; }
        done
    else
        local f
        for f in /sys/bus/usb/devices/*/idVendor; do
            [[ -e "$f" ]] || continue
            case "$(cat "$f" 2>/dev/null)" in
                27c6|06cb|138a|04f3|1c7a|147e|08ff|2808|1fae) have_fp=1; break ;;
            esac
        done
    fi
    if [[ $have_fp -eq 1 ]]; then
        echo "NOMARCHY hardware.fingerprint.enable=true"
        echo "DETAIL fingerprint reader detected → fprintd (PAM login/sudo opt-in)"
    fi

    # Dual-sensor webcam (RGB + IR face-unlock). Such modules expose the IR
    # sensor as a SECOND, identically-named "Integrated Camera"; an app that
    # picks it shows a dark, 8-bit-greyscale image. When an IR-companion node
    # sits alongside a normal camera node, enable hiding the IR one from
    # PipeWire (the colour camera stays; the kernel /dev/video* is untouched, so
    # Howdy can still read the IR sensor directly). Read from /sys — no
    # v4l2-ctl needed in the installer env.
    local cam_name have_ir_cam=0 have_color_cam=0 vf
    local ir_re='Integrated I|IR Camera|Infrared'
    for vf in /sys/class/video4linux/video*/name; do
        [[ -e "$vf" ]] || continue
        cam_name=$(cat "$vf" 2>/dev/null)
        shopt -s nocasematch
        if [[ "$cam_name" =~ $ir_re ]]; then
            have_ir_cam=1
        elif [[ "$cam_name" =~ (Camera|Webcam) ]]; then
            have_color_cam=1
        fi
        shopt -u nocasematch
    done
    if [[ $have_ir_cam -eq 1 && $have_color_cam -eq 1 ]]; then
        echo "NOMARCHY hardware.camera.hideIrSensor=true"
        echo "DETAIL dual-sensor webcam (RGB+IR) → hide the IR node so apps get the colour camera"
    fi

    # NPU (detect-only → a commented, experimental opt-in). Match the PCI
    # "Processing accelerators" class [1200] (or accelerator keywords) and
    # attribute by vendor — future-proof vs a per-device-ID list, so new gens
    # (e.g. Panther Lake) are caught without a code change. Known IDs kept for
    # reference: Intel VPU 8086:{7d1d,643e,ad1d,b03e}, AMD XDNA 1022:1502.
    if command -v lspci >/dev/null 2>&1; then
        local npu_line
        npu_line=$(lspci -nn 2>/dev/null \
            | grep -iE '\[1200\]|processing accelerat|neural|\[8086:(7d1d|643e|ad1d|b03e)\]|\[1022:1502\]' \
            | head -1)
        case "$npu_line" in
            *"[8086:"*|*Intel*)
                echo "NOMARCHY-NPU intel"; echo "DETAIL Intel NPU detected (opt-in, experimental)" ;;
            *"[1022:"*|*"Advanced Micro Devices"*|*AMD*)
                echo "NOMARCHY-NPU amd";   echo "DETAIL AMD XDNA NPU detected (opt-in, experimental)" ;;
        esac
    fi

    # Chassis: any system battery (type=Battery, not scope=Device) —
    # name-agnostic BAT0/CMB0/… (BACKLOG #60). Not a bare BAT* glob.
    local has_bat=0 d
    for d in /sys/class/power_supply/*/; do
        [[ "$(cat "$d/type" 2>/dev/null)" == Battery ]] || continue
        [[ "$(cat "$d/scope" 2>/dev/null || echo System)" == Device ]] && continue
        has_bat=1
        break
    done
    if (( has_bat )); then
        echo "MODULE common-pc-laptop"
        echo "DETAIL chassis: laptop (battery present)"
    else
        echo "MODULE common-pc"
        echo "DETAIL chassis: desktop"
    fi
    detected=1

    # SSD
    local rotational
    rotational=$(cat /sys/block/nvme0n1/queue/rotational 2>/dev/null \
                 || cat /sys/block/sda/queue/rotational 2>/dev/null || echo "")
    if [[ "$rotational" == "0" ]]; then
        echo "MODULE common-pc-ssd"
        echo "DETAIL storage: SSD"
    fi

    # Model-specific match from the DB
    local entry v_re p_re mod
    for entry in "${HARDWARE_DB[@]}"; do
        IFS='|' read -r v_re p_re mod <<< "$entry"
        shopt -s nocasematch
        if [[ "$sys_vendor" =~ $v_re ]] && [[ "$product_name" =~ $p_re ]]; then
            shopt -u nocasematch
            echo "MODULE $mod"
            echo "DETAIL model: $sys_vendor $product_name → $mod"
            break
        fi
        shopt -u nocasematch
    done

    return $(( 1 - detected ))
}
