#!/usr/bin/env bash
# discover-gaps.sh — find devices in discovery TSVs that aren't in
# servers/*/ssh-target (the authoritative "managed hosts" list).
#
# Input: one or more TSV files, each a line of:
#   IP  MAC  HOSTNAME  (EXTRA1)  (EXTRA2)  … SOURCE
# Produced by discover-fortigate.sh, discover-unifi.sh, or equivalent.
#
# Usage:
#   scripts/discover-gaps.sh [--ignore-unifi] leases.tsv [more.tsv ...]
#
# Flags:
#   --ignore-unifi   Drop rows whose final column starts with "unifi:".
#                    Useful when you want to focus on servers / IoT /
#                    endpoints rather than UniFi infrastructure
#                    (APs, switches, UDMs).
#
# An IP is considered "known" if it matches any of:
#   - an IPv4 in servers/<host>/ssh-target
#   - a resolvable hostname in ssh-target
#   - any IPv4 token found in servers/<host>/README.md
#   - any IPv4 token in servers/<host>/proxmox-details.txt
#     (so Proxmox-tracked VM IPs surfaced by proxmox_inspect.sh are
#      automatically absorbed)
#
# Exit code:
#   0 if zero gaps found
#   1 if one or more unmanaged IPs detected (useful for cron alerting)

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
SERVERS_DIR="$REPO_ROOT/servers"

IGNORE_UNIFI=0
FILES=()
for arg in "$@"; do
  case "$arg" in
    --ignore-unifi) IGNORE_UNIFI=1 ;;
    -h|--help)
      sed -n '2,30p' "$0"
      exit 0
      ;;
    -*)
      echo "error: unknown flag $arg" >&2
      exit 2
      ;;
    *)
      FILES+=("$arg")
      ;;
  esac
done

if [ "${#FILES[@]}" -lt 1 ]; then
  echo "usage: $(basename "$0") [--ignore-unifi] <leases.tsv> [more.tsv ...]" >&2
  exit 2
fi

# ---- Build the set of managed IPs ---------------------------------------

# 1. servers/*/ssh-target
managed_ips=$(
  for stf in "$SERVERS_DIR"/*/ssh-target; do
    [ -r "$stf" ] || continue
    target=$(awk 'NF{print $1; exit}' "$stf")
    val=${target##*@}
    if [[ "$val" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
      echo "$val"
    else
      ip=$(getent hosts "$val" 2>/dev/null | awk 'NR==1{print $1}')
      [ -n "$ip" ] && echo "$ip"
    fi
  done | sort -u
)

if [ -z "$managed_ips" ]; then
  echo "warning: no managed IPs found under $SERVERS_DIR/*/ssh-target" >&2
fi

# 2. Any IPv4 in README.md files under servers/
readme_ips=$(
  grep -rhEo '([0-9]+\.){3}[0-9]+' "$SERVERS_DIR"/*/README.md 2>/dev/null \
    | sort -u || true
)

# 3. Any IPv4 in proxmox-details.txt (VM IPs + cluster-resources table)
pve_ips=$(
  grep -rhEo '([0-9]+\.){3}[0-9]+' "$SERVERS_DIR"/*/proxmox-details.txt 2>/dev/null \
    | sort -u || true
)

# 4. Any IPv4 in system-details.txt (ip addr show output, docker inspect, etc.)
sys_ips=$(
  grep -rhEo '([0-9]+\.){3}[0-9]+' "$SERVERS_DIR"/*/system-details.txt 2>/dev/null \
    | sort -u || true
)

# Drop meaningless addresses from the "known" set (they'd falsely mask
# real unmanaged hosts that happen to also be 127.0.0.1 in some probe).
all_known=$(
  printf '%s\n%s\n%s\n%s\n' "$managed_ips" "$readme_ips" "$pve_ips" "$sys_ips" \
    | grep -Ev '^(0\.0\.0\.0|127\.|255\.255\.255\.255|169\.254\.)' \
    | sort -u
)

# ---- Filter discovery TSVs ---------------------------------------------
unmanaged=$(
  cat "${FILES[@]}" | awk -F'\t' -v known="$all_known" -v drop_unifi="$IGNORE_UNIFI" '
    BEGIN {
      n = split(known, arr, "\n")
      for (i=1; i<=n; i++) if (arr[i] != "") k[arr[i]] = 1
    }
    # Need a valid IP in column 1 and IP must not be "known".
    NF >= 1 && $1 ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ && !($1 in k) {
      if (drop_unifi && $NF ~ /^unifi:/) next
      print
    }
  ' | sort -t$'\t' -k1,1 -V -u
)

if [ -z "$unmanaged" ]; then
  echo "no unmanaged devices — every discovered IP is tracked under servers/" >&2
  exit 0
fi

printf '%s\n' "$unmanaged"
exit 1
