//! Build mTLS server/client configs for the federation endpoint.
//!
//! Federation uses a separate CA from the public-facing QUIC endpoint.
//! Both server and client present certificates; the server verifies the client
//! cert is signed by the federation CA.

use std::path::Path;
use std::sync::Arc;

use anyhow::Context;
use quinn::ServerConfig;
use quinn_proto::crypto::rustls::QuicServerConfig;
use rustls::pki_types::{CertificateDer, PrivateKeyDer};
use rustls::version::TLS13;

/// Build a QUIC server config for the federation listener with mutual TLS.
///
/// `cert`/`key`: this server's federation certificate and private key.
/// `ca`: the federation CA certificate used to verify peer certificates.
pub fn build_federation_server_config(
    cert_path: &Path,
    key_path: &Path,
    ca_path: &Path,
) -> anyhow::Result<ServerConfig> {
    let cert_bytes = std::fs::read(cert_path)
        .with_context(|| format!("read federation cert: {:?}", cert_path))?;
    let key_bytes = std::fs::read(key_path)
        .with_context(|| format!("read federation key: {:?}", key_path))?;
    let ca_bytes = std::fs::read(ca_path)
        .with_context(|| format!("read federation CA: {:?}", ca_path))?;

    let cert_chain = vec![CertificateDer::from(cert_bytes)];
    let key = PrivateKeyDer::try_from(key_bytes)
        .map_err(|_| anyhow::anyhow!("invalid federation private key"))?;

    // Build a root cert store with the federation CA for client verification.
    let mut root_store = rustls::RootCertStore::empty();
    root_store
        .add(CertificateDer::from(ca_bytes))
        .context("add federation CA to root store")?;

    let client_verifier = rustls::server::WebPkiClientVerifier::builder(Arc::new(root_store))
        .build()
        .context("build client cert verifier")?;

    let mut tls = rustls::ServerConfig::builder_with_protocol_versions(&[&TLS13])
        .with_client_cert_verifier(client_verifier)
        .with_single_cert(cert_chain, key)?;
    tls.alpn_protocols = vec![b"quicproquo/federation/1".to_vec()];

    let crypto = QuicServerConfig::try_from(tls)
        .map_err(|e| anyhow::anyhow!("invalid federation server TLS config: {e}"))?;

    Ok(ServerConfig::with_crypto(Arc::new(crypto)))
}

/// Build a QUIC client config for connecting to a federation peer with mutual TLS.
pub fn build_federation_client_config(
    cert_path: &Path,
    key_path: &Path,
    ca_path: &Path,
) -> anyhow::Result<rustls::ClientConfig> {
    let cert_bytes = std::fs::read(cert_path)
        .with_context(|| format!("read federation cert: {:?}", cert_path))?;
    let key_bytes = std::fs::read(key_path)
        .with_context(|| format!("read federation key: {:?}", key_path))?;
    let ca_bytes = std::fs::read(ca_path)
        .with_context(|| format!("read federation CA: {:?}", ca_path))?;

    let cert_chain = vec![CertificateDer::from(cert_bytes)];
    let key = PrivateKeyDer::try_from(key_bytes)
        .map_err(|_| anyhow::anyhow!("invalid federation client private key"))?;

    let mut root_store = rustls::RootCertStore::empty();
    root_store
        .add(CertificateDer::from(ca_bytes))
        .context("add federation CA to root store")?;

    let tls = rustls::ClientConfig::builder_with_protocol_versions(&[&TLS13])
        .with_root_certificates(root_store)
        .with_client_auth_cert(cert_chain, key)
        .context("set client auth cert")?;

    Ok(tls)
}
