use ed25519::pkcs8::EncodePrivateKey;
use fastcrypto::ed25519::Ed25519PrivateKey;
use rcgen::{CertificateParams, KeyPair};
use rustls::pki_types::{CertificateDer, PrivateKeyDer};

pub struct SelfSignedCertificate {
    inner: rcgen::Certificate,
    key: KeyPair,
}

impl SelfSignedCertificate {
    pub fn new(private_key: Ed25519PrivateKey, server_name: &str) -> Self {
        let (cert, key) = generate_self_signed_tls_certificate(private_key, server_name);
        Self { inner: cert, key }
    }

    pub fn rustls_certificate(&self) -> CertificateDer<'static> {
        self.inner.der().to_owned()
    }

    pub fn rustls_private_key(&self) -> PrivateKeyDer<'static> {
        PrivateKeyDer::Pkcs8(self.key.serialize_der().into())
    }

    pub fn reqwest_identity(&self) -> reqwest::tls::Identity {
        let pem = self.inner.pem() + &self.key.serialize_pem();
        reqwest::tls::Identity::from_pem(pem.as_ref()).unwrap()
    }

    pub fn reqwest_certificate(&self) -> reqwest::tls::Certificate {
        reqwest::tls::Certificate::from_der(self.inner.der()).unwrap()
    }
}

fn generate_self_signed_tls_certificate(
    private_key: Ed25519PrivateKey,
    server_name: &str,
) -> (rcgen::Certificate, KeyPair) {
    let keypair = ed25519::KeypairBytes {
        secret_key: private_key.0.to_bytes(),
        // ring cannot handle the optional public key that would be legal der here
        // that is, ring expects PKCS#8 v.1
        public_key: None,
    };

    let pkcs8 = keypair.to_pkcs8_der().unwrap();
    let key_der = PrivateKeyDer::Pkcs8(pkcs8.as_bytes().to_vec().into());
    let keypair = KeyPair::from_der_and_sign_algo(&key_der, &rcgen::PKCS_ED25519).unwrap();

    (generate_cert(&keypair, server_name), keypair)
}

fn generate_cert(keypair: &KeyPair, server_name: &str) -> rcgen::Certificate {
    CertificateParams::new(vec![server_name.to_owned()]).unwrap().self_signed(keypair).expect(
        "unreachable! from_params should only fail if the key is incompatible with params.algo",
    )
}
