use std::cmp::{Ordering, min};
use std::collections::{BTreeMap, BTreeSet};
use std::net::IpAddr;
use std::path::Path;
use std::sync::Arc;
use std::time::{Duration, SystemTime, UNIX_EPOCH};

use clap::Parser;
use futures::FutureExt;
use imbl_value::InternedString;
use libc::time_t;
use openssl::asn1::{Asn1Integer, Asn1Time, Asn1TimeRef};
use openssl::bn::{BigNum, MsbOption};
use openssl::ec::{EcGroup, EcKey};
use openssl::error::ErrorStack;
use openssl::hash::MessageDigest;
use openssl::pkey::{PKey, Private};
use openssl::x509::extension::{
    AuthorityKeyIdentifier, BasicConstraints, KeyUsage, SubjectAlternativeName,
    SubjectKeyIdentifier,
};
use openssl::x509::{X509, X509Builder, X509NameBuilder, X509Ref};
use openssl::*;
use patch_db::HasModel;
use rpc_toolkit::{Context, HandlerExt, ParentHandler, from_fn_async};
use serde::{Deserialize, Serialize};
use tokio_rustls::rustls::ServerConfig;
use tokio_rustls::rustls::crypto::CryptoProvider;
use tokio_rustls::rustls::pki_types::{PrivateKeyDer, PrivatePkcs8KeyDer};
use tokio_rustls::rustls::server::ClientHello;
use tracing::instrument;
use ts_rs::TS;
use visit_rs::Visit;

use crate::SOURCE_DATE;
use crate::account::AccountInfo;
use crate::context::{CliContext, RpcContext};
use crate::db::model::Database;
use crate::db::{DbAccess, DbAccessMut};
use crate::hostname::ServerHostname;
use crate::init::check_time_is_synchronized;
use crate::net::gateway::GatewayInfo;
use crate::net::tls::{TlsHandler, TlsHandlerAction};
use crate::net::web_server::{Accept, ExtractVisitor, TcpMetadata, extract};
use crate::prelude::*;
use crate::util::serde::{HandlerExtSerde, Pem};

pub fn should_use_cert(cert: &X509Ref) -> Result<bool, ErrorStack> {
    Ok(cert
        .not_before()
        .compare(Asn1Time::days_from_now(0)?.as_ref())?
        == Ordering::Less
        && cert
            .not_after()
            .compare(Asn1Time::days_from_now(30)?.as_ref())?
            == Ordering::Greater)
}

