// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

use std::collections::hash_map::IntoIter;
use std::collections::HashMap;
use std::iter::empty;

use serde::de::value::MapDeserializer;
use serde::de::value::SeqDeserializer;
use serde::de::Deserializer;
use serde::de::IntoDeserializer;
use serde::de::Visitor;
use serde::de::{self};

use crate::*;

/// Parse xml serialize error into opendal::Error.
pub fn new_xml_serialize_error(e: quick_xml::SeError) -> Error {
    Error::new(ErrorKind::Unexpected, "serialize xml").set_source(e)
}

/// Parse xml deserialize error into opendal::Error.
pub fn new_xml_deserialize_error(e: quick_xml::DeError) -> Error {
    Error::new(ErrorKind::Unexpected, "deserialize xml").set_source(e)
}

/// Parse json serialize error into opendal::Error.
pub fn new_json_serialize_error(e: serde_json::Error) -> Error {
    Error::new(ErrorKind::Unexpected, "serialize json").set_source(e)
}

/// Parse json deserialize error into opendal::Error.
pub fn new_json_deserialize_error(e: serde_json::Error) -> Error {
    Error::new(ErrorKind::Unexpected, "deserialize json").set_source(e)
}

/// ConfigDeserializer is used to deserialize given configs from `HashMap<String, String>`.
///
/// This is only used by our services' config.
pub struct ConfigDeserializer(MapDeserializer<'static, Pairs, de::value::Error>);

impl ConfigDeserializer {
    /// Create a new config deserializer.
    pub fn new(map: HashMap<String, String>) -> Self {
        let pairs = Pairs(map.into_iter());
        Self(MapDeserializer::new(pairs))
    }
}

impl<'de> Deserializer<'de> for ConfigDeserializer {
    type Error = de::value::Error;

    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        self.deserialize_map(visitor)
    }

    fn deserialize_map<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        visitor.visit_map(self.0)
    }

    serde::forward_to_deserialize_any! {
        bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit seq
        bytes byte_buf unit_struct tuple_struct
        identifier tuple ignored_any option newtype_struct enum
        struct
    }
}

/// Pairs is used to implement Iterator to meet the requirement of [`MapDeserializer`].
struct Pairs(IntoIter<String, String>);

impl Iterator for Pairs {
    type Item = (String, Pair);

    fn next(&mut self) -> Option<Self::Item> {
        self.0.next().map(|(k, v)| (k.to_lowercase(), Pair(k, v)))
    }
}

/// Pair is used to hold both key and value of a config for better error output.
struct Pair(String, String);

impl IntoDeserializer<'_, de::value::Error> for Pair {
    type Deserializer = Self;

    fn into_deserializer(self) -> Self::Deserializer {
        self
    }
}

impl<'de> Deserializer<'de> for Pair {
    type Error = de::value::Error;

    fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        self.1.into_deserializer().deserialize_any(visitor)
    }

    fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        match self.1.to_lowercase().as_str() {
            "true" | "on" => true.into_deserializer().deserialize_bool(visitor),
            "false" | "off" => false.into_deserializer().deserialize_bool(visitor),
            _ => Err(de::Error::custom(format_args!(
                "parse config '{}' with value '{}' failed for {:?}",
                self.0, self.1, "invalid bool value"
            ))),
        }
    }

    fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        match self.1.parse::<i8>() {
            Ok(val) => val.into_deserializer().deserialize_i8(visitor),
            Err(e) => Err(de::Error::custom(format_args!(
                "parse config '{}' with value '{}' failed for {:?}",
                self.0, self.1, e
            ))),
        }
    }

    fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        match self.1.parse::<i16>() {
            Ok(val) => val.into_deserializer().deserialize_i16(visitor),
            Err(e) => Err(de::Error::custom(format_args!(
                "parse config '{}' with value '{}' failed for {:?}",
                self.0, self.1, e
            ))),
        }
    }

    fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        match self.1.parse::<i32>() {
            Ok(val) => val.into_deserializer().deserialize_i32(visitor),
            Err(e) => Err(de::Error::custom(format_args!(
                "parse config '{}' with value '{}' failed for {:?}",
                self.0, self.1, e
            ))),
        }
    }

    fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        match self.1.parse::<i64>() {
            Ok(val) => val.into_deserializer().deserialize_i64(visitor),
            Err(e) => Err(de::Error::custom(format_args!(
                "parse config '{}' with value '{}' failed for {:?}",
                self.0, self.1, e
            ))),
        }
    }

    fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        match self.1.parse::<u8>() {
            Ok(val) => val.into_deserializer().deserialize_u8(visitor),
            Err(e) => Err(de::Error::custom(format_args!(
                "parse config '{}' with value '{}' failed for {:?}",
                self.0, self.1, e
            ))),
        }
    }

    fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        match self.1.parse::<u16>() {
            Ok(val) => val.into_deserializer().deserialize_u16(visitor),
            Err(e) => Err(de::Error::custom(format_args!(
                "parse config '{}' with value '{}' failed for {:?}",
                self.0, self.1, e
            ))),
        }
    }

    fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        match self.1.parse::<u32>() {
            Ok(val) => val.into_deserializer().deserialize_u32(visitor),
            Err(e) => Err(de::Error::custom(format_args!(
                "parse config '{}' with value '{}' failed for {:?}",
                self.0, self.1, e
            ))),
        }
    }

    fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        match self.1.parse::<u64>() {
            Ok(val) => val.into_deserializer().deserialize_u64(visitor),
            Err(e) => Err(de::Error::custom(format_args!(
                "parse config '{}' with value '{}' failed for {:?}",
                self.0, self.1, e
            ))),
        }
    }

    fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        match self.1.parse::<f32>() {
            Ok(val) => val.into_deserializer().deserialize_f32(visitor),
            Err(e) => Err(de::Error::custom(format_args!(
                "parse config '{}' with value '{}' failed for {:?}",
                self.0, self.1, e
            ))),
        }
    }

    fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        match self.1.parse::<f64>() {
            Ok(val) => val.into_deserializer().deserialize_f64(visitor),
            Err(e) => Err(de::Error::custom(format_args!(
                "parse config '{}' with value '{}' failed for {:?}",
                self.0, self.1, e
            ))),
        }
    }

    fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        if self.1.is_empty() {
            visitor.visit_none()
        } else {
            visitor.visit_some(self)
        }
    }

    fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
    where
        V: Visitor<'de>,
    {
        // Return empty instead of `[""]`.
        if self.1.is_empty() {
            SeqDeserializer::new(empty::<Pair>())
                .deserialize_seq(visitor)
                .map_err(|e| {
                    de::Error::custom(format_args!(
                        "parse config '{}' with value '{}' failed for {:?}",
                        self.0, self.1, e
                    ))
                })
        } else {
            let values = self
                .1
                .split(',')
                .map(|v| Pair(self.0.clone(), v.trim().to_owned()));
            SeqDeserializer::new(values)
                .deserialize_seq(visitor)
                .map_err(|e| {
                    de::Error::custom(format_args!(
                        "parse config '{}' with value '{}' failed for {:?}",
                        self.0, self.1, e
                    ))
                })
        }
    }

    serde::forward_to_deserialize_any! {
        char str string unit newtype_struct enum
        bytes byte_buf map unit_struct tuple_struct
        identifier tuple ignored_any
        struct
    }
}

