use log::{debug, warn};
use sha2::{Sha224, Sha256, Sha384, Sha512, Sha512Trunc224, Sha512Trunc256};
use super::{DigestCreate, DigestPart, DigestVerify};
fn create(digest: &mut impl sha2::Digest, input: &[u8]) -> String {
    digest.update(input);
    base64::encode(&digest.finalize_reset())
}
fn verify(digest: &mut impl sha2::Digest, name: &str, parts: &[DigestPart], bytes: &[u8]) -> bool {
    if let Some(part) = parts
        .iter()
        .find(|p| p.algorithm.to_lowercase() == name.to_lowercase())
    {
        debug!("Verifying digest type, {}", name);
        digest.update(bytes);
        let encoded = base64::encode(&digest.finalize_reset());
        return part.digest == encoded;
    }
    warn!("No matching digest algorithm found for {}", name);
    warn!(
        "Provided: [{}]",
        parts.iter().fold(String::new(), |mut acc, item| {
            if acc.is_empty() {
            } else {
                acc.push_str(", ");
            }
            acc.push_str(&item.algorithm);
            acc
        })
    );
    false
}
impl DigestCreate for Sha224 {
    const NAME: &'static str = "SHA-224";
    fn compute(&mut self, input: &[u8]) -> String {
        create(self, input)
    }
}
impl DigestVerify for Sha224 {
    fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
        verify(self, <Self as DigestCreate>::NAME, parts, bytes)
    }
}
impl DigestCreate for Sha256 {
    const NAME: &'static str = "SHA-256";
    fn compute(&mut self, input: &[u8]) -> String {
        create(self, input)
    }
}
impl DigestVerify for Sha256 {
    fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
        verify(self, <Self as DigestCreate>::NAME, parts, bytes)
    }
}
impl DigestCreate for Sha384 {
    const NAME: &'static str = "SHA-384";
    fn compute(&mut self, input: &[u8]) -> String {
        create(self, input)
    }
}
impl DigestVerify for Sha384 {
    fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
        verify(self, <Self as DigestCreate>::NAME, parts, bytes)
    }
}
impl DigestCreate for Sha512 {
    const NAME: &'static str = "SHA-512";
    fn compute(&mut self, input: &[u8]) -> String {
        create(self, input)
    }
}
impl DigestVerify for Sha512 {
    fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
        verify(self, <Self as DigestCreate>::NAME, parts, bytes)
    }
}
impl DigestCreate for Sha512Trunc224 {
    const NAME: &'static str = "SHA-512-224";
    fn compute(&mut self, input: &[u8]) -> String {
        create(self, input)
    }
}
impl DigestVerify for Sha512Trunc224 {
    fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
        verify(self, <Self as DigestCreate>::NAME, parts, bytes)
    }
}
impl DigestCreate for Sha512Trunc256 {
    const NAME: &'static str = "SHA-512-256";
    fn compute(&mut self, input: &[u8]) -> String {
        create(self, input)
    }
}
impl DigestVerify for Sha512Trunc256 {
    fn verify(&mut self, parts: &[DigestPart], bytes: &[u8]) -> bool {
        verify(self, <Self as DigestCreate>::NAME, parts, bytes)
    }
}