#[derive(Debug, Deserialize, Serialize, HasModel)]
#[model = "Model<Self>"]
#[serde(rename_all = "camelCase")]
pub struct CertStore {
    pub root_key: Pem<PKey<Private>>,
    pub root_cert: Pem<X509>,
    pub int_key: Pem<PKey<Private>>,
    pub int_cert: Pem<X509>,
    pub leaves: BTreeMap<JsonKey<BTreeSet<InternedString>>, CertData>,
}
impl CertStore {
    pub fn new(account: &AccountInfo) -> Result<Self, Error> {
        let int_key = gen_nistp256()?;
        let int_cert = make_int_cert((&account.root_ca_key, &account.root_ca_cert), &int_key)?;
        Ok(Self {
            root_key: Pem::new(account.root_ca_key.clone()),
            root_cert: Pem::new(account.root_ca_cert.clone()),
            int_key: Pem::new(int_key),
            int_cert: Pem::new(int_cert),
            leaves: BTreeMap::new(),
        })
    }
}
impl Model<CertStore> {
    /// This function will grant any cert for any domain. It is up to the *caller* to enusure that the calling service has permission to sign a cert for the requested domain
    pub fn cert_for(
        &mut self,
        hostnames: &BTreeSet<InternedString>,
    ) -> Result<FullchainCertData, Error> {
        let keys = if let Some(cert_data) = self
            .as_leaves()
            .as_idx(JsonKey::new_ref(hostnames))
            .map(|m| m.de())
            .transpose()?
        {
            if should_use_cert(&cert_data.certs.ed25519)?
                && should_use_cert(&cert_data.certs.nistp256)?
            {
                return Ok(FullchainCertData {
                    root: self.as_root_cert().de()?.0,
                    int: self.as_int_cert().de()?.0,
                    leaf: cert_data,
                });
            }
            cert_data.keys
        } else {
            PKeyPair {
                ed25519: PKey::generate_ed25519()?,
                nistp256: gen_nistp256()?,
            }
        };
        let int_key = self.as_int_key().de()?.0;
        let int_cert = self.as_int_cert().de()?.0;
        let cert_data = CertData {
            certs: CertPair {
                ed25519: make_leaf_cert(
                    (&int_key, &int_cert),
                    (&keys.ed25519, &SANInfo::new(hostnames)),
                )?,
                nistp256: make_leaf_cert(
                    (&int_key, &int_cert),
                    (&keys.nistp256, &SANInfo::new(hostnames)),
                )?,
            },
            keys,
        };
        self.as_leaves_mut()
            .insert(JsonKey::new_ref(hostnames), &cert_data)?;
        Ok(FullchainCertData {
            root: self.as_root_cert().de()?.0,
            int: self.as_int_cert().de()?.0,
            leaf: cert_data,
        })
    }
}
impl DbAccess<CertStore> for Database {
    fn access<'a>(db: &'a Model<Self>) -> &'a Model<CertStore> {
        db.as_private().as_key_store().as_local_certs()
    }
}
impl DbAccessMut<CertStore> for Database {
    fn access_mut<'a>(db: &'a mut Model<Self>) -> &'a mut Model<CertStore> {
        db.as_private_mut().as_key_store_mut().as_local_certs_mut()
    }
}

