use crate::{
    sha512::{Sha512Hash, Sha512Hasher, SHA512_LENGTH},
    Hasher,
};

enum Key<'a> {
    Borrowed(&'a [u8]),
    Owned([u8; SHA512_LENGTH]),
}

impl Key<'_> {
    fn get(&self, index: usize) -> Option<u8> {
        match self {
            Key::Borrowed(key) => key.get(index),
            Key::Owned(key) => key.get(index),
        }
        .cloned()
    }
}

pub fn compute_hmac_sha512(key: &[u8], data: &[u8]) -> Sha512Hash {
    let hashed_key = if key.len() > Sha512Hash::block_size() {
        Key::Owned(Sha512Hasher::default().digest(key).into())
    } else {
        Key::Borrowed(key)
    };

    let mut inner_key: Vec<u8> = Vec::with_capacity(Sha512Hash::block_size());
    let mut outer_key: Vec<u8> = Vec::with_capacity(Sha512Hash::block_size());
    for i in 0..Sha512Hash::block_size() {
        let byte: u8 = hashed_key.get(i).unwrap_or(0);
        inner_key.push(0x36 ^ byte);
        outer_key.push(0x5c ^ byte);
    }

    let inner_hash = Sha512Hasher::default()
        .chain(&inner_key)
        .chain(&data)
        .finish();
    Sha512Hasher::default()
        .chain(&outer_key)
        .chain(&inner_hash)
        .finish()
}
