use std::sync::Arc;

#[async_trait::async_trait]
pub trait Module: Send + Sync {
    type AppConfig;
    fn name(&self) -> &'static str {
        std::any::type_name::<Self>()
    }
    async fn init(&self) {}
    fn configure(&self, cfg: &mut Self::AppConfig);
}

pub struct Modules<TAppConfig> {
    modules: Vec<Arc<dyn Module<AppConfig = TAppConfig>>>,
}

impl<TAppConfig> Modules<TAppConfig> {
    pub fn new() -> Self {
        Self {
            modules: Vec::new(),
        }
    }

    pub fn add<TModule>(&mut self) -> &mut Self
    where
        TModule: Module<AppConfig = TAppConfig> + 'static + Default,
    {
        self.modules.push(Arc::new(TModule::default()));
        self
    }

    pub async fn init(&self) {
        for module in &self.modules {
            module.init().await;
        }
    }

    pub fn configure(&self, cfg: &mut TAppConfig) {
        for module in &self.modules {
            module.configure(cfg);
        }
    }
}
