use json::{object, JsonValue};
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::{Path, PathBuf};

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Config {
    /// 调试
    pub debug: bool,
    /// 主机
    pub host: String,
    /// SSL/TLS
    pub https: bool,
    pub tls: TlsConfig,
    /// 日志记录
    pub log: bool,
    /// 运行根目录
    pub root_path: PathBuf,
    /// 公开目录
    pub public: String,
    /// 网页目录
    pub webpage: String,
    /// 运行时目录
    pub runtime: String,
    /// 语言
    pub charset: String,
    /// 密钥token
    pub token: String,
    /// 密钥文件路径
    pub pub_key_path: String,
    #[cfg(feature = "pools")]
    /// 线程池数量
    pub pools_max: usize,
}
impl Config {
    #[must_use]
    pub fn default(path: PathBuf) -> Self {
        Self {
            debug: true,
            host: "0.0.0.0:3535".to_string(),
            https: false,
            tls: TlsConfig::new(),
            log: true,
            root_path: path,
            public: "public".to_string(),
            webpage: "spa".to_string(),
            runtime: "runtime".to_string(),
            charset: "utf-8".to_string(),
            token: String::new(),
            pub_key_path: String::new(),
            #[cfg(feature = "pools")]
            pools_max: 0,
        }
    }

    #[must_use]
    pub fn create(config_file: &Path, pkg_name: bool) -> Config {
        let dir = config_file.parent().unwrap().to_path_buf();
        #[derive(Clone, Debug, Deserialize, Serialize)]
        pub struct ConfigPkg {
            pub br_web_server: Config,
        }
        impl ConfigPkg {
            pub fn new(dir: PathBuf) -> ConfigPkg {
                Self {
                    br_web_server: Config::default(dir)
                }
            }
        }
        let mut data = Config::default(dir.clone());
        data.from();
        match fs::read_to_string(config_file) {
            Ok(e) => {
                if pkg_name {
                    toml::from_str::<ConfigPkg>(&e).unwrap().br_web_server
                } else {
                    data
                }
            }
            Err(_) => {
                if pkg_name {
                    let data = ConfigPkg::new(dir);
                    fs::create_dir_all(config_file.parent().unwrap()).unwrap();
                    let toml = toml::to_string(&data).unwrap();
                    let _ = fs::write(config_file.to_str().unwrap(), toml);
                    data.br_web_server
                } else {
                    let data = Config::default(dir);
                    fs::create_dir_all(config_file.parent().unwrap()).unwrap();
                    let toml = toml::to_string(&data).unwrap();
                    let _ = fs::write(config_file.to_str().unwrap(), toml);
                    data
                }
            }
        }
    }
    fn from(&mut self) -> &mut Config {
        let root = self.root_path.clone();
        let runtime = root.join(self.runtime.clone()).to_str().unwrap().to_string();
        let public = root.join(self.public.clone()).to_str().unwrap().to_string();
        let webpage = root.join("webpage").to_str().unwrap().to_string();
        let ssl = root.join("ssl").to_str().unwrap().to_string();

        fs::create_dir_all(ssl.clone()).unwrap();
        fs::create_dir_all(runtime.clone()).unwrap();
        fs::create_dir_all(public.clone()).unwrap();
        fs::create_dir_all(webpage.clone()).unwrap();

        self
    }
    #[must_use]
    pub fn json(self) -> JsonValue {
        object! {
            debug: self.debug,
            log: self.log,
            host: self.host,
            https: self.https,
            root_path:self.root_path.to_str().unwrap(),
            public: self.public,
            webpage: self.webpage,
            runtime: self.runtime,
            charset: self.charset,
            token: self.token,
            pub_key_path: self.pub_key_path,
        }
    }
}


#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct TlsConfig {
    pub certs: PathBuf,
    pub key: PathBuf,
}
impl TlsConfig {
    fn new() -> Self {
        Self {
            certs: PathBuf::default(),
            key: PathBuf::default(),
        }
    }
    pub fn json(&mut self) -> JsonValue {
        object! {
            certs:self.certs.to_str().unwrap_or(""),
            key: self.key.to_str().unwrap_or(""),
        }
    }
    #[must_use]
    pub fn from(value: &JsonValue, root: &Path) -> Self {
        Self {
            certs: root.join(value["certs"].as_str().unwrap_or("")),
            key: root.join(value["key"].as_str().unwrap_or("")),
        }
    }
}
