#!/usr/bin/env bash
# uos-mongo.sh — one-shot query of the UniFi OS Server (UOS) Network database.
#
# The UOS is the self-hosted UniFi controller VM "Unifi" on Jupiter (172.16.3.20,
# virsh dom id 1); guest IP 172.16.3.29, Rocky Linux 9. The UniFi Network app
# (ace.jar) + its classic MongoDB `ace` (127.0.0.1:27117) run INSIDE a rootless
# podman container `uosserver` (user uosserver, uid 1000). There is no mongo
# client on the host — the shell lives at /usr/bin/mongo *inside* the container.
# Access is by SSH key (our standard key is already authorized as root on .29).
#
# This is the single-shot path: local stdin (Mongo JS) -> ssh root@.29
#   -> su - uosserver -> podman exec -i uosserver mongo --port 27117 ace.
#
# Usage:
#   echo 'db.device.count()' | bash .claude/scripts/uos-mongo.sh
#   bash .claude/scripts/uos-mongo.sh < query.js
#   bash .claude/scripts/uos-mongo.sh --find-mac 36:c4     # search device+user by MAC suffix, all sites
#   bash .claude/scripts/uos-mongo.sh --sites              # list all sites (_id -> name)
#
# Env overrides: UOS_HOST (default 172.16.3.29), UOS_SSH_USER (default root).
#
# Notes:
#  - Cloud Site Manager key (vault infrastructure/unifi-site-manager-api) hits
#    api.ui.com for ADOPTED devices only; it does NOT auth the local integration
#    API (401). The local integration key "Claude" (ace.api_key) is hashed/
#    unrecoverable — this Mongo path is the reliable read.
#  - Unadopted/pending devices: the controller only persists DISCOVERED devices
#    into `device` with adopted:false. If `db.device.count({adopted:false})` is 0,
#    there are no pending devices controller-wide (nothing reaching it to adopt).
#  - `ace` collections of interest: device (adopted infra), user (clients/stations),
#    rogue (neighbor/over-the-air BSSIDs — NOT your gear), site (_id->desc name map).
set -euo pipefail

UOS_HOST="${UOS_HOST:-172.16.3.29}"
UOS_SSH_USER="${UOS_SSH_USER:-root}"

# Resolve the SSH key. Prefer $UOS_SSH_KEY; otherwise extract the vaulted dedicated
# UOS key (infrastructure/uos-server-ssh-key, stored base64 in ssh-private-key-b64
# because vault-helper can't store multiline) to a temp file so this works on ANY
# fleet machine, not just one whose personal key happens to be authorized on .29.
_uos_key=""
if [ -n "${UOS_SSH_KEY:-}" ]; then
    _uos_key="$UOS_SSH_KEY"
else
    _repo="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
    _vault="$_repo/.claude/scripts/vault.sh"
    if [ -f "$_vault" ]; then
        _uos_key="$(mktemp "${TMPDIR:-/tmp}/uos-key.XXXXXX")"
        bash "$_vault" get-field infrastructure/uos-server-ssh-key credentials.ssh-private-key-b64 2>/dev/null \
            | tr -d '\r\n ' | base64 -d > "$_uos_key" 2>/dev/null
        chmod 600 "$_uos_key" 2>/dev/null || true
        trap '[ -z "${UOS_SSH_KEY:-}" ] && [ -n "$_uos_key" ] && rm -f "$_uos_key"' EXIT
        [ -s "$_uos_key" ] || { rm -f "$_uos_key"; _uos_key=""; }   # fall back to default key
    fi
fi

if [ -n "$_uos_key" ]; then
    SSH=(ssh -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new -o IdentitiesOnly=yes -i "$_uos_key" "${UOS_SSH_USER}@${UOS_HOST}")
else
    SSH=(ssh -o ConnectTimeout=15 -o StrictHostKeyChecking=accept-new "${UOS_SSH_USER}@${UOS_HOST}")
fi
REMOTE='su - uosserver -c "XDG_RUNTIME_DIR=/run/user/1000 podman exec -i uosserver mongo --quiet --port 27117 ace"'

run_js() { "${SSH[@]}" "$REMOTE"; }   # reads Mongo JS from this function's stdin

case "${1:-}" in
  --find-mac)
    suf="${2:?usage: --find-mac <mac-or-suffix, e.g. 36:c4>}"
    run_js <<JS
var sites={}; db.site.find({},{name:1,desc:1}).forEach(function(s){sites[s._id.str]=(s.desc||s.name);});
print("# UOS search: MAC matching /${suf}\$/i across "+Object.keys(sites).length+" sites (device=infra, user=clients)");
["device","user"].forEach(function(c){
  var n=0;
  db[c].find({mac:/${suf}\$/i}).forEach(function(d){
    n++;
    print(c+" | "+d.mac+" | model="+(d.model||d.oui||"?")+" | name="+(d.name||d.hostname||"(none)")
      +" | adopted="+d.adopted+" | ip="+(d.ip||d.last_ip||"-")+" | site="+(sites[d.site_id]||d.site_id));
  });
  print("# "+c+" matches: "+n);
});
print("# pending (adopted:false) devices controller-wide: "+db.device.count({adopted:false}));
JS
    ;;
  --sites)
    run_js <<'JS'
db.site.find({},{name:1,desc:1}).forEach(function(s){ print(s._id.str+"  "+(s.desc||s.name)); });
JS
    ;;
  -h|--help)
    sed -n '2,40p' "$0"
    ;;
  *)
    # pipe arbitrary Mongo JS from stdin
    run_js
    ;;
esac
