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

# a2k-network-info
# Collect the networking details most useful when diagnosing upstream
# configuration problems affecting this node.

DNS_TEST_NAME="${A2K_DNS_TEST_NAME:-example.com}"
PUBLIC_IP_URLS=(
    "https://api.ipify.org"
    "https://ifconfig.me/ip"
    "https://icanhazip.com"
)

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

trim() {
    local value="${1:-}"
    value="${value#"${value%%[![:space:]]*}"}"
    value="${value%"${value##*[![:space:]]}"}"
    printf '%s' "$value"
}

prefix_to_netmask() {
    local prefix="${1:?prefix required}"
    local full_octets=$((prefix / 8))
    local partial_bits=$((prefix % 8))
    local -a octets=(0 0 0 0)
    local i

    for ((i = 0; i < full_octets; i++)); do
        octets[$i]=255
    done

    if ((full_octets < 4 && partial_bits > 0)); then
        octets[$full_octets]=$((256 - (1 << (8 - partial_bits))))
    fi

    printf '%d.%d.%d.%d' "${octets[0]}" "${octets[1]}" "${octets[2]}" "${octets[3]}"
}

print_field() {
    printf '%-22s %s\n' "$1:" "${2:-N/A}"
}

get_default_interface() {
    ip -4 route show default 2>/dev/null | awk 'NR == 1 {for (i=1; i<=NF; i++) if ($i == "dev") {print $(i+1); exit}}'
}

get_default_gateway() {
    ip -4 route show default 2>/dev/null | awk 'NR == 1 {for (i=1; i<=NF; i++) if ($i == "via") {print $(i+1); exit}}'
}

get_effective_dns() {
    local dns=""

    if have resolvectl; then
        # Prefer per-link DNS for the default interface. This avoids reporting only
        # systemd-resolved's 127.0.0.53 stub from /etc/resolv.conf.
        if [[ -n "${DEFAULT_IFACE:-}" ]]; then
            dns="$(resolvectl dns "$DEFAULT_IFACE" 2>/dev/null \
                | sed -n 's/^[^:]*:[[:space:]]*//p' \
                | tr ' ' '\n' \
                | sed '/^$/d' \
                | awk '!seen[$0]++' \
                | paste -sd ', ' - || true)"
        fi

        if [[ -z "$dns" ]]; then
            dns="$(resolvectl dns 2>/dev/null \
                | sed -n 's/^[^:]*:[[:space:]]*//p' \
                | tr ' ' '\n' \
                | sed '/^$/d' \
                | awk '!seen[$0]++' \
                | paste -sd ', ' - || true)"
        fi
    fi

    if [[ -z "$dns" && -r /etc/resolv.conf ]]; then
        dns="$(awk '/^[[:space:]]*nameserver[[:space:]]+/ {print $2}' /etc/resolv.conf \
            | awk '!seen[$0]++' \
            | paste -sd ', ' - || true)"
    fi

    printf '%s' "$dns"
}

get_public_ip() {
    local url ip

    have curl || return 0

    for url in "${PUBLIC_IP_URLS[@]}"; do
        ip="$(curl -4fsS --connect-timeout 2 --max-time 4 "$url" 2>/dev/null | head -n1 || true)"
        ip="$(trim "$ip")"
        if [[ "$ip" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
            printf '%s' "$ip"
            return 0
        fi
    done
}

if ! have ip; then
    printf 'ERROR: iproute2 is required (missing "ip" command).\n' >&2
    exit 1
fi

HOST_SHORT="$(hostname -s 2>/dev/null || hostname 2>/dev/null || true)"
HOST_FQDN="$(hostname -f 2>/dev/null || true)"
[[ -n "$HOST_FQDN" ]] || HOST_FQDN="$HOST_SHORT"

DEFAULT_IFACE="$(get_default_interface)"
DEFAULT_GATEWAY="$(get_default_gateway)"
PUBLIC_IP="$(get_public_ip)"
DNS_SERVERS="$(get_effective_dns)"

printf 'A2K Network Diagnostic Information\n'
printf '==================================\n'
print_field "Hostname" "$HOST_SHORT"
print_field "FQDN" "$HOST_FQDN"
print_field "Default interface" "$DEFAULT_IFACE"
print_field "Default gateway" "$DEFAULT_GATEWAY"
print_field "DNS servers" "$DNS_SERVERS"
print_field "Public IPv4" "${PUBLIC_IP:-Unavailable}"

if getent ahostsv4 "$DNS_TEST_NAME" >/dev/null 2>&1; then
    DNS_TEST_IP="$(getent ahostsv4 "$DNS_TEST_NAME" 2>/dev/null | awk 'NR == 1 {print $1}')"
    print_field "DNS resolution" "PASS ($DNS_TEST_NAME -> $DNS_TEST_IP)"
else
    print_field "DNS resolution" "FAIL ($DNS_TEST_NAME)"
fi

if [[ -n "$HOST_FQDN" ]] && getent ahostsv4 "$HOST_FQDN" >/dev/null 2>&1; then
    HOST_RESOLVED="$(getent ahostsv4 "$HOST_FQDN" 2>/dev/null | awk 'NR == 1 {print $1}')"
    print_field "FQDN resolution" "PASS ($HOST_FQDN -> $HOST_RESOLVED)"
else
    print_field "FQDN resolution" "FAIL ($HOST_FQDN)"
fi

printf '\nInterface Details\n'
printf '%s\n' '-----------------'

while IFS= read -r iface; do
    [[ -n "$iface" ]] || continue

    mac="$(cat "/sys/class/net/$iface/address" 2>/dev/null || true)"
    state="$(cat "/sys/class/net/$iface/operstate" 2>/dev/null || true)"
    mtu="$(cat "/sys/class/net/$iface/mtu" 2>/dev/null || true)"

    printf '\n[%s]%s\n' "$iface" "$([[ "$iface" == "$DEFAULT_IFACE" ]] && printf ' (default)' || true)"
    print_field "Link state" "$state"
    print_field "MAC address" "$mac"
    print_field "MTU" "$mtu"

    addresses="$(ip -o -4 addr show dev "$iface" scope global 2>/dev/null | awk '{print $4}' || true)"
    if [[ -z "$addresses" ]]; then
        print_field "IPv4 address" "None"
        continue
    fi

    while IFS= read -r cidr; do
        [[ -n "$cidr" ]] || continue
        ipaddr="${cidr%/*}"
        prefix="${cidr#*/}"
        netmask="$(prefix_to_netmask "$prefix")"
        network="$(ip -4 route show dev "$iface" proto kernel scope link 2>/dev/null \
            | awk -v ip="$ipaddr" '$0 ~ ("src " ip "([[:space:]]|$)") {print $1; exit}' || true)"

        print_field "IPv4 address" "$ipaddr"
        print_field "Subnet CIDR" "${network:-$cidr}"
        print_field "Prefix length" "/$prefix"
        print_field "Subnet mask" "$netmask"
    done <<< "$addresses"
done < <(ip -o link show 2>/dev/null | awk -F': ' '{print $2}' | sed 's/@.*//' | grep -v '^lo$')

printf '\nRouting\n'
printf '%s\n' '-------'
ip -4 route show 2>/dev/null || true

printf '\nResolver Configuration\n'
printf '%s\n' '----------------------'
if have resolvectl; then
    resolvectl status "${DEFAULT_IFACE:-}" 2>/dev/null || resolvectl status 2>/dev/null || true
elif [[ -r /etc/resolv.conf ]]; then
    cat /etc/resolv.conf
else
    printf 'No resolver configuration available.\n'
fi