#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
pub struct CertData {
    pub keys: PKeyPair,
    pub certs: CertPair,
}
impl CertData {
    pub fn expiration(&self) -> Result<SystemTime, Error> {
        self.certs.expiration()
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FullchainCertData {
    pub root: X509,
    pub int: X509,
    pub leaf: CertData,
}
impl FullchainCertData {
    pub fn fullchain_ed25519(&self) -> Vec<&X509> {
        vec![&self.leaf.certs.ed25519, &self.int, &self.root]
    }
    pub fn fullchain_nistp256(&self) -> Vec<&X509> {
        vec![&self.leaf.certs.nistp256, &self.int, &self.root]
    }
    pub fn expiration(&self) -> Result<SystemTime, Error> {
        [
            asn1_time_to_system_time(self.root.not_after())?,
            asn1_time_to_system_time(self.int.not_after())?,
            self.leaf.expiration()?,
        ]
        .into_iter()
        .min()
        .ok_or_else(|| Error::new(eyre!("{}", t!("net.ssl.unreachable")), ErrorKind::Unknown))
    }
}

static CERTIFICATE_VERSION: i32 = 2; // X509 version 3 is actually encoded as '2' in the cert because fuck you.

fn unix_time(time: SystemTime) -> time_t {
    time.duration_since(UNIX_EPOCH)
        .map(|d| d.as_secs() as time_t)
        .or_else(|_| UNIX_EPOCH.elapsed().map(|d| -(d.as_secs() as time_t)))
        .unwrap_or_default()
}

lazy_static::lazy_static! {
    static ref ASN1_UNIX_EPOCH: Asn1Time = Asn1Time::from_unix(0).unwrap();
}

fn asn1_time_to_system_time(time: &Asn1TimeRef) -> Result<SystemTime, Error> {
    let diff = ASN1_UNIX_EPOCH.diff(time)?;
    let mut res = UNIX_EPOCH;
    if diff.days >= 0 {
        res += Duration::from_secs(diff.days as u64 * 86400);
    } else {
        res -= Duration::from_secs((-1 * diff.days) as u64 * 86400);
    }
    if diff.secs >= 0 {
        res += Duration::from_secs(diff.secs as u64);
    } else {
        res -= Duration::from_secs((-1 * diff.secs) as u64);
    }
    Ok(res)
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct PKeyPair {
    #[serde(with = "crate::util::serde::pem")]
    pub ed25519: PKey<Private>,
    #[serde(with = "crate::util::serde::pem")]
    pub nistp256: PKey<Private>,
}
impl PartialEq for PKeyPair {
    fn eq(&self, other: &Self) -> bool {
        self.ed25519.public_eq(&other.ed25519) && self.nistp256.public_eq(&other.nistp256)
    }
}
impl Eq for PKeyPair {}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
pub struct CertPair {
    #[serde(with = "crate::util::serde::pem")]
    pub ed25519: X509,
    #[serde(with = "crate::util::serde::pem")]
    pub nistp256: X509,
}
impl CertPair {
    pub fn expiration(&self) -> Result<SystemTime, Error> {
        Ok(min(
            asn1_time_to_system_time(self.ed25519.not_after())?,
            asn1_time_to_system_time(self.nistp256.not_after())?,
        ))
    }
}

pub async fn root_ca_start_time() -> SystemTime {
    if check_time_is_synchronized()
        .await
        .log_err()
        .unwrap_or(false)
    {
        SystemTime::now()
    } else {
        *SOURCE_DATE
    }
}

const EC_CURVE_NAME: nid::Nid = nid::Nid::X9_62_PRIME256V1;
lazy_static::lazy_static! {
    static ref EC_GROUP: EcGroup = EcGroup::from_curve_name(EC_CURVE_NAME).unwrap();
}

pub async fn export_key(key: &PKey<Private>, target: &Path) -> Result<(), Error> {
    tokio::fs::write(target, key.private_key_to_pem_pkcs8()?)
        .map(|res| res.with_ctx(|_| (ErrorKind::Filesystem, target.display().to_string())))
        .await?;
    Ok(())
}
pub async fn export_cert(chain: &[&X509], target: &Path) -> Result<(), Error> {
    tokio::fs::write(
        target,
        chain
            .into_iter()
            .flat_map(|c| c.to_pem().unwrap())
            .collect::<Vec<u8>>(),
    )
    .await?;
    Ok(())
}

#[instrument(skip_all)]
fn rand_serial() -> Result<Asn1Integer, Error> {
    let mut bn = BigNum::new()?;
    bn.rand(64, MsbOption::MAYBE_ZERO, false)?;
    let asn1 = Asn1Integer::from_bn(&bn)?;
    Ok(asn1)
}
#[instrument(skip_all)]
pub fn gen_nistp256() -> Result<PKey<Private>, Error> {
    Ok(PKey::from_ec_key(EcKey::generate(EC_GROUP.as_ref())?)?)
}

#[instrument(skip_all)]
pub fn make_root_cert(
    root_key: &PKey<Private>,
    hostname: &ServerHostname,
    start_time: SystemTime,
) -> Result<X509, Error> {
    let mut builder = X509Builder::new()?;
    builder.set_version(CERTIFICATE_VERSION)?;

    let unix_start_time = unix_time(start_time);

    let embargo = Asn1Time::from_unix(unix_start_time - 86400)?;
    builder.set_not_before(&embargo)?;

    let expiration = Asn1Time::from_unix(unix_start_time + (10 * 364 * 86400))?;
    builder.set_not_after(&expiration)?;

    builder.set_serial_number(&*rand_serial()?)?;

    let mut subject_name_builder = X509NameBuilder::new()?;
    subject_name_builder
        .append_entry_by_text("CN", &format!("{} Local Root CA", hostname.as_ref()))?;
    subject_name_builder.append_entry_by_text("O", "Start9")?;
    subject_name_builder.append_entry_by_text("OU", "StartOS")?;
    let subject_name = subject_name_builder.build();
    builder.set_subject_name(&subject_name)?;

    builder.set_issuer_name(&subject_name)?;

    builder.set_pubkey(&root_key)?;

    // Extensions
    let cfg = conf::Conf::new(conf::ConfMethod::default())?;
    let ctx = builder.x509v3_context(None, Some(&cfg));
    // subjectKeyIdentifier = hash
    let subject_key_identifier = SubjectKeyIdentifier::new().build(&ctx)?;
    // authorityKeyIdentifier = keyid,issuer:always
    let authority_key_identifier = AuthorityKeyIdentifier::new()
        .keyid(false)
        .issuer(true)
        .build(&ctx)?;
    // basicConstraints = critical, CA:true, pathlen:0
    let basic_constraints = BasicConstraints::new().critical().ca().build()?;
    // keyUsage = critical, digitalSignature, cRLSign, keyCertSign
    let key_usage = KeyUsage::new()
        .critical()
        .digital_signature()
        .crl_sign()
        .key_cert_sign()
        .build()?;
    builder.append_extension(subject_key_identifier)?;
    builder.append_extension(authority_key_identifier)?;
    builder.append_extension(basic_constraints)?;
    builder.append_extension(key_usage)?;
    builder.sign(&root_key, MessageDigest::sha256())?;
    let cert = builder.build();
    Ok(cert)
}
#[instrument(skip_all)]
pub fn make_int_cert(
    signer: (&PKey<Private>, &X509),
    applicant: &PKey<Private>,
) -> Result<X509, Error> {
    let mut builder = X509Builder::new()?;
    builder.set_version(CERTIFICATE_VERSION)?;

    builder.set_not_before(signer.1.not_before())?;

    builder.set_not_after(signer.1.not_after())?;

    builder.set_serial_number(&*rand_serial()?)?;

    let mut subject_name_builder = X509NameBuilder::new()?;
    subject_name_builder.append_entry_by_text("CN", "StartOS Local Intermediate CA")?;
    subject_name_builder.append_entry_by_text("O", "Start9")?;
    subject_name_builder.append_entry_by_text("OU", "StartOS")?;
    let subject_name = subject_name_builder.build();
    builder.set_subject_name(&subject_name)?;

    builder.set_issuer_name(signer.1.subject_name())?;

    builder.set_pubkey(&applicant)?;

    let cfg = conf::Conf::new(conf::ConfMethod::default())?;
    let ctx = builder.x509v3_context(Some(&signer.1), Some(&cfg));

    // subjectKeyIdentifier = hash
    let subject_key_identifier = SubjectKeyIdentifier::new().build(&ctx)?;
    // authorityKeyIdentifier = keyid:always,issuer:always
    let authority_key_identifier = AuthorityKeyIdentifier::new()
        .keyid(true)
        .issuer(true)
        .build(&ctx)?;
    // basicConstraints = critical, CA:true, pathlen:0
    let basic_constraints = BasicConstraints::new().critical().ca().pathlen(0).build()?;
    // keyUsage = critical, digitalSignature, cRLSign, keyCertSign
    let key_usage = KeyUsage::new()
        .critical()
        .digital_signature()
        .crl_sign()
        .key_cert_sign()
        .build()?;
    builder.append_extension(subject_key_identifier)?;
    builder.append_extension(authority_key_identifier)?;
    builder.append_extension(basic_constraints)?;
    builder.append_extension(key_usage)?;
    builder.sign(&signer.0, MessageDigest::sha256())?;
    let cert = builder.build();
    Ok(cert)
}

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum MaybeWildcard {
    WithWildcard(String),
    WithoutWildcard(InternedString),
}
impl MaybeWildcard {
    pub fn as_str(&self) -> &str {
        match self {
            MaybeWildcard::WithWildcard(s) => s.as_str(),
            MaybeWildcard::WithoutWildcard(s) => &**s,
        }
    }
}
impl std::fmt::Display for MaybeWildcard {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            MaybeWildcard::WithWildcard(dns) => write!(f, "DNS:{dns},DNS:*.{dns}"),
            MaybeWildcard::WithoutWildcard(dns) => write!(f, "DNS:{dns}"),
        }
    }
}

#[derive(Debug)]
pub struct SANInfo {
    pub dns: BTreeSet<MaybeWildcard>,
    pub ips: BTreeSet<IpAddr>,
}
impl SANInfo {
    pub fn new(hostnames: &BTreeSet<InternedString>) -> Self {
        let mut dns = BTreeSet::new();
        let mut ips = BTreeSet::new();
        for hostname in hostnames {
            if let Ok(ip) = hostname.parse::<IpAddr>() {
                ips.insert(ip);
            } else {
                dns.insert(MaybeWildcard::WithoutWildcard(hostname.clone())); // TODO: wildcards?
            }
        }
        Self { dns, ips }
    }
    pub fn x509_extension(&self) -> SubjectAlternativeName {
        let mut san = SubjectAlternativeName::new();
        for h in &self.dns {
            san.dns(&h.as_str());
        }
        for ip in &self.ips {
            san.ip(&ip.to_string());
        }
        san
    }
}
impl std::fmt::Display for SANInfo {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut written = false;
        for dns in &self.dns {
            if written {
                write!(f, ",")?;
            }
            written = true;
            write!(f, "{dns}")?;
        }
        for ip in &self.ips {
            if written {
                write!(f, ",")?;
            }
            written = true;
            write!(f, "IP:{ip}")?;
        }
        Ok(())
    }
}

