use super::{Options, create, delete, find, find_many, update};
use crate::storage::associations::{BelongsTo, HasMany};
use crate::storage::config_store::config_store::FindOptions;
use crate::storage::{Error, config_store::ConfigStore};
use async_trait::async_trait;
use pbbson::Model;
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

pub(crate) struct DocHolder {
    pub(crate) docs: Mutex<HashMap<String, Model>>,
}

#[allow(unused)]
pub struct MemoryConfigStore<B: Clone + Send + Sync + ToString> {
    pub(crate) belongs_tos_by_bucket: HashMap<String, Vec<BelongsTo<B>>>,
    pub(crate) doc_holders: Mutex<HashMap<String, Arc<DocHolder>>>,
    pub(crate) has_manys_by_bucket: HashMap<String, Vec<HasMany<B>>>,
}

impl<B: Clone + Send + Sync + ToString> MemoryConfigStore<B> {
    #[allow(unused)]
    pub fn new(options: Options<B>) -> Self {
        let doc_holders = Mutex::new(HashMap::new());
        Self {
            belongs_tos_by_bucket: options.belongs_tos_by_bucket,
            doc_holders,
            has_manys_by_bucket: options.has_manys_by_bucket,
        }
    }
}

#[async_trait]
impl<B: Clone + Send + Sync + ToString> ConfigStore<B> for MemoryConfigStore<B> {
    async fn create(&self, bucket: B, model: Model) -> Result<Model, Error> {
        create::perform(self, bucket, model).await
    }

    async fn delete(&self, bucket: B, id: &str, by_account_id: Option<String>) -> Result<(), Error> {
        delete::perform(self, bucket, id, by_account_id).await
    }

    async fn find(&self, bucket: B, id: &str) -> Result<Model, Error> {
        find::perform(self, bucket, id).await
    }

    async fn find_many(&self, bucket: B, options: FindOptions) -> Result<Vec<Model>, Error> {
        find_many::perform(self, bucket, options).await
    }

    async fn update(&self, bucket: B, model: Model) -> Result<Model, Error> {
        update::perform(self, bucket, model).await
    }
}

impl<B: Clone + Send + Sync + ToString> MemoryConfigStore<B> {
    pub(crate) fn doc_holder(&self, bucket: &B) -> Result<Arc<DocHolder>, Error> {
        let mut doc_holders = self
            .doc_holders
            .lock()
            .map_err(|e| Error::failed_precondition(e.to_string()))?;
        match doc_holders.get(&bucket.to_string()) {
            Some(doc_holder) => Ok(doc_holder.clone()),
            None => {
                let doc_holder = Arc::new(DocHolder {
                    docs: Default::default(),
                });
                doc_holders.insert(bucket.to_string(), doc_holder.clone());
                Ok(doc_holder)
            }
        }
    }
}
