use std::path::PathBuf;

use crate::config::Config;

/// Loads and persists the application configuration at one configured path.
#[derive(Clone, Debug)]
pub(crate) struct ConfigRepository {
    path: Option<PathBuf>,
}

impl ConfigRepository {
    pub(crate) fn new(path: Option<PathBuf>) -> Self {
        Self { path }
    }

    pub(crate) fn load(&self) -> anyhow::Result<Config> {
        Config::load(self.path.clone())
    }

    pub(crate) fn save(&self, config: &Config) -> anyhow::Result<()> {
        config.save(self.path.clone())
    }

    /// Loads the latest config, applies one typed mutation, and persists it before returning.
    ///
    /// The update is atomic from the caller's perspective, but it does not provide concurrent
    /// filesystem transaction semantics.
    pub(crate) fn update<T>(&self, update: impl FnOnce(&mut Config) -> T) -> anyhow::Result<T> {
        let mut config = self.load()?;
        let value = update(&mut config);
        self.save(&config)?;
        Ok(value)
    }
}

#[cfg(test)]
#[path = "config_repository_tests.rs"]
mod tests;