#[instrument(skip_all)]
pub fn make_leaf_cert(
    signer: (&PKey<Private>, &X509),
    applicant: (&PKey<Private>, &SANInfo),
) -> Result<X509, Error> {
    let mut builder = X509Builder::new()?;
    builder.set_version(CERTIFICATE_VERSION)?;

    let embargo = Asn1Time::from_unix(unix_time(SystemTime::now()) - 86400)?;
    builder.set_not_before(&embargo)?;

    // Google Apple and Mozilla reject certificate horizons longer than 398 days
    // https://techbeacon.com/security/google-apple-mozilla-enforce-1-year-max-security-certifications
    let expiration = Asn1Time::days_from_now(397)?;
    builder.set_not_after(&expiration)?;

    builder.set_serial_number(&*rand_serial()?)?;

    let mut subject_name_builder = X509NameBuilder::new()?;
    subject_name_builder.append_entry_by_text(
        "CN",
        applicant
            .1
            .dns
            .first()
            .map(MaybeWildcard::as_str)
            .unwrap_or("localhost"),
    )?;
    subject_name_builder.append_entry_by_text("O", "Start9")?;
    subject_name_builder.append_entry_by_text("OU", "StartOS")?;
    let subject_name = subject_name_builder.build();
    builder.set_subject_name(&subject_name)?;

    builder.set_issuer_name(signer.1.subject_name())?;

    builder.set_pubkey(&applicant.0)?;

    // Extensions
    let cfg = conf::Conf::new(conf::ConfMethod::default())?;
    let ctx = builder.x509v3_context(Some(&signer.1), Some(&cfg));

    let subject_key_identifier = SubjectKeyIdentifier::new().build(&ctx)?;
    let authority_key_identifier = AuthorityKeyIdentifier::new()
        .keyid(true)
        .issuer(false)
        .build(&ctx)?;
    let subject_alt_name = applicant.1.x509_extension().build(&ctx)?;
    let basic_constraints = BasicConstraints::new().build()?;
    let key_usage = KeyUsage::new()
        .critical()
        .digital_signature()
        .key_encipherment()
        .build()?;

    builder.append_extension(subject_key_identifier)?;
    builder.append_extension(authority_key_identifier)?;
    builder.append_extension(subject_alt_name)?;
    builder.append_extension(basic_constraints)?;
    builder.append_extension(key_usage)?;

    builder.sign(&signer.0, MessageDigest::sha256())?;

    let cert = builder.build();
    Ok(cert)
}

