//! # serve_tls — production in-process TLS listener (deploy spec §5.4)
//!
//! rustls Phase 1: BYO-cert via `RUTSTER_TLS_CERT` / `RUTSTER_TLS_KEY`
//! (paths to PEM files). When BOTH are set, `main.rs` switches from
//! `rutster::serve::serve_with_nodelay` (plan A's plaintext path) to
//! this helper. Hot-reload: `RustlsConfig::reload_from_pem_file` is
//! called by the operator's SIGHUP signal (Task 4); a `notify`-crate
//! file-watcher is explicitly deferred (would add a 3rd new dep to
//! this slice — out of scope). `RustlsConfig::reload_from_pem_file` is
//! verified atomic (arc-swap) so live WS connections are NEVER dropped
//! during reload.
//!
//! ## Why a TLS path at all when Caddy is the default edge?
//!
//! The shipped artifact default IS Caddy (deploy spec §3.2). The TLS
//! path exists for two operators: the VPS-forwarder topology where the
//! engine terminates TLS at home (deploy spec §3.5 Tier 3 — the
//! forwarder is a dumb TCP relay and cannot terminate TLS), and the
//! operator who already runs nginx/HAProxy. Both have the same
//! requirement: the engine MUST terminate TLS itself, OR it must accept
//! plaintext behind a TLS-terminating edge. See the full topology
//! decision brief at
//! docs/superpowers/specs/2026-07-05-tls-edge-decision-brief.md.
//!
//! ## Why axum-server and not axum-0.8-native TLS?
//!
//! Axum 0.7.9 (locked) does not ship a TLS listener. The successor
//! axum 0.8 has `axum::serve(...).tls_config(...)` but bumping axum
//! 0.7.9 → 0.8 is a workspace-wide migration across `axum::extract::*`,
//! `axum::routing::*`, and `axum::Server` — out of scope for this
//! deploy slice. `axum-server 0.8` composes with `axum::Router` (its
//! `serve` takes `app.into_make_service()`) without forcing an axum
//! version bump. This keeps the slice contained.
//!
//! ## External references
//!
//! - axum-server `Handle`: <https://docs.rs/axum-server/0.8.0/axum_server/handle/struct.Handle.html>
//! - axum-server `RustlsConfig`: <https://docs.rs/axum-server/0.8.0/axum_server/tls_rustls/struct.RustlsConfig.html>
//! - axum-server rustls example: <https://github.com/programatik29/axum-server/blob/v0.8.0/examples/rustls_session.rs>

use std::future::Future;
use std::net::SocketAddr;
use std::time::Duration;

use axum::Router;
use axum_server::Handle;
use axum_server::tls_rustls::{RustlsAcceptor, RustlsConfig};

use crate::tls_acceptor::NodeLayAcceptor;

/// Serve `app` bound to `addr` with rustls TLS using `rustls_config`,
/// TCP_NODELAY on every accepted socket (via `NodeLayAcceptor`), and
/// graceful shutdown via the `shutdown` future (driving
/// `Handle::graceful_shutdown` with a 60 s cap).
pub async fn serve_tls_with_nodelay<F>(
    addr: SocketAddr,
    rustls_config: RustlsConfig,
    app: Router,
    shutdown: F,
) -> std::io::Result<()>
where
    F: Future<Output = ()> + Send + 'static,
{
    let handle = Handle::new();
    let handle_for_shutdown = handle.clone();
    tokio::spawn(async move {
        shutdown.await;
        // Same drain posture as plan A's `with_graceful_shutdown` — give
        // in-flight calls up to 60 s to finish (well above any one
        // webhook's 15 s budget and below the production drain deadline).
        // The hard cap matches the hot-path "drop + observe" policy:
        // graceful is best-effort, the hard stop is the floor.
        handle_for_shutdown.graceful_shutdown(Some(Duration::from_secs(60)));
    });

    let acceptor = NodeLayAcceptor::new(RustlsAcceptor::new(rustls_config));
    axum_server::bind(addr)
        .acceptor(acceptor)
        .handle(handle)
        .serve(app.into_make_service())
        .await
}
