#!/usr/bin/env bash

#####################################################################################
#### vpn-smoke-tests.sh — Verify VPN tunnels are healthy                         ####
#### Checks WireGuard and OpenVPN interface, handshake, peers, DNS, routes, MTU. ####
####                                                                             ####
#### Author:  Phil Connor                                                        ####
#### Contact: contact@mylinux.work                                               ####
#### License: MIT                                                                ####
#### Version: 1.0                                                                ####
####                                                                             ####
#### Usage:   ./vpn-smoke-tests.sh                                               ####
####          VPN_TYPE=wireguard VPN_INTERFACE=wg0 ./vpn-smoke-tests.sh          ####
####                                                                             ####
#### See --help for all options.                                                 ####
#####################################################################################

set -euo pipefail

# ── Defaults ──────────────────────────────────────────────────────────
VPN_TYPE="${VPN_TYPE:-auto}"
VPN_INTERFACE="${VPN_INTERFACE:-}"
HANDSHAKE_MAX_SEC="${HANDSHAKE_MAX_SEC:-180}"
PING_TARGET="${PING_TARGET:-}"
DNS_TEST_DOMAIN="${DNS_TEST_DOMAIN:-}"
VPN_DNS_SERVER="${VPN_DNS_SERVER:-}"
EXPECTED_ROUTES="${EXPECTED_ROUTES:-}"
MGMT_SOCKET="${MGMT_SOCKET:-}"
EXPECTED_MTU="${EXPECTED_MTU:-}"
VPN_ENDPOINT="${VPN_ENDPOINT:-}"
SKIP_HANDSHAKE="${SKIP_HANDSHAKE:-false}"
SKIP_DNS="${SKIP_DNS:-false}"
SKIP_PING="${SKIP_PING:-false}"
OUTPUT_FORMAT="${OUTPUT_FORMAT:-text}"
COLOR="${COLOR:-auto}"
VERBOSE="${VERBOSE:-false}"

# ── State ─────────────────────────────────────────────────────────────
PASS=0
FAIL=0
SKIP=0
TOTAL=0
RESULTS=()
START_TIME=""

# ── Colors ────────────────────────────────────────────────────────────
setup_colors() {
    if [[ "$COLOR" == "never" ]]; then
        RED="" GREEN="" YELLOW="" BLUE="" BOLD="" RESET=""
        return
    fi
    if [[ "$COLOR" == "always" ]] || [[ -t 1 ]]; then
        RED='\033[0;31m'
        GREEN='\033[0;32m'
        YELLOW='\033[0;33m'
        BLUE='\033[0;34m'
        BOLD='\033[1m'
        RESET='\033[0m'
    else
        RED="" GREEN="" YELLOW="" BLUE="" BOLD="" RESET=""
    fi
}

# ── Logging ───────────────────────────────────────────────────────────
log()     { echo -e "${BLUE}[INFO]${RESET} $*"; }
warn()    { echo -e "${YELLOW}[WARN]${RESET} $*" >&2; }
err()     { echo -e "${RED}[ERROR]${RESET} $*" >&2; }
verbose() { if [[ "$VERBOSE" == "true" ]]; then echo -e "${BLUE}[DEBUG]${RESET} $*"; fi; }

# ── Test Result Recording ─────────────────────────────────────────────
record_pass() {
    local name="$1" detail="${2:-}"
    ((PASS++)) || true; ((TOTAL++)) || true
    RESULTS+=("PASS|${name}|${detail}")
    if [[ "$OUTPUT_FORMAT" == "tap" ]]; then echo "ok ${TOTAL} - ${name}${detail:+ (${detail})}"
    else echo -e "  ${GREEN}✓${RESET} ${name}${detail:+ — ${detail}}"; fi
}

record_fail() {
    local name="$1" detail="${2:-}"
    ((FAIL++)) || true; ((TOTAL++)) || true
    RESULTS+=("FAIL|${name}|${detail}")
    if [[ "$OUTPUT_FORMAT" == "tap" ]]; then
        echo "not ok ${TOTAL} - ${name}"
        [[ -n "$detail" ]] && echo "  # ${detail}"
    else echo -e "  ${RED}✗${RESET} ${name}${detail:+ — ${detail}}"; fi
}

record_skip() {
    local name="$1" reason="${2:-}"
    ((SKIP++)) || true; ((TOTAL++)) || true
    RESULTS+=("SKIP|${name}|${reason}")
    if [[ "$OUTPUT_FORMAT" == "tap" ]]; then echo "ok ${TOTAL} - ${name} # SKIP ${reason}"
    else echo -e "  ${YELLOW}⊘${RESET} ${name}${reason:+ — ${reason}}"; fi
}

# ── Helpers ───────────────────────────────────────────────────────────
has_cmd() { command -v "$1" >/dev/null 2>&1; }

section() {
    if [[ "$OUTPUT_FORMAT" != "tap" ]]; then echo ""; echo -e "${BOLD}$1${RESET}"; fi
}

# ── Cleanup ───────────────────────────────────────────────────────────
# shellcheck disable=SC2317
cleanup() {
    verbose "Cleanup complete."
}
trap cleanup EXIT

# ── VPN Type Detection ───────────────────────────────────────────────
detect_vpn_type() {
    if [[ "$VPN_TYPE" != "auto" ]]; then
        verbose "VPN type set to ${VPN_TYPE}"
        return
    fi
    if has_cmd wg && wg show interfaces 2>/dev/null | grep -q .; then
        VPN_TYPE="wireguard"
        verbose "Auto-detected WireGuard"
    elif pgrep -x openvpn >/dev/null 2>&1; then
        VPN_TYPE="openvpn"
        verbose "Auto-detected OpenVPN"
    elif ip link show wg0 >/dev/null 2>&1; then
        VPN_TYPE="wireguard"
        verbose "Auto-detected WireGuard (interface wg0 exists)"
    elif ip link show tun0 >/dev/null 2>&1; then
        VPN_TYPE="openvpn"
        verbose "Auto-detected OpenVPN (interface tun0 exists)"
    else
        err "Could not auto-detect VPN type. Set VPN_TYPE=wireguard or VPN_TYPE=openvpn."
        exit 1
    fi
}

set_defaults() {
    if [[ -z "$VPN_INTERFACE" ]]; then
        case "$VPN_TYPE" in
            wireguard) VPN_INTERFACE="wg0" ;;
            openvpn)   VPN_INTERFACE="tun0" ;;
        esac
        verbose "Default interface: ${VPN_INTERFACE}"
    fi
}

# ══════════════════════════════════════════════════════════════════════
#                          TEST FUNCTIONS
# ══════════════════════════════════════════════════════════════════════

# ── 1. VPN type detection ────────────────────────────────────────────
test_vpn_type() {
    case "$VPN_TYPE" in
        wireguard|openvpn) record_pass "VPN type detection" "${VPN_TYPE}" ;;
        *) record_fail "VPN type detection" "unknown type: ${VPN_TYPE}" ;;
    esac
}

# ── 2. Interface up ──────────────────────────────────────────────────
test_interface_up() {
    if ! ip link show "$VPN_INTERFACE" >/dev/null 2>&1; then
        record_fail "Interface up" "${VPN_INTERFACE} does not exist"
        return
    fi
    local state
    state=$(ip -o link show "$VPN_INTERFACE" 2>/dev/null | grep -oP 'state \K\S+') || true
    if [[ "$state" == "UP" || "$state" == "UNKNOWN" ]]; then
        record_pass "Interface up" "${VPN_INTERFACE} state=${state}"
    else
        record_fail "Interface up" "${VPN_INTERFACE} state=${state:-DOWN}"
    fi
}

# ── 3. WireGuard handshake ───────────────────────────────────────────
test_wg_handshake() {
    if [[ "$VPN_TYPE" != "wireguard" ]]; then record_skip "WireGuard handshake" "not WireGuard"; return; fi
    if [[ "$SKIP_HANDSHAKE" == "true" ]]; then record_skip "WireGuard handshake" "SKIP_HANDSHAKE=true"; return; fi
    if ! has_cmd wg; then record_skip "WireGuard handshake" "wg not installed"; return; fi

    local handshakes oldest_age=0 peer_count=0 now
    now=$(date +%s)
    handshakes=$(wg show "$VPN_INTERFACE" latest-handshakes 2>/dev/null) || true

    if [[ -z "$handshakes" ]]; then
        record_fail "WireGuard handshake" "no handshake data from wg show"
        return
    fi

    while IFS=$'\t' read -r _peer ts; do
        ((peer_count++)) || true
        if [[ "$ts" == "0" ]]; then
            record_fail "WireGuard handshake" "peer has never completed a handshake"
            return
        fi
        local age=$(( now - ts ))
        if [[ $age -gt $oldest_age ]]; then oldest_age=$age; fi
    done <<< "$handshakes"

    if [[ $peer_count -eq 0 ]]; then
        record_fail "WireGuard handshake" "no peers configured"
        return
    fi

    if [[ $oldest_age -le $HANDSHAKE_MAX_SEC ]]; then
        record_pass "WireGuard handshake" "${oldest_age}s ago (<= ${HANDSHAKE_MAX_SEC}s), ${peer_count} peer(s)"
    else
        record_fail "WireGuard handshake" "${oldest_age}s ago (> ${HANDSHAKE_MAX_SEC}s)"
    fi
}

