use crate::{errors::ConfigError, list::UniqueList, TableConfig};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatasetConfig {
    pub tables: UniqueList<TableConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DatasetConfigWithIndexRootKey {
        pub index_root_key: [u8; 32],
    #[serde(flatten)]
    pub config: DatasetConfig,
}
impl DatasetConfig {
    pub fn init() -> Self {
        Self {
            tables: Default::default(),
        }
    }
    pub fn add_table(mut self, config: TableConfig) -> Result<Self, ConfigError> {
        self.tables.try_insert(config)?;
        Ok(self)
    }
        pub fn has_table<Q>(&self, query: &Q) -> bool
    where
        TableConfig: PartialEq<Q>,
    {
        self.get_table(query).is_some()
    }
        pub fn get_table<Q>(&self, query: &Q) -> Option<&TableConfig>
    where
        TableConfig: PartialEq<Q>,
    {
        self.tables.get(query)
    }
                        pub fn sort_indexes_by_type(mut self) -> Self {
        self.tables
            .iter_mut()
            .for_each(TableConfig::sort_indexes_by_type);
        self
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    use indoc::indoc;
    use std::error::Error;
    use toml::to_string_pretty;
    use crate::*;
    #[test]
    fn add_and_get_table() -> Result<(), Box<dyn Error>> {
        let config = DatasetConfig::init().add_table(TableConfig::new("users")?)?;
        assert!(matches!(
            config.get_table(&"users"),
            Some(TableConfig { path, .. }) if path.as_string() == "users"
        ));
        Ok(())
    }
    #[test]
    fn add_and_get_table_with_schema() -> Result<(), Box<dyn Error>> {
        let config = DatasetConfig::init().add_table(TableConfig::new("public.users")?)?;
        assert!(matches!(
            config.get_table(&"users"),
            Some(TableConfig { path, .. }) if path.as_string() == "public.users"
        ));
        Ok(())
    }
    #[test]
    fn test_serialise_to_toml_single_table() -> Result<(), Box<dyn Error>> {
        let config = DatasetConfig::init().add_table(TableConfig::new("test")?)?;
        assert_eq!(
            to_string_pretty(&config)?,
            indoc! { r#"
                [[tables]]
                path = "test"
                fields = []
            "#}
        );
        Ok(())
    }
    #[test]
    fn test_serialise_to_toml_multiple_table() -> Result<(), Box<dyn Error>> {
        let config = DatasetConfig::init()
            .add_table(TableConfig::new("test")?)?
            .add_table(
                TableConfig::new("another")?.add_column(
                    ColumnConfig::build("great-column")
                        .add_index(column::Index::new_ore())
                        .add_index(column::Index::new_match()),
                )?,
            )?;
        assert_eq!(
            to_string_pretty(&config)?,
            indoc! { r#"
                [[tables]]
                path = "test"
                fields = []
                [[tables]]
                path = "another"
                [[tables.fields]]
                name = "great-column"
                in_place = false
                cast_type = "utf8-str"
                mode = "encrypted-duplicate"
                [[tables.fields.indexes]]
                version = 1
                kind = "ore"
                [[tables.fields.indexes]]
                version = 1
                kind = "match"
                k = 6
                m = 2048
                include_original = true
                [tables.fields.indexes.tokenizer]
                kind = "ngram"
                token_length = 3
                [[tables.fields.indexes.token_filters]]
                kind = "downcase"
            "#}
        );
        Ok(())
    }
    #[test]
    fn test_sort_indexes() -> Result<(), Box<dyn Error>> {
        let config = DatasetConfig::init()
            .add_table(
                TableConfig::new("users")?.add_column(
                    ColumnConfig::build("name")
                        .add_index(column::Index::new_ore())
                        .add_index(column::Index::new_unique())
                        .add_index(column::Index::new_match()),
                )?,
            )?
            .sort_indexes_by_type();
        let index_types = &config.tables[0].fields[0]
            .indexes
            .iter()
            .map(|index| index.as_str())
            .collect::<Vec<_>>();
        assert_eq!(index_types, &vec!["match", "ore", "unique"]);
        Ok(())
    }
}