use crate::FileUtil;
use serde_json::Value;
use std::io;
use std::path::Path;

/// Json configuration reading
/// ```rust
/// use mitoo::JsonWrapper;
/// 
/// #[test]
/// fn test_config_util() {
///    // read the configuration file
///     let wrapper = JsonWrapper::new("docs/config.json").unwrap();
///     // Directly convert the json string into JsonWrapper
///     // let warpper = JsonWrapper::from_string("......").unwrap();
/// 
///     // Whether it is an Object or an array, they as directly operated using the . (dot).
///     // Here, "address" is an object, "children" is an array, and "name" is an attribute of the objects within the "children" array.
///     let x = wrapper.get("address.children.name");
///     println!("address.children.name = {:?}", x);
///     
///     // The get method is used to retrieve an array, while get_one is used to retrieve the first element.
///     let x = wrapper.get_one("address.x.y").as_str().unwrap();
///     println!("address.x.y = {}", x);
/// }
/// ```
#[derive(Debug)]
pub struct JsonWrapper {
    pub raw_data: String,
    pub data: Value,
}

pub struct ValueWrapper<'a> {
    value: &'a Value,
}

impl<'a> ValueWrapper<'a> {
    /// 创建一个新的实例
    ///
    /// # 参数
    /// * `value` - 指向Value的引用
    ///
    /// # 返回值
    /// 返回包含指定value的新实例
    pub fn new(value: &'a Value) -> Self {
        Self { value }
    }

    /// 根据键名获取对应的值
    ///
    /// 该函数支持通过点号分隔的多层键名来访问嵌套的数据结构，
    /// 也支持在数组中查找指定键名的值。
    ///
    /// # 参数
    /// * `key` - 要查找的键名，可以是单层键名或点号分隔的多层键名
    ///
    /// # 返回值
    /// 返回包含查找到的值的向量，如果未找到则包含Null值
    pub fn get(&self, key: &'a str) -> Vec<&'a Value> {
        let mut results = Vec::new();
        if let Some((left, right)) = key.split_once(".") {
            // 处理多层键名的情况
            let x = self.value.get(left);
            if x.is_none() {
                results.push(&Value::Null);
            } else {
                let x = x.unwrap();
                let next = ValueWrapper::new(x);
                results.append(&mut next.get(right));
            }
        } else {
            // 处理单层键名的情况
            match self.value {
                Value::Object(map) => {
                    // 在对象中查找键名
                    if let Some(v) = map.get(key) {
                        results.push(v);
                    } else {
                        results.push(&Value::Null);
                    }
                }
                Value::Array(vec) => {
                    // 在数组的每个元素中查找键名
                    for item in vec {
                        match item.get(key) {
                            Some(v) => results.push(v),
                            None => results.push(&Value::Null),
                        }
                    }
                }
                _ => results.push(&Value::Null),
            }
        }
        results
    }
}

impl JsonWrapper {
    /// 创建一个新的配置实例
    ///
    /// 该函数会读取指定路径的配置文件，解析JSON格式的内容，并创建配置对象
    ///
    /// # 参数
    /// * `file_path` - 配置文件的路径字符串引用
    ///
    /// # 返回值
    /// 返回Result类型，成功时包含配置实例，失败时包含IO错误
    ///
    /// # 错误
    /// 当文件读取失败或JSON解析失败时会返回相应的错误
    pub fn new(file_path: &str) -> io::Result<Self> {
        // 读取配置文件内容
        let conf_string = FileUtil::read_string(Path::new(file_path))?;
        // 解析JSON数据
        let data: Value = serde_json::from_str(&conf_string)?;
        Ok(Self {
            raw_data: conf_string,
            data,
        })
    }

    pub fn from_string(conf_string: &str) -> io::Result<Self> {
        let data: Value = serde_json::from_str(conf_string)?;
        Ok(Self {
            raw_data: conf_string.to_string(),
            data,
        })
    }

    /// 获取指定键对应的值列表
    ///
    /// 该函数通过创建一个值包装器来访问内部数据，并返回与给定键关联的所有值。
    ///
    /// # 参数
    /// * `key` - 要查找的静态字符串键
    ///
    /// # 返回值
    /// 返回一个包含所有匹配值的引用向量
    pub fn get<'a, 'b: 'a>(&'a self, key: &'b str) -> Vec<&'a Value> {
        let wrapper = ValueWrapper::new(&self.data);
        wrapper.get(key)
    }

    /// 获取指定键对应的第一个值
    ///
    /// # 参数
    /// * `key` - 要查找的键名，必须是静态字符串
    ///
    /// # 返回值
    /// 返回键对应值的引用
    ///
    /// # 注意
    /// 如果键不存在或对应的值为空，会触发panic
    pub fn get_one<'a, 'b: 'a>(&'a self, key: &'b str) -> &'a Value {
        // 该方法注释，要求b的生命周期不小于a的生命周期
        // 创建值包装器来处理数据查询
        let wrapper = ValueWrapper::new(&self.data);
        // 获取指定键对应的值向量
        let vec = wrapper.get(key);
        // 返回向量中的第一个值，如果不存在则panic
        vec.first().unwrap()
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_json_conf_util() {
        
        let json_wrapper = JsonWrapper::new("docs/config.json").unwrap();

        let x = json_wrapper.get("name");
        // name = [String("Alice")]
        println!("name = {:?}", x);

        let x = json_wrapper.get("age");
        // age = [Number(30)]
        println!("age = {:?}", x);

        let x = json_wrapper.get("is_student");
        // is_student = [Bool(false)]
        println!("is_student = {:?}", x);

        let x = json_wrapper.get("hobbies");
        // hobbies = [Array [String("reading"), String("swimming"), String("cooking")]]
        println!("hobbies = {:?}", x);

        let x = json_wrapper.get("address");
        // address = [Object {"children": Array [Object {"age": Number(5), "name": String("r")}, Object {"age": Number(6), "name": String("s")}], "city": String("New York"), "releases": Array [String("v1"), String("v2")], "state": String("NY"), "street": String("123 Main St"), "x": Object {"y": String("hello, json!")}, "zip": String("10001")}]
        println!("address = {:?}", x);

        let x = json_wrapper.get("address.street");
        // address.street = [String("123 Main St")]
        println!("address.street = {:?}", x);

        let x = json_wrapper.get("address.releases");
        // address.releases = [Array [String("v1"), String("v2")]]
        println!("address.releases = {:?}", x);

        let x = json_wrapper.get("address.x.y");
        // address.x.y = [String("hello, json!")]
        println!("address.x.y = {:?}", x);

        let x = json_wrapper.get("address.children.name");
        // address.children.name = [String("r"), String("s")]
        println!("address.children.name = {:?}", x);
        println!("=============================================================");

        let name = json_wrapper.get_one("name").as_str().unwrap();
        let age = json_wrapper.get_one("age").as_i64().unwrap();
        println!("name = {:?}", name);
        println!("age = {:?}", age);

        let key = String::from("address.x.y");
        let x = json_wrapper.get_one(&key).as_str().unwrap();
        // address.x.y = hello, json!
        println!("address.x.y = {}", x);

    }
}
