#!/usr/bin/env bash
# refresh-proxmox-info.sh — pull a fresh proxmox-details.txt from PVE nodes.
#
# Companion to refresh-server-info.sh. Same discovery pattern (hosts under
# servers/*/, ssh-target fallback), but pipes scripts/proxmox_inspect.sh
# over SSH and writes to servers/<host>/proxmox-details.txt.
#
# Host selection:
#   - `all` expands to every servers/*/ dir name matching `*-pve` or whose
#     existing proxmox-details.txt suggests it's a PVE node. Use the name
#     explicitly to target a PVE host that doesn't match the pattern.
#   - Explicit names are never filtered — they're trusted.
#
# For each host:
#   1. Run scripts/proxmox_inspect.sh on the remote via `ssh <target> 'bash -s'`.
#   2. Write output atomically to servers/<host>/proxmox-details.txt.
#      A failed SSH/run never clobbers the previous good snapshot.
#
# Exit status is non-zero if any host failed.
#
# Usage:
#   scripts/refresh-proxmox-info.sh                         show this help
#   scripts/refresh-proxmox-info.sh all                     refresh every PVE host
#   scripts/refresh-proxmox-info.sh pfi-pve                 refresh one host
#   scripts/refresh-proxmox-info.sh pfi-pve nh3-pve         refresh several
#   scripts/refresh-proxmox-info.sh --dry-run all           preview, no ssh
#   scripts/refresh-proxmox-info.sh --validate-only all     checks only
#
# Validation surfaces problems in the captured snapshot — missing `=====
# DONE =====` trailer, absent `pveversion` output (wrong script pointed at
# a non-PVE host), empty guest list when /etc/pve/jobs.cfg mentions vmids
# (backup job targeting retired VMs), etc.

set -euo pipefail

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

if [ ! -f "$INSPECT" ]; then
  echo "error: $INSPECT not found" >&2
  exit 2
fi

DRY_RUN=0
VALIDATE_ONLY=0
REQUESTED=()
for arg in "$@"; do
  case "$arg" in
    --dry-run) DRY_RUN=1 ;;
    --validate-only|--validate) VALIDATE_ONLY=1 ;;
    -h|--help)
      sed -n '2,32p' "$0"
      exit 0
      ;;
    -*) echo "error: unknown flag $arg" >&2; exit 2 ;;
    *)  REQUESTED+=("$arg") ;;
  esac
done

discover_proxmox_hosts() {
  # Default discovery: match names containing `-pve` either at the end
  # (e.g. pfi-pve, nh3-pve, esh-pve) or as a segment (e.g. esh-pve-nas).
  # Hosts with other naming that happen to be PVE should be called by
  # name; they're outside the fleet-wide `all` sweep.
  find "$SERVERS_DIR" -mindepth 1 -maxdepth 1 -type d -printf '%f\n' \
    | awk '/-pve(-|$)/' | sort
}

# No positional args → show help. Fleet-wide runs must be explicit.
if [ "${#REQUESTED[@]}" -eq 0 ]; then
  sed -n '2,32p' "$0"
  exit 0
fi

if [ "${#REQUESTED[@]}" -eq 1 ] && [ "${REQUESTED[0]}" = "all" ]; then
  mapfile -t HOSTS < <(discover_proxmox_hosts)
else
  for r in "${REQUESTED[@]}"; do
    if [ "$r" = "all" ]; then
      echo "error: 'all' must be the only argument when used" >&2
      exit 2
    fi
  done
  HOSTS=("${REQUESTED[@]}")
fi

if [ "${#HOSTS[@]}" -eq 0 ]; then
  echo "error: no PVE hosts found (expected servers/*-pve/ dirs under $SERVERS_DIR)" >&2
  exit 2
fi

resolve_target() {
  # Identical logic to refresh-server-info.sh: ssh-target file wins when
  # present, then ssh_config + /etc/hosts, finally the dir name as-is.
  # Proxmox hosts in this fleet typically use `root@<name>` via key auth.
  local host="$1"
  local fallback="$SERVERS_DIR/$host/ssh-target"
  if [ -f "$fallback" ]; then
    local target
    target=$(awk 'NF{print $1; exit}' "$fallback")
    if [ -n "$target" ]; then echo "$target"; return; fi
  fi
  local effective
  effective=$(ssh -G "$host" 2>/dev/null | awk '/^hostname /{print $2; exit}')
  if [ -n "$effective" ] && getent hosts "$effective" >/dev/null 2>&1; then
    echo "$host"
    return
  fi
  echo "$host"
}

