use std::marker::PhantomData;

use aes::cipher::typenum;
use rustls::{
    crypto::cipher::{AeadKey, Iv},
    ConnectionTrafficSecrets,
};

use crate::{
    cipher::TlsAeadCipherAlgorithm,
    traits::{AeadDataInput, TlsExtractKeys},
};

pub static TLS_AES128GCM: TlsAeadCipherAlgorithm<aes_gcm::AesGcm<aes::Aes128, typenum::U12>> =
    TlsAeadCipherAlgorithm(PhantomData);
pub static TLS_AES256GCM: TlsAeadCipherAlgorithm<aes_gcm::AesGcm<aes::Aes256, typenum::U12>> =
    TlsAeadCipherAlgorithm(PhantomData);
pub static TLS_CHACHA20POLY1305: TlsAeadCipherAlgorithm<chacha20poly1305::ChaCha20Poly1305> =
    TlsAeadCipherAlgorithm(PhantomData);

impl TlsExtractKeys for aes_gcm::AesGcm<aes::Aes128, typenum::U12> {
    fn tls13_wrap_keys(key: AeadKey, iv: Iv) -> ConnectionTrafficSecrets {
        ConnectionTrafficSecrets::Aes128Gcm { key, iv }
    }

    fn tls12_wrap_keys(key: AeadKey, iv: &[u8], explicit: &[u8]) -> ConnectionTrafficSecrets {
        let _ = explicit;
        ConnectionTrafficSecrets::Aes128Gcm {
            key,
            iv: Iv::new(iv[..].try_into().unwrap()),
        }
    }
}

impl TlsExtractKeys for aes_gcm::AesGcm<aes::Aes256, typenum::U12> {
    fn tls13_wrap_keys(key: AeadKey, iv: Iv) -> ConnectionTrafficSecrets {
        ConnectionTrafficSecrets::Aes256Gcm { key, iv }
    }

    fn tls12_wrap_keys(key: AeadKey, iv: &[u8], explicit: &[u8]) -> ConnectionTrafficSecrets {
        let _ = explicit;
        ConnectionTrafficSecrets::Aes256Gcm {
            key,
            iv: Iv::new(iv[..].try_into().unwrap()),
        }
    }
}

impl TlsExtractKeys for chacha20poly1305::ChaCha20Poly1305 {
    fn tls13_wrap_keys(key: AeadKey, iv: Iv) -> ConnectionTrafficSecrets {
        ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv }
    }

    fn tls12_wrap_keys(key: AeadKey, iv: &[u8], explicit: &[u8]) -> ConnectionTrafficSecrets {
        let _ = explicit;
        ConnectionTrafficSecrets::Chacha20Poly1305 {
            key,
            iv: Iv::new(iv[..].try_into().unwrap()),
        }
    }
}

impl<Aes> AeadDataInput for aes_gcm::AesGcm<Aes, typenum::U12> {
    const ENCRYPTED_OVERHEAD: usize = 16;
}

impl AeadDataInput for chacha20poly1305::ChaCha20Poly1305 {
    const ENCRYPTED_OVERHEAD: usize = 16;
}
