#!/bin/bash
# Deploy lm-sensors extend (native sensors + TEMPer) + snmpd on a host [#341/#439]
#
# Run ON the target host via the remote.sh chokepoint (bash -s). Copy the
# wrapper over first (vm-copy / prox-copy). Idempotent: safe to re-run.
#
# Native lm-sensors (CPU/board temps, fans) is installed on every target.
# The TEMPer USB layer (venv + temperusb + udev rule) is installed ONLY when
# a 0c45:7401 probe is attached.
#
# Usage (from repo root):
#   VM_IP=stlpc-artroom VM_USER=root bash tests/remote.sh vm-copy dcinfra/sensors/temper/lmsensors-extend.sh /tmp/lmsensors-extend.sh
#   VM_IP=stlpc-artroom VM_USER=root bash tests/remote.sh vm-file dcinfra/sensors/temper/deploy.sh
#   PROX_HOST=pfv-tsys1 bash tests/remote.sh prox-copy dcinfra/sensors/temper/lmsensors-extend.sh /tmp/lmsensors-extend.sh
#   PROX_HOST=pfv-tsys1 bash tests/remote.sh prox-file dcinfra/sensors/temper/deploy.sh
#
# snmpd is a targeted exception: sensor hosts + tsys-* Proxmox hosts
# (founder-directed 2026-08-27), not fleet-wide.
set -euo pipefail

COMMUNITY="${SNMP_COMMUNITY:-kn3lmgmt}"
VENV_DIR="${VENV_DIR:-/opt/temper-venv}"
WRAP_SRC="${WRAP_SRC:-/tmp/lmsensors-extend.sh}"
WRAP_DST="/usr/local/bin/lmsensors-extend"
WRAP_DST_OLD="/usr/local/bin/temper-lmsensors"
SNMPD_CONF="/etc/snmp/snmpd.conf"
UDEV_RULE="/etc/udev/rules.d/99-temper.rules"
MARK_BEGIN="# lmsensors-extend managed block begin"
MARK_END="# lmsensors-extend managed block end"
OLD_MARK_BEGIN="# temper-lmsensors managed block begin"
OLD_MARK_END="# temper-lmsensors managed block end"
# Source allowlist (founder ruling 2026-08-27: bind Tailscale-only where
# possible; HA is a Tailscale APP connector so its polls source from its LAN
# IP and must be ACL'd explicitly — no broad subnet grants).
#   192.168.3.176   tsys-librenms LAN (poller)
#   100.86.204.77   tsys-librenms Tailscale
#   192.168.3.12    pfv-bms / Home Assistant LAN (app connector source)
#   100.67.108.125  pfv-bms Tailscale
#   100.101.187.119 admin workstation Tailscale
#   152.53.37.179 + 2a0a:4cc0:2000:686::a496:3bff:fe35:610a  Cloudron VPS
#                  (future Uptime Kuma SNMP polling)
SNMP_SOURCES="${SNMP_SOURCES:-192.168.3.176 100.86.204.77 192.168.3.12 100.67.108.125 100.101.187.119 152.53.37.179 2a0a:4cc0:2000:686:a496:3bff:fe35:610a}"

log() { echo "[deploy] $*"; }

has_probe() {
    command -v lsusb >/dev/null 2>&1 && lsusb | grep -qi '0c45:7401'
}

host_lan_ip() {
    # first 192.168.* address (mgmt/user LAN; excludes 127/10.100 storage/dockers)
    ip -4 addr show 2>/dev/null | grep -oP 'inet \K192\.168\.[0-9.]+' | head -1
}

host_ts_ip() {
    command -v tailscale >/dev/null 2>&1 && tailscale ip -4 2>/dev/null | head -1
}

[[ $EUID -eq 0 ]] || { echo "must run as root" >&2; exit 1; }
[[ -f "$WRAP_SRC" ]] || { echo "wrapper not found at $WRAP_SRC (copy it first)" >&2; exit 1; }

# --- native lm-sensors layer (all hosts) ---
if ! dpkg -s lm-sensors >/dev/null 2>&1; then
    log "installing lm-sensors"
    DEBIAN_FRONTEND=noninteractive apt-get update -qq
    DEBIAN_FRONTEND=noninteractive apt-get install -y -qq lm-sensors
