//!
//! This module holds traits for extending functionalities for existing datatypes
//! & inbuilt datatypes.
//!
use error_stack::{IntoReport, ResultExt};
use masking::{ExposeInterface, Secret, Strategy};
use quick_xml::de;
use serde::{Deserialize, Serialize};
use crate::errors::{self, CustomResult};
///
/// Encode interface
/// An interface for performing type conversions and serialization
///
pub trait Encode<'e, P>
where
    Self: 'e + std::fmt::Debug,
{
    // If needed get type information/custom error implementation.
    ///
    /// Converting `Self` into an intermediate representation `
`
    /// and then performing encoding operation using the `Serialize` trait from `serde`
    /// Specifically to convert into json, by using `serde_json`
    ///
    fn convert_and_encode(&'e self) -> CustomResult
    where
        P: TryFrom<&'e Self> + Serialize,
        Result>::Error>: ResultExt,
        >::Error> as ResultExt>::Ok: Serialize;
    ///
    /// Converting `Self` into an intermediate representation ``
    /// and then performing encoding operation using the `Serialize` trait from `serde`
    /// Specifically, to convert into urlencoded, by using `serde_urlencoded`
    ///
    fn convert_and_url_encode(&'e self) -> CustomResult
    where
        P: TryFrom<&'e Self> + Serialize,
        Result>::Error>: ResultExt,
        >::Error> as ResultExt>::Ok: Serialize;
    ///
    /// Functionality, for specifically encoding `Self` into `String`
    /// after serialization by using `serde::Serialize`
    ///
    fn url_encode(&'e self) -> CustomResult
    where
        Self: Serialize;
    ///
    /// Functionality, for specifically encoding `Self` into `String`
    /// after serialization by using `serde::Serialize`
    /// specifically, to convert into JSON `String`.
    ///
    fn encode_to_string_of_json(&'e self) -> CustomResult
    where
        Self: Serialize;
    ///
    /// Functionality, for specifically encoding `Self` into `serde_json::Value`
    /// after serialization by using `serde::Serialize`
    ///
    fn encode_to_value(&'e self) -> CustomResult
    where
        Self: Serialize;
    ///
    /// Functionality, for specifically encoding `Self` into `Vec`
    /// after serialization by using `serde::Serialize`
    ///
    fn encode_to_vec(&'e self) -> CustomResult, errors::ParsingError>
    where
        Self: Serialize;
}
impl<'e, P, A> Encode<'e, P> for A
where
    Self: 'e + std::fmt::Debug,
{
    fn convert_and_encode(&'e self) -> CustomResult
    where
        P: TryFrom<&'e Self> + Serialize,
        Result>::Error>: ResultExt,
        >::Error> as ResultExt>::Ok: Serialize,
    {
        serde_json::to_string(
            &P::try_from(self).change_context(errors::ParsingError::UnknownError)?,
        )
        .into_report()
        .change_context(errors::ParsingError::EncodeError("string"))
        .attach_printable_lazy(|| format!("Unable to convert {self:?} to a request"))
    }
    fn convert_and_url_encode(&'e self) -> CustomResult
    where
        P: TryFrom<&'e Self> + Serialize,
        Result>::Error>: ResultExt,
        >::Error> as ResultExt>::Ok: Serialize,
    {
        serde_urlencoded::to_string(
            &P::try_from(self).change_context(errors::ParsingError::UnknownError)?,
        )
        .into_report()
        .change_context(errors::ParsingError::EncodeError("url-encoded"))
        .attach_printable_lazy(|| format!("Unable to convert {self:?} to a request"))
    }
    // Check without two functions can we combine this
    fn url_encode(&'e self) -> CustomResult
    where
        Self: Serialize,
    {
        serde_urlencoded::to_string(self)
            .into_report()
            .change_context(errors::ParsingError::EncodeError("url-encoded"))
            .attach_printable_lazy(|| format!("Unable to convert {self:?} to a request"))
    }
    fn encode_to_string_of_json(&'e self) -> CustomResult
    where
        Self: Serialize,
    {
        serde_json::to_string(self)
            .into_report()
            .change_context(errors::ParsingError::EncodeError("json"))
            .attach_printable_lazy(|| format!("Unable to convert {self:?} to a request"))
    }
    fn encode_to_value(&'e self) -> CustomResult
    where
        Self: Serialize,
    {
        serde_json::to_value(self)
            .into_report()
            .change_context(errors::ParsingError::EncodeError("json-value"))
            .attach_printable_lazy(|| format!("Unable to convert {self:?} to a value"))
    }
    fn encode_to_vec(&'e self) -> CustomResult, errors::ParsingError>
    where
        Self: Serialize,
    {
        serde_json::to_vec(self)
            .into_report()
            .change_context(errors::ParsingError::EncodeError("byte-vec"))
            .attach_printable_lazy(|| format!("Unable to convert {self:?} to a value"))
    }
}
///
/// Extending functionalities of `bytes::Bytes`
///
pub trait BytesExt {
    ///
    /// Convert `bytes::Bytes` into type `` using `serde::Deserialize`
    ///
    fn parse_struct<'de, T>(
        &'de self,
        type_name: &'static str,
    ) -> CustomResult
    where
        T: Deserialize<'de>;
}
impl BytesExt for bytes::Bytes {
    fn parse_struct<'de, T>(
        &'de self,
        type_name: &'static str,
    ) -> CustomResult
    where
        T: Deserialize<'de>,
    {
        use bytes::Buf;
        serde_json::from_slice::(self.chunk())
            .into_report()
            .change_context(errors::ParsingError::StructParseFailure(type_name))
            .attach_printable_lazy(|| {
                let variable_type = std::any::type_name::();
                format!("Unable to parse {variable_type} from bytes {self:?}")
            })
    }
}
///
/// Extending functionalities of `[u8]` for performing parsing
///
pub trait ByteSliceExt {
    ///
    /// Convert `[u8]` into type `` by using `serde::Deserialize`
    ///
    fn parse_struct<'de, T>(
        &'de self,
        type_name: &'static str,
    ) -> CustomResult
    where
        T: Deserialize<'de>;
}
impl ByteSliceExt for [u8] {
    fn parse_struct<'de, T>(
        &'de self,
        type_name: &'static str,
    ) -> CustomResult
    where
        T: Deserialize<'de>,
    {
        serde_json::from_slice(self)
            .into_report()
            .change_context(errors::ParsingError::StructParseFailure(type_name))
            .attach_printable_lazy(|| format!("Unable to parse {type_name} from &[u8] {:?}", &self))
    }
}
///
/// Extending functionalities of `serde_json::Value` for performing parsing
///
pub trait ValueExt {
    ///
    /// Convert `serde_json::Value` into type `` by using `serde::Deserialize`
    ///
    fn parse_value(self, type_name: &'static str) -> CustomResult
    where
        T: serde::de::DeserializeOwned;
}
impl ValueExt for serde_json::Value {
    fn parse_value(self, type_name: &'static str) -> CustomResult
    where
        T: serde::de::DeserializeOwned,
    {
        let debug = format!(
            "Unable to parse {type_name} from serde_json::Value: {:?}",
            &self
        );
        serde_json::from_value::(self)
            .into_report()
            .change_context(errors::ParsingError::StructParseFailure(type_name))
            .attach_printable_lazy(|| debug)
    }
}
impl ValueExt for Secret
where
    MaskingStrategy: Strategy,
{
    fn parse_value(self, type_name: &'static str) -> CustomResult
    where
        T: serde::de::DeserializeOwned,
    {
        self.expose().parse_value(type_name)
    }
}
///
/// Extending functionalities of `String` for performing parsing
///
pub trait StringExt {
    ///
    /// Convert `String` into type `` (which being an `enum`)
    ///
    fn parse_enum(self, enum_name: &'static str) -> CustomResult
    where
        T: std::str::FromStr,
        // Requirement for converting the `Err` variant of `FromStr` to `Report`
        ::Err: std::error::Error + Send + Sync + 'static;
    ///
    /// Convert `serde_json::Value` into type `` by using `serde::Deserialize`
    ///
    fn parse_struct<'de>(
        &'de self,
        type_name: &'static str,
    ) -> CustomResult
    where
        T: Deserialize<'de>;
}
impl StringExt for String {
    fn parse_enum(self, enum_name: &'static str) -> CustomResult
    where
        T: std::str::FromStr,
        ::Err: std::error::Error + Send + Sync + 'static,
    {
        T::from_str(&self)
            .into_report()
            .change_context(errors::ParsingError::EnumParseFailure(enum_name))
            .attach_printable_lazy(|| format!("Invalid enum variant {self:?} for enum {enum_name}"))
    }
    fn parse_struct<'de>(
        &'de self,
        type_name: &'static str,
    ) -> CustomResult
    where
        T: Deserialize<'de>,
    {
        serde_json::from_str::(self)
            .into_report()
            .change_context(errors::ParsingError::StructParseFailure(type_name))
            .attach_printable_lazy(|| {
                format!("Unable to parse {type_name} from string {:?}", &self)
            })
    }
}
///
/// Extending functionalities of Wrapper types for idiomatic
///
#[cfg(feature = "async_ext")]
#[cfg_attr(feature = "async_ext", async_trait::async_trait)]
pub trait AsyncExt {
    /// Output type of the map function
    type WrappedSelf;
    ///
    /// Extending map by allowing functions which are async
    ///
    async fn async_map(self, func: F) -> Self::WrappedSelf
    where
        F: FnOnce(A) -> Fut + Send,
        Fut: futures::Future