use std::net::SocketAddr;

use anyhow::Context;
use serde::Deserialize;
use validator::Validate;

#[derive(Clone, Debug, Deserialize, Validate)]
pub struct AppConfig {
    #[validate(length(min = 1))]
    pub service_name: String,
    #[validate(length(min = 1))]
    pub environment: String,
    pub bind_addr: SocketAddr,
    #[validate(length(min = 1))]
    pub app_version: String,
    #[validate(length(min = 1))]
    pub default_locale: String,
    #[validate(length(min = 1))]
    pub default_device_platform: String,
    #[validate(length(min = 1))]
    pub default_experiment_variant: String,
    #[validate(length(min = 1))]
    pub log_level: String,
    pub stream_base_url: Option<String>,
    pub cdn_base_url: Option<String>,
    #[validate(length(min = 1))]
    pub analytics_mode: String,
    #[validate(length(min = 1))]
    pub experiment_config: String,
    #[validate(length(min = 1))]
    pub localization_source: String,
    pub database_url: Option<String>,
    pub redis_url: Option<String>,
}

impl Default for AppConfig {
    fn default() -> Self {
        Self {
            service_name: "hermes-backend".to_string(),
            environment: "local".to_string(),
            bind_addr: "127.0.0.1:3000".parse().expect("valid bind address"),
            app_version: "0.1.0-dev".to_string(),
            default_locale: "en".to_string(),
            default_device_platform: "ios".to_string(),
            default_experiment_variant: "control".to_string(),
            log_level: "info".to_string(),
            stream_base_url: None,
            cdn_base_url: None,
            analytics_mode: "relational".to_string(),
            experiment_config: "study-default".to_string(),
            localization_source: "contracts/localization".to_string(),
            database_url: None,
            redis_url: None,
        }
    }
}

impl AppConfig {
    pub fn load() -> anyhow::Result<Self> {
        let settings = config::Config::builder()
            .set_default("service_name", "hermes-backend")?
            .set_default("environment", "local")?
            .set_default("bind_addr", "127.0.0.1:3000")?
            .set_default("app_version", "0.1.0-dev")?
            .set_default("default_locale", "en")?
            .set_default("default_device_platform", "ios")?
            .set_default("default_experiment_variant", "control")?
            .set_default("log_level", "info")?
            .set_default("analytics_mode", "relational")?
            .set_default("experiment_config", "study-default")?
            .set_default("localization_source", "contracts/localization")?
            .add_source(config::Environment::with_prefix("HERMES").separator("__"))
            .build()
            .context("failed to load Hermes configuration")?;

        let config: Self = settings
            .try_deserialize()
            .context("failed to deserialize Hermes configuration")?;
        config.validate().context("invalid Hermes configuration")?;
        Ok(config)
    }
}
