use super::{EntryWithTokenizedSelector, StePlaintextTerm};
use crate::encryption::{
    json_indexer::{
        path_values::PathSegment,
        prefix_mac::{PrefixMac, UpdatePrefixMac},
        ste_vec::Selector,
    },
    Plaintext,
};
use serde_json::Value;

#[cfg_attr(test, derive(Debug))]
pub struct PlaintextEntry<'p> {
    path: Vec<PathSegment<'p>>,
    term: StePlaintextTerm,
    plaintext: Plaintext,
    parent_is_array: bool,
}

impl<'p> PlaintextEntry<'p> {
    pub(crate) fn new(path: Vec<PathSegment<'p>>, value: &Value) -> Self {
        let parent_is_array = matches!(
            path.last().expect("path cannot be empty"),
            PathSegment::ArrayItem | PathSegment::ArrayPositionItem(_)
        );
        Self {
            path,
            term: StePlaintextTerm::from(value),
            plaintext: Plaintext::from(value.clone()),
            parent_is_array,
        }
    }

    pub(crate) fn build_selector<const N: usize, M>(
        self,
        macca: &mut M,
    ) -> EntryWithTokenizedSelector<N>
    where
        M: PrefixMac + UpdatePrefixMac<String>,
    {
        let tokenized_selector = Selector::from_path(self.path).tokenize(macca);
        EntryWithTokenizedSelector::new(
            tokenized_selector,
            self.term,
            self.plaintext,
            self.parent_is_array,
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{
        assert_was_finalized_with,
        encryption::json_indexer::{
            path_values::PathSegment,
            ste_vec::{
                priv_state::{mac_term::MacTerm, ste_plaintext_term::OrderableTerm},
                tests::TestPrefixMac,
            },
        },
    };

    #[test]
    fn test_entry_build_selector_mac_build() {
        use PathSegment::*;

        fn build_selector(path_segments: Vec<PathSegment<'_>>) -> TestPrefixMac {
            let mut macca: TestPrefixMac = Default::default();
            let _ = PlaintextEntry::new(path_segments, &Value::Null)
                .build_selector::<16, _>(&mut macca);
            macca
        }

        assert_was_finalized_with!(build_selector(vec![Root]), "$");
        // assert_was_finalized_with!(build_selector(vec![Root, ArrayWildcardItem]), "$[*]");
        assert_was_finalized_with!(build_selector(vec![Root, ObjectItem("name")]), "$['name']");
        assert_was_finalized_with!(
            build_selector(vec![Root, ObjectItem("name"), ArrayWildcardItem]),
            "$['name'][*]"
        );
        assert_was_finalized_with!(
            build_selector(vec![Root, ArrayWildcardItem, ObjectItem("name")]),
            "$[*]['name']"
        );

        assert_was_finalized_with!(build_selector(vec![Root, ObjectItem("name")]), "$['name']");
    }

    #[test]
    fn test_entry_build_selector_term() {
        use PathSegment::*;

        fn build_selector(value: Value) -> EntryWithTokenizedSelector<16> {
            let mut macca: TestPrefixMac = Default::default();
            PlaintextEntry::new(vec![Root, ObjectItem("name")], &value)
                .build_selector::<16, _>(&mut macca)
        }

        assert_eq!(
            build_selector(Value::Null).term,
            StePlaintextTerm::Mac(MacTerm::Null)
        );
        assert_eq!(
            build_selector(Value::Object(Default::default())).term,
            StePlaintextTerm::Mac(MacTerm::Map)
        );
        assert_eq!(
            build_selector(Value::Array(Default::default())).term,
            StePlaintextTerm::Mac(MacTerm::Array)
        );
        assert_eq!(
            build_selector(Value::Bool(true)).term,
            StePlaintextTerm::Mac(MacTerm::Bool(true))
        );
        assert_eq!(
            build_selector(Value::Bool(false)).term,
            StePlaintextTerm::Mac(MacTerm::Bool(false))
        );
        assert_eq!(
            build_selector(Value::String(String::from("secret"))).term,
            StePlaintextTerm::Orderable(OrderableTerm::String(String::from("secret")))
        );
    }
}
