#![allow(dead_code)]

use std::collections::HashMap;

use config_derive::config;
use twelf::Layer;

fn default_array() -> Vec<String> {
    vec!["first".to_owned(), "second".to_owned()]
}

const TOML_TEST_FILE: &str = "./tests/fixtures/test.toml";
const JSON_TEST_FILE: &str = "./tests/fixtures/test.json";

#[test]
fn missing_values() {
    #[config]
    #[derive(Debug)]
    struct TestCfg {
        test: String,
        another: usize,
    }
    let prio = vec![Layer::Env(None)];
    let config = TestCfg::with_layers(&prio);
    assert!(config.is_err());
}

#[test]
fn simple_only_env() {
    #[config]
    #[derive(Debug)]
    struct TestCfg {
        test: String,
        another: usize,
    }
    let prio = vec![Layer::Env(Some("TEST_".to_string()))];
    std::env::set_var("TEST_ANOTHER", "5");
    std::env::set_var("TEST_TEST", "hello");
    let config = TestCfg::with_layers(&prio).unwrap();
    assert_eq!(config.test, String::from("hello"));
    assert_eq!(config.another, 5usize);
}

#[test]
fn nested_only_env() {
    #[derive(Debug, PartialEq, serde::Deserialize, serde::Serialize)]
    struct Another {
        inner: String,
        // second: u8,
    }
    #[config]
    #[derive(Debug)]
    struct TestCfg {
        test: String,
        #[serde(flatten)]
        another: Another,
    }
    let prio = vec![Layer::Env(Some("NESTED_".to_string()))];
    std::env::set_var("NESTED_INNER", "inner_value");
    std::env::set_var("NESTED_TEST", "hello");
    let cfg: TestCfg = envy::prefixed("NESTED_").from_env().unwrap();
    println!("----- {:?}", cfg);
    let config = TestCfg::with_layers(&prio).unwrap();
    assert_eq!(config.test, String::from("hello"));
    assert_eq!(
        config.another,
        Another {
            inner: String::from("inner_value"),
            // second: 25,
        }
    );
}

#[test]
fn mixed_simple_types() {
    #[config]
    #[derive(Debug)]
    struct TestCfg {
        test: String,
        another: usize,
    }
    std::env::set_var("ANOTHER", "5");
    let prio = vec![
        Layer::Toml(TOML_TEST_FILE.into()),
        Layer::Json(JSON_TEST_FILE.into()),
        Layer::Env(None),
    ];
    let config = TestCfg::with_layers(&prio).unwrap();
    assert_eq!(config.test, String::from("from file"));
    assert_eq!(config.another, 5usize);

    let prio = vec![
        Layer::Env(None),
        Layer::Toml(TOML_TEST_FILE.into()),
        Layer::Json(JSON_TEST_FILE.into()),
    ];
    let config = TestCfg::with_layers(&prio).unwrap();
    assert_eq!(config.test, String::from("from file"));
    assert_eq!(config.another, 25usize);
}

#[test]
fn mixed_simple_with_prefix() {
    #[config]
    #[derive(Debug)]
    struct Conf {
        elements_def: HashMap<String, String>,
        #[serde(default = "default_array")]
        array_def: Vec<String>,
    }

    std::env::set_var("APP_ELEMENTS_DEF", "coucou=toi,hello=you");
    let prio = vec![
        Layer::Toml(TOML_TEST_FILE.into()),
        Layer::Json(JSON_TEST_FILE.into()),
        Layer::Env(Some("APP_".to_string())),
    ];
    let config = Conf::with_layers(&prio).unwrap();
    let mut map = HashMap::new();
    map.insert("coucou".to_string(), "toi".to_string());
    map.insert("hello".to_string(), "you".to_string());

    assert_eq!(config.elements_def, map);
    let array = vec![String::from("first"), String::from("second")];
    assert_eq!(config.array_def, array);
}

#[test]
fn mixed_simple_with_option() {
    #[config]
    #[derive(Debug)]
    struct Conf {
        elements_def: Option<HashMap<String, String>>,
        array_def: Option<Vec<String>>,
    }

    std::env::set_var("APPBIS_ELEMENTS_DEF", "coucou=toi,hello=you");
    let prio = vec![
        Layer::Toml(TOML_TEST_FILE.into()),
        Layer::Json(JSON_TEST_FILE.into()),
        Layer::Env(Some("APPBIS_".to_string())),
    ];
    let config = Conf::with_layers(&prio).unwrap();
    let mut map = HashMap::new();
    map.insert("coucou".to_string(), "toi".to_string());
    map.insert("hello".to_string(), "you".to_string());

    assert_eq!(config.elements_def, Some(map));
    assert_eq!(config.array_def, None);
}

#[test]
fn mixed_with_array_and_hashmap_string() {
    #[config]
    #[derive(Debug)]
    struct Conf {
        elements: HashMap<String, String>,
        #[serde(default = "default_array")]
        array: Vec<String>,
    }

    std::env::set_var("ARRAY", "test,test2");
    std::env::set_var("ELEMENTS", "coucou=toi,hello=you");
    let prio = vec![
        Layer::Toml(TOML_TEST_FILE.into()),
        Layer::Json(JSON_TEST_FILE.into()),
        Layer::Env(None),
    ];
    let config = Conf::with_layers(&prio).unwrap();
    let mut map = HashMap::new();
    map.insert("coucou".to_string(), "toi".to_string());
    map.insert("hello".to_string(), "you".to_string());

    assert_eq!(config.elements, map);
    let array = vec![String::from("test"), String::from("test2")];
    assert_eq!(config.array, array);

    let mut map = HashMap::new();
    map.insert("key".to_string(), "value".to_string());
    map.insert("key2".to_string(), "value2".to_string());

    let prio = vec![
        Layer::Env(None),
        Layer::Toml(TOML_TEST_FILE.into()),
        Layer::Json(JSON_TEST_FILE.into()),
    ];
    let config = Conf::with_layers(&prio).unwrap();
    assert_eq!(config.elements, map);
    let array = vec![String::from("test"), String::from("test2")];
    assert_eq!(config.array, array);
}

#[test]
fn mixed_with_array_and_hashmap_with_default() {
    #[config]
    #[derive(Debug)]
    struct Conf {
        elements_def: HashMap<String, String>,
        #[serde(default = "default_array")]
        array_def: Vec<String>,
    }

    std::env::set_var("ELEMENTS_DEF", "coucou=toi,hello=you");
    let prio = vec![
        Layer::Toml(TOML_TEST_FILE.into()),
        Layer::Json(JSON_TEST_FILE.into()),
        Layer::Env(None),
    ];
    let config = Conf::with_layers(&prio).unwrap();
    let mut map = HashMap::new();
    map.insert("coucou".to_string(), "toi".to_string());
    map.insert("hello".to_string(), "you".to_string());

    assert_eq!(config.elements_def, map);
    let array = vec![String::from("first"), String::from("second")];
    assert_eq!(config.array_def, array);
}
