use std::path::Path;

use derive_more::{Debug, Deref};
use fjall::{
    DatabaseBuilder, KeyspaceCreateOptions, OptimisticTxDatabase, OptimisticTxKeyspace, PersistMode,
};

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

#[derive(Debug, Clone, Deref)]
pub struct TxDb {
    #[debug(skip)]
    #[deref]
    pub db: OptimisticTxDatabase,
    pub db_info: Persist<DbInfo>,
    pub path: String,
    #[debug(skip)]
    pub seq_tree: OptimisticTxKeyspace,
}

impl TxDb {
    pub fn new<P>(path: P) -> Self
    where
        P: AsRef<Path>,
    {
        let db_path = path.as_ref().join("db");
        Self::with_builder(&db_path, OptimisticTxDatabase::builder(&db_path))
    }

    pub fn builder<P: AsRef<Path>>(path: P) -> DatabaseBuilder<OptimisticTxDatabase> {
        OptimisticTxDatabase::builder(path.as_ref())
    }

    pub fn with_builder<P: AsRef<Path>>(
        path: P,
        config: DatabaseBuilder<OptimisticTxDatabase>,
    ) -> Self {
        let path = path.as_ref();
        let db = config.open().expect("Failed to open keyspace");

        let seq_tree = db
            .keyspace("__sequences", KeyspaceCreateOptions::default)
            .expect("Failed to open sequence tree");

        let db_info = Persist::<DbInfo>::open(path.join("db.info"));

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

    pub fn open_tree(
        &self,
        name: &str,
        options: Option<KeyspaceCreateOptions>,
    ) -> fjall::OptimisticTxKeyspace {
        self.db
            .keyspace(name, || options.unwrap_or_default())
            .expect("Failed to open partition")
    }

    #[instrument(skip(self), name = "Database::close", fields(path = self.path))]
    pub fn close(&self) {
        self.db_info.save();
        self.db
            .persist(PersistMode::SyncAll)
            .expect("Failed to persist");
    }
}
