use config::{Config, File};
use fxhash::FxHashMap;
use serde::Deserialize;
use springtime::config::CONFIG_FILE;
use springtime::future::{BoxFuture, FutureExt};
use springtime_di::component_registry::conditional::unregistered_component;
use springtime_di::instance_provider::ErrorPtr;
use springtime_di::{component_alias, injectable, Component};
use std::sync::Arc;
pub const DEFAULT_SERVER_NAME: &str = "default";
#[non_exhaustive]
#[derive(Clone, Debug, Deserialize)]
#[serde(default)]
pub struct ServerConfig {
    pub listen_address: String,
}
impl Default for ServerConfig {
    fn default() -> Self {
        Self {
            listen_address: "0.0.0.0:80".to_string(),
        }
    }
}
#[non_exhaustive]
#[derive(Clone, Debug, Deserialize)]
#[serde(default)]
pub struct WebConfig {
    pub servers: FxHashMap<String, ServerConfig>,
}
impl Default for WebConfig {
    fn default() -> Self {
        Self {
            servers: [(DEFAULT_SERVER_NAME.to_string(), Default::default())]
                .into_iter()
                .collect(),
        }
    }
}
impl WebConfig {
    fn init_from_config() -> Result<Self, ErrorPtr> {
        Config::builder()
            .add_source(File::with_name(CONFIG_FILE).required(false))
            .build()
            .and_then(|config| config.try_deserialize::<WebConfigWrapper>())
            .map(|config| config.web)
            .map_err(|error| Arc::new(error) as ErrorPtr)
    }
}
#[injectable]
pub trait WebConfigProvider {
    fn config(&self) -> BoxFuture<'_, Result<&WebConfig, ErrorPtr>>;
}
#[derive(Component)]
#[component(priority = -128, condition = "unregistered_component::<dyn WebConfigProvider + Send + Sync>", constructor = "DefaultWebConfigProvider::new")]
struct DefaultWebConfigProvider {
    #[component(ignore)]
    config: Result<WebConfig, ErrorPtr>,
}
#[component_alias]
impl WebConfigProvider for DefaultWebConfigProvider {
    fn config(&self) -> BoxFuture<'_, Result<&WebConfig, ErrorPtr>> {
        async {
            match &self.config {
                Ok(config) => Ok(config),
                Err(error) => Err(error.clone()),
            }
        }
        .boxed()
    }
}
impl DefaultWebConfigProvider {
    fn new() -> BoxFuture<'static, Result<Self, ErrorPtr>> {
        async {
            Ok(Self {
                config: WebConfig::init_from_config(),
            })
        }
        .boxed()
    }
}
#[derive(Deserialize, Default)]
#[serde(default)]
struct WebConfigWrapper {
    web: WebConfig,
}