# ── 4. WireGuard peer reachable ──────────────────────────────────────
test_wg_peer_reachable() {
    if [[ "$VPN_TYPE" != "wireguard" ]]; then record_skip "WireGuard peer reachable" "not WireGuard"; return; fi
    if [[ "$SKIP_PING" == "true" ]]; then record_skip "WireGuard peer reachable" "SKIP_PING=true"; return; fi
    if ! has_cmd wg; then record_skip "WireGuard peer reachable" "wg not installed"; return; fi

    local endpoints peer_count=0 reachable=0
    endpoints=$(wg show "$VPN_INTERFACE" endpoints 2>/dev/null) || true

    if [[ -z "$endpoints" ]]; then
        record_skip "WireGuard peer reachable" "no endpoint data"
        return
    fi

    while IFS=$'\t' read -r _peer endpoint; do
        [[ "$endpoint" == "(none)" || -z "$endpoint" ]] && continue
        ((peer_count++)) || true
        local host="${endpoint%:*}"
        # Strip brackets from IPv6
        host="${host#[}"
        host="${host%]}"
        if ping -c 1 -W 3 "$host" >/dev/null 2>&1; then
            ((reachable++)) || true
        fi
    done <<< "$endpoints"

    if [[ $peer_count -eq 0 ]]; then
        record_skip "WireGuard peer reachable" "no endpoints configured"
    elif [[ $reachable -eq $peer_count ]]; then
        record_pass "WireGuard peer reachable" "${reachable}/${peer_count} endpoints"
    else
        record_fail "WireGuard peer reachable" "${reachable}/${peer_count} endpoints reachable"
    fi
}

# ── 5. WireGuard transfer ────────────────────────────────────────────
test_wg_transfer() {
    if [[ "$VPN_TYPE" != "wireguard" ]]; then record_skip "WireGuard transfer" "not WireGuard"; return; fi
    if ! has_cmd wg; then record_skip "WireGuard transfer" "wg not installed"; return; fi

    local transfer total_rx=0 total_tx=0
    transfer=$(wg show "$VPN_INTERFACE" transfer 2>/dev/null) || true

    if [[ -z "$transfer" ]]; then
        record_fail "WireGuard transfer" "no transfer data"
        return
    fi

    while IFS=$'\t' read -r _peer rx tx; do
        total_rx=$((total_rx + rx))
        total_tx=$((total_tx + tx))
    done <<< "$transfer"

    if [[ $total_rx -gt 0 && $total_tx -gt 0 ]]; then
        local rx_h tx_h
        rx_h=$(numfmt --to=iec "$total_rx" 2>/dev/null) || rx_h="${total_rx}B"
        tx_h=$(numfmt --to=iec "$total_tx" 2>/dev/null) || tx_h="${total_tx}B"
        record_pass "WireGuard transfer" "rx=${rx_h} tx=${tx_h}"
    elif [[ $total_rx -gt 0 || $total_tx -gt 0 ]]; then
        record_fail "WireGuard transfer" "one-way traffic only (rx=${total_rx} tx=${total_tx})"
    else
        record_fail "WireGuard transfer" "no traffic (rx=0 tx=0)"
    fi
}

# ── 6. OpenVPN process ───────────────────────────────────────────────
test_ovpn_process() {
    if [[ "$VPN_TYPE" != "openvpn" ]]; then record_skip "OpenVPN process" "not OpenVPN"; return; fi

    local pid
    pid=$(pgrep -x openvpn 2>/dev/null | head -1) || true
    if [[ -n "$pid" ]]; then
        record_pass "OpenVPN process" "PID ${pid}"
    else
        record_fail "OpenVPN process" "openvpn not running"
    fi
}

# ── 7. OpenVPN management ────────────────────────────────────────────
test_ovpn_management() {
    if [[ "$VPN_TYPE" != "openvpn" ]]; then record_skip "OpenVPN management" "not OpenVPN"; return; fi
    if [[ -z "$MGMT_SOCKET" ]]; then record_skip "OpenVPN management" "MGMT_SOCKET not set"; return; fi

    if [[ -S "$MGMT_SOCKET" ]]; then
        local status_output
        status_output=$(echo "status" | socat - "UNIX-CONNECT:${MGMT_SOCKET}" 2>/dev/null) || true
        if [[ -n "$status_output" ]] && echo "$status_output" | grep -qi "client\|connected\|bytes"; then
            record_pass "OpenVPN management" "socket responding"
        elif [[ -n "$status_output" ]]; then
            record_pass "OpenVPN management" "socket responding"
        else
            record_fail "OpenVPN management" "socket not responding"
        fi
    elif [[ -e "$MGMT_SOCKET" ]]; then
        record_fail "OpenVPN management" "${MGMT_SOCKET} exists but is not a socket"
    else
        record_fail "OpenVPN management" "${MGMT_SOCKET} not found"
    fi
}

# ── 8. Tunnel IP assigned ────────────────────────────────────────────
test_tunnel_ip() {
    local ip_addr
    ip_addr=$(ip -o -4 addr show "$VPN_INTERFACE" 2>/dev/null | awk '{print $4}' | head -1) || true

    if [[ -z "$ip_addr" ]]; then
        # Try IPv6
        ip_addr=$(ip -o -6 addr show "$VPN_INTERFACE" 2>/dev/null | grep -v "fe80" | awk '{print $4}' | head -1) || true
    fi

    if [[ -n "$ip_addr" ]]; then
        record_pass "Tunnel IP assigned" "${VPN_INTERFACE} ${ip_addr}"
    else
        record_fail "Tunnel IP assigned" "${VPN_INTERFACE} has no IP address"
    fi
}

# ── 9. DNS over VPN ──────────────────────────────────────────────────
test_dns_over_vpn() {
    if [[ "$SKIP_DNS" == "true" ]]; then record_skip "DNS over VPN" "SKIP_DNS=true"; return; fi
    if [[ -z "$DNS_TEST_DOMAIN" ]]; then record_skip "DNS over VPN" "DNS_TEST_DOMAIN not set"; return; fi
    if [[ -z "$VPN_DNS_SERVER" ]]; then record_skip "DNS over VPN" "VPN_DNS_SERVER not set"; return; fi

    local output
    if has_cmd dig; then
        output=$(dig +short +time=5 +tries=1 "@${VPN_DNS_SERVER}" "${DNS_TEST_DOMAIN}" A 2>/dev/null) || true
    elif has_cmd nslookup; then
        output=$(nslookup "${DNS_TEST_DOMAIN}" "${VPN_DNS_SERVER}" 2>/dev/null | grep -i "address" | tail -1) || true
    elif has_cmd drill; then
        output=$(drill "@${VPN_DNS_SERVER}" "${DNS_TEST_DOMAIN}" A 2>/dev/null | grep -A1 "ANSWER SECTION" | tail -1) || true
    else
        record_skip "DNS over VPN" "no DNS tool available (dig, nslookup, drill)"
        return
    fi

    if [[ -n "$output" ]] && ! echo "$output" | grep -qi "timed out\|SERVFAIL\|connection refused"; then
        record_pass "DNS over VPN" "${DNS_TEST_DOMAIN} via ${VPN_DNS_SERVER}"
    else
        record_fail "DNS over VPN" "failed to resolve ${DNS_TEST_DOMAIN} via ${VPN_DNS_SERVER}"
    fi
}

# ── 10. Route check ─────────────────────────────────────────────────
test_route_check() {
    if [[ -z "$EXPECTED_ROUTES" ]]; then record_skip "Route check" "EXPECTED_ROUTES not set"; return; fi

    local IFS=',' missing=0 checked=0
    for route in $EXPECTED_ROUTES; do
        route=$(echo "$route" | xargs)
        [[ -z "$route" ]] && continue
        ((checked++)) || true
        if ip route show "$route" 2>/dev/null | grep -q "$VPN_INTERFACE"; then
            verbose "Route ${route} via ${VPN_INTERFACE} — OK"
        else
            verbose "Route ${route} via ${VPN_INTERFACE} — MISSING"
            ((missing++)) || true
        fi
    done

    if [[ $checked -eq 0 ]]; then
        record_skip "Route check" "no routes to check"
    elif [[ $missing -eq 0 ]]; then
        record_pass "Route check" "${checked} route(s) via ${VPN_INTERFACE}"
    else
        record_fail "Route check" "${missing}/${checked} route(s) missing from ${VPN_INTERFACE}"
    fi
}

