mod ext;
mod handler;
mod model;
#[cfg(test)]
mod tests;

#[cfg(test)]
pub use self::ext::MockConfigHandlerExt;

pub use self::ext::ConfigHandlerExt;
pub use self::handler::ConfigHandler;
pub use self::model::Config;
use crate::models::PluginID;
use chrono::{DateTime, Duration, Utc};
use std::io::Error;
use std::path::Path;

pub struct ConfigProxy {
    handler: Box<dyn ext::ConfigHandlerExt>,
    config: Config,
}

impl ConfigProxy {
    pub fn open<P: AsRef<Path>>(mut handler: Box<dyn ext::ConfigHandlerExt>, config_dir: P) -> Result<Self, Error> {
        let config = handler.load(config_dir.as_ref())?;

        Ok(Self { handler, config })
    }

    pub fn get_backend(&self) -> Option<PluginID> {
        self.config.backend.as_ref().map(|plugin_id| PluginID::new(plugin_id))
    }

    pub fn set_backend(&mut self, backend: Option<&PluginID>) -> Result<(), Error> {
        self.config.backend = backend.map(|plugin_id| plugin_id.as_str().to_owned());
        self.handler.save(&self.config)?;
        Ok(())
    }

    pub fn get_sync_amount(&self) -> u32 {
        self.config.sync_amount
    }

    pub fn set_sync_amount(&mut self, amount: u32) -> Result<(), Error> {
        self.config.sync_amount = amount;
        self.handler.save(&self.config)?;
        Ok(())
    }

    pub fn get_last_sync(&self) -> DateTime<Utc> {
        self.config.last_sync
    }

    pub fn set_last_sync(&mut self, time: DateTime<Utc>) -> Result<(), Error> {
        self.config.last_sync = time;
        self.handler.save(&self.config)?;
        Ok(())
    }

    pub fn get_keep_articles_duration(&self) -> Option<Duration> {
        self.config.keep_articles_days.map(|days| Duration::try_days(days as i64).unwrap())
    }

    pub fn set_keep_articles_duration(&mut self, duration: Option<Duration>) -> Result<(), Error> {
        if let Some(duration) = duration
            && duration < Duration::try_days(1).unwrap()
        {
            tracing::error!(%duration, "Duration to keep articles is not allowed to be < 1 Day");
            return Err(Error::other("Duration too low"));
        }
        self.config.keep_articles_days = duration.map(|d| d.num_days() as u64);
        self.handler.save(&self.config)?;
        Ok(())
    }

    pub fn get_concurrent_downloads(&self) -> u32 {
        self.config.concurrent_downloads.unwrap_or(model::CONCURRENT_DOWNLOADS_DEFAULT)
    }
}
