#!/usr/bin/env bash
# gateway-map.sh — manage + query the persisted UOS-site -> pfSense-cred map (references/site-gateways.tsv).
# This is the "auto-select" half of ROADMAP §E: gw-audit/gw-control call `lookup` to pick the pfSense SSH
# backend for a site automatically (no --pfsense). Humans use list/validate/suggest to keep the map honest.
#
# Usage:
#   gateway-map.sh lookup <site_id|site_name>   # print "<cred_path>\t<port>" for a mapped site (exit 1 if none)
#   gateway-map.sh list                          # show the map (with cred-resolves check)
#   gateway-map.sh validate                      # check every row: 24-hex site_id + cred resolves in vault
#   gateway-map.sh suggest                       # no-UniFi-gw UOS sites NOT yet mapped + unbound pfSense creds
set -uo pipefail
REPO="$(git rev-parse --show-toplevel 2>/dev/null || echo .)"
HERE="$(cd "$(dirname "$0")" && pwd)"
UOS="$REPO/.claude/scripts/uos-mongo.sh"; VAULT="$REPO/.claude/scripts/vault.sh"
MAP="$HERE/../references/site-gateways.tsv"
# Mandatory skill error logging (skill-creator rule): log GENUINE functional failures only.
logerr(){ bash "$REPO/.claude/scripts/log-skill-error.sh" "unifi-wifi/gateway-map" "$1" --context "${2:-}" >/dev/null 2>&1 || true; }
VROOT="${VAULT_ROOT:-$(jq -r '.vault_path // empty' "$REPO/.claude/identity.json" 2>/dev/null)}"
[ -n "$VROOT" ] || VROOT="$REPO/../vault"

ACT="${1:-list}"; ARG="${2:-}"
[ -f "$MAP" ] || { echo "[ERROR] map not found: $MAP"; exit 2; }
is_hex24(){ printf '%s' "$1" | grep -qE '^[0-9a-f]{24}$'; }
cred_ok(){ [ -n "$(bash "$VAULT" get-field "$1" credentials.password 2>/dev/null || bash "$VAULT" get-field "$1" password 2>/dev/null)" ]; }

# iterate data rows -> calls `row <site_id> <cred> <port> <name>`
each_row(){ while IFS=$'\t' read -r sid cred port name; do case "${sid:-}" in ''|'#'*) continue;; esac; "$1" "$sid" "$cred" "${port:-}" "${name:-}"; done < "$MAP"; }

case "$ACT" in
  lookup)
    [ -n "$ARG" ] || { echo "[ERROR] lookup needs <site_id|site_name>" >&2; exit 2; }
    found=""
    while IFS=$'\t' read -r sid cred port name; do
      case "${sid:-}" in ''|'#'*) continue;; esac
      if [ "$sid" = "$ARG" ]; then found="$cred	${port:-}"; break; fi
      if ! is_hex24 "$ARG"; then
        ln=$(printf '%s' "${name:-}" | tr 'A-Z' 'a-z'); lk=$(printf '%s' "$ARG" | tr 'A-Z' 'a-z')
        [ -n "$ln" ] && case "$ln" in *"$lk"*) found="$cred	${port:-}"; break;; esac
      fi
    done < "$MAP"
    [ -n "$found" ] && { printf '%s\n' "$found"; exit 0; } || exit 1
    ;;

  list)
    echo "[INFO] gateway map ($MAP):"
    printf "  %-26s %-42s %-5s %s\n" "site_id" "cred_path" "port" "site_name / cred-check"
    printf "  %-26s %-42s %-5s %s\n" "-------" "---------" "----" "---------"
    _row(){ local sid="$1" cred="$2" port="$3" name="$4" tag
      if cred_ok "$cred"; then tag="[ok]"; else tag="[MISSING CRED]"; fi
      printf "  %-26s %-42s %-5s %s %s\n" "$sid" "$cred" "${port:--}" "$name" "$tag"; }
    each_row _row
    ;;

  validate)
    rc=0
    _row(){ local sid="$1" cred="$2" port="$3" name="$4"
      is_hex24 "$sid" || { echo "[BAD] site_id not 24-hex: '$sid' ($name)"; rc=1; return; }
      if cred_ok "$cred"; then echo "[ok]  $name ($sid) -> $cred"; else echo "[MISSING CRED] $name ($sid) -> $cred"; rc=1; fi; }
    each_row _row
    [ $rc -eq 0 ] && echo "[OK] all rows valid" || echo "[WARNING] some rows need attention"
    exit $rc
    ;;

  suggest)
    echo "[INFO] Cross-referencing controller (no-UniFi-gateway sites) + vault (pfSense creds) vs the map..."
    # mapped site_ids
    mapped="$(grep -vE '^\s*#|^\s*$' "$MAP" | cut -f1 | tr '\n' ' ')"
    # no-UniFi-gateway sites from the controller (site_id<TAB>name)
    third="$(cat <<'JS' | bash "$UOS" 2>/dev/null | grep -viE 'WARNING|post-quantum|store now|upgraded|openssh'
var s={}; db.site.find({},{name:1,desc:1}).forEach(function(x){ s[x._id.str]={n:(x.desc||x.name),gw:0}; });
db.device.find({type:{$in:['ugw','uxg','udm','ucg']}},{site_id:1}).forEach(function(d){ if(s[d.site_id])s[d.site_id].gw++; });
Object.keys(s).forEach(function(id){ if(s[id].gw===0) print(id+"\t"+s[id].n); });
JS
)"
    if [ -z "$third" ]; then echo "[WARNING] no controller data (UOS unreachable?)"; logerr "UOS query returned no sites (suggest)" "uos=$UOS"; fi
    echo
    echo "  No-UniFi-gateway sites NOT yet in the map (candidates to bind):"
    printf '%s\n' "$third" | while IFS=$'\t' read -r id nm; do
      [ -n "$id" ] || continue
      case " $mapped " in *" $id "*) ;; *) echo "    [unmapped] $id  $nm";; esac
    done
    echo
    echo "  Vaulted pfSense creds (clients/*/pfsense-firewall) and whether a map row references them:"
    if [ -d "$VROOT" ]; then
      find "$VROOT/clients" -name 'pfsense-firewall.sops.yaml' 2>/dev/null | sort | while IFS= read -r f; do
        slug=$(printf '%s' "$f" | sed -E 's#.*/clients/([^/]+)/.*#\1#'); cp="clients/$slug/pfsense-firewall"
        if grep -qF "	$cp	" "$MAP" 2>/dev/null || grep -qF "	$cp" "$MAP" 2>/dev/null; then echo "    [bound]   $cp"; else echo "    [UNBOUND] $cp  (add a row binding a site_id to it)"; fi
      done
    fi
    echo
    echo "  To bind: add a TAB-separated row to $MAP:  <site_id>\\t<cred_path>\\t<port|->\\t<name>"
    ;;

  *) echo "usage: gateway-map.sh <lookup|list|validate|suggest> [arg]"; exit 1;;
esac