# ── 11. Peer connectivity ───────────────────────────────────────────
test_peer_connectivity() {
    if [[ "$SKIP_PING" == "true" ]]; then record_skip "Peer connectivity" "SKIP_PING=true"; return; fi
    if [[ -z "$PING_TARGET" ]]; then record_skip "Peer connectivity" "PING_TARGET not set"; return; fi

    if ping -c 3 -W 5 -I "$VPN_INTERFACE" "$PING_TARGET" >/dev/null 2>&1; then
        record_pass "Peer connectivity" "ping ${PING_TARGET} via ${VPN_INTERFACE}"
    else
        record_fail "Peer connectivity" "cannot reach ${PING_TARGET} via ${VPN_INTERFACE}"
    fi
}

# ── 12. MTU check ────────────────────────────────────────────────────
test_mtu_check() {
    if [[ -z "$EXPECTED_MTU" ]]; then record_skip "MTU check" "EXPECTED_MTU not set"; return; fi

    local actual_mtu
    actual_mtu=$(ip -o link show "$VPN_INTERFACE" 2>/dev/null | grep -oP 'mtu \K[0-9]+') || true

    if [[ -z "$actual_mtu" ]]; then
        record_fail "MTU check" "could not read MTU for ${VPN_INTERFACE}"
    elif [[ "$actual_mtu" == "$EXPECTED_MTU" ]]; then
        record_pass "MTU check" "${VPN_INTERFACE} MTU=${actual_mtu}"
    else
        record_fail "MTU check" "${VPN_INTERFACE} MTU=${actual_mtu} (expected ${EXPECTED_MTU})"
    fi
}

# ── 13. Endpoint reachable ───────────────────────────────────────────
test_endpoint_reachable() {
    if [[ -z "$VPN_ENDPOINT" ]]; then record_skip "Endpoint reachable" "VPN_ENDPOINT not set"; return; fi

    local host port
    # Handle host:port format
    if [[ "$VPN_ENDPOINT" =~ ^\[.*\]:[0-9]+$ ]]; then
        # IPv6 [addr]:port
        host="${VPN_ENDPOINT%:*}"
        host="${host#[}"
        host="${host%]}"
        port="${VPN_ENDPOINT##*:}"
    elif [[ "$VPN_ENDPOINT" =~ ^[^:]+:[0-9]+$ ]]; then
        host="${VPN_ENDPOINT%:*}"
        port="${VPN_ENDPOINT##*:}"
    else
        host="$VPN_ENDPOINT"
        port=""
    fi

    if [[ -n "$port" ]]; then
        # Test TCP/UDP reachability
        if has_cmd nc; then
            if nc -z -w 5 "$host" "$port" >/dev/null 2>&1; then
                record_pass "Endpoint reachable" "${VPN_ENDPOINT}"
                return
            fi
        fi
        # Fall back to ping if nc fails or unavailable
        if ping -c 1 -W 5 "$host" >/dev/null 2>&1; then
            record_pass "Endpoint reachable" "${host} (ping OK, port ${port} not tested)"
        else
            record_fail "Endpoint reachable" "${VPN_ENDPOINT} unreachable"
        fi
    else
        if ping -c 1 -W 5 "$host" >/dev/null 2>&1; then
            record_pass "Endpoint reachable" "${host}"
        else
            record_fail "Endpoint reachable" "${host} unreachable"
        fi
    fi
}

# ══════════════════════════════════════════════════════════════════════
#                          OUTPUT
# ══════════════════════════════════════════════════════════════════════

print_tap_header() {
    echo "TAP version 13"
}

print_tap_footer() {
    echo "1..${TOTAL}"
    echo "# pass ${PASS}"
    echo "# fail ${FAIL}"
    echo "# skip ${SKIP}"
}

print_summary() {
    local end_time; end_time=$(date +%s)
    local duration=$(( end_time - START_TIME ))
    echo ""
    echo -e "${BOLD}────────────────────────────────────────${RESET}"
    echo -e "${BOLD}Summary${RESET}  VPN Smoke Tests  ${VPN_TYPE}/${VPN_INTERFACE}"
    echo -e "  ${GREEN}${PASS} passed${RESET}  ${RED}${FAIL} failed${RESET}  ${YELLOW}${SKIP} skipped${RESET}  (${duration}s)"
    echo -e "${BOLD}────────────────────────────────────────${RESET}"
    if [[ $FAIL -eq 0 ]]; then echo -e "${GREEN}${BOLD}All tests passed.${RESET}"
    else echo -e "${RED}${BOLD}${FAIL} test(s) failed.${RESET}"; fi
}