#[instrument(skip_all)]
pub fn make_self_signed(applicant: (&PKey<Private>, &SANInfo)) -> Result<X509, Error> {
    let mut builder = X509Builder::new()?;
    builder.set_version(CERTIFICATE_VERSION)?;

    let embargo = Asn1Time::from_unix(unix_time(SystemTime::now()) - 86400)?;
    builder.set_not_before(&embargo)?;

    // Google Apple and Mozilla reject certificate horizons longer than 398 days
    // https://techbeacon.com/security/google-apple-mozilla-enforce-1-year-max-security-certifications
    let expiration = Asn1Time::days_from_now(397)?;
    builder.set_not_after(&expiration)?;

    builder.set_serial_number(&*rand_serial()?)?;

    let mut subject_name_builder = X509NameBuilder::new()?;
    subject_name_builder.append_entry_by_text(
        "CN",
        applicant
            .1
            .dns
            .first()
            .map(MaybeWildcard::as_str)
            .unwrap_or("localhost"),
    )?;
    subject_name_builder.append_entry_by_text("O", "Start9")?;
    subject_name_builder.append_entry_by_text("OU", "StartOS")?;
    let subject_name = subject_name_builder.build();
    builder.set_subject_name(&subject_name)?;

    builder.set_issuer_name(&subject_name)?;

    builder.set_pubkey(&applicant.0)?;

    // Extensions
    let cfg = conf::Conf::new(conf::ConfMethod::default())?;
    let ctx = builder.x509v3_context(None, Some(&cfg));

    let subject_key_identifier = SubjectKeyIdentifier::new().build(&ctx)?;
    let authority_key_identifier = AuthorityKeyIdentifier::new()
        .keyid(false)
        .issuer(true)
        .build(&ctx)?;
    let subject_alt_name = applicant.1.x509_extension().build(&ctx)?;
    let basic_constraints = BasicConstraints::new().build()?;
    let key_usage = KeyUsage::new()
        .critical()
        .digital_signature()
        .key_encipherment()
        .build()?;

    builder.append_extension(subject_key_identifier)?;
    builder.append_extension(authority_key_identifier)?;
    builder.append_extension(subject_alt_name)?;
    builder.append_extension(basic_constraints)?;
    builder.append_extension(key_usage)?;

    builder.sign(&applicant.0, MessageDigest::sha256())?;

    let cert = builder.build();
    Ok(cert)
}

