use super::StorageBuilder;
use crate::{
    encryption::{
        indexer::IndexerInit, EncryptionError, JsonIndexer, MatchIndexer, OpeIndexer, OreIndexer,
        Plaintext, PlaintextTarget, UniqueIndexer,
    },
    zerokms::IndexKey,
};
use std::borrow::Cow;
use uuid::Uuid;
use zerokms_protocol::cipherstash_config::column::IndexType;

pub trait Encryptable<'k> {
    type Output;

    // TODO: This should take a StorageBuilder instead of IndexKey
    fn build_encryptable(
        self,
        index_key: &'k IndexKey,
        keyset_id: Uuid,
    ) -> Result<StorageBuilder<'k, Self::Output>, EncryptionError>;
}

/// Encryptable implementation for PlaintextTarget
impl<'k> Encryptable<'k> for PlaintextTarget {
    type Output = Plaintext;

    fn build_encryptable(
        self,
        index_key: &'k IndexKey,
        keyset_id: Uuid,
    ) -> Result<StorageBuilder<'k, Self::Output>, EncryptionError> {
        let PlaintextTarget {
            plaintext,
            config,
            context,
            ..
        } = self;

        let mut builder = StorageBuilder::new(plaintext, index_key, keyset_id)
            .with_descriptor(config.name)
            .set_context(Cow::Owned(context));
        // TODO: We may want to add the column and table name to the context but not sure if this will break stuff

        for index in config.indexes.iter() {
            match &index.index_type {
                unique @ IndexType::Unique { .. } => {
                    let index = UniqueIndexer::try_init(unique)?;
                    builder = builder.index_with(index)?;
                }
                match_ @ IndexType::Match { .. } => {
                    let index = MatchIndexer::try_init(match_)?;
                    builder = builder.index_with(index)?;
                }
                ore @ IndexType::Ore => {
                    let index = OreIndexer::try_init(ore)?;
                    builder = builder.index_with(index)?;
                }
                ope @ IndexType::Ope => {
                    let index = OpeIndexer::try_init(ope)?;
                    builder = builder.index_with(index)?;
                }
                json @ IndexType::SteVec { .. } => {
                    let index = JsonIndexer::try_init(json)?;
                    builder = builder.index_with(index)?;
                }
            }
        }

        Ok(builder)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use uuid::Uuid;
    use zerokms_protocol::cipherstash_config::column::ColumnConfig;

    #[test]
    fn build_encryptable_sets_keyset_id_correctly() -> Result<(), EncryptionError> {
        // Create a test keyset_id
        let keyset_id = Uuid::new_v4();

        // Create a test IndexKey
        let index_key = IndexKey::from([0; 32]);

        // Create a ColumnConfig
        let config = ColumnConfig::build("test_column");

        // Create a PlaintextTarget with Text variant
        let plaintext_target = PlaintextTarget::new("hello world", config);

        // Call build_encryptable
        let storage_builder = plaintext_target.build_encryptable(&index_key, keyset_id)?;

        // Verify the keyset_id is set correctly
        assert_eq!(storage_builder.keyset_id(), keyset_id);

        Ok(())
    }
}
