use std::path::Path;

use derive_more::{Debug, Deref};
use fjall::{Database, DatabaseBuilder, Keyspace, KeyspaceCreateOptions, PersistMode};

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

#[derive(Debug, Clone, Deref)]
pub struct LogDb {
    #[debug(skip)]
    #[deref]
    pub db: Database,
    pub db_info: Persist<DbInfo>,
    pub path: String,
    #[debug(skip)]
    pub(crate) service_tree: Keyspace,
}

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

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

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

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

        let service_tree = db
            .keyspace("__service_tree", KeyspaceCreateOptions::default)
            .expect("Failed to open __service_tree partition");

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

    pub fn open_tree(&self, name: &str, options: Option<KeyspaceCreateOptions>) -> Keyspace {
        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");
    }
}