pub fn ssl_api<C: Context>() -> ParentHandler<C> {
    ParentHandler::new().subcommand(
        "generate-certificate",
        from_fn_async(generate_certificate)
            .with_display_serializable()
            .with_custom_display_fn(|_, res: GenerateCertificateResponse| {
                println!("Private Key:");
                print!("{}", res.key);
                println!("\nCertificate Chain:");
                print!("{}", res.fullchain);
                Ok(())
            })
            .with_about("about.ssl-generate-certificate")
            .with_call_remote::<CliContext>(),
    )
}

#[derive(Debug, Clone, Deserialize, Serialize, Parser, TS)]
#[serde(rename_all = "camelCase")]
#[command(rename_all = "kebab-case")]
#[group(skip)]
#[ts(export)]
pub struct GenerateCertificateParams {
    #[arg(help = "help.arg.hostnames")]
    pub hostnames: Vec<String>,
    #[arg(long, help = "help.arg.ed25519")]
    #[serde(default)]
    pub ed25519: bool,
}

#[derive(Debug, Clone, Deserialize, Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export)]
pub struct GenerateCertificateResponse {
    pub key: String,
    pub fullchain: String,
}

pub async fn generate_certificate(
    ctx: RpcContext,
    GenerateCertificateParams { hostnames, ed25519 }: GenerateCertificateParams,
) -> Result<GenerateCertificateResponse, Error> {
    let peek = ctx.db.peek().await;
    let cert_store = peek.as_private().as_key_store().as_local_certs();
    let int_key = cert_store.as_int_key().de()?.0;
    let int_cert = cert_store.as_int_cert().de()?.0;
    let root_cert = cert_store.as_root_cert().de()?.0;
    drop(peek);

    let hostnames: BTreeSet<InternedString> = hostnames.into_iter().map(InternedString::from).collect();
    let san_info = SANInfo::new(&hostnames);

    let (key, cert) = if ed25519 {
        let key = PKey::generate_ed25519()?;
        let cert = make_leaf_cert((&int_key, &int_cert), (&key, &san_info))?;
        (key, cert)
    } else {
        let key = gen_nistp256()?;
        let cert = make_leaf_cert((&int_key, &int_cert), (&key, &san_info))?;
        (key, cert)
    };

    let key_pem =
        String::from_utf8(key.private_key_to_pem_pkcs8()?).with_kind(ErrorKind::Utf8)?;
    let fullchain_pem = String::from_utf8(
        [&cert, &int_cert, &root_cert]
            .into_iter()
            .map(|c| c.to_pem())
            .collect::<Result<Vec<_>, _>>()?
            .concat(),
    )
    .with_kind(ErrorKind::Utf8)?;

    Ok(GenerateCertificateResponse {
        key: key_pem,
        fullchain: fullchain_pem,
    })
}