#[cfg(test)]
mod tests {
    use serde::Deserialize;

    use super::*;

    #[derive(Debug, Default, Deserialize, Eq, PartialEq)]
    #[serde(default)]
    #[non_exhaustive]
    pub struct TestConfig {
        bool_value: bool,
        bool_option_value_none: Option<bool>,
        bool_option_value_some: Option<bool>,
        bool_value_with_on: bool,
        bool_value_with_off: bool,

        string_value: String,
        string_option_value_none: Option<String>,
        string_option_value_some: Option<String>,

        u8_value: u8,
        u16_value: u16,
        u32_value: u32,
        u64_value: u64,
        i8_value: i8,
        i16_value: i16,
        i32_value: i32,
        i64_value: i64,

        vec_value: Vec<String>,
        vec_value_two: Vec<String>,
        vec_none: Option<Vec<String>>,
        vec_empty: Vec<String>,
    }

    #[test]
    fn test_config_deserializer() {
        let mut map = HashMap::new();
        map.insert("bool_value", "true");
        map.insert("bool_option_value_none", "");
        map.insert("bool_option_value_some", "false");
        map.insert("bool_value_with_on", "on");
        map.insert("bool_value_with_off", "off");
        map.insert("string_value", "hello");
        map.insert("string_option_value_none", "");
        map.insert("string_option_value_some", "hello");
        map.insert("u8_value", "8");
        map.insert("u16_value", "16");
        map.insert("u32_value", "32");
        map.insert("u64_value", "64");
        map.insert("i8_value", "-8");
        map.insert("i16_value", "16");
        map.insert("i32_value", "-32");
        map.insert("i64_value", "64");
        map.insert("vec_value", "hello");
        map.insert("vec_value_two", "hello,world");
        map.insert("vec_none", "");
        map.insert("vec_empty", "");
        let map = map
            .into_iter()
            .map(|(k, v)| (k.to_string(), v.to_string()))
            .collect();

        let output = TestConfig::deserialize(ConfigDeserializer::new(map)).unwrap();
        assert_eq!(
            output,
            TestConfig {
                bool_value: true,
                bool_option_value_none: None,
                bool_option_value_some: Some(false),
                bool_value_with_on: true,
                bool_value_with_off: false,
                string_value: "hello".to_string(),
                string_option_value_none: None,
                string_option_value_some: Some("hello".to_string()),
                u8_value: 8,
                u16_value: 16,
                u32_value: 32,
                u64_value: 64,
                i8_value: -8,
                i16_value: 16,
                i32_value: -32,
                i64_value: 64,
                vec_value: vec!["hello".to_string()],
                vec_value_two: vec!["hello".to_string(), "world".to_string()],
                vec_none: None,
                vec_empty: vec![],
            }
        );
    }

    #[test]
    fn test_part_config_deserializer() {
        let mut map = HashMap::new();
        map.insert("bool_value", "true");
        let map = map
            .into_iter()
            .map(|(k, v)| (k.to_string(), v.to_string()))
            .collect();

        let output = TestConfig::deserialize(ConfigDeserializer::new(map)).unwrap();
        assert_eq!(
            output,
            TestConfig {
                bool_value: true,
                ..TestConfig::default()
            }
        );
    }
}
