//! # tls_acceptor — TCP_NODELAY parity for the TLS accept path
//! (deploy slice C §5.4)
//!
//! `axum_server::bind_rustls(addr, config)` exposes NO `tcp_nodelay`
//! setting — neither on the `Server` builder nor on `bind_rustls`
//! (verified in `axum-server 0.8/src/axum_server/server.rs:32-360` and
//! documented at <https://docs.rs/axum-server/0.8.0/axum_server/>).
//! Plan A made TCP_NODELAY unconditional on the plaintext listener
//! (`axum::serve(...).tcp_nodelay(true)`); the same option must apply
//! to the TLS listen path, or a VPS-forwarder topology serving WSS
//! directly to Twilio would re-introduce the Nagle + delayed-ACK stalls
//! that the plaintext NODELAY closed (axum #2521 / #1961).
//!
//! The canonical pattern is a custom `Accept` impl — axum-server's own
//! `examples/rustls_session.rs` documents this wrapping shape, although
//! for session resumption rather than NODELAY. `Accept<TcpStream, S>`
//! runs BEFORE the TLS handshake, so that is the moment to call
//! `stream.set_nodelay(true)`. The wrapper delegates everything else to
//! the inner `RustlsAcceptor`.

use axum_server::accept::{Accept, DefaultAcceptor};
use axum_server::tls_rustls::RustlsAcceptor;
use tokio::net::TcpStream;

/// `Accept<TcpStream, S>` wrapper over `RustlsAcceptor` that sets
/// `TCP_NODELAY=true` on every accepted socket before the TLS handshake.
///
/// The generic parameter `A` is the *inner* acceptor wrapped by
/// `RustlsAcceptor`. `RustlsAcceptor<A = DefaultAcceptor>` is the
/// concrete type produced by `RustlsAcceptor::new(config)`; keeping the
/// generic here matches axum-server's API and makes the newtype usable
/// with any inner acceptor if we later need to stack another layer.
///
/// `Clone` is required by `axum_server::Server::serve`'s bounds
/// (`Acc: Accept<...> + Clone + Send + Sync + 'static`).
/// `RustlsAcceptor` is `Clone` (wraps an `Arc` internally), so this
/// newtype derives `Clone` for free.
#[derive(Clone)]
pub struct NodeLayAcceptor<A = DefaultAcceptor>(RustlsAcceptor<A>);

impl<A> NodeLayAcceptor<A> {
    /// Wrap a `RustlsAcceptor`. Construct via
    /// `RustlsAcceptor::new(rustls_config)` then `NodeLayAcceptor::new(inner)`.
    pub fn new(inner: RustlsAcceptor<A>) -> Self {
        Self(inner)
    }
}

/// `Accept` for the `TcpStream`-bound server path (the only path we
/// register this acceptor on — `axum_server::bind(SocketAddr)` whose
/// `Address::Stream = TcpStream`).
///
/// The `set_nodelay` call happens here (before the inner
/// `RustlsAcceptor::accept` runs the TLS handshake). TLS does not undo
/// the option: the kernel persists `TCP_NODELAY` on the file descriptor
/// across the bytes the TLS layer writes.
impl<A, S> Accept<TcpStream, S> for NodeLayAcceptor<A>
where
    S: Clone + Send + Sync + 'static,
    RustlsAcceptor<A>: Accept<TcpStream, S>,
{
    type Stream = <RustlsAcceptor<A> as Accept<TcpStream, S>>::Stream;
    type Service = <RustlsAcceptor<A> as Accept<TcpStream, S>>::Service;
    type Future = <RustlsAcceptor<A> as Accept<TcpStream, S>>::Future;

    fn accept(&self, stream: TcpStream, service: S) -> Self::Future {
        // Parity with plan A's serve_with_nodelay (deploy spec §5.1). Best
        // effort: if the option fails (rare — the kernel never rejects it),
        // log + continue. Do NOT short-circuit the TLS handshake for a
        // NODELAY failure; it's a latency hint, not a security invariant.
        if let Err(e) = stream.set_nodelay(true) {
            tracing::warn!(error = %e, "set_nodelay on TLS accept failed; continuing");
        }
        self.0.accept(stream, service)
    }
}
