//! TLS configuration helpers for tonic server and client.

use std::fs;

use tonic::transport::{Certificate, ClientTlsConfig, Identity, ServerTlsConfig};

use crate::{config::TlsConfig, error::GrpcTransportError};

/// Build a tonic `ServerTlsConfig` from our TLS config.
///
/// # Errors
///
/// Returns [`GrpcTransportError`] if the server cert, server key, or CA cert
/// file cannot be read.
pub fn server_tls_config(tls: &TlsConfig) -> Result<ServerTlsConfig, GrpcTransportError> {
    let server_cert = fs::read(&tls.server_cert)
        .map_err(|e| GrpcTransportError::TlsConfig(format!("read server cert: {e}")))?;
    let server_key = fs::read(&tls.server_key)
        .map_err(|e| GrpcTransportError::TlsConfig(format!("read server key: {e}")))?;
    let ca_cert = fs::read(&tls.ca_cert)
        .map_err(|e| GrpcTransportError::TlsConfig(format!("read CA cert: {e}")))?;

    let identity = Identity::from_pem(server_cert, server_key);
    let ca = Certificate::from_pem(ca_cert);

    Ok(ServerTlsConfig::new().identity(identity).client_ca_root(ca))
}

/// Build a tonic `ClientTlsConfig` from our TLS config.
///
/// # Errors
///
/// Returns [`GrpcTransportError`] if the CA cert (or, when mutual TLS is
/// configured, the client cert/key) file cannot be read.
pub fn client_tls_config(tls: &TlsConfig) -> Result<ClientTlsConfig, GrpcTransportError> {
    let ca_cert = fs::read(&tls.ca_cert)
        .map_err(|e| GrpcTransportError::TlsConfig(format!("read CA cert: {e}")))?;
    let ca = Certificate::from_pem(ca_cert);

    let mut config = ClientTlsConfig::new().ca_certificate(ca);

    if let (Some(cert_path), Some(key_path)) = (&tls.client_cert, &tls.client_key) {
        let cert = fs::read(cert_path)
            .map_err(|e| GrpcTransportError::TlsConfig(format!("read client cert: {e}")))?;
        let key = fs::read(key_path)
            .map_err(|e| GrpcTransportError::TlsConfig(format!("read client key: {e}")))?;
        config = config.identity(Identity::from_pem(cert, key));
    }

    Ok(config)
}