# ══════════════════════════════════════════════════════════════════════
#                          MAIN
# ══════════════════════════════════════════════════════════════════════

usage() {
    cat <<EOF
Usage: $(basename "$0") [OPTIONS]

Smoke-test VPN tunnels. Supports WireGuard and OpenVPN.

Environment variables (all optional):
  VPN_TYPE            VPN backend: auto, wireguard, openvpn  (default: auto)
  VPN_INTERFACE       Network interface                      (default: wg0 or tun0)
  HANDSHAKE_MAX_SEC   Max seconds since WireGuard handshake  (default: 180)
  PING_TARGET         IP to ping through tunnel              (skip if empty)
  DNS_TEST_DOMAIN     Domain to resolve via VPN DNS          (skip if empty)
  VPN_DNS_SERVER      DNS server for VPN resolution          (skip if empty)
  EXPECTED_ROUTES     Comma-separated CIDRs to verify        (skip if empty)
  MGMT_SOCKET         OpenVPN management socket path         (skip if empty)
  EXPECTED_MTU        Expected MTU value                     (skip if empty)
  VPN_ENDPOINT        VPN server IP:port to check            (skip if empty)
  SKIP_HANDSHAKE      Skip WireGuard handshake check         (default: false)
  SKIP_DNS            Skip DNS-over-VPN test                 (default: false)
  SKIP_PING           Skip ping-based tests                  (default: false)
  OUTPUT_FORMAT       Output: text (default), tap
  COLOR               Color: auto (default), always, never
  VERBOSE             Show debug output                      (default: false)

Options:
  --type TYPE         Set VPN_TYPE
  --interface IFACE   Set VPN_INTERFACE
  --format FORMAT     Output format: text (default), tap
  --skip-handshake    Skip WireGuard handshake check
  --skip-dns          Skip DNS-over-VPN test
  --skip-ping         Skip ping-based tests
  --verbose           Show debug output
  --no-color          Disable colored output
  --help              Show this help

Examples:
  ./$(basename "$0")
  VPN_TYPE=wireguard PING_TARGET=10.0.0.1 ./$(basename "$0")
  VPN_TYPE=openvpn MGMT_SOCKET=/run/openvpn/mgmt.sock ./$(basename "$0")
  OUTPUT_FORMAT=tap ./$(basename "$0") --skip-ping
EOF
}

main() {
    while [[ $# -gt 0 ]]; do
        case "$1" in
            --type)            VPN_TYPE="$2"; shift ;;
            --interface)       VPN_INTERFACE="$2"; shift ;;
            --format)          OUTPUT_FORMAT="$2"; shift ;;
            --skip-handshake)  SKIP_HANDSHAKE=true ;;
            --skip-dns)        SKIP_DNS=true ;;
            --skip-ping)       SKIP_PING=true ;;
            --verbose)         VERBOSE=true ;;
            --no-color)        COLOR=never ;;
            --help|-h)         usage; exit 0 ;;
            *)                 err "Unknown option: $1"; usage; exit 1 ;;
        esac
        shift
    done

    setup_colors
    detect_vpn_type
    set_defaults

    START_TIME=$(date +%s)

    if [[ "$OUTPUT_FORMAT" == "tap" ]]; then
        print_tap_header
    else
        echo ""
        echo -e "${BOLD}VPN Smoke Tests${RESET}"
        echo -e "Type: ${VPN_TYPE}  Interface: ${VPN_INTERFACE}"
        echo -e "Time: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
    fi

    section "Detection"
    test_vpn_type

    section "Interface"
    test_interface_up
    test_tunnel_ip
    test_mtu_check

    section "WireGuard"
    test_wg_handshake
    test_wg_peer_reachable
    test_wg_transfer

    section "OpenVPN"
    test_ovpn_process
    test_ovpn_management

    section "Connectivity"
    test_endpoint_reachable
    test_peer_connectivity
    test_dns_over_vpn
    test_route_check

    # ── Results ──
    if [[ "$OUTPUT_FORMAT" == "tap" ]]; then
        print_tap_footer
    else
        print_summary
    fi

    [[ $FAIL -eq 0 ]] && exit 0 || exit 1
}

main "$@"
