use super::priv_state::{PlaintextEntry, TermBuilder};
use super::SteVecPendingEncryption;
use crate::encryption::{
    json_indexer::{
        path_values::{PathSegment, PathValue},
        prefix_mac::{PrefixMac, UpdatePrefixMac},
    },
    EncryptionError,
};
use itertools::Itertools;

#[cfg_attr(test, derive(Debug))]
pub struct StePlaintextVec<'p>(Vec<PlaintextEntry<'p>>);

impl<'p> StePlaintextVec<'p> {
    pub fn new() -> Self {
        Self(Vec::new())
    }

    /// Build a SteVec from a StePlaintextVec with selector length `N`.
    ///
    /// Threads two MACs through the build pipeline:
    /// - `selector_macca` (Blake3) tokenizes the JSON path into the entry's
    ///   selector. Always Blake3 regardless of mode.
    /// - `term_macca` (HMAC-SHA256) feeds the per-leaf term MAC and the
    ///   orderable-key derivation. Always HMAC regardless of mode.
    ///
    /// The mode-dependent piece is the orderable primitive (ORE vs OPE) —
    /// selected by the `TB: TermBuilder` type parameter, not by the MAC
    /// flavour.
    pub fn index<const N: usize, TB, MSel, MTerm>(
        self,
        selector_macca: &mut MSel,
        term_macca: &mut MTerm,
    ) -> Result<SteVecPendingEncryption<N>, EncryptionError>
    where
        TB: TermBuilder,
        for<'u> MSel: PrefixMac
            + UpdatePrefixMac<PathSegment<'p>>
            + UpdatePrefixMac<[u8; N]>
            + UpdatePrefixMac<&'u str>
            + UpdatePrefixMac<String>,
        for<'u> MTerm: PrefixMac + UpdatePrefixMac<&'u str> + UpdatePrefixMac<[u8; N]>,
    {
        let indexed = self
            .0
            .into_iter()
            .map(|entry| {
                entry
                    .build_selector::<N, MSel>(selector_macca)
                    .build_term::<TB, _>(term_macca)
            })
            .try_collect()?;

        Ok(SteVecPendingEncryption(indexed))
    }

    fn push(&mut self, PathValue(path, value): PathValue<'p>) {
        self.0.push(PlaintextEntry::new(path, value));
    }
}

impl<'p> FromIterator<PathValue<'p>> for StePlaintextVec<'p> {
    fn from_iter<I: IntoIterator<Item = PathValue<'p>>>(iter: I) -> Self {
        let mut ste_vec = StePlaintextVec::new();
        for entry in iter {
            ste_vec.push(entry);
        }
        ste_vec
    }
}