validate_host() {
  local host="$1"
  local dir="$SERVERS_DIR/$host"

  if [ ! -d "$dir" ]; then
    printf '%s\n' "dir missing: $dir"
    return
  fi
  if [ ! -r "$dir" ] || [ ! -x "$dir" ]; then
    printf '%s\n' "dir not readable/searchable (check permissions)"
    return
  fi

  local stf="$dir/ssh-target"
  if [ -e "$stf" ]; then
    if [ ! -f "$stf" ]; then
      printf '%s\n' "ssh-target is not a regular file"
    elif [ ! -r "$stf" ]; then
      printf '%s\n' "ssh-target not readable"
    elif [ ! -s "$stf" ]; then
      printf '%s\n' "ssh-target is empty"
    else
      local t
      t=$(awk 'NF{print $1; exit}' "$stf" 2>/dev/null || true)
      if [ -z "$t" ]; then
        printf '%s\n' "ssh-target has no non-blank content"
      elif [[ "$t" =~ [[:space:]] ]]; then
        printf '%s\n' "ssh-target first token contains whitespace ('$t')"
      fi
    fi
  fi

  local effective=""
  effective=$(ssh -G "$host" 2>/dev/null | awk '/^hostname /{print $2; exit}')
  local resolves=0
  if [ -n "$effective" ] && getent hosts "$effective" >/dev/null 2>&1; then
    resolves=1
  fi
  if [ "$resolves" -eq 0 ] && [ ! -s "$stf" ]; then
    printf '%s\n' "name '$host' does not resolve and no ssh-target fallback present"
  fi

  [ -f "$dir/README.md" ] || printf '%s\n' "README.md missing"
  if [ ! -f "$dir/proxmox-details.txt" ]; then
    printf '%s\n' "proxmox-details.txt missing (never refreshed)"
  else
    validate_snapshot "$dir/proxmox-details.txt"
  fi
}

validate_snapshot() {
  local file="$1"
  if ! grep -q '^===== DONE =====' "$file"; then
    printf '%s\n' "snapshot appears truncated (missing trailing '===== DONE =====' marker)"
  fi
  if ! grep -q '^PVE:' "$file"; then
    printf '%s\n' "PVE version line missing — snapshot may have been captured against a non-PVE host"
  fi
  if grep -q 'pveversion not found' "$file"; then
    printf '%s\n' "remote reports pveversion not found — target is not a Proxmox node"
  fi
  # Backup coverage sanity: flag any VERDICT=NO lines so gaps surface.
  if grep -qE '^\s*[0-9]+\s+NO\s' "$file"; then
    local n
    n=$(grep -cE '^\s*[0-9]+\s+NO\s' "$file")
    printf '%s\n' "backup coverage: $n guest(s) marked NO — review 'per-guest coverage' section"
  fi
}

print_warnings() {
  local indent="$1"; shift
  local w
  for w in "$@"; do
    printf '%s! %s\n' "$indent" "$w"
  done
}

pad=0
for h in "${HOSTS[@]}"; do (( ${#h} > pad )) && pad=${#h}; done

if [ "$VALIDATE_ONLY" -eq 1 ]; then
  printf 'Validating %d PVE host(s):\n' "${#HOSTS[@]}"
  total_warn=0
  for host in "${HOSTS[@]}"; do
    mapfile -t warnings < <(validate_host "$host")
    if [ "${#warnings[@]}" -eq 0 ]; then
      printf '  %-*s  ok\n' "$pad" "$host"
    else
      printf '  %-*s  %d warning(s)\n' "$pad" "$host" "${#warnings[@]}"
      print_warnings "      " "${warnings[@]}"
      total_warn=$((total_warn + ${#warnings[@]}))
    fi
  done
  if [ "$total_warn" -gt 0 ]; then exit 1; fi
  exit 0
fi

printf 'Refreshing %d PVE host(s):\n' "${#HOSTS[@]}"
failed=()
for host in "${HOSTS[@]}"; do
  out="$SERVERS_DIR/$host/proxmox-details.txt"
  tmp="$out.new"

  mapfile -t warnings < <(validate_host "$host")

  target=$(resolve_target "$host")
  if [ "$target" = "$host" ]; then
    label="$host"
  else
    label="$host → $target"
  fi

  printf '  %-*s  ' "$pad" "$label"

  if [ "$DRY_RUN" -eq 1 ]; then
    printf 'would run: ssh %s bash -s < %s > %s\n' "$target" "$INSPECT" "$out"
    [ "${#warnings[@]}" -gt 0 ] && print_warnings "      " "${warnings[@]}"
    continue
  fi

  mkdir -p "$SERVERS_DIR/$host"

  if ssh -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new "$target" 'bash -s' < "$INSPECT" > "$tmp" 2> "$tmp.err"; then
    mv "$tmp" "$out"
    rm -f "$tmp.err"
    bytes=$(wc -c < "$out")
    printf 'ok (%s bytes)\n' "$bytes"
  else
    rc=$?
    rm -f "$tmp"
    err=$(head -n 1 "$tmp.err" 2>/dev/null || true)
    rm -f "$tmp.err"
    printf 'FAIL (rc=%d) %s\n' "$rc" "$err"
    failed+=("$host")
  fi

  [ "${#warnings[@]}" -gt 0 ] && print_warnings "      " "${warnings[@]}"
done

if [ "${#failed[@]}" -gt 0 ]; then
  printf '\n%d host(s) failed: %s\n' "${#failed[@]}" "${failed[*]}" >&2
  exit 1
fi
