#!/usr/bin/env bash
# H7 SENSE + SYSTEM LEARN · OCR · hearing · USB cam · motherboard · full box · Nighthawk
#
#   ./Build/h7-sense-system.sh once|hw|cam|hear|ocr|nighthawk|status|light
#
# light  = fast sensors (default forever tick) · no long captures
# once   = light + short cam/hear + optional OCR if H7_SENSE_OCR=1
#
# Law: love field · iron measure · never thrash · God Bless
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
OUT="$ROOT/out"
JSON="$OUT/h7_sense_system.json"
FEED="$OUT/h7_vocab_feed.tsv"
VOCAB="$OUT/h7_vocab_self.tsv"
cmd="${1:-light}"
TS="$(date -Iseconds)"
mkdir -p "$OUT" "$OUT/h7sense"

n0() { local v="${1//[^0-9]/}"; echo "${v:-0}"; }
say() { echo "[$TS] SENSE · $*"; }

jbool() { case "${1,,}" in 1|true|yes|ok) echo true ;; *) echo false ;; esac }

# ── motherboard + complete system ──
learn_hw() {
  local board vendor product bios cpu cores mem_kb mem_gb gpu nics disks usb_n pci_n
  board=$(cat /sys/class/dmi/id/board_name 2>/dev/null || echo "?")
  vendor=$(cat /sys/class/dmi/id/board_vendor 2>/dev/null || echo "?")
  product=$(cat /sys/class/dmi/id/product_name 2>/dev/null || echo "?")
  bios=$(cat /sys/class/dmi/id/bios_version 2>/dev/null || echo "?")
  cpu=$(lscpu 2>/dev/null | awk -F: '/Model name/{gsub(/^ +/,"",$2); print $2; exit}' || true)
  cores=$(nproc 2>/dev/null || echo 0)
  mem_kb=$(awk '/MemTotal/{print $2}' /proc/meminfo 2>/dev/null || echo 0)
  mem_gb=$(awk -v k="$mem_kb" 'BEGIN{printf "%.1f", k/1024/1024}')
  # single line gpu · no SIGPIPE thrash
  gpu=$(lspci 2>/dev/null | awk -F': ' '/VGA|3D|Display/{print $2; exit}' || true)
  gpu=${gpu//$'\n'/ }; gpu=${gpu:-?}
  nics=$(ip -br link 2>/dev/null | awk '$1!="lo"{c++} END{print c+0}' || echo 0)
  disks=$(lsblk -d -n -o NAME,SIZE,TYPE 2>/dev/null | awk '$3=="disk"{print $1"("$2")"}' | tr '\n' ' ' || true)
  usb_n=$(lsusb 2>/dev/null | wc -l | tr -d ' ' || echo 0)
  pci_n=$(lspci 2>/dev/null | wc -l | tr -d ' ' || echo 0)
  local tC=0 z raw
  for z in /sys/class/thermal/thermal_zone*/temp /sys/class/hwmon/hwmon*/temp*_input; do
    [[ -r "$z" ]] || continue
    raw=$(cat "$z" 2>/dev/null || echo 0)
    raw=$(n0 "$raw")
    if (( raw > 1000 )); then raw=$((raw / 1000)); fi
    if (( raw >= 25 && raw <= 105 )); then tC=$raw; break; fi
  done
  local load1
  load1=$(awk '{print $1}' /proc/loadavg)

  HW_BOARD=$board HW_VENDOR=$vendor HW_PRODUCT=$product HW_BIOS=$bios
  HW_CPU=$cpu HW_CORES=$cores HW_MEM_GB=$mem_gb HW_GPU=$gpu
  HW_NICS=$nics HW_DISKS=$disks HW_USB=$usb_n HW_PCI=$pci_n
  HW_TEMP=$tC HW_LOAD=$load1

  # inventory · || true on pipes (pipefail SIGPIPE from head)
  {
    echo "ts=$TS"
    echo "board=$vendor $board · product=$product · bios=$bios"
    echo "cpu=$cpu · cores=$cores"
    echo "mem_gb=$mem_gb · gpu=$gpu"
    echo "nics=$nics · usb=$usb_n · pci=$pci_n"
    echo "disks=$disks"
    echo "temp_c=$tC · load1=$load1"
    echo "--- cpu ---"; lscpu 2>/dev/null | head -25 || true
    echo "--- memory ---"; free -h 2>/dev/null || true
    echo "--- block ---"; lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT 2>/dev/null | head -40 || true
    echo "--- pci top ---"; lspci 2>/dev/null | head -40 || true
    echo "--- usb ---"; lsusb 2>/dev/null | head -30 || true
    echo "--- net ---"; ip -br addr 2>/dev/null || true
  } >"$OUT/h7sense/hw_inventory.txt" || true
}

# ── nighthawk router ──
learn_nighthawk() {
  local edge="${NIGHTHAWK_EDGE:-192.168.1.1}"
  local iface="${NIGHTHAWK_IFACE:-enp10s0}"
  local edge_ok=false inet_ok=false edge_net=false http_code=000
  local ip_now gw_now carrier=0

  ip link show "$iface" >/dev/null 2>&1 || iface=$(ip -br link | awk '$1!="lo"{print $1; exit}')
  ip_now=$(ip -4 -br addr show dev "$iface" 2>/dev/null | awk '{print $3}' | head -1)
  gw_now=$(ip route 2>/dev/null | awk '/default/{print $3; exit}')
  carrier=$(cat "/sys/class/net/$iface/carrier" 2>/dev/null || echo 0)

  if [[ "$ip_now" == 192.168.1.* ]]; then edge_net=true; fi
  if ping -c1 -W2 "$edge" >/dev/null 2>&1; then edge_ok=true; fi
  if ping -c1 -W3 1.1.1.1 >/dev/null 2>&1; then inet_ok=true; fi
  if command -v curl >/dev/null 2>&1; then
    # curl -w prints code even on fail · do not append extra 000
    http_code=$(curl -sS -m 3 -o /dev/null -w '%{http_code}' "http://${edge}/" 2>/dev/null || true)
    http_code=$(n0 "$http_code"); http_code=${http_code:-000}
    # normalize to 3 digits
    [[ ${#http_code} -gt 3 ]] && http_code=${http_code: -3}
  fi

  local fw="?"
  [[ -f "$OUT/NIGHTHAWK_FOUNDATION.txt" ]] && fw=$(head -1 "$OUT/NIGHTHAWK_FOUNDATION.txt")
  local board_nh="R6900v2"
  grep -q 'board=R6900' "$OUT/NIGHTHAWK_FOUNDATION.txt" 2>/dev/null && board_nh=$(grep '^board=' "$OUT/NIGHTHAWK_FOUNDATION.txt" | head -1 | cut -d= -f2)

  NH_EDGE=$edge NH_IFACE=$iface NH_IP=$ip_now NH_GW=$gw_now
  NH_CARRIER=$carrier NH_EDGE_OK=$edge_ok NH_INET_OK=$inet_ok
  NH_EDGE_NET=$edge_net NH_HTTP=$http_code NH_FW=$fw NH_BOARD=$board_nh

  {
    echo "ts=$TS"
    echo "edge=$edge edge_ok=$edge_ok edge_net=$edge_net http=$http_code"
    echo "iface=$iface ip=$ip_now gw=$gw_now carrier=$carrier inet_ok=$inet_ok"
    echo "firmware=$fw board=$board_nh"
    echo "mode_hint=$(if $edge_net; then echo 'on_router_LAN'; else echo 'ISP_or_other · EDGE may fail if not on 192.168.1.x'; fi)"
  } >"$OUT/h7sense/nighthawk.txt"
}

# ── USB camera ──
learn_cam() {
  local devs=() names="" n=0 grab_ok=false frame=""
  for d in /dev/video*; do
    [[ -e "$d" ]] || continue
    devs+=("$d"); n=$((n+1))
  done
  if command -v v4l2-ctl >/dev/null 2>&1; then
    names=$(v4l2-ctl --list-devices 2>/dev/null | head -20 | tr '\n' ';' | sed 's/"/\\"/g')
  fi
  # short grab if H7_SENSE_CAM=1 or once mode
  if [[ "${H7_SENSE_CAM:-0}" == "1" || "$cmd" == "once" || "$cmd" == "cam" ]]; then
    if command -v ffmpeg >/dev/null 2>&1 && [[ -e /dev/video0 ]]; then
      frame="$OUT/h7sense/cam_frame.jpg"
      if timeout 8 ffmpeg -y -f v4l2 -video_size 640x480 -i /dev/video0 -frames:v 1 -q:v 4 "$frame" >/dev/null 2>&1; then
        grab_ok=true
      fi
    fi
  fi
  CAM_N=$n CAM_DEVS="${devs[*]:-}" CAM_NAMES=$names CAM_GRAB=$grab_ok CAM_FRAME=${frame:-}
}

# ── hearing / audio capture ──
learn_hear() {
  local cards="" cap_devs=() n_cap=0 level=0 rec_ok=false
  cards=$(cat /proc/asound/cards 2>/dev/null | tr '\n' ';' | sed 's/"/\\"/g' | head -c 400)
  for d in /dev/snd/pcm*c; do
    [[ -e "$d" ]] || continue
    cap_devs+=("$d"); n_cap=$((n_cap+1))
  done
  if [[ "${H7_SENSE_HEAR:-0}" == "1" || "$cmd" == "once" || "$cmd" == "hear" ]]; then
    if command -v arecord >/dev/null 2>&1 && [[ $n_cap -gt 0 ]]; then
      local wav="$OUT/h7sense/hear_sample.wav"
      # prefer USB CAMERA card (card 1) · then board mic · then default
      local try_dev
      for try_dev in plughw:1,0 plughw:CAMERA,0 plughw:3,0 default; do
        if timeout 4 arecord -q -d 1 -f S16_LE -r 16000 -c 1 -D "$try_dev" "$wav" 2>/dev/null; then
          rec_ok=true
          break
        fi
      done
      if [[ "$rec_ok" == "true" ]] && [[ -s "$wav" ]]; then
        level=$(python3 - "$wav" <<'PY' 2>/dev/null || echo 0
import sys,wave,struct,math
w=wave.open(sys.argv[1],"rb")
n=w.getnframes(); raw=w.readframes(n); w.close()
if not raw: print(0); raise SystemExit
fmt="<%dh"%(len(raw)//2)
samples=struct.unpack(fmt, raw[:len(raw)//2*2])
if not samples: print(0); raise SystemExit
rms=math.sqrt(sum(s*s for s in samples)/len(samples))
print(int(min(100, rms/200)))
PY
)
      fi
    fi
  fi
  HEAR_CARDS=$cards HEAR_CAP_N=$n_cap HEAR_OK=$rec_ok HEAR_LEVEL=$(n0 "$level")
}

# ── OCR (desk or camera frame) ──
learn_ocr() {
  local ok=false chars=0 lines=0 preview="" mode="skip"
  if [[ "${H7_SENSE_OCR:-0}" != "1" && "$cmd" != "ocr" && "$cmd" != "once" ]]; then
    # light: read latest only
    if [[ -f "$OUT/desk-ocr-latest.json" ]]; then
      chars=$(python3 -c "import json;d=json.load(open('$OUT/desk-ocr-latest.json'));print(d.get('chars',0))" 2>/dev/null || echo 0)
      lines=$(python3 -c "import json;d=json.load(open('$OUT/desk-ocr-latest.json'));print(d.get('lines',0))" 2>/dev/null || echo 0)
      preview=$(python3 -c "import json;d=json.load(open('$OUT/desk-ocr-latest.json'));print((d.get('preview') or '')[:160].replace('\"','\\\"'))" 2>/dev/null || echo "")
      mode="latest"
      ok=true
    fi
    OCR_OK=$ok OCR_CHARS=$chars OCR_LINES=$lines OCR_PREVIEW=$preview OCR_MODE=$mode
    return
  fi
  mode="screen"
  if [[ -x "$ROOT/Build/desk-ocr.sh" ]]; then
    if timeout 25 bash "$ROOT/Build/desk-ocr.sh" screen >/dev/null 2>&1; then
      ok=true
    fi
  fi
  # also OCR camera frame if present
  if [[ -f "${CAM_FRAME:-}" && -x /usr/bin/tesseract ]]; then
    tesseract "$CAM_FRAME" "$OUT/h7sense/cam_ocr" -l eng --psm 6 >/dev/null 2>&1 || true
    if [[ -f "$OUT/h7sense/cam_ocr.txt" ]]; then
      mode="screen+cam"
      ok=true
    fi
  fi
  if [[ -f "$OUT/desk-ocr-latest.json" ]]; then
    chars=$(python3 -c "import json;d=json.load(open('$OUT/desk-ocr-latest.json'));print(d.get('chars',0))" 2>/dev/null || echo 0)
    lines=$(python3 -c "import json;d=json.load(open('$OUT/desk-ocr-latest.json'));print(d.get('lines',0))" 2>/dev/null || echo 0)
    preview=$(python3 -c "import json;d=json.load(open('$OUT/desk-ocr-latest.json'));print((d.get('preview') or '')[:160].replace(chr(10),' ').replace('\"','\\\"'))" 2>/dev/null || echo "")
  fi
  OCR_OK=$ok OCR_CHARS=$(n0 "$chars") OCR_LINES=$(n0 "$lines") OCR_PREVIEW=$preview OCR_MODE=$mode
}

# inject a few safe system words into vocab (persistent _lang)
seed_vocab() {
  [[ -f "$VOCAB" ]] || return 0
  local known tmp w
  known=$(mktemp)
  cut -f1 "$VOCAB" | tr '[:upper:]' '[:lower:]' | sort -u >"$known"
  tmp=$(mktemp)
  for w in motherboard asrock x870 ryzen nighthawk router camera hearing ocr \
           usb_cam thermal carrier ethernet firmware sense system inventory; do
    grep -qxF "$w" "$known" 2>/dev/null && continue
    printf '%s\t%s\t%s\t%s\t%s\n' "$w" "_lang" "SYSTEM" "1" "sense-system seed" >>"$tmp"
  done
  if [[ -s "$tmp" ]]; then
    cat "$tmp" >>"$VOCAB"
    LC_ALL=C sort -t$'\t' -k1,1 -u "$VOCAB" -o "$VOCAB"
  fi
  rm -f "$known" "$tmp"
}

write_json() {
  local comfortable=true
  # comfort = sensors readable + no public dirt required here
  [[ "${HW_BOARD:-?}" == "?" ]] && comfortable=false

  # escape preview for json
  local prev_esc
  prev_esc=$(printf '%s' "${OCR_PREVIEW:-}" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read())[1:-1])' 2>/dev/null || echo "")

  cat >"$JSON" <<EOF
{
  "ts": "$TS",
  "word": "H7_SENSE_SYSTEM",
  "she": "Hostess 7",
  "ok": true,
  "comfortable": $comfortable,
  "mode": "$cmd",
  "hardware": {
    "board": "${HW_VENDOR:-} ${HW_BOARD:-}",
    "product": "${HW_PRODUCT:-}",
    "bios": "${HW_BIOS:-}",
    "cpu": "${HW_CPU:-}",
    "cores": $(n0 "${HW_CORES:-0}"),
    "mem_gb": ${HW_MEM_GB:-0},
    "gpu": "${HW_GPU:-}",
    "nics": $(n0 "${HW_NICS:-0}"),
    "usb_devices": $(n0 "${HW_USB:-0}"),
    "pci_devices": $(n0 "${HW_PCI:-0}"),
    "disks": "${HW_DISKS:-}",
    "temp_c": $(n0 "${HW_TEMP:-0}"),
    "load1": "${HW_LOAD:-0}",
    "inventory": "out/h7sense/hw_inventory.txt"
  },
  "nighthawk": {
    "board": "${NH_BOARD:-R6900v2}",
    "edge": "${NH_EDGE:-192.168.1.1}",
    "iface": "${NH_IFACE:-}",
    "ip": "${NH_IP:-}",
    "gateway": "${NH_GW:-}",
    "carrier": $(n0 "${NH_CARRIER:-0}"),
    "edge_ok": $(jbool "${NH_EDGE_OK:-false}"),
    "edge_net": $(jbool "${NH_EDGE_NET:-false}"),
    "inet_ok": $(jbool "${NH_INET_OK:-false}"),
    "http": "${NH_HTTP:-000}",
    "firmware": "${NH_FW:-}",
    "note": "EDGE_OK needs PC on router LAN 192.168.1.x · ISP DHCP is different path"
  },
  "camera": {
    "devices_n": $(n0 "${CAM_N:-0}"),
    "devices": "${CAM_DEVS:-}",
    "names": "${CAM_NAMES:-}",
    "grab_ok": $(jbool "${CAM_GRAB:-false}"),
    "frame": "${CAM_FRAME:-}"
  },
  "hearing": {
    "capture_devs": $(n0 "${HEAR_CAP_N:-0}"),
    "cards": "${HEAR_CARDS:-}",
    "sample_ok": $(jbool "${HEAR_OK:-false}"),
    "level": $(n0 "${HEAR_LEVEL:-0}")
  },
  "ocr": {
    "ok": $(jbool "${OCR_OK:-false}"),
    "mode": "${OCR_MODE:-skip}",
    "chars": $(n0 "${OCR_CHARS:-0}"),
    "lines": $(n0 "${OCR_LINES:-0}"),
    "preview": "$prev_esc"
  },
  "paths": {
    "json": "out/h7_sense_system.json",
    "hw": "out/h7sense/hw_inventory.txt",
    "nighthawk": "out/h7sense/nighthawk.txt"
  },
  "law": "OCR + hearing + USB cam + motherboard + full system + Nighthawk · iron sense · never ML · God Bless"
}
EOF

  {
    printf '%s\t%s\t%s\t%s\n' "$TS" "sense_system" "SYSTEM" "learn"
    printf '%s\t%s\t%s\t%s\n' "$TS" "board_${HW_BOARD// /_}" "SYSTEM" "scroll"
    printf '%s\t%s\t%s\t%s\n' "$TS" "nighthawk_$(if [[ "${NH_EDGE_OK:-false}" == "true" ]]; then echo ok; else echo edge; fi)" "SYSTEM" "scroll"
  } >>"$FEED" 2>/dev/null || true
}

run_light() {
  learn_hw
  learn_nighthawk
  # cam/hear inventory only
  H7_SENSE_CAM=0 H7_SENSE_HEAR=0
  learn_cam
  learn_hear
  learn_ocr  # latest only
  seed_vocab
  write_json
  say "light · board=${HW_VENDOR} ${HW_BOARD} · cam=${CAM_N} · hear_cap=${HEAR_CAP_N} · edge_ok=${NH_EDGE_OK} · ocr_chars=${OCR_CHARS:-0}"
  cat "$JSON"
}

run_once() {
  learn_hw
  learn_nighthawk
  H7_SENSE_CAM=1 learn_cam
  H7_SENSE_HEAR=1 learn_hear
  H7_SENSE_OCR="${H7_SENSE_OCR:-0}" learn_ocr
  seed_vocab
  write_json
  say "once · full sense pass"
  cat "$JSON"
}

status() {
  if [[ -f "$JSON" ]]; then cat "$JSON"; else say "no sense yet · run light|once"; fi
  echo "--- hw head ---"
  head -12 "$OUT/h7sense/hw_inventory.txt" 2>/dev/null || true
  echo "--- nighthawk ---"
  cat "$OUT/h7sense/nighthawk.txt" 2>/dev/null || true
}

case "$cmd" in
  light) run_light ;;
  once) run_once ;;
  hw) learn_hw; learn_nighthawk; write_json; cat "$JSON" ;;
  cam) cmd=cam; H7_SENSE_CAM=1; learn_hw; learn_cam; write_json; cat "$JSON" ;;
  hear) cmd=hear; H7_SENSE_HEAR=1; learn_hw; learn_hear; write_json; cat "$JSON" ;;
  ocr) cmd=ocr; H7_SENSE_OCR=1; learn_ocr; write_json; cat "$JSON" ;;
  nighthawk) learn_nighthawk; write_json; cat "$JSON" ;;
  status) status ;;
  *)
    echo "usage: $0 light|once|hw|cam|hear|ocr|nighthawk|status" >&2
    exit 2
    ;;
esac
