use super::{EncryptedSteVecTerm, TokenizedSelector};
use crate::zerokms::{encrypted_record, EncryptedRecord};
use serde::{Deserialize, Serialize};

#[cfg_attr(test, derive(PartialEq, Eq))]
#[derive(Debug, Serialize, Deserialize)]
pub struct EncryptedEntry<const N: usize> {
    #[serde(rename = "s")]
    pub tokenized_selector: TokenizedSelector<N>,
    #[serde(flatten)]
    pub term: EncryptedSteVecTerm,
    #[serde(rename = "c", with = "encrypted_record::formats::mp_base85")]
    pub record: EncryptedRecord,
    pub parent_is_array: bool,
}

impl<const N: usize> EncryptedEntry<N> {
    pub(crate) fn new(
        tokenized_selector: TokenizedSelector<N>,
        term: EncryptedSteVecTerm,
        record: EncryptedRecord,
        parent_is_array: bool,
    ) -> Self {
        Self {
            tokenized_selector,
            term,
            record,
            parent_is_array,
        }
    }
}

#[cfg(test)]
use super::QueryEntry;

impl<const N: usize> EncryptedEntry<N> {
    #[cfg(test)]
    pub fn contains(&self, q: &QueryEntry<N>) -> bool {
        self.tokenized_selector == q.0 && self.term == q.1
    }
}

#[cfg(test)]
mod tests {
    use crate::encryption::{
        json_indexer::ste_vec::encrypted_term::{
            EncryptedSteVecTerm, EncryptedSteVecTermCompat, EncryptedSteVecTermStandard, Mac,
        },
        EncryptedEntry, TokenizedSelector,
    };
    use crate::zerokms::EncryptedRecord;
    use serde_json::json;
    use std::vec;

    fn make_encrypted_entry(term: EncryptedSteVecTerm) -> EncryptedEntry<16> {
        let ciphertext = EncryptedRecord {
            iv: Default::default(),
            ciphertext: vec![1, 2, 3],
            tag: vec![1, 16],
            descriptor: Default::default(),
            keyset_id: Default::default(),
            decryption_policy: None,
        };

        EncryptedEntry::new(TokenizedSelector([0; 16]), term, ciphertext, false)
    }

    mod mac {
        use super::*;

        fn expected() -> serde_json::Value {
            json!({
                "parent_is_array": false,
                // Hex-encoded tokenized_selector
                "s": "00000000000000000000000000000000",
                // Hex encoded HMAC-SHA256 MAC term (serializes as "hm")
                "hm": "01010101010101010101010101010101",
                // Base85-encoded ciphertext
                "c": "mBbJL0000000000000000002Q0|5d9!~y{jpuh"
            })
        }

        #[test]
        fn serialize_shape() {
            let entry = make_encrypted_entry(EncryptedSteVecTerm::Compat(
                EncryptedSteVecTermCompat::Mac(Mac::new(vec![1; 16])),
            ));
            let serialized = serde_json::to_string(&entry).unwrap();

            assert_eq!(
                serde_json::from_str::<serde_json::Value>(&serialized).unwrap(),
                expected()
            );
        }

        #[test]
        fn deserialize_shape() {
            let serialized = expected();
            let entry: Result<EncryptedEntry<16>, _> = serde_json::from_value(serialized);
            assert!(entry.is_ok());
        }
    }

    mod ore {
        use super::*;
        use cllw_ore::OreCllw8VariableV1;

        // ORE terms now collapse to a single variable-width ciphertext field
        // `oc` with no ciphertext-side domain tag byte. Numeric/string
        // disjointness is enforced on the plaintext side by `OrderableTerm`'s
        // type-tag bit; the wire form is just the raw ORE bytes.
        fn expected() -> serde_json::Value {
            json!({
                "parent_is_array": false,
                "s": "00000000000000000000000000000000",
                "oc": "04040404040404040404040404040404040404",
                "c": "mBbJL0000000000000000002Q0|5d9!~y{jpuh"
            })
        }

        #[test]
        fn serialize_shape() {
            let bytes = vec![4; 19];
            let entry = make_encrypted_entry(EncryptedSteVecTerm::Standard(
                EncryptedSteVecTermStandard::Ore(OreCllw8VariableV1::from(bytes)),
            ));

            let serialized = serde_json::to_string(&entry).unwrap();

            assert_eq!(
                serde_json::from_str::<serde_json::Value>(&serialized).unwrap(),
                expected()
            );
        }

        #[test]
        fn deserialize_shape() {
            let serialized = expected();
            let entry: Result<EncryptedEntry<16>, _> = serde_json::from_value(serialized);
            assert!(entry.is_ok());
        }
    }

    mod ope {
        use super::*;
        use cllw_ore::OpeCllw8VariableV1;

        fn expected() -> serde_json::Value {
            json!({
                "parent_is_array": false,
                "s": "00000000000000000000000000000000",
                "op": "07070707070707070707",
                "c": "mBbJL0000000000000000002Q0|5d9!~y{jpuh"
            })
        }

        #[test]
        fn serialize_shape() {
            let bytes = vec![7; 10];
            let entry = make_encrypted_entry(EncryptedSteVecTerm::Compat(
                EncryptedSteVecTermCompat::Ope(OpeCllw8VariableV1::from_bytes(bytes)),
            ));

            let serialized = serde_json::to_string(&entry).unwrap();

            assert_eq!(
                serde_json::from_str::<serde_json::Value>(&serialized).unwrap(),
                expected()
            );
        }

        #[test]
        fn deserialize_shape() {
            let serialized = expected();
            let entry: Result<EncryptedEntry<16>, _> = serde_json::from_value(serialized);
            assert!(entry.is_ok());
        }
    }
}
