#!/usr/bin/env bash
set -euo pipefail

OUTPUT_DIR="${1:-/var/lib/node_exporter/textfile_collector}"
METRIC_FILE="${OUTPUT_DIR}/soria_network.prom"
TMP_FILE="${METRIC_FILE}.$$"

TARGET_ICMP="${TARGET_ICMP:-127.0.0.1}"
TARGET_HTTP="${TARGET_HTTP:-http://127.0.0.1:8080/}"
TARGET_TCP_HOST="${TARGET_TCP_HOST:-127.0.0.1}"
TARGET_TCP_PORT="${TARGET_TCP_PORT:-22}"
CHECK_INTERFACE="${CHECK_INTERFACE:-}"
VRRP_VIP="${VRRP_VIP:-}"

usage() {
  cat <<'EOF'
Usage:
  sudo ./module5c-network-observer.sh [textfile-directory]

Optional environment variables:
  TARGET_ICMP       ICMP target (default: 127.0.0.1)
  TARGET_HTTP       HTTP(S) URL (default: http://127.0.0.1:8080/)
  TARGET_TCP_HOST   TCP target host (default: 127.0.0.1)
  TARGET_TCP_PORT   TCP target port (default: 22)
  CHECK_INTERFACE   Interface to validate; auto-detected from default route
  VRRP_VIP          Optional VIP that must exist locally
EOF
}

escape_label() {
  local value="$1"
  value="${value//\\/\\\\}"
  value="${value//\"/\\\"}"
  value="${value//$'\n'/\\n}"
  printf '%s' "$value"
}

metric() {
  printf '%s\n' "$1" >> "$TMP_FILE"
}

command_exists() {
  command -v "$1" >/dev/null 2>&1
}

probe_icmp() {
  local target label success=0
  target="$TARGET_ICMP"
  label="$(escape_label "$target")"
  if command_exists ping && ping -c 1 -W 1 "$target" >/dev/null 2>&1; then
    success=1
  fi
  metric "# HELP soria_network_probe_success Whether a network probe succeeded."
  metric "# TYPE soria_network_probe_success gauge"
  metric "soria_network_probe_success{probe=\"icmp\",target=\"$label\"} $success"
}

probe_http() {
  local target label result code duration success=0
  target="$TARGET_HTTP"
  label="$(escape_label "$target")"
  result="000 0"
  if command_exists curl; then
    result="$(curl -kfsS -o /dev/null --max-time 3 \
      -w '%{http_code} %{time_total}' "$target" 2>/dev/null || true)"
  fi
  code="${result%% *}"
  duration="${result#* }"
  [[ "$code" =~ ^[23][0-9][0-9]$ ]] && success=1

  metric "soria_network_probe_success{probe=\"http\",target=\"$label\"} $success"
  metric "# HELP soria_network_http_status_code Last observed HTTP status code."
  metric "# TYPE soria_network_http_status_code gauge"
  metric "soria_network_http_status_code{target=\"$label\"} ${code:-0}"
  metric "# HELP soria_network_http_duration_seconds Last HTTP probe duration in seconds."
  metric "# TYPE soria_network_http_duration_seconds gauge"
  metric "soria_network_http_duration_seconds{target=\"$label\"} ${duration:-0}"
}

probe_tcp() {
  local host port label success=0
  host="$TARGET_TCP_HOST"
  port="$TARGET_TCP_PORT"
  label="$(escape_label "${host}:${port}")"
  if command_exists nc && nc -z -w 2 "$host" "$port" >/dev/null 2>&1; then
    success=1
  fi
  metric "soria_network_probe_success{probe=\"tcp\",target=\"$label\"} $success"
}

collect_route_and_interface() {
  local iface iface_label route_present=0 iface_up=0
  if ip route show default 2>/dev/null | grep -q '^default'; then
    route_present=1
  fi

  iface="$CHECK_INTERFACE"
  if [[ -z "$iface" ]]; then
    iface="$(ip route show default 2>/dev/null | awk 'NR==1 {print $5}')"
  fi
  [[ -n "$iface" ]] || iface="none"
  iface_label="$(escape_label "$iface")"

  if [[ "$iface" != "none" && -r "/sys/class/net/${iface}/operstate" ]]; then
    [[ "$(cat "/sys/class/net/${iface}/operstate")" == "up" ]] && iface_up=1
  fi

  metric "# HELP soria_network_default_route_present Whether a default IPv4 route exists."
  metric "# TYPE soria_network_default_route_present gauge"
  metric "soria_network_default_route_present $route_present"
  metric "# HELP soria_network_interface_up Whether the selected interface is operationally up."
  metric "# TYPE soria_network_interface_up gauge"
  metric "soria_network_interface_up{interface=\"$iface_label\"} $iface_up"
}

collect_wireguard() {
  local configured=0 recent=0 latest=0 age=0 now
  if command_exists wg && wg show interfaces 2>/dev/null | grep -q '[^[:space:]]'; then
    configured=1
    latest="$(wg show all latest-handshakes 2>/dev/null | awk '$2 > max {max=$2} END {print max+0}')"
    if [[ "$latest" -gt 0 ]]; then
      now="$(date +%s)"
      age=$((now - latest))
      [[ "$age" -le 180 ]] && recent=1
    fi
  fi

  metric "# HELP soria_wireguard_configured Whether at least one WireGuard interface exists."
  metric "# TYPE soria_wireguard_configured gauge"
  metric "soria_wireguard_configured $configured"
  metric "# HELP soria_wireguard_recent_handshake Whether a WireGuard handshake occurred within 180 seconds."
  metric "# TYPE soria_wireguard_recent_handshake gauge"
  metric "soria_wireguard_recent_handshake $recent"
  metric "# HELP soria_wireguard_latest_handshake_age_seconds Age of the newest WireGuard handshake."
  metric "# TYPE soria_wireguard_latest_handshake_age_seconds gauge"
  metric "soria_wireguard_latest_handshake_age_seconds $age"
}

collect_ospf() {
  local count=0 available=0 output
  if command_exists vtysh; then
    output="$(vtysh -c 'show ip ospf neighbor' 2>/dev/null || true)"
    if [[ -n "$output" ]]; then
      available=1
      count="$(printf '%s\n' "$output" | grep -c 'Full/' || true)"
    fi
  fi

  metric "# HELP soria_ospf_metrics_available Whether OSPF neighbor data could be collected."
  metric "# TYPE soria_ospf_metrics_available gauge"
  metric "soria_ospf_metrics_available $available"
  metric "# HELP soria_ospf_full_neighbors Number of OSPF neighbors in Full state."
  metric "# TYPE soria_ospf_full_neighbors gauge"
  metric "soria_ospf_full_neighbors $count"
}

collect_vrrp_vip() {
  local vip label present=0
  vip="$VRRP_VIP"
  [[ -n "$vip" ]] || return 0
  label="$(escape_label "$vip")"
  if ip -o address show 2>/dev/null | awk '{print $4}' | cut -d/ -f1 | grep -Fxq "$vip"; then
    present=1
  fi
  metric "# HELP soria_vrrp_vip_present Whether the expected VRRP VIP exists locally."
  metric "# TYPE soria_vrrp_vip_present gauge"
  metric "soria_vrrp_vip_present{vip=\"$label\"} $present"
}

main() {
  if [[ "${1:-}" == "--help" || "${1:-}" == "-h" ]]; then
    usage
    exit 0
  fi

  mkdir -p "$OUTPUT_DIR"
  umask 022
  : > "$TMP_FILE"
  trap 'rm -f "$TMP_FILE"' EXIT

  probe_icmp
  probe_http
  probe_tcp
  collect_route_and_interface
  collect_wireguard
  collect_ospf
  collect_vrrp_vip

  metric "# HELP soria_network_observer_last_success_unixtime Unix time of the last successful collector run."
  metric "# TYPE soria_network_observer_last_success_unixtime gauge"
  metric "soria_network_observer_last_success_unixtime $(date +%s)"

  mv "$TMP_FILE" "$METRIC_FILE"
  trap - EXIT
  echo "Metrics écrites dans $METRIC_FILE"
}

main "$@"
