// Copyright 2024 Saorsa Labs Ltd.
//
// This Saorsa Network Software is licensed under the General Public License (GPL), version 3.
// Please see the file LICENSE-GPL, or visit <http://www.gnu.org/licenses/> for the full text.
//
// Full details available at https://saorsalabs.com/licenses
#![allow(missing_docs)]

//! High-level NAT Traversal API for Autonomi P2P Networks
//!
//! This module provides a simple, high-level interface for establishing
//! QUIC connections through NATs using sophisticated hole punching and
//! coordination protocols.

#[allow(unused_imports)]
use crate::coordinator_control::{
    CoordinatorControlEnvelope, CoordinatorControlMessage, InboundOffer, LiveRequest,
    PendingRequest, RejectionReason, clear_live_request, decode_coordinator_control,
    encode_coordinator_control, get_pending_request, inbound_offer, live_request,
    monotonic_deadline_from_unix_ms, next_request_id, note_rate_limit_and_check, now_unix_ms,
    record_rejection, remember_inbound_offer, remember_live_request, remember_pending_request,
    remove_inbound_offer, remove_pending_request, take_live_rejection,
    wire_and_monotonic_expiry_after,
};
use std::{
    collections::{HashMap, HashSet},
    fmt,
    net::SocketAddr,
    sync::Arc,
    time::Duration,
};

use crate::constrained::{ConstrainedEngine, EngineConfig, EngineEvent};
use crate::transport::TransportRegistry;

use crate::SHUTDOWN_DRAIN_TIMEOUT;

/// Creates a bind address that allows the OS to select a random available port
///
/// This provides protocol obfuscation by preventing port fingerprinting, which improves
/// security by making it harder for attackers to identify and target QUIC endpoints.
///
/// # Security Benefits
/// - **Port Randomization**: Each endpoint gets a different random port, preventing easy detection
/// - **Fingerprinting Resistance**: Makes protocol identification more difficult for attackers
/// - **Attack Surface Reduction**: Reduces predictable network patterns that could be exploited
///
/// # Implementation Details
/// - Binds to `0.0.0.0:0` to let the OS choose an available port
/// - Used automatically when `bind_addr` is `None` in endpoint configuration
/// - Provides better security than static or predictable port assignments
///
/// # Added in Version 0.6.1
/// This function was introduced as part of security improvements in commit 6e633cd9
/// to enhance protocol obfuscation capabilities.
fn create_random_port_bind_addr() -> SocketAddr {
    // SAFETY: This is a compile-time constant string that is always valid.
    // Using a const assertion to ensure this at compile time.
    const BIND_ADDR: &str = "0.0.0.0:0";
    // This parse will never fail for a valid constant, but we handle it gracefully
    // by falling back to a known-good default constructed directly.
    BIND_ADDR.parse().unwrap_or_else(|_| {
        SocketAddr::new(std::net::IpAddr::V4(std::net::Ipv4Addr::UNSPECIFIED), 0)
    })
}

/// Extract ML-DSA-65 public key from SubjectPublicKeyInfo DER structure.
///
/// v0.2: Pure PQC - Uses ML-DSA-65 for all authentication.
/// RFC 7250 Raw Public Keys use SubjectPublicKeyInfo format.
///
/// Returns the extracted ML-DSA-65 public key if valid SPKI, None otherwise.
fn extract_ml_dsa_from_spki(spki: &[u8]) -> Option<crate::crypto::pqc::types::MlDsaPublicKey> {
    crate::crypto::raw_public_keys::pqc::extract_public_key_from_spki(spki).ok()
}

// Import shared normalize_socket_addr utility
use crate::shared::normalize_socket_addr;

/// Broadcast an ADD_ADDRESS frame to all connected peers.
///
/// This helper consolidates the duplicate broadcast logic throughout the codebase.
/// It iterates over all connections and sends the NAT address advertisement frame
/// to each peer, logging success or failure.
fn broadcast_address_to_peers(
    connections: &dashmap::DashMap<PeerId, InnerConnection>,
    address: SocketAddr,
    priority: u32,
) {
    // Snapshot keys to avoid holding iter_mut() write guards on all DashMap shards
    let peer_ids: Vec<PeerId> = connections.iter().map(|e| *e.key()).collect();
    for peer_id in peer_ids {
        if let Some(mut entry) = connections.get_mut(&peer_id) {
            let conn = entry.value_mut();
            match conn.send_nat_address_advertisement(address, priority) {
                Ok(seq) => {
                    info!(
                        "Sent ADD_ADDRESS to peer {:?}: addr={}, seq={}",
                        peer_id, address, seq
                    );
                }
                Err(e) => {
                    debug!("Failed to send ADD_ADDRESS to peer {:?}: {:?}", peer_id, e);
                }
            }
        }
    }
}

fn encode_relay_response_frame(response: &ConnectUdpResponse) -> Vec<u8> {
    let response_bytes = response.encode();
    let mut frame = Vec::with_capacity(4 + response_bytes.len());
    frame.extend_from_slice(&(response_bytes.len() as u32).to_be_bytes());
    frame.extend_from_slice(&response_bytes);
    frame
}

/// Multi-transport candidate advertisement
///
/// Stores information about an advertised transport address with optional capability flags.
/// This extends the basic UDP address model to support BLE, LoRa, and other transports.
#[derive(Debug, Clone)]
pub struct TransportCandidate {
    /// The transport address being advertised
    pub address: TransportAddr,
    /// Priority for candidate selection (higher = better)
    pub priority: u32,
    /// How this candidate was discovered
    pub source: CandidateSource,
    /// Current validation state
    pub state: CandidateState,
    /// Optional capability flags summarizing transport characteristics
    pub capabilities: Option<CapabilityFlags>,
}

impl TransportCandidate {
    /// Create a new transport candidate for a UDP address
    pub fn udp(address: SocketAddr, priority: u32, source: CandidateSource) -> Self {
        Self {
            address: TransportAddr::Udp(address),
            priority,
            source,
            state: CandidateState::New,
            capabilities: Some(CapabilityFlags::broadband()),
        }
    }

    /// Create a new transport candidate for any transport address
    pub fn new(address: TransportAddr, priority: u32, source: CandidateSource) -> Self {
        Self {
            address,
            priority,
            source,
            state: CandidateState::New,
            capabilities: None,
        }
    }

    /// Create a new transport candidate with capability information
    pub fn with_capabilities(
        address: TransportAddr,
        priority: u32,
        source: CandidateSource,
        capabilities: &TransportCapabilities,
    ) -> Self {
        Self {
            address,
            priority,
            source,
            state: CandidateState::New,
            capabilities: Some(CapabilityFlags::from_capabilities(capabilities)),
        }
    }

    /// Get the socket address if this is a UDP transport
    pub fn socket_addr(&self) -> Option<SocketAddr> {
        self.address.as_socket_addr()
    }

    /// Get the transport type
    pub fn transport_type(&self) -> TransportType {
        self.address.transport_type()
    }

    /// Check if this transport supports full QUIC (if capability info is available)
    pub fn supports_full_quic(&self) -> Option<bool> {
        self.capabilities.map(|c| c.supports_full_quic())
    }
}

use tracing::{debug, error, info, trace, warn};

use blake3::Hasher as Blake3Hasher;
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
// Use parking_lot for faster, non-poisoning locks that work better with async code
use parking_lot::{Mutex as ParkingMutex, RwLock as ParkingRwLock};

use tokio::{
    net::UdpSocket,
    sync::{Mutex as TokioMutex, mpsc},
    time::{sleep, timeout},
};

use crate::connection_lifecycle::{ConnectionCloseReason, ConnectionLifecycleState};
use crate::high_level::default_runtime;

use crate::{
    VarInt,
    candidate_discovery::{CandidateDiscoveryManager, DiscoveryConfig, DiscoveryEvent},
    // v0.13.0: NatTraversalRole removed - all nodes are symmetric P2P nodes
    connection::nat_traversal::{CandidateSource, CandidateState},
    masque::connect::{ConnectUdpRequest, ConnectUdpResponse},
    masque::integration::{RelayManager, RelayManagerConfig},
    // Symmetric P2P: Every node provides relay services
    masque::relay_server::{MasqueRelayConfig, MasqueRelayServer},
    // Multi-transport support
    nat_traversal::CapabilityFlags,
    transport::{TransportAddr, TransportCapabilities, TransportType},
};

use crate::{
    ClientConfig, EndpointConfig, ServerConfig, Side, TransportConfig,
    high_level::{Connection as InnerConnection, Endpoint as InnerEndpoint},
};

use crate::{crypto::rustls::QuicClientConfig, crypto::rustls::QuicServerConfig};

use crate::config::validation::{ConfigValidator, ValidationResult};

use crate::crypto::{pqc::PqcConfig, raw_public_keys::RawPublicKeyConfigBuilder};

#[derive(Debug, Clone, Copy)]
struct PendingAccept {
    peer_id: PeerId,
    generation: u64,
}

#[derive(Debug, Clone, Copy)]
struct ObservedAddressReport {
    reporter_peer_id: PeerId,
    reported_by: SocketAddr,
    address: SocketAddr,
}

#[derive(Debug, Clone)]
struct TrackedConnection {
    connection: InnerConnection,
    generation: u64,
    established_at_unix_ms: u64,
    connection_family_id: [u8; 32],
    connection_id: [u8; 32],
    state: ConnectionLifecycleState,
}

impl TrackedConnection {
    fn stable_id(&self) -> usize {
        self.connection.stable_id()
    }

    fn connection_id_hex(&self) -> String {
        hex::encode(&self.connection_id[..8])
    }
}

#[derive(Debug, Clone, Copy)]
pub(crate) struct ConnectionLifecycleSnapshot {
    pub generation: u64,
    pub stable_id: usize,
    pub connection_id: [u8; 32],
    pub state: ConnectionLifecycleState,
    pub established_at_unix_ms: u64,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct ConnectionCanonicalSortKey {
    connection_family_id: [u8; 32],
    connection_id: [u8; 32],
    generation: u64,
    established_at_unix_ms: u64,
    stable_id: usize,
}

#[derive(Debug, Clone, Copy)]
pub(crate) enum ConnectionRegistrationOutcome {
    Live {
        generation: u64,
        superseded_generation: Option<u64>,
    },
    Rejected {
        winner_generation: u64,
    },
}

#[derive(Debug, Clone, Copy)]
pub(crate) enum ReaderExitOutcome {
    Noop,
    ConnectionReaped,
    PeerDisconnected { close_reason: ConnectionCloseReason },
}

/// An active relay session for MASQUE CONNECT-UDP
///
/// Stores the QUIC connection to a relay server and the public address
/// allocated for receiving inbound connections.
#[derive(Debug)]
pub struct RelaySession {
    /// QUIC connection to the relay server
    pub connection: InnerConnection,
    /// Public address allocated by the relay for inbound traffic
    pub public_address: Option<SocketAddr>,
    /// Virtual UDP socket backed by the relay bind stream.
    pub relay_socket: Option<Arc<crate::masque::MasqueRelaySocket>>,
    /// When the session was established
    pub established_at: std::time::Instant,
    /// Relay server address
    pub relay_addr: SocketAddr,
}

impl RelaySession {
    /// Check if the session is still active
    pub fn is_active(&self) -> bool {
        // Connection is active if there's no close reason
        self.connection.close_reason().is_none()
    }

    /// Get the allocated public address if available
    pub fn public_addr(&self) -> Option<SocketAddr> {
        self.public_address
    }
}

/// Event from the constrained engine with transport address context
///
/// This wrapper adds the transport address to engine events so that P2pEndpoint
/// can properly route and track data from constrained transports (BLE/LoRa).
#[derive(Debug, Clone)]
pub struct ConstrainedEventWithAddr {
    /// The engine event (DataReceived, ConnectionAccepted, etc.)
    pub event: EngineEvent,
    /// The transport address of the remote peer
    pub remote_addr: crate::transport::TransportAddr,
}

/// High-level NAT traversal endpoint for Autonomi P2P networks
pub struct NatTraversalEndpoint {
    /// Underlying QUIC endpoint
    inner_endpoint: Option<InnerEndpoint>,
    /// Fallback internal endpoint for non-production builds

    /// NAT traversal configuration
    config: NatTraversalConfig,
    /// Known bootstrap/coordinator nodes
    /// Uses parking_lot::RwLock for faster, non-poisoning reads
    bootstrap_nodes: Arc<ParkingRwLock<Vec<BootstrapNode>>>,
    /// Active NAT traversal sessions
    /// Uses DashMap for fine-grained concurrent access without blocking workers
    active_sessions: Arc<dashmap::DashMap<PeerId, NatTraversalSession>>,
    /// Candidate discovery manager
    /// Uses parking_lot::Mutex for faster, non-poisoning access
    discovery_manager: Arc<ParkingMutex<CandidateDiscoveryManager>>,
    /// Event callback for coordination (simplified without async channels)
    /// Wrapped in Arc so it can be shared with background tasks
    event_callback: Option<Arc<dyn Fn(NatTraversalEvent) + Send + Sync>>,
    /// Shutdown flag for async operations
    shutdown: Arc<AtomicBool>,
    /// Channel for internal communication
    event_tx: Option<mpsc::UnboundedSender<NatTraversalEvent>>,
    /// Receiver for internal event notifications
    /// Uses parking_lot::Mutex for faster, non-poisoning access
    event_rx: ParkingMutex<mpsc::UnboundedReceiver<NatTraversalEvent>>,
    /// Sender for observed-address updates surfaced by per-connection watchers.
    observed_address_tx: mpsc::UnboundedSender<ObservedAddressReport>,
    /// Notify the discovery/orchestration driver that discovery state changed.
    discovery_state_notify: Arc<tokio::sync::Notify>,
    /// Notify traversal waiters when session progress or runtime events arrive.
    traversal_event_notify: Arc<tokio::sync::Notify>,
    /// Notify waiters when a new accepted connection is available.
    /// Eliminates the 10ms polling loop in accept_connection().
    incoming_notify: Arc<tokio::sync::Notify>,
    /// Queue of accepted inbound live connections waiting to be surfaced via
    /// `accept_connection()`. Generation tagging prevents a replaced connection
    /// from being surfaced twice after a fast reconnect race.
    pending_accepts: Arc<ParkingMutex<std::collections::VecDeque<PendingAccept>>>,
    /// Notify waiters when the endpoint is shutting down.
    /// Eliminates polling loops that check the AtomicBool in transport listeners.
    shutdown_notify: Arc<tokio::sync::Notify>,
    /// Active live connections by peer ID.
    /// Uses DashMap for fine-grained concurrent access without blocking workers.
    connections: Arc<dashmap::DashMap<PeerId, InnerConnection>>,
    /// Lifecycle state for all tracked connections, including superseded/closed
    /// entries retained briefly for diagnostics and deterministic replacement.
    connection_lifecycle: Arc<ParkingRwLock<HashMap<PeerId, Vec<TrackedConnection>>>>,
    /// Monotonic local generation counter used for tracked connections.
    next_connection_generation: Arc<AtomicU64>,
    /// Local peer ID
    local_peer_id: PeerId,
    /// Timeout configuration
    timeout_config: crate::config::nat_timeouts::TimeoutConfig,
    /// Track peers for which ConnectionEstablished has already been emitted
    /// This prevents duplicate events from being sent multiple times for the same connection
    /// Uses DashSet for fine-grained concurrent access without blocking workers
    emitted_established_events: Arc<dashmap::DashSet<PeerId>>,
    /// MASQUE relay manager for fallback connections
    relay_manager: Option<Arc<RelayManager>>,
    /// Active relay sessions by relay server address
    /// Uses DashMap for fine-grained concurrent access without blocking workers
    relay_sessions: Arc<dashmap::DashMap<SocketAddr, RelaySession>>,
    /// Shared QUIC endpoint bound to the relay socket.
    ///
    /// The relay server allows one CONNECT-UDP bind session per client address,
    /// so callers must reuse the same abstract socket/endpoint instead of
    /// trying to create a second independent bind session on the same local
    /// QUIC endpoint.
    shared_relay_endpoint: Arc<std::sync::Mutex<Option<InnerEndpoint>>>,
    /// Whether the shared relay endpoint already has an accept loop attached.
    relay_accept_loop_started: Arc<std::sync::atomic::AtomicBool>,
    /// MASQUE relay server - every node provides relay services (symmetric P2P)
    /// Per ADR-004: All nodes are equal and participate in relaying with resource budgets
    relay_server: Option<Arc<MasqueRelayServer>>,
    /// Successful candidate pairs discovered via hole punching
    /// Maps peer ID to the remote address that successfully responded
    /// Uses DashMap for fine-grained concurrent access without blocking workers
    successful_candidates: Arc<dashmap::DashMap<PeerId, SocketAddr>>,
    /// Transport candidates received from peers (multi-transport support)
    /// Maps peer ID to all known transport candidates for that peer
    /// Enables routing decisions based on transport type and capabilities
    transport_candidates: Arc<dashmap::DashMap<PeerId, Vec<TransportCandidate>>>,
    /// Transport registry for multi-transport support
    /// When present, allows using transport-provided sockets instead of creating new ones
    transport_registry: Option<Arc<TransportRegistry>>,
    /// Channel for receiving peer address updates (ADD_ADDRESS → DHT bridge)
    #[allow(dead_code)] // Used when full symmetric NAT relay is wired
    pub(crate) peer_address_update_rx:
        TokioMutex<mpsc::UnboundedReceiver<(SocketAddr, SocketAddr)>>,
    /// Whether symmetric NAT relay setup has been attempted (one-shot)
    relay_setup_attempted: Arc<std::sync::atomic::AtomicBool>,
    /// Relay address to re-advertise to new peers (set after proactive relay setup)
    relay_public_addr: Arc<std::sync::Mutex<Option<SocketAddr>>>,
    /// Peers already advertised the relay address to
    relay_advertised_peers: Arc<std::sync::Mutex<std::collections::HashSet<SocketAddr>>>,
    #[cfg(test)]
    /// Synthetic observed external addresses for tests that do not open a real
    /// QUIC connection.
    test_observed_external_addrs: Arc<std::sync::Mutex<Vec<SocketAddr>>>,
    #[cfg(test)]
    /// Tracks relay publication attempts in tests without a live inner
    /// connection.
    test_relay_publish_attempts: Arc<std::sync::Mutex<std::collections::HashSet<PeerId>>>,
    /// Server config for creating secondary endpoints (e.g., relay accept endpoint)
    #[allow(dead_code)] // Used when full symmetric NAT relay is wired
    server_config: Option<crate::ServerConfig>,
    /// Task handles for transport listener tasks
    /// Used for cleanup on shutdown
    transport_listener_handles: Arc<ParkingMutex<Vec<tokio::task::JoinHandle<()>>>>,
    /// Constrained protocol engine for BLE/LoRa/Serial transports
    /// Handles the constrained protocol for non-UDP transports
    constrained_engine: Arc<ParkingMutex<ConstrainedEngine>>,
    /// Channel for forwarding constrained engine events to P2pEndpoint
    /// Events like DataReceived from BLE/LoRa transports are sent through this channel
    constrained_event_tx: mpsc::UnboundedSender<ConstrainedEventWithAddr>,
    /// Receiver for constrained engine events
    /// P2pEndpoint polls this to receive data from constrained transports
    /// Uses TokioMutex (not ParkingMutex) because MutexGuard is held across .await
    constrained_event_rx: TokioMutex<mpsc::UnboundedReceiver<ConstrainedEventWithAddr>>,
}

/// Configuration for NAT traversal behavior
///
/// This configuration controls various aspects of NAT traversal including security,
/// performance, and reliability settings. Recent improvements in version 0.6.1 include
/// enhanced security through protocol obfuscation and robust error handling.
///
/// # Pure P2P Design (v0.13.0+)
/// All nodes are now symmetric - they can both connect and accept connections.
/// The `role` field is deprecated and ignored. Every node automatically:
/// - Accepts incoming connections
/// - Initiates outgoing connections
/// - Coordinates NAT traversal for connected peers
/// - Discovers its external address from any connected peer
///
/// # Security Features (Added in v0.6.1)
/// - **Protocol Obfuscation**: Random port binding prevents fingerprinting attacks
/// - **Robust Error Handling**: Panic-free operation with graceful error recovery
/// - **Input Validation**: Enhanced validation of configuration parameters
///
/// # Example
/// ```rust
/// use ant_quic::nat_traversal_api::NatTraversalConfig;
/// use std::time::Duration;
/// use std::net::SocketAddr;
///
/// // Recommended secure configuration
/// let config = NatTraversalConfig {
///     known_peers: vec!["127.0.0.1:9000".parse::<SocketAddr>().unwrap()],
///     max_candidates: 10,
///     coordination_timeout: Duration::from_secs(10),
///     enable_symmetric_nat: true,
///     enable_relay_fallback: true,
///     max_concurrent_attempts: 5,
///     bind_addr: None, // Auto-select for security
///     prefer_rfc_nat_traversal: true,
///     timeouts: Default::default(),
///     ..Default::default()
/// };
/// ```
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct NatTraversalConfig {
    /// Known peer addresses for initial discovery
    /// These peers are used to discover external addresses and coordinate NAT traversal.
    /// In v0.13.0+ all nodes are symmetric - any connected peer can help with discovery.
    pub known_peers: Vec<SocketAddr>,
    /// Maximum number of address candidates to maintain
    pub max_candidates: usize,
    /// Timeout for coordination rounds
    pub coordination_timeout: Duration,
    /// Enable symmetric NAT prediction algorithms (always true; legacy flag ignored)
    pub enable_symmetric_nat: bool,
    /// Enable automatic relay fallback (always true; legacy flag ignored)
    pub enable_relay_fallback: bool,
    /// Enable relay service for other peers (always true; legacy flag ignored)
    /// When true, this node will accept and forward CONNECT-UDP Bind requests from peers.
    /// Per ADR-004: All nodes are equal and participate in relaying with resource budgets.
    /// Default: true (every node provides relay services)
    pub enable_relay_service: bool,
    /// Known relay nodes for MASQUE CONNECT-UDP Bind fallback
    /// When direct NAT traversal fails, connections can be relayed through these nodes
    /// NOTE: In symmetric P2P, connected peers are used as relays automatically.
    /// This is only for bootstrapping when no peers are connected yet.
    pub relay_nodes: Vec<SocketAddr>,
    /// Maximum concurrent NAT traversal attempts
    pub max_concurrent_attempts: usize,
    /// Bind address for the endpoint
    ///
    /// - `Some(addr)`: Bind to the specified address
    /// - `None`: Auto-select random port for enhanced security (recommended)
    ///
    /// When `None`, the system uses an internal method to automatically
    /// select a random available port, providing protocol obfuscation and improved
    /// security through port randomization.
    ///
    /// # Security Benefits of None (Auto-Select)
    /// - **Protocol Obfuscation**: Makes endpoint detection harder for attackers
    /// - **Port Randomization**: Each instance gets a different port
    /// - **Fingerprinting Resistance**: Reduces predictable network patterns
    ///
    /// # Added in Version 0.6.1
    /// Enhanced security through automatic random port selection
    pub bind_addr: Option<SocketAddr>,
    /// Additional bound addresses for dual-stack nodes.
    ///
    /// When using separate IPv4/IPv6 sockets, the primary `bind_addr` is typically
    /// the IPv6 address, and this field holds the IPv4 address (or vice versa).
    /// These are added as local NAT traversal candidates so peers can reach us
    /// via either address family.
    pub additional_bind_addrs: Vec<SocketAddr>,
    /// Prefer RFC-compliant NAT traversal frame format
    /// When true, will send RFC-compliant frames if the peer supports it
    pub prefer_rfc_nat_traversal: bool,
    /// Post-Quantum Cryptography configuration
    pub pqc: Option<PqcConfig>,
    /// Timeout configuration for NAT traversal operations
    pub timeouts: crate::config::nat_timeouts::TimeoutConfig,
    /// Identity keypair for TLS authentication (ML-DSA-65)
    ///
    /// v0.2: Pure PQC - Uses ML-DSA-65 for all authentication.
    /// v0.13.0+: This keypair is used for RFC 7250 Raw Public Key TLS authentication.
    /// If provided, peers will derive the same PeerId from this key via TLS handshake.
    /// If None, a random keypair is generated (not recommended for production as it
    /// won't match the application-layer PeerId).
    #[serde(skip)]
    pub identity_key: Option<(
        crate::crypto::pqc::types::MlDsaPublicKey,
        crate::crypto::pqc::types::MlDsaSecretKey,
    )>,
    /// Allow IPv4-mapped IPv6 addresses (::ffff:x.x.x.x) as valid candidates
    ///
    /// When true, IPv4-mapped addresses are accepted. These addresses represent
    /// IPv4 connections on dual-stack sockets (sockets with IPV6_V6ONLY=0).
    /// When a dual-stack socket accepts an IPv4 connection, the remote address
    /// appears as an IPv4-mapped IPv6 address.
    ///
    /// Default: true (required for dual-stack socket support)
    pub allow_ipv4_mapped: bool,

    /// Transport registry containing available transport providers.
    ///
    /// When provided, NatTraversalEndpoint uses registered transports
    /// for socket binding instead of hardcoded UDP. This enables
    /// multi-transport support (UDP, BLE, etc.).
    ///
    /// Default: None (uses traditional UdpSocket::bind directly)
    #[serde(skip)]
    pub transport_registry: Option<Arc<TransportRegistry>>,

    /// Maximum message size in bytes (read-side guard).
    ///
    /// Caps the bytes `read_to_end()` will accept per stream. QUIC flow-control
    /// windows are **not** derived from this value — they use transport-layer
    /// defaults based on bandwidth-delay products.
    ///
    /// Default: [`crate::unified_config::P2pConfig::DEFAULT_MAX_MESSAGE_SIZE`] (4 MiB).
    #[serde(default = "default_max_message_size")]
    pub max_message_size: usize,

    /// Maximum concurrent unidirectional QUIC streams per connection.
    ///
    /// Each `send()` opens a new uni stream. Applications with high message
    /// throughput should increase this. Default: 100.
    #[serde(default = "default_max_concurrent_uni_streams")]
    pub max_concurrent_uni_streams: u32,
}

fn default_max_message_size() -> usize {
    crate::unified_config::P2pConfig::DEFAULT_MAX_MESSAGE_SIZE
}

fn default_max_concurrent_uni_streams() -> u32 {
    100
}

// varint_from_max_message_size removed — QUIC flow-control windows now use
// transport-layer defaults (bandwidth-delay product) instead of being derived
// from the application-level max_message_size.

// v0.13.0: EndpointRole enum has been removed.
// All nodes are now symmetric P2P nodes - they can connect, accept connections,
// and coordinate NAT traversal. No role configuration is needed.

/// Unique identifier for a peer in the network
#[derive(
    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
)]
pub struct PeerId(pub [u8; 32]);

/// Information about a bootstrap/coordinator node
#[derive(Debug, Clone)]
pub struct BootstrapNode {
    /// Network address of the bootstrap node
    pub address: SocketAddr,
    /// Last successful contact time
    pub last_seen: std::time::Instant,
    /// Whether this node can coordinate NAT traversal
    pub can_coordinate: bool,
    /// RTT to this bootstrap node
    pub rtt: Option<Duration>,
    /// Number of successful coordinations via this node
    pub coordination_count: u32,
}

impl BootstrapNode {
    /// Create a new bootstrap node
    pub fn new(address: SocketAddr) -> Self {
        Self {
            address,
            last_seen: std::time::Instant::now(),
            can_coordinate: true,
            rtt: None,
            coordination_count: 0,
        }
    }
}

/// A candidate pair for hole punching (ICE-like)
#[derive(Debug, Clone)]
pub struct CandidatePair {
    /// Local candidate address
    pub local_candidate: CandidateAddress,
    /// Remote candidate address
    pub remote_candidate: CandidateAddress,
    /// Combined priority for this pair
    pub priority: u64,
    /// Current state of this candidate pair
    pub state: CandidatePairState,
}

/// State of a candidate pair during hole punching
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CandidatePairState {
    /// Waiting to be checked
    Waiting,
    /// Currently being checked
    InProgress,
    /// Check succeeded
    Succeeded,
    /// Check failed
    Failed,
    /// Cancelled due to higher priority success
    Cancelled,
}

/// The specific protocol progress condition currently bounding traversal.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TraversalDeadlineKind {
    /// Discovery failed to produce enough evidence before its contract deadline.
    DiscoveryProgress,
    /// Coordination did not receive the expected response in time.
    CoordinationResponse,
    /// Synchronization did not make progress before its deadline.
    SynchronizationProgress,
    /// Hole punching did not produce a viable path before the punch window closed.
    PunchProgress,
    /// Path validation did not complete before its deadline.
    ValidationProgress,
    /// Session-owned retry backoff has not expired yet.
    RetryBackoff,
}

#[derive(Debug, Clone, PartialEq, Eq)]
struct SessionDeadline {
    kind: TraversalDeadlineKind,
    at: std::time::Instant,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TraversalFailureReason {
    DiscoveryExhausted,
    CoordinatorUnavailable,
    CoordinationRejected {
        reason: crate::coordinator_control::RejectionReason,
    },
    CoordinationExpired,
    SynchronizationExpired,
    PunchWindowMissed,
    ValidationTimedOut,
    ValidationFailed,
    ConnectionFailed,
    ProtocolViolation(String),
    NetworkError(String),
    ShuttingDown,
}

impl TraversalFailureReason {
    pub(crate) fn from_session_error(
        error: &NatTraversalError,
        phase: TraversalPhase,
        shutting_down: bool,
    ) -> Self {
        match error {
            NatTraversalError::NoCandidatesFound
            | NatTraversalError::CandidateDiscoveryFailed(_) => Self::DiscoveryExhausted,
            NatTraversalError::NoBootstrapNodes => Self::CoordinatorUnavailable,
            NatTraversalError::CoordinationFailed(message) => Self::NetworkError(message.clone()),
            NatTraversalError::HolePunchingFailed | NatTraversalError::PunchingFailed(_) => {
                Self::PunchWindowMissed
            }
            NatTraversalError::ValidationTimeout => Self::ValidationTimedOut,
            NatTraversalError::ValidationFailed(_) => Self::ValidationFailed,
            NatTraversalError::Timeout => match phase {
                TraversalPhase::Coordination => Self::CoordinationExpired,
                TraversalPhase::Synchronization => Self::SynchronizationExpired,
                TraversalPhase::Punching => Self::PunchWindowMissed,
                TraversalPhase::Validation => Self::ValidationTimedOut,
                _ => Self::NetworkError("Traversal timed out".to_string()),
            },
            NatTraversalError::ConnectionFailed(_) | NatTraversalError::PeerNotConnected => {
                Self::ConnectionFailed
            }
            NatTraversalError::ProtocolError(message)
            | NatTraversalError::ConfigError(message)
            | NatTraversalError::TraversalFailed(message) => {
                Self::ProtocolViolation(message.clone())
            }
            NatTraversalError::NetworkError(message) => {
                if shutting_down {
                    Self::ShuttingDown
                } else {
                    Self::NetworkError(message.clone())
                }
            }
        }
    }

    pub(crate) fn from_public_operation_error(error: &NatTraversalError) -> Option<Self> {
        match error {
            NatTraversalError::NoBootstrapNodes => Some(Self::CoordinatorUnavailable),
            NatTraversalError::NoCandidatesFound
            | NatTraversalError::CandidateDiscoveryFailed(_) => Some(Self::DiscoveryExhausted),
            NatTraversalError::CoordinationFailed(message) => {
                Some(Self::NetworkError(message.clone()))
            }
            NatTraversalError::HolePunchingFailed | NatTraversalError::PunchingFailed(_) => {
                Some(Self::PunchWindowMissed)
            }
            NatTraversalError::ValidationTimeout => Some(Self::ValidationTimedOut),
            NatTraversalError::ValidationFailed(_) => Some(Self::ValidationFailed),
            NatTraversalError::Timeout => Some(Self::NetworkError(
                "overall hole-punch deadline expired".to_string(),
            )),
            NatTraversalError::ConnectionFailed(_) | NatTraversalError::PeerNotConnected => {
                Some(Self::ConnectionFailed)
            }
            NatTraversalError::ProtocolError(message)
            | NatTraversalError::ConfigError(message)
            | NatTraversalError::TraversalFailed(message) => {
                Some(Self::ProtocolViolation(message.clone()))
            }
            NatTraversalError::NetworkError(message) => Some(Self::NetworkError(message.clone())),
        }
    }
}

impl std::fmt::Display for TraversalFailureReason {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::DiscoveryExhausted => write!(f, "candidate discovery exhausted"),
            Self::CoordinatorUnavailable => write!(f, "coordinator unavailable"),
            Self::CoordinationRejected { reason } => {
                write!(f, "coordination rejected: {reason}")
            }
            Self::CoordinationExpired => write!(f, "coordination expired"),
            Self::SynchronizationExpired => write!(f, "synchronization expired"),
            Self::PunchWindowMissed => write!(f, "punch window missed"),
            Self::ValidationTimedOut => write!(f, "validation timed out"),
            Self::ValidationFailed => write!(f, "validation failed"),
            Self::ConnectionFailed => write!(f, "connection failed"),
            Self::ProtocolViolation(message) => write!(f, "protocol violation: {message}"),
            Self::NetworkError(message) => write!(f, "network error: {message}"),
            Self::ShuttingDown => write!(f, "shutting down"),
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum RetryDisposition {
    Never,
    After(std::time::Instant),
}

/// Active NAT traversal session state
#[derive(Debug)]
struct NatTraversalSession {
    /// Target peer we're trying to connect to
    peer_id: PeerId,
    /// Coordinator being used for this session
    #[allow(dead_code)]
    coordinator: SocketAddr,
    /// Current attempt number
    attempt: u32,
    /// Overall traversal session start time.
    started_at: std::time::Instant,
    /// Current phase start time.
    phase_started_at: std::time::Instant,
    /// Current phase of traversal
    phase: TraversalPhase,
    /// Discovered candidate addresses
    candidates: Vec<CandidateAddress>,
    /// Last traversal progress timestamp
    last_progress_at: std::time::Instant,
    /// Authoritative next protocol deadline for this session
    next_deadline: Option<SessionDeadline>,
    /// Stored retry schedule, if any
    retry_at: Option<std::time::Instant>,
    /// Last classified failure for this session
    last_failure: Option<TraversalFailureReason>,
    /// Session state machine
    session_state: SessionState,
}

/// Session state machine for tracking connection lifecycle
#[derive(Debug, Clone)]
pub struct SessionState {
    /// Current connection state
    pub state: ConnectionState,
    /// Last state transition time
    pub last_transition: std::time::Instant,
    /// Connection handle if established
    pub connection: Option<InnerConnection>,
    /// Active connection attempts
    pub active_attempts: Vec<(SocketAddr, std::time::Instant)>,
    /// Connection quality metrics
    pub metrics: ConnectionMetrics,
}

/// Connection state in the session lifecycle
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConnectionState {
    /// Not connected, no active attempts
    Idle,
    /// Actively attempting to connect
    Connecting,
    /// Connection established and active
    Connected,
    /// Connection is migrating to new path
    Migrating,
    /// Connection closed or failed
    Closed,
}

/// Connection quality metrics
#[derive(Debug, Clone, Default)]
pub struct ConnectionMetrics {
    /// Round-trip time estimate
    pub rtt: Option<Duration>,
    /// Packet loss rate (0.0 - 1.0)
    pub loss_rate: f64,
    /// Bytes sent
    pub bytes_sent: u64,
    /// Bytes received
    pub bytes_received: u64,
    /// Last activity timestamp
    pub last_activity: Option<std::time::Instant>,
}

/// Session state update notification
#[derive(Debug, Clone)]
pub struct SessionStateUpdate {
    /// Peer ID for this session
    pub peer_id: PeerId,
    /// Previous connection state
    pub old_state: ConnectionState,
    /// New connection state
    pub new_state: ConnectionState,
    /// Reason for state change
    pub reason: StateChangeReason,
}

#[derive(Debug, Clone, Copy)]
enum SessionStateUpdateKind {
    Timeout,
    Disconnected,
    UpdateMetrics,
    InvalidState,
    Retry,
    MigrationTimeout,
    Remove,
}

/// Reason for connection state change
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StateChangeReason {
    /// Connection attempt timed out
    Timeout,
    /// Connection successfully established
    ConnectionEstablished,
    /// Connection was closed
    ConnectionClosed,
    /// Connection migration completed
    MigrationComplete,
    /// Connection migration failed
    MigrationFailed,
    /// Connection lost due to network error
    NetworkError,
    /// Explicit close requested
    UserClosed,
}

/// Phases of NAT traversal process
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TraversalPhase {
    /// Discovering local candidates
    Discovery,
    /// Requesting coordination from bootstrap
    Coordination,
    /// Waiting for peer coordination
    Synchronization,
    /// Active hole punching
    Punching,
    /// Validating established paths
    Validation,
    /// Successfully connected
    Connected,
    /// Failed, may retry or fallback
    Failed,
}

/// Address candidate discovered during NAT traversal
#[derive(Debug, Clone)]
pub struct CandidateAddress {
    /// The candidate address
    pub address: SocketAddr,
    /// Priority for ICE-like selection
    pub priority: u32,
    /// How this candidate was discovered
    pub source: CandidateSource,
    /// Current validation state
    pub state: CandidateState,
}

impl CandidateAddress {
    /// Create a new candidate address with validation
    pub fn new(
        address: SocketAddr,
        priority: u32,
        source: CandidateSource,
    ) -> Result<Self, CandidateValidationError> {
        Self::validate_address(&address)?;
        Ok(Self {
            address,
            priority,
            source,
            state: CandidateState::New,
        })
    }

    /// Create a new candidate address with custom validation options
    ///
    /// Use this constructor when working with dual-stack sockets that may
    /// produce IPv4-mapped IPv6 addresses.
    pub fn new_with_options(
        address: SocketAddr,
        priority: u32,
        source: CandidateSource,
        allow_ipv4_mapped: bool,
    ) -> Result<Self, CandidateValidationError> {
        Self::validate_address_with_options(&address, allow_ipv4_mapped)?;
        Ok(Self {
            address,
            priority,
            source,
            state: CandidateState::New,
        })
    }

    /// Validate a candidate address for security and correctness
    ///
    /// This is the strict version that rejects IPv4-mapped addresses.
    /// For dual-stack socket support, use `validate_address_with_options`.
    pub fn validate_address(addr: &SocketAddr) -> Result<(), CandidateValidationError> {
        Self::validate_address_with_options(addr, false)
    }

    /// Validate a candidate address with configurable options
    ///
    /// # Arguments
    /// * `addr` - The address to validate
    /// * `allow_ipv4_mapped` - If true, accept IPv4-mapped IPv6 addresses (::ffff:x.x.x.x)
    ///   These addresses are produced by dual-stack sockets (IPV6_V6ONLY=0) when accepting
    ///   IPv4 connections.
    pub fn validate_address_with_options(
        addr: &SocketAddr,
        allow_ipv4_mapped: bool,
    ) -> Result<(), CandidateValidationError> {
        // Port validation
        if addr.port() == 0 {
            return Err(CandidateValidationError::InvalidPort(0));
        }

        // Well-known port validation (allow for testing)
        #[cfg(not(test))]
        if addr.port() < 1024 {
            return Err(CandidateValidationError::PrivilegedPort(addr.port()));
        }

        match addr.ip() {
            std::net::IpAddr::V4(ipv4) => {
                // IPv4 validation
                if ipv4.is_unspecified() {
                    return Err(CandidateValidationError::UnspecifiedAddress);
                }
                if ipv4.is_broadcast() {
                    return Err(CandidateValidationError::BroadcastAddress);
                }
                if ipv4.is_multicast() {
                    return Err(CandidateValidationError::MulticastAddress);
                }
                // 0.0.0.0/8 - Current network
                if ipv4.octets()[0] == 0 {
                    return Err(CandidateValidationError::ReservedAddress);
                }
                // 224.0.0.0/3 - Reserved for future use
                if ipv4.octets()[0] >= 240 {
                    return Err(CandidateValidationError::ReservedAddress);
                }
            }
            std::net::IpAddr::V6(ipv6) => {
                // IPv6 validation
                if ipv6.is_unspecified() {
                    return Err(CandidateValidationError::UnspecifiedAddress);
                }
                if ipv6.is_multicast() {
                    return Err(CandidateValidationError::MulticastAddress);
                }
                // Documentation prefix (2001:db8::/32)
                let segments = ipv6.segments();
                if segments[0] == 0x2001 && segments[1] == 0x0db8 {
                    return Err(CandidateValidationError::DocumentationAddress);
                }
                // IPv4-mapped IPv6 addresses (::ffff:0:0/96)
                // These are valid when using dual-stack sockets (IPV6_V6ONLY=0)
                if ipv6.to_ipv4_mapped().is_some() && !allow_ipv4_mapped {
                    return Err(CandidateValidationError::IPv4MappedAddress);
                }
            }
        }

        Ok(())
    }

    /// Check if this candidate is suitable for NAT traversal
    pub fn is_suitable_for_nat_traversal(&self) -> bool {
        let allow_loopback = allow_loopback_from_env();
        match self.address.ip() {
            std::net::IpAddr::V4(ipv4) => {
                // For NAT traversal, we want:
                // - Not loopback (unless testing)
                // - Not link-local (169.254.0.0/16)
                // - Not multicast/broadcast
                #[cfg(test)]
                if ipv4.is_loopback() {
                    return true;
                }
                if ipv4.is_loopback() {
                    return allow_loopback;
                }
                !ipv4.is_loopback()
                    && !ipv4.is_link_local()
                    && !ipv4.is_multicast()
                    && !ipv4.is_broadcast()
            }
            std::net::IpAddr::V6(ipv6) => {
                // For IPv6:
                // - Not loopback (unless testing)
                // - Not link-local (fe80::/10)
                // - Not unique local (fc00::/7) for external traversal
                // - Not multicast
                #[cfg(test)]
                if ipv6.is_loopback() {
                    return true;
                }
                if ipv6.is_loopback() {
                    return allow_loopback;
                }
                let segments = ipv6.segments();
                let is_link_local = (segments[0] & 0xffc0) == 0xfe80;
                let is_unique_local = (segments[0] & 0xfe00) == 0xfc00;

                !ipv6.is_loopback() && !is_link_local && !is_unique_local && !ipv6.is_multicast()
            }
        }
    }

    /// Get the priority adjusted for the current state
    pub fn effective_priority(&self) -> u32 {
        match self.state {
            CandidateState::Valid => self.priority,
            CandidateState::New => self.priority.saturating_sub(10),
            CandidateState::Validating => self.priority.saturating_sub(5),
            CandidateState::Failed => 0,
            CandidateState::Removed => 0,
        }
    }
}

/// Drop non-Global NAT-traversal candidates when at least one Global-scope
/// address is present (issue #163).
///
/// When a peer's `ADD_ADDRESS` / `CoordinationAccepted` payload leaks RFC1918,
/// link-local, ULA, or loopback entries alongside its public address(es),
/// running hole-punch probes against those private entries from an off-LAN
/// peer stalls for the full NAT probe window and never produces a usable
/// path. Keep non-globals only when the list has nothing better (e.g. pure
/// LAN peers).
fn drop_non_global_nat_candidates_when_global_present(addrs: &mut Vec<SocketAddr>) {
    let has_global = addrs.iter().any(|addr| {
        crate::reachability::socket_addr_scope(*addr)
            == Some(crate::reachability::ReachabilityScope::Global)
    });
    if !has_global {
        return;
    }
    addrs.retain(|addr| {
        crate::reachability::socket_addr_scope(*addr)
            == Some(crate::reachability::ReachabilityScope::Global)
    });
}

fn allow_loopback_from_env() -> bool {
    matches!(
        std::env::var("ANT_QUIC_ALLOW_LOOPBACK")
            .unwrap_or_default()
            .trim()
            .to_ascii_lowercase()
            .as_str(),
        "1" | "true" | "yes"
    )
}

/// Errors that can occur during candidate address validation
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum CandidateValidationError {
    /// Port number is invalid
    #[error("invalid port number: {0}")]
    InvalidPort(u16),
    /// Port is in privileged range (< 1024)
    #[error("privileged port not allowed: {0}")]
    PrivilegedPort(u16),
    /// Address is unspecified (0.0.0.0 or ::)
    #[error("unspecified address not allowed")]
    UnspecifiedAddress,
    /// Address is broadcast (IPv4 only)
    #[error("broadcast address not allowed")]
    BroadcastAddress,
    /// Address is multicast
    #[error("multicast address not allowed")]
    MulticastAddress,
    /// Address is reserved
    #[error("reserved address not allowed")]
    ReservedAddress,
    /// Address is documentation prefix
    #[error("documentation address not allowed")]
    DocumentationAddress,
    /// IPv4-mapped IPv6 address
    #[error("IPv4-mapped IPv6 address not allowed")]
    IPv4MappedAddress,
}

/// Events generated during NAT traversal process
#[derive(Debug, Clone)]
pub enum NatTraversalEvent {
    /// New candidate address discovered
    CandidateDiscovered {
        /// The peer this event relates to
        peer_id: PeerId,
        /// The discovered candidate address
        candidate: CandidateAddress,
    },
    /// Coordination request sent to bootstrap
    CoordinationRequested {
        /// The peer this event relates to
        peer_id: PeerId,
        /// Coordinator address used for synchronization
        coordinator: SocketAddr,
    },
    /// Peer coordination synchronized
    CoordinationSynchronized {
        /// The peer this event relates to
        peer_id: PeerId,
        /// The synchronized round identifier
        round_id: VarInt,
    },
    /// Hole punching started
    HolePunchingStarted {
        /// The peer this event relates to
        peer_id: PeerId,
        /// Target addresses to punch
        targets: Vec<SocketAddr>,
    },
    /// Path validated successfully
    PathValidated {
        /// The peer this event relates to
        peer_id: PeerId,
        /// Validated remote address
        address: SocketAddr,
        /// Measured round-trip time
        rtt: Duration,
    },
    /// Candidate validated successfully
    CandidateValidated {
        /// The peer this event relates to
        peer_id: PeerId,
        /// Validated candidate address
        candidate_address: SocketAddr,
    },
    /// NAT traversal completed successfully.
    ///
    /// `TraversalSucceeded` is the long-term success payload for external
    /// consumers. It carries the final connected address plus best-effort
    /// winning-candidate metadata before downstream APIs ossify around a more
    /// minimal shape.
    TraversalSucceeded {
        /// The peer this event relates to
        peer_id: PeerId,
        /// Final established address
        final_address: SocketAddr,
        /// Best-effort winning candidate pair summary when both sides remain
        /// reconstructable from discovery state.
        winning_pair: Option<CandidatePair>,
        /// Total traversal rounds / attempts consumed by this session.
        attempts: u32,
        /// Total traversal time across the full session.
        total_time: Duration,
    },
    /// Connection established after NAT traversal
    ConnectionEstablished {
        peer_id: PeerId,
        /// The socket address where the connection was established
        remote_address: SocketAddr,
        /// Who initiated the connection (Client = we connected, Server = they connected)
        side: Side,
    },
    /// Legacy compatibility failure event.
    ///
    /// New consumers should prefer [`NatTraversalEvent::TraversalTerminated`].
    /// This variant remains emitted alongside the typed terminal event during
    /// the migration window so older observers continue to function.
    TraversalFailed {
        /// The peer ID that failed to connect
        peer_id: PeerId,
        /// The NAT traversal error that occurred
        error: NatTraversalError,
        /// Whether fallback mechanisms are available
        fallback_available: bool,
    },
    /// Typed traversal progress event emitted alongside legacy phase transitions.
    TraversalProgress {
        peer_id: PeerId,
        phase: TraversalPhase,
        deadline: Option<std::time::Instant>,
    },
    /// Typed coordinator rejection event emitted alongside existing rejection handling.
    CoordinationRejected {
        peer_id: PeerId,
        coordinator: SocketAddr,
        reason: crate::coordinator_control::RejectionReason,
    },
    /// Retry was scheduled for a classified traversal failure.
    RetryScheduled {
        peer_id: PeerId,
        attempt: u32,
        retry_at: std::time::Instant,
        reason: TraversalFailureReason,
    },
    /// Traversal stalled waiting for progress before a deadline expired.
    TraversalStalled {
        peer_id: PeerId,
        phase: TraversalPhase,
        deadline_kind: TraversalDeadlineKind,
    },
    /// Typed terminal traversal outcome emitted alongside legacy TraversalFailed.
    TraversalTerminated {
        peer_id: PeerId,
        reason: TraversalFailureReason,
        fallback_available: bool,
    },
    /// Connection lost
    ConnectionLost {
        /// The peer this event relates to
        peer_id: PeerId,
        /// Reason for the connection loss
        reason: String,
    },
    /// Phase transition in NAT traversal state machine
    PhaseTransition {
        /// The peer this event relates to
        peer_id: PeerId,
        /// Old traversal phase
        from_phase: TraversalPhase,
        /// New traversal phase
        to_phase: TraversalPhase,
    },
    /// Session state changed
    SessionStateChanged {
        /// The peer this event relates to
        peer_id: PeerId,
        /// New connection state
        new_state: ConnectionState,
    },
    /// External address discovered via QUIC extension
    ExternalAddressDiscovered {
        /// The address that reported our address
        reported_by: SocketAddr,
        /// Our observed external address
        address: SocketAddr,
    },
    /// A connected peer advertised a new reachable address (ADD_ADDRESS frame).
    ///
    /// The upper layer should update its routing table so that future lookups
    /// for this peer return the advertised address.
    PeerAddressUpdated {
        /// The connected peer that sent the advertisement
        peer_addr: SocketAddr,
        /// The address the peer is advertising as reachable
        advertised_addr: SocketAddr,
    },
}

/// Errors that can occur during NAT traversal
#[derive(Debug, Clone)]
pub enum NatTraversalError {
    /// No bootstrap nodes available
    NoBootstrapNodes,
    /// Failed to discover any candidates
    NoCandidatesFound,
    /// Candidate discovery failed
    CandidateDiscoveryFailed(String),
    /// Coordination with bootstrap failed
    CoordinationFailed(String),
    /// All hole punching attempts failed
    HolePunchingFailed,
    /// Hole punching failed with specific reason
    PunchingFailed(String),
    /// Path validation failed
    ValidationFailed(String),
    /// Connection validation timed out
    ValidationTimeout,
    /// Network error during traversal
    NetworkError(String),
    /// Configuration error
    ConfigError(String),
    /// Internal protocol error
    ProtocolError(String),
    /// NAT traversal timed out
    Timeout,
    /// Connection failed after successful traversal
    ConnectionFailed(String),
    /// General traversal failure
    TraversalFailed(String),
    /// Peer not connected
    PeerNotConnected,
}

impl Default for NatTraversalConfig {
    fn default() -> Self {
        Self {
            known_peers: Vec::new(),
            max_candidates: 8,
            coordination_timeout: Duration::from_secs(10),
            enable_symmetric_nat: true,
            enable_relay_fallback: true,
            enable_relay_service: true, // Symmetric P2P: every node provides relay services
            relay_nodes: Vec::new(),
            max_concurrent_attempts: 3,
            bind_addr: None,
            additional_bind_addrs: Vec::new(),
            prefer_rfc_nat_traversal: true, // Default to RFC format for standards compliance
            // v0.13.0+: PQC is ALWAYS enabled - default to PqcConfig::default()
            // This ensures non-PQC handshakes cannot happen
            pqc: Some(crate::crypto::pqc::PqcConfig::default()),
            timeouts: crate::config::nat_timeouts::TimeoutConfig::default(),
            identity_key: None,       // Generate random key if not provided
            allow_ipv4_mapped: true,  // Required for dual-stack socket support
            transport_registry: None, // Use direct UDP binding by default
            max_message_size: crate::unified_config::P2pConfig::DEFAULT_MAX_MESSAGE_SIZE,
            max_concurrent_uni_streams: 100,
        }
    }
}

impl ConfigValidator for NatTraversalConfig {
    fn validate(&self) -> ValidationResult<()> {
        use crate::config::validation::*;

        // v0.13.0+: All nodes are symmetric P2P nodes
        // Role-based validation is removed - any node can connect/accept/coordinate

        // Validate known peers if provided
        if !self.known_peers.is_empty() {
            validate_bootstrap_nodes(&self.known_peers)?;
        }

        // Validate candidate limits
        validate_range(self.max_candidates, 1, 256, "max_candidates")?;

        // Validate coordination timeout
        validate_duration(
            self.coordination_timeout,
            Duration::from_millis(100),
            Duration::from_secs(300),
            "coordination_timeout",
        )?;

        // Validate concurrent attempts
        validate_range(
            self.max_concurrent_attempts,
            1,
            16,
            "max_concurrent_attempts",
        )?;

        // Validate max_message_size
        if self.max_message_size == 0 {
            return Err(ConfigValidationError::IncompatibleConfiguration(
                "max_message_size must be at least 1".to_string(),
            ));
        }

        // Validate configuration compatibility
        if self.max_concurrent_attempts > self.max_candidates {
            return Err(ConfigValidationError::IncompatibleConfiguration(
                "max_concurrent_attempts cannot exceed max_candidates".to_string(),
            ));
        }

        Ok(())
    }
}

impl NatTraversalEndpoint {
    fn normalize_config(mut config: NatTraversalConfig) -> NatTraversalConfig {
        // v0.13.0+: symmetric P2P is mandatory. No opt-out for NAT traversal,
        // relay fallback, or relay service.
        config.enable_symmetric_nat = true;
        config.enable_relay_fallback = true;
        config.enable_relay_service = true;
        config.prefer_rfc_nat_traversal = true;

        // Ensure PQC is always enabled, even if callers attempted to disable it.
        if config.pqc.is_none() {
            config.pqc = Some(crate::crypto::pqc::PqcConfig::default());
        }

        config
    }

    fn create_symmetric_relay_server(
        local_addr: SocketAddr,
        relay_config: MasqueRelayConfig,
    ) -> Arc<MasqueRelayServer> {
        let server = MasqueRelayServer::new(relay_config, local_addr);
        let server_arc = Arc::new(server);
        // Sessions expire after MasqueRelayConfig::session_timeout (5 min
        // default) of inactivity. Without this background reaper, expired
        // sessions accumulate forever — each retaining its outbound queues
        // and per-target HashMap, growing process RSS without bound on nodes
        // that act as a NAT-traversal relay.
        MasqueRelayServer::spawn_cleanup_task(&server_arc);
        server_arc
    }

    /// Create a new NAT traversal endpoint with proper UDP socket sharing
    ///
    /// This is the recommended constructor for most use cases. It:
    /// 1. Binds a UDP socket at the specified address
    /// 2. Creates a transport registry with the UDP transport (delegated to Quinn)
    /// 3. Passes the same socket to Quinn's QUIC endpoint
    ///
    /// This ensures that the transport registry and Quinn share the same UDP socket,
    /// enabling proper multi-transport routing.
    ///
    /// # Arguments
    ///
    /// * `bind_addr` - Address to bind the UDP socket (use `0.0.0.0:0` for random port)
    /// * `config` - NAT traversal configuration (transport_registry field is ignored)
    /// * `event_callback` - Optional callback for NAT traversal events
    /// * `token_store` - Optional token store for connection resumption
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// let config = NatTraversalConfig::default();
    /// let endpoint = NatTraversalEndpoint::new_with_shared_socket(
    ///     "0.0.0.0:9000".parse().unwrap(),
    ///     config,
    ///     None,
    ///     None,
    /// ).await?;
    /// ```
    pub async fn new_with_shared_socket(
        bind_addr: std::net::SocketAddr,
        mut config: NatTraversalConfig,
        event_callback: Option<Box<dyn Fn(NatTraversalEvent) + Send + Sync>>,
        token_store: Option<Arc<dyn crate::TokenStore>>,
    ) -> Result<Self, NatTraversalError> {
        use crate::transport::UdpTransport;

        // Bind UDP socket for both transport registry and Quinn
        let (udp_transport, quinn_socket) =
            UdpTransport::bind_for_quinn(bind_addr).await.map_err(|e| {
                NatTraversalError::NetworkError(format!("Failed to bind UDP socket: {e}"))
            })?;

        let local_addr = quinn_socket.local_addr().map_err(|e| {
            NatTraversalError::NetworkError(format!("Failed to get local address: {e}"))
        })?;

        info!("Bound shared UDP socket at {}", local_addr);

        // Create transport registry with the UDP transport
        let mut registry = TransportRegistry::new();
        registry.register(Arc::new(udp_transport));

        // Override config with our registry and bind address
        config.transport_registry = Some(Arc::new(registry));
        config.bind_addr = Some(local_addr);

        // Use new_with_socket to create the endpoint with the shared socket
        Self::new_with_socket(config, event_callback, token_store, Some(quinn_socket)).await
    }

    /// Create a new NAT traversal endpoint with optional event callback and token store
    ///
    /// **Note:** For proper multi-transport socket sharing, consider using
    /// [`new_with_shared_socket`](Self::new_with_shared_socket) instead.
    ///
    /// This constructor creates a separate UDP socket for Quinn if the transport_registry
    /// in config already has a UDP provider. Use `new_with_socket` if you need to provide
    /// a pre-bound socket for socket sharing.
    pub async fn new(
        config: NatTraversalConfig,
        event_callback: Option<Box<dyn Fn(NatTraversalEvent) + Send + Sync>>,
        token_store: Option<Arc<dyn crate::TokenStore>>,
    ) -> Result<Self, NatTraversalError> {
        // Wrap the callback in Arc so it can be shared with background tasks
        let event_callback: Option<Arc<dyn Fn(NatTraversalEvent) + Send + Sync>> =
            event_callback.map(|cb| Arc::from(cb) as Arc<dyn Fn(NatTraversalEvent) + Send + Sync>);

        let config = Self::normalize_config(config);

        // Validate configuration
        config
            .validate()
            .map_err(|e| NatTraversalError::ConfigError(e.to_string()))?;

        // Initialize known peers for discovery and coordination
        // Uses parking_lot::RwLock for faster, non-poisoning access
        let bootstrap_nodes = Arc::new(ParkingRwLock::new(
            config
                .known_peers
                .iter()
                .map(|&address| BootstrapNode {
                    address,
                    last_seen: std::time::Instant::now(),
                    can_coordinate: true, // All nodes can coordinate in v0.13.0+
                    rtt: None,
                    coordination_count: 0,
                })
                .collect(),
        ));

        // Create candidate discovery manager
        let discovery_config = DiscoveryConfig {
            total_timeout: config.coordination_timeout,
            max_candidates: config.max_candidates,
            enable_symmetric_prediction: true,
            bound_address: config.bind_addr, // Will be updated with actual address after binding
            ..DiscoveryConfig::default()
        };

        // v0.13.0+: All nodes are symmetric P2P nodes - no role parameter needed

        // Uses parking_lot::Mutex for faster, non-poisoning access
        let discovery_manager = Arc::new(ParkingMutex::new(CandidateDiscoveryManager::new(
            discovery_config,
        )));

        // Create QUIC endpoint with NAT traversal enabled
        // If transport_registry is provided in config, use it; otherwise create empty registry
        let empty_registry = crate::transport::TransportRegistry::new();
        let registry_ref = config
            .transport_registry
            .as_ref()
            .map(|arc| arc.as_ref())
            .unwrap_or(&empty_registry);
        let (inner_endpoint, event_tx, event_rx, local_addr, relay_server_config) =
            Self::create_inner_endpoint(&config, token_store, registry_ref, None).await?;

        // Update discovery manager with the actual bound address and any additional addresses
        {
            let mut discovery = discovery_manager.lock();
            discovery.set_bound_address(local_addr);
            for &extra_addr in &config.additional_bind_addrs {
                discovery.add_additional_bound_address(extra_addr);
                info!(
                    "Added dual-stack address {} to discovery candidates",
                    extra_addr
                );
            }
            info!(
                "Updated discovery manager with bound address: {} (+{} additional)",
                local_addr,
                config.additional_bind_addrs.len()
            );
        }

        let emitted_established_events = Arc::new(dashmap::DashSet::new());

        // Create MASQUE relay manager if relay fallback is enabled
        let relay_manager = if !config.relay_nodes.is_empty() {
            let relay_config = RelayManagerConfig {
                max_relays: config.relay_nodes.len().min(5), // Cap at 5 relays
                connect_timeout: config.coordination_timeout,
                ..RelayManagerConfig::default()
            };
            let manager = RelayManager::new(relay_config);
            // Add configured relay nodes
            for relay_addr in &config.relay_nodes {
                manager.add_relay_node(*relay_addr).await;
            }
            Some(Arc::new(manager))
        } else {
            None
        };

        // Symmetric P2P: Create MASQUE relay server so this node can provide relay services
        // Per ADR-004: All nodes are equal and participate in relaying with resource budgets
        let relay_server = {
            let relay_config = MasqueRelayConfig {
                max_sessions: 100, // Reasonable limit for resource budget
                require_authentication: true,
                ..MasqueRelayConfig::default()
            };
            // Use the local address as the public address (will be updated when external address is discovered)
            let server_arc = Self::create_symmetric_relay_server(local_addr, relay_config);
            info!(
                "Created MASQUE relay server on {} (symmetric P2P node)",
                local_addr
            );
            Some(server_arc)
        };

        // Clone the callback for background tasks before moving into endpoint
        let event_callback_for_poll = event_callback.clone();

        // Store transport registry from config for multi-transport support
        let transport_registry = config.transport_registry.clone();

        // Create constrained protocol engine for BLE/LoRa/Serial transports
        let constrained_engine = Arc::new(ParkingMutex::new(ConstrainedEngine::new(
            EngineConfig::default(),
        )));

        // Create channel for forwarding constrained engine events to P2pEndpoint
        let (constrained_event_tx, constrained_event_rx) = mpsc::unbounded_channel();

        // Channel for peer address updates (ADD_ADDRESS → DHT bridge)
        let (peer_addr_tx, peer_addr_rx) = mpsc::unbounded_channel();
        inner_endpoint.set_peer_address_update_tx(peer_addr_tx);

        let (observed_address_tx, observed_address_rx) = mpsc::unbounded_channel();

        let endpoint = Self {
            inner_endpoint: Some(inner_endpoint.clone()),
            config: config.clone(),
            bootstrap_nodes,
            active_sessions: Arc::new(dashmap::DashMap::new()),
            discovery_manager,
            event_callback,
            shutdown: Arc::new(AtomicBool::new(false)),
            event_tx: Some(event_tx.clone()),
            event_rx: ParkingMutex::new(event_rx),
            observed_address_tx,
            discovery_state_notify: Arc::new(tokio::sync::Notify::new()),
            traversal_event_notify: Arc::new(tokio::sync::Notify::new()),
            incoming_notify: Arc::new(tokio::sync::Notify::new()),
            pending_accepts: Arc::new(ParkingMutex::new(std::collections::VecDeque::new())),
            shutdown_notify: Arc::new(tokio::sync::Notify::new()),
            connections: Arc::new(dashmap::DashMap::new()),
            connection_lifecycle: Arc::new(ParkingRwLock::new(HashMap::new())),
            next_connection_generation: Arc::new(AtomicU64::new(1)),
            local_peer_id: Self::generate_local_peer_id(),
            timeout_config: config.timeouts.clone(),
            emitted_established_events: emitted_established_events.clone(),
            relay_manager,
            relay_sessions: Arc::new(dashmap::DashMap::new()),
            shared_relay_endpoint: Arc::new(std::sync::Mutex::new(None)),
            relay_accept_loop_started: Arc::new(std::sync::atomic::AtomicBool::new(false)),
            relay_server,
            successful_candidates: Arc::new(dashmap::DashMap::new()),
            transport_candidates: Arc::new(dashmap::DashMap::new()),
            transport_registry,
            peer_address_update_rx: TokioMutex::new(peer_addr_rx),
            relay_setup_attempted: Arc::new(std::sync::atomic::AtomicBool::new(false)),
            relay_public_addr: Arc::new(std::sync::Mutex::new(None)),
            relay_advertised_peers: Arc::new(std::sync::Mutex::new(
                std::collections::HashSet::new(),
            )),
            #[cfg(test)]
            test_observed_external_addrs: Arc::new(std::sync::Mutex::new(Vec::new())),
            #[cfg(test)]
            test_relay_publish_attempts: Arc::new(std::sync::Mutex::new(
                std::collections::HashSet::new(),
            )),
            server_config: relay_server_config,
            transport_listener_handles: Arc::new(ParkingMutex::new(Vec::new())),
            constrained_engine,
            constrained_event_tx: constrained_event_tx.clone(),
            constrained_event_rx: TokioMutex::new(constrained_event_rx),
        };

        // Multi-transport listening: Spawn receive tasks for all online transports
        // Phase 1.2: Listen on all transports, log for now (full routing in Phase 2.3)
        if let Some(registry) = &endpoint.transport_registry {
            let online_providers: Vec<_> = registry.online_providers().collect();
            let transport_count = online_providers.len();

            if transport_count > 0 {
                let transport_names: Vec<_> = online_providers
                    .iter()
                    .map(|p| format!("{}({})", p.name(), p.transport_type()))
                    .collect();

                debug!(
                    "Listening on {} transports: {}",
                    transport_count,
                    transport_names.join(", ")
                );

                let mut handles = Vec::new();

                for provider in online_providers {
                    let transport_type = provider.transport_type();
                    let transport_name = provider.name().to_string();

                    // Skip UDP transports since they're already handled by the QUIC endpoint
                    if transport_type == crate::transport::TransportType::Udp {
                        debug!(
                            "Skipping UDP transport '{}' (already handled by QUIC endpoint)",
                            transport_name
                        );
                        continue;
                    }

                    // Spawn task to receive from this transport's inbound channel
                    let mut inbound_rx = provider.inbound();
                    let shutdown_notify_clone = endpoint.shutdown_notify.clone();
                    let shutdown_flag_clone = endpoint.shutdown.clone();
                    let engine_clone = endpoint.constrained_engine.clone();
                    let registry_clone = endpoint.transport_registry.clone();
                    let event_tx_clone = endpoint.constrained_event_tx.clone();

                    let handle = tokio::spawn(async move {
                        debug!("Started listening on transport '{}'", transport_name);

                        loop {
                            // Fallback shutdown check: notify_waiters() can be missed
                            // if no task is awaiting .notified() at the moment shutdown()
                            // fires, so we check the AtomicBool on each iteration.
                            if shutdown_flag_clone.load(std::sync::atomic::Ordering::Relaxed) {
                                debug!("Shutting down transport listener for '{}'", transport_name);
                                break;
                            }

                            tokio::select! {
                                // Instant shutdown via Notify
                                _ = shutdown_notify_clone.notified() => {
                                    debug!("Shutting down transport listener for '{}'", transport_name);
                                    break;
                                }

                                // Receive inbound datagrams
                                datagram = inbound_rx.recv() => {
                                    match datagram {
                                        Some(datagram) => {
                                            debug!(
                                                "Received {} bytes from {} on transport '{}' ({})",
                                                datagram.data.len(),
                                                datagram.source,
                                                transport_name,
                                                transport_type
                                            );

                                            // Convert TransportAddr to SocketAddr for constrained engine
                                            // The constrained engine uses SocketAddr internally for connection tracking
                                            let remote_addr = datagram.source.to_synthetic_socket_addr();

                                            // Route to constrained engine for processing
                                            let responses = {
                                                let mut engine = engine_clone.lock();
                                                match engine.process_incoming(remote_addr, &datagram.data) {
                                                    Ok(responses) => responses,
                                                    Err(e) => {
                                                        debug!(
                                                            "Constrained engine error processing packet from {}: {:?}",
                                                            datagram.source, e
                                                        );
                                                        Vec::new()
                                                    }
                                                }
                                            };

                                            // Send any response packets back through the transport
                                            if !responses.is_empty() {
                                                if let Some(registry) = &registry_clone {
                                                    for (_dest_addr, response_data) in responses {
                                                        // Send response back to the source transport address
                                                        if let Err(e) = registry.send(&response_data, &datagram.source).await {
                                                            debug!(
                                                                "Failed to send constrained response to {}: {:?}",
                                                                datagram.source, e
                                                            );
                                                        }
                                                    }
                                                }
                                            }

                                            // Process events from the constrained engine and forward to P2pEndpoint
                                            // Save the source address before processing events
                                            let source_addr = datagram.source.clone();
                                            {
                                                let mut engine = engine_clone.lock();
                                                while let Some(event) = engine.next_event() {
                                                    debug!("Constrained engine event: {:?}", event);
                                                    // Forward event to P2pEndpoint via channel
                                                    let event_with_addr = ConstrainedEventWithAddr {
                                                        event,
                                                        remote_addr: source_addr.clone(),
                                                    };
                                                    if let Err(e) = event_tx_clone.send(event_with_addr) {
                                                        debug!("Failed to forward constrained event: {}", e);
                                                    }
                                                }
                                            }
                                        }
                                        None => {
                                            debug!("Transport '{}' inbound channel closed", transport_name);
                                            break;
                                        }
                                    }
                                }
                            }
                        }

                        debug!("Transport listener for '{}' terminated", transport_name);
                    });

                    handles.push(handle);
                }

                // Store handles for cleanup on shutdown
                if !handles.is_empty() {
                    let mut listener_handles = endpoint.transport_listener_handles.lock();
                    listener_handles.extend(handles);
                    info!(
                        "Started {} transport listener tasks (excluding UDP)",
                        listener_handles.len()
                    );
                }
            } else {
                debug!("No online transports found in registry");
            }
        }

        // v0.13.0+: All nodes are symmetric P2P nodes - always start accepting connections
        {
            let endpoint_clone = inner_endpoint.clone();
            let shutdown_clone = endpoint.shutdown.clone();
            let event_tx_clone = event_tx.clone();
            let connections_clone = endpoint.connections.clone();
            let connection_lifecycle_clone = endpoint.connection_lifecycle.clone();
            let next_connection_generation_clone = endpoint.next_connection_generation.clone();
            let local_peer_id = endpoint.local_peer_id;
            let emitted_events_clone = emitted_established_events.clone();
            let relay_server_clone = endpoint.relay_server.clone();
            let observed_address_tx_clone = endpoint.observed_address_tx.clone();
            let traversal_event_notify_clone = endpoint.traversal_event_notify.clone();
            let incoming_notify_clone = endpoint.incoming_notify.clone();
            let pending_accepts_clone = endpoint.pending_accepts.clone();

            tokio::spawn(async move {
                Self::accept_connections(
                    endpoint_clone,
                    shutdown_clone,
                    event_tx_clone,
                    connections_clone,
                    connection_lifecycle_clone,
                    next_connection_generation_clone,
                    local_peer_id,
                    emitted_events_clone,
                    relay_server_clone,
                    observed_address_tx_clone,
                    traversal_event_notify_clone,
                    incoming_notify_clone,
                    pending_accepts_clone,
                )
                .await;
            });

            info!("Started accepting connections (symmetric P2P node)");
        }

        // Start background discovery/orchestration task
        let discovery_manager_clone = endpoint.discovery_manager.clone();
        let discovery_state_notify_for_poll = endpoint.discovery_state_notify.clone();
        let shutdown_clone = endpoint.shutdown.clone();
        let shutdown_notify_for_poll = endpoint.shutdown_notify.clone();
        let event_tx_clone = event_tx;
        let connections_clone = endpoint.connections.clone();
        let relay_server_for_poll = endpoint.relay_server.clone();
        let traversal_event_notify_for_poll = endpoint.traversal_event_notify.clone();

        let local_peer_id_for_poll = endpoint.local_peer_id;
        let relay_setup_attempted_clone = endpoint.relay_setup_attempted.clone();
        tokio::spawn(async move {
            Self::poll_discovery(
                discovery_manager_clone,
                discovery_state_notify_for_poll,
                observed_address_rx,
                shutdown_clone,
                shutdown_notify_for_poll,
                event_tx_clone,
                connections_clone,
                relay_server_for_poll,
                event_callback_for_poll,
                traversal_event_notify_for_poll,
                local_peer_id_for_poll,
                relay_setup_attempted_clone,
            )
            .await;
        });

        info!("Started discovery/orchestration task");

        // Start local candidate discovery for our own address
        {
            // parking_lot locks don't poison - no need for map_err
            let mut discovery = endpoint.discovery_manager.lock();

            // Start discovery for our own peer ID to discover local candidates
            let local_peer_id = endpoint.local_peer_id;
            let bootstrap_nodes = endpoint.bootstrap_nodes.read().clone();

            discovery
                .start_discovery(local_peer_id, bootstrap_nodes)
                .map_err(|e| NatTraversalError::CandidateDiscoveryFailed(e.to_string()))?;

            info!(
                "Started local candidate discovery for peer {:?}",
                local_peer_id
            );
        }
        endpoint.discovery_state_notify.notify_waiters();

        Ok(endpoint)
    }

    /// Create a new NAT traversal endpoint with a pre-bound socket for Quinn sharing
    ///
    /// This variant allows passing a pre-bound `std::net::UdpSocket` that will be
    /// shared between the transport registry and Quinn's QUIC endpoint. Use this
    /// with `UdpTransport::bind_for_quinn()` for proper socket sharing.
    ///
    /// # Arguments
    ///
    /// * `config` - NAT traversal configuration
    /// * `event_callback` - Optional callback for NAT traversal events
    /// * `token_store` - Optional token store for authentication
    /// * `quinn_socket` - Pre-bound socket from `UdpTransport::bind_for_quinn()`
    ///
    /// # Example
    ///
    /// ```ignore
    /// use ant_quic::transport::udp::UdpTransport;
    ///
    /// // Bind transport and get socket for Quinn
    /// let (udp_transport, quinn_socket) = UdpTransport::bind_for_quinn(addr).await?;
    ///
    /// // Register transport
    /// registry.register(Arc::new(udp_transport))?;
    ///
    /// // Create endpoint with shared socket
    /// let endpoint = NatTraversalEndpoint::new_with_socket(
    ///     config,
    ///     None,
    ///     None,
    ///     Some(quinn_socket),
    /// ).await?;
    /// ```
    /// Create a NatTraversalEndpoint with a pre-built abstract socket.
    ///
    /// Accepts an `Arc<dyn AsyncUdpSocket>` (e.g. `DualStackSocket`) instead of a raw
    /// std socket. This allows custom socket implementations like dual-stack wrappers.
    pub async fn new_with_abstract_socket(
        config: NatTraversalConfig,
        event_callback: Option<Box<dyn Fn(NatTraversalEvent) + Send + Sync>>,
        token_store: Option<Arc<dyn crate::TokenStore>>,
        abstract_socket: Arc<dyn crate::high_level::runtime::AsyncUdpSocket>,
    ) -> Result<Self, NatTraversalError> {
        Self::new_with_socket_inner(
            config,
            event_callback,
            token_store,
            None,
            Some(abstract_socket),
        )
        .await
    }

    pub async fn new_with_socket(
        config: NatTraversalConfig,
        event_callback: Option<Box<dyn Fn(NatTraversalEvent) + Send + Sync>>,
        token_store: Option<Arc<dyn crate::TokenStore>>,
        quinn_socket: Option<std::net::UdpSocket>,
    ) -> Result<Self, NatTraversalError> {
        Self::new_with_socket_inner(config, event_callback, token_store, quinn_socket, None).await
    }

    async fn new_with_socket_inner(
        config: NatTraversalConfig,
        event_callback: Option<Box<dyn Fn(NatTraversalEvent) + Send + Sync>>,
        token_store: Option<Arc<dyn crate::TokenStore>>,
        quinn_socket: Option<std::net::UdpSocket>,
        abstract_socket: Option<Arc<dyn crate::high_level::runtime::AsyncUdpSocket>>,
    ) -> Result<Self, NatTraversalError> {
        // Wrap the callback in Arc so it can be shared with background tasks
        let event_callback: Option<Arc<dyn Fn(NatTraversalEvent) + Send + Sync>> =
            event_callback.map(|cb| Arc::from(cb) as Arc<dyn Fn(NatTraversalEvent) + Send + Sync>);

        let config = Self::normalize_config(config);

        // Validate configuration
        config
            .validate()
            .map_err(|e| NatTraversalError::ConfigError(e.to_string()))?;

        // Initialize known peers for discovery and coordination
        // Uses parking_lot::RwLock for faster, non-poisoning access
        let bootstrap_nodes = Arc::new(ParkingRwLock::new(
            config
                .known_peers
                .iter()
                .map(|&address| BootstrapNode {
                    address,
                    last_seen: std::time::Instant::now(),
                    can_coordinate: true, // All nodes can coordinate in v0.13.0+
                    rtt: None,
                    coordination_count: 0,
                })
                .collect(),
        ));

        // Create candidate discovery manager
        let discovery_config = DiscoveryConfig {
            total_timeout: config.coordination_timeout,
            max_candidates: config.max_candidates,
            enable_symmetric_prediction: true,
            bound_address: config.bind_addr, // Will be updated with actual address after binding
            ..DiscoveryConfig::default()
        };

        // v0.13.0+: All nodes are symmetric P2P nodes - no role parameter needed

        // Uses parking_lot::Mutex for faster, non-poisoning access
        let discovery_manager = Arc::new(ParkingMutex::new(CandidateDiscoveryManager::new(
            discovery_config,
        )));

        // Create QUIC endpoint with NAT traversal enabled
        // If transport_registry is provided in config, use it; otherwise create empty registry
        let empty_registry = crate::transport::TransportRegistry::new();
        let registry_ref = config
            .transport_registry
            .as_ref()
            .map(|arc| arc.as_ref())
            .unwrap_or(&empty_registry);
        let (inner_endpoint, event_tx, event_rx, local_addr, relay_server_config) =
            if let Some(abs_socket) = abstract_socket {
                let (ep, tx, rx, addr) = Self::create_inner_endpoint_with_abstract_socket(
                    &config,
                    token_store.clone(),
                    abs_socket,
                )
                .await?;
                (ep, tx, rx, addr, None)
            } else {
                Self::create_inner_endpoint(
                    &config,
                    token_store.clone(),
                    registry_ref,
                    quinn_socket,
                )
                .await?
            };

        // Update discovery manager with the actual bound address and any additional addresses
        {
            let mut discovery = discovery_manager.lock();
            discovery.set_bound_address(local_addr);
            for &extra_addr in &config.additional_bind_addrs {
                discovery.add_additional_bound_address(extra_addr);
                info!(
                    "Added dual-stack address {} to discovery candidates",
                    extra_addr
                );
            }
            info!(
                "Updated discovery manager with bound address: {} (+{} additional)",
                local_addr,
                config.additional_bind_addrs.len()
            );
        }

        let emitted_established_events = Arc::new(dashmap::DashSet::new());

        // Create MASQUE relay manager if relay fallback is enabled
        let relay_manager = if !config.relay_nodes.is_empty() {
            let relay_config = RelayManagerConfig {
                max_relays: config.relay_nodes.len().min(5), // Cap at 5 relays
                connect_timeout: config.coordination_timeout,
                ..RelayManagerConfig::default()
            };
            let manager = RelayManager::new(relay_config);
            // Add configured relay nodes
            for relay_addr in &config.relay_nodes {
                manager.add_relay_node(*relay_addr).await;
            }
            Some(Arc::new(manager))
        } else {
            None
        };

        // Symmetric P2P: Create MASQUE relay server so this node can provide relay services
        // Per ADR-004: All nodes are equal and participate in relaying with resource budgets
        let relay_server = {
            let relay_config = MasqueRelayConfig {
                max_sessions: 100, // Reasonable limit for resource budget
                require_authentication: true,
                ..MasqueRelayConfig::default()
            };
            // Use the local address as the public address (will be updated when external address is discovered)
            let server_arc = Self::create_symmetric_relay_server(local_addr, relay_config);
            info!(
                "Created MASQUE relay server on {} (symmetric P2P node)",
                local_addr
            );
            Some(server_arc)
        };

        // Clone the callback for background tasks before moving into endpoint
        let event_callback_for_poll = event_callback.clone();

        // Store transport registry from config for multi-transport support
        let transport_registry = config.transport_registry.clone();

        // Create constrained protocol engine for BLE/LoRa/Serial transports
        let constrained_engine = Arc::new(ParkingMutex::new(ConstrainedEngine::new(
            EngineConfig::default(),
        )));

        // Create channel for forwarding constrained engine events to P2pEndpoint
        let (constrained_event_tx, constrained_event_rx) = mpsc::unbounded_channel();

        // Channel for peer address updates (ADD_ADDRESS → DHT bridge)
        let (peer_addr_tx, peer_addr_rx) = mpsc::unbounded_channel();
        inner_endpoint.set_peer_address_update_tx(peer_addr_tx);

        let (observed_address_tx, observed_address_rx) = mpsc::unbounded_channel();

        let endpoint = Self {
            inner_endpoint: Some(inner_endpoint.clone()),
            config: config.clone(),
            bootstrap_nodes,
            active_sessions: Arc::new(dashmap::DashMap::new()),
            discovery_manager,
            event_callback,
            shutdown: Arc::new(AtomicBool::new(false)),
            event_tx: Some(event_tx.clone()),
            event_rx: ParkingMutex::new(event_rx),
            observed_address_tx,
            discovery_state_notify: Arc::new(tokio::sync::Notify::new()),
            traversal_event_notify: Arc::new(tokio::sync::Notify::new()),
            incoming_notify: Arc::new(tokio::sync::Notify::new()),
            pending_accepts: Arc::new(ParkingMutex::new(std::collections::VecDeque::new())),
            shutdown_notify: Arc::new(tokio::sync::Notify::new()),
            connections: Arc::new(dashmap::DashMap::new()),
            connection_lifecycle: Arc::new(ParkingRwLock::new(HashMap::new())),
            next_connection_generation: Arc::new(AtomicU64::new(1)),
            local_peer_id: Self::generate_local_peer_id(),
            timeout_config: config.timeouts.clone(),
            emitted_established_events: emitted_established_events.clone(),
            relay_manager,
            relay_sessions: Arc::new(dashmap::DashMap::new()),
            shared_relay_endpoint: Arc::new(std::sync::Mutex::new(None)),
            relay_accept_loop_started: Arc::new(std::sync::atomic::AtomicBool::new(false)),
            relay_server,
            successful_candidates: Arc::new(dashmap::DashMap::new()),
            transport_candidates: Arc::new(dashmap::DashMap::new()),
            transport_registry,
            peer_address_update_rx: TokioMutex::new(peer_addr_rx),
            relay_setup_attempted: Arc::new(std::sync::atomic::AtomicBool::new(false)),
            relay_public_addr: Arc::new(std::sync::Mutex::new(None)),
            relay_advertised_peers: Arc::new(std::sync::Mutex::new(
                std::collections::HashSet::new(),
            )),
            #[cfg(test)]
            test_observed_external_addrs: Arc::new(std::sync::Mutex::new(Vec::new())),
            #[cfg(test)]
            test_relay_publish_attempts: Arc::new(std::sync::Mutex::new(
                std::collections::HashSet::new(),
            )),
            server_config: relay_server_config,
            transport_listener_handles: Arc::new(ParkingMutex::new(Vec::new())),
            constrained_engine,
            constrained_event_tx: constrained_event_tx.clone(),
            constrained_event_rx: TokioMutex::new(constrained_event_rx),
        };

        // Multi-transport listening: Spawn receive tasks for all online transports
        // Phase 1.2: Listen on all transports, log for now (full routing in Phase 2.3)
        if let Some(registry) = &endpoint.transport_registry {
            let online_providers: Vec<_> = registry.online_providers().collect();
            let transport_count = online_providers.len();

            if transport_count > 0 {
                let transport_names: Vec<_> = online_providers
                    .iter()
                    .map(|p| format!("{}({})", p.name(), p.transport_type()))
                    .collect();

                debug!(
                    "Listening on {} transports: {}",
                    transport_count,
                    transport_names.join(", ")
                );

                let mut handles = Vec::new();

                for provider in online_providers {
                    let transport_type = provider.transport_type();
                    let transport_name = provider.name().to_string();

                    // Skip UDP transports since they're already handled by the QUIC endpoint
                    if transport_type == crate::transport::TransportType::Udp {
                        debug!(
                            "Skipping UDP transport '{}' (already handled by QUIC endpoint)",
                            transport_name
                        );
                        continue;
                    }

                    // Spawn task to receive from this transport's inbound channel
                    let mut inbound_rx = provider.inbound();
                    let shutdown_notify_clone = endpoint.shutdown_notify.clone();
                    let shutdown_flag_clone = endpoint.shutdown.clone();
                    let engine_clone = endpoint.constrained_engine.clone();
                    let registry_clone = endpoint.transport_registry.clone();
                    let event_tx_clone = endpoint.constrained_event_tx.clone();

                    let handle = tokio::spawn(async move {
                        debug!("Started listening on transport '{}'", transport_name);

                        loop {
                            // Fallback shutdown check: notify_waiters() can be missed
                            // if no task is awaiting .notified() at the moment shutdown()
                            // fires, so we check the AtomicBool on each iteration.
                            if shutdown_flag_clone.load(std::sync::atomic::Ordering::Relaxed) {
                                debug!("Shutting down transport listener for '{}'", transport_name);
                                break;
                            }

                            tokio::select! {
                                // Instant shutdown via Notify
                                _ = shutdown_notify_clone.notified() => {
                                    debug!("Shutting down transport listener for '{}'", transport_name);
                                    break;
                                }

                                // Receive inbound datagrams
                                datagram = inbound_rx.recv() => {
                                    match datagram {
                                        Some(datagram) => {
                                            debug!(
                                                "Received {} bytes from {} on transport '{}' ({})",
                                                datagram.data.len(),
                                                datagram.source,
                                                transport_name,
                                                transport_type
                                            );

                                            // Convert TransportAddr to SocketAddr for constrained engine
                                            // The constrained engine uses SocketAddr internally for connection tracking
                                            let remote_addr = datagram.source.to_synthetic_socket_addr();

                                            // Route to constrained engine for processing
                                            let responses = {
                                                let mut engine = engine_clone.lock();
                                                match engine.process_incoming(remote_addr, &datagram.data) {
                                                    Ok(responses) => responses,
                                                    Err(e) => {
                                                        debug!(
                                                            "Constrained engine error processing packet from {}: {:?}",
                                                            datagram.source, e
                                                        );
                                                        Vec::new()
                                                    }
                                                }
                                            };

                                            // Send any response packets back through the transport
                                            if !responses.is_empty() {
                                                if let Some(registry) = &registry_clone {
                                                    for (_dest_addr, response_data) in responses {
                                                        // Send response back to the source transport address
                                                        if let Err(e) = registry.send(&response_data, &datagram.source).await {
                                                            debug!(
                                                                "Failed to send constrained response to {}: {:?}",
                                                                datagram.source, e
                                                            );
                                                        }
                                                    }
                                                }
                                            }

                                            // Process events from the constrained engine and forward to P2pEndpoint
                                            // Save the source address before processing events
                                            let source_addr = datagram.source.clone();
                                            {
                                                let mut engine = engine_clone.lock();
                                                while let Some(event) = engine.next_event() {
                                                    debug!("Constrained engine event: {:?}", event);
                                                    // Forward event to P2pEndpoint via channel
                                                    let event_with_addr = ConstrainedEventWithAddr {
                                                        event,
                                                        remote_addr: source_addr.clone(),
                                                    };
                                                    if let Err(e) = event_tx_clone.send(event_with_addr) {
                                                        debug!("Failed to forward constrained event: {}", e);
                                                    }
                                                }
                                            }
                                        }
                                        None => {
                                            debug!("Transport '{}' inbound channel closed", transport_name);
                                            break;
                                        }
                                    }
                                }
                            }
                        }

                        debug!("Transport listener for '{}' terminated", transport_name);
                    });

                    handles.push(handle);
                }

                // Store handles for cleanup on shutdown
                if !handles.is_empty() {
                    let mut listener_handles = endpoint.transport_listener_handles.lock();
                    listener_handles.extend(handles);
                    info!(
                        "Started {} transport listener tasks (excluding UDP)",
                        listener_handles.len()
                    );
                }
            } else {
                debug!("No online transports found in registry");
            }
        }

        // v0.13.0+: All nodes are symmetric P2P nodes - always start accepting connections
        {
            let endpoint_clone = inner_endpoint.clone();
            let shutdown_clone = endpoint.shutdown.clone();
            let event_tx_clone = event_tx.clone();
            let connections_clone = endpoint.connections.clone();
            let connection_lifecycle_clone = endpoint.connection_lifecycle.clone();
            let next_connection_generation_clone = endpoint.next_connection_generation.clone();
            let local_peer_id = endpoint.local_peer_id;
            let emitted_events_clone = emitted_established_events.clone();
            let relay_server_clone = endpoint.relay_server.clone();
            let observed_address_tx_clone = endpoint.observed_address_tx.clone();
            let traversal_event_notify_clone = endpoint.traversal_event_notify.clone();
            let incoming_notify_clone = endpoint.incoming_notify.clone();
            let pending_accepts_clone = endpoint.pending_accepts.clone();

            tokio::spawn(async move {
                Self::accept_connections(
                    endpoint_clone,
                    shutdown_clone,
                    event_tx_clone,
                    connections_clone,
                    connection_lifecycle_clone,
                    next_connection_generation_clone,
                    local_peer_id,
                    emitted_events_clone,
                    relay_server_clone,
                    observed_address_tx_clone,
                    traversal_event_notify_clone,
                    incoming_notify_clone,
                    pending_accepts_clone,
                )
                .await;
            });

            info!("Started accepting connections (symmetric P2P node)");
        }

        // Start background discovery/orchestration task
        let discovery_manager_clone = endpoint.discovery_manager.clone();
        let discovery_state_notify_for_poll = endpoint.discovery_state_notify.clone();
        let shutdown_clone = endpoint.shutdown.clone();
        let shutdown_notify_for_poll = endpoint.shutdown_notify.clone();
        let event_tx_clone = event_tx;
        let connections_clone = endpoint.connections.clone();
        let relay_server_for_poll = endpoint.relay_server.clone();
        let traversal_event_notify_for_poll = endpoint.traversal_event_notify.clone();

        let local_peer_id_for_poll = endpoint.local_peer_id;
        let relay_setup_attempted_clone = endpoint.relay_setup_attempted.clone();
        tokio::spawn(async move {
            Self::poll_discovery(
                discovery_manager_clone,
                discovery_state_notify_for_poll,
                observed_address_rx,
                shutdown_clone,
                shutdown_notify_for_poll,
                event_tx_clone,
                connections_clone,
                relay_server_for_poll,
                event_callback_for_poll,
                traversal_event_notify_for_poll,
                local_peer_id_for_poll,
                relay_setup_attempted_clone,
            )
            .await;
        });

        info!("Started discovery/orchestration task");

        // Start local candidate discovery for our own address
        {
            // parking_lot locks don't poison - no need for map_err
            let mut discovery = endpoint.discovery_manager.lock();

            // Start discovery for our own peer ID to discover local candidates
            let local_peer_id = endpoint.local_peer_id;
            let bootstrap_nodes = endpoint.bootstrap_nodes.read().clone();

            discovery
                .start_discovery(local_peer_id, bootstrap_nodes)
                .map_err(|e| NatTraversalError::CandidateDiscoveryFailed(e.to_string()))?;

            info!(
                "Started local candidate discovery for peer {:?}",
                local_peer_id
            );
        }
        endpoint.discovery_state_notify.notify_waiters();

        Ok(endpoint)
    }

    /// Get the underlying QUIC endpoint
    pub fn get_endpoint(&self) -> Option<&crate::high_level::Endpoint> {
        self.inner_endpoint.as_ref()
    }

    /// Register a peer ID at the low-level endpoint for PUNCH_ME_NOW routing.
    pub fn register_connection_peer_id(&self, addr: SocketAddr, peer_id: PeerId) {
        if let Some(ep) = &self.inner_endpoint {
            ep.register_connection_peer_id(addr, peer_id);
        }
    }

    /// Check if a peer with the given ID has an active connection,
    /// returning its actual socket address if found. This is essential
    /// for symmetric NAT where the peer's address in the DHT differs
    /// from the connection's actual address.
    #[allow(dead_code)] // Used by try_hole_punch peer ID fallback path
    pub(crate) fn find_connection_by_peer_id(&self, peer_id: &[u8; 32]) -> Option<SocketAddr> {
        if let Some(ep) = &self.inner_endpoint {
            return ep.peer_connection_addr_by_id(peer_id);
        }
        None
    }

    /// Detect likely endpoint-dependent mapping behavior from native QUIC observations.
    ///
    /// Returns `true` if at least 2 different external ports are observed via
    /// `OBSERVED_ADDRESS` reports from different peers, indicating that the
    /// current path likely uses destination-dependent port mapping.
    ///
    /// This is used as a native QUIC optimization hint for proactive relay
    /// setup. It is not a full NAT classification and does not prove filtering
    /// behavior or long-term mapping stability.
    pub(crate) fn is_symmetric_nat(&self) -> bool {
        let mut observed_ports = std::collections::HashSet::new();

        for entry in self.connections.iter() {
            if let Some(addr) = entry.value().observed_address() {
                observed_ports.insert(addr.port());
            }
        }

        let is_symmetric = observed_ports.len() >= 2;
        if is_symmetric {
            info!(
                "Symmetric NAT detected: {} different external ports observed ({:?})",
                observed_ports.len(),
                observed_ports
            );
        }
        is_symmetric
    }

    /// Set up proactive relay for a node behind symmetric NAT.
    ///
    /// Establishes a MASQUE relay session with the bootstrap node, creates a
    /// secondary Quinn endpoint on the `MasqueRelaySocket` to accept incoming
    /// connections via the relay, and only then advertises the relay address
    /// to all connected peers.
    pub(crate) async fn setup_proactive_relay(
        &self,
        bootstrap_addr: SocketAddr,
    ) -> Result<SocketAddr, NatTraversalError> {
        info!(
            "Setting up proactive relay via bootstrap {} for symmetric NAT",
            bootstrap_addr
        );

        // Step 1: Establish or reuse the shared relay endpoint.
        let (public_addr, relay_endpoint) =
            self.ensure_shared_relay_endpoint(bootstrap_addr).await?;
        let relay_public_addr = public_addr.ok_or_else(|| {
            NatTraversalError::ConnectionFailed("Relay did not provide public address".to_string())
        })?;

        info!(
            "Relay session established, public address: {}",
            relay_public_addr
        );

        // Step 2: Spawn the accept loop exactly once for the shared relay endpoint.
        if !self
            .relay_accept_loop_started
            .swap(true, std::sync::atomic::Ordering::Relaxed)
        {
            info!(
                "Shared relay endpoint ready for accepting connections at {}",
                relay_public_addr
            );

            let connections = self.connections.clone();
            let connection_lifecycle = self.connection_lifecycle.clone();
            let next_connection_generation = self.next_connection_generation.clone();
            let local_peer_id = self.local_peer_id;
            let emitted_events = self.emitted_established_events.clone();
            let observed_address_tx = self.observed_address_tx.clone();
            let traversal_event_notify = self.traversal_event_notify.clone();
            let incoming_notify = self.incoming_notify.clone();
            let pending_accepts = self.pending_accepts.clone();
            let relay_event_tx = self.event_tx.clone();
            tokio::spawn(async move {
                loop {
                    match relay_endpoint.accept().await {
                        Some(incoming) => match incoming.await {
                            Ok(conn) => {
                                let remote = conn.remote_address();
                                // Derive a temporary PeerId from the remote address.
                                // The real identity exchange will update this later
                                // via extract_peer_id_from_connection in accept().
                                let peer_id = {
                                    use std::hash::{Hash, Hasher};
                                    let mut hasher =
                                        std::collections::hash_map::DefaultHasher::new();
                                    remote.hash(&mut hasher);
                                    let hash = hasher.finish();
                                    let mut id = [0u8; 32];
                                    id[..8].copy_from_slice(&hash.to_le_bytes());
                                    id[8..16].copy_from_slice(&hash.to_be_bytes());
                                    PeerId(id)
                                };
                                info!(
                                    "Accepted relayed connection from {} (peer {}) — registering",
                                    remote,
                                    hex::encode(&peer_id.0[..8])
                                );
                                let outcome = Self::register_connection_lifecycle_parts(
                                    local_peer_id,
                                    connections.as_ref(),
                                    connection_lifecycle.as_ref(),
                                    next_connection_generation.as_ref(),
                                    emitted_events.as_ref(),
                                    peer_id,
                                    conn.clone(),
                                );
                                let generation = match outcome {
                                    ConnectionRegistrationOutcome::Live { generation, .. } => {
                                        generation
                                    }
                                    ConnectionRegistrationOutcome::Rejected {
                                        winner_generation,
                                    } => {
                                        debug!(
                                            "Rejected relayed connection for peer {:?}; live generation {} kept",
                                            peer_id, winner_generation
                                        );
                                        continue;
                                    }
                                };

                                pending_accepts.lock().push_back(PendingAccept {
                                    peer_id,
                                    generation,
                                });
                                if let Some(ref tx) = relay_event_tx {
                                    if let Err(e) =
                                        tx.send(NatTraversalEvent::ConnectionEstablished {
                                            peer_id,
                                            remote_address: remote,
                                            side: Side::Server,
                                        })
                                    {
                                        tracing::warn!(
                                            target: "ant_quic::silent_drop",
                                            kind = "event_tx_relay_accept_established",
                                            peer_id = ?peer_id,
                                            error = %e,
                                            "silent drop"
                                        );
                                    }
                                    traversal_event_notify.notify_waiters();
                                }
                                incoming_notify.notify_waiters();
                                traversal_event_notify.notify_waiters();
                                Self::spawn_observed_address_watch_task_parts(
                                    observed_address_tx.clone(),
                                    peer_id,
                                    conn.clone(),
                                );
                            }
                            Err(e) => {
                                debug!("Relayed connection handshake failed: {}", e);
                            }
                        },
                        None => {
                            info!("Relay accept endpoint closed");
                            break;
                        }
                    }
                }
            });
        }

        // Step 4: Store relay public address for re-advertisement to future peers
        self.relay_setup_attempted
            .store(true, std::sync::atomic::Ordering::Relaxed);
        if let Ok(mut addr) = self.relay_public_addr.lock() {
            *addr = Some(relay_public_addr);
        }
        if let Ok(mut peers) = self.relay_advertised_peers.lock() {
            peers.clear();
        }

        // Step 5: Now that the endpoint is accepting, advertise to all peers
        let mut advertised = 0;
        let peer_ids: Vec<_> = self.connections.iter().map(|entry| *entry.key()).collect();
        for peer_id in peer_ids {
            if self.publish_active_relay_to_peer(peer_id) {
                advertised += 1;
            }
        }

        info!(
            "Proactive relay active at {} — advertised to {} peers",
            relay_public_addr, advertised
        );

        Ok(relay_public_addr)
    }

    /// Get the event callback
    pub fn get_event_callback(&self) -> Option<&Arc<dyn Fn(NatTraversalEvent) + Send + Sync>> {
        self.event_callback.as_ref()
    }

    fn select_best_relay_public_address_for_family<I>(
        candidates: I,
        want_ipv4: bool,
    ) -> Option<SocketAddr>
    where
        I: IntoIterator<Item = SocketAddr>,
    {
        let mut addresses: Vec<_> = candidates
            .into_iter()
            .map(normalize_socket_addr)
            .filter(|addr| !addr.ip().is_unspecified() && addr.is_ipv4() == want_ipv4)
            .filter(|addr| {
                matches!(
                    crate::reachability::socket_addr_scope(*addr),
                    Some(crate::reachability::ReachabilityScope::Global)
                )
            })
            .collect();
        addresses.sort_unstable();
        addresses.dedup();
        addresses.into_iter().next()
    }

    fn select_relay_server_public_addresses(
        candidates: &[SocketAddr],
    ) -> (Option<SocketAddr>, Option<SocketAddr>) {
        (
            Self::select_best_relay_public_address_for_family(candidates.iter().copied(), true),
            Self::select_best_relay_public_address_for_family(candidates.iter().copied(), false),
        )
    }

    fn select_best_relay_public_address_with_known_preference<I, J>(
        preferred_candidates: I,
        fallback_candidates: J,
        want_ipv4: bool,
    ) -> Option<SocketAddr>
    where
        I: IntoIterator<Item = SocketAddr>,
        J: IntoIterator<Item = SocketAddr>,
    {
        Self::select_best_relay_public_address_for_family(preferred_candidates, want_ipv4).or_else(
            || Self::select_best_relay_public_address_for_family(fallback_candidates, want_ipv4),
        )
    }

    fn best_observed_external_address_for_family(&self, want_ipv4: bool) -> Option<SocketAddr> {
        #[cfg(test)]
        if let Ok(addrs) = self.test_observed_external_addrs.lock()
            && let Some(addr) =
                Self::select_best_relay_public_address_for_family(addrs.iter().copied(), want_ipv4)
        {
            return Some(addr);
        }

        let known_peer_addrs: std::collections::HashSet<_> =
            self.config.known_peers.iter().copied().collect();
        let known_peer_candidates: Vec<_> = self
            .connections
            .iter()
            .filter(|entry| known_peer_addrs.contains(&entry.value().remote_address()))
            .flat_map(|entry| entry.value().all_observed_addresses())
            .collect();

        let all_candidates: Vec<_> = self
            .connections
            .iter()
            .flat_map(|entry| entry.value().all_observed_addresses())
            .collect();
        Self::select_best_relay_public_address_with_known_preference(
            known_peer_candidates,
            all_candidates,
            want_ipv4,
        )
    }

    pub(crate) fn reconcile_relay_server_public_addresses(&self, mapped_addr: Option<SocketAddr>) {
        let mapped_addr = mapped_addr
            .map(normalize_socket_addr)
            .filter(|addr| !addr.ip().is_unspecified());
        let ipv4 = match mapped_addr {
            Some(addr) if addr.is_ipv4() => Some(addr),
            _ => self.best_observed_external_address_for_family(true),
        };
        let ipv6 = match mapped_addr {
            Some(addr) if addr.is_ipv6() => Some(addr),
            _ => self.best_observed_external_address_for_family(false),
        };

        if let Some(server) = self.relay_server.as_ref() {
            server.reconcile_public_addresses(ipv4, ipv6);
        }
    }

    pub(crate) fn publish_active_relay_to_peer(&self, peer_id: PeerId) -> bool {
        let relay_public_addr = match self.relay_public_addr.lock().ok().and_then(|guard| *guard) {
            Some(addr) => addr,
            None => return false,
        };

        let connection = match self.connections.get_mut(&peer_id) {
            Some(connection) => connection,
            None => {
                #[cfg(test)]
                {
                    return self
                        .test_relay_publish_attempts
                        .lock()
                        .ok()
                        .is_some_and(|mut attempts| attempts.insert(peer_id));
                }
                #[allow(unreachable_code)]
                return false;
            }
        };
        let remote_addr = connection.remote_address();

        if self
            .relay_advertised_peers
            .lock()
            .ok()
            .is_some_and(|peers| peers.contains(&remote_addr))
        {
            return false;
        }

        match connection.send_nat_address_advertisement(relay_public_addr, 100) {
            Ok(sequence) => {
                if let Ok(mut peers) = self.relay_advertised_peers.lock() {
                    peers.insert(remote_addr);
                }
                info!(
                    peer_id = ?peer_id,
                    remote_addr = %remote_addr,
                    relay_public_addr = %relay_public_addr,
                    seq = sequence,
                    "Advertised proactive relay address to connected peer"
                );
                true
            }
            Err(error) => {
                debug!(
                    peer_id = ?peer_id,
                    remote_addr = %remote_addr,
                    relay_public_addr = %relay_public_addr,
                    error = %error,
                    "Failed to advertise proactive relay address to connected peer"
                );
                false
            }
        }
    }

    #[cfg(test)]
    pub(crate) fn relay_server_public_address(&self) -> Option<SocketAddr> {
        self.relay_server
            .as_ref()
            .map(|server| server.public_address())
    }

    #[cfg(test)]
    pub(crate) fn set_test_relay_public_addr(&self, relay_public_addr: SocketAddr) {
        if let Ok(mut addr) = self.relay_public_addr.lock() {
            *addr = Some(relay_public_addr);
        }
        if let Ok(mut peers) = self.relay_advertised_peers.lock() {
            peers.clear();
        }
    }

    #[cfg(test)]
    pub(crate) fn set_test_observed_external_addrs(&self, addrs: Vec<SocketAddr>) {
        if let Ok(mut observed) = self.test_observed_external_addrs.lock() {
            *observed = addrs;
        }
    }

    #[cfg(test)]
    pub(crate) fn test_relay_publish_attempted_for(&self, peer_id: PeerId) -> bool {
        self.test_relay_publish_attempts
            .lock()
            .ok()
            .is_some_and(|attempts| attempts.contains(&peer_id))
    }

    /// Get the transport registry if configured
    ///
    /// Returns the transport registry that was provided at construction time,
    /// enabling multi-transport support and shared socket management.
    pub fn transport_registry(&self) -> Option<&Arc<TransportRegistry>> {
        self.transport_registry.as_ref()
    }

    /// Get a reference to the constrained protocol engine
    ///
    /// The constrained engine handles connections over non-QUIC transports
    /// (BLE, LoRa, Serial, etc.). Use this for:
    /// - Initiating constrained connections
    /// - Sending/receiving data on constrained connections
    /// - Processing constrained connection events
    ///
    /// # Thread Safety
    ///
    /// The returned `Arc<ParkingMutex<ConstrainedEngine>>` is thread-safe and can
    /// be shared across async tasks.
    pub fn constrained_engine(&self) -> &Arc<ParkingMutex<ConstrainedEngine>> {
        &self.constrained_engine
    }

    /// Try to receive a constrained engine event without blocking
    ///
    /// Returns the next event from constrained transports (BLE/LoRa) if available.
    /// This allows P2pEndpoint to poll for data received on non-UDP transports.
    ///
    /// # Returns
    ///
    /// - `Some(event)` - An event with the data and source transport address
    /// - `None` - No events currently available
    pub fn try_recv_constrained_event(&self) -> Option<ConstrainedEventWithAddr> {
        // Use try_lock() since this is a synchronous function
        self.constrained_event_rx.try_lock().ok()?.try_recv().ok()
    }

    /// Receive a constrained engine event asynchronously
    ///
    /// Waits for the next event from constrained transports (BLE/LoRa) without polling.
    /// This eliminates the need for polling loops with sleep intervals.
    ///
    /// # Returns
    ///
    /// - `Some(event)` - An event with the data and source transport address
    /// - `None` - The channel has been closed
    pub async fn recv_constrained_event(&self) -> Option<ConstrainedEventWithAddr> {
        self.constrained_event_rx.lock().await.recv().await
    }

    /// Get a reference to the constrained event sender for testing
    ///
    /// This is primarily used for testing to inject events.
    pub fn constrained_event_tx(&self) -> &mpsc::UnboundedSender<ConstrainedEventWithAddr> {
        &self.constrained_event_tx
    }

    /// Emit an event to both the events vector and the callback (if present)
    ///
    /// This helper method eliminates the repeated pattern of:
    /// ```ignore
    /// if let Some(ref callback) = self.event_callback {
    ///     callback(event.clone());
    /// }
    /// events.push(event);
    /// ```
    #[inline]
    fn emit_event(&self, events: &mut Vec<NatTraversalEvent>, event: NatTraversalEvent) {
        if let Some(ref callback) = self.event_callback {
            callback(event.clone());
        }
        self.traversal_event_notify.notify_waiters();
        events.push(event);
    }

    /// Initiate NAT traversal to a peer (returns immediately, progress via events)
    pub fn initiate_nat_traversal(
        &self,
        peer_id: PeerId,
        coordinator: SocketAddr,
    ) -> Result<(), NatTraversalError> {
        // CRITICAL: Check for existing connection FIRST - no NAT traversal needed if already connected.
        // This prevents wasting resources on hole punching when we already have a direct connection.
        if self.has_existing_connection(&peer_id) {
            debug!(
                "Direct connection already exists for peer {:?}, skipping NAT traversal",
                peer_id
            );
            return Ok(()); // Already connected, not an error
        }

        // CRITICAL: Check for existing active session FIRST to prevent race conditions.
        // Multiple concurrent calls for the same peer would otherwise:
        // 1. Each create a new session
        // 2. Each insert into DashMap (replacing previous)
        // 3. Then fail in start_discovery() with "already in progress"
        // This race condition can cause resource leaks and potential deadlocks.
        if self.active_sessions.contains_key(&peer_id) {
            debug!(
                "NAT traversal already in progress for peer {:?}, skipping duplicate request",
                peer_id
            );
            return Ok(()); // Already handling this peer, not an error
        }

        info!(
            "Starting NAT traversal to peer {:?} via coordinator {}",
            peer_id, coordinator
        );

        // Create new session
        let now = std::time::Instant::now();
        let session = NatTraversalSession {
            peer_id,
            coordinator,
            attempt: 1,
            started_at: now,
            phase_started_at: now,
            phase: TraversalPhase::Discovery,
            candidates: Vec::new(),
            last_progress_at: now,
            next_deadline: None,
            retry_at: None,
            last_failure: None,
            session_state: SessionState {
                state: ConnectionState::Connecting,
                last_transition: now,

                connection: None,
                active_attempts: Vec::new(),
                metrics: ConnectionMetrics::default(),
            },
        };

        // Store session
        // DashMap provides lock-free .insert()
        self.active_sessions.insert(peer_id, session);

        // Start candidate discovery - parking_lot::RwLock doesn't poison
        let bootstrap_nodes_vec = self.bootstrap_nodes.read().clone();

        {
            // parking_lot::Mutex doesn't poison
            let mut discovery = self.discovery_manager.lock();

            discovery
                .start_discovery(peer_id, bootstrap_nodes_vec)
                .map_err(|e| NatTraversalError::CandidateDiscoveryFailed(e.to_string()))?;
        }
        self.discovery_state_notify.notify_waiters();

        // Emit event
        if let Some(ref callback) = self.event_callback {
            callback(NatTraversalEvent::CoordinationRequested {
                peer_id,
                coordinator,
            });
        }

        if self.send_coordination_request(peer_id, coordinator).is_ok() {
            if let Some(mut entry) = self.active_sessions.get_mut(&peer_id) {
                let session = entry.value_mut();
                Self::set_session_phase(
                    session,
                    std::time::Instant::now(),
                    TraversalPhase::Synchronization,
                );
            }
            self.incoming_notify.notify_waiters();
            self.traversal_event_notify.notify_waiters();
        }

        // NAT traversal will proceed via poll() calls and state machine updates
        Ok(())
    }

    /// Poll all active sessions and update their states
    pub fn poll_sessions(&self) -> Result<Vec<SessionStateUpdate>, NatTraversalError> {
        let mut updates = Vec::new();
        let now = std::time::Instant::now();

        // Snapshot keys to avoid holding iter_mut() write guards on all shards
        // while accessing self.connections (another DashMap)
        let session_keys: Vec<PeerId> = self.active_sessions.iter().map(|e| *e.key()).collect();
        for peer_id in session_keys {
            let Some(mut entry) = self.active_sessions.get_mut(&peer_id) else {
                continue;
            };
            let session = entry.value_mut();
            let mut state_changed = false;

            match session.session_state.state {
                ConnectionState::Connecting => {
                    // Check connection timeout
                    let elapsed = now.duration_since(session.session_state.last_transition);
                    if elapsed
                        > self
                            .timeout_config
                            .nat_traversal
                            .connection_establishment_timeout
                    {
                        session.session_state.state = ConnectionState::Closed;
                        session.session_state.last_transition = now;
                        state_changed = true;

                        updates.push(SessionStateUpdate {
                            peer_id,
                            old_state: ConnectionState::Connecting,
                            new_state: ConnectionState::Closed,
                            reason: StateChangeReason::Timeout,
                        });
                    }

                    // Check if any connection attempts succeeded
                    // First, check the connections DashMap to see if a connection was established
                    let has_connection = self.connections.contains_key(&peer_id);

                    if has_connection || session.session_state.connection.is_some() {
                        // Update session_state.connection from the connections DashMap
                        if session.session_state.connection.is_none() {
                            if let Some(conn_ref) = self.connections.get(&peer_id) {
                                session.session_state.connection = Some(conn_ref.clone());
                            }
                        }

                        session.session_state.state = ConnectionState::Connected;
                        session.session_state.last_transition = now;
                        state_changed = true;

                        updates.push(SessionStateUpdate {
                            peer_id,
                            old_state: ConnectionState::Connecting,
                            new_state: ConnectionState::Connected,
                            reason: StateChangeReason::ConnectionEstablished,
                        });
                    }
                }
                ConnectionState::Connected => {
                    // Check connection health

                    {
                        // TODO: Implement proper connection health check
                        // For now, just update metrics
                    }

                    // Update metrics
                    session.session_state.metrics.last_activity = Some(now);
                }
                ConnectionState::Migrating => {
                    // Check migration timeout
                    let elapsed = now.duration_since(session.session_state.last_transition);
                    if elapsed > self.timeout_config.nat_traversal.migration_timeout {
                        // Migration timed out, return to connected or close

                        if session.session_state.connection.is_some() {
                            session.session_state.state = ConnectionState::Connected;
                            state_changed = true;

                            updates.push(SessionStateUpdate {
                                peer_id,
                                old_state: ConnectionState::Migrating,
                                new_state: ConnectionState::Connected,
                                reason: StateChangeReason::MigrationComplete,
                            });
                        } else {
                            session.session_state.state = ConnectionState::Closed;
                            state_changed = true;

                            updates.push(SessionStateUpdate {
                                peer_id,
                                old_state: ConnectionState::Migrating,
                                new_state: ConnectionState::Closed,
                                reason: StateChangeReason::MigrationFailed,
                            });
                        }

                        session.session_state.last_transition = now;
                    }
                }
                _ => {}
            }

            // Emit events for state changes
            if state_changed {
                if let Some(ref callback) = self.event_callback {
                    callback(NatTraversalEvent::SessionStateChanged {
                        peer_id,
                        new_state: session.session_state.state,
                    });
                }
            }
        }

        Ok(updates)
    }

    /// Start a legacy session-state driver task.
    ///
    /// The public API remains the same, but the implementation is no longer a
    /// fixed-interval ticker. Runtime events and explicit deadlines drive wakeups;
    /// `interval` is now only a maximum silence cap for refreshing connected
    /// metrics or catching straggler legacy state transitions.
    pub fn start_session_polling(&self, interval: Duration) -> tokio::task::JoinHandle<()> {
        let sessions = self.active_sessions.clone();
        let shutdown = self.shutdown.clone();
        let shutdown_notify = self.shutdown_notify.clone();
        let traversal_event_notify = self.traversal_event_notify.clone();
        let discovery_state_notify = self.discovery_state_notify.clone();
        let timeout_config = self.timeout_config.clone();
        let interval_cap = if interval.is_zero() {
            Duration::from_millis(1)
        } else {
            interval
        };

        tokio::spawn(async move {
            loop {
                if shutdown.load(Ordering::Relaxed) {
                    break;
                }

                let traversal_notified = traversal_event_notify.notified();
                let discovery_notified = discovery_state_notify.notified();
                tokio::pin!(traversal_notified);
                tokio::pin!(discovery_notified);

                let now = std::time::Instant::now();
                let wake_at = sessions
                    .iter()
                    .filter_map(|entry| {
                        let session = entry.value();
                        let deadline = match session.session_state.state {
                            ConnectionState::Connecting => Some(
                                session.session_state.last_transition
                                    + timeout_config
                                        .nat_traversal
                                        .connection_establishment_timeout,
                            ),
                            ConnectionState::Connected => None,
                            ConnectionState::Idle => Some(
                                session.session_state.last_transition
                                    + timeout_config.discovery.server_reflexive_cache_ttl,
                            ),
                            ConnectionState::Migrating => Some(
                                session.session_state.last_transition
                                    + timeout_config.nat_traversal.migration_timeout,
                            ),
                            ConnectionState::Closed => Some(
                                session.session_state.last_transition
                                    + timeout_config.discovery.interface_cache_ttl,
                            ),
                        }?;
                        Some(deadline.max(now))
                    })
                    .min()
                    .map(tokio::time::Instant::from_std)
                    .map(|next| next.min(tokio::time::Instant::now() + interval_cap))
                    .unwrap_or_else(|| tokio::time::Instant::now() + interval_cap);

                tokio::select! {
                    _ = traversal_notified.as_mut() => {}
                    _ = discovery_notified.as_mut() => {}
                    _ = tokio::time::sleep_until(wake_at) => {}
                    _ = shutdown_notify.notified() => break,
                }

                if shutdown.load(Ordering::Relaxed) {
                    break;
                }

                let sessions_to_update: Vec<_> = sessions
                    .iter()
                    .filter_map(|entry| {
                        let peer_id = *entry.key();
                        let session = entry.value();
                        let now = std::time::Instant::now();
                        let elapsed = now.duration_since(session.session_state.last_transition);

                        match session.session_state.state {
                            ConnectionState::Connecting => {
                                if elapsed
                                    > timeout_config
                                        .nat_traversal
                                        .connection_establishment_timeout
                                {
                                    Some((peer_id, SessionStateUpdateKind::Timeout))
                                } else {
                                    None
                                }
                            }
                            ConnectionState::Connected => {
                                if let Some(ref conn) = session.session_state.connection {
                                    if conn.close_reason().is_some() {
                                        Some((peer_id, SessionStateUpdateKind::Disconnected))
                                    } else {
                                        Some((peer_id, SessionStateUpdateKind::UpdateMetrics))
                                    }
                                } else {
                                    Some((peer_id, SessionStateUpdateKind::InvalidState))
                                }
                            }
                            ConnectionState::Idle => {
                                if elapsed > timeout_config.discovery.server_reflexive_cache_ttl {
                                    Some((peer_id, SessionStateUpdateKind::Retry))
                                } else {
                                    None
                                }
                            }
                            ConnectionState::Migrating => {
                                if elapsed > timeout_config.nat_traversal.migration_timeout {
                                    Some((peer_id, SessionStateUpdateKind::MigrationTimeout))
                                } else {
                                    None
                                }
                            }
                            ConnectionState::Closed => {
                                if elapsed > timeout_config.discovery.interface_cache_ttl {
                                    Some((peer_id, SessionStateUpdateKind::Remove))
                                } else {
                                    None
                                }
                            }
                        }
                    })
                    .collect();

                for (peer_id, update) in sessions_to_update {
                    match update {
                        SessionStateUpdateKind::Timeout => {
                            if let Some(mut session) = sessions.get_mut(&peer_id) {
                                session.session_state.state = ConnectionState::Closed;
                                session.session_state.last_transition = std::time::Instant::now();
                                tracing::warn!("Connection to {:?} timed out", peer_id);
                            }
                        }
                        SessionStateUpdateKind::Disconnected => {
                            if let Some(mut session) = sessions.get_mut(&peer_id) {
                                session.session_state.state = ConnectionState::Closed;
                                session.session_state.last_transition = std::time::Instant::now();
                                session.session_state.connection = None;
                                tracing::info!("Connection to {:?} closed", peer_id);
                            }
                        }
                        SessionStateUpdateKind::UpdateMetrics => {
                            if let Some(mut session) = sessions.get_mut(&peer_id) {
                                if let Some(ref conn) = session.session_state.connection {
                                    let stats = conn.stats();
                                    session.session_state.metrics.rtt = Some(stats.path.rtt);
                                    session.session_state.metrics.loss_rate =
                                        stats.path.lost_packets as f64
                                            / stats.path.sent_packets.max(1) as f64;
                                }
                            }
                        }
                        SessionStateUpdateKind::InvalidState => {
                            if let Some(mut session) = sessions.get_mut(&peer_id) {
                                session.session_state.state = ConnectionState::Closed;
                                session.session_state.last_transition = std::time::Instant::now();
                                tracing::error!("Session {:?} in invalid state", peer_id);
                            }
                        }
                        SessionStateUpdateKind::Retry => {
                            if let Some(mut session) = sessions.get_mut(&peer_id) {
                                session.session_state.state = ConnectionState::Connecting;
                                session.session_state.last_transition = std::time::Instant::now();
                                session.attempt += 1;
                                tracing::info!(
                                    "Retrying connection to {:?} (attempt {})",
                                    peer_id,
                                    session.attempt
                                );
                            }
                        }
                        SessionStateUpdateKind::MigrationTimeout => {
                            if let Some(mut session) = sessions.get_mut(&peer_id) {
                                session.session_state.state = ConnectionState::Closed;
                                session.session_state.last_transition = std::time::Instant::now();
                                tracing::warn!("Migration timeout for {:?}", peer_id);
                            }
                        }
                        SessionStateUpdateKind::Remove => {
                            sessions.remove(&peer_id);
                            tracing::debug!("Removed old session for {:?}", peer_id);
                        }
                    }
                }
            }
        })
    }

    // OBSERVED_ADDRESS frames are now handled at the connection layer; manual injection removed

    /// Get current NAT traversal statistics
    pub fn set_local_peer_id(&mut self, peer_id: PeerId) {
        self.local_peer_id = peer_id;
    }

    pub fn bootstrap_addresses(&self) -> Vec<SocketAddr> {
        self.bootstrap_nodes
            .read()
            .iter()
            .map(|n| n.address)
            .collect()
    }

    pub fn bootstrap_address_for_peer(&self, peer_id: PeerId) -> Option<SocketAddr> {
        self.successful_candidates
            .get(&peer_id)
            .map(|v| *v.value())
            .or_else(|| self.connections.get(&peer_id).map(|c| c.remote_address()))
    }

    pub fn preferred_coordinator(&self) -> Option<SocketAddr> {
        self.select_coordinator()
            .or_else(|| self.bootstrap_addresses().into_iter().next())
    }

    pub fn record_bootstrap_direct_connection(
        &self,
        peer_id: PeerId,
        addr: &SocketAddr,
        _rtt: Option<Duration>,
    ) {
        self.successful_candidates.insert(peer_id, *addr);
        let _ = self.add_bootstrap_node(*addr);
    }

    pub fn add_local_external_candidate(
        &self,
        addr: SocketAddr,
    ) -> Result<bool, NatTraversalError> {
        self.discovery_manager
            .lock()
            .accept_quic_discovered_address(self.local_peer_id, addr)
            .map_err(|error| NatTraversalError::CandidateDiscoveryFailed(error.to_string()))
    }

    pub fn remove_local_external_candidate(&self, addr: SocketAddr) -> bool {
        self.discovery_manager
            .lock()
            .remove_external_address(self.local_peer_id, addr)
    }

    pub async fn handle_coordinator_control_message(
        &self,
        from_peer_id: PeerId,
        _connection: InnerConnection,
        bytes: &[u8],
    ) -> Result<bool, NatTraversalError> {
        let Some(envelope) = decode_coordinator_control(bytes).map_err(|e| {
            NatTraversalError::ProtocolError(format!("coordinator control decode failed: {e}"))
        })?
        else {
            return Ok(false);
        };

        match &envelope.message {
            CoordinatorControlMessage::CoordinationRequest {
                initiator,
                target,
                round,
                initiator_addrs,
            } => {
                info!(
                    "coordinator control request request_id={} from_peer={:?} initiator={:?} target={:?} round={}",
                    envelope.request_id, from_peer_id, initiator, target, round
                );

                if *initiator != from_peer_id {
                    debug!(
                        "coordinator control request initiator_mismatch request_id={} from_peer={:?} initiator={:?}",
                        envelope.request_id, from_peer_id, initiator
                    );
                    return Ok(true);
                }

                let reject = |reason| CoordinatorControlEnvelope {
                    request_id: envelope.request_id,
                    expires_at_unix_ms: envelope.expires_at_unix_ms,
                    message: CoordinatorControlMessage::CoordinationRejected {
                        initiator: *initiator,
                        target: *target,
                        round: *round,
                        reason,
                    },
                };

                if *initiator == *target {
                    let _ = self
                        .send_coordinator_control_on_connection(
                            _connection.clone(),
                            reject(RejectionReason::SelfTarget),
                        )
                        .await;
                    return Ok(true);
                }

                if envelope.expires_at_unix_ms < now_unix_ms() {
                    let _ = self
                        .send_coordinator_control_on_connection(
                            _connection.clone(),
                            reject(RejectionReason::Expired),
                        )
                        .await;
                    return Ok(true);
                }

                if !note_rate_limit_and_check(self.local_peer_id(), from_peer_id) {
                    let _ = self
                        .send_coordinator_control_on_connection(
                            _connection.clone(),
                            reject(RejectionReason::RateLimited),
                        )
                        .await;
                    return Ok(true);
                }

                if initiator_addrs.is_empty() {
                    let _ = self
                        .send_coordinator_control_on_connection(
                            _connection.clone(),
                            reject(RejectionReason::InternalError),
                        )
                        .await;
                    return Ok(true);
                }

                let target_conn = match self.get_connection(target)? {
                    Some(c) => c,
                    None => {
                        let _ = self
                            .send_coordinator_control_on_connection(
                                _connection.clone(),
                                reject(RejectionReason::UnknownTarget),
                            )
                            .await;
                        return Ok(true);
                    }
                };

                let initiator_conn = _connection.clone();
                let mut normalized_initiator_addrs = initiator_addrs
                    .iter()
                    .copied()
                    .map(normalize_socket_addr)
                    .collect::<Vec<_>>();
                normalized_initiator_addrs.sort_unstable();
                normalized_initiator_addrs.dedup();

                let mut normalized_target_addrs =
                    vec![normalize_socket_addr(target_conn.remote_address())];
                normalized_target_addrs.sort_unstable();
                normalized_target_addrs.dedup();

                if !normalized_initiator_addrs.is_empty() && !normalized_target_addrs.is_empty() {
                    for addr in normalized_target_addrs.iter().copied() {
                        if let Ok(sequence) = initiator_conn.send_nat_address_advertisement(addr, 0)
                        {
                            let _ =
                                initiator_conn.send_nat_punch_coordination(sequence, addr, *round);
                        }
                    }

                    for addr in normalized_initiator_addrs.iter().copied() {
                        if let Ok(sequence) = target_conn.send_nat_address_advertisement(addr, 0) {
                            let _ = target_conn.send_nat_punch_coordination(sequence, addr, *round);
                        }
                    }

                    let accepted = CoordinatorControlEnvelope {
                        request_id: envelope.request_id,
                        expires_at_unix_ms: envelope.expires_at_unix_ms,
                        message: CoordinatorControlMessage::CoordinationAccepted {
                            initiator: *initiator,
                            target: *target,
                            round: *round,
                            initiator_addrs: normalized_initiator_addrs.clone(),
                            target_addrs: normalized_target_addrs.clone(),
                        },
                    };

                    if self
                        .send_coordinator_control_on_connection(
                            initiator_conn.clone(),
                            accepted.clone(),
                        )
                        .await
                        .is_ok()
                    {
                        let _ = self
                            .send_coordinator_control_on_connection(target_conn.clone(), accepted)
                            .await;
                        info!(
                            "coordinator control request fast-path handled request_id={} from_peer={:?} initiator={:?} target={:?} round={}",
                            envelope.request_id, from_peer_id, initiator, target, round
                        );
                        return Ok(true);
                    }
                }

                remember_pending_request(
                    envelope.request_id,
                    PendingRequest {
                        initiator: *initiator,
                        target: *target,
                        round: *round,
                        initiator_addrs: initiator_addrs.clone(),
                        expires_at_unix_ms: envelope.expires_at_unix_ms,
                        local_expires_at: monotonic_deadline_from_unix_ms(
                            envelope.expires_at_unix_ms,
                        ),
                    },
                );

                let offer = CoordinatorControlEnvelope {
                    request_id: envelope.request_id,
                    expires_at_unix_ms: envelope.expires_at_unix_ms,
                    message: CoordinatorControlMessage::CoordinationOffer {
                        initiator: *initiator,
                        target: *target,
                        round: *round,
                        initiator_addrs: initiator_addrs.clone(),
                    },
                };

                self.send_coordinator_control_on_connection(target_conn, offer)
                    .await?;
                info!(
                    "coordinator control request handled request_id={} from_peer={:?} initiator={:?} target={:?} round={}",
                    envelope.request_id, from_peer_id, initiator, target, round
                );
                Ok(true)
            }
            CoordinatorControlMessage::CoordinationOffer {
                initiator,
                target,
                round,
                initiator_addrs,
            } => {
                info!(
                    "coordinator control offer request_id={} from_peer={:?} initiator={:?} target={:?} round={}",
                    envelope.request_id, from_peer_id, initiator, target, round
                );

                if *target != self.local_peer_id() {
                    debug!(
                        "coordinator control offer ignored wrong_target request_id={} local={:?} target={:?}",
                        envelope.request_id,
                        self.local_peer_id(),
                        target
                    );
                    return Ok(true);
                }

                if *initiator == *target {
                    debug!(
                        "coordinator control offer ignored self_target request_id={} initiator={:?} target={:?}",
                        envelope.request_id, initiator, target
                    );
                    return Ok(true);
                }

                if envelope.expires_at_unix_ms < now_unix_ms() {
                    debug!(
                        "coordinator control offer ignored expired request_id={} expires_at_unix_ms={}",
                        envelope.request_id, envelope.expires_at_unix_ms
                    );
                    return Ok(true);
                }

                if from_peer_id == *initiator {
                    debug!(
                        "coordinator control offer ignored coordinator_is_initiator request_id={} from_peer={:?} initiator={:?}",
                        envelope.request_id, from_peer_id, initiator
                    );
                    return Ok(true);
                }

                if initiator_addrs.is_empty() {
                    debug!(
                        "coordinator control offer ignored empty_initiator_addrs request_id={}",
                        envelope.request_id
                    );
                    return Ok(true);
                }

                remember_inbound_offer(
                    self.local_peer_id(),
                    envelope.request_id,
                    InboundOffer {
                        coordinator: from_peer_id,
                        initiator: *initiator,
                        target: *target,
                        request_id: envelope.request_id,
                        round: *round,
                        initiator_addrs: initiator_addrs.clone(),
                        expires_at_unix_ms: envelope.expires_at_unix_ms,
                        local_expires_at: monotonic_deadline_from_unix_ms(
                            envelope.expires_at_unix_ms,
                        ),
                    },
                );

                let mut target_addrs = self
                    .get_all_observed_external_addresses()?
                    .into_iter()
                    .map(normalize_socket_addr)
                    .collect::<Vec<_>>();

                if target_addrs.is_empty()
                    && let Some(addr) = self.get_observed_external_address()?
                {
                    target_addrs.push(normalize_socket_addr(addr));
                }

                if target_addrs.is_empty()
                    && let Some(endpoint) = &self.inner_endpoint
                {
                    if let Ok(addr) = endpoint.local_addr() {
                        target_addrs.push(normalize_socket_addr(addr));
                    }
                }

                target_addrs.sort_unstable();
                target_addrs.dedup();

                if target_addrs.is_empty() {
                    return Ok(true);
                }

                let ready = CoordinatorControlEnvelope {
                    request_id: envelope.request_id,
                    expires_at_unix_ms: envelope.expires_at_unix_ms,
                    message: CoordinatorControlMessage::CoordinationReady {
                        initiator: *initiator,
                        target: *target,
                        round: *round,
                        target_addrs,
                    },
                };

                self.send_coordinator_control_on_connection(_connection.clone(), ready)
                    .await?;
                info!(
                    "coordinator control offer handled request_id={} from_peer={:?} initiator={:?} target={:?} round={}",
                    envelope.request_id, from_peer_id, initiator, target, round
                );
                Ok(true)
            }
            CoordinatorControlMessage::CoordinationReady {
                initiator,
                target,
                round,
                target_addrs,
            } => {
                info!(
                    "coordinator control ready request_id={} from_peer={:?} initiator={:?} target={:?} round={}",
                    envelope.request_id, from_peer_id, initiator, target, round
                );

                let pending = match get_pending_request(envelope.request_id) {
                    Some(p) => p,
                    None => return Ok(true),
                };

                if from_peer_id != pending.target {
                    let _ = remove_pending_request(envelope.request_id);
                    return Ok(true);
                }
                if *initiator != pending.initiator {
                    let _ = remove_pending_request(envelope.request_id);
                    return Ok(true);
                }
                if *target != pending.target {
                    let _ = remove_pending_request(envelope.request_id);
                    return Ok(true);
                }
                if *round != pending.round {
                    let _ = remove_pending_request(envelope.request_id);
                    return Ok(true);
                }
                if pending.initiator_addrs.is_empty() {
                    let _ = remove_pending_request(envelope.request_id);
                    return Ok(true);
                }
                if target_addrs.is_empty() {
                    let _ = remove_pending_request(envelope.request_id);
                    return Ok(true);
                }

                let mut normalized_initiator_addrs = pending.initiator_addrs.clone();
                normalized_initiator_addrs = normalized_initiator_addrs
                    .into_iter()
                    .map(normalize_socket_addr)
                    .collect();
                normalized_initiator_addrs.sort_unstable();
                normalized_initiator_addrs.dedup();

                let mut normalized_target_addrs = target_addrs.clone();
                normalized_target_addrs = normalized_target_addrs
                    .into_iter()
                    .map(normalize_socket_addr)
                    .collect();
                normalized_target_addrs.sort_unstable();
                normalized_target_addrs.dedup();

                if normalized_initiator_addrs.is_empty() || normalized_target_addrs.is_empty() {
                    let _ = remove_pending_request(envelope.request_id);
                    return Ok(true);
                }

                let initiator_conn = match self.get_connection(initiator)? {
                    Some(c) => c,
                    None => {
                        let _ = remove_pending_request(envelope.request_id);
                        return Ok(true);
                    }
                };
                let target_conn = match self.get_connection(target)? {
                    Some(c) => c,
                    None => {
                        let _ = remove_pending_request(envelope.request_id);
                        return Ok(true);
                    }
                };

                let _ = remove_pending_request(envelope.request_id);

                for addr in normalized_target_addrs.iter().copied() {
                    if let Ok(sequence) = initiator_conn.send_nat_address_advertisement(addr, 0) {
                        let _ = initiator_conn.send_nat_punch_coordination(sequence, addr, *round);
                    }
                }

                for addr in normalized_initiator_addrs.iter().copied() {
                    if let Ok(sequence) = target_conn.send_nat_address_advertisement(addr, 0) {
                        let _ = target_conn.send_nat_punch_coordination(sequence, addr, *round);
                    }
                }

                let accepted = CoordinatorControlEnvelope {
                    request_id: envelope.request_id,
                    expires_at_unix_ms: envelope.expires_at_unix_ms,
                    message: CoordinatorControlMessage::CoordinationAccepted {
                        initiator: *initiator,
                        target: *target,
                        round: *round,
                        initiator_addrs: normalized_initiator_addrs.clone(),
                        target_addrs: normalized_target_addrs,
                    },
                };

                self.send_coordinator_control_on_connection(
                    initiator_conn.clone(),
                    accepted.clone(),
                )
                .await?;
                let _ = self
                    .send_coordinator_control_on_connection(target_conn.clone(), accepted)
                    .await;
                info!(
                    "coordinator control ready handled request_id={} from_peer={:?} initiator={:?} target={:?} round={}",
                    envelope.request_id, from_peer_id, initiator, target, round
                );
                Ok(true)
            }
            CoordinatorControlMessage::CoordinationAccepted {
                initiator,
                target,
                round,
                initiator_addrs,
                target_addrs,
            } => {
                info!(
                    "coordinator control accepted request_id={} from_peer={:?} initiator={:?} target={:?} round={}",
                    envelope.request_id, from_peer_id, initiator, target, round
                );

                if *initiator == self.local_peer_id() {
                    let Some(live) = live_request(self.local_peer_id(), *target) else {
                        return Ok(true);
                    };
                    if live.request_id != envelope.request_id {
                        return Ok(true);
                    }
                    if live.round != *round {
                        return Ok(true);
                    }
                    if let Some(expected) = live.expected_coordinator
                        && expected != from_peer_id
                    {
                        return Ok(true);
                    }

                    let mut normalized_target_addrs = target_addrs.clone();
                    normalized_target_addrs = normalized_target_addrs
                        .into_iter()
                        .map(normalize_socket_addr)
                        .collect();
                    normalized_target_addrs.sort_unstable();
                    normalized_target_addrs.dedup();

                    // Issue #163: drop non-Global candidates before they enter
                    // the NAT-traversal session. See
                    // `drop_non_global_nat_candidates_when_global_present` for
                    // rationale.
                    drop_non_global_nat_candidates_when_global_present(
                        &mut normalized_target_addrs,
                    );

                    if let Some(mut entry) = self.active_sessions.get_mut(target) {
                        let session = entry.value_mut();
                        session.candidates.clear();
                        for addr in normalized_target_addrs.iter().copied() {
                            if let Ok(candidate) =
                                CandidateAddress::new(addr, 0, CandidateSource::Peer)
                            {
                                session.candidates.push(candidate);
                            }
                        }
                        Self::set_session_phase(
                            session,
                            std::time::Instant::now(),
                            TraversalPhase::Validation,
                        );
                        for candidate in &mut session.candidates {
                            candidate.state = CandidateState::Validating;
                        }
                    }

                    let endpoint = self.inner_endpoint.clone().ok_or_else(|| {
                        NatTraversalError::ConfigError("QUIC endpoint not initialized".to_string())
                    })?;
                    let active_sessions = Arc::clone(&self.active_sessions);
                    let connections = Arc::clone(&self.connections);
                    let emitted_established_events = Arc::clone(&self.emitted_established_events);
                    let traversal_event_notify = Arc::clone(&self.traversal_event_notify);
                    let incoming_notify = Arc::clone(&self.incoming_notify);
                    let event_tx = self.event_tx.clone();
                    let event_callback = self.event_callback.clone();
                    let observed_address_tx = self.observed_address_tx.clone();
                    let connection_timeout = self
                        .timeout_config
                        .nat_traversal
                        .connection_establishment_timeout;
                    let candidate_addrs = normalized_target_addrs.clone();
                    let target_peer = *target;

                    self.incoming_notify.notify_waiters();
                    self.traversal_event_notify.notify_waiters();
                    tokio::spawn(async move {
                        let mut last_error = None;
                        for addr in candidate_addrs {
                            match Self::establish_connection_to_validated_candidate_inner(
                                endpoint.clone(),
                                Arc::clone(&active_sessions),
                                Arc::clone(&connections),
                                Arc::clone(&emitted_established_events),
                                observed_address_tx.clone(),
                                Arc::clone(&traversal_event_notify),
                                Arc::clone(&incoming_notify),
                                event_tx.clone(),
                                event_callback.clone(),
                                connection_timeout,
                                target_peer,
                                addr,
                            )
                            .await
                            {
                                Ok(()) => return,
                                Err(error) => {
                                    debug!(
                                        "Background validated connection attempt to {} for peer {:?} failed: {}",
                                        addr, target_peer, error
                                    );
                                    last_error = Some(error);
                                }
                            }
                        }

                        if let Some(error) = last_error {
                            warn!(
                                "Failed to establish any accepted direct path for peer {:?}: {}",
                                target_peer, error
                            );
                        }
                    });

                    info!(
                        "coordinator control accepted scheduled request_id={} from_peer={:?} initiator={:?} target={:?} round={}",
                        envelope.request_id, from_peer_id, initiator, target, round
                    );
                    return Ok(true);
                } else if *target == self.local_peer_id() {
                    let mut normalized_initiator_addrs = initiator_addrs
                        .iter()
                        .copied()
                        .map(normalize_socket_addr)
                        .collect::<Vec<_>>();
                    normalized_initiator_addrs.sort_unstable();
                    normalized_initiator_addrs.dedup();

                    if normalized_initiator_addrs.is_empty() {
                        return Ok(true);
                    }

                    if let Some(offer) = inbound_offer(self.local_peer_id(), envelope.request_id) {
                        if offer.initiator != *initiator {
                            return Ok(true);
                        }
                        if offer.target != *target {
                            return Ok(true);
                        }
                        if offer.round != *round {
                            return Ok(true);
                        }
                        if offer.coordinator != from_peer_id {
                            return Ok(true);
                        }

                        let mut offered_initiator_addrs = offer
                            .initiator_addrs
                            .iter()
                            .copied()
                            .map(normalize_socket_addr)
                            .collect::<Vec<_>>();
                        offered_initiator_addrs.sort_unstable();
                        offered_initiator_addrs.dedup();
                        if offered_initiator_addrs != normalized_initiator_addrs {
                            return Ok(true);
                        }

                        let _ = remove_inbound_offer(self.local_peer_id(), envelope.request_id);
                    }

                    if !normalized_initiator_addrs.is_empty() {
                        let endpoint = self.inner_endpoint.clone().ok_or_else(|| {
                            NatTraversalError::ConfigError(
                                "QUIC endpoint not initialized".to_string(),
                            )
                        })?;
                        let active_sessions = Arc::clone(&self.active_sessions);
                        let connections = Arc::clone(&self.connections);
                        let emitted_established_events =
                            Arc::clone(&self.emitted_established_events);
                        let traversal_event_notify = Arc::clone(&self.traversal_event_notify);
                        let incoming_notify = Arc::clone(&self.incoming_notify);
                        let event_tx = self.event_tx.clone();
                        let event_callback = self.event_callback.clone();
                        let observed_address_tx = self.observed_address_tx.clone();
                        let connection_timeout = self
                            .timeout_config
                            .nat_traversal
                            .connection_establishment_timeout;
                        let initiator_peer = *initiator;

                        tokio::spawn(async move {
                            let mut last_error = None;
                            for addr in normalized_initiator_addrs {
                                match Self::establish_connection_to_validated_candidate_inner(
                                    endpoint.clone(),
                                    Arc::clone(&active_sessions),
                                    Arc::clone(&connections),
                                    Arc::clone(&emitted_established_events),
                                    observed_address_tx.clone(),
                                    Arc::clone(&traversal_event_notify),
                                    Arc::clone(&incoming_notify),
                                    event_tx.clone(),
                                    event_callback.clone(),
                                    connection_timeout,
                                    initiator_peer,
                                    addr,
                                )
                                .await
                                {
                                    Ok(()) => return,
                                    Err(error) => {
                                        debug!(
                                            "Background inbound accepted connection attempt to {} for peer {:?} failed: {}",
                                            addr, initiator_peer, error
                                        );
                                        last_error = Some(error);
                                    }
                                }
                            }

                            if let Some(error) = last_error {
                                warn!(
                                    "Failed to establish any inbound accepted direct path for peer {:?}: {}",
                                    initiator_peer, error
                                );
                            }
                        });
                    }
                    info!(
                        "coordinator control accepted handled inbound request_id={} from_peer={:?} initiator={:?} target={:?} round={}",
                        envelope.request_id, from_peer_id, initiator, target, round
                    );
                }

                Ok(true)
            }
            CoordinatorControlMessage::CoordinationRejected {
                initiator,
                target,
                round,
                reason,
            } => {
                info!(
                    "coordinator control rejected request_id={} from_peer={:?} initiator={:?} target={:?} round={} reason={:?}",
                    envelope.request_id, from_peer_id, initiator, target, round, reason
                );

                if *initiator == self.local_peer_id() {
                    let Some(live) = live_request(self.local_peer_id(), *target) else {
                        return Ok(true);
                    };
                    if live.request_id != envelope.request_id {
                        return Ok(true);
                    }
                    if live.round != *round {
                        return Ok(true);
                    }
                    if let Some(expected) = live.expected_coordinator
                        && expected != from_peer_id
                    {
                        return Ok(true);
                    }

                    record_rejection(
                        self.local_peer_id(),
                        *target,
                        envelope.request_id,
                        *round,
                        Some(from_peer_id),
                        *reason,
                    );
                    let coordinator = live
                        .expected_coordinator
                        .and_then(|peer_id| {
                            self.connections
                                .get(&peer_id)
                                .map(|entry| entry.remote_address())
                        })
                        .unwrap_or_else(|| {
                            self.connections
                                .get(&from_peer_id)
                                .map(|entry| entry.remote_address())
                                .unwrap_or_else(create_random_port_bind_addr)
                        });
                    if let Some(event_tx) = self.event_tx.as_ref() {
                        NatTraversalEndpoint::emit_runtime_event_parts(
                            event_tx,
                            self.event_callback.as_ref(),
                            &self.traversal_event_notify,
                            NatTraversalEvent::CoordinationRejected {
                                peer_id: *target,
                                coordinator,
                                reason: *reason,
                            },
                        );
                    } else if let Some(ref callback) = self.event_callback {
                        callback(NatTraversalEvent::CoordinationRejected {
                            peer_id: *target,
                            coordinator,
                            reason: *reason,
                        });
                        self.traversal_event_notify.notify_waiters();
                    }
                    self.incoming_notify.notify_waiters();
                    info!(
                        "coordinator control rejected handled request_id={} from_peer={:?} initiator={:?} target={:?} round={} reason={:?}",
                        envelope.request_id, from_peer_id, initiator, target, round, reason
                    );
                    return Ok(true);
                } else if *target == self.local_peer_id() {
                    let Some(offer) = inbound_offer(self.local_peer_id(), envelope.request_id)
                    else {
                        return Ok(true);
                    };
                    if offer.initiator != *initiator {
                        return Ok(true);
                    }
                    if offer.target != *target {
                        return Ok(true);
                    }
                    if offer.round != *round {
                        return Ok(true);
                    }
                    if offer.coordinator != from_peer_id {
                        return Ok(true);
                    }

                    let _ = remove_inbound_offer(self.local_peer_id(), envelope.request_id);
                }

                Ok(true)
            }
        }
    }

    #[allow(dead_code)]
    async fn send_coordinator_control_on_connection(
        &self,
        connection: InnerConnection,
        envelope: CoordinatorControlEnvelope,
    ) -> Result<(), NatTraversalError> {
        let bytes = encode_coordinator_control(&envelope).map_err(|e| {
            NatTraversalError::ProtocolError(format!("coordinator control encode failed: {e}"))
        })?;
        let mut stream = connection
            .open_uni()
            .await
            .map_err(|e| NatTraversalError::CoordinationFailed(format!("{e}")))?;
        stream
            .write_all(&bytes)
            .await
            .map_err(|e| NatTraversalError::CoordinationFailed(format!("{e}")))?;
        stream
            .finish()
            .map_err(|e| NatTraversalError::CoordinationFailed(format!("{e}")))?;
        Ok(())
    }

    fn materialize_authenticated_connection(
        local_peer_id: PeerId,
        connections: &dashmap::DashMap<PeerId, InnerConnection>,
        connection_lifecycle: &ParkingRwLock<HashMap<PeerId, Vec<TrackedConnection>>>,
        next_connection_generation: &AtomicU64,
        emitted_established_events: &dashmap::DashSet<PeerId>,
        connection: InnerConnection,
    ) -> Result<(PeerId, InnerConnection), NatTraversalError> {
        let peer_id = Self::derive_peer_id_from_connection(&connection).ok_or_else(|| {
            NatTraversalError::ProtocolError(
                "authenticated QUIC connection did not expose a peer identity".to_string(),
            )
        })?;

        let outcome = Self::register_connection_lifecycle_parts(
            local_peer_id,
            connections,
            connection_lifecycle,
            next_connection_generation,
            emitted_established_events,
            peer_id,
            connection.clone(),
        );

        match outcome {
            ConnectionRegistrationOutcome::Live { .. } => Ok((peer_id, connection)),
            ConnectionRegistrationOutcome::Rejected { .. } => {
                let existing = connections
                    .get(&peer_id)
                    .map(|entry| entry.value().clone())
                    .ok_or_else(|| {
                        NatTraversalError::ConnectionFailed(
                            "rejected authenticated connection but no live winner remained"
                                .to_string(),
                        )
                    })?;
                Ok((peer_id, existing))
            }
        }
    }

    fn send_coordination_request_v2(
        &self,
        peer_id: PeerId,
        coordinator: SocketAddr,
    ) -> Result<(), NatTraversalError> {
        let mut initiator_addrs = self
            .get_all_observed_external_addresses()?
            .into_iter()
            .map(normalize_socket_addr)
            .collect::<Vec<_>>();

        if initiator_addrs.is_empty()
            && let Some(addr) = self.get_observed_external_address()?
        {
            initiator_addrs.push(normalize_socket_addr(addr));
        }

        if initiator_addrs.is_empty()
            && let Some(endpoint) = &self.inner_endpoint
        {
            if let Ok(addr) = endpoint.local_addr() {
                initiator_addrs.push(normalize_socket_addr(addr));
            }
        }

        initiator_addrs.sort_unstable();
        initiator_addrs.dedup();

        if initiator_addrs.is_empty() {
            return Err(NatTraversalError::ConfigError(
                "No initiator address available for coordination".to_string(),
            ));
        }

        let request_id = next_request_id();
        let round = 1u32;
        let expiry_duration = self.coordinator_request_expiry_duration(peer_id, coordinator);
        let (expires_at_unix_ms, local_expires_at) =
            wire_and_monotonic_expiry_after(expiry_duration);
        let local_peer_id = self.local_peer_id();
        let envelope = CoordinatorControlEnvelope {
            request_id,
            expires_at_unix_ms,
            message: CoordinatorControlMessage::CoordinationRequest {
                initiator: local_peer_id,
                target: peer_id,
                round,
                initiator_addrs: initiator_addrs.clone(),
            },
        };

        let normalized_coordinator = normalize_socket_addr(coordinator);
        for entry in self.connections.iter() {
            let coordinator_peer_id = *entry.key();
            let conn = entry.value();
            let normalized_remote = normalize_socket_addr(conn.remote_address());
            if normalized_remote == normalized_coordinator {
                remember_live_request(
                    local_peer_id,
                    peer_id,
                    LiveRequest {
                        request_id,
                        round,
                        expires_at_unix_ms,
                        local_expires_at,
                        expected_coordinator: Some(coordinator_peer_id),
                    },
                );

                let connection = conn.clone();
                let envelope = envelope.clone();
                let traversal_event_notify = self.traversal_event_notify.clone();
                tokio::spawn(async move {
                    let send_result: Result<(), ()> = async {
                        let bytes = encode_coordinator_control(&envelope).map_err(|_| ())?;
                        let mut stream = connection.open_uni().await.map_err(|_| ())?;
                        stream.write_all(&bytes).await.map_err(|_| ())?;
                        stream.finish().map_err(|_| ())?;
                        Ok(())
                    }
                    .await;

                    if send_result.is_err() {
                        record_rejection(
                            local_peer_id,
                            peer_id,
                            request_id,
                            round,
                            Some(coordinator_peer_id),
                            RejectionReason::InternalError,
                        );
                        traversal_event_notify.notify_waiters();
                    }
                });
                return Ok(());
            }
        }

        if let Some(endpoint) = &self.inner_endpoint {
            let server_name = "localhost".to_string();
            match endpoint.connect(coordinator, &server_name) {
                Ok(connecting) => {
                    remember_live_request(
                        local_peer_id,
                        peer_id,
                        LiveRequest {
                            request_id,
                            round,
                            expires_at_unix_ms,
                            local_expires_at,
                            expected_coordinator: None,
                        },
                    );

                    let connections = self.connections.clone();
                    let connection_lifecycle = self.connection_lifecycle.clone();
                    let next_connection_generation = self.next_connection_generation.clone();
                    let emitted_established_events = self.emitted_established_events.clone();
                    let low_level_endpoint = endpoint.clone();
                    let envelope = envelope.clone();
                    let traversal_event_notify = self.traversal_event_notify.clone();
                    let connect_timeout = Self::coordination_connect_timeout(&self.config);

                    tokio::spawn(async move {
                        match timeout(connect_timeout, connecting).await {
                            Ok(Ok(connection)) => {
                                let (coordinator_peer_id, coordinator_connection) =
                                    match Self::materialize_authenticated_connection(
                                        local_peer_id,
                                        connections.as_ref(),
                                        connection_lifecycle.as_ref(),
                                        next_connection_generation.as_ref(),
                                        emitted_established_events.as_ref(),
                                        connection,
                                    ) {
                                        Ok(result) => result,
                                        Err(error) => {
                                            warn!(
                                                "Failed to register coordinator {} with authenticated identity: {}",
                                                coordinator, error
                                            );
                                            record_rejection(
                                                local_peer_id,
                                                peer_id,
                                                request_id,
                                                round,
                                                None,
                                                RejectionReason::InternalError,
                                            );
                                            traversal_event_notify.notify_waiters();
                                            return;
                                        }
                                    };
                                low_level_endpoint
                                    .register_connection_peer_id(coordinator, coordinator_peer_id);
                                remember_live_request(
                                    local_peer_id,
                                    peer_id,
                                    LiveRequest {
                                        request_id,
                                        round,
                                        expires_at_unix_ms,
                                        local_expires_at,
                                        expected_coordinator: Some(coordinator_peer_id),
                                    },
                                );

                                let send_result: Result<(), ()> = async {
                                    let bytes =
                                        encode_coordinator_control(&envelope).map_err(|_| ())?;
                                    let mut stream =
                                        coordinator_connection.open_uni().await.map_err(|_| ())?;
                                    stream.write_all(&bytes).await.map_err(|_| ())?;
                                    stream.finish().map_err(|_| ())?;
                                    Ok(())
                                }
                                .await;

                                if send_result.is_err() {
                                    record_rejection(
                                        local_peer_id,
                                        peer_id,
                                        request_id,
                                        round,
                                        Some(coordinator_peer_id),
                                        RejectionReason::InternalError,
                                    );
                                    traversal_event_notify.notify_waiters();
                                }
                            }
                            Ok(Err(e)) => {
                                warn!("Failed to connect to coordinator {}: {:?}", coordinator, e);
                                record_rejection(
                                    local_peer_id,
                                    peer_id,
                                    request_id,
                                    round,
                                    None,
                                    RejectionReason::InternalError,
                                );
                                traversal_event_notify.notify_waiters();
                            }
                            Err(_) => {
                                warn!("Timeout connecting to coordinator {}", coordinator);
                                record_rejection(
                                    local_peer_id,
                                    peer_id,
                                    request_id,
                                    round,
                                    None,
                                    RejectionReason::InternalError,
                                );
                                traversal_event_notify.notify_waiters();
                            }
                        }
                    });

                    Ok(())
                }
                Err(e) => Err(NatTraversalError::CoordinationFailed(format!(
                    "Failed to initiate connection to coordinator: {:?}",
                    e
                ))),
            }
        } else {
            Err(NatTraversalError::ConfigError(
                "No endpoint available to connect to coordinator".to_string(),
            ))
        }
    }

    pub async fn get_connection_by_authenticated_peer(
        &self,
        peer_id: PeerId,
    ) -> Option<InnerConnection> {
        if let Some(conn) = self.get_connection(&peer_id).ok().flatten() {
            return Some(conn);
        }

        let connections: Vec<_> = self
            .connections
            .iter()
            .map(|entry| entry.value().clone())
            .collect();

        for conn in connections {
            if self.extract_peer_id_from_connection(&conn).await == Some(peer_id) {
                return Some(conn);
            }
        }

        None
    }

    pub fn session_connection(&self, peer_id: PeerId) -> Option<InnerConnection> {
        self.active_sessions
            .get(&peer_id)
            .and_then(|entry| entry.value().session_state.connection.clone())
    }

    pub fn get_statistics(&self) -> Result<NatTraversalStatistics, NatTraversalError> {
        // DashMap provides lock-free .len() for session count
        let session_count = self.active_sessions.len();
        // parking_lot::RwLock doesn't poison
        let bootstrap_nodes = self.bootstrap_nodes.read();

        // Calculate average coordination time based on bootstrap node RTTs
        let avg_coordination_time = {
            let rtts: Vec<Duration> = bootstrap_nodes.iter().filter_map(|b| b.rtt).collect();

            if rtts.is_empty() {
                Duration::from_millis(500) // Default if no RTT data available
            } else {
                let total_millis: u64 = rtts.iter().map(|d| d.as_millis() as u64).sum();
                Duration::from_millis(total_millis / rtts.len() as u64 * 2) // Multiply by 2 for round-trip coordination
            }
        };

        Ok(NatTraversalStatistics {
            active_sessions: session_count,
            total_bootstrap_nodes: bootstrap_nodes.len(),
            successful_coordinations: bootstrap_nodes.iter().map(|b| b.coordination_count).sum(),
            average_coordination_time: avg_coordination_time,
            total_attempts: 0,
            successful_connections: 0,
            direct_connections: 0,
            relayed_connections: 0,
        })
    }

    /// Add a new bootstrap node
    pub fn add_bootstrap_node(&self, address: SocketAddr) -> Result<(), NatTraversalError> {
        // parking_lot::RwLock doesn't poison
        let mut bootstrap_nodes = self.bootstrap_nodes.write();

        // Check if already exists
        if !bootstrap_nodes
            .iter()
            .any(|b| normalize_socket_addr(b.address) == normalize_socket_addr(address))
        {
            bootstrap_nodes.push(BootstrapNode {
                address,
                last_seen: std::time::Instant::now(),
                can_coordinate: true,
                rtt: None,
                coordination_count: 0,
            });
            info!("Added bootstrap node: {}", address);
        }
        Ok(())
    }

    /// Remove a bootstrap node
    pub fn remove_bootstrap_node(&self, address: SocketAddr) -> Result<(), NatTraversalError> {
        // parking_lot::RwLock doesn't poison
        let mut bootstrap_nodes = self.bootstrap_nodes.write();
        bootstrap_nodes.retain(|b| b.address != address);
        info!("Removed bootstrap node: {}", address);
        Ok(())
    }

    // Private implementation methods

    fn build_nat_transport_config(config: &NatTraversalConfig) -> TransportConfig {
        let mut transport_config = TransportConfig::default();
        transport_config.enable_address_discovery(true);
        transport_config.max_idle_timeout(Some(crate::VarInt::from_u32(30000).into()));
        transport_config.max_concurrent_uni_streams(
            crate::VarInt::from_u32(config.max_concurrent_uni_streams).into(),
        );

        // QUIC keep-alive is a transport-level concern. Do not couple it to NAT
        // traversal retry cadence; use the transport default unless/until we add
        // an explicit transport-level override to NatTraversalConfig/P2pConfig.
        let nat_config = crate::transport_parameters::NatTraversalConfig::ServerSupport {
            concurrency_limit: VarInt::from_u32(config.max_concurrent_attempts as u32),
        };
        transport_config.nat_traversal_config(Some(nat_config));
        transport_config
    }

    fn coordination_connect_timeout(config: &NatTraversalConfig) -> Duration {
        config.coordination_timeout
    }

    fn coordinator_rtt_hint(&self, peer_id: PeerId, coordinator: SocketAddr) -> Option<Duration> {
        let normalized_coordinator = normalize_socket_addr(coordinator);
        let bootstrap_rtt = self.bootstrap_nodes.read().iter().find_map(|node| {
            (normalize_socket_addr(node.address) == normalized_coordinator)
                .then_some(node.rtt)
                .flatten()
        });
        let session_rtt = self
            .active_sessions
            .get(&peer_id)
            .and_then(|entry| entry.value().session_state.metrics.rtt);

        match (bootstrap_rtt, session_rtt) {
            (Some(lhs), Some(rhs)) => Some(lhs.max(rhs)),
            (Some(rtt), None) | (None, Some(rtt)) => Some(rtt),
            (None, None) => None,
        }
    }

    fn coordinator_request_expiry_duration(
        &self,
        peer_id: PeerId,
        coordinator: SocketAddr,
    ) -> Duration {
        const MIN_COORDINATOR_REQUEST_EXPIRY: Duration = Duration::from_millis(250);
        const COORDINATION_WINDOW_RTT_MULTIPLIER: u32 = 3;

        let now = std::time::Instant::now();
        let upper_bound = self.config.coordination_timeout;
        let session_budget = self
            .active_sessions
            .get(&peer_id)
            .and_then(|entry| self.recompute_session_deadline(entry.value(), now))
            .map(|deadline| deadline.at.saturating_duration_since(now));
        let rtt_floor = self
            .coordinator_rtt_hint(peer_id, coordinator)
            .and_then(|rtt| rtt.checked_mul(COORDINATION_WINDOW_RTT_MULTIPLIER))
            .unwrap_or(MIN_COORDINATOR_REQUEST_EXPIRY)
            .max(MIN_COORDINATOR_REQUEST_EXPIRY)
            .min(upper_bound);

        session_budget
            .unwrap_or(upper_bound)
            .max(rtt_floor)
            .min(upper_bound)
    }

    fn coordinator_request_expires_at_unix_ms(
        &self,
        peer_id: PeerId,
        coordinator: SocketAddr,
    ) -> u64 {
        let expiry_duration = self.coordinator_request_expiry_duration(peer_id, coordinator);
        let expiry_ms = expiry_duration.as_millis().min(u128::from(u64::MAX)) as u64;
        now_unix_ms().saturating_add(expiry_ms)
    }

    /// Create a QUIC endpoint with NAT traversal configured (async version)
    ///
    /// v0.13.0: role parameter removed - all nodes are symmetric P2P nodes.
    async fn create_inner_endpoint(
        config: &NatTraversalConfig,
        token_store: Option<Arc<dyn crate::TokenStore>>,
        transport_registry: &crate::transport::TransportRegistry,
        quinn_socket: Option<std::net::UdpSocket>,
    ) -> Result<
        (
            InnerEndpoint,
            mpsc::UnboundedSender<NatTraversalEvent>,
            mpsc::UnboundedReceiver<NatTraversalEvent>,
            SocketAddr,
            Option<crate::ServerConfig>,
        ),
        NatTraversalError,
    > {
        use std::sync::Arc;

        // v0.13.0+: All nodes are symmetric P2P nodes - always create server config
        let server_config = {
            info!("Creating server config using Raw Public Keys (RFC 7250) for symmetric P2P node");

            // Use provided identity key or generate a new one
            // v0.13.0+: For consistent identity between TLS and application layers,
            // P2pEndpoint should pass its auth keypair here via config.identity_key
            let (server_pub_key, server_sec_key) = match config.identity_key.clone() {
                Some(key) => {
                    debug!("Using provided identity key for TLS authentication");
                    key
                }
                None => {
                    debug!(
                        "No identity key provided - generating new keypair (identity mismatch warning)"
                    );
                    crate::crypto::raw_public_keys::key_utils::generate_ml_dsa_keypair().map_err(
                        |e| {
                            NatTraversalError::ConfigError(format!(
                                "ML-DSA-65 keygen failed: {e:?}"
                            ))
                        },
                    )?
                }
            };

            // Build RFC 7250 server config with Raw Public Keys (ML-DSA-65)
            let mut rpk_builder = RawPublicKeyConfigBuilder::new()
                .with_server_key(server_pub_key, server_sec_key)
                .allow_any_key(); // P2P network - accept any valid ML-DSA-65 key

            if let Some(ref pqc) = config.pqc {
                rpk_builder = rpk_builder.with_pqc(pqc.clone());
            }

            let rpk_config = rpk_builder.build_rfc7250_server_config().map_err(|e| {
                NatTraversalError::ConfigError(format!("RPK server config failed: {e}"))
            })?;

            let server_crypto = QuicServerConfig::try_from(rpk_config.inner().as_ref().clone())
                .map_err(|e| NatTraversalError::ConfigError(e.to_string()))?;

            let mut server_config = ServerConfig::with_crypto(Arc::new(server_crypto));

            // Configure transport parameters for NAT traversal
            let transport_config = Self::build_nat_transport_config(config);

            // QUIC flow-control windows (stream_receive_window, send_window) use
            // TransportConfig defaults — calculated from bandwidth-delay products.
            // max_message_size is a read-side guard only, not a flow-control knob.

            server_config.transport_config(Arc::new(transport_config));

            Some(server_config)
        };

        // Create client config for outgoing connections
        let client_config = {
            info!("Creating client config using Raw Public Keys (RFC 7250)");

            // v0.13.0+: For symmetric P2P identity, client MUST also present its key
            // This allows servers to derive our peer ID from TLS, not from address
            let (client_pub_key, client_sec_key) = match config.identity_key.clone() {
                Some(key) => {
                    debug!("Using provided identity key for client TLS authentication");
                    key
                }
                None => {
                    debug!("No identity key provided for client - generating new keypair");
                    crate::crypto::raw_public_keys::key_utils::generate_ml_dsa_keypair().map_err(
                        |e| {
                            NatTraversalError::ConfigError(format!(
                                "ML-DSA-65 keygen failed: {e:?}"
                            ))
                        },
                    )?
                }
            };

            // Build RFC 7250 client config with Raw Public Keys (ML-DSA-65)
            // v0.13.0+: Client presents its own key for mutual authentication
            let mut rpk_builder = RawPublicKeyConfigBuilder::new()
                .with_client_key(client_pub_key, client_sec_key) // Present our identity to servers
                .allow_any_key(); // P2P network - accept any valid ML-DSA-65 key

            if let Some(ref pqc) = config.pqc {
                rpk_builder = rpk_builder.with_pqc(pqc.clone());
            }

            let rpk_config = rpk_builder.build_rfc7250_client_config().map_err(|e| {
                NatTraversalError::ConfigError(format!("RPK client config failed: {e}"))
            })?;

            let client_crypto = QuicClientConfig::try_from(rpk_config.inner().as_ref().clone())
                .map_err(|e| NatTraversalError::ConfigError(e.to_string()))?;

            let mut client_config = ClientConfig::new(Arc::new(client_crypto));

            // Set token store if provided
            if let Some(store) = token_store {
                client_config.token_store(store);
            }

            // Configure transport parameters for NAT traversal
            let transport_config = Self::build_nat_transport_config(config);

            // QUIC flow-control windows (stream_receive_window, send_window) use
            // TransportConfig defaults — calculated from bandwidth-delay products.
            // max_message_size is a read-side guard only, not a flow-control knob.

            client_config.transport_config(Arc::new(transport_config));

            client_config
        };

        // Get UDP socket for Quinn endpoint
        // Priority: 1) quinn_socket parameter, 2) transport registry address, 3) create new
        let std_socket = if let Some(socket) = quinn_socket {
            // Use pre-bound socket (preferred for socket sharing with transport registry)
            let socket_addr = socket
                .local_addr()
                .map(|addr| addr.to_string())
                .unwrap_or_else(|_| "unknown".to_string());
            info!("Using pre-bound UDP socket at {}", socket_addr);
            socket
        } else if let Some(registry_addr) = transport_registry.get_udp_local_addr() {
            // Transport registry has UDP - bind new socket on same interface
            // Note: We can't share the registry's socket directly because:
            // 1. It's wrapped in Arc<UdpSocket> which we can't unwrap
            // 2. Both Quinn and transport would try to recv, causing races
            // Instead, bind to same IP with random port for consistency
            info!(
                "Transport registry has UDP at {}, creating Quinn socket on same interface",
                registry_addr
            );
            let new_addr = std::net::SocketAddr::new(registry_addr.ip(), 0);
            let socket = UdpSocket::bind(new_addr).await.map_err(|e| {
                NatTraversalError::NetworkError(format!("Failed to bind UDP socket: {e}"))
            })?;
            socket.into_std().map_err(|e| {
                NatTraversalError::NetworkError(format!("Failed to convert socket: {e}"))
            })?
        } else {
            // No transport registry UDP - create new socket
            // Use config.bind_addr if provided, otherwise random port
            let bind_addr = config
                .bind_addr
                .unwrap_or_else(create_random_port_bind_addr);
            info!(
                "No UDP transport in registry, binding new endpoint to {}",
                bind_addr
            );
            let socket = UdpSocket::bind(bind_addr).await.map_err(|e| {
                NatTraversalError::NetworkError(format!("Failed to bind UDP socket: {e}"))
            })?;
            socket.into_std().map_err(|e| {
                NatTraversalError::NetworkError(format!("Failed to convert socket: {e}"))
            })?
        };

        // Create QUIC endpoint
        let runtime = default_runtime().ok_or_else(|| {
            NatTraversalError::ConfigError("No compatible async runtime found".to_string())
        })?;

        // Clone server config for potential secondary endpoint (relay accept)
        let server_config_for_relay = server_config.clone();

        let mut endpoint = InnerEndpoint::new(
            EndpointConfig::default(),
            server_config,
            std_socket,
            runtime,
        )
        .map_err(|e| {
            NatTraversalError::ConfigError(format!("Failed to create QUIC endpoint: {e}"))
        })?;

        // Set default client config
        endpoint.set_default_client_config(client_config);

        // Get the actual bound address
        let local_addr = endpoint.local_addr().map_err(|e| {
            NatTraversalError::NetworkError(format!("Failed to get local address: {e}"))
        })?;

        info!("Endpoint bound to actual address: {}", local_addr);

        // Create event channel
        let (event_tx, event_rx) = mpsc::unbounded_channel();

        Ok((
            endpoint,
            event_tx,
            event_rx,
            local_addr,
            server_config_for_relay,
        ))
    }

    /// Create the inner QUIC endpoint using a pre-built abstract socket.
    ///
    /// This variant accepts an `Arc<dyn AsyncUdpSocket>` (e.g. a `DualStackSocket`)
    /// instead of a raw std socket, allowing custom socket implementations.
    async fn create_inner_endpoint_with_abstract_socket(
        config: &NatTraversalConfig,
        token_store: Option<Arc<dyn crate::TokenStore>>,
        abstract_socket: Arc<dyn crate::high_level::runtime::AsyncUdpSocket>,
    ) -> Result<
        (
            InnerEndpoint,
            mpsc::UnboundedSender<NatTraversalEvent>,
            mpsc::UnboundedReceiver<NatTraversalEvent>,
            SocketAddr,
        ),
        NatTraversalError,
    > {
        use std::sync::Arc;

        // Build crypto configs (identical to create_inner_endpoint)
        let server_config = {
            let (server_pub_key, server_sec_key) = match config.identity_key.clone() {
                Some(key) => key,
                None => crate::crypto::raw_public_keys::key_utils::generate_ml_dsa_keypair()
                    .map_err(|e| {
                        NatTraversalError::ConfigError(format!("ML-DSA-65 keygen failed: {e:?}"))
                    })?,
            };

            let mut rpk_builder = RawPublicKeyConfigBuilder::new()
                .with_server_key(server_pub_key, server_sec_key)
                .allow_any_key();

            if let Some(ref pqc) = config.pqc {
                rpk_builder = rpk_builder.with_pqc(pqc.clone());
            }

            let rpk_config = rpk_builder.build_rfc7250_server_config().map_err(|e| {
                NatTraversalError::ConfigError(format!("RPK server config failed: {e}"))
            })?;

            let server_crypto = QuicServerConfig::try_from(rpk_config.inner().as_ref().clone())
                .map_err(|e| NatTraversalError::ConfigError(e.to_string()))?;

            let mut server_config = ServerConfig::with_crypto(Arc::new(server_crypto));

            let transport_config = Self::build_nat_transport_config(config);
            server_config.transport_config(Arc::new(transport_config));
            Some(server_config)
        };

        let client_config = {
            let (client_pub_key, client_sec_key) = match config.identity_key.clone() {
                Some(key) => key,
                None => crate::crypto::raw_public_keys::key_utils::generate_ml_dsa_keypair()
                    .map_err(|e| {
                        NatTraversalError::ConfigError(format!("ML-DSA-65 keygen failed: {e:?}"))
                    })?,
            };

            let mut rpk_builder = RawPublicKeyConfigBuilder::new()
                .with_client_key(client_pub_key, client_sec_key)
                .allow_any_key();

            if let Some(ref pqc) = config.pqc {
                rpk_builder = rpk_builder.with_pqc(pqc.clone());
            }

            let rpk_config = rpk_builder.build_rfc7250_client_config().map_err(|e| {
                NatTraversalError::ConfigError(format!("RPK client config failed: {e}"))
            })?;

            let client_crypto = QuicClientConfig::try_from(rpk_config.inner().as_ref().clone())
                .map_err(|e| NatTraversalError::ConfigError(e.to_string()))?;

            let mut client_config = ClientConfig::new(Arc::new(client_crypto));

            if let Some(store) = token_store {
                client_config.token_store(store);
            }

            let transport_config = Self::build_nat_transport_config(config);
            client_config.transport_config(Arc::new(transport_config));
            client_config
        };

        // Create QUIC endpoint with abstract socket
        let runtime = default_runtime().ok_or_else(|| {
            NatTraversalError::ConfigError("No compatible async runtime found".to_string())
        })?;

        let mut endpoint = InnerEndpoint::new_with_abstract_socket(
            EndpointConfig::default(),
            server_config,
            abstract_socket,
            runtime,
        )
        .map_err(|e| {
            NatTraversalError::ConfigError(format!("Failed to create QUIC endpoint: {e}"))
        })?;

        endpoint.set_default_client_config(client_config);

        let local_addr = endpoint.local_addr().map_err(|e| {
            NatTraversalError::NetworkError(format!("Failed to get local address: {e}"))
        })?;

        info!("Endpoint bound to actual address: {}", local_addr);

        let (event_tx, event_rx) = mpsc::unbounded_channel();
        Ok((endpoint, event_tx, event_rx, local_addr))
    }

    /// Start listening for incoming connections (async version)
    #[allow(clippy::panic)]
    pub async fn start_listening(&self, bind_addr: SocketAddr) -> Result<(), NatTraversalError> {
        let endpoint = self.inner_endpoint.as_ref().ok_or_else(|| {
            NatTraversalError::ConfigError("QUIC endpoint not initialized".to_string())
        })?;

        // Rebind the endpoint to the specified address
        let _socket = UdpSocket::bind(bind_addr).await.map_err(|e| {
            NatTraversalError::NetworkError(format!("Failed to bind to {bind_addr}: {e}"))
        })?;

        info!("Started listening on {}", bind_addr);

        // Start accepting connections in a background task
        let endpoint_clone = endpoint.clone();
        let shutdown_clone = self.shutdown.clone();
        let event_tx = match self.event_tx.as_ref() {
            Some(tx) => tx.clone(),
            None => {
                return Err(NatTraversalError::ProtocolError(
                    "Event transmitter not initialized - endpoint may not have been properly constructed".to_string(),
                ));
            }
        };
        let connections_clone = self.connections.clone();
        let connection_lifecycle_clone = self.connection_lifecycle.clone();
        let next_connection_generation_clone = self.next_connection_generation.clone();
        let local_peer_id = self.local_peer_id;
        let emitted_events_clone = self.emitted_established_events.clone();
        let relay_server_clone = self.relay_server.clone();
        let observed_address_tx_clone = self.observed_address_tx.clone();
        let traversal_event_notify_clone = self.traversal_event_notify.clone();
        let incoming_notify_clone = self.incoming_notify.clone();
        let pending_accepts_clone = self.pending_accepts.clone();

        tokio::spawn(async move {
            Self::accept_connections(
                endpoint_clone,
                shutdown_clone,
                event_tx,
                connections_clone,
                connection_lifecycle_clone,
                next_connection_generation_clone,
                local_peer_id,
                emitted_events_clone,
                relay_server_clone,
                observed_address_tx_clone,
                traversal_event_notify_clone,
                incoming_notify_clone,
                pending_accepts_clone,
            )
            .await;
        });

        Ok(())
    }

    /// Accept incoming connections
    async fn accept_connections(
        endpoint: InnerEndpoint,
        shutdown: Arc<AtomicBool>,
        event_tx: mpsc::UnboundedSender<NatTraversalEvent>,
        connections: Arc<dashmap::DashMap<PeerId, InnerConnection>>,
        connection_lifecycle: Arc<ParkingRwLock<HashMap<PeerId, Vec<TrackedConnection>>>>,
        next_connection_generation: Arc<AtomicU64>,
        local_peer_id: PeerId,
        emitted_events: Arc<dashmap::DashSet<PeerId>>,
        relay_server: Option<Arc<MasqueRelayServer>>,
        observed_address_tx: mpsc::UnboundedSender<ObservedAddressReport>,
        traversal_event_notify: Arc<tokio::sync::Notify>,
        incoming_notify: Arc<tokio::sync::Notify>,
        pending_accepts: Arc<ParkingMutex<std::collections::VecDeque<PendingAccept>>>,
    ) {
        while !shutdown.load(Ordering::Relaxed) {
            match endpoint.accept().await {
                Some(connecting) => {
                    let event_tx = event_tx.clone();
                    let connections = connections.clone();
                    let connection_lifecycle = connection_lifecycle.clone();
                    let next_connection_generation = next_connection_generation.clone();
                    let emitted_events = emitted_events.clone();
                    let relay_server = relay_server.clone();
                    let observed_address_tx = observed_address_tx.clone();
                    let traversal_event_notify = traversal_event_notify.clone();
                    let incoming_notify = incoming_notify.clone();
                    let pending_accepts = pending_accepts.clone();
                    tokio::spawn(async move {
                        match connecting.await {
                            Ok(connection) => {
                                info!("Accepted connection from {}", connection.remote_address());

                                // Prefer peer ID from the authenticated public key when available.
                                let peer_id = Self::derive_peer_id_from_connection(&connection)
                                    .unwrap_or_else(|| {
                                        Self::generate_peer_id_from_address(
                                            connection.remote_address(),
                                        )
                                    });

                                let outcome = Self::register_connection_lifecycle_parts(
                                    local_peer_id,
                                    connections.as_ref(),
                                    connection_lifecycle.as_ref(),
                                    next_connection_generation.as_ref(),
                                    emitted_events.as_ref(),
                                    peer_id,
                                    connection.clone(),
                                );

                                let generation = match outcome {
                                    ConnectionRegistrationOutcome::Live { generation, .. } => {
                                        generation
                                    }
                                    ConnectionRegistrationOutcome::Rejected {
                                        winner_generation,
                                    } => {
                                        debug!(
                                            "Rejected inbound connection for peer {:?}; live generation {} kept",
                                            peer_id, winner_generation
                                        );
                                        return;
                                    }
                                };

                                pending_accepts.lock().push_back(PendingAccept {
                                    peer_id,
                                    generation,
                                });
                                incoming_notify.notify_one();
                                traversal_event_notify.notify_waiters();

                                let should_emit = emitted_events.insert(peer_id);
                                if should_emit {
                                    let _ =
                                        event_tx.send(NatTraversalEvent::ConnectionEstablished {
                                            peer_id,
                                            remote_address: connection.remote_address(),
                                            side: Side::Server,
                                        });
                                    traversal_event_notify.notify_waiters();
                                }

                                if let Some(ref server) = relay_server {
                                    let conn_clone = connection.clone();
                                    let server_clone = Arc::clone(server);
                                    tokio::spawn(async move {
                                        Self::handle_relay_requests(conn_clone, server_clone).await;
                                    });
                                }

                                Self::spawn_observed_address_watch_task_parts(
                                    observed_address_tx.clone(),
                                    peer_id,
                                    connection.clone(),
                                );

                                Self::handle_connection(
                                    peer_id,
                                    connection,
                                    event_tx,
                                    traversal_event_notify,
                                )
                                .await;
                            }
                            Err(e) => {
                                debug!("Connection failed: {}", e);
                            }
                        }
                    });
                }
                None => {
                    // Endpoint closed
                    break;
                }
            }
        }
    }

    /// Handle relay requests from a connected peer (symmetric P2P)
    ///
    /// This listens for bidirectional streams and processes CONNECT-UDP Bind requests.
    /// Per ADR-004: All nodes are equal and participate in relaying with resource budgets.
    async fn handle_relay_requests(
        connection: InnerConnection,
        relay_server: Arc<MasqueRelayServer>,
    ) {
        let client_addr = connection.remote_address();
        debug!("Started relay request handler for peer at {}", client_addr);

        loop {
            // Accept bidirectional streams for relay requests
            match connection.accept_bi().await {
                Ok((mut send_stream, mut recv_stream)) => {
                    let server = Arc::clone(&relay_server);
                    let addr = client_addr;
                    let _conn_for_relay = connection.clone();

                    tokio::spawn(async move {
                        // Read length-prefixed request
                        let mut req_len_buf = [0u8; 4];
                        if let Err(e) = recv_stream.read_exact(&mut req_len_buf).await {
                            debug!("Failed to read relay request length from {}: {}", addr, e);
                            return;
                        }
                        let req_len = u32::from_be_bytes(req_len_buf) as usize;
                        if req_len > 1024 {
                            debug!("Relay request too large from {}: {} bytes", addr, req_len);
                            return;
                        }
                        let mut request_bytes = vec![0u8; req_len];
                        if let Err(e) = recv_stream.read_exact(&mut request_bytes).await {
                            debug!("Failed to read relay request from {}: {}", addr, e);
                            return;
                        }

                        {
                            {
                                // Try to parse as CONNECT-UDP request
                                match ConnectUdpRequest::decode(&mut bytes::Bytes::from(
                                    request_bytes,
                                )) {
                                    Ok(request) => {
                                        debug!(
                                            "Received CONNECT-UDP request from {}: {:?}",
                                            addr, request
                                        );

                                        // Handle the request via relay server
                                        match server.handle_connect_request(&request, addr).await {
                                            Ok(response) => {
                                                let is_success = response.is_success();
                                                debug!(
                                                    "Sending CONNECT-UDP response to {}: {:?}",
                                                    addr, response
                                                );

                                                // Send response with length prefix (stream stays open for data)
                                                let response_frame =
                                                    encode_relay_response_frame(&response);
                                                if let Err(e) =
                                                    send_stream.write_all(&response_frame).await
                                                {
                                                    warn!(
                                                        "Failed to send relay response to {}: {}",
                                                        addr, e
                                                    );
                                                    return;
                                                }
                                                // Do NOT call finish() — stream stays open for forwarding

                                                // Start stream-based forwarding loop
                                                if is_success {
                                                    if let Some(session_info) =
                                                        server.get_session_for_client(addr).await
                                                    {
                                                        info!(
                                                            "Starting stream-based relay forwarding for session {} (client: {})",
                                                            session_info.session_id, addr
                                                        );
                                                        server
                                                            .run_stream_forwarding_loop(
                                                                session_info.session_id,
                                                                send_stream,
                                                                recv_stream,
                                                            )
                                                            .await;
                                                    }
                                                }
                                            }
                                            Err(e) => {
                                                warn!(
                                                    "Failed to handle relay request from {}: {}",
                                                    addr, e
                                                );
                                                // Send error response
                                                let response = ConnectUdpResponse::error(
                                                    500,
                                                    format!("Internal error: {}", e),
                                                );
                                                let response_frame =
                                                    encode_relay_response_frame(&response);
                                                let _ =
                                                    send_stream.write_all(&response_frame).await;
                                                let _ = send_stream.finish();
                                            }
                                        }
                                    }
                                    Err(e) => {
                                        // Not a CONNECT-UDP request, ignore
                                        debug!(
                                            "Stream from {} is not a CONNECT-UDP request: {}",
                                            addr, e
                                        );
                                    }
                                }
                            }
                        }
                    });
                }
                Err(e) => {
                    // Connection closed or error
                    debug!(
                        "Relay handler stopping for {} - accept_bi error: {}",
                        client_addr, e
                    );
                    break;
                }
            }
        }
    }

    /// Drive discovery progression and observed-address ingestion without a
    /// coarse fixed polling interval.
    async fn poll_discovery(
        discovery_manager: Arc<ParkingMutex<CandidateDiscoveryManager>>,
        discovery_state_notify: Arc<tokio::sync::Notify>,
        mut observed_address_rx: mpsc::UnboundedReceiver<ObservedAddressReport>,
        shutdown: Arc<AtomicBool>,
        shutdown_notify: Arc<tokio::sync::Notify>,
        event_tx: mpsc::UnboundedSender<NatTraversalEvent>,
        connections: Arc<dashmap::DashMap<PeerId, InnerConnection>>,
        relay_server: Option<Arc<MasqueRelayServer>>,
        event_callback: Option<Arc<dyn Fn(NatTraversalEvent) + Send + Sync>>,
        traversal_event_notify: Arc<tokio::sync::Notify>,
        local_peer_id: PeerId,
        relay_setup_attempted: Arc<std::sync::atomic::AtomicBool>,
    ) {
        let mut observed_address_reports = HashSet::new();

        loop {
            if shutdown.load(Ordering::Relaxed) {
                break;
            }

            let now = std::time::Instant::now();
            let next_deadline = discovery_manager.lock().next_global_poll_deadline(now);

            tokio::select! {
                _ = shutdown_notify.notified() => break,
                maybe_report = observed_address_rx.recv() => {
                    let Some(report) = maybe_report else {
                        break;
                    };
                    Self::process_observed_address_report(
                        discovery_manager.as_ref(),
                        &discovery_state_notify,
                        &mut observed_address_reports,
                        &event_tx,
                        connections.as_ref(),
                        relay_server.as_ref(),
                        event_callback.as_ref(),
                        &traversal_event_notify,
                        local_peer_id,
                        &relay_setup_attempted,
                        report,
                    )
                    .await;
                }
                _ = discovery_state_notify.notified() => {}
                _ = async {
                    if let Some(deadline) = next_deadline {
                        tokio::time::sleep_until(tokio::time::Instant::from_std(deadline)).await;
                    } else {
                        std::future::pending::<()>().await;
                    }
                } => {}
            }

            let events = discovery_manager.lock().poll(std::time::Instant::now());
            for event in events {
                match &event {
                    DiscoveryEvent::ServerReflexiveCandidateDiscovered {
                        candidate,
                        bootstrap_node,
                    } => {
                        debug!("{}", event);
                        Self::process_observed_address_report(
                            discovery_manager.as_ref(),
                            &discovery_state_notify,
                            &mut observed_address_reports,
                            &event_tx,
                            connections.as_ref(),
                            relay_server.as_ref(),
                            event_callback.as_ref(),
                            &traversal_event_notify,
                            local_peer_id,
                            &relay_setup_attempted,
                            ObservedAddressReport {
                                reporter_peer_id: Self::generate_peer_id_from_address(
                                    *bootstrap_node,
                                ),
                                reported_by: *bootstrap_node,
                                address: candidate.address,
                            },
                        )
                        .await;
                    }
                    DiscoveryEvent::DiscoveryCompleted { .. } => info!("{}", event),
                    DiscoveryEvent::DiscoveryFailed { .. } => warn!("{}", event),
                    _ => debug!("{}", event),
                }
            }

            Self::reconcile_relay_server_public_addresses_from_connections(
                connections.as_ref(),
                relay_server.as_ref(),
            );
        }

        info!("Discovery/orchestration task shutting down");
    }

    /// Handle an established connection
    async fn handle_connection(
        peer_id: PeerId,
        connection: InnerConnection,
        event_tx: mpsc::UnboundedSender<NatTraversalEvent>,
        traversal_event_notify: Arc<tokio::sync::Notify>,
    ) {
        let remote_address = connection.remote_address();
        let closed = connection.closed();
        tokio::pin!(closed);

        debug!(
            "Handling connection from peer {:?} at {}",
            peer_id, remote_address
        );

        // Monitor for connection closure only
        // Application data streams are handled by the application layer (QuicP2PNode)
        // not by this background task to avoid race conditions
        closed.await;

        let reason = connection
            .close_reason()
            .map(|reason| format!("Connection closed: {reason}"))
            .unwrap_or_else(|| "Connection closed".to_string());
        if let Err(e) = event_tx.send(NatTraversalEvent::ConnectionLost { peer_id, reason }) {
            tracing::warn!(
                target: "ant_quic::silent_drop",
                kind = "event_tx_connection_lost",
                peer_id = ?peer_id,
                error = %e,
                "silent drop"
            );
        }
        traversal_event_notify.notify_waiters();
    }

    fn emit_runtime_event_parts(
        event_tx: &mpsc::UnboundedSender<NatTraversalEvent>,
        event_callback: Option<&Arc<dyn Fn(NatTraversalEvent) + Send + Sync>>,
        traversal_event_notify: &Arc<tokio::sync::Notify>,
        event: NatTraversalEvent,
    ) {
        if let Err(e) = event_tx.send(event.clone()) {
            tracing::warn!(
                target: "ant_quic::silent_drop",
                kind = "event_tx_emit_event",
                error = %e,
                "silent drop"
            );
        }
        if let Some(callback) = event_callback {
            callback(event);
        }
        traversal_event_notify.notify_waiters();
    }

    fn reconcile_relay_server_public_addresses_from_connections(
        connections: &dashmap::DashMap<PeerId, InnerConnection>,
        relay_server: Option<&Arc<MasqueRelayServer>>,
    ) {
        let Some(server) = relay_server else {
            return;
        };

        let mut candidates = Vec::new();
        for entry in connections.iter() {
            for addr in entry.value().all_observed_addresses() {
                candidates.push(normalize_socket_addr(addr));
            }
        }
        let (ipv4, ipv6) = Self::select_relay_server_public_addresses(candidates.as_slice());
        server.reconcile_public_addresses(ipv4, ipv6);
    }

    async fn process_observed_address_report(
        discovery_manager: &ParkingMutex<CandidateDiscoveryManager>,
        discovery_state_notify: &Arc<tokio::sync::Notify>,
        observed_address_reports: &mut HashSet<(PeerId, SocketAddr)>,
        event_tx: &mpsc::UnboundedSender<NatTraversalEvent>,
        connections: &dashmap::DashMap<PeerId, InnerConnection>,
        relay_server: Option<&Arc<MasqueRelayServer>>,
        event_callback: Option<&Arc<dyn Fn(NatTraversalEvent) + Send + Sync>>,
        traversal_event_notify: &Arc<tokio::sync::Notify>,
        local_peer_id: PeerId,
        relay_setup_attempted: &Arc<std::sync::atomic::AtomicBool>,
        report: ObservedAddressReport,
    ) {
        let address = normalize_socket_addr(report.address);
        let reported_by = normalize_socket_addr(report.reported_by);

        let should_emit = observed_address_reports.insert((report.reporter_peer_id, address));
        if !should_emit {
            return;
        }

        Self::emit_runtime_event_parts(
            event_tx,
            event_callback,
            traversal_event_notify,
            NatTraversalEvent::ExternalAddressDiscovered {
                reported_by,
                address,
            },
        );

        let accepted = {
            let mut discovery = discovery_manager.lock();
            discovery
                .accept_quic_discovered_address(local_peer_id, address)
                .unwrap_or(false)
        };
        if accepted {
            discovery_state_notify.notify_waiters();
        }

        if !relay_setup_attempted.load(std::sync::atomic::Ordering::Relaxed) {
            broadcast_address_to_peers(connections, address, 100);
        }

        Self::reconcile_relay_server_public_addresses_from_connections(connections, relay_server);
    }

    fn spawn_observed_address_watch_task_parts(
        observed_address_tx: mpsc::UnboundedSender<ObservedAddressReport>,
        peer_id: PeerId,
        connection: InnerConnection,
    ) {
        tokio::spawn(async move {
            let mut seen = HashSet::new();
            loop {
                let reported_by = normalize_socket_addr(connection.remote_address());
                for address in connection
                    .all_observed_addresses()
                    .into_iter()
                    .map(normalize_socket_addr)
                {
                    if seen.insert(address) {
                        if let Err(e) = observed_address_tx.send(ObservedAddressReport {
                            reporter_peer_id: peer_id,
                            reported_by,
                            address,
                        }) {
                            tracing::warn!(
                                target: "ant_quic::silent_drop",
                                kind = "observed_address_tx",
                                peer_id = ?peer_id,
                                addr = ?address,
                                error = %e,
                                "silent drop"
                            );
                        }
                    }
                }

                let observed_change = connection.observed_address_updated();
                tokio::select! {
                    _ = observed_change => {}
                    _ = connection.closed() => break,
                }
            }
        });
    }

    fn spawn_observed_address_watch_task(&self, peer_id: PeerId, connection: InnerConnection) {
        Self::spawn_observed_address_watch_task_parts(
            self.observed_address_tx.clone(),
            peer_id,
            connection,
        );
    }

    /// Connect to a peer using NAT traversal
    pub async fn connect_to_peer(
        &self,
        peer_id: PeerId,
        server_name: &str,
        remote_addr: SocketAddr,
    ) -> Result<InnerConnection, NatTraversalError> {
        let endpoint = self.inner_endpoint.as_ref().ok_or_else(|| {
            NatTraversalError::ConfigError("QUIC endpoint not initialized".to_string())
        })?;

        info!("Connecting to peer {:?} at {}", peer_id, remote_addr);

        // Attempt connection with timeout
        let connecting = endpoint.connect(remote_addr, server_name).map_err(|e| {
            NatTraversalError::ConnectionFailed(format!("Failed to initiate connection: {e}"))
        })?;

        let connection = timeout(
            self.timeout_config
                .nat_traversal
                .connection_establishment_timeout,
            connecting,
        )
        .await
        .map_err(|_| NatTraversalError::Timeout)?
        .map_err(|e| NatTraversalError::ConnectionFailed(format!("Connection failed: {e}")))?;

        info!(
            "Successfully connected to peer {:?} at {}",
            peer_id, remote_addr
        );

        // Send event notification (we initiated = Client side)
        if let Some(ref event_tx) = self.event_tx {
            if let Err(e) = event_tx.send(NatTraversalEvent::ConnectionEstablished {
                peer_id,
                remote_address: remote_addr,
                side: Side::Client,
            }) {
                tracing::warn!(
                    target: "ant_quic::silent_drop",
                    kind = "event_tx_connection_established_client",
                    peer_id = ?peer_id,
                    error = %e,
                    "silent drop"
                );
            }
            self.incoming_notify.notify_one();
            self.traversal_event_notify.notify_waiters();
        }

        Ok(connection)
    }

    /// Attempt connection with automatic fallback strategies
    ///
    /// Connection attempts follow this priority order:
    /// 1. **Direct connection** - simple QUIC connect to the target address
    /// 2. **Hole punching** - coordinated NAT traversal with candidate discovery
    /// 3. **Relay** - last resort via MASQUE through connected peers (symmetric P2P)
    ///
    /// # Symmetric P2P Relay Strategy
    /// When relay is needed:
    /// - First try connected peers as relays (any peer can relay)
    /// - Fall back to configured relay_nodes (for bootstrap scenarios only)
    pub async fn connect_with_fallback(
        &self,
        peer_id: PeerId,
        server_name: &str,
        remote_addr: SocketAddr,
    ) -> Result<InnerConnection, NatTraversalError> {
        // Step 1: Try direct connection first
        info!(
            "Attempting direct connection to {:?} at {}",
            peer_id, remote_addr
        );
        match self
            .connect_to_peer(peer_id, server_name, remote_addr)
            .await
        {
            Ok(conn) => {
                info!("Direct connection to {:?} succeeded", peer_id);
                return Ok(conn);
            }
            Err(e) => {
                info!(
                    "Direct connection to {:?} failed ({:?}), trying hole punching",
                    peer_id, e
                );
            }
        }

        // Step 2: Try hole punching (coordinated NAT traversal)
        info!("Attempting hole punching for {:?}", peer_id);
        match self.attempt_hole_punching(peer_id) {
            Ok(()) => {
                // Hole punching succeeded - NAT mappings are established
                // Now try to connect again using the discovered path
                info!(
                    "Hole punching succeeded for {:?}, retrying connection",
                    peer_id
                );

                // Get the successful candidate pair address if available
                let connect_addr = self
                    .get_successful_candidate_address(peer_id)
                    .unwrap_or(remote_addr);

                match self
                    .connect_to_peer(peer_id, server_name, connect_addr)
                    .await
                {
                    Ok(conn) => {
                        info!("Connection via hole punching to {:?} succeeded", peer_id);
                        return Ok(conn);
                    }
                    Err(e) => {
                        info!(
                            "Connection after hole punching failed ({:?}), trying relay",
                            e
                        );
                    }
                }
            }
            Err(e) => {
                info!(
                    "Hole punching for {:?} failed ({:?}), trying relay",
                    peer_id, e
                );
            }
        }

        // Step 3: Relay is the last resort
        info!("Attempting relay connection to {:?} (last resort)", peer_id);

        // Symmetric P2P: Collect connected peers to use as potential relays
        // Any connected peer can provide relay services
        // DashMap provides lock-free concurrent access
        let connected_peers: Vec<SocketAddr> = self
            .connections
            .iter()
            .filter(|entry| entry.value().close_reason().is_none()) // Only active connections
            .map(|entry| entry.value().remote_address())
            .filter(|addr| *addr != remote_addr) // Don't try to relay through the target
            .collect();

        info!(
            "Found {} connected peers to try as relays",
            connected_peers.len()
        );

        // Also add configured relay nodes as fallback (for bootstrapping)
        let mut relay_candidates: Vec<SocketAddr> = connected_peers;
        if let Some(ref manager) = self.relay_manager {
            let configured_relays = manager.available_relays().await;
            for relay in configured_relays {
                if !relay_candidates.contains(&relay) {
                    relay_candidates.push(relay);
                }
            }
        }

        if relay_candidates.is_empty() {
            return Err(NatTraversalError::ConnectionFailed(
                "No connected peers or relay nodes available".to_string(),
            ));
        }

        // Try each relay in order
        let mut last_error = None;
        for relay_addr in relay_candidates {
            info!("Attempting connection via relay: {}", relay_addr);

            // Establish relay session (CONNECT-UDP Bind)
            match self.establish_relay_session(relay_addr).await {
                Ok(public_addr) => {
                    info!(
                        "Relay session established via {} with public address {:?}",
                        relay_addr, public_addr
                    );

                    // Now attempt the connection through the relay
                    // The relay session is stored and the connection can use datagram forwarding
                    // For now, we attempt a direct connection to the peer using our relay public address
                    // The peer should be able to reach us through the relay

                    // Try connecting to the peer - the relay will forward our traffic
                    match self
                        .connect_to_peer(peer_id, server_name, remote_addr)
                        .await
                    {
                        Ok(conn) => {
                            info!(
                                "Connected to peer {:?} via relay {} (public addr: {:?})",
                                peer_id, relay_addr, public_addr
                            );
                            return Ok(conn);
                        }
                        Err(e) => {
                            warn!(
                                "Connection via relay {} failed: {:?}, trying next relay",
                                relay_addr, e
                            );
                            last_error = Some(e);
                        }
                    }
                }
                Err(e) => {
                    warn!(
                        "Failed to establish relay session with {}: {:?}",
                        relay_addr, e
                    );
                    last_error = Some(e);
                }
            }
        }

        Err(last_error.unwrap_or_else(|| {
            NatTraversalError::ConnectionFailed("All relay attempts failed".to_string())
        }))
    }

    /// Get the relay manager for advanced relay operations
    ///
    /// Returns None if no relay nodes are configured (connected peers are still
    /// eligible for relay fallback).
    pub fn relay_manager(&self) -> Option<Arc<RelayManager>> {
        self.relay_manager.clone()
    }

    /// Get the relay public address, if a proactive relay has been established.
    pub fn relay_public_addr(&self) -> Option<SocketAddr> {
        self.relay_public_addr.lock().ok().and_then(|g| *g)
    }

    /// Check if the proactive relay session is still alive. Returns true if
    /// no relay was established (nothing to monitor) or the relay is healthy.
    /// Returns false if a relay was established but the underlying QUIC
    /// connection has closed.
    pub fn is_relay_healthy(&self) -> bool {
        let relay_addr = match self.relay_public_addr.lock().ok().and_then(|g| *g) {
            Some(addr) => addr,
            None => return true, // No relay — nothing to monitor
        };

        // Check if any relay session is still active
        for entry in self.relay_sessions.iter() {
            if entry.value().is_active() {
                return true;
            }
        }

        // All relay sessions are dead
        warn!(
            "Relay session for {} is dead — resetting for re-establishment",
            relay_addr
        );
        false
    }

    /// Reset relay state so the next poll cycle can re-establish. Called when
    /// the relay session is detected as dead.
    pub fn reset_relay_state(&self) {
        self.relay_setup_attempted
            .store(false, std::sync::atomic::Ordering::Relaxed);
        self.relay_accept_loop_started
            .store(false, std::sync::atomic::Ordering::Relaxed);
        if let Ok(mut addr) = self.relay_public_addr.lock() {
            *addr = None;
        }
        if let Ok(mut endpoint) = self.shared_relay_endpoint.lock() {
            *endpoint = None;
        }
        if let Ok(mut peers) = self.relay_advertised_peers.lock() {
            peers.clear();
        }
        // Remove dead sessions
        self.relay_sessions.retain(|_, session| session.is_active());
        info!("Relay state reset — will re-establish on next poll cycle");
    }

    /// Check if relay fallback is available
    pub async fn has_relay_fallback(&self) -> bool {
        match &self.relay_manager {
            Some(manager) => manager.has_available_relay().await,
            None => false,
        }
    }

    /// Receive a peer address update originating from an ADD_ADDRESS frame.
    pub async fn recv_peer_address_update(&self) -> Option<(SocketAddr, SocketAddr)> {
        self.peer_address_update_rx.lock().await.recv().await
    }

    /// Get or create the shared QUIC endpoint that runs over the relay socket.
    pub(crate) async fn ensure_shared_relay_endpoint(
        &self,
        relay_addr: SocketAddr,
    ) -> Result<(Option<SocketAddr>, InnerEndpoint), NatTraversalError> {
        let relay_addr = normalize_socket_addr(relay_addr);

        if let Some(endpoint) = self
            .shared_relay_endpoint
            .lock()
            .ok()
            .and_then(|guard| guard.clone())
        {
            let public_addr = self
                .relay_sessions
                .get(&relay_addr)
                .and_then(|session| session.public_address)
                .or_else(|| self.relay_public_addr());
            return Ok((public_addr, endpoint));
        }

        let (public_addr, relay_socket) = self.establish_relay_session(relay_addr).await?;
        let relay_socket = relay_socket.ok_or_else(|| {
            NatTraversalError::ConnectionFailed("Relay did not provide socket".to_string())
        })?;
        let runtime = crate::high_level::default_runtime().ok_or_else(|| {
            NatTraversalError::ConfigError("No async runtime for relay endpoint".to_string())
        })?;

        let mut relay_endpoint = InnerEndpoint::new_with_abstract_socket(
            crate::EndpointConfig::default(),
            self.server_config.clone(),
            relay_socket,
            runtime,
        )
        .map_err(|e| {
            NatTraversalError::ConnectionFailed(format!("Failed to create relay endpoint: {}", e))
        })?;

        if let Some(client_config) = self
            .inner_endpoint
            .as_ref()
            .and_then(|endpoint| endpoint.default_client_config.clone())
        {
            relay_endpoint.set_default_client_config(client_config);
        }

        if let Ok(mut shared) = self.shared_relay_endpoint.lock() {
            *shared = Some(relay_endpoint.clone());
        }

        Ok((public_addr, relay_endpoint))
    }

    /// Establish a relay session with a MASQUE relay server
    ///
    /// This connects to the relay server, sends a CONNECT-UDP Bind request,
    /// and stores the session for use in relayed connections.
    ///
    /// # Arguments
    /// * `relay_addr` - Address of the MASQUE relay server
    ///
    /// # Returns
    /// The public address allocated by the relay, or an error
    pub async fn establish_relay_session(
        &self,
        relay_addr: SocketAddr,
    ) -> Result<
        (
            Option<SocketAddr>,
            Option<Arc<crate::masque::MasqueRelaySocket>>,
        ),
        NatTraversalError,
    > {
        // Normalize to prevent IPv4 vs IPv4-mapped-IPv6 key mismatches
        let relay_addr = crate::shared::normalize_socket_addr(relay_addr);

        // Check if we already have an active session to this relay
        // DashMap provides lock-free .get() that returns Option<Ref<K, V>>
        if let Some(session) = self.relay_sessions.get(&relay_addr) {
            if session.is_active() {
                debug!("Reusing existing relay session to {}", relay_addr);
                return Ok((session.public_address, session.relay_socket.clone()));
            }
        }

        info!("Establishing relay session to {}", relay_addr);

        // Prefer reusing an existing peer connection to the relay.
        // The relay server's handle_relay_requests is spawned for each ACCEPTED
        // connection, so using the existing connection ensures a handler is
        // already listening for bidi streams.
        let existing_conn = self.connections.iter().find_map(|entry| {
            let conn = entry.value();
            if conn.remote_address() == relay_addr && conn.close_reason().is_none() {
                Some(conn.clone())
            } else {
                None
            }
        });
        let connection = if let Some(existing) = existing_conn {
            info!("Reusing existing peer connection to relay {}", relay_addr);
            existing
        } else {
            // No existing connection — create one
            self.connect_new_to_relay(relay_addr).await?
        };

        // Open a bidirectional stream for the CONNECT-UDP handshake
        let (mut send_stream, mut recv_stream) = connection.open_bi().await.map_err(|e| {
            NatTraversalError::ConnectionFailed(format!("Failed to open relay stream: {}", e))
        })?;

        // Send CONNECT-UDP Bind request with length prefix (stream stays open for data)
        let request = ConnectUdpRequest::bind_any();
        let request_bytes = request.encode();

        debug!("Sending CONNECT-UDP Bind request to relay: {:?}", request);

        // Length-prefixed framing: [4-byte BE length][payload]
        let req_len = request_bytes.len() as u32;
        send_stream
            .write_all(&req_len.to_be_bytes())
            .await
            .map_err(|e| {
                NatTraversalError::ConnectionFailed(format!("Failed to send request length: {}", e))
            })?;
        send_stream.write_all(&request_bytes).await.map_err(|e| {
            NatTraversalError::ConnectionFailed(format!("Failed to send relay request: {}", e))
        })?;
        // Do NOT call finish() — stream stays open for data forwarding

        // Read length-prefixed response
        let mut resp_len_buf = [0u8; 4];
        recv_stream
            .read_exact(&mut resp_len_buf)
            .await
            .map_err(|e| {
                NatTraversalError::ConnectionFailed(format!(
                    "Failed to read relay response length: {}",
                    e
                ))
            })?;
        let resp_len = u32::from_be_bytes(resp_len_buf) as usize;
        let mut response_bytes = vec![0u8; resp_len];
        recv_stream
            .read_exact(&mut response_bytes)
            .await
            .map_err(|e| {
                NatTraversalError::ConnectionFailed(format!("Failed to read relay response: {}", e))
            })?;

        let response = ConnectUdpResponse::decode(&mut bytes::Bytes::from(response_bytes))
            .map_err(|e| {
                NatTraversalError::ProtocolError(format!("Invalid relay response: {}", e))
            })?;

        if !response.is_success() {
            let reason = response.reason.unwrap_or_else(|| "unknown".to_string());
            return Err(NatTraversalError::ConnectionFailed(format!(
                "Relay rejected request: {} (status {})",
                reason, response.status
            )));
        }

        let public_address = response.proxy_public_address;

        info!(
            "Relay session established with public address: {:?}",
            public_address
        );

        // Create the MasqueRelaySocket from the open streams
        let relay_socket = public_address
            .map(|addr| crate::masque::MasqueRelaySocket::new(send_stream, recv_stream, addr));

        // Store the session
        let session = RelaySession {
            connection,
            public_address,
            relay_socket: relay_socket.clone(),
            established_at: std::time::Instant::now(),
            relay_addr,
        };

        // DashMap provides lock-free .insert()
        self.relay_sessions.insert(relay_addr, session);

        // Notify the relay manager
        if let Some(ref manager) = self.relay_manager {
            if let Ok(resp) =
                ConnectUdpResponse::decode(&mut bytes::Bytes::from(response.encode().to_vec()))
            {
                let _ = manager.handle_connect_response(relay_addr, resp).await;
            }
        }

        Ok((public_address, relay_socket))
    }

    /// Create a fresh QUIC connection to a relay server.
    ///
    /// Used as a fallback when no existing peer connection is available.
    async fn connect_new_to_relay(
        &self,
        relay_addr: SocketAddr,
    ) -> Result<InnerConnection, NatTraversalError> {
        let endpoint = self.inner_endpoint.as_ref().ok_or_else(|| {
            NatTraversalError::ConfigError("QUIC endpoint not initialized".to_string())
        })?;

        let server_name = relay_addr.ip().to_string();
        let connecting = endpoint.connect(relay_addr, &server_name).map_err(|e| {
            NatTraversalError::ConnectionFailed(format!(
                "Failed to initiate relay connection: {}",
                e
            ))
        })?;

        let connection = timeout(Self::coordination_connect_timeout(&self.config), connecting)
            .await
            .map_err(|_| NatTraversalError::Timeout)?
            .map_err(|e| {
                NatTraversalError::ConnectionFailed(format!("Relay connection failed: {}", e))
            })?;

        info!("Connected to relay server {}", relay_addr);
        Ok(connection)
    }

    /// Get active relay sessions
    pub fn relay_sessions(&self) -> Arc<dashmap::DashMap<SocketAddr, RelaySession>> {
        self.relay_sessions.clone()
    }

    /// Best-effort runtime metrics for the embedded relay server.
    pub(crate) fn relay_server_runtime_metrics(&self) -> (usize, u64) {
        let Some(server) = self.relay_server.as_ref() else {
            return (0, 0);
        };

        let stats = server.stats();
        let active_sessions =
            usize::try_from(stats.current_active_sessions()).unwrap_or(usize::MAX);
        let bytes_forwarded = stats.total_bytes_relayed();
        (active_sessions, bytes_forwarded)
    }

    #[cfg(test)]
    pub(crate) fn record_test_relay_server_activity(&self, active_sessions: u64, bytes: u64) {
        if let Some(server) = self.relay_server.as_ref() {
            let stats = server.stats();
            for _ in 0..active_sessions {
                stats.record_session_created();
            }
            if bytes > 0 {
                stats.record_bytes(bytes);
            }
        }
    }

    /// Accept incoming connections on the endpoint
    pub async fn accept_connection(&self) -> Result<(PeerId, InnerConnection), NatTraversalError> {
        debug!("Waiting for incoming connection via accept queue...");
        loop {
            // Check shutdown
            if self.shutdown.load(Ordering::Relaxed) {
                return Err(NatTraversalError::NetworkError(
                    "Endpoint shutting down".to_string(),
                ));
            }

            if let Some(pending) = self.pending_accepts.lock().pop_front() {
                let still_live = self
                    .lifecycle_snapshot_for_generation(&pending.peer_id, pending.generation)
                    .is_some_and(|snapshot| {
                        matches!(snapshot.state, ConnectionLifecycleState::Live)
                    });
                if !still_live {
                    debug!(
                        "Skipping stale pending accept for peer {:?}: generation {:?} no longer live",
                        pending.peer_id, pending.generation
                    );
                    continue;
                }
                let Some(connection) = self
                    .connections
                    .get(&pending.peer_id)
                    .map(|entry| entry.value().clone())
                else {
                    debug!(
                        "Skipping stale pending accept for peer {:?}: connection no longer in storage",
                        pending.peer_id
                    );
                    continue;
                };
                info!(
                    "Retrieved accepted connection from peer {:?}",
                    pending.peer_id
                );
                return Ok((pending.peer_id, connection));
            }

            // Drain all pending events (non-blocking, under ParkingMutex) for
            // relay/legacy paths that may still only signal via event_rx.
            {
                let mut event_rx = self.event_rx.lock();
                loop {
                    match event_rx.try_recv() {
                        Ok(NatTraversalEvent::ConnectionEstablished {
                            peer_id,
                            remote_address,
                            side,
                        }) => {
                            info!(
                                "Received ConnectionEstablished event for peer {:?} at {} (side: {:?})",
                                peer_id, remote_address, side
                            );
                            let Some(connection) = self
                                .connections
                                .get(&peer_id)
                                .map(|entry| entry.value().clone())
                            else {
                                debug!(
                                    "Ignoring stale ConnectionEstablished event for peer {:?}: connection no longer in storage",
                                    peer_id
                                );
                                continue;
                            };
                            return Ok((peer_id, connection));
                        }
                        Ok(event) => {
                            debug!(
                                "Ignoring non-connection event while waiting for accept: {:?}",
                                event
                            );
                        }
                        Err(mpsc::error::TryRecvError::Empty) => break,
                        Err(mpsc::error::TryRecvError::Disconnected) => {
                            return Err(NatTraversalError::NetworkError(
                                "Event channel closed".to_string(),
                            ));
                        }
                    }
                }
            }

            self.incoming_notify.notified().await;
        }
    }

    /// Get the local peer ID
    pub fn local_peer_id(&self) -> PeerId {
        self.local_peer_id
    }

    /// Returns a reference to the traversal progress notification handle.
    ///
    /// This `Notify` is triggered whenever NAT-traversal session progress or
    /// runtime connection events occur. Callers still need an explicit timeout
    /// or next-deadline budget for phases that are driven by absence of events.
    pub(crate) fn traversal_event_notify(&self) -> &tokio::sync::Notify {
        &self.traversal_event_notify
    }

    /// Returns a reference to the connection notification handle.
    ///
    /// This `Notify` is triggered whenever a `ConnectionEstablished` event
    /// is produced, allowing callers waiting in `accept_connection()` to sleep
    /// without polling.
    pub fn connection_notify(&self) -> &tokio::sync::Notify {
        &self.incoming_notify
    }

    fn lifecycle_peer_prefix(peer_id: &PeerId) -> String {
        hex::encode(&peer_id.0[..4])
    }

    fn log_lifecycle_transition(
        peer_id: &PeerId,
        generation: u64,
        connection_id: &[u8; 32],
        stable_id: usize,
        from_state: &str,
        to_state: &str,
        reason: ConnectionCloseReason,
    ) {
        info!(
            target: "ant_quic::p2p_endpoint::lifecycle",
            peer_id = %Self::lifecycle_peer_prefix(peer_id),
            generation,
            from_state,
            to_state,
            reason = %reason,
            connection_id = %hex::encode(&connection_id[..8]),
            stable_id,
            "connection lifecycle transition"
        );
    }

    fn log_lifecycle_live(peer_id: &PeerId, tracked: &TrackedConnection) {
        info!(
            target: "ant_quic::p2p_endpoint::lifecycle",
            peer_id = %Self::lifecycle_peer_prefix(peer_id),
            generation = tracked.generation,
            from_state = "Init",
            to_state = "Live",
            reason = "None",
            connection_id = %tracked.connection_id_hex(),
            stable_id = tracked.stable_id(),
            "connection lifecycle transition"
        );
    }

    fn canonical_sort_key(
        connection_family_id: [u8; 32],
        connection_id: [u8; 32],
        generation: u64,
        established_at_unix_ms: u64,
        stable_id: usize,
    ) -> ConnectionCanonicalSortKey {
        ConnectionCanonicalSortKey {
            connection_family_id,
            connection_id,
            generation,
            established_at_unix_ms,
            stable_id,
        }
    }

    fn canonical_sort_key_cmp(
        left: ConnectionCanonicalSortKey,
        right: ConnectionCanonicalSortKey,
    ) -> std::cmp::Ordering {
        if left.connection_family_id != right.connection_family_id {
            left.connection_id.cmp(&right.connection_id)
        } else {
            (
                left.generation,
                left.established_at_unix_ms,
                left.connection_id,
                left.stable_id,
            )
                .cmp(&(
                    right.generation,
                    right.established_at_unix_ms,
                    right.connection_id,
                    right.stable_id,
                ))
        }
    }

    /// Deterministically rank competing connections for the same peer.
    ///
    /// Connections from the same initiator share a `connection_family_id`, so
    /// reconnects on that family prefer the newer local generation / timestamp.
    /// Simultaneous opens from opposite initiators compare the shared
    /// per-connection `connection_id` derived from TLS exporter material, so
    /// both endpoints choose the same winner without relying on local
    /// generation counters.
    fn candidate_wins(existing: &TrackedConnection, candidate: &TrackedConnection) -> bool {
        Self::canonical_sort_key_cmp(
            Self::canonical_sort_key(
                candidate.connection_family_id,
                candidate.connection_id,
                candidate.generation,
                candidate.established_at_unix_ms,
                candidate.stable_id(),
            ),
            Self::canonical_sort_key(
                existing.connection_family_id,
                existing.connection_id,
                existing.generation,
                existing.established_at_unix_ms,
                existing.stable_id(),
            ),
        ) == std::cmp::Ordering::Greater
    }

    fn lifecycle_connection_family_id_from_initiator(
        local_peer_id: PeerId,
        peer_id: PeerId,
        initiator: PeerId,
    ) -> [u8; 32] {
        let (left, right) = if local_peer_id.0 <= peer_id.0 {
            (local_peer_id, peer_id)
        } else {
            (peer_id, local_peer_id)
        };

        let mut hasher = Blake3Hasher::new();
        hasher.update(b"ant-quic.lifecycle.family.v1");
        hasher.update(&left.0);
        hasher.update(&right.0);
        hasher.update(&initiator.0);

        let mut out = [0u8; 32];
        out.copy_from_slice(hasher.finalize().as_bytes());
        out
    }

    fn lifecycle_connection_family_id_for(
        local_peer_id: PeerId,
        peer_id: PeerId,
        connection: &InnerConnection,
    ) -> [u8; 32] {
        let derived_peer_id = Self::derive_peer_id_from_connection(connection);
        debug_assert!(
            derived_peer_id.is_some(),
            "lifecycle registration missing authenticated peer id"
        );
        let canonical_peer_id = derived_peer_id.unwrap_or(peer_id);
        let initiator = if connection.side().is_client() {
            local_peer_id
        } else {
            canonical_peer_id
        };
        Self::lifecycle_connection_family_id_from_initiator(
            local_peer_id,
            canonical_peer_id,
            initiator,
        )
    }

    fn lifecycle_connection_id_for(
        local_peer_id: PeerId,
        peer_id: PeerId,
        connection: &InnerConnection,
    ) -> [u8; 32] {
        let derived_peer_id = Self::derive_peer_id_from_connection(connection);
        debug_assert!(
            derived_peer_id.is_some(),
            "lifecycle registration missing authenticated peer id"
        );
        let canonical_peer_id = derived_peer_id.unwrap_or(peer_id);
        let mut exporter = [0u8; 32];
        if connection
            .export_keying_material(
                &mut exporter,
                b"ant-quic/lifecycle-connection-id/v1",
                b"canonical",
            )
            .is_ok()
        {
            return exporter;
        }

        let initiator = if connection.side().is_client() {
            local_peer_id
        } else {
            canonical_peer_id
        };
        Self::lifecycle_connection_family_id_from_initiator(
            local_peer_id,
            canonical_peer_id,
            initiator,
        )
    }

    fn record_rejected_connection(
        peer_id: &PeerId,
        entries: &mut Vec<TrackedConnection>,
        mut tracked: TrackedConnection,
        winner_generation: u64,
    ) {
        let superseded_state = ConnectionLifecycleState::Superseded {
            replaced_by_generation: winner_generation,
        };
        Self::log_lifecycle_transition(
            peer_id,
            tracked.generation,
            &tracked.connection_id,
            tracked.stable_id(),
            "Init",
            superseded_state.name(),
            ConnectionCloseReason::Superseded,
        );
        tracked.state = superseded_state;

        let closing_state = ConnectionLifecycleState::Closing {
            reason: ConnectionCloseReason::Superseded,
        };
        Self::log_lifecycle_transition(
            peer_id,
            tracked.generation,
            &tracked.connection_id,
            tracked.stable_id(),
            tracked.state.name(),
            closing_state.name(),
            ConnectionCloseReason::Superseded,
        );
        tracked.state = closing_state;

        let closed_state = ConnectionLifecycleState::Closed {
            reason: ConnectionCloseReason::Superseded,
            closed_at_unix_ms: now_unix_ms(),
        };
        Self::log_lifecycle_transition(
            peer_id,
            tracked.generation,
            &tracked.connection_id,
            tracked.stable_id(),
            tracked.state.name(),
            closed_state.name(),
            ConnectionCloseReason::Superseded,
        );
        tracked.state = closed_state;
        entries.push(tracked);
    }

    fn register_connection_lifecycle_parts(
        local_peer_id: PeerId,
        connections: &dashmap::DashMap<PeerId, InnerConnection>,
        connection_lifecycle: &ParkingRwLock<HashMap<PeerId, Vec<TrackedConnection>>>,
        next_connection_generation: &AtomicU64,
        emitted_established_events: &dashmap::DashSet<PeerId>,
        peer_id: PeerId,
        connection: InnerConnection,
    ) -> ConnectionRegistrationOutcome {
        let generation = next_connection_generation.fetch_add(1, Ordering::Relaxed);
        let tracked = TrackedConnection {
            connection: connection.clone(),
            generation,
            established_at_unix_ms: now_unix_ms(),
            connection_family_id: Self::lifecycle_connection_family_id_for(
                local_peer_id,
                peer_id,
                &connection,
            ),
            connection_id: Self::lifecycle_connection_id_for(local_peer_id, peer_id, &connection),
            state: ConnectionLifecycleState::Live,
        };

        let mut superseded_generation = None;
        let mut rejected_connection = None;

        {
            let mut lifecycle = connection_lifecycle.write();
            let entries = lifecycle.entry(peer_id).or_default();
            if let Some(live_idx) = entries
                .iter()
                .position(|entry| matches!(entry.state, ConnectionLifecycleState::Live))
            {
                let live = entries[live_idx].clone();
                if Self::candidate_wins(&live, &tracked) {
                    superseded_generation = Some(live.generation);
                    Self::log_lifecycle_transition(
                        &peer_id,
                        live.generation,
                        &live.connection_id,
                        live.stable_id(),
                        live.state.name(),
                        ConnectionLifecycleState::Superseded {
                            replaced_by_generation: generation,
                        }
                        .name(),
                        ConnectionCloseReason::Superseded,
                    );
                    entries[live_idx].state = ConnectionLifecycleState::Superseded {
                        replaced_by_generation: generation,
                    };
                    Self::log_lifecycle_live(&peer_id, &tracked);
                    entries.push(tracked.clone());
                } else {
                    let winner_generation = live.generation;
                    Self::record_rejected_connection(
                        &peer_id,
                        entries,
                        tracked.clone(),
                        winner_generation,
                    );
                    rejected_connection = Some((tracked.connection.clone(), winner_generation));
                }
            } else {
                Self::log_lifecycle_live(&peer_id, &tracked);
                entries.push(tracked.clone());
            }
            Self::prune_closed_lifecycle_entries(entries);
        }

        if let Some((rejected_connection, winner_generation)) = rejected_connection {
            if rejected_connection.close_reason().is_none()
                && let Some(code) = ConnectionCloseReason::Superseded.app_error_code()
            {
                rejected_connection.close(code, ConnectionCloseReason::Superseded.reason_bytes());
            }
            return ConnectionRegistrationOutcome::Rejected { winner_generation };
        }

        connections.insert(peer_id, connection);
        emitted_established_events.remove(&peer_id);
        ConnectionRegistrationOutcome::Live {
            generation,
            superseded_generation,
        }
    }

    fn lifecycle_snapshot_for_generation(
        &self,
        peer_id: &PeerId,
        generation: u64,
    ) -> Option<ConnectionLifecycleSnapshot> {
        self.connection_lifecycle
            .read()
            .get(peer_id)
            .and_then(|entries| entries.iter().find(|entry| entry.generation == generation))
            .map(|entry| ConnectionLifecycleSnapshot {
                generation: entry.generation,
                stable_id: entry.stable_id(),
                connection_id: entry.connection_id,
                state: entry.state,
                established_at_unix_ms: entry.established_at_unix_ms,
            })
    }

    pub(crate) fn connection_snapshot_by_stable_id(
        &self,
        peer_id: &PeerId,
        stable_id: usize,
    ) -> Option<ConnectionLifecycleSnapshot> {
        self.connection_lifecycle
            .read()
            .get(peer_id)
            .and_then(|entries| entries.iter().find(|entry| entry.stable_id() == stable_id))
            .map(|entry| ConnectionLifecycleSnapshot {
                generation: entry.generation,
                stable_id: entry.stable_id(),
                connection_id: entry.connection_id,
                state: entry.state,
                established_at_unix_ms: entry.established_at_unix_ms,
            })
    }

    pub(crate) fn recent_close_reason_for_peer(
        &self,
        peer_id: &PeerId,
    ) -> Option<ConnectionCloseReason> {
        let lifecycle = self.connection_lifecycle.read();
        let entries = lifecycle.get(peer_id)?;
        if entries
            .iter()
            .any(|entry| matches!(entry.state, ConnectionLifecycleState::Live))
        {
            return None;
        }

        entries
            .iter()
            .filter_map(|entry| match entry.state {
                ConnectionLifecycleState::Closing { reason }
                | ConnectionLifecycleState::Closed { reason, .. } => {
                    Some((entry.generation, reason))
                }
                ConnectionLifecycleState::Live | ConnectionLifecycleState::Superseded { .. } => {
                    None
                }
            })
            .max_by_key(|(generation, _)| *generation)
            .map(|(_, reason)| reason)
    }

    fn prune_closed_lifecycle_entries(entries: &mut Vec<TrackedConnection>) {
        const MAX_CLOSED_ENTRIES: usize = 4;
        let closed_count = entries
            .iter()
            .filter(|entry| matches!(entry.state, ConnectionLifecycleState::Closed { .. }))
            .count();
        if closed_count <= MAX_CLOSED_ENTRIES {
            return;
        }

        let mut to_remove = closed_count - MAX_CLOSED_ENTRIES;
        entries.retain(|entry| {
            if to_remove > 0 && matches!(entry.state, ConnectionLifecycleState::Closed { .. }) {
                to_remove -= 1;
                false
            } else {
                true
            }
        });
    }

    fn register_connection_lifecycle(
        &self,
        peer_id: PeerId,
        connection: InnerConnection,
    ) -> Result<ConnectionRegistrationOutcome, NatTraversalError> {
        Ok(Self::register_connection_lifecycle_parts(
            self.local_peer_id,
            self.connections.as_ref(),
            self.connection_lifecycle.as_ref(),
            self.next_connection_generation.as_ref(),
            self.emitted_established_events.as_ref(),
            peer_id,
            connection,
        ))
    }

    fn mark_connection_closed(
        &self,
        peer_id: &PeerId,
        stable_id: usize,
        default_reason: ConnectionCloseReason,
    ) -> Option<ConnectionCloseReason> {
        let mut lifecycle = self.connection_lifecycle.write();
        let entries = lifecycle.get_mut(peer_id)?;
        let index = entries
            .iter()
            .position(|entry| entry.stable_id() == stable_id)?;
        let effective_reason = if matches!(
            default_reason,
            ConnectionCloseReason::Superseded
                | ConnectionCloseReason::ReaderExit
                | ConnectionCloseReason::PeerShutdown
                | ConnectionCloseReason::Banned
                | ConnectionCloseReason::LifecycleCleanup
        ) {
            default_reason
        } else {
            entries[index]
                .connection
                .close_reason()
                .as_ref()
                .map(ConnectionCloseReason::from_connection_error)
                .unwrap_or(default_reason)
        };

        let from_state = entries[index].state;
        if !matches!(from_state, ConnectionLifecycleState::Closing { .. }) {
            Self::log_lifecycle_transition(
                peer_id,
                entries[index].generation,
                &entries[index].connection_id,
                entries[index].stable_id(),
                from_state.name(),
                ConnectionLifecycleState::Closing {
                    reason: effective_reason,
                }
                .name(),
                effective_reason,
            );
            entries[index].state = ConnectionLifecycleState::Closing {
                reason: effective_reason,
            };
        }

        Self::log_lifecycle_transition(
            peer_id,
            entries[index].generation,
            &entries[index].connection_id,
            entries[index].stable_id(),
            entries[index].state.name(),
            ConnectionLifecycleState::Closed {
                reason: effective_reason,
                closed_at_unix_ms: now_unix_ms(),
            }
            .name(),
            effective_reason,
        );
        entries[index].state = ConnectionLifecycleState::Closed {
            reason: effective_reason,
            closed_at_unix_ms: now_unix_ms(),
        };
        Some(effective_reason)
    }

    pub(crate) fn handle_reader_exit(
        &self,
        peer_id: &PeerId,
        generation: u64,
        stable_id: usize,
    ) -> ReaderExitOutcome {
        let snapshot = self
            .lifecycle_snapshot_for_generation(peer_id, generation)
            .or_else(|| self.connection_snapshot_by_stable_id(peer_id, stable_id));
        let Some(snapshot) = snapshot else {
            return ReaderExitOutcome::Noop;
        };

        match snapshot.state {
            ConnectionLifecycleState::Superseded { .. } => {
                if let Some(connection) =
                    self.connection_lifecycle
                        .read()
                        .get(peer_id)
                        .and_then(|entries| {
                            entries
                                .iter()
                                .find(|entry| entry.stable_id() == snapshot.stable_id)
                                .map(|entry| entry.connection.clone())
                        })
                    && connection.close_reason().is_none()
                    && let Some(code) = ConnectionCloseReason::Superseded.app_error_code()
                {
                    connection.close(code, ConnectionCloseReason::Superseded.reason_bytes());
                }
                let _ = self.mark_connection_closed(
                    peer_id,
                    snapshot.stable_id,
                    ConnectionCloseReason::Superseded,
                );

                let mut lifecycle = self.connection_lifecycle.write();
                if let Some(entries) = lifecycle.get_mut(peer_id) {
                    entries.retain(|entry| entry.stable_id() != snapshot.stable_id);
                    if entries.is_empty() {
                        lifecycle.remove(peer_id);
                    }
                }
                ReaderExitOutcome::ConnectionReaped
            }
            ConnectionLifecycleState::Live => {
                let current_live_stable_id = self
                    .connections
                    .get(peer_id)
                    .map(|entry| entry.value().stable_id());
                if current_live_stable_id.is_some()
                    && current_live_stable_id != Some(snapshot.stable_id)
                {
                    if let Some(connection) = self
                        .connection_lifecycle
                        .read()
                        .get(peer_id)
                        .and_then(|entries| {
                            entries
                                .iter()
                                .find(|entry| entry.stable_id() == snapshot.stable_id)
                                .map(|entry| entry.connection.clone())
                        })
                        && connection.close_reason().is_none()
                        && let Some(code) = ConnectionCloseReason::Superseded.app_error_code()
                    {
                        connection.close(code, ConnectionCloseReason::Superseded.reason_bytes());
                    }
                    let _ = self.mark_connection_closed(
                        peer_id,
                        snapshot.stable_id,
                        ConnectionCloseReason::Superseded,
                    );
                    let mut lifecycle = self.connection_lifecycle.write();
                    if let Some(entries) = lifecycle.get_mut(peer_id) {
                        entries.retain(|entry| entry.stable_id() != snapshot.stable_id);
                        if entries.is_empty() {
                            lifecycle.remove(peer_id);
                        }
                    }
                    return ReaderExitOutcome::ConnectionReaped;
                }

                if let Some(connection) =
                    self.connection_lifecycle
                        .read()
                        .get(peer_id)
                        .and_then(|entries| {
                            entries
                                .iter()
                                .find(|entry| entry.stable_id() == snapshot.stable_id)
                                .map(|entry| entry.connection.clone())
                        })
                    && connection.close_reason().is_none()
                    && let Some(code) = ConnectionCloseReason::ReaderExit.app_error_code()
                {
                    connection.close(code, ConnectionCloseReason::ReaderExit.reason_bytes());
                }
                let close_reason = self
                    .mark_connection_closed(
                        peer_id,
                        snapshot.stable_id,
                        ConnectionCloseReason::ReaderExit,
                    )
                    .unwrap_or(ConnectionCloseReason::ReaderExit);
                self.connections.remove(peer_id);
                self.emitted_established_events.remove(peer_id);
                let mut lifecycle = self.connection_lifecycle.write();
                if let Some(entries) = lifecycle.get_mut(peer_id) {
                    entries.retain(|entry| entry.stable_id() != snapshot.stable_id);
                    if entries.is_empty() {
                        lifecycle.remove(peer_id);
                    }
                }
                ReaderExitOutcome::PeerDisconnected { close_reason }
            }
            ConnectionLifecycleState::Closing { .. } | ConnectionLifecycleState::Closed { .. } => {
                let mut lifecycle = self.connection_lifecycle.write();
                if let Some(entries) = lifecycle.get_mut(peer_id) {
                    entries.retain(|entry| entry.stable_id() != snapshot.stable_id);
                    if entries.is_empty() {
                        lifecycle.remove(peer_id);
                    }
                }
                ReaderExitOutcome::ConnectionReaped
            }
        }
    }

    /// Check if we have a live connection to the given peer.
    ///
    /// If the connection exists but is dead (closed/draining), removes it
    /// from the connection table and returns `false`. This enables automatic
    /// cleanup of phantom connections during deduplication checks.
    pub fn is_peer_connected(&self, peer_id: &PeerId) -> bool {
        if let Some(conn) = self.connections.get(peer_id) {
            if conn.is_alive() && conn.close_reason().is_none() {
                return true;
            }
            drop(conn);
            let _ = self.remove_connection(peer_id);
        }
        false
    }

    /// Get an active live connection by peer ID.
    pub fn get_connection(
        &self,
        peer_id: &PeerId,
    ) -> Result<Option<InnerConnection>, NatTraversalError> {
        let Some(connection) = self
            .connections
            .get(peer_id)
            .map(|entry| entry.value().clone())
        else {
            return Ok(None);
        };
        if let Some(reason) = connection.close_reason() {
            let _ = self.remove_connection_with_reason(
                peer_id,
                ConnectionCloseReason::from_connection_error(&reason),
            );
            return Ok(None);
        }
        Ok(Some(connection))
    }

    /// Add or update a connection for a peer using lifecycle-aware replacement.
    pub(crate) fn add_connection_with_outcome(
        &self,
        peer_id: PeerId,
        connection: InnerConnection,
    ) -> Result<ConnectionRegistrationOutcome, NatTraversalError> {
        let remote_addr = connection.remote_address();
        let observed = connection.observed_address();
        info!(
            "add_connection: peer {:?} at {} observed_address={:?}",
            peer_id, remote_addr, observed
        );
        let observed_watch_connection = connection.clone();
        let outcome = self.register_connection_lifecycle(peer_id, connection)?;
        if matches!(outcome, ConnectionRegistrationOutcome::Live { .. }) {
            self.spawn_observed_address_watch_task(peer_id, observed_watch_connection);
        }
        info!(
            "add_connection: now have {} live connections",
            self.connections.len()
        );
        Ok(outcome)
    }

    /// Add or update a connection for a peer.
    pub fn add_connection(
        &self,
        peer_id: PeerId,
        connection: InnerConnection,
    ) -> Result<(), NatTraversalError> {
        let _ = self.add_connection_with_outcome(peer_id, connection)?;
        Ok(())
    }

    pub fn remove_connection_with_reason(
        &self,
        peer_id: &PeerId,
        close_reason: ConnectionCloseReason,
    ) -> Result<Option<InnerConnection>, NatTraversalError> {
        self.emitted_established_events.remove(peer_id);
        let removed = self
            .connections
            .remove(peer_id)
            .map(|(_, connection)| connection);
        if let Some(connection) = removed.as_ref() {
            let effective_reason = self
                .mark_connection_closed(peer_id, connection.stable_id(), close_reason)
                .unwrap_or(close_reason);
            if connection.close_reason().is_none()
                && let Some(code) = effective_reason.app_error_code()
            {
                connection.close(code, effective_reason.reason_bytes());
            }
            if let Ok(mut peers) = self.relay_advertised_peers.lock() {
                peers.remove(&connection.remote_address());
            }
        }
        Ok(removed)
    }

    /// Spawn the NAT traversal handler loop for an existing connection referenced by the endpoint.
    ///
    /// # Arguments
    /// * `peer_id` - The peer ID of the remote endpoint
    /// * `connection` - The established QUIC connection
    /// * `side` - Who initiated the connection (Client = we connected, Server = they connected)
    pub fn spawn_connection_handler(
        &self,
        peer_id: PeerId,
        connection: InnerConnection,
        side: Side,
    ) -> Result<(), NatTraversalError> {
        let event_tx = self.event_tx.as_ref().cloned().ok_or_else(|| {
            NatTraversalError::ConfigError("NAT traversal event channel not configured".to_string())
        })?;

        let remote_address = connection.remote_address();

        // Only emit ConnectionEstablished if we haven't already for this peer
        // DashSet::insert returns true if this is a new peer (not already present)
        let should_emit = self.emitted_established_events.insert(peer_id);

        if should_emit {
            if let Err(e) = event_tx.send(NatTraversalEvent::ConnectionEstablished {
                peer_id,
                remote_address,
                side,
            }) {
                tracing::warn!(
                    target: "ant_quic::silent_drop",
                    kind = "event_tx_connection_established_inner",
                    peer_id = ?peer_id,
                    error = %e,
                    "silent drop"
                );
            }
            self.incoming_notify.notify_one();
            self.traversal_event_notify.notify_waiters();
        }

        // Spawn connection monitoring task
        let traversal_event_notify = Arc::clone(&self.traversal_event_notify);
        tokio::spawn(async move {
            Self::handle_connection(peer_id, connection, event_tx, traversal_event_notify).await;
        });

        Ok(())
    }

    /// Remove a connection by peer ID
    pub fn remove_connection(
        &self,
        peer_id: &PeerId,
    ) -> Result<Option<InnerConnection>, NatTraversalError> {
        // Clear emitted event tracking so reconnections can generate new events
        // DashSet provides lock-free .remove()
        self.emitted_established_events.remove(peer_id);

        // DashMap provides lock-free .remove() that returns Option<(K, V)>
        let removed = self
            .connections
            .remove(peer_id)
            .map(|(_, connection)| connection);
        self.connection_lifecycle.write().remove(peer_id);
        Ok(removed.inspect(|connection| {
            if let Ok(mut peers) = self.relay_advertised_peers.lock() {
                peers.remove(&connection.remote_address());
            }
        }))
    }

    /// List all active connections
    pub fn list_connections(&self) -> Result<Vec<(PeerId, SocketAddr)>, NatTraversalError> {
        // DashMap provides lock-free iteration
        let result: Vec<_> = self
            .connections
            .iter()
            .map(|entry| (*entry.key(), entry.value().remote_address()))
            .collect();
        Ok(result)
    }

    /// Get the external/reflexive address as observed by remote peers
    ///
    /// This returns the public address of this endpoint as seen by other peers,
    /// discovered via OBSERVED_ADDRESS frames during QUIC connections.
    ///
    /// Returns the first observed address found from any active connection,
    /// preferring connections to bootstrap nodes.
    ///
    /// Returns `None` if:
    /// - No connections are active
    /// - No OBSERVED_ADDRESS frame has been received from any peer
    pub fn get_observed_external_address(&self) -> Result<Option<SocketAddr>, NatTraversalError> {
        // Check all connections for an observed address
        // First try to find one from a known peer (more reliable)
        let known_peer_addrs: std::collections::HashSet<_> =
            self.config.known_peers.iter().copied().collect();

        // Check known peer connections first (DashMap lock-free iteration)
        for entry in self.connections.iter() {
            let connection = entry.value();
            if known_peer_addrs.contains(&connection.remote_address()) {
                if let Some(addr) = connection.observed_address() {
                    debug!(
                        "Found observed external address {} from known peer connection",
                        addr
                    );
                    return Ok(Some(addr));
                }
            }
        }

        // Fall back to any connection with an observed address
        for entry in self.connections.iter() {
            if let Some(addr) = entry.value().observed_address() {
                debug!(
                    "Found observed external address {} from peer connection",
                    addr
                );
                return Ok(Some(addr));
            }
        }

        debug!("No observed external address available from any connection");
        Ok(None)
    }

    /// Returns ALL observed external addresses from all connected peers and paths.
    ///
    /// Unlike `get_observed_external_address()` which returns only the first match,
    /// this collects unique addresses across all connections and QUIC path IDs.
    /// This is critical for dual-stack nodes where IPv4 and IPv6 addresses are
    /// discovered from different peers or paths.
    pub fn get_all_observed_external_addresses(
        &self,
    ) -> Result<Vec<SocketAddr>, NatTraversalError> {
        let mut addrs = Vec::new();
        for entry in self.connections.iter() {
            let connection = entry.value();
            for addr in connection.all_observed_addresses() {
                if !addrs.contains(&addr) {
                    addrs.push(addr);
                }
            }
        }
        if !addrs.is_empty() {
            debug!(
                "Collected {} observed external addresses: {:?}",
                addrs.len(),
                addrs
            );
        }
        Ok(addrs)
    }

    // ============ Multi-Transport Address Advertising ============

    /// Advertise a transport address to all connected peers
    ///
    /// This method broadcasts the transport address to all active connections
    /// using ADD_ADDRESS frames. For UDP transports, this falls back to the
    /// standard socket address advertising. For other transports (BLE, LoRa, etc.),
    /// the transport type and optional capability flags are included in the advertisement.
    ///
    /// # Arguments
    /// * `address` - The transport address to advertise
    /// * `priority` - ICE-style priority (higher = better)
    /// * `capabilities` - Optional capability flags for the transport
    ///
    /// # Example
    /// ```ignore
    /// use ant_quic::transport::TransportAddr;
    /// use ant_quic::nat_traversal::CapabilityFlags;
    ///
    /// // Advertise a UDP address
    /// endpoint.advertise_transport_address(
    ///     TransportAddr::Udp("192.168.1.100:9000".parse().unwrap()),
    ///     100,
    ///     Some(CapabilityFlags::broadband()),
    /// );
    ///
    /// // Advertise a BLE address
    /// endpoint.advertise_transport_address(
    ///     TransportAddr::Ble {
    ///         device_id: [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC],
    ///         service_uuid: None,
    ///     },
    ///     50,
    ///     Some(CapabilityFlags::ble()),
    /// );
    /// ```
    pub fn advertise_transport_address(
        &self,
        address: TransportAddr,
        priority: u32,
        capabilities: Option<CapabilityFlags>,
    ) -> Result<(), NatTraversalError> {
        // For UDP addresses, use the existing broadcast mechanism
        if let Some(socket_addr) = address.as_socket_addr() {
            broadcast_address_to_peers(&self.connections, socket_addr, priority);
            info!(
                "Advertised UDP transport address {} with priority {} to {} peers",
                socket_addr,
                priority,
                self.connections.len()
            );
            return Ok(());
        }

        // For non-UDP transports, we need to store the transport candidate
        // and advertise it via the extended ADD_ADDRESS frames
        let candidate = TransportCandidate {
            address: address.clone(),
            priority,
            source: CandidateSource::Local,
            state: CandidateState::New,
            capabilities,
        };

        info!(
            "Advertising {:?} transport address with priority {} (capabilities: {:?})",
            candidate.transport_type(),
            priority,
            capabilities
        );

        // For now, log the advertisement - full frame transmission for non-UDP
        // transports will be implemented when we have multi-transport connections
        debug!(
            "Transport candidate registered: {:?}, capabilities: {:?}",
            address, capabilities
        );

        Ok(())
    }

    /// Advertise a transport address with full capability information
    ///
    /// This is a convenience method that creates capability flags from the
    /// full TransportCapabilities struct.
    pub fn advertise_transport_with_capabilities(
        &self,
        address: TransportAddr,
        priority: u32,
        capabilities: &TransportCapabilities,
    ) -> Result<(), NatTraversalError> {
        let flags = CapabilityFlags::from_capabilities(capabilities);
        self.advertise_transport_address(address, priority, Some(flags))
    }

    /// Get the transport type filter for candidate selection
    ///
    /// Returns the set of transport types that should be considered
    /// when selecting candidates for connection.
    pub fn get_transport_filter(&self) -> Vec<TransportType> {
        // Default: prefer UDP, but accept other transports
        vec![
            TransportType::Udp,
            TransportType::Ble,
            TransportType::LoRa,
            TransportType::Serial,
        ]
    }

    /// Check if a transport type is supported by this endpoint
    pub fn supports_transport(&self, transport_type: TransportType) -> bool {
        match transport_type {
            // UDP is always supported
            TransportType::Udp => true,
            // Other transports depend on registered providers
            _ => {
                if let Some(registry) = &self.transport_registry {
                    !registry.providers_by_type(transport_type).is_empty()
                } else {
                    false
                }
            }
        }
    }

    // ============ Transport-Aware Candidate Selection ============

    /// Select the best candidate from a list of transport candidates
    ///
    /// This method filters candidates by transport type support and selects
    /// the best one based on priority and capability matching.
    ///
    /// # Selection Criteria
    /// 1. Filter out unsupported transport types
    /// 2. Prefer transports that support full QUIC (if available)
    /// 3. Within QUIC-capable transports, prefer higher priority
    /// 4. Fall back to constrained transports if no QUIC-capable available
    pub fn select_best_transport_candidate<'a>(
        &self,
        candidates: &'a [TransportCandidate],
    ) -> Option<&'a TransportCandidate> {
        if candidates.is_empty() {
            return None;
        }

        // Filter to supported transports
        let supported: Vec<_> = candidates
            .iter()
            .filter(|c| self.supports_transport(c.transport_type()))
            .collect();

        if supported.is_empty() {
            debug!("No supported transport candidates available");
            return None;
        }

        // Separate into QUIC-capable and constrained candidates
        let (quic_capable, constrained): (Vec<_>, Vec<_>) = supported
            .into_iter()
            .partition(|c| c.supports_full_quic().unwrap_or(false));

        // Prefer QUIC-capable transports, sorted by priority
        if !quic_capable.is_empty() {
            return quic_capable.into_iter().max_by_key(|c| c.priority);
        }

        // Fall back to constrained transports, sorted by priority
        constrained.into_iter().max_by_key(|c| c.priority)
    }

    /// Filter candidates by transport type
    ///
    /// Returns candidates that match the specified transport type.
    pub fn filter_candidates_by_transport<'a>(
        &self,
        candidates: &'a [TransportCandidate],
        transport_type: TransportType,
    ) -> Vec<&'a TransportCandidate> {
        candidates
            .iter()
            .filter(|c| c.transport_type() == transport_type)
            .collect()
    }

    /// Filter candidates to only QUIC-capable transports
    ///
    /// Returns candidates whose transports support the full QUIC protocol
    /// (bandwidth >= 10kbps, MTU >= 1200, RTT < 2s).
    pub fn filter_quic_capable_candidates<'a>(
        &self,
        candidates: &'a [TransportCandidate],
    ) -> Vec<&'a TransportCandidate> {
        candidates
            .iter()
            .filter(|c| {
                c.supports_full_quic().unwrap_or(false)
                    && self.supports_transport(c.transport_type())
            })
            .collect()
    }

    /// Calculate a transport score for candidate comparison
    ///
    /// Higher scores are better. The score considers:
    /// - Transport type preference (UDP > BLE > LoRa > Serial)
    /// - QUIC capability (bonus for full QUIC support)
    /// - Latency tier (lower latency = higher score)
    /// - User-specified priority
    pub fn calculate_transport_score(&self, candidate: &TransportCandidate) -> u32 {
        let mut score: u32 = 0;

        // Base score from priority (0-65535 range)
        score += candidate.priority;

        // Transport type bonus (0-10000)
        let transport_bonus = match candidate.transport_type() {
            TransportType::Udp => 10000,
            TransportType::Yggdrasil => 9000,
            TransportType::I2p => 8000,
            TransportType::Ble => 7000,
            TransportType::Serial => 5000,
            TransportType::LoRa => 3000,
            TransportType::Ax25 => 2000,
        };
        score += transport_bonus;

        // QUIC capability bonus (0-50000)
        if candidate.supports_full_quic().unwrap_or(false) {
            score += 50000;
        }

        // Latency tier bonus (0-30000)
        if let Some(caps) = candidate.capabilities {
            let latency_bonus = match caps.latency_tier() {
                3 => 30000, // <100ms
                2 => 20000, // 100-500ms
                1 => 10000, // 500ms-2s
                0 => 0,     // >2s
                _ => 0,
            };
            score += latency_bonus;

            // Bandwidth tier bonus (0-20000)
            let bandwidth_bonus = match caps.bandwidth_tier() {
                3 => 20000, // High
                2 => 15000, // Medium
                1 => 10000, // Low
                0 => 5000,  // VeryLow
                _ => 0,
            };
            score += bandwidth_bonus;
        }

        score
    }

    /// Sort candidates by transport score (best first)
    pub fn sort_candidates_by_score(&self, candidates: &mut [TransportCandidate]) {
        candidates.sort_by(|a, b| {
            let score_a = self.calculate_transport_score(a);
            let score_b = self.calculate_transport_score(b);
            score_b.cmp(&score_a) // Descending order (highest first)
        });
    }

    // ============ Transport Candidate Storage ============

    /// Store a transport candidate for a peer
    ///
    /// This adds a new transport candidate to the peer's candidate list.
    /// Duplicate addresses are updated with the new priority and capabilities.
    pub fn store_transport_candidate(&self, peer_id: PeerId, candidate: TransportCandidate) {
        let mut entry = self
            .transport_candidates
            .entry(peer_id)
            .or_insert_with(Vec::new);

        // Check if we already have this address
        if let Some(existing) = entry.iter_mut().find(|c| c.address == candidate.address) {
            // Update existing candidate
            existing.priority = candidate.priority;
            existing.capabilities = candidate.capabilities;
            existing.state = candidate.state;
            debug!(
                "Updated transport candidate for peer {:?}: {:?}",
                peer_id, candidate.address
            );
        } else {
            // Add new candidate
            entry.push(candidate.clone());
            debug!(
                "Stored new transport candidate for peer {:?}: {:?}",
                peer_id, candidate.address
            );
        }
    }

    /// Get all transport candidates for a peer
    ///
    /// Returns an empty Vec if no candidates are known for the peer.
    pub fn get_transport_candidates(&self, peer_id: PeerId) -> Vec<TransportCandidate> {
        self.transport_candidates
            .get(&peer_id)
            .map(|entry| entry.value().clone())
            .unwrap_or_default()
    }

    /// Get transport candidates filtered by transport type
    pub fn get_candidates_by_type(
        &self,
        peer_id: PeerId,
        transport_type: TransportType,
    ) -> Vec<TransportCandidate> {
        self.transport_candidates
            .get(&peer_id)
            .map(|entry| {
                entry
                    .value()
                    .iter()
                    .filter(|c| c.transport_type() == transport_type)
                    .cloned()
                    .collect()
            })
            .unwrap_or_default()
    }

    /// Get the best transport candidate for a peer
    ///
    /// This considers transport support and capability matching.
    pub fn get_best_candidate(&self, peer_id: PeerId) -> Option<TransportCandidate> {
        let candidates = self.get_transport_candidates(peer_id);
        self.select_best_transport_candidate(&candidates).cloned()
    }

    /// Remove all transport candidates for a peer
    pub fn remove_transport_candidates(&self, peer_id: PeerId) {
        self.transport_candidates.remove(&peer_id);
        debug!("Removed all transport candidates for peer {:?}", peer_id);
    }

    /// Remove a specific transport candidate for a peer
    pub fn remove_transport_candidate(&self, peer_id: PeerId, address: &TransportAddr) {
        if let Some(mut entry) = self.transport_candidates.get_mut(&peer_id) {
            entry.retain(|c| &c.address != address);
            debug!(
                "Removed transport candidate {:?} for peer {:?}",
                address, peer_id
            );
        }
    }

    /// Get count of transport candidates for a peer
    pub fn transport_candidate_count(&self, peer_id: PeerId) -> usize {
        self.transport_candidates
            .get(&peer_id)
            .map(|entry| entry.len())
            .unwrap_or(0)
    }

    /// Get total count of all stored transport candidates
    pub fn total_transport_candidates(&self) -> usize {
        self.transport_candidates
            .iter()
            .map(|entry| entry.value().len())
            .sum()
    }

    /// Generate a local peer ID
    fn generate_local_peer_id() -> PeerId {
        use std::collections::hash_map::DefaultHasher;
        use std::hash::{Hash, Hasher};
        use std::time::SystemTime;

        let mut hasher = DefaultHasher::new();
        SystemTime::now().hash(&mut hasher);
        std::process::id().hash(&mut hasher);

        let hash = hasher.finish();
        let mut peer_id = [0u8; 32];
        peer_id[0..8].copy_from_slice(&hash.to_be_bytes());

        // Add some randomness
        for i in 8..32 {
            peer_id[i] = rand::random();
        }

        PeerId(peer_id)
    }

    /// Generate a deterministic temporary peer ID from a socket address.
    ///
    /// This is a fallback only for legacy/error paths where the authenticated
    /// peer identity is unavailable. It is stable for a given address but must
    /// not be treated as durable identity.
    fn generate_peer_id_from_address(addr: SocketAddr) -> PeerId {
        use std::collections::hash_map::DefaultHasher;
        use std::hash::{Hash, Hasher};

        let mut hasher = DefaultHasher::new();
        addr.hash(&mut hasher);

        let hash = hasher.finish();
        let mut peer_id = [0u8; 32];
        peer_id[0..8].copy_from_slice(&hash.to_be_bytes());

        warn!(
            "Generated temporary address-derived peer ID for {}. This ID is not durable identity.",
            addr
        );
        PeerId(peer_id)
    }

    /// Derive a peer ID from the authenticated raw public key if available.
    ///
    /// For rustls, `peer_identity()` returns `Vec<CertificateDer>`. For RFC 7250 Raw Public Keys,
    /// this contains SubjectPublicKeyInfo for ML-DSA-65. We extract the
    /// ML-DSA-65 public key from the SPKI structure and derive the PeerId.
    fn derive_peer_id_from_connection(connection: &InnerConnection) -> Option<PeerId> {
        if let Some(identity) = connection.peer_identity() {
            // rustls returns Vec<CertificateDer> - downcast to that type
            if let Some(certs) =
                identity.downcast_ref::<Vec<rustls::pki_types::CertificateDer<'static>>>()
            {
                if let Some(cert) = certs.first() {
                    // v0.2: For RFC 7250 Raw Public Keys with ML-DSA-65
                    let spki = cert.as_ref();
                    if let Some(public_key) = extract_ml_dsa_from_spki(spki) {
                        let peer_id =
                            crate::crypto::raw_public_keys::pqc::derive_peer_id_from_public_key(
                                &public_key,
                            );
                        debug!("Derived peer ID from ML-DSA-65 public key in SPKI");
                        return Some(peer_id);
                    } else {
                        debug!(
                            "Certificate is not ML-DSA-65 SPKI format (len={})",
                            spki.len()
                        );
                    }
                }
            }
        }

        None
    }

    /// Extract peer ID from connection by deriving it from the peer's public key
    ///
    /// v0.2: Pure PQC - Uses ML-DSA-65 for all authentication.
    /// For rustls, `peer_identity()` returns `Vec<CertificateDer>`. For RFC 7250 Raw Public Keys,
    /// this contains SubjectPublicKeyInfo for ML-DSA-65. We extract the
    /// ML-DSA-65 public key from the SPKI structure and derive the PeerId.
    pub async fn extract_peer_id_from_connection(
        &self,
        connection: &InnerConnection,
    ) -> Option<PeerId> {
        // Delegate to the static method which handles both CertificateDer and raw [u8; 32]
        Self::derive_peer_id_from_connection(connection)
    }

    /// Shutdown the endpoint
    pub async fn shutdown(&self) -> Result<(), NatTraversalError> {
        // Set shutdown flag and wake any task parked in accept_connection()
        // or transport listener loops
        self.shutdown.store(true, Ordering::Relaxed);
        self.incoming_notify.notify_waiters();
        self.traversal_event_notify.notify_waiters();
        self.shutdown_notify.notify_waiters();

        // Close all active connections
        // DashMap: collect peer_ids then remove them one by one
        let peer_ids: Vec<PeerId> = self.connections.iter().map(|e| *e.key()).collect();
        for peer_id in peer_ids {
            if let Some((_, connection)) = self.connections.remove(&peer_id) {
                info!("Closing connection to peer {:?}", peer_id);
                connection.close(crate::VarInt::from_u32(0), b"Shutdown");
            }
        }

        // Bounded drain: in simultaneous-shutdown scenarios both sides may
        // close at once, so wait_idle can stall until the idle timeout.
        if let Some(ref endpoint) = self.inner_endpoint {
            if tokio::time::timeout(SHUTDOWN_DRAIN_TIMEOUT, endpoint.wait_idle())
                .await
                .is_err()
            {
                info!("wait_idle timed out during shutdown, proceeding");
            }
        }

        // Wait for transport listener tasks to complete
        let handles = {
            let mut listener_handles = self.transport_listener_handles.lock();
            std::mem::take(&mut *listener_handles)
        };

        if !handles.is_empty() {
            debug!(
                "Waiting for {} transport listener tasks to complete",
                handles.len()
            );
            match tokio::time::timeout(SHUTDOWN_DRAIN_TIMEOUT, async {
                for handle in handles {
                    if let Err(e) = handle.await {
                        warn!("Transport listener task failed during shutdown: {e}");
                    }
                }
            })
            .await
            {
                Ok(()) => debug!("All transport listener tasks completed"),
                Err(_) => warn!("Transport listener tasks timed out during shutdown, proceeding"),
            }
        }

        info!("NAT traversal endpoint shutdown completed");
        Ok(())
    }

    /// Discover address candidates for a peer
    pub async fn discover_candidates(
        &self,
        peer_id: PeerId,
    ) -> Result<Vec<CandidateAddress>, NatTraversalError> {
        debug!("Discovering address candidates for peer {:?}", peer_id);

        let mut candidates = Vec::new();

        // Get bootstrap nodes - parking_lot::RwLock doesn't poison
        let bootstrap_nodes = self.bootstrap_nodes.read().clone();

        // Start discovery process - parking_lot::Mutex doesn't poison
        {
            let mut discovery = self.discovery_manager.lock();

            discovery
                .start_discovery(peer_id, bootstrap_nodes)
                .map_err(|e| NatTraversalError::CandidateDiscoveryFailed(e.to_string()))?;
        }
        self.discovery_state_notify.notify_waiters();

        // Poll for discovery results with timeout
        let timeout_duration = self.config.coordination_timeout;
        let start_time = std::time::Instant::now();

        while start_time.elapsed() < timeout_duration {
            let discovery_events = {
                let mut discovery = self.discovery_manager.lock();
                discovery.poll(std::time::Instant::now())
            };

            for event in discovery_events {
                match event {
                    DiscoveryEvent::LocalCandidateDiscovered { candidate } => {
                        candidates.push(candidate.clone());

                        // Send ADD_ADDRESS frame to advertise this candidate to the peer
                        self.send_candidate_advertisement(peer_id, &candidate)
                            .await
                            .unwrap_or_else(|e| {
                                debug!("Failed to send candidate advertisement: {}", e)
                            });
                    }
                    DiscoveryEvent::ServerReflexiveCandidateDiscovered { candidate, .. } => {
                        candidates.push(candidate.clone());

                        // Send ADD_ADDRESS frame to advertise this candidate to the peer
                        self.send_candidate_advertisement(peer_id, &candidate)
                            .await
                            .unwrap_or_else(|e| {
                                debug!("Failed to send candidate advertisement: {}", e)
                            });
                    }
                    // Prediction events removed in minimal flow
                    DiscoveryEvent::DiscoveryCompleted { .. } => {
                        // Discovery complete, return candidates
                        return Ok(candidates);
                    }
                    DiscoveryEvent::DiscoveryFailed {
                        error,
                        partial_results,
                    } => {
                        // Use partial results if available
                        candidates.extend(partial_results);
                        if candidates.is_empty() {
                            return Err(NatTraversalError::CandidateDiscoveryFailed(
                                error.to_string(),
                            ));
                        }
                        return Ok(candidates);
                    }
                    _ => {}
                }
            }

            // Wait until the discovery manager's next meaningful deadline instead
            // of blindly polling on a fixed cadence. Local interface scans still
            // use a small poll interval because the platform abstraction does not
            // yet expose a waitable completion event.
            let now = std::time::Instant::now();
            let overall_deadline = start_time + timeout_duration;
            let wake_at = self
                .discovery_manager
                .lock()
                .next_poll_deadline_for_peer(peer_id, now)
                .unwrap_or(overall_deadline)
                .min(overall_deadline);
            if wake_at <= now {
                continue;
            }
            tokio::select! {
                _ = self.discovery_state_notify.notified() => {}
                _ = sleep(wake_at.saturating_duration_since(now)) => {}
            }
        }

        if candidates.is_empty() {
            Err(NatTraversalError::NoCandidatesFound)
        } else {
            Ok(candidates)
        }
    }

    /// Create PUNCH_ME_NOW extension frame for NAT traversal coordination
    #[allow(dead_code)]
    fn create_punch_me_now_frame(&self, peer_id: PeerId) -> Result<Vec<u8>, NatTraversalError> {
        // PUNCH_ME_NOW frame format (IETF QUIC NAT Traversal draft):
        // Frame Type: 0x41 (PUNCH_ME_NOW)
        // Length: Variable
        // Peer ID: 32 bytes
        // Timestamp: 8 bytes
        // Coordination Token: 16 bytes

        let mut frame = Vec::new();

        // Frame type
        frame.push(0x41);

        // Peer ID (32 bytes)
        frame.extend_from_slice(&peer_id.0);

        // Timestamp (8 bytes, current time as milliseconds since epoch)
        let timestamp = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .unwrap_or_default()
            .as_millis() as u64;
        frame.extend_from_slice(&timestamp.to_be_bytes());

        // Coordination token (16 random bytes for this session)
        let mut token = [0u8; 16];
        for byte in &mut token {
            *byte = rand::random();
        }
        frame.extend_from_slice(&token);

        Ok(frame)
    }

    #[allow(dead_code)]
    fn attempt_hole_punching(&self, peer_id: PeerId) -> Result<(), NatTraversalError> {
        debug!("Attempting hole punching for peer {:?}", peer_id);

        // Get candidate pairs for this peer
        let candidate_pairs = self.get_candidate_pairs_for_peer(peer_id)?;

        if candidate_pairs.is_empty() {
            return Err(NatTraversalError::NoCandidatesFound);
        }

        info!(
            "Generated {} candidate pairs for hole punching with peer {:?}",
            candidate_pairs.len(),
            peer_id
        );

        // Attempt hole punching with each candidate pair

        self.attempt_quic_hole_punching(peer_id, candidate_pairs)
    }

    /// Generate candidate pairs for hole punching based on ICE-like algorithm
    #[allow(dead_code)]
    fn get_candidate_pairs_for_peer(
        &self,
        peer_id: PeerId,
    ) -> Result<Vec<CandidatePair>, NatTraversalError> {
        // Get discovered candidates from the discovery manager
        // parking_lot::Mutex doesn't poison
        let discovery_candidates = {
            let discovery = self.discovery_manager.lock();
            discovery.get_candidates_for_peer(peer_id)
        };

        if discovery_candidates.is_empty() {
            return Err(NatTraversalError::NoCandidatesFound);
        }

        // Create candidate pairs with priorities (ICE-like pairing)
        let mut candidate_pairs = Vec::new();
        let local_candidates = discovery_candidates
            .iter()
            .filter(|c| matches!(c.source, CandidateSource::Local))
            .collect::<Vec<_>>();
        let remote_candidates = discovery_candidates
            .iter()
            .filter(|c| !matches!(c.source, CandidateSource::Local))
            .collect::<Vec<_>>();

        // Pair each local candidate with each remote candidate
        // Skip cross-family pairs (IPv4 ↔ IPv6) as they cannot connect at the socket level
        for local in &local_candidates {
            for remote in &remote_candidates {
                // Cross-family pairs will always fail - skip them
                let local_is_v4 = local.address.ip().is_ipv4();
                let remote_is_v4 = remote.address.ip().is_ipv4();
                if local_is_v4 != remote_is_v4 {
                    trace!(
                        "Skipping cross-family candidate pair: {} ↔ {}",
                        local.address, remote.address
                    );
                    continue;
                }

                let pair_priority = self.calculate_candidate_pair_priority(local, remote);
                candidate_pairs.push(CandidatePair {
                    local_candidate: (*local).clone(),
                    remote_candidate: (*remote).clone(),
                    priority: pair_priority,
                    state: CandidatePairState::Waiting,
                });
            }
        }

        // Sort by priority (highest first)
        candidate_pairs.sort_by_key(|pair| std::cmp::Reverse(pair.priority));

        // Limit to reasonable number for initial attempts
        candidate_pairs.truncate(8);

        Ok(candidate_pairs)
    }

    /// Calculate candidate pair priority using ICE algorithm
    #[allow(dead_code)]
    fn calculate_candidate_pair_priority(
        &self,
        local: &CandidateAddress,
        remote: &CandidateAddress,
    ) -> u64 {
        // ICE candidate pair priority formula: min(G,D) * 2^32 + max(G,D) * 2 + (G>D ? 1 : 0)
        // Where G is controlling agent priority, D is controlled agent priority

        let local_type_preference = match local.source {
            CandidateSource::Local => 126,
            CandidateSource::Observed { .. } => 100,
            CandidateSource::Predicted => 75,
            CandidateSource::Peer => 50,
        };

        let remote_type_preference = match remote.source {
            CandidateSource::Local => 126,
            CandidateSource::Observed { .. } => 100,
            CandidateSource::Predicted => 75,
            CandidateSource::Peer => 50,
        };

        // Simplified priority calculation
        let local_priority = (local_type_preference as u64) << 8 | local.priority as u64;
        let remote_priority = (remote_type_preference as u64) << 8 | remote.priority as u64;

        let min_priority = local_priority.min(remote_priority);
        let max_priority = local_priority.max(remote_priority);

        (min_priority << 32)
            | (max_priority << 1)
            | if local_priority > remote_priority {
                1
            } else {
                0
            }
    }

    /// Real QUIC-based hole punching implementation
    #[allow(dead_code)]
    fn attempt_quic_hole_punching(
        &self,
        peer_id: PeerId,
        candidate_pairs: Vec<CandidatePair>,
    ) -> Result<(), NatTraversalError> {
        let _endpoint = self.inner_endpoint.as_ref().ok_or_else(|| {
            NatTraversalError::ConfigError("QUIC endpoint not initialized".to_string())
        })?;

        for pair in candidate_pairs {
            debug!(
                "Attempting hole punch with candidate pair: {} -> {}",
                pair.local_candidate.address, pair.remote_candidate.address
            );

            // Create PATH_CHALLENGE frame data (8 random bytes)
            let mut challenge_data = [0u8; 8];
            for byte in &mut challenge_data {
                *byte = rand::random();
            }

            // Create a raw UDP socket bound to the local candidate address
            let local_socket =
                std::net::UdpSocket::bind(pair.local_candidate.address).map_err(|e| {
                    NatTraversalError::NetworkError(format!(
                        "Failed to bind to local candidate: {e}"
                    ))
                })?;

            // Craft a minimal QUIC packet with PATH_CHALLENGE frame
            let path_challenge_packet = self.create_path_challenge_packet(challenge_data)?;

            // Send the packet to the remote candidate address
            match local_socket.send_to(&path_challenge_packet, pair.remote_candidate.address) {
                Ok(bytes_sent) => {
                    debug!(
                        "Sent {} bytes for hole punch from {} to {}",
                        bytes_sent, pair.local_candidate.address, pair.remote_candidate.address
                    );

                    // Set a short timeout for response
                    local_socket
                        .set_read_timeout(Some(Duration::from_millis(100)))
                        .map_err(|e| {
                            NatTraversalError::NetworkError(format!("Failed to set timeout: {e}"))
                        })?;

                    // Try to receive a response
                    let mut response_buffer = [0u8; 1024];
                    match local_socket.recv_from(&mut response_buffer) {
                        Ok((_bytes_received, response_addr)) => {
                            if response_addr == pair.remote_candidate.address {
                                info!(
                                    "Hole punch succeeded for peer {:?}: {} <-> {}",
                                    peer_id,
                                    pair.local_candidate.address,
                                    pair.remote_candidate.address
                                );

                                // Store successful candidate pair for connection establishment
                                self.store_successful_candidate_pair(peer_id, pair)?;
                                return Ok(());
                            } else {
                                debug!(
                                    "Received response from unexpected address: {}",
                                    response_addr
                                );
                            }
                        }
                        Err(e)
                            if e.kind() == std::io::ErrorKind::WouldBlock
                                || e.kind() == std::io::ErrorKind::TimedOut =>
                        {
                            debug!("No response received for hole punch attempt");
                        }
                        Err(e) => {
                            debug!("Error receiving hole punch response: {}", e);
                        }
                    }
                }
                Err(e) => {
                    debug!("Failed to send hole punch packet: {}", e);
                }
            }
        }

        // If we get here, all hole punch attempts failed
        Err(NatTraversalError::HolePunchingFailed)
    }

    /// Create a minimal QUIC packet with PATH_CHALLENGE frame for hole punching
    fn create_path_challenge_packet(
        &self,
        challenge_data: [u8; 8],
    ) -> Result<Vec<u8>, NatTraversalError> {
        // Create a minimal QUIC packet structure
        // This is a simplified implementation - in production, you'd use proper QUIC packet construction
        let mut packet = Vec::new();

        // QUIC packet header (simplified)
        packet.push(0x40); // Short header, fixed bit set
        packet.extend_from_slice(&[0, 0, 0, 1]); // Connection ID (simplified)

        // PATH_CHALLENGE frame
        packet.push(0x1a); // PATH_CHALLENGE frame type
        packet.extend_from_slice(&challenge_data); // 8-byte challenge data

        Ok(packet)
    }

    /// Store successful candidate pair for later connection establishment
    fn store_successful_candidate_pair(
        &self,
        peer_id: PeerId,
        pair: CandidatePair,
    ) -> Result<(), NatTraversalError> {
        debug!(
            "Storing successful candidate pair for peer {:?}: {} <-> {}",
            peer_id, pair.local_candidate.address, pair.remote_candidate.address
        );

        // Store the successful remote address for use in connection establishment
        // DashMap provides lock-free .insert()
        self.successful_candidates
            .insert(peer_id, pair.remote_candidate.address);
        info!(
            "Stored successful candidate for peer {:?}: {}",
            peer_id, pair.remote_candidate.address
        );

        if let Some(mut session) = self.active_sessions.get_mut(&peer_id) {
            for candidate in &mut session.candidates {
                if normalize_socket_addr(candidate.address)
                    == normalize_socket_addr(pair.remote_candidate.address)
                {
                    candidate.state = CandidateState::Validating;
                    break;
                }
            }
        }

        if let Some(ref callback) = self.event_callback {
            callback(NatTraversalEvent::CandidateValidated {
                peer_id,
                candidate_address: pair.remote_candidate.address,
            });
        }

        Ok(())
    }

    /// Get the successful candidate address for a peer (discovered via hole punching)
    ///
    /// Returns the remote address that successfully responded during hole punching.
    /// This address should be used for establishing the actual QUIC connection.
    fn get_successful_candidate_address(&self, peer_id: PeerId) -> Option<SocketAddr> {
        // DashMap provides lock-free .get() that returns Option<Ref<K, V>>
        self.successful_candidates.get(&peer_id).map(|r| *r.value())
    }

    fn set_session_phase(
        session: &mut NatTraversalSession,
        now: std::time::Instant,
        phase: TraversalPhase,
    ) {
        if session.phase != phase {
            session.phase = phase;
            session.phase_started_at = now;
            session.last_progress_at = now;
            session.retry_at = None;
            session.next_deadline = None;
            session.session_state.last_transition = now;
        }
    }

    /// Attempt connection to a specific candidate address
    fn attempt_connection_to_candidate(
        &self,
        peer_id: PeerId,
        candidate: &CandidateAddress,
    ) -> Result<(), NatTraversalError> {
        // Check if connection already exists - another candidate may have succeeded
        if self.has_existing_connection(&peer_id) {
            debug!(
                "Connection already exists for peer {:?}, skipping candidate {}",
                peer_id, candidate.address
            );
            return Ok(());
        }

        {
            let endpoint = self.inner_endpoint.as_ref().ok_or_else(|| {
                NatTraversalError::ConfigError("QUIC endpoint not initialized".to_string())
            })?;

            // Create server name for the connection
            let server_name = format!("peer-{:x}", peer_id.0[0] as u32);

            debug!(
                "Attempting QUIC connection to candidate {} for peer {:?}",
                candidate.address, peer_id
            );

            // Use the sync connect method from QUIC endpoint
            match endpoint.connect(candidate.address, &server_name) {
                Ok(connecting) => {
                    info!(
                        "Connection attempt initiated to {} for peer {:?}",
                        candidate.address, peer_id
                    );

                    // Spawn a task to handle the connection completion
                    if let Some(event_tx) = &self.event_tx {
                        let event_tx = event_tx.clone();
                        let connections = self.connections.clone();
                        let observed_address_tx = self.observed_address_tx.clone();
                        let traversal_event_notify = self.traversal_event_notify.clone();
                        let incoming_notify = self.incoming_notify.clone();
                        let peer_id_clone = peer_id;
                        let address = candidate.address;

                        tokio::spawn(async move {
                            match connecting.await {
                                Ok(connection) => {
                                    // Check if another task already inserted a connection for this peer
                                    // This prevents race conditions when multiple candidates succeed
                                    if connections.contains_key(&peer_id_clone) {
                                        debug!(
                                            "Connection already exists for peer {:?}, discarding duplicate from {}",
                                            peer_id_clone, address
                                        );
                                        // Close the duplicate connection to free resources
                                        connection.close(0u32.into(), b"duplicate connection");
                                        return;
                                    }

                                    info!(
                                        "Successfully connected to {} for peer {:?}",
                                        address, peer_id_clone
                                    );

                                    // Store the connection
                                    // DashMap provides lock-free .insert()
                                    connections.insert(peer_id_clone, connection.clone());

                                    // Send connection established event (we initiated hole punch = Client side)
                                    let _ =
                                        event_tx.send(NatTraversalEvent::ConnectionEstablished {
                                            peer_id: peer_id_clone,
                                            remote_address: address,
                                            side: Side::Client,
                                        });
                                    incoming_notify.notify_one();
                                    traversal_event_notify.notify_waiters();
                                    Self::spawn_observed_address_watch_task_parts(
                                        observed_address_tx.clone(),
                                        peer_id_clone,
                                        connection.clone(),
                                    );

                                    // Handle the connection
                                    Self::handle_connection(
                                        peer_id_clone,
                                        connection,
                                        event_tx,
                                        traversal_event_notify,
                                    )
                                    .await;
                                }
                                Err(e) => {
                                    warn!("Connection to {} failed: {}", address, e);
                                }
                            }
                        });
                    }

                    Ok(())
                }
                Err(e) => {
                    warn!(
                        "Failed to initiate connection to {}: {}",
                        candidate.address, e
                    );
                    Err(NatTraversalError::ConnectionFailed(format!(
                        "Failed to connect to {}: {}",
                        candidate.address, e
                    )))
                }
            }
        }
    }

    /// Drain any pending events from async tasks
    #[inline]
    fn drain_pending_events(&self, events: &mut Vec<NatTraversalEvent>) {
        let mut event_rx = self.event_rx.lock();
        while let Ok(event) = event_rx.try_recv() {
            self.emit_event(events, event);
        }
    }

    /// Detect closed connections and emit ConnectionLost events
    fn poll_closed_connections(&self, events: &mut Vec<NatTraversalEvent>) {
        let closed_connections: Vec<_> = self
            .connections
            .iter()
            .filter_map(|entry| {
                entry
                    .value()
                    .close_reason()
                    .map(|reason| (*entry.key(), reason.clone()))
            })
            .collect();

        for (peer_id, reason) in closed_connections {
            self.connections.remove(&peer_id);
            self.emit_event(
                events,
                NatTraversalEvent::ConnectionLost {
                    peer_id,
                    reason: reason.to_string(),
                },
            );
        }
    }

    /// Poll candidate discovery manager and convert events
    fn poll_discovery_manager(&self, now: std::time::Instant, events: &mut Vec<NatTraversalEvent>) {
        let mut discovery = self.discovery_manager.lock();
        let discovery_events = discovery.poll(now);
        drop(discovery);

        for discovery_event in discovery_events {
            let current_peer_id = self.get_current_discovery_peer_id();
            if self.discovery_retry_backoff_active(current_peer_id, now) {
                continue;
            }
            if let Some(nat_event) = self.convert_discovery_event(discovery_event) {
                self.emit_event(events, nat_event);
            }
        }
    }

    /// Poll for NAT traversal progress and state machine updates
    pub fn poll(
        &self,
        now: std::time::Instant,
    ) -> Result<Vec<NatTraversalEvent>, NatTraversalError> {
        let mut events = Vec::new();

        // Drain pending events from async tasks
        self.drain_pending_events(&mut events);

        // Handle closed connections
        self.poll_closed_connections(&mut events);

        // Poll candidate discovery
        self.poll_discovery_manager(now, &mut events);

        // CRITICAL: Two-phase approach to prevent deadlocks
        // Phase 1: Collect work to be done (hold DashMap entries briefly)
        // Phase 2: Execute work (no DashMap entries held)

        let mut coordination_requests: Vec<(PeerId, SocketAddr)> = Vec::new();
        let mut hole_punch_requests: Vec<(PeerId, Vec<CandidateAddress>)> = Vec::new();
        let mut validation_requests: Vec<(PeerId, SocketAddr)> = Vec::new();
        // Collect Discovery-phase peers for deferred discovery_manager access
        // to avoid locking discovery_manager while iter_mut() holds all shard guards
        let mut early_discovery_peers: Vec<PeerId> = Vec::new();
        let mut timeout_discovery_peers: Vec<PeerId> = Vec::new();
        let active_session_ids: Vec<PeerId> = self
            .active_sessions
            .iter()
            .map(|entry| *entry.key())
            .collect();
        let discovery_phase_deadlines: HashMap<PeerId, std::time::Instant> = {
            let discovery = self.discovery_manager.lock();
            active_session_ids
                .iter()
                .filter_map(|peer_id| {
                    discovery
                        .phase_timeout_deadline_for_peer(*peer_id)
                        .map(|deadline| (*peer_id, deadline))
                })
                .collect()
        };

        // Phase 1: Collect work and update session states (brief DashMap access)
        for mut entry in self.active_sessions.iter_mut() {
            let session = entry.value_mut();
            session.next_deadline = self.recompute_session_deadline(session, now);
            let total_elapsed = now.duration_since(session.started_at);
            let timed_out = match session.phase {
                TraversalPhase::Discovery => discovery_phase_deadlines
                    .get(&session.peer_id)
                    .copied()
                    .is_some_and(|deadline| now >= deadline),
                _ => session
                    .next_deadline
                    .as_ref()
                    .map(|deadline| deadline.at)
                    .is_some_and(|deadline| now >= deadline),
            };

            match session.phase {
                TraversalPhase::Discovery => {
                    if let Some(SessionDeadline {
                        kind: TraversalDeadlineKind::RetryBackoff,
                        at,
                    }) = session.next_deadline.as_ref()
                    {
                        if now < *at {
                            continue;
                        }

                        session.retry_at = None;
                        session.next_deadline = None;
                        session.last_progress_at = now;
                        session.phase_started_at = now;
                        continue;
                    }

                    if timed_out {
                        timeout_discovery_peers.push(session.peer_id);
                    } else {
                        early_discovery_peers.push(session.peer_id);
                    }
                }
                TraversalPhase::Coordination => {
                    if let Some(coordinator) = self.select_coordinator() {
                        // Coordination is now event-driven: once candidates exist and a
                        // coordinator is available, request help immediately. The timeout
                        // only governs absence of an available coordinator.
                        Self::set_session_phase(session, now, TraversalPhase::Synchronization);
                        coordination_requests.push((session.peer_id, coordinator));
                    } else if timed_out {
                        self.handle_phase_failure(
                            session,
                            now,
                            &mut events,
                            NatTraversalError::NoBootstrapNodes,
                        );
                    }
                }
                TraversalPhase::Synchronization => {
                    if Self::session_is_synchronized(session) {
                        Self::set_session_phase(session, now, TraversalPhase::Punching);
                        self.emit_event(
                            &mut events,
                            NatTraversalEvent::TraversalProgress {
                                peer_id: session.peer_id,
                                phase: TraversalPhase::Punching,
                                deadline: self
                                    .recompute_session_deadline(session, now)
                                    .map(|deadline| deadline.at),
                            },
                        );
                        self.emit_event(
                            &mut events,
                            NatTraversalEvent::HolePunchingStarted {
                                peer_id: session.peer_id,
                                targets: session.candidates.iter().map(|c| c.address).collect(),
                            },
                        );
                        hole_punch_requests.push((session.peer_id, session.candidates.clone()));
                    } else if timed_out {
                        self.handle_phase_failure(
                            session,
                            now,
                            &mut events,
                            NatTraversalError::Timeout,
                        );
                    }
                }
                TraversalPhase::Punching => {
                    if let Some(successful_path) = self.check_punch_results_for_session(session) {
                        Self::set_session_phase(session, now, TraversalPhase::Validation);
                        self.emit_event(
                            &mut events,
                            NatTraversalEvent::TraversalProgress {
                                peer_id: session.peer_id,
                                phase: TraversalPhase::Validation,
                                deadline: self
                                    .recompute_session_deadline(session, now)
                                    .map(|deadline| deadline.at),
                            },
                        );
                        if let Some(candidate) = session.candidates.iter_mut().find(|candidate| {
                            normalize_socket_addr(candidate.address)
                                == normalize_socket_addr(successful_path)
                        }) {
                            candidate.state = CandidateState::Validating;
                        }

                        if self.has_existing_connection(&session.peer_id) {
                            self.emit_event(
                                &mut events,
                                NatTraversalEvent::PathValidated {
                                    peer_id: session.peer_id,
                                    address: successful_path,
                                    rtt: self.path_validation_rtt(session),
                                },
                            );
                            validation_requests.push((session.peer_id, successful_path));
                        }
                    } else if timed_out {
                        self.handle_phase_failure(
                            session,
                            now,
                            &mut events,
                            NatTraversalError::PunchingFailed("No successful punch".to_string()),
                        );
                    }
                }
                TraversalPhase::Validation => {
                    if self.session_path_is_validated(session) {
                        Self::set_session_phase(session, now, TraversalPhase::Connected);
                        self.emit_event(
                            &mut events,
                            NatTraversalEvent::TraversalProgress {
                                peer_id: session.peer_id,
                                phase: TraversalPhase::Connected,
                                deadline: self
                                    .recompute_session_deadline(session, now)
                                    .map(|deadline| deadline.at),
                            },
                        );
                        let winning_remote_candidate = self.winning_remote_candidate(session);
                        let final_address = winning_remote_candidate
                            .as_ref()
                            .map(|candidate| candidate.address)
                            .or_else(|| {
                                session
                                    .candidates
                                    .first()
                                    .map(|candidate| candidate.address)
                            })
                            .unwrap_or_else(create_random_port_bind_addr);
                        let winning_pair =
                            winning_remote_candidate.as_ref().and_then(|candidate| {
                                self.best_effort_winning_pair(session, candidate)
                            });
                        self.emit_event(
                            &mut events,
                            NatTraversalEvent::TraversalSucceeded {
                                peer_id: session.peer_id,
                                final_address,
                                winning_pair,
                                attempts: session.attempt,
                                total_time: total_elapsed,
                            },
                        );
                        let phase_elapsed = now.duration_since(session.phase_started_at);
                        info!(
                            "NAT traversal succeeded for peer {:?} in {:?} total ({:?} in {:?})",
                            session.peer_id, total_elapsed, phase_elapsed, session.phase
                        );
                    } else if timed_out {
                        self.handle_phase_failure(
                            session,
                            now,
                            &mut events,
                            NatTraversalError::ValidationFailed(
                                "Path validation timeout".to_string(),
                            ),
                        );
                    }
                }
                TraversalPhase::Connected => {
                    if !self.is_connection_healthy(&session.peer_id) {
                        warn!(
                            "Connection to peer {:?} is no longer healthy",
                            session.peer_id
                        );
                    }
                }
                TraversalPhase::Failed => {
                    // Session has already failed, no action needed
                }
            }
        }
        // Phase 1 complete - all DashMap entries are now released

        // Phase 1b: Process deferred discovery_manager checks
        // Each get_mut() locks only one shard, safe to lock discovery_manager
        for peer_id in early_discovery_peers {
            let discovered_candidates = self
                .discovery_manager
                .lock()
                .get_candidates_for_peer(peer_id);

            if !discovered_candidates.is_empty() {
                if let Some(mut entry) = self.active_sessions.get_mut(&peer_id) {
                    let session = entry.value_mut();
                    session.candidates = discovered_candidates;
                    Self::set_session_phase(session, now, TraversalPhase::Coordination);
                }
                self.emit_event(
                    &mut events,
                    NatTraversalEvent::PhaseTransition {
                        peer_id,
                        from_phase: TraversalPhase::Discovery,
                        to_phase: TraversalPhase::Coordination,
                    },
                );
                self.emit_event(
                    &mut events,
                    NatTraversalEvent::TraversalProgress {
                        peer_id,
                        phase: TraversalPhase::Coordination,
                        deadline: self
                            .active_sessions
                            .get(&peer_id)
                            .and_then(|entry| self.recompute_session_deadline(entry.value(), now))
                            .map(|deadline| deadline.at),
                    },
                );
                let time_remaining = discovery_phase_deadlines
                    .get(&peer_id)
                    .copied()
                    .map(|deadline| deadline.saturating_duration_since(now));
                if let Some(time_remaining) = time_remaining {
                    info!(
                        "Peer {:?} early-advanced from Discovery to Coordination ({:.1}s before timeout)",
                        peer_id,
                        time_remaining.as_secs_f64()
                    );
                } else {
                    info!(
                        "Peer {:?} early-advanced from Discovery to Coordination on evidence",
                        peer_id,
                    );
                }
            }
        }

        for peer_id in timeout_discovery_peers {
            let discovered_candidates = self
                .discovery_manager
                .lock()
                .get_candidates_for_peer(peer_id);

            if !discovered_candidates.is_empty() {
                if let Some(mut entry) = self.active_sessions.get_mut(&peer_id) {
                    let session = entry.value_mut();
                    session.candidates = discovered_candidates;
                    Self::set_session_phase(session, now, TraversalPhase::Coordination);
                    self.emit_event(
                        &mut events,
                        NatTraversalEvent::PhaseTransition {
                            peer_id: session.peer_id,
                            from_phase: TraversalPhase::Discovery,
                            to_phase: TraversalPhase::Coordination,
                        },
                    );
                    self.emit_event(
                        &mut events,
                        NatTraversalEvent::TraversalProgress {
                            peer_id: session.peer_id,
                            phase: TraversalPhase::Coordination,
                            deadline: self
                                .recompute_session_deadline(session, now)
                                .map(|deadline| deadline.at),
                        },
                    );
                    info!(
                        "Peer {:?} advanced from Discovery to Coordination at timeout",
                        session.peer_id,
                    );
                }
            } else if let Some(mut entry) = self.active_sessions.get_mut(&peer_id) {
                let session = entry.value_mut();
                self.handle_phase_failure(
                    session,
                    now,
                    &mut events,
                    NatTraversalError::NoCandidatesFound,
                );
            }
        }

        // Phase 2: Execute deferred work (no DashMap entries held)

        // Execute coordination requests
        for (peer_id, coordinator) in coordination_requests {
            // Re-check for existing connection before executing deferred coordination
            // A connection may have been established during the gap between phase collection and execution
            if self.has_existing_connection(&peer_id) {
                debug!(
                    "Connection established for peer {:?} before coordination execution, skipping",
                    peer_id
                );
                continue;
            }
            match self.send_coordination_request(peer_id, coordinator) {
                Ok(_) => {
                    self.emit_event(
                        &mut events,
                        NatTraversalEvent::CoordinationRequested {
                            peer_id,
                            coordinator,
                        },
                    );
                    info!(
                        "Coordination requested for peer {:?} via {}",
                        peer_id, coordinator
                    );
                }
                Err(e) => {
                    if let Some(mut session) = self.active_sessions.get_mut(&peer_id) {
                        self.handle_phase_failure(&mut session, now, &mut events, e);
                    }
                }
            }
        }

        // Execute hole punch requests
        for (peer_id, candidates) in hole_punch_requests {
            // Re-check for existing connection before executing deferred hole punch
            // A connection may have been established during the gap between phase collection and execution
            if self.has_existing_connection(&peer_id) {
                debug!(
                    "Connection established for peer {:?} before hole punch execution, skipping",
                    peer_id
                );
                continue;
            }
            if let Err(e) = self.initiate_hole_punching(peer_id, &candidates) {
                if let Some(mut session) = self.active_sessions.get_mut(&peer_id) {
                    self.handle_phase_failure(&mut session, now, &mut events, e);
                }
            }
        }

        // Execute validation requests
        for (peer_id, address) in validation_requests {
            if let Err(e) = self.validate_path(peer_id, address) {
                if let Some(mut session) = self.active_sessions.get_mut(&peer_id) {
                    self.handle_phase_failure(&mut session, now, &mut events, e);
                }
            }
        }

        Ok(events)
    }

    /// Compute the next deadline at which the legacy session-state driver may
    /// need to wake.
    #[cfg(test)]
    fn next_session_state_poll_deadline(
        &self,
        now: std::time::Instant,
    ) -> Option<std::time::Instant> {
        self.active_sessions
            .iter()
            .filter_map(|entry| {
                let session = entry.value();
                let deadline = match session.session_state.state {
                    ConnectionState::Connecting => Some(
                        session.session_state.last_transition
                            + self
                                .timeout_config
                                .nat_traversal
                                .connection_establishment_timeout,
                    ),
                    ConnectionState::Connected => None,
                    ConnectionState::Idle => Some(
                        session.session_state.last_transition
                            + self.timeout_config.discovery.server_reflexive_cache_ttl,
                    ),
                    ConnectionState::Migrating => Some(
                        session.session_state.last_transition
                            + self.timeout_config.nat_traversal.migration_timeout,
                    ),
                    ConnectionState::Closed => Some(
                        session.session_state.last_transition
                            + self.timeout_config.discovery.interface_cache_ttl,
                    ),
                }?;
                Some(deadline.max(now))
            })
            .min()
    }

    /// Compute the next deadline at which polling this peer's session may make
    /// forward progress.
    ///
    /// This replaces blanket sleep-loop polling with the earliest meaningful
    /// phase timeout or discovery-manager wakeup for the specific peer.
    pub(crate) fn next_session_poll_deadline(
        &self,
        peer_id: PeerId,
        now: std::time::Instant,
    ) -> Option<std::time::Instant> {
        let (session_deadline, include_discovery_deadline) = self
            .active_sessions
            .get(&peer_id)
            .map(|entry| {
                let session = entry.value();
                let authoritative = session
                    .next_deadline
                    .as_ref()
                    .cloned()
                    .or_else(|| self.recompute_session_deadline(session, now));
                (
                    authoritative.map(|deadline| deadline.at.max(now)),
                    matches!(session.phase, TraversalPhase::Discovery),
                )
            })
            .unwrap_or((None, false));
        let discovery_deadline = include_discovery_deadline
            .then(|| {
                self.discovery_manager
                    .lock()
                    .next_poll_deadline_for_peer(peer_id, now)
            })
            .flatten();

        match (session_deadline, discovery_deadline) {
            (Some(left), Some(right)) => Some(left.min(right)),
            (Some(deadline), None) | (None, Some(deadline)) => Some(deadline),
            (None, None) => None,
        }
    }

    fn expected_session_rtt(&self, session: &NatTraversalSession) -> Duration {
        // Keep this aligned with TransportConfig::default().initial_rtt without
        // constructing a full transport config in the poll hot path.
        const DEFAULT_EXPECTED_SESSION_RTT: Duration = Duration::from_millis(333);

        session
            .session_state
            .metrics
            .rtt
            .unwrap_or(DEFAULT_EXPECTED_SESSION_RTT)
    }

    fn winning_remote_candidate(&self, session: &NatTraversalSession) -> Option<CandidateAddress> {
        let winning_addr = self
            .get_successful_candidate_address(session.peer_id)
            .or_else(|| {
                session
                    .candidates
                    .first()
                    .map(|candidate| candidate.address)
            })?;

        session
            .candidates
            .iter()
            .find(|candidate| {
                normalize_socket_addr(candidate.address) == normalize_socket_addr(winning_addr)
            })
            .cloned()
    }

    fn best_effort_winning_pair(
        &self,
        session: &NatTraversalSession,
        remote_candidate: &CandidateAddress,
    ) -> Option<CandidatePair> {
        session
            .candidates
            .iter()
            .filter(|candidate| {
                matches!(candidate.source, CandidateSource::Local)
                    && candidate.address.ip().is_ipv4() == remote_candidate.address.ip().is_ipv4()
            })
            .cloned()
            .map(|local_candidate| CandidatePair {
                priority: self
                    .calculate_candidate_pair_priority(&local_candidate, remote_candidate),
                local_candidate,
                remote_candidate: remote_candidate.clone(),
                state: CandidatePairState::Succeeded,
            })
            .max_by_key(|pair| pair.priority)
    }

    fn connectivity_phase_budget(&self, session: &NatTraversalSession) -> Duration {
        const MIN_CONNECTIVITY_PHASE_BUDGET: Duration = Duration::from_secs(1);
        const CONNECTIVITY_PHASE_RTT_MULTIPLIER: u32 = 6;
        // Keep a modest fixed cushion for punch / validation bookkeeping without
        // drifting back toward the old multi-second transition gates.
        const CONNECTIVITY_PHASE_BUDGET_SLACK: Duration = Duration::from_millis(500);

        let scaled_rtt = self
            .expected_session_rtt(session)
            .checked_mul(CONNECTIVITY_PHASE_RTT_MULTIPLIER)
            .unwrap_or(Duration::MAX)
            .saturating_add(CONNECTIVITY_PHASE_BUDGET_SLACK);
        // Enforce a 1s safety floor even if the configured establishment timeout
        // is tighter; NAT punch/validation needs at least one real scheduling
        // window to observe I/O progress before declaring absence.
        let upper = self
            .timeout_config
            .nat_traversal
            .connection_establishment_timeout
            .max(MIN_CONNECTIVITY_PHASE_BUDGET);

        scaled_rtt.max(MIN_CONNECTIVITY_PHASE_BUDGET).min(upper)
    }

    fn resolve_path_validation_rtt(
        connection_rtt: Option<Duration>,
        session_rtt: Option<Duration>,
        expected_rtt: Duration,
    ) -> Duration {
        connection_rtt
            .filter(|rtt| !rtt.is_zero())
            .or(session_rtt)
            .unwrap_or(expected_rtt)
    }

    fn path_validation_rtt(&self, session: &NatTraversalSession) -> Duration {
        Self::resolve_path_validation_rtt(
            self.connections
                .get(&session.peer_id)
                .map(|entry| entry.value().stats().path.rtt),
            session.session_state.metrics.rtt,
            self.expected_session_rtt(session),
        )
    }

    fn recompute_session_deadline(
        &self,
        session: &NatTraversalSession,
        now: std::time::Instant,
    ) -> Option<SessionDeadline> {
        if let Some(retry_at) = session.retry_at {
            return Some(SessionDeadline {
                kind: TraversalDeadlineKind::RetryBackoff,
                at: retry_at.max(now),
            });
        }

        match session.phase {
            TraversalPhase::Discovery => self
                .discovery_manager
                .lock()
                .phase_timeout_deadline_for_peer(session.peer_id)
                .map(|at| SessionDeadline {
                    kind: TraversalDeadlineKind::DiscoveryProgress,
                    at,
                }),
            TraversalPhase::Coordination => Some(SessionDeadline {
                kind: TraversalDeadlineKind::CoordinationResponse,
                at: session.last_progress_at + self.config.coordination_timeout,
            }),
            TraversalPhase::Synchronization => Some(SessionDeadline {
                kind: TraversalDeadlineKind::SynchronizationProgress,
                at: session.last_progress_at + self.config.coordination_timeout,
            }),
            TraversalPhase::Punching => Some(SessionDeadline {
                kind: TraversalDeadlineKind::PunchProgress,
                at: session.last_progress_at + self.connectivity_phase_budget(session),
            }),
            TraversalPhase::Validation => Some(SessionDeadline {
                kind: TraversalDeadlineKind::ValidationProgress,
                at: session.last_progress_at + self.connectivity_phase_budget(session),
            }),
            TraversalPhase::Connected | TraversalPhase::Failed => None,
        }
    }

    /// Calculate exponential backoff duration for retries
    fn calculate_backoff(&self, attempt: u32) -> Duration {
        let base = Duration::from_millis(1000);
        let max = Duration::from_secs(30);
        let backoff = base * 2u32.pow(attempt.saturating_sub(1));
        let jitter = std::time::Duration::from_millis((rand::random::<u64>() % 200) as u64);
        backoff.min(max) + jitter
    }

    /// Session-level retry policy for the current traversal negotiation.
    ///
    /// This answers whether the active NAT traversal session should back off and
    /// retry internally. It is intentionally distinct from
    /// `P2pEndpoint::should_retry_hole_punch_reason`, which decides whether to
    /// start a fresh outer hole-punch session after this one terminates.
    fn retry_disposition(
        &self,
        reason: &TraversalFailureReason,
        attempt: u32,
        now: std::time::Instant,
    ) -> RetryDisposition {
        if attempt >= self.config.max_concurrent_attempts as u32 {
            return RetryDisposition::Never;
        }

        match reason {
            TraversalFailureReason::ShuttingDown
            | TraversalFailureReason::ProtocolViolation(_)
            | TraversalFailureReason::CoordinationRejected { .. } => RetryDisposition::Never,
            TraversalFailureReason::CoordinatorUnavailable
            | TraversalFailureReason::DiscoveryExhausted
            | TraversalFailureReason::CoordinationExpired
            | TraversalFailureReason::SynchronizationExpired
            | TraversalFailureReason::PunchWindowMissed
            | TraversalFailureReason::ValidationTimedOut
            | TraversalFailureReason::ValidationFailed
            | TraversalFailureReason::ConnectionFailed
            | TraversalFailureReason::NetworkError(_) => {
                RetryDisposition::After(now + self.calculate_backoff(attempt + 1))
            }
        }
    }

    /// Handle phase failure with retry logic
    fn handle_phase_failure(
        &self,
        session: &mut NatTraversalSession,
        now: std::time::Instant,
        events: &mut Vec<NatTraversalEvent>,
        error: NatTraversalError,
    ) {
        let reason = TraversalFailureReason::from_session_error(
            &error,
            session.phase,
            self.shutdown.load(Ordering::Relaxed),
        );
        let deadline_kind = session
            .next_deadline
            .as_ref()
            .map(|deadline| deadline.kind)
            .or({
                match session.phase {
                    TraversalPhase::Coordination => {
                        Some(TraversalDeadlineKind::CoordinationResponse)
                    }
                    TraversalPhase::Synchronization => {
                        Some(TraversalDeadlineKind::SynchronizationProgress)
                    }
                    TraversalPhase::Punching => Some(TraversalDeadlineKind::PunchProgress),
                    TraversalPhase::Validation => Some(TraversalDeadlineKind::ValidationProgress),
                    TraversalPhase::Discovery => Some(TraversalDeadlineKind::DiscoveryProgress),
                    TraversalPhase::Connected | TraversalPhase::Failed => None,
                }
            });
        session.last_failure = Some(reason.clone());

        if matches!(
            reason,
            TraversalFailureReason::CoordinationExpired
                | TraversalFailureReason::SynchronizationExpired
                | TraversalFailureReason::PunchWindowMissed
                | TraversalFailureReason::ValidationTimedOut
                | TraversalFailureReason::DiscoveryExhausted
        ) && let Some(deadline_kind) = deadline_kind
        {
            self.emit_event(
                events,
                NatTraversalEvent::TraversalStalled {
                    peer_id: session.peer_id,
                    phase: session.phase,
                    deadline_kind,
                },
            );
        }

        match self.retry_disposition(&reason, session.attempt, now) {
            RetryDisposition::Never => {
                session.retry_at = None;
                session.next_deadline = None;
                session.phase = TraversalPhase::Failed;
                self.emit_event(
                    events,
                    NatTraversalEvent::TraversalTerminated {
                        peer_id: session.peer_id,
                        reason: reason.clone(),
                        fallback_available: true,
                    },
                );
                self.emit_event(
                    events,
                    NatTraversalEvent::TraversalFailed {
                        peer_id: session.peer_id,
                        error,
                        fallback_available: true,
                    },
                );
                error!(
                    "NAT traversal failed for peer {:?} after {} attempts",
                    session.peer_id, session.attempt
                );
            }
            RetryDisposition::After(retry_at) => {
                session.attempt += 1;
                session.phase_started_at = now;
                session.last_progress_at = now;
                session.retry_at = Some(retry_at);
                session.next_deadline = Some(SessionDeadline {
                    kind: TraversalDeadlineKind::RetryBackoff,
                    at: retry_at,
                });
                self.emit_event(
                    events,
                    NatTraversalEvent::RetryScheduled {
                        peer_id: session.peer_id,
                        attempt: session.attempt,
                        retry_at,
                        reason: reason.clone(),
                    },
                );
                warn!(
                    "Phase {:?} failed for peer {:?}: {:?}, retrying (attempt {}) at {:?}",
                    session.phase, session.peer_id, reason, session.attempt, retry_at
                );
            }
        }
    }

    /// Select a coordinator from available bootstrap nodes
    fn select_coordinator(&self) -> Option<SocketAddr> {
        // parking_lot::RwLock doesn't poison - always succeeds
        let nodes = self.bootstrap_nodes.read();
        // Simple round-robin or random selection
        if !nodes.is_empty() {
            let idx = rand::random::<usize>() % nodes.len();
            return Some(nodes[idx].address);
        }
        None
    }

    /// Send coordination request to bootstrap node
    ///
    /// This sends a PUNCH_ME_NOW frame with `target_peer_id` set to the coordinator,
    /// asking it to relay the coordination request to the target peer.
    fn send_coordination_request(
        &self,
        peer_id: PeerId,
        coordinator: SocketAddr,
    ) -> Result<(), NatTraversalError> {
        if self.event_tx.is_some() {
            return self.send_coordination_request_v2(peer_id, coordinator);
        }

        info!(
            "Sending authenticated coordination request for peer {} to coordinator {}",
            hex::encode(&peer_id.0[..8]),
            coordinator
        );

        let mut initiator_addrs = self
            .get_all_observed_external_addresses()?
            .into_iter()
            .map(normalize_socket_addr)
            .collect::<Vec<_>>();

        if initiator_addrs.is_empty()
            && let Some(addr) = self.get_observed_external_address()?
        {
            initiator_addrs.push(normalize_socket_addr(addr));
        }

        if initiator_addrs.is_empty()
            && let Some(endpoint) = &self.inner_endpoint
        {
            if let Ok(addr) = endpoint.local_addr() {
                initiator_addrs.push(normalize_socket_addr(addr));
            }
        }

        initiator_addrs.sort_unstable();
        initiator_addrs.dedup();

        if initiator_addrs.is_empty() {
            return Err(NatTraversalError::ConfigError(
                "No initiator address available for coordination".to_string(),
            ));
        }

        let request_id = next_request_id();
        let round = 1u32;
        let expires_at_unix_ms = self.coordinator_request_expires_at_unix_ms(peer_id, coordinator);
        let local_peer_id = self.local_peer_id();
        let _envelope = CoordinatorControlEnvelope {
            request_id,
            expires_at_unix_ms,
            message: CoordinatorControlMessage::CoordinationRequest {
                initiator: local_peer_id,
                target: peer_id,
                round,
                initiator_addrs: initiator_addrs.clone(),
            },
        };

        info!(
            "Using authenticated coordination envelope for target peer {} via normalized coordinator {}",
            hex::encode(&peer_id.0[..8]),
            normalize_socket_addr(coordinator)
        );

        let our_external_address = initiator_addrs[0];

        // Find the connection to the coordinator
        // DashMap provides lock-free mutable iteration
        // Normalize addresses to handle IPv4-mapped IPv6 (e.g., [::ffff:1.2.3.4]:9000 == 1.2.3.4:9000)
        let normalized_coordinator = normalize_socket_addr(coordinator);
        for mut entry in self.connections.iter_mut() {
            let conn = entry.value_mut();
            let normalized_remote = normalize_socket_addr(conn.remote_address());
            if normalized_remote == normalized_coordinator {
                // Found connection to coordinator - send PUNCH_ME_NOW with target_peer_id
                info!(
                    "Sending PUNCH_ME_NOW via coordinator {} (normalized: {}) to target peer {}",
                    coordinator,
                    normalized_coordinator,
                    hex::encode(&peer_id.0[..8])
                );

                // Use round 1 for initial coordination
                match conn.send_nat_punch_via_relay(peer_id.0, our_external_address, 1) {
                    Ok(()) => {
                        info!(
                            "Successfully queued PUNCH_ME_NOW for relay to peer {}",
                            hex::encode(&peer_id.0[..8])
                        );
                        return Ok(());
                    }
                    Err(e) => {
                        warn!("Failed to queue PUNCH_ME_NOW frame: {:?}", e);
                        return Err(NatTraversalError::CoordinationFailed(format!(
                            "Failed to send PUNCH_ME_NOW: {:?}",
                            e
                        )));
                    }
                }
            }
        }

        // If no existing connection, try to establish one
        info!(
            "No existing connection to coordinator {}, establishing...",
            coordinator
        );
        if let Some(endpoint) = &self.inner_endpoint {
            // Use "localhost" as server name - actual authentication is via PQC raw public keys
            let server_name = "localhost".to_string();
            match endpoint.connect(coordinator, &server_name) {
                Ok(connecting) => {
                    // For sync context, we spawn async task to complete connection and send
                    info!("Initiated connection to coordinator {}", coordinator);

                    // Spawn task to handle connection and send coordination
                    let connections = self.connections.clone();
                    let connection_lifecycle = self.connection_lifecycle.clone();
                    let next_connection_generation = self.next_connection_generation.clone();
                    let emitted_established_events = self.emitted_established_events.clone();
                    let low_level_endpoint = endpoint.clone();
                    let target_peer_id = peer_id;
                    let external_addr = our_external_address;
                    let connect_timeout = Self::coordination_connect_timeout(&self.config);

                    tokio::spawn(async move {
                        // Bound coordinator dial by the configured coordination budget.
                        match timeout(connect_timeout, connecting).await {
                            Ok(Ok(connection)) => {
                                info!("Connected to coordinator {}", coordinator);

                                let (coordinator_peer_id, coordinator_connection) =
                                    match Self::materialize_authenticated_connection(
                                        local_peer_id,
                                        connections.as_ref(),
                                        connection_lifecycle.as_ref(),
                                        next_connection_generation.as_ref(),
                                        emitted_established_events.as_ref(),
                                        connection,
                                    ) {
                                        Ok(result) => result,
                                        Err(error) => {
                                            warn!(
                                                "Failed to register coordinator {} with authenticated identity: {}",
                                                coordinator, error
                                            );
                                            return;
                                        }
                                    };
                                low_level_endpoint
                                    .register_connection_peer_id(coordinator, coordinator_peer_id);

                                // Now send the PUNCH_ME_NOW via this new connection
                                match coordinator_connection.send_nat_punch_via_relay(
                                    target_peer_id.0,
                                    external_addr,
                                    1,
                                ) {
                                    Ok(()) => {
                                        info!(
                                            "Sent PUNCH_ME_NOW to coordinator {} for peer {}",
                                            coordinator,
                                            hex::encode(&target_peer_id.0[..8])
                                        );
                                    }
                                    Err(e) => {
                                        warn!(
                                            "Failed to send PUNCH_ME_NOW after connecting: {:?}",
                                            e
                                        );
                                    }
                                }
                            }
                            Ok(Err(e)) => {
                                warn!("Failed to connect to coordinator {}: {}", coordinator, e);
                            }
                            Err(_) => {
                                warn!(
                                    "Connection to coordinator {} timed out after {:?}",
                                    coordinator, connect_timeout
                                );
                            }
                        }
                    });

                    // Return success to allow traversal to continue
                    // The actual coordination will happen once connected
                    Ok(())
                }
                Err(e) => Err(NatTraversalError::CoordinationFailed(format!(
                    "Failed to connect to coordinator {coordinator}: {e}"
                ))),
            }
        } else {
            Err(NatTraversalError::ConfigError(
                "QUIC endpoint not initialized".to_string(),
            ))
        }
    }

    /// Check if the current session has enough state to proceed to hole punching.
    fn session_is_synchronized(session: &NatTraversalSession) -> bool {
        let has_candidates = !session.candidates.is_empty();
        let past_discovery = session.phase as u8 > TraversalPhase::Discovery as u8;

        debug!(
            "Checking sync for peer {:?}: phase={:?}, candidates={}, past_discovery={}",
            session.peer_id,
            session.phase,
            session.candidates.len(),
            past_discovery
        );

        if has_candidates && past_discovery {
            info!(
                "Peer {:?} is synchronized with {} candidates",
                session.peer_id,
                session.candidates.len()
            );
            return true;
        }

        if session.phase == TraversalPhase::Synchronization && has_candidates {
            info!(
                "Peer {:?} in synchronization phase with {} candidates, considering synchronized",
                session.peer_id,
                session.candidates.len()
            );
            return true;
        }

        warn!("Peer {:?} is not synchronized", session.peer_id);
        false
    }

    /// Initiate hole punching to candidate addresses
    fn initiate_hole_punching(
        &self,
        peer_id: PeerId,
        candidates: &[CandidateAddress],
    ) -> Result<(), NatTraversalError> {
        if candidates.is_empty() {
            return Err(NatTraversalError::NoCandidatesFound);
        }

        // Check if connection already exists - no hole punching needed
        if self.has_existing_connection(&peer_id) {
            info!(
                "Connection already exists for peer {:?}, skipping hole punching",
                peer_id
            );
            return Ok(());
        }

        info!(
            "Initiating hole punching for peer {:?} to {} candidates",
            peer_id,
            candidates.len()
        );

        {
            // Attempt to connect to each candidate address
            for candidate in candidates {
                debug!(
                    "Attempting QUIC connection to candidate: {}",
                    candidate.address
                );

                // Use the attempt_connection_to_candidate method which handles the actual connection
                match self.attempt_connection_to_candidate(peer_id, candidate) {
                    Ok(_) => {
                        info!(
                            "Successfully initiated connection attempt to {}",
                            candidate.address
                        );
                    }
                    Err(e) => {
                        warn!(
                            "Failed to initiate connection to {}: {:?}",
                            candidate.address, e
                        );
                    }
                }
            }

            Ok(())
        }
    }

    /// Check if any hole punch succeeded
    fn check_punch_results_for_session(&self, session: &NatTraversalSession) -> Option<SocketAddr> {
        let peer_id = session.peer_id;

        // Check if we have an established connection to this peer.
        if let Some(entry) = self.connections.get(&peer_id) {
            // We have a connection! Return its address
            let addr = entry.value().remote_address();
            info!(
                "Found successful connection to peer {:?} at {}",
                peer_id, addr
            );
            return Some(addr);
        }

        if let Some(addr) = self
            .get_successful_candidate_address(peer_id)
            .filter(|addr| {
                session.candidates.iter().any(|candidate| {
                    normalize_socket_addr(candidate.address) == normalize_socket_addr(*addr)
                })
            })
        {
            info!(
                "Found successful hole punch candidate for peer {:?} at {}",
                peer_id, addr
            );
            return Some(addr);
        }

        // No connection found, check if we have any validated candidates.
        for candidate in &session.candidates {
            if matches!(candidate.state, CandidateState::Valid) {
                info!(
                    "Found validated candidate for peer {:?} at {}",
                    peer_id, candidate.address
                );
                return Some(candidate.address);
            }
        }

        debug!("No successful punch results for peer {:?}", peer_id);
        None
    }

    /// Validate a punched path
    fn validate_path(&self, peer_id: PeerId, address: SocketAddr) -> Result<(), NatTraversalError> {
        debug!("Validating path to peer {:?} at {}", peer_id, address);

        // Check if we have a connection to validate
        // DashMap provides lock-free .get()
        if let Some(entry) = self.connections.get(&peer_id) {
            let conn = entry.value();
            // Connection exists, check if it's to the expected address
            if normalize_socket_addr(conn.remote_address()) == normalize_socket_addr(address) {
                info!(
                    "Path validation successful for peer {:?} at {}",
                    peer_id, address
                );

                // Update candidate state to valid
                // DashMap provides lock-free .get_mut() that returns Option<RefMut<K, V>>
                if let Some(mut session) = self.active_sessions.get_mut(&peer_id) {
                    for candidate in &mut session.candidates {
                        if normalize_socket_addr(candidate.address)
                            == normalize_socket_addr(address)
                        {
                            candidate.state = CandidateState::Valid;
                            break;
                        }
                    }
                }

                return Ok(());
            } else {
                warn!(
                    "Connection address mismatch: expected {}, got {}",
                    address,
                    conn.remote_address()
                );
            }
        }

        // No connection found, validation failed
        Err(NatTraversalError::ValidationFailed(format!(
            "No connection found for peer {peer_id:?} at {address}"
        )))
    }

    /// Check if a connection already exists for the given peer.
    ///
    /// This is used to skip unnecessary NAT traversal when a direct connection
    /// has already been established. Checking this at multiple points prevents
    /// wasted resources on hole punching attempts.
    #[inline]
    fn has_existing_connection(&self, peer_id: &PeerId) -> bool {
        self.connections.contains_key(peer_id)
    }

    /// Check if path validation succeeded for the in-hand session state.
    fn session_path_is_validated(&self, session: &NatTraversalSession) -> bool {
        debug!("Checking path validation for peer {:?}", session.peer_id);

        if self.has_existing_connection(&session.peer_id) {
            info!(
                "Path validated: connection exists for peer {:?}",
                session.peer_id
            );
            return true;
        }

        let validated = session
            .candidates
            .iter()
            .any(|c| matches!(c.state, CandidateState::Valid));

        if validated {
            info!(
                "Path validated: found validated candidate for peer {:?}",
                session.peer_id
            );
            return true;
        }

        warn!("Path not validated for peer {:?}", session.peer_id);
        false
    }

    /// Check if connection is healthy
    fn is_connection_healthy(&self, peer_id: &PeerId) -> bool {
        // In real implementation, check QUIC connection status
        // DashMap provides lock-free .get()
        if self.connections.get(peer_id).is_some() {
            // Check if connection is still active
            // Note: Connection doesn't have is_closed/is_drained methods
            // We use the closed() future to check if still active
            return true; // Assume healthy if connection exists in map
        }
        true
    }

    fn discovery_retry_backoff_active(&self, peer_id: PeerId, _now: std::time::Instant) -> bool {
        self.active_sessions.get(&peer_id).is_some_and(|entry| {
            let session = entry.value();
            matches!(session.phase, TraversalPhase::Discovery)
                && matches!(
                    session.next_deadline.as_ref(),
                    Some(SessionDeadline {
                        kind: TraversalDeadlineKind::RetryBackoff,
                        ..
                    })
                )
                && session.retry_at.is_some()
        })
    }

    /// Convert discovery events to NAT traversal events with proper peer ID resolution
    fn convert_discovery_event(
        &self,
        discovery_event: DiscoveryEvent,
    ) -> Option<NatTraversalEvent> {
        // Get the current active peer ID from sessions
        let current_peer_id = self.get_current_discovery_peer_id();

        match discovery_event {
            DiscoveryEvent::LocalCandidateDiscovered { candidate } => {
                Some(NatTraversalEvent::CandidateDiscovered {
                    peer_id: current_peer_id,
                    candidate,
                })
            }
            DiscoveryEvent::ServerReflexiveCandidateDiscovered {
                candidate,
                bootstrap_node: _,
            } => Some(NatTraversalEvent::CandidateDiscovered {
                peer_id: current_peer_id,
                candidate,
            }),
            // Prediction events removed in minimal flow
            DiscoveryEvent::DiscoveryCompleted {
                candidate_count: _,
                total_duration: _,
                success_rate: _,
            } => {
                // This could trigger the coordination phase
                None // For now, don't emit specific event
            }
            DiscoveryEvent::DiscoveryFailed {
                error,
                partial_results,
            } => Some(NatTraversalEvent::TraversalFailed {
                peer_id: current_peer_id,
                error: NatTraversalError::CandidateDiscoveryFailed(error.to_string()),
                fallback_available: !partial_results.is_empty(),
            }),
            _ => None, // Other events don't need to be converted
        }
    }

    /// Get the peer ID for the current discovery session
    fn get_current_discovery_peer_id(&self) -> PeerId {
        // Try to get the peer ID from the most recent active session
        // DashMap provides lock-free iteration with .iter()
        if let Some(entry) = self
            .active_sessions
            .iter()
            .find(|entry| matches!(entry.value().phase, TraversalPhase::Discovery))
        {
            return *entry.key();
        }

        // If no discovery phase session is active, discovery events describe our
        // local address set rather than a remote peer candidate set.
        self.local_peer_id
    }

    /// Handle endpoint events from connection-level NAT traversal state machine
    #[allow(dead_code)]
    pub(crate) async fn handle_endpoint_event(
        &self,
        event: crate::shared::EndpointEventInner,
    ) -> Result<(), NatTraversalError> {
        match event {
            crate::shared::EndpointEventInner::NatCandidateValidated { address, challenge } => {
                info!(
                    "NAT candidate validation succeeded for {} with challenge {:016x}",
                    address, challenge
                );

                // Find and update the active session with validated candidate
                // Use read-only scan + targeted get_mut to avoid iter_mut() + callback re-entrancy
                let matching_peer_id = self
                    .active_sessions
                    .iter()
                    .find(|entry| {
                        entry.value().candidates.iter().any(|c| {
                            normalize_socket_addr(c.address) == normalize_socket_addr(address)
                        })
                    })
                    .map(|entry| *entry.key());

                if let Some(peer_id) = matching_peer_id {
                    if let Some(mut entry) = self.active_sessions.get_mut(&peer_id) {
                        let session = entry.value_mut();
                        if let Some(candidate) = session.candidates.iter_mut().find(|candidate| {
                            normalize_socket_addr(candidate.address)
                                == normalize_socket_addr(address)
                        }) {
                            candidate.state = CandidateState::Validating;
                        }
                        Self::set_session_phase(
                            session,
                            std::time::Instant::now(),
                            TraversalPhase::Validation,
                        );
                    }
                    // Callback outside DashMap guard to prevent re-entrancy deadlock
                    if let Some(ref callback) = self.event_callback {
                        callback(NatTraversalEvent::CandidateValidated {
                            peer_id,
                            candidate_address: address,
                        });
                    }
                }

                // Attempt to establish connection using this validated candidate (after releasing DashMap ref)
                if let Some(peer_id) = matching_peer_id {
                    return self
                        .establish_connection_to_validated_candidate(peer_id, address)
                        .await;
                }

                debug!(
                    "Validated candidate {} not found in active sessions",
                    address
                );
                Ok(())
            }

            crate::shared::EndpointEventInner::RelayPunchMeNow(target_peer_id, punch_frame) => {
                // RFC-compliant address-based relay: find peer by address, not synthetic peer ID
                let target_address = punch_frame.address;
                let normalized_target = normalize_socket_addr(target_address);

                info!(
                    "Relaying PUNCH_ME_NOW to address {} (normalized: {}) or peer {:?}",
                    target_address,
                    normalized_target,
                    hex::encode(&target_peer_id[..8])
                );

                // DashMap provides lock-free access
                // First try peer ID lookup (if we have actual peer ID)
                let target_peer = PeerId(target_peer_id);
                let connection_found = if let Some(entry) = self.connections.get(&target_peer) {
                    Some((target_peer, entry.value().clone()))
                } else {
                    // RFC approach: find connection by address match
                    // Check both remote_address and observed_address for the target
                    self.connections.iter().find_map(|entry| {
                        let peer_id = *entry.key();
                        let conn = entry.value();
                        let remote_normalized = normalize_socket_addr(conn.remote_address());
                        let observed_normalized = conn.observed_address().map(normalize_socket_addr);

                        // Match on IP (port may differ due to NAT)
                        let remote_ip_match = remote_normalized.ip() == normalized_target.ip();
                        let observed_ip_match = observed_normalized
                            .map(|obs| obs.ip() == normalized_target.ip())
                            .unwrap_or(false);

                        if remote_ip_match || observed_ip_match {
                            debug!(
                                "Found peer {} by address match: remote={}, observed={:?}, target={}",
                                hex::encode(&peer_id.0[..8]),
                                remote_normalized,
                                observed_normalized,
                                normalized_target
                            );
                            Some((peer_id, conn.clone()))
                        } else {
                            None
                        }
                    })
                };

                if let Some((_peer_id, connection)) = connection_found {
                    // Send the PUNCH_ME_NOW frame via a unidirectional stream
                    let mut send_stream = connection.open_uni().await.map_err(|e| {
                        NatTraversalError::NetworkError(format!("Failed to open stream: {e}"))
                    })?;

                    // Encode the frame data
                    let mut frame_data = Vec::new();
                    punch_frame.encode(&mut frame_data);

                    send_stream.write_all(&frame_data).await.map_err(|e| {
                        NatTraversalError::NetworkError(format!("Failed to send frame: {e}"))
                    })?;

                    let _ = send_stream.finish();

                    info!(
                        "Successfully relayed PUNCH_ME_NOW frame to address {}",
                        normalized_target
                    );
                    Ok(())
                } else {
                    warn!(
                        "No connection found for target address {} (checked {} connections)",
                        normalized_target,
                        self.connections.len()
                    );
                    Err(NatTraversalError::PeerNotConnected)
                }
            }

            crate::shared::EndpointEventInner::SendAddressFrame(add_address_frame) => {
                info!(
                    "Sending AddAddress frame for address {}",
                    add_address_frame.address
                );

                // Find all active connections and send the AddAddress frame
                // DashMap: collect connections to avoid holding ref during async operations
                let connections_snapshot: Vec<_> = self
                    .connections
                    .iter()
                    .map(|entry| (*entry.key(), entry.value().clone()))
                    .collect();

                for (peer_id, connection) in connections_snapshot {
                    // Send AddAddress frame via unidirectional stream
                    let mut send_stream = connection.open_uni().await.map_err(|e| {
                        NatTraversalError::NetworkError(format!("Failed to open stream: {e}"))
                    })?;

                    // Encode the frame data
                    let mut frame_data = Vec::new();
                    add_address_frame.encode(&mut frame_data);

                    send_stream.write_all(&frame_data).await.map_err(|e| {
                        NatTraversalError::NetworkError(format!("Failed to send frame: {e}"))
                    })?;

                    let _ = send_stream.finish();

                    debug!("Sent AddAddress frame to peer {:?}", peer_id);
                }

                Ok(())
            }

            _ => {
                // Other endpoint events not related to NAT traversal
                debug!("Ignoring non-NAT traversal endpoint event: {:?}", event);
                Ok(())
            }
        }
    }

    /// Establish connection to a validated candidate address
    #[allow(dead_code)]
    async fn establish_connection_to_validated_candidate(
        &self,
        peer_id: PeerId,
        candidate_address: SocketAddr,
    ) -> Result<(), NatTraversalError> {
        let endpoint = self.inner_endpoint.as_ref().cloned().ok_or_else(|| {
            NatTraversalError::ConfigError("QUIC endpoint not initialized".to_string())
        })?;

        Self::establish_connection_to_validated_candidate_inner(
            endpoint,
            Arc::clone(&self.active_sessions),
            Arc::clone(&self.connections),
            Arc::clone(&self.emitted_established_events),
            self.observed_address_tx.clone(),
            Arc::clone(&self.traversal_event_notify),
            Arc::clone(&self.incoming_notify),
            self.event_tx.clone(),
            self.event_callback.clone(),
            self.timeout_config
                .nat_traversal
                .connection_establishment_timeout,
            peer_id,
            candidate_address,
        )
        .await
    }

    async fn establish_connection_to_validated_candidate_inner(
        endpoint: InnerEndpoint,
        active_sessions: Arc<dashmap::DashMap<PeerId, NatTraversalSession>>,
        connections: Arc<dashmap::DashMap<PeerId, InnerConnection>>,
        emitted_established_events: Arc<dashmap::DashSet<PeerId>>,
        observed_address_tx: mpsc::UnboundedSender<ObservedAddressReport>,
        traversal_event_notify: Arc<tokio::sync::Notify>,
        incoming_notify: Arc<tokio::sync::Notify>,
        event_tx: Option<mpsc::UnboundedSender<NatTraversalEvent>>,
        event_callback: Option<Arc<dyn Fn(NatTraversalEvent) + Send + Sync>>,
        connection_timeout: Duration,
        peer_id: PeerId,
        candidate_address: SocketAddr,
    ) -> Result<(), NatTraversalError> {
        info!(
            "Establishing connection to validated candidate {} for peer {:?}",
            candidate_address, peer_id
        );

        // Attempt connection to the validated address
        let connecting = endpoint
            .connect(candidate_address, "nat-traversal-peer")
            .map_err(|e| {
                NatTraversalError::ConnectionFailed(format!("Failed to initiate connection: {e}"))
            })?;

        let connection = timeout(connection_timeout, connecting)
            .await
            .map_err(|_| NatTraversalError::Timeout)?
            .map_err(|e| NatTraversalError::ConnectionFailed(format!("Connection failed: {e}")))?;

        // CRITICAL: Lock ordering fix for deadlock prevention
        // Always access active_sessions BEFORE connections to prevent A-B vs B-A deadlock.
        // Pattern in poll(): active_sessions.iter_mut() -> connections access
        // Pattern here must match: active_sessions access -> connections.insert()
        //
        // Step 1: Update session state first (acquires active_sessions lock)
        if let Some(mut session) = active_sessions.get_mut(&peer_id) {
            Self::set_session_phase(
                session.value_mut(),
                std::time::Instant::now(),
                TraversalPhase::Connected,
            );
        }
        // Step 2: Drop the active_sessions ref before accessing connections
        // (ref is dropped when session goes out of scope at end of if block)

        // Step 3: Now safe to insert into connections
        connections.insert(peer_id, connection.clone());
        if let Some(mut entry) = active_sessions.get_mut(&peer_id) {
            entry.value_mut().session_state.connection = Some(connection.clone());
        }

        let event_tx = event_tx.ok_or_else(|| {
            NatTraversalError::ConfigError("NAT traversal event channel not configured".to_string())
        })?;

        Self::spawn_observed_address_watch_task_parts(
            observed_address_tx.clone(),
            peer_id,
            connection.clone(),
        );

        let should_emit = emitted_established_events.insert(peer_id);
        if should_emit {
            let event = NatTraversalEvent::ConnectionEstablished {
                peer_id,
                remote_address: candidate_address,
                side: Side::Client,
            };
            if let Err(e) = event_tx.send(event.clone()) {
                tracing::warn!(
                    target: "ant_quic::silent_drop",
                    kind = "event_tx_emit_event_after_relay",
                    peer_id = ?peer_id,
                    error = %e,
                    "silent drop"
                );
            }
            if let Some(callback) = event_callback {
                callback(event);
            }
            incoming_notify.notify_waiters();
            traversal_event_notify.notify_waiters();
        }

        tokio::spawn(async move {
            Self::handle_connection(peer_id, connection, event_tx, traversal_event_notify).await;
        });

        info!(
            "Successfully established connection to peer {:?} at {}",
            peer_id, candidate_address
        );
        Ok(())
    }

    /// Send ADD_ADDRESS frame to advertise a candidate to a peer
    ///
    /// This is the bridge between candidate discovery and actual frame transmission.
    /// It finds the connection to the peer and sends an ADD_ADDRESS frame using
    /// the QUIC extension frame API.
    async fn send_candidate_advertisement(
        &self,
        peer_id: PeerId,
        candidate: &CandidateAddress,
    ) -> Result<(), NatTraversalError> {
        debug!(
            "Sending candidate advertisement to peer {:?}: {}",
            peer_id, candidate.address
        );

        // DashMap provides lock-free .get_mut()
        if let Some(mut entry) = self.connections.get_mut(&peer_id) {
            let conn = entry.value_mut();
            // Use the connection's API to enqueue a proper NAT traversal frame
            match conn.send_nat_address_advertisement(candidate.address, candidate.priority) {
                Ok(seq) => {
                    info!(
                        "Queued ADD_ADDRESS via connection API: peer={:?}, addr={}, priority={}, seq={}",
                        peer_id, candidate.address, candidate.priority, seq
                    );
                    Ok(())
                }
                Err(e) => Err(NatTraversalError::ProtocolError(format!(
                    "Failed to queue ADD_ADDRESS: {e:?}"
                ))),
            }
        } else {
            debug!("No active connection for peer {:?}", peer_id);
            Ok(())
        }
    }

    /// Send PUNCH_ME_NOW frame to coordinate hole punching
    ///
    /// This method sends hole punching coordination frames using the real
    /// QUIC extension frame API instead of application-level streams.
    #[allow(dead_code)]
    async fn send_punch_coordination(
        &self,
        peer_id: PeerId,
        paired_with_sequence_number: u64,
        address: SocketAddr,
        round: u32,
    ) -> Result<(), NatTraversalError> {
        debug!(
            "Sending punch coordination to peer {:?}: seq={}, addr={}, round={}",
            peer_id, paired_with_sequence_number, address, round
        );

        // DashMap provides lock-free .get_mut()
        if let Some(mut entry) = self.connections.get_mut(&peer_id) {
            entry
                .value_mut()
                .send_nat_punch_coordination(paired_with_sequence_number, address, round)
                .map_err(|e| {
                    NatTraversalError::ProtocolError(format!("Failed to queue PUNCH_ME_NOW: {e:?}"))
                })
        } else {
            Err(NatTraversalError::PeerNotConnected)
        }
    }

    /// Get NAT traversal statistics
    #[allow(clippy::panic)]
    pub fn get_nat_stats(
        &self,
    ) -> Result<NatTraversalStatistics, Box<dyn std::error::Error + Send + Sync>> {
        // Return default statistics for now
        // In a real implementation, this would collect actual stats from the endpoint
        Ok(NatTraversalStatistics {
            active_sessions: self.active_sessions.len(),
            // parking_lot::RwLock doesn't poison - always succeeds
            total_bootstrap_nodes: self.bootstrap_nodes.read().len(),
            successful_coordinations: 7,
            average_coordination_time: self.config.coordination_timeout,
            total_attempts: 10,
            successful_connections: 7,
            direct_connections: 5,
            relayed_connections: 2,
        })
    }
}

impl fmt::Debug for NatTraversalEndpoint {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.debug_struct("NatTraversalEndpoint")
            .field("config", &self.config)
            .field("bootstrap_nodes", &"<RwLock>")
            .field("active_sessions", &"<DashMap>")
            .field("event_callback", &self.event_callback.is_some())
            .finish()
    }
}

/// Statistics about NAT traversal performance
#[derive(Debug, Clone, Default)]
pub struct NatTraversalStatistics {
    /// Number of active NAT traversal sessions
    pub active_sessions: usize,
    /// Total number of known bootstrap nodes
    pub total_bootstrap_nodes: usize,
    /// Total successful coordinations
    pub successful_coordinations: u32,
    /// Average time for coordination
    pub average_coordination_time: Duration,
    /// Total NAT traversal attempts
    pub total_attempts: u32,
    /// Successful connections established
    pub successful_connections: u32,
    /// Direct connections established (no relay)
    pub direct_connections: u32,
    /// Relayed connections
    pub relayed_connections: u32,
}

impl fmt::Display for NatTraversalError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Self::NoBootstrapNodes => write!(f, "no bootstrap nodes available"),
            Self::NoCandidatesFound => write!(f, "no address candidates found"),
            Self::CandidateDiscoveryFailed(msg) => write!(f, "candidate discovery failed: {msg}"),
            Self::CoordinationFailed(msg) => write!(f, "coordination failed: {msg}"),
            Self::HolePunchingFailed => write!(f, "hole punching failed"),
            Self::PunchingFailed(msg) => write!(f, "punching failed: {msg}"),
            Self::ValidationFailed(msg) => write!(f, "validation failed: {msg}"),
            Self::ValidationTimeout => write!(f, "validation timeout"),
            Self::NetworkError(msg) => write!(f, "network error: {msg}"),
            Self::ConfigError(msg) => write!(f, "configuration error: {msg}"),
            Self::ProtocolError(msg) => write!(f, "protocol error: {msg}"),
            Self::Timeout => write!(f, "operation timed out"),
            Self::ConnectionFailed(msg) => write!(f, "connection failed: {msg}"),
            Self::TraversalFailed(msg) => write!(f, "traversal failed: {msg}"),
            Self::PeerNotConnected => write!(f, "peer not connected"),
        }
    }
}

impl std::error::Error for NatTraversalError {}

impl fmt::Display for PeerId {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Display first 8 bytes as hex (16 characters)
        for byte in &self.0[..8] {
            write!(f, "{byte:02x}")?;
        }
        Ok(())
    }
}

impl From<[u8; 32]> for PeerId {
    fn from(bytes: [u8; 32]) -> Self {
        Self(bytes)
    }
}

impl PeerId {
    /// Convert the peer ID to a hexadecimal string
    pub fn to_hex(&self) -> String {
        hex::encode(self.0)
    }
}

/// Dummy certificate verifier that accepts any certificate
/// WARNING: This is only for testing/demo purposes - use proper verification in production!
#[derive(Debug)]
#[allow(dead_code)]
struct SkipServerVerification;

impl SkipServerVerification {
    #[allow(dead_code)]
    fn new() -> Arc<Self> {
        Arc::new(Self)
    }
}

impl rustls::client::danger::ServerCertVerifier for SkipServerVerification {
    fn verify_server_cert(
        &self,
        _end_entity: &rustls::pki_types::CertificateDer<'_>,
        _intermediates: &[rustls::pki_types::CertificateDer<'_>],
        _server_name: &rustls::pki_types::ServerName<'_>,
        _ocsp_response: &[u8],
        _now: rustls::pki_types::UnixTime,
    ) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
        Ok(rustls::client::danger::ServerCertVerified::assertion())
    }

    fn verify_tls12_signature(
        &self,
        _message: &[u8],
        _cert: &rustls::pki_types::CertificateDer<'_>,
        _dss: &rustls::DigitallySignedStruct,
    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
        Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
    }

    fn verify_tls13_signature(
        &self,
        _message: &[u8],
        _cert: &rustls::pki_types::CertificateDer<'_>,
        _dss: &rustls::DigitallySignedStruct,
    ) -> Result<rustls::client::danger::HandshakeSignatureValid, rustls::Error> {
        Ok(rustls::client::danger::HandshakeSignatureValid::assertion())
    }

    fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
        // v0.2: Pure PQC - only ML-DSA-65 (IANA 0x0905)
        vec![rustls::SignatureScheme::ML_DSA_65]
    }
}

/// Default token store that accepts all tokens (for demo purposes)
#[allow(dead_code)]
struct DefaultTokenStore;

impl crate::TokenStore for DefaultTokenStore {
    fn insert(&self, _server_name: &str, _token: bytes::Bytes) {
        // Ignore token storage for demo
    }

    fn take(&self, _server_name: &str) -> Option<bytes::Bytes> {
        None
    }
}

#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
    use super::*;

    #[test]
    fn test_nat_traversal_config_default() {
        let config = NatTraversalConfig::default();
        // v0.13.0+: No role field - all nodes are symmetric P2P nodes
        assert!(config.known_peers.is_empty());
        assert_eq!(config.max_candidates, 8);
        assert!(config.enable_symmetric_nat);
        assert!(config.enable_relay_fallback);
    }

    #[test]
    fn test_lifecycle_connection_family_id_is_symmetric_for_same_initiator() {
        let a = PeerId([0x11; 32]);
        let b = PeerId([0x22; 32]);

        let a_initiated_from_a =
            NatTraversalEndpoint::lifecycle_connection_family_id_from_initiator(a, b, a);
        let a_initiated_from_b =
            NatTraversalEndpoint::lifecycle_connection_family_id_from_initiator(b, a, a);
        let b_initiated =
            NatTraversalEndpoint::lifecycle_connection_family_id_from_initiator(a, b, b);

        assert_eq!(a_initiated_from_a, a_initiated_from_b);
        assert_ne!(a_initiated_from_a, b_initiated);
    }

    #[test]
    fn test_canonical_sort_key_prefers_connection_id_across_simultaneous_open() {
        let a_family = [0x11; 32];
        let b_family = [0x22; 32];
        let a_connection_id = [0xAA; 32];
        let b_connection_id = [0xBB; 32];

        let a_key =
            NatTraversalEndpoint::canonical_sort_key(a_family, a_connection_id, 99, 9_999, 99);
        let b_key = NatTraversalEndpoint::canonical_sort_key(b_family, b_connection_id, 1, 1, 1);

        assert_eq!(
            NatTraversalEndpoint::canonical_sort_key_cmp(a_key, b_key),
            a_connection_id.cmp(&b_connection_id),
            "shared connection_id must dominate simultaneous-open races across families"
        );
    }

    #[test]
    fn test_canonical_sort_key_prefers_newer_generation_when_connection_family_matches() {
        let family_id = [0xAB; 32];
        let older = NatTraversalEndpoint::canonical_sort_key(family_id, [0x01; 32], 1, 200, 1);
        let newer = NatTraversalEndpoint::canonical_sort_key(family_id, [0x02; 32], 2, 100, 0);

        assert_eq!(
            NatTraversalEndpoint::canonical_sort_key_cmp(newer, older),
            std::cmp::Ordering::Greater
        );
    }

    #[test]
    fn test_canonical_sort_key_prefers_newer_established_at_when_generation_matches() {
        let family_id = [0xCD; 32];
        let older = NatTraversalEndpoint::canonical_sort_key(family_id, [0x10; 32], 7, 100, 1);
        let newer = NatTraversalEndpoint::canonical_sort_key(family_id, [0x11; 32], 7, 101, 0);

        assert_eq!(
            NatTraversalEndpoint::canonical_sort_key_cmp(newer, older),
            std::cmp::Ordering::Greater
        );
    }

    /// Regression for issue #163.
    ///
    /// A mixed list of Global + private/link-local/loopback candidates from
    /// `CoordinationAccepted` must be stripped down to Global only before the
    /// session fires hole-punch probes — otherwise we stall on RFC1918
    /// entries (like `10.x.y.z`) that cannot be reached from an off-LAN
    /// peer.
    #[test]
    fn test_drop_non_global_nat_candidates_filters_mixed_list() {
        let private_v4: SocketAddr = "10.200.0.1:5483".parse().unwrap();
        let global_v4: SocketAddr = "198.51.100.20:5483".parse().unwrap();
        let global_v6: SocketAddr = "[2001:db8::20]:5483".parse().unwrap();
        let link_local: SocketAddr = "169.254.10.1:5483".parse().unwrap();
        let loopback: SocketAddr = "127.0.0.1:5483".parse().unwrap();
        let ula_v6: SocketAddr = "[fc00::1]:5483".parse().unwrap();

        let mut addrs = vec![
            private_v4, link_local, loopback, ula_v6, global_v4, global_v6,
        ];
        drop_non_global_nat_candidates_when_global_present(&mut addrs);
        addrs.sort();
        let mut expected = vec![global_v4, global_v6];
        expected.sort();
        assert_eq!(
            addrs, expected,
            "private/link-local/loopback/ULA must be dropped when Global candidates are present"
        );
    }

    /// The filter is a no-op on a purely non-global list so that pure-LAN
    /// peers (e.g. mDNS-discovered `192.168.x` neighbours) remain dialable.
    #[test]
    fn test_drop_non_global_nat_candidates_preserves_lan_only() {
        let private_v4: SocketAddr = "192.168.1.25:5483".parse().unwrap();
        let link_local_v6: SocketAddr = "[fe80::1]:5483".parse().unwrap();

        let original = vec![private_v4, link_local_v6];
        let mut addrs = original.clone();
        drop_non_global_nat_candidates_when_global_present(&mut addrs);
        assert_eq!(
            addrs, original,
            "LAN-only candidate sets must not be emptied"
        );
    }

    #[test]
    fn test_nat_config_default_has_no_transport_registry() {
        let config = NatTraversalConfig::default();
        assert!(
            config.transport_registry.is_none(),
            "Default NatTraversalConfig should have no transport_registry"
        );
    }

    #[test]
    fn test_nat_config_can_set_transport_registry() {
        use crate::transport::TransportRegistry;

        let registry = Arc::new(TransportRegistry::new());
        let config = NatTraversalConfig {
            transport_registry: Some(Arc::clone(&registry)),
            ..Default::default()
        };

        assert!(config.transport_registry.is_some());
        let config_registry = config.transport_registry.unwrap();
        assert!(Arc::ptr_eq(&config_registry, &registry));
    }

    /// Test that TransportRegistry::get_udp_local_addr() returns None when empty
    #[test]
    fn test_registry_get_udp_local_addr_empty() {
        use crate::transport::TransportRegistry;

        let registry = TransportRegistry::new();
        assert!(
            registry.get_udp_local_addr().is_none(),
            "Empty registry should return None for UDP address"
        );
    }

    /// Test that TransportRegistry::get_udp_socket() returns None when empty
    #[test]
    fn test_registry_get_udp_socket_empty() {
        use crate::transport::TransportRegistry;

        let registry = TransportRegistry::new();
        assert!(
            registry.get_udp_socket().is_none(),
            "Empty registry should return None for UDP socket"
        );
    }

    /// Test that NatTraversalEndpoint stores and exposes transport_registry
    #[tokio::test]
    async fn test_endpoint_stores_transport_registry() {
        use crate::transport::TransportRegistry;

        // Create a registry
        let registry = Arc::new(TransportRegistry::new());

        // Create config with registry
        let config = NatTraversalConfig {
            transport_registry: Some(Arc::clone(&registry)),
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };

        // Create endpoint
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        // Verify registry is accessible
        let stored_registry = endpoint.transport_registry();
        assert!(
            stored_registry.is_some(),
            "Endpoint should have transport_registry"
        );
        assert!(
            Arc::ptr_eq(stored_registry.unwrap(), &registry),
            "Stored registry should be the same Arc as provided"
        );
    }

    /// Test endpoint creation without registry (backward compatibility)
    #[tokio::test]
    async fn test_endpoint_without_transport_registry() {
        let config = NatTraversalConfig {
            transport_registry: None,
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };

        // Create endpoint - should succeed without registry
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation without registry should succeed");

        // Verify registry is None
        assert!(
            endpoint.transport_registry().is_none(),
            "Endpoint without registry config should have None"
        );
    }

    #[tokio::test]
    async fn test_relay_server_public_address_can_be_refreshed() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };

        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let refreshed_addr: SocketAddr = "198.51.100.44:49000".parse().expect("valid addr");
        endpoint.reconcile_relay_server_public_addresses(Some(refreshed_addr));

        assert_eq!(endpoint.relay_server_public_address(), Some(refreshed_addr));
    }

    #[tokio::test]
    async fn test_symmetric_relay_server_helper_spawns_cleanup_task() {
        let relay_config = MasqueRelayConfig {
            cleanup_interval: Duration::from_millis(50),
            session_config: crate::masque::RelaySessionConfig {
                session_timeout: Duration::from_millis(10),
                ..Default::default()
            },
            max_sessions: 100,
            require_authentication: true,
            ..Default::default()
        };
        let server = NatTraversalEndpoint::create_symmetric_relay_server(
            "127.0.0.1:0".parse().expect("valid addr"),
            relay_config,
        );
        let request = ConnectUdpRequest::bind_any();
        let response = server
            .handle_connect_request(&request, "127.0.0.1:44001".parse().expect("valid addr"))
            .await
            .expect("connect request should succeed");

        assert_eq!(response.status, 200);
        assert_eq!(server.session_count().await, 1);

        tokio::time::sleep(Duration::from_millis(120)).await;

        assert_eq!(
            server.session_count().await,
            0,
            "production relay-server creation helper must wire the expiry reaper"
        );
    }

    #[test]
    fn test_best_relay_public_address_ignores_private_candidates() {
        let private_v4: SocketAddr = "10.0.0.1:5483".parse().expect("valid addr");
        let global_v4: SocketAddr = "198.51.100.44:5483".parse().expect("valid addr");
        let global_v6: SocketAddr = "[2001:db8::44]:5483".parse().expect("valid addr");

        assert_eq!(
            NatTraversalEndpoint::select_best_relay_public_address_for_family(
                [private_v4, global_v4],
                true,
            ),
            Some(global_v4)
        );
        assert_eq!(
            NatTraversalEndpoint::select_best_relay_public_address_for_family(
                [private_v4, global_v6],
                false,
            ),
            Some(global_v6)
        );
    }

    #[test]
    fn test_best_relay_public_address_prefers_known_peer_candidates() {
        let known_peer_addr: SocketAddr = "198.51.100.44:5483".parse().expect("valid addr");
        let arbitrary_peer_addr: SocketAddr = "198.51.100.99:5483".parse().expect("valid addr");

        assert_eq!(
            NatTraversalEndpoint::select_best_relay_public_address_with_known_preference(
                [known_peer_addr],
                [arbitrary_peer_addr],
                true,
            ),
            Some(known_peer_addr)
        );
    }

    #[test]
    fn test_relay_public_address_selection_is_stable_across_conflicting_observers() {
        let private_v4: SocketAddr = "10.0.0.1:5483".parse().expect("valid addr");
        let public_v4_a: SocketAddr = "198.51.100.80:5483".parse().expect("valid addr");
        let public_v4_b: SocketAddr = "198.51.100.44:5483".parse().expect("valid addr");
        let public_v6_a: SocketAddr = "[2001:db8::80]:5483".parse().expect("valid addr");
        let public_v6_b: SocketAddr = "[2001:db8::44]:5483".parse().expect("valid addr");

        assert_eq!(
            NatTraversalEndpoint::select_relay_server_public_addresses(&[
                public_v4_a,
                private_v4,
                public_v6_a,
                public_v4_b,
                public_v6_b,
            ]),
            (Some(public_v4_b), Some(public_v6_b))
        );
        assert_eq!(
            NatTraversalEndpoint::select_relay_server_public_addresses(&[
                public_v6_b,
                public_v4_b,
                public_v4_a,
                public_v6_a,
                private_v4,
            ]),
            (Some(public_v4_b), Some(public_v6_b))
        );
    }

    #[test]
    fn test_encode_relay_response_frame_prefixes_length() {
        let response =
            ConnectUdpResponse::success(Some("198.51.100.55:5483".parse().expect("valid addr")));
        let frame = encode_relay_response_frame(&response);
        let len = u32::from_be_bytes(frame[..4].try_into().expect("length prefix")) as usize;
        assert_eq!(len, frame.len() - 4);

        let decoded = ConnectUdpResponse::decode(&mut bytes::Bytes::from(frame[4..].to_vec()))
            .expect("framed response should decode");
        assert_eq!(decoded.status, response.status);
        assert_eq!(decoded.proxy_public_address, response.proxy_public_address);
    }

    #[test]
    fn test_peer_id_display() {
        let peer_id = PeerId([
            0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
            0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11, 0x22, 0x33,
            0x44, 0x55, 0x66, 0x77,
        ]);
        assert_eq!(format!("{peer_id}"), "0123456789abcdef");
    }

    #[test]
    fn test_bootstrap_node_management() {
        let _config = NatTraversalConfig::default();
        // Note: This will fail due to ServerConfig requirement in new() - for illustration only
        // let endpoint = NatTraversalEndpoint::new(config, None).unwrap();
    }

    #[test]
    fn test_candidate_address_validation() {
        use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

        // Valid addresses
        assert!(
            CandidateAddress::validate_address(&SocketAddr::new(
                IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)),
                8080
            ))
            .is_ok()
        );

        assert!(
            CandidateAddress::validate_address(&SocketAddr::new(
                IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)),
                53
            ))
            .is_ok()
        );

        assert!(
            CandidateAddress::validate_address(&SocketAddr::new(
                IpAddr::V6(Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888)),
                443
            ))
            .is_ok()
        );

        // Invalid port 0
        assert!(matches!(
            CandidateAddress::validate_address(&SocketAddr::new(
                IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)),
                0
            )),
            Err(CandidateValidationError::InvalidPort(0))
        ));

        // Privileged port (non-test mode would fail)
        #[cfg(not(test))]
        assert!(matches!(
            CandidateAddress::validate_address(&SocketAddr::new(
                IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)),
                80
            )),
            Err(CandidateValidationError::PrivilegedPort(80))
        ));

        // Unspecified addresses
        assert!(matches!(
            CandidateAddress::validate_address(&SocketAddr::new(
                IpAddr::V4(Ipv4Addr::UNSPECIFIED),
                8080
            )),
            Err(CandidateValidationError::UnspecifiedAddress)
        ));

        assert!(matches!(
            CandidateAddress::validate_address(&SocketAddr::new(
                IpAddr::V6(Ipv6Addr::UNSPECIFIED),
                8080
            )),
            Err(CandidateValidationError::UnspecifiedAddress)
        ));

        // Broadcast address
        assert!(matches!(
            CandidateAddress::validate_address(&SocketAddr::new(
                IpAddr::V4(Ipv4Addr::BROADCAST),
                8080
            )),
            Err(CandidateValidationError::BroadcastAddress)
        ));

        // Multicast addresses
        assert!(matches!(
            CandidateAddress::validate_address(&SocketAddr::new(
                IpAddr::V4(Ipv4Addr::new(224, 0, 0, 1)),
                8080
            )),
            Err(CandidateValidationError::MulticastAddress)
        ));

        assert!(matches!(
            CandidateAddress::validate_address(&SocketAddr::new(
                IpAddr::V6(Ipv6Addr::new(0xff02, 0, 0, 0, 0, 0, 0, 1)),
                8080
            )),
            Err(CandidateValidationError::MulticastAddress)
        ));

        // Reserved addresses
        assert!(matches!(
            CandidateAddress::validate_address(&SocketAddr::new(
                IpAddr::V4(Ipv4Addr::new(0, 0, 0, 1)),
                8080
            )),
            Err(CandidateValidationError::ReservedAddress)
        ));

        assert!(matches!(
            CandidateAddress::validate_address(&SocketAddr::new(
                IpAddr::V4(Ipv4Addr::new(240, 0, 0, 1)),
                8080
            )),
            Err(CandidateValidationError::ReservedAddress)
        ));

        // Documentation address
        assert!(matches!(
            CandidateAddress::validate_address(&SocketAddr::new(
                IpAddr::V6(Ipv6Addr::new(0x2001, 0x0db8, 0, 0, 0, 0, 0, 1)),
                8080
            )),
            Err(CandidateValidationError::DocumentationAddress)
        ));

        // IPv4-mapped IPv6
        assert!(matches!(
            CandidateAddress::validate_address(&SocketAddr::new(
                IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0xffff, 0xc0a8, 0x0001)),
                8080
            )),
            Err(CandidateValidationError::IPv4MappedAddress)
        ));
    }

    #[test]
    fn test_candidate_address_suitability_for_nat_traversal() {
        use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};

        // Create valid candidates
        let public_v4 = CandidateAddress::new(
            SocketAddr::new(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)), 8080),
            100,
            CandidateSource::Observed { by_node: None },
        )
        .unwrap();
        assert!(public_v4.is_suitable_for_nat_traversal());

        let private_v4 = CandidateAddress::new(
            SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 8080),
            100,
            CandidateSource::Local,
        )
        .unwrap();
        assert!(private_v4.is_suitable_for_nat_traversal());

        // Link-local should not be suitable
        let link_local_v4 = CandidateAddress::new(
            SocketAddr::new(IpAddr::V4(Ipv4Addr::new(169, 254, 1, 1)), 8080),
            100,
            CandidateSource::Local,
        )
        .unwrap();
        assert!(!link_local_v4.is_suitable_for_nat_traversal());

        // Global unicast IPv6 should be suitable
        let global_v6 = CandidateAddress::new(
            SocketAddr::new(
                IpAddr::V6(Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888)),
                8080,
            ),
            100,
            CandidateSource::Observed { by_node: None },
        )
        .unwrap();
        assert!(global_v6.is_suitable_for_nat_traversal());

        // Link-local IPv6 should not be suitable
        let link_local_v6 = CandidateAddress::new(
            SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 1)), 8080),
            100,
            CandidateSource::Local,
        )
        .unwrap();
        assert!(!link_local_v6.is_suitable_for_nat_traversal());

        // Unique local IPv6 should not be suitable for external traversal
        let unique_local_v6 = CandidateAddress::new(
            SocketAddr::new(IpAddr::V6(Ipv6Addr::new(0xfc00, 0, 0, 0, 0, 0, 0, 1)), 8080),
            100,
            CandidateSource::Local,
        )
        .unwrap();
        assert!(!unique_local_v6.is_suitable_for_nat_traversal());

        // Loopback should be suitable only in test mode
        #[cfg(test)]
        {
            let loopback_v4 = CandidateAddress::new(
                SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8080),
                100,
                CandidateSource::Local,
            )
            .unwrap();
            assert!(loopback_v4.is_suitable_for_nat_traversal());

            let loopback_v6 = CandidateAddress::new(
                SocketAddr::new(IpAddr::V6(Ipv6Addr::LOCALHOST), 8080),
                100,
                CandidateSource::Local,
            )
            .unwrap();
            assert!(loopback_v6.is_suitable_for_nat_traversal());
        }
    }

    #[test]
    fn test_candidate_effective_priority() {
        use std::net::{IpAddr, Ipv4Addr};

        let mut candidate = CandidateAddress::new(
            SocketAddr::new(IpAddr::V4(Ipv4Addr::new(192, 168, 1, 1)), 8080),
            100,
            CandidateSource::Local,
        )
        .unwrap();

        // New state - slightly reduced priority
        assert_eq!(candidate.effective_priority(), 90);

        // Validating state - small reduction
        candidate.state = CandidateState::Validating;
        assert_eq!(candidate.effective_priority(), 95);

        // Valid state - full priority
        candidate.state = CandidateState::Valid;
        assert_eq!(candidate.effective_priority(), 100);

        // Failed state - zero priority
        candidate.state = CandidateState::Failed;
        assert_eq!(candidate.effective_priority(), 0);

        // Removed state - zero priority
        candidate.state = CandidateState::Removed;
        assert_eq!(candidate.effective_priority(), 0);
    }

    #[test]
    fn test_session_is_synchronized_requires_candidates() {
        let started_at = std::time::Instant::now();
        let session = NatTraversalSession {
            peer_id: PeerId([7; 32]),
            coordinator: "127.0.0.1:9000".parse().unwrap(),
            attempt: 1,
            started_at,
            phase_started_at: started_at,
            phase: TraversalPhase::Synchronization,
            candidates: Vec::new(),
            last_progress_at: started_at,
            next_deadline: None,
            retry_at: None,
            last_failure: None,
            session_state: SessionState {
                state: ConnectionState::Connecting,
                last_transition: started_at,
                connection: None,
                active_attempts: Vec::new(),
                metrics: ConnectionMetrics::default(),
            },
        };

        assert!(
            !NatTraversalEndpoint::session_is_synchronized(&session),
            "Synchronization requires at least one candidate"
        );
    }

    #[tokio::test]
    async fn test_check_punch_results_requires_real_success() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let peer_id = PeerId([9; 32]);
        let now = std::time::Instant::now();
        let session = NatTraversalSession {
            peer_id,
            coordinator: "127.0.0.1:9000".parse().unwrap(),
            attempt: 1,
            started_at: now,
            phase_started_at: now,
            phase: TraversalPhase::Punching,
            candidates: vec![
                CandidateAddress::new(
                    "127.0.0.1:44444".parse().unwrap(),
                    100,
                    CandidateSource::Peer,
                )
                .unwrap(),
            ],
            last_progress_at: now,
            next_deadline: None,
            retry_at: None,
            last_failure: None,
            session_state: SessionState {
                state: ConnectionState::Connecting,
                last_transition: now,
                connection: None,
                active_attempts: Vec::new(),
                metrics: ConnectionMetrics::default(),
            },
        };

        assert_eq!(endpoint.check_punch_results_for_session(&session), None);

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_check_punch_results_uses_recorded_successful_candidate() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let peer_id = PeerId([10; 32]);
        let candidate_addr: SocketAddr = "127.0.0.1:45555".parse().unwrap();
        endpoint
            .successful_candidates
            .insert(peer_id, candidate_addr);

        let now = std::time::Instant::now();
        let session = NatTraversalSession {
            peer_id,
            coordinator: "127.0.0.1:9000".parse().unwrap(),
            attempt: 1,
            started_at: now,
            phase_started_at: now,
            phase: TraversalPhase::Punching,
            candidates: vec![
                CandidateAddress::new(candidate_addr, 100, CandidateSource::Peer).unwrap(),
            ],
            last_progress_at: now,
            next_deadline: None,
            retry_at: None,
            last_failure: None,
            session_state: SessionState {
                state: ConnectionState::Connecting,
                last_transition: now,
                connection: None,
                active_attempts: Vec::new(),
                metrics: ConnectionMetrics::default(),
            },
        };

        assert_eq!(
            endpoint.check_punch_results_for_session(&session),
            Some(candidate_addr)
        );

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[test]
    fn test_build_nat_transport_config_uses_transport_default_keepalive() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            timeouts: crate::config::nat_timeouts::TimeoutConfig {
                nat_traversal: crate::config::nat_timeouts::NatTraversalTimeouts {
                    retry_interval: Duration::from_millis(250),
                    ..Default::default()
                },
                ..Default::default()
            },
            ..Default::default()
        };

        let transport_config = NatTraversalEndpoint::build_nat_transport_config(&config);
        let default_keepalive = TransportConfig::default().keep_alive_interval;

        assert_eq!(transport_config.keep_alive_interval, default_keepalive);
        assert_ne!(
            transport_config.keep_alive_interval,
            Some(config.timeouts.nat_traversal.retry_interval),
            "QUIC keepalive must not follow NAT retry cadence"
        );
    }

    #[test]
    fn test_coordination_connect_timeout_uses_configured_budget() {
        let config = NatTraversalConfig {
            coordination_timeout: Duration::from_millis(321),
            ..Default::default()
        };

        assert_eq!(
            NatTraversalEndpoint::coordination_connect_timeout(&config),
            Duration::from_millis(321)
        );
    }

    #[tokio::test]
    async fn test_traversal_event_notify_wakes_waiters_on_emit_event() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let notified = endpoint.traversal_event_notify().notified();
        let mut events = Vec::new();
        endpoint.emit_event(
            &mut events,
            NatTraversalEvent::TraversalFailed {
                peer_id: PeerId([0x42; 32]),
                error: NatTraversalError::Timeout,
                fallback_available: true,
            },
        );

        tokio::time::timeout(Duration::from_millis(50), notified)
            .await
            .expect("traversal event notify should fire");
        assert_eq!(events.len(), 1);

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_next_session_state_poll_deadline_uses_earliest_transition() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let now = std::time::Instant::now();
        let closed_deadline = now + Duration::from_millis(20);
        let connecting_deadline = now + Duration::from_secs(2);

        endpoint.active_sessions.insert(
            PeerId([0x52; 32]),
            NatTraversalSession {
                peer_id: PeerId([0x52; 32]),
                coordinator: "127.0.0.1:9000".parse().unwrap(),
                attempt: 1,
                started_at: now,
                phase_started_at: now,
                phase: TraversalPhase::Discovery,
                candidates: Vec::new(),
                last_progress_at: now,
                next_deadline: None,
                retry_at: None,
                last_failure: None,
                session_state: SessionState {
                    state: ConnectionState::Connecting,
                    last_transition: connecting_deadline
                        - endpoint
                            .timeout_config
                            .nat_traversal
                            .connection_establishment_timeout,
                    connection: None,
                    active_attempts: Vec::new(),
                    metrics: ConnectionMetrics::default(),
                },
            },
        );
        endpoint.active_sessions.insert(
            PeerId([0x53; 32]),
            NatTraversalSession {
                peer_id: PeerId([0x53; 32]),
                coordinator: "127.0.0.1:9001".parse().unwrap(),
                attempt: 1,
                started_at: now,
                phase_started_at: now,
                phase: TraversalPhase::Failed,
                candidates: Vec::new(),
                last_progress_at: now,
                next_deadline: None,
                retry_at: None,
                last_failure: None,
                session_state: SessionState {
                    state: ConnectionState::Closed,
                    last_transition: closed_deadline
                        - endpoint.timeout_config.discovery.interface_cache_ttl,
                    connection: None,
                    active_attempts: Vec::new(),
                    metrics: ConnectionMetrics::default(),
                },
            },
        );

        let deadline = endpoint
            .next_session_state_poll_deadline(now)
            .expect("legacy session-state driver should have pending work");

        assert!(deadline >= now, "deadline must not move backwards");
        assert!(
            deadline <= now + Duration::from_millis(50),
            "earliest closed-session cleanup deadline should win"
        );
        assert!(
            deadline < connecting_deadline,
            "closed-session cleanup should wake before the slower connecting deadline"
        );

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_start_session_polling_exits_promptly_on_shutdown_notify() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let handle = endpoint.start_session_polling(Duration::from_secs(3600));
        tokio::task::yield_now().await;
        tokio::task::yield_now().await;

        endpoint.shutdown().await.expect("Shutdown should succeed");

        tokio::time::timeout(Duration::from_millis(100), handle)
            .await
            .expect("session driver should not wait for the long interval to exit")
            .expect("session driver should join cleanly");
    }

    #[tokio::test]
    async fn test_poll_advances_synchronization_without_waiting_for_fixed_timeout() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let peer_id = PeerId([0x61; 32]);
        endpoint.active_sessions.insert(
            peer_id,
            NatTraversalSession {
                peer_id,
                coordinator: "127.0.0.1:9000".parse().unwrap(),
                attempt: 1,
                started_at: std::time::Instant::now(),
                phase_started_at: std::time::Instant::now(),
                phase: TraversalPhase::Synchronization,
                candidates: vec![
                    CandidateAddress::new(
                        "127.0.0.1:9001".parse().unwrap(),
                        100,
                        CandidateSource::Observed { by_node: None },
                    )
                    .expect("candidate"),
                ],
                last_progress_at: std::time::Instant::now(),
                next_deadline: None,
                retry_at: None,
                last_failure: None,
                session_state: SessionState {
                    state: ConnectionState::Connecting,
                    last_transition: std::time::Instant::now(),
                    connection: None,
                    active_attempts: Vec::new(),
                    metrics: ConnectionMetrics::default(),
                },
            },
        );

        let events = endpoint
            .poll(std::time::Instant::now())
            .expect("poll should succeed");

        assert!(matches!(
            endpoint
                .active_sessions
                .get(&peer_id)
                .expect("session")
                .phase,
            TraversalPhase::Punching
        ));
        assert!(events.iter().any(|event| matches!(
            event,
            NatTraversalEvent::TraversalProgress {
                peer_id: event_peer,
                phase: TraversalPhase::Punching,
                ..
            } if *event_peer == peer_id
        )));
        assert!(events.iter().any(|event| matches!(
            event,
            NatTraversalEvent::HolePunchingStarted { peer_id: event_peer, .. }
                if *event_peer == peer_id
        )));

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_poll_advances_punching_to_validation_without_waiting_for_fixed_timeout() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let peer_id = PeerId([0x62; 32]);
        let candidate_addr: SocketAddr = "127.0.0.1:9002".parse().expect("candidate addr");
        endpoint
            .successful_candidates
            .insert(peer_id, candidate_addr);
        endpoint.active_sessions.insert(
            peer_id,
            NatTraversalSession {
                peer_id,
                coordinator: "127.0.0.1:9000".parse().unwrap(),
                attempt: 1,
                started_at: std::time::Instant::now(),
                phase_started_at: std::time::Instant::now(),
                phase: TraversalPhase::Punching,
                candidates: vec![
                    CandidateAddress::new(
                        candidate_addr,
                        100,
                        CandidateSource::Observed { by_node: None },
                    )
                    .expect("candidate"),
                ],
                last_progress_at: std::time::Instant::now(),
                next_deadline: None,
                retry_at: None,
                last_failure: None,
                session_state: SessionState {
                    state: ConnectionState::Connecting,
                    last_transition: std::time::Instant::now(),
                    connection: None,
                    active_attempts: Vec::new(),
                    metrics: ConnectionMetrics::default(),
                },
            },
        );

        let events = endpoint
            .poll(std::time::Instant::now())
            .expect("poll should succeed");

        let session = endpoint.active_sessions.get(&peer_id).expect("session");
        assert!(matches!(session.phase, TraversalPhase::Validation));
        assert!(matches!(
            session.candidates.first().expect("candidate").state,
            CandidateState::Validating
        ));
        assert!(
            !events
                .iter()
                .any(|event| matches!(event, NatTraversalEvent::TraversalFailed { .. })),
            "successful punch results should advance immediately instead of waiting for a timeout"
        );

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_poll_advances_validation_to_connected_without_waiting_for_fixed_timeout() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let peer_id = PeerId([0x63; 32]);
        let candidate_addr: SocketAddr = "127.0.0.1:9003".parse().expect("candidate addr");
        let local_addr: SocketAddr = "10.0.0.10:9004".parse().expect("local candidate");
        let now = std::time::Instant::now();
        endpoint
            .successful_candidates
            .insert(peer_id, candidate_addr);

        let local_candidate = CandidateAddress::new(local_addr, 110, CandidateSource::Local)
            .expect("local candidate");
        let mut remote_candidate = CandidateAddress::new(
            candidate_addr,
            100,
            CandidateSource::Observed { by_node: None },
        )
        .expect("candidate");
        remote_candidate.state = CandidateState::Valid;
        endpoint.active_sessions.insert(
            peer_id,
            NatTraversalSession {
                peer_id,
                coordinator: "127.0.0.1:9000".parse().unwrap(),
                attempt: 2,
                started_at: now - Duration::from_secs(2),
                phase_started_at: now - Duration::from_millis(50),
                phase: TraversalPhase::Validation,
                candidates: vec![local_candidate.clone(), remote_candidate.clone()],
                last_progress_at: now,
                next_deadline: None,
                retry_at: None,
                last_failure: None,
                session_state: SessionState {
                    state: ConnectionState::Connecting,
                    last_transition: std::time::Instant::now(),
                    connection: None,
                    active_attempts: Vec::new(),
                    metrics: ConnectionMetrics::default(),
                },
            },
        );

        let events = endpoint
            .poll(std::time::Instant::now())
            .expect("poll should succeed");

        assert!(matches!(
            endpoint
                .active_sessions
                .get(&peer_id)
                .expect("session")
                .phase,
            TraversalPhase::Connected
        ));
        assert!(events.iter().any(|event| matches!(
            event,
            NatTraversalEvent::TraversalSucceeded {
                peer_id: event_peer,
                final_address,
                winning_pair: Some(CandidatePair {
                    local_candidate,
                    remote_candidate,
                    state: CandidatePairState::Succeeded,
                    ..
                }),
                attempts,
                total_time,
            } if *event_peer == peer_id
                && *final_address == candidate_addr
                && *attempts == 2
                && *total_time >= Duration::from_secs(2)
                && local_candidate.address == local_addr
                && remote_candidate.address == candidate_addr
        )));

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[test]
    fn test_resolve_path_validation_rtt_ignores_zero_connection_sample() {
        assert_eq!(
            NatTraversalEndpoint::resolve_path_validation_rtt(
                Some(Duration::ZERO),
                Some(Duration::from_millis(42)),
                Duration::from_millis(333),
            ),
            Duration::from_millis(42),
            "unsampled zero-RTT connection stats should fall through to the session RTT"
        );

        assert_eq!(
            NatTraversalEndpoint::resolve_path_validation_rtt(
                Some(Duration::from_millis(7)),
                Some(Duration::from_millis(42)),
                Duration::from_millis(333),
            ),
            Duration::from_millis(7),
            "real connection RTT samples should still win when present"
        );
    }

    #[tokio::test]
    async fn test_connectivity_phase_budget_uses_small_floor_and_establishment_cap() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            coordination_timeout: Duration::from_secs(60),
            timeouts: crate::config::nat_timeouts::TimeoutConfig {
                nat_traversal: crate::config::nat_timeouts::NatTraversalTimeouts {
                    connection_establishment_timeout: Duration::from_secs(4),
                    ..Default::default()
                },
                ..Default::default()
            },
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let started_at = std::time::Instant::now();
        let mut session = NatTraversalSession {
            peer_id: PeerId([0x64; 32]),
            coordinator: "127.0.0.1:9000".parse().unwrap(),
            attempt: 1,
            started_at,
            phase_started_at: started_at,
            phase: TraversalPhase::Punching,
            candidates: Vec::new(),
            last_progress_at: started_at,
            next_deadline: None,
            retry_at: None,
            last_failure: None,
            session_state: SessionState {
                state: ConnectionState::Connecting,
                last_transition: started_at,
                connection: None,
                active_attempts: Vec::new(),
                metrics: ConnectionMetrics::default(),
            },
        };

        session.session_state.metrics.rtt = Some(Duration::from_micros(20));
        assert_eq!(
            endpoint.connectivity_phase_budget(&session),
            Duration::from_secs(1),
            "very fast paths should use the small connectivity floor instead of a large coordination-derived floor"
        );

        session.session_state.metrics.rtt = Some(Duration::from_millis(100));
        assert_eq!(
            endpoint.connectivity_phase_budget(&session),
            Duration::from_millis(1100),
            "mid-range RTTs should use the scaled path budget rather than only the floor or cap"
        );

        session.session_state.metrics.rtt = Some(Duration::from_secs(10));
        assert_eq!(
            endpoint.connectivity_phase_budget(&session),
            Duration::from_secs(4),
            "connectivity phases should cap at the connection-establishment timeout even if coordination is configured larger"
        );

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_connected_phase_has_no_contract_timeout_deadline() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");
        let started_at = std::time::Instant::now();
        let session = NatTraversalSession {
            peer_id: PeerId([0x62; 32]),
            coordinator: "127.0.0.1:9000".parse().unwrap(),
            attempt: 1,
            started_at,
            phase_started_at: started_at,
            phase: TraversalPhase::Connected,
            candidates: Vec::new(),
            last_progress_at: started_at,
            next_deadline: None,
            retry_at: None,
            last_failure: None,
            session_state: SessionState {
                state: ConnectionState::Connected,
                last_transition: started_at,
                connection: None,
                active_attempts: Vec::new(),
                metrics: ConnectionMetrics::default(),
            },
        };

        assert!(
            endpoint
                .recompute_session_deadline(&session, started_at)
                .is_none()
        );
        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_recompute_session_deadline_prefers_retry_backoff() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            coordination_timeout: Duration::from_secs(5),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let now = std::time::Instant::now();
        let retry_at = now + Duration::from_millis(250);
        let session = NatTraversalSession {
            peer_id: PeerId([0x71; 32]),
            coordinator: "127.0.0.1:9000".parse().unwrap(),
            attempt: 1,
            started_at: now,
            phase_started_at: now,
            phase: TraversalPhase::Synchronization,
            candidates: Vec::new(),
            last_progress_at: now - Duration::from_secs(2),
            next_deadline: None,
            retry_at: Some(retry_at),
            last_failure: Some(TraversalFailureReason::NetworkError(
                "temporary".to_string(),
            )),
            session_state: SessionState {
                state: ConnectionState::Connecting,
                last_transition: now,
                connection: None,
                active_attempts: Vec::new(),
                metrics: ConnectionMetrics::default(),
            },
        };

        let deadline = endpoint
            .recompute_session_deadline(&session, now)
            .expect("retry deadline");
        assert_eq!(deadline.kind, TraversalDeadlineKind::RetryBackoff);
        assert_eq!(deadline.at, retry_at);

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_recompute_session_deadline_uses_session_owned_non_discovery_budget() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            coordination_timeout: Duration::from_secs(3),
            ..Default::default()
        };
        let coordination_timeout = config.coordination_timeout;
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let now = std::time::Instant::now();
        let last_progress_at = now - Duration::from_millis(400);
        let session = NatTraversalSession {
            peer_id: PeerId([0x72; 32]),
            coordinator: "127.0.0.1:9000".parse().unwrap(),
            attempt: 1,
            started_at: now - Duration::from_secs(10),
            phase_started_at: now - Duration::from_secs(10),
            phase: TraversalPhase::Synchronization,
            candidates: vec![
                CandidateAddress::new(
                    "127.0.0.1:9001".parse().unwrap(),
                    100,
                    CandidateSource::Observed { by_node: None },
                )
                .expect("candidate"),
            ],
            last_progress_at,
            next_deadline: None,
            retry_at: None,
            last_failure: None,
            session_state: SessionState {
                state: ConnectionState::Connecting,
                last_transition: now,
                connection: None,
                active_attempts: Vec::new(),
                metrics: ConnectionMetrics::default(),
            },
        };

        let deadline = endpoint
            .recompute_session_deadline(&session, now)
            .expect("phase deadline");
        assert_eq!(
            deadline.kind,
            TraversalDeadlineKind::SynchronizationProgress
        );
        assert_eq!(deadline.at, last_progress_at + coordination_timeout);

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[test]
    fn test_set_session_phase_clears_stale_retry_state_and_resets_progress_timestamp() {
        let original_now = std::time::Instant::now();
        let transition_now = original_now + Duration::from_secs(2);
        let mut session = NatTraversalSession {
            peer_id: PeerId([0x76; 32]),
            coordinator: "127.0.0.1:9000".parse().unwrap(),
            attempt: 2,
            started_at: original_now,
            phase_started_at: original_now,
            phase: TraversalPhase::Coordination,
            candidates: Vec::new(),
            last_progress_at: original_now,
            next_deadline: Some(SessionDeadline {
                kind: TraversalDeadlineKind::RetryBackoff,
                at: original_now + Duration::from_secs(1),
            }),
            retry_at: Some(original_now + Duration::from_secs(1)),
            last_failure: Some(TraversalFailureReason::CoordinatorUnavailable),
            session_state: SessionState {
                state: ConnectionState::Connecting,
                last_transition: original_now,
                connection: None,
                active_attempts: Vec::new(),
                metrics: ConnectionMetrics::default(),
            },
        };

        NatTraversalEndpoint::set_session_phase(
            &mut session,
            transition_now,
            TraversalPhase::Synchronization,
        );

        assert!(matches!(session.phase, TraversalPhase::Synchronization));
        assert_eq!(session.started_at, original_now);
        assert_eq!(session.phase_started_at, transition_now);
        assert_eq!(session.last_progress_at, transition_now);
        assert!(
            session.retry_at.is_none(),
            "phase progress should clear stale retry scheduling"
        );
        assert!(
            session.next_deadline.is_none(),
            "phase progress should clear stale retry deadline ownership"
        );
        assert_eq!(session.session_state.last_transition, transition_now);
    }

    #[tokio::test]
    async fn test_retry_disposition_distinguishes_retryable_vs_terminal_reasons() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let now = std::time::Instant::now();
        let retryable =
            endpoint.retry_disposition(&TraversalFailureReason::CoordinatorUnavailable, 1, now);
        let terminal = endpoint.retry_disposition(
            &TraversalFailureReason::ProtocolViolation("bad frame".to_string()),
            1,
            now,
        );

        assert!(matches!(retryable, RetryDisposition::After(at) if at > now));
        assert!(matches!(terminal, RetryDisposition::Never));

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_handle_phase_failure_stores_retry_state_for_retryable_failures() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let now = std::time::Instant::now();
        let mut session = NatTraversalSession {
            peer_id: PeerId([0x73; 32]),
            coordinator: "127.0.0.1:9000".parse().unwrap(),
            attempt: 1,
            started_at: now - Duration::from_secs(1),
            phase_started_at: now - Duration::from_secs(1),
            phase: TraversalPhase::Coordination,
            candidates: Vec::new(),
            last_progress_at: now - Duration::from_secs(1),
            next_deadline: None,
            retry_at: None,
            last_failure: None,
            session_state: SessionState {
                state: ConnectionState::Connecting,
                last_transition: now,
                connection: None,
                active_attempts: Vec::new(),
                metrics: ConnectionMetrics::default(),
            },
        };
        let mut events = Vec::new();

        endpoint.handle_phase_failure(
            &mut session,
            now,
            &mut events,
            NatTraversalError::NoBootstrapNodes,
        );

        assert!(matches!(
            session.last_failure,
            Some(TraversalFailureReason::CoordinatorUnavailable)
        ));
        let retry_at = session.retry_at.expect("retry scheduled");
        assert!(retry_at > now);
        assert!(matches!(
            session.next_deadline,
            Some(SessionDeadline {
                kind: TraversalDeadlineKind::RetryBackoff,
                at,
            }) if at == retry_at
        ));
        assert!(matches!(
            events.as_slice(),
            [NatTraversalEvent::RetryScheduled {
                peer_id,
                attempt,
                retry_at: scheduled_retry_at,
                reason: TraversalFailureReason::CoordinatorUnavailable,
            }] if *peer_id == session.peer_id && *attempt == session.attempt && *scheduled_retry_at == retry_at
        ));

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_handle_phase_failure_classifies_synchronization_timeout_as_retryable_expiration()
    {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let now = std::time::Instant::now();
        let mut session = NatTraversalSession {
            peer_id: PeerId([0x77; 32]),
            coordinator: "127.0.0.1:9000".parse().unwrap(),
            attempt: 1,
            started_at: now - Duration::from_secs(1),
            phase_started_at: now - Duration::from_secs(1),
            phase: TraversalPhase::Synchronization,
            candidates: Vec::new(),
            last_progress_at: now - Duration::from_secs(1),
            next_deadline: None,
            retry_at: None,
            last_failure: None,
            session_state: SessionState {
                state: ConnectionState::Connecting,
                last_transition: now,
                connection: None,
                active_attempts: Vec::new(),
                metrics: ConnectionMetrics::default(),
            },
        };
        let mut events = Vec::new();

        endpoint.handle_phase_failure(&mut session, now, &mut events, NatTraversalError::Timeout);

        assert!(matches!(
            session.last_failure,
            Some(TraversalFailureReason::SynchronizationExpired)
        ));
        assert!(
            session.retry_at.is_some(),
            "synchronization timeout should be retryable"
        );
        assert!(matches!(
            session.next_deadline,
            Some(SessionDeadline {
                kind: TraversalDeadlineKind::RetryBackoff,
                ..
            })
        ));
        assert!(matches!(
            events.as_slice(),
            [
                NatTraversalEvent::TraversalStalled {
                    peer_id,
                    phase: TraversalPhase::Synchronization,
                    deadline_kind: TraversalDeadlineKind::SynchronizationProgress,
                },
                NatTraversalEvent::RetryScheduled {
                    peer_id: retry_peer_id,
                    attempt,
                    retry_at: _,
                    reason: TraversalFailureReason::SynchronizationExpired,
                }
            ] if *peer_id == session.peer_id && *retry_peer_id == session.peer_id && *attempt == session.attempt
        ));

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_handle_phase_failure_stores_retry_schedule_for_discovery_exhaustion() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let now = std::time::Instant::now();
        let mut session = NatTraversalSession {
            peer_id: PeerId([0x78; 32]),
            coordinator: "127.0.0.1:9000".parse().unwrap(),
            attempt: 1,
            started_at: now - Duration::from_secs(1),
            phase_started_at: now - Duration::from_secs(1),
            phase: TraversalPhase::Discovery,
            candidates: Vec::new(),
            last_progress_at: now - Duration::from_secs(1),
            next_deadline: None,
            retry_at: None,
            last_failure: None,
            session_state: SessionState {
                state: ConnectionState::Connecting,
                last_transition: now,
                connection: None,
                active_attempts: Vec::new(),
                metrics: ConnectionMetrics::default(),
            },
        };
        let mut events = Vec::new();

        endpoint.handle_phase_failure(
            &mut session,
            now,
            &mut events,
            NatTraversalError::NoCandidatesFound,
        );

        assert!(matches!(
            session.last_failure,
            Some(TraversalFailureReason::DiscoveryExhausted)
        ));
        assert!(
            session.retry_at.is_some(),
            "discovery exhaustion should persist retry scheduling"
        );
        assert!(matches!(
            session.next_deadline,
            Some(SessionDeadline {
                kind: TraversalDeadlineKind::RetryBackoff,
                ..
            })
        ));
        assert!(matches!(
            events.as_slice(),
            [
                NatTraversalEvent::TraversalStalled {
                    peer_id,
                    phase: TraversalPhase::Discovery,
                    deadline_kind: TraversalDeadlineKind::DiscoveryProgress,
                },
                NatTraversalEvent::RetryScheduled {
                    peer_id: retry_peer_id,
                    attempt,
                    retry_at: _,
                    reason: TraversalFailureReason::DiscoveryExhausted,
                }
            ] if *peer_id == session.peer_id && *retry_peer_id == session.peer_id && *attempt == session.attempt
        ));

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_handle_phase_failure_emits_terminal_event_and_clears_retry_state() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let now = std::time::Instant::now();
        let mut session = NatTraversalSession {
            peer_id: PeerId([0x74; 32]),
            coordinator: "127.0.0.1:9000".parse().unwrap(),
            attempt: 1,
            started_at: now,
            phase_started_at: now,
            phase: TraversalPhase::Synchronization,
            candidates: Vec::new(),
            last_progress_at: now,
            next_deadline: Some(SessionDeadline {
                kind: TraversalDeadlineKind::RetryBackoff,
                at: now + Duration::from_secs(1),
            }),
            retry_at: Some(now + Duration::from_secs(1)),
            last_failure: None,
            session_state: SessionState {
                state: ConnectionState::Connecting,
                last_transition: now,
                connection: None,
                active_attempts: Vec::new(),
                metrics: ConnectionMetrics::default(),
            },
        };
        let mut events = Vec::new();

        endpoint.handle_phase_failure(
            &mut session,
            now,
            &mut events,
            NatTraversalError::ProtocolError("malformed control message".to_string()),
        );

        assert!(matches!(
            session.last_failure,
            Some(TraversalFailureReason::ProtocolViolation(_))
        ));
        assert!(
            session.retry_at.is_none(),
            "terminal failures clear retry_at"
        );
        assert!(
            session.next_deadline.is_none(),
            "terminal failures clear next_deadline"
        );
        assert!(matches!(session.phase, TraversalPhase::Failed));
        assert!(matches!(
            events.as_slice(),
            [
                NatTraversalEvent::TraversalTerminated {
                    peer_id,
                    reason: TraversalFailureReason::ProtocolViolation(message),
                    fallback_available: true,
                },
                NatTraversalEvent::TraversalFailed {
                    peer_id: failed_peer_id,
                    error: NatTraversalError::ProtocolError(failed_message),
                    fallback_available: true,
                }
            ] if *peer_id == session.peer_id
                && *failed_peer_id == session.peer_id
                && message == "malformed control message"
                && failed_message == "malformed control message"
        ));

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_coordinator_request_expiry_helper_is_bounded_by_session_budget_and_timeout() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            coordination_timeout: Duration::from_secs(5),
            ..Default::default()
        };
        let coordination_timeout = config.coordination_timeout;
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let peer_id = PeerId([0x75; 32]);
        let now = std::time::Instant::now();
        endpoint.active_sessions.insert(
            peer_id,
            NatTraversalSession {
                peer_id,
                coordinator: "127.0.0.1:9000".parse().unwrap(),
                attempt: 1,
                started_at: now - Duration::from_secs(10),
                phase_started_at: now - Duration::from_secs(10),
                phase: TraversalPhase::Synchronization,
                candidates: Vec::new(),
                last_progress_at: now - Duration::from_secs(1),
                next_deadline: None,
                retry_at: None,
                last_failure: None,
                session_state: SessionState {
                    state: ConnectionState::Connecting,
                    last_transition: now,
                    connection: None,
                    active_attempts: Vec::new(),
                    metrics: ConnectionMetrics::default(),
                },
            },
        );

        let expiry_duration = endpoint.coordinator_request_expiry_duration(
            peer_id,
            "127.0.0.1:9000".parse().expect("coordinator addr"),
        );
        assert!(
            expiry_duration <= coordination_timeout,
            "expiry must stay within configured coordination timeout"
        );
        assert!(
            expiry_duration <= Duration::from_secs(4),
            "expiry should clamp to remaining session-owned budget"
        );

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_coordinator_request_expiry_helper_applies_minimum_floor() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            coordination_timeout: Duration::from_secs(5),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let peer_id = PeerId([0x79; 32]);
        let now = std::time::Instant::now();
        endpoint.active_sessions.insert(
            peer_id,
            NatTraversalSession {
                peer_id,
                coordinator: "127.0.0.1:9000".parse().unwrap(),
                attempt: 1,
                started_at: now - Duration::from_secs(1),
                phase_started_at: now - Duration::from_secs(1),
                phase: TraversalPhase::Synchronization,
                candidates: Vec::new(),
                last_progress_at: now - Duration::from_secs(5),
                next_deadline: Some(SessionDeadline {
                    kind: TraversalDeadlineKind::SynchronizationProgress,
                    at: now,
                }),
                retry_at: None,
                last_failure: None,
                session_state: SessionState {
                    state: ConnectionState::Connecting,
                    last_transition: now,
                    connection: None,
                    active_attempts: Vec::new(),
                    metrics: ConnectionMetrics::default(),
                },
            },
        );

        let expiry_duration = endpoint.coordinator_request_expiry_duration(
            peer_id,
            "127.0.0.1:9000".parse().expect("coordinator addr"),
        );
        assert!(
            expiry_duration >= Duration::from_millis(250),
            "expiry should honor the minimum floor when session budget is exhausted"
        );
        assert!(
            expiry_duration <= Duration::from_secs(5),
            "expiry floor must still respect the configured upper bound"
        );

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_coordinator_request_expiry_helper_uses_rtt_floor_when_available() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            coordination_timeout: Duration::from_secs(5),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let coordinator: SocketAddr = "127.0.0.1:9000".parse().expect("coordinator addr");
        let peer_id = PeerId([0x7A; 32]);
        endpoint.bootstrap_nodes.write().push(BootstrapNode {
            address: coordinator,
            last_seen: std::time::Instant::now(),
            can_coordinate: true,
            rtt: Some(Duration::from_millis(400)),
            coordination_count: 1,
        });

        let expiry_duration = endpoint.coordinator_request_expiry_duration(peer_id, coordinator);
        assert!(
            expiry_duration >= Duration::from_millis(1200),
            "expiry should allow at least a few coordinator RTTs when that hint is known"
        );
        assert!(
            expiry_duration <= Duration::from_secs(5),
            "RTT-derived floor must still respect the configured upper bound"
        );

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_discovery_retry_backoff_is_preserved_until_due_and_then_consumed() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            coordination_timeout: Duration::from_secs(5),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let peer_id = PeerId([0x7A; 32]);
        endpoint
            .discovery_manager
            .lock()
            .start_discovery(peer_id, Vec::new())
            .expect("discovery should start");

        let base_now = std::time::Instant::now();
        let retry_at = base_now + Duration::from_secs(30);
        endpoint.active_sessions.insert(
            peer_id,
            NatTraversalSession {
                peer_id,
                coordinator: "127.0.0.1:9000".parse().unwrap(),
                attempt: 1,
                started_at: base_now - Duration::from_secs(10),
                phase_started_at: base_now - Duration::from_secs(10),
                phase: TraversalPhase::Discovery,
                candidates: Vec::new(),
                last_progress_at: base_now - Duration::from_secs(10),
                next_deadline: Some(SessionDeadline {
                    kind: TraversalDeadlineKind::RetryBackoff,
                    at: retry_at,
                }),
                retry_at: Some(retry_at),
                last_failure: Some(TraversalFailureReason::DiscoveryExhausted),
                session_state: SessionState {
                    state: ConnectionState::Connecting,
                    last_transition: base_now,
                    connection: None,
                    active_attempts: Vec::new(),
                    metrics: ConnectionMetrics::default(),
                },
            },
        );

        let events_before = endpoint
            .poll(base_now)
            .expect("poll before retry deadline should succeed");
        assert!(
            events_before.is_empty(),
            "inert retry wait should not emit events"
        );
        {
            let session = endpoint
                .active_sessions
                .get(&peer_id)
                .expect("session before due");
            assert_eq!(session.retry_at, Some(retry_at));
            assert!(matches!(
                session.next_deadline,
                Some(SessionDeadline {
                    kind: TraversalDeadlineKind::RetryBackoff,
                    at,
                }) if at == retry_at
            ));
        }

        let events_after = endpoint
            .poll(retry_at)
            .expect("poll at retry deadline should succeed");
        assert!(
            events_after.is_empty(),
            "consuming retry state should not itself emit terminal events or stale discovery output"
        );
        {
            let session = endpoint
                .active_sessions
                .get(&peer_id)
                .expect("session after due");
            assert!(
                session.retry_at.is_none(),
                "retry state should be consumed once due"
            );
            assert!(
                !matches!(
                    session.next_deadline,
                    Some(SessionDeadline {
                        kind: TraversalDeadlineKind::RetryBackoff,
                        ..
                    })
                ),
                "retry backoff should no longer remain authoritative after consumption"
            );
            assert_eq!(session.last_progress_at, retry_at);
            assert_eq!(session.phase_started_at, retry_at);
        }

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_coordinator_rejected_runtime_event_uses_normal_event_path() {
        let (event_tx, mut event_rx) = mpsc::unbounded_channel();
        let captured_events = Arc::new(std::sync::Mutex::new(Vec::new()));
        let captured_events_for_callback = Arc::clone(&captured_events);
        let traversal_event_notify = Arc::new(tokio::sync::Notify::new());
        let callback: Arc<dyn Fn(NatTraversalEvent) + Send + Sync> = Arc::new(move |event| {
            captured_events_for_callback.lock().unwrap().push(event);
        });

        NatTraversalEndpoint::emit_runtime_event_parts(
            &event_tx,
            Some(&callback),
            &traversal_event_notify,
            NatTraversalEvent::CoordinationRejected {
                peer_id: PeerId([0x7B; 32]),
                coordinator: "127.0.0.1:9009".parse().unwrap(),
                reason: RejectionReason::RateLimited,
            },
        );

        let runtime_event = event_rx
            .try_recv()
            .expect("runtime event should be enqueued");
        assert!(matches!(
            runtime_event,
            NatTraversalEvent::CoordinationRejected {
                peer_id,
                coordinator,
                reason: RejectionReason::RateLimited,
            } if peer_id == PeerId([0x7B; 32]) && coordinator == "127.0.0.1:9009".parse().unwrap()
        ));
        let callback_events = captured_events.lock().unwrap();
        assert!(callback_events.iter().any(|event| matches!(
            event,
            NatTraversalEvent::CoordinationRejected {
                peer_id,
                reason: RejectionReason::RateLimited,
                ..
            } if *peer_id == PeerId([0x7B; 32])
        )));
    }

    #[tokio::test]
    async fn test_next_session_poll_deadline_prefers_discovery_cadence() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let peer_id = PeerId([0x43; 32]);
        endpoint
            .discovery_manager
            .lock()
            .start_discovery(peer_id, Vec::new())
            .expect("discovery should start");
        endpoint.active_sessions.insert(
            peer_id,
            NatTraversalSession {
                peer_id,
                coordinator: "127.0.0.1:9000".parse().unwrap(),
                attempt: 1,
                started_at: std::time::Instant::now(),
                phase_started_at: std::time::Instant::now(),
                phase: TraversalPhase::Discovery,
                candidates: Vec::new(),
                last_progress_at: std::time::Instant::now(),
                next_deadline: None,
                retry_at: None,
                last_failure: None,
                session_state: SessionState {
                    state: ConnectionState::Connecting,
                    last_transition: std::time::Instant::now(),
                    connection: None,
                    active_attempts: Vec::new(),
                    metrics: ConnectionMetrics::default(),
                },
            },
        );

        let now = std::time::Instant::now();
        let deadline = endpoint
            .next_session_poll_deadline(peer_id, now)
            .expect("active discovery session should schedule a wakeup");

        assert!(deadline >= now, "deadline must not move backwards");
        assert!(
            deadline <= now + Duration::from_millis(20),
            "discovery cadence should win over the coarse phase timeout"
        );
        let discovery_contract_deadline = endpoint
            .discovery_manager
            .lock()
            .phase_timeout_deadline_for_peer(peer_id)
            .expect("discovery contract deadline");
        assert!(
            deadline < discovery_contract_deadline,
            "session-specific discovery polling should wake before the discovery contract deadline"
        );

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_next_session_poll_deadline_ignores_discovery_cadence_after_phase_progress() {
        let config = NatTraversalConfig {
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            coordination_timeout: Duration::from_secs(5),
            ..Default::default()
        };
        let coordination_timeout = config.coordination_timeout;
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        let peer_id = PeerId([0x45; 32]);
        endpoint
            .discovery_manager
            .lock()
            .start_discovery(peer_id, Vec::new())
            .expect("discovery should start");

        let now = std::time::Instant::now();
        endpoint.active_sessions.insert(
            peer_id,
            NatTraversalSession {
                peer_id,
                coordinator: "127.0.0.1:9000".parse().unwrap(),
                attempt: 1,
                started_at: now,
                phase_started_at: now,
                phase: TraversalPhase::Coordination,
                candidates: vec![CandidateAddress {
                    address: "127.0.0.1:9001".parse().unwrap(),
                    priority: 1,
                    source: CandidateSource::Local,
                    state: CandidateState::New,
                }],
                last_progress_at: now,
                next_deadline: None,
                retry_at: None,
                last_failure: None,
                session_state: SessionState {
                    state: ConnectionState::Connecting,
                    last_transition: now,
                    connection: None,
                    active_attempts: Vec::new(),
                    metrics: ConnectionMetrics::default(),
                },
            },
        );

        let deadline = endpoint
            .next_session_poll_deadline(peer_id, now)
            .expect("coordination session should schedule a wakeup");

        assert_eq!(deadline, now + coordination_timeout);

        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    #[tokio::test]
    async fn test_process_observed_address_report_deduplicates_and_wakes_discovery() {
        let local_peer_id = PeerId([0x44; 32]);
        let mut manager = CandidateDiscoveryManager::new(DiscoveryConfig::test_default());
        manager
            .start_discovery(local_peer_id, Vec::new())
            .expect("discovery should start");
        let discovery_manager = ParkingMutex::new(manager);
        let discovery_state_notify = Arc::new(tokio::sync::Notify::new());
        let traversal_event_notify = Arc::new(tokio::sync::Notify::new());
        let relay_setup_attempted = Arc::new(std::sync::atomic::AtomicBool::new(false));
        let mut observed_address_reports = HashSet::new();
        let (event_tx, mut event_rx) = mpsc::unbounded_channel();
        let connections = dashmap::DashMap::new();
        let report = ObservedAddressReport {
            reporter_peer_id: PeerId([0x45; 32]),
            reported_by: "127.0.0.1:9000".parse().unwrap(),
            address: "203.0.113.44:45000".parse().unwrap(),
        };

        let discovery_notified = discovery_state_notify.notified();
        let traversal_notified = traversal_event_notify.notified();
        NatTraversalEndpoint::process_observed_address_report(
            &discovery_manager,
            &discovery_state_notify,
            &mut observed_address_reports,
            &event_tx,
            &connections,
            None,
            None,
            &traversal_event_notify,
            local_peer_id,
            &relay_setup_attempted,
            report,
        )
        .await;

        tokio::time::timeout(Duration::from_millis(50), discovery_notified)
            .await
            .expect("discovery notify should fire");
        tokio::time::timeout(Duration::from_millis(50), traversal_notified)
            .await
            .expect("traversal notify should fire");

        let event = event_rx.try_recv().expect("external address event");
        assert!(matches!(
            event,
            NatTraversalEvent::ExternalAddressDiscovered { address, .. }
                if address == report.address
        ));
        assert_eq!(observed_address_reports.len(), 1);
        assert_eq!(
            discovery_manager
                .lock()
                .get_discovery_status(local_peer_id)
                .expect("discovery status")
                .statistics
                .server_reflexive_candidates_found,
            1
        );

        NatTraversalEndpoint::process_observed_address_report(
            &discovery_manager,
            &discovery_state_notify,
            &mut observed_address_reports,
            &event_tx,
            &connections,
            None,
            None,
            &traversal_event_notify,
            local_peer_id,
            &relay_setup_attempted,
            report,
        )
        .await;
        assert!(
            event_rx.try_recv().is_err(),
            "duplicate reports must be suppressed"
        );
    }

    /// Test that transport listener handles field is properly initialized
    /// This verifies Phase 1.2 infrastructure: field exists and is empty by default
    #[tokio::test]
    async fn test_transport_listener_handles_initialized() {
        // Create config without transport registry
        let config = NatTraversalConfig {
            transport_registry: None,
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };

        // Create endpoint without registry
        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        // Verify handles field exists and is empty when no registry provided
        let handles = endpoint.transport_listener_handles.lock();
        assert!(
            handles.is_empty(),
            "Should have no listener tasks when no transport registry provided"
        );

        drop(handles);
        endpoint.shutdown().await.expect("Shutdown should succeed");
    }

    /// Test that shutdown properly handles empty transport listener handles
    #[tokio::test]
    async fn test_shutdown_with_no_transport_listeners() {
        let config = NatTraversalConfig {
            transport_registry: None,
            bind_addr: Some("127.0.0.1:0".parse().unwrap()),
            ..Default::default()
        };

        let endpoint = NatTraversalEndpoint::new(config, None, None)
            .await
            .expect("Endpoint creation should succeed");

        // Shutdown should succeed even with no transport listeners
        endpoint
            .shutdown()
            .await
            .expect("Shutdown should succeed with no listeners");

        // Verify handles remain empty after shutdown
        let handles = endpoint.transport_listener_handles.lock();
        assert!(
            handles.is_empty(),
            "Handles should remain empty after shutdown"
        );
    }
}