pub struct RootCaTlsHandler<M: HasModel> {
    pub db: TypedPatchDb<M>,
    pub crypto_provider: Arc<CryptoProvider>,
}
impl<M: HasModel> Clone for RootCaTlsHandler<M> {
    fn clone(&self) -> Self {
        Self {
            db: self.db.clone(),
            crypto_provider: self.crypto_provider.clone(),
        }
    }
}

impl<'a, A, M> TlsHandler<'a, A> for RootCaTlsHandler<M>
where
    A: Accept + 'a,
    <A as Accept>::Metadata: Visit<ExtractVisitor<TcpMetadata>>
        + Visit<ExtractVisitor<GatewayInfo>>
        + Clone
        + Send
        + Sync
        + 'static,
    M: HasModel<Model = Model<M>> + DbAccessMut<CertStore> + Send + Sync,
{
    async fn get_config(
        &mut self,
        hello: &ClientHello<'_>,
        metadata: &<A as Accept>::Metadata,
    ) -> Option<TlsHandlerAction> {
        let hostnames: BTreeSet<InternedString> = hello
            .server_name()
            .map(InternedString::from)
            .into_iter()
            .chain(
                extract::<TcpMetadata, _>(metadata)
                    .map(|m| m.local_addr.ip())
                    .as_ref()
                    .map(InternedString::from_display),
            )
            .chain(
                extract::<GatewayInfo, _>(metadata)
                    .and_then(|i| i.info.ip_info)
                    .and_then(|i| i.wan_ip)
                    .as_ref()
                    .map(InternedString::from_display),
            )
            .collect();
        let cert = self
            .db
            .mutate(|db| M::access_mut(db).cert_for(&hostnames))
            .await
            .result
            .log_err()?;
        let cfg = ServerConfig::builder_with_provider(self.crypto_provider.clone())
            .with_safe_default_protocol_versions()
            .log_err()?
            .with_no_client_auth();
        if hello
            .signature_schemes()
            .contains(&tokio_rustls::rustls::SignatureScheme::ED25519)
        {
            cfg.with_single_cert(
                cert.fullchain_ed25519()
                    .into_iter()
                    .map(|c| {
                        Ok(tokio_rustls::rustls::pki_types::CertificateDer::from(
                            c.to_der()?,
                        ))
                    })
                    .collect::<Result<_, Error>>()
                    .log_err()?,
                PrivateKeyDer::from(PrivatePkcs8KeyDer::from(
                    cert.leaf.keys.ed25519.private_key_to_pkcs8().log_err()?,
                )),
            )
        } else {
            cfg.with_single_cert(
                cert.fullchain_nistp256()
                    .into_iter()
                    .map(|c| {
                        Ok(tokio_rustls::rustls::pki_types::CertificateDer::from(
                            c.to_der()?,
                        ))
                    })
                    .collect::<Result<_, Error>>()
                    .log_err()?,
                PrivateKeyDer::from(PrivatePkcs8KeyDer::from(
                    cert.leaf.keys.nistp256.private_key_to_pkcs8().log_err()?,
                )),
            )
        }
        .log_err()
        .map(TlsHandlerAction::Tls)
    }
}