fi
if [[ -z "$(sensors 2>/dev/null)" ]]; then
    log "no sensor readings yet; trying coretemp/k10temp modules"
    modprobe coretemp 2>/dev/null || true
    modprobe k10temp 2>/dev/null || true
fi
log "native sensors: $(sensors 2>/dev/null | grep -c '°C') temp readings"

# --- TEMPer USB layer (probe-conditional) ---
if has_probe; then
    log "TEMPer probe present: installing venv layer"
    if ! dpkg -s python3-venv >/dev/null 2>&1; then
        log "installing python3-venv"
        DEBIAN_FRONTEND=noninteractive apt-get update -qq
        DEBIAN_FRONTEND=noninteractive apt-get install -y -qq python3-venv
    fi
    if [[ ! -x "$VENV_DIR/bin/pip" ]]; then
        [[ -d "$VENV_DIR" ]] && { log "removing broken venv $VENV_DIR"; rm -rf "$VENV_DIR"; }
        log "creating venv $VENV_DIR"
        python3 -m venv "$VENV_DIR"
    fi
    log "ensuring temperusb in venv"
    "$VENV_DIR/bin/pip" install --quiet --disable-pip-version-check temperusb

    # TEMPer USB dongle must be readable by the unprivileged snmpd user
    # (extend runs as Debian-snmp; pyusb needs the raw usb device node).
    log "installing udev rule for 0c45:7401"
    cat >"$UDEV_RULE" <<'EOF'
SUBSYSTEM=="usb", ATTRS{idVendor}=="0c45", ATTRS{idProduct}=="7401", MODE="0666"
EOF
    udevadm control --reload-rules
    udevadm trigger
else
    log "no TEMPer probe: skipping venv/udev layer"
fi

log "installing wrapper to $WRAP_DST"
install -m 0755 "$WRAP_SRC" "$WRAP_DST"
rm -f "$WRAP_DST_OLD"

if ! dpkg -s snmpd >/dev/null 2>&1 || ! command -v snmpget >/dev/null 2>&1; then
    log "installing snmpd + snmp client"
    DEBIAN_FRONTEND=noninteractive apt-get update -qq
    DEBIAN_FRONTEND=noninteractive apt-get install -y -qq snmpd snmp
fi

log "configuring snmpd (community=$COMMUNITY, scoped sources)"
LAN_IP="$(host_lan_ip)"
TS_IP="$(host_ts_ip)"
AGENT_ADDRS=""
[[ -n "$LAN_IP" ]] && AGENT_ADDRS="udp:${LAN_IP}:161"
[[ -n "$TS_IP" ]] && AGENT_ADDRS="${AGENT_ADDRS:+$AGENT_ADDRS,}udp:${TS_IP}:161"
[[ -n "$AGENT_ADDRS" ]] || { echo "no LAN/Tailscale address found; refusing to bind 0.0.0.0" >&2; exit 1; }
sed -i "/^$OLD_MARK_BEGIN\$/,/^$OLD_MARK_END\$/d" "$SNMPD_CONF"
sed -i "/^$MARK_BEGIN\$/,/^$MARK_END\$/d" "$SNMPD_CONF"
sed -i 's/^agentaddress/#agentaddress/' "$SNMPD_CONF"
{
    echo "$MARK_BEGIN"
    echo "agentaddress $AGENT_ADDRS"
    for src in $SNMP_SOURCES; do
        echo "rocommunity $COMMUNITY $src"
    done
    echo "extend lmsensors $WRAP_DST"
    echo "$MARK_END"
} >>"$SNMPD_CONF"
log "agentaddress: $AGENT_ADDRS"

log "restarting snmpd"
systemctl enable --now snmpd >/dev/null
systemctl restart snmpd

log "wrapper output:"
"$WRAP_DST"
log "snmp extend self-test (expect lm-sensors block below):"
sleep 1
snmpget -v2c -c "$COMMUNITY" -Ovq localhost \
    .1.3.6.1.4.1.8072.1.3.2.3.1.2.9.108.109.115.101.110.115.111.114.115 2>/dev/null || \
    log "WARN: snmpget self-test failed (check snmpd logs)"
log "done"
