use anyhow::anyhow;
use std::env;
use std::net::IpAddr;

#[derive(Clone, Debug)]
pub struct Config {
    /// the ip the server will bind to, e.g. 127.0.0.1 or ::1
    pub server_address: IpAddr,
    /// the port the server will bind to, e.g. 3000
    pub server_port: u16,
    /// the database connection string e.g. "postgresql://user:password@localhost:5432/database"
    pub database_url: String,
    pub secret_key: String,
    pub hostname: String,
    pub smtp_server: String,
    pub smtp_port: u16,
    pub smtp_login: Option<String>,
    pub smtp_password: Option<String>,
    pub smtp_tlstype: SmtpTlsType,
}

#[derive(Clone, Debug)]
pub enum SmtpTlsType {
    TLS,
    StartTLS,
    NoTLS,
}

impl From<String> for SmtpTlsType {
    fn from(value: String) -> Self {
        match value.as_str() {
            "starttls" => SmtpTlsType::StartTLS,
            "tls" => SmtpTlsType::TLS,
            _ => SmtpTlsType::NoTLS,
        }
    }
}

pub enum Environment {
    Development,
    Test,
    Production,
}

pub fn load_config(env: &Environment) -> Result<Config, anyhow::Error> {
    match env {
        Environment::Development => {
            dotenvy::dotenv().ok();
        }
        Environment::Test => {
            dotenvy::from_filename(".env.test").ok();
        }
        Environment::Production => {
            // do not load a file for prod
        }
    }

    let config = Config {
        server_address: env::var("SERVER_ADDRESS")?.parse()?,
        server_port: env::var("SERVER_PORT")?.parse()?,
        database_url: env::var("DATABASE_URL")?.parse()?,
        secret_key: env::var("SECRET_KEY")?,
        hostname: env::var("HOSTNAME")?,
        smtp_server: env::var("SMTP_SERVER")?,
        smtp_port: env::var("SMTP_PORT")?.parse()?,
        smtp_login: env::var("SMTP_LOGIN")
            .and_then(|x| Ok(Some(x)))
            .unwrap_or(None),
        smtp_password: env::var("SMTP_PASSWORD")
            .and_then(|x| Ok(Some(x)))
            .unwrap_or(None),
        smtp_tlstype: SmtpTlsType::from(env::var("SMTP_TLSTYPE")?),
    };

    Ok(config)
}

pub fn get_env() -> Result<Environment, anyhow::Error> {
    match env::var("APP_ENVIRONMENT") {
        Ok(val) => {
            //info!(r#"Setting environment from APP_ENVIRONMENT: "{}""#, val);
            parse_env(&val)
        }
        Err(_) => {
            //info!("Defaulting to environment: development");
            Ok(Environment::Development)
        }
    }
}

pub fn parse_env(env: &str) -> Result<Environment, anyhow::Error> {
    let env = &env.to_lowercase();
    match env.as_str() {
        "dev" => Ok(Environment::Development),
        "development" => Ok(Environment::Development),
        "test" => Ok(Environment::Test),
        "prod" => Ok(Environment::Production),
        "production" => Ok(Environment::Production),
        unknown => Err(anyhow!(r#"Unknown environment: "{}"!"#, unknown)),
    }
}
