use std::{path::Path, sync::Arc};

use derive_more::Debug;

use crate::utils::{DbInfo, Persist};

#[derive(Debug, Clone)]
pub struct Database {
    #[debug(skip)]
    pub db: super::Db,
    pub seq_tree: super::Tree,
    pub db_info: Persist<DbInfo>,
    pub path: String,
    pub trees: super::TreeRegistry,
}

impl Database {
    #[instrument(name = "Database::new")]
    pub fn new<P>(path: P, cache_capacity: usize) -> Self
    where
        P: AsRef<Path> + std::fmt::Debug,
    {
        use parking_lot::ReentrantMutex;
        use sled::Config;

        let path = path.as_ref();
        let sled_path = path.join("sled");

        let db = Config::new()
            .path(sled_path)
            .cache_capacity_bytes(cache_capacity)
            .open()
            .expect("cannot open persist db");

        let db_info = Persist::<DbInfo>::open(path.join("db.info"));
        let seq_tree = db.open_tree("__seq_tree").expect("cannot open tree").into();
        let db = Arc::new(ReentrantMutex::new(db));
        let trees = Default::default();

        Self {
            db,
            db_info,
            seq_tree,
            path: path.to_string_lossy().to_string(),
            trees,
        }
    }

    pub fn open_tree(&self, name: &str) -> super::Tree {
        self.db
            .lock()
            .open_tree(name)
            .expect("cannot open tree")
            .into()
    }

    pub fn tree_count(&self) -> usize {
        self.trees.lock().len()
    }

    /// save seq number, type hash, version to db, flush tree
    #[instrument(skip(self), name = "Database::close", fields(path = self.path))]
    pub fn close(self) {
        let read = self.trees.lock();
        let mut db_info = self.db_info.data.lock();
        for (name, tree) in read.iter() {
            let info = tree.tree_info();
            let count = Arc::strong_count(&tree.inner);
            if count != 1 {
                error!(count, "strong refs");
            }
            db_info.collections.insert(name.clone(), info);
        }
        // self.db_info.save();
    }
}
