use crate::encryption::{
    json_indexer::{
        prefix_mac::{PrefixMac, UpdatePrefixMac},
        ste_vec::encrypted_term::Mac,
    },
    TokenizedSelector,
};

#[cfg_attr(test, derive(Debug, PartialEq, Eq))]
pub(crate) enum MacTerm {
    Bool(bool),
    Null,
    Map,
    Array,
}

impl MacTerm {
    // Note that the info strings are padded to all be the same length
    pub(crate) const INFO_TRUE: &'static str = "TRUE00";
    pub(crate) const INFO_FALSE: &'static str = "FALSE0";
    pub(crate) const INFO_NULL: &'static str = "NULL00";
    pub(crate) const INFO_MAP: &'static str = "MAP0{}";
    pub(crate) const INFO_ARRY: &'static str = "ARRY[]";

    pub(crate) fn as_str(&self) -> &'static str {
        match self {
            MacTerm::Bool(true) => Self::INFO_TRUE,
            MacTerm::Bool(false) => Self::INFO_FALSE,
            MacTerm::Null => Self::INFO_NULL,
            MacTerm::Map => Self::INFO_MAP,
            MacTerm::Array => Self::INFO_ARRY,
        }
    }
}

impl<M> UpdatePrefixMac<MacTerm> for M
where
    for<'u> M: UpdatePrefixMac<&'u str>,
{
    fn update(&mut self, value: MacTerm) {
        self.update(value.as_str());
    }
}

/// Pairs a [`MacTerm`] with the tokenized selector it lives under, ready to
/// be MAC'd into a leaf-term ciphertext. Mirrors the structure of
/// [`super::ste_plaintext_term::OrderableTerm`] (the OPE/ORE side):
/// `From<(TokenizedSelector, MacTerm)>` carries the inputs, `build(macca)`
/// derives the bytes. The caller wraps the resulting [`Mac`] in either
/// `EncryptedSteVecTermCompat::Mac` or `EncryptedSteVecTermStandard::Mac`.
pub(crate) struct MacBuild<const N: usize> {
    selector: TokenizedSelector<N>,
    term: MacTerm,
}

impl<const N: usize> From<(TokenizedSelector<N>, MacTerm)> for MacBuild<N> {
    fn from((selector, term): (TokenizedSelector<N>, MacTerm)) -> Self {
        Self { selector, term }
    }
}

impl<const N: usize> MacBuild<N> {
    pub(crate) fn build<M>(self, macca: &mut M) -> Mac
    where
        M: PrefixMac + UpdatePrefixMac<MacTerm> + UpdatePrefixMac<TokenizedSelector<N>>,
    {
        macca.update(self.selector);
        macca.update(self.term);
        Mac::new(macca.finalize_reset::<N>().to_vec())
    }
}
