use serde::Deserialize;

#[derive(Deserialize)]
struct ConfigFile {
    pub name: String,
    pub write: bool,
    pub master: String,
    pub ram_limit: String,
    pub gc_limit: String,
    pub avg_entry_size: String,
    pub log: String,
    pub nats_cert: String,
    pub nats_key: String,
    pub nats_url: String,
}

#[derive(Clone)]
pub struct LypheDBConfig {
    pub name: String,
    pub write: bool,
    pub master: String,
    pub ram_limit: usize,
    pub gc_limit: usize,
    pub avg_entry_size: usize,
    pub log: String,
    pub nats_cert: String,
    pub nats_key: String,
    pub nats_url: String,
}

pub fn load_config(path: &str) -> LypheDBConfig {
    let s = std::fs::read_to_string(path);
    if let Err(e) = s {
        panic!("failed to read config file: {}", e);
    }
    let s = s.unwrap();
    let cnf = toml::from_str::<ConfigFile>(&s);
    if let Err(e) = cnf {
        panic!("failed to parse config file: {}", e);
    }
    let cnf = cnf.unwrap();

    // quick checks and conversions
    let mut config = LypheDBConfig {
        name: cnf.name,
        write: cnf.write,
        master: cnf.master,
        ram_limit: 0,
        gc_limit: 0,
        avg_entry_size: 0,
        log: cnf.log,
        nats_cert: cnf.nats_cert,
        nats_key: cnf.nats_key,
        nats_url: cnf.nats_url,
    };

    if !(cnf.ram_limit.ends_with("b") &&
        (
            cnf.ram_limit.trim_end_matches("b").ends_with("k") ||
                cnf.ram_limit.trim_end_matches("b").ends_with("m") ||
                cnf.ram_limit.trim_end_matches("b").ends_with("g")
        )
    ) {
        panic!("invalid ram limit");
    }
    config.ram_limit = if cnf.ram_limit.ends_with("gb") {
        cnf.ram_limit.trim_end_matches("gb").parse::<usize>().unwrap() * 1024 * 1024 * 1024
    } else if cnf.ram_limit.ends_with("mb") {
        cnf.ram_limit.trim_end_matches("mb").parse::<usize>().unwrap() * 1024 * 1024
    } else if cnf.ram_limit.ends_with("kb") {
        cnf.ram_limit.trim_end_matches("kb").parse::<usize>().unwrap() * 1024
    } else {
        cnf.ram_limit.trim_end_matches("b").parse::<usize>().unwrap()
    };

    if !(cnf.gc_limit.ends_with("b") &&
        (
            cnf.gc_limit.trim_end_matches("b").ends_with("k") ||
                cnf.gc_limit.trim_end_matches("b").ends_with("m") ||
                cnf.gc_limit.trim_end_matches("b").ends_with("g")
        )
    ) {
        panic!("invalid ram limit");
    }
    config.gc_limit = if cnf.gc_limit.ends_with("gb") {
        cnf.gc_limit.trim_end_matches("gb").parse::<usize>().unwrap() * 1024 * 1024 * 1024
    } else if cnf.gc_limit.ends_with("mb") {
        cnf.gc_limit.trim_end_matches("mb").parse::<usize>().unwrap() * 1024 * 1024
    } else if cnf.gc_limit.ends_with("kb") {
        cnf.gc_limit.trim_end_matches("kb").parse::<usize>().unwrap() * 1024
    } else {
        cnf.gc_limit.trim_end_matches("b").parse::<usize>().unwrap()
    };

    if !(cnf.avg_entry_size.ends_with("b") &&
        (
            cnf.avg_entry_size.trim_end_matches("b").ends_with("k") ||
                cnf.avg_entry_size.trim_end_matches("b").ends_with("m") ||
                cnf.avg_entry_size.trim_end_matches("b").ends_with("g")
        )
    ) {
        panic!("invalid ram limit");
    }
    config.avg_entry_size = if cnf.avg_entry_size.ends_with("gb") {
        cnf.avg_entry_size.trim_end_matches("gb").parse::<usize>().unwrap() * 1024 * 1024 * 1024
    } else if cnf.avg_entry_size.ends_with("mb") {
        cnf.avg_entry_size.trim_end_matches("mb").parse::<usize>().unwrap() * 1024 * 1024
    } else if cnf.avg_entry_size.ends_with("kb") {
        cnf.avg_entry_size.trim_end_matches("kb").parse::<usize>().unwrap() * 1024
    } else {
        cnf.avg_entry_size.trim_end_matches("b").parse::<usize>().unwrap()
    };

    if config.avg_entry_size > config.ram_limit {
        panic!("avg entry size is larger than ram limit");
    }
    if config.gc_limit > config.ram_limit {
        panic!("gc limit is larger than ram limit");
    }

    if config.log.is_empty() {
        config.log = "info".to_string();
    }
    if config.log != "debug" && config.log != "info" && config.log != "warn" && config.log != "error" {
        panic!("invalid log level");
    }

    config
}