use crate::prelude::*;
use bytes::BufMut;
use core::fmt;
use scale_info::{Type, TypeInfo};
use serde::{ser, Serialize};

use crate::{EnumVariant, SerdeType, TupleOrArray};

type Result<T> = core::result::Result<T, Error>;

#[derive(TypeInfo)]
struct Noop;

#[inline]
pub fn to_vec<T>(value: &T) -> Result<Vec<u8>>
where
    T: Serialize + ?Sized,
{
    let mut out = vec![];
    to_bytes(&mut out, value)?;
    Ok(out)
}

#[inline]
pub fn to_vec_with_info<T>(value: &T, info: impl Into<Option<Type>>) -> Result<Vec<u8>>
where
    T: Serialize + ?Sized,
{
    let mut out = vec![];
    to_bytes_with_info(&mut out, value, info)?;
    Ok(out)
}

pub fn to_bytes<B, T>(bytes: B, value: &T) -> Result<()>
where
    T: Serialize + ?Sized,
    B: BufMut,
{
    to_bytes_with_info(bytes, value, None)
}

pub fn to_bytes_with_info<B, T>(bytes: B, value: &T, info: impl Into<Option<Type>>) -> Result<()>
where
    T: Serialize + ?Sized,
    B: BufMut,
{
    let mut serializer = Serializer::new(bytes, info);
    value.serialize(&mut serializer)?;
    Ok(())
}

/// A serializer that encodes types to SCALE with the option to coerce
/// the output to an equivalent representation given by some type information.
pub struct Serializer<B> {
    out: B,
    ty: Option<SerdeType>,
}

impl<B> Serializer<B>
where
    B: BufMut,
{
    pub fn new(out: B, ty: impl Into<Option<Type>>) -> Self {
        Serializer {
            out,
            ty: ty.into().map(SerdeType::from),
        }
    }
}

impl<'a, B> ser::Serializer for &'a mut Serializer<B>
where
    B: BufMut,
{
    type Ok = ();
    type Error = Error;

    type SerializeSeq = TypedSerializer<'a, B>;
    type SerializeTuple = TypedSerializer<'a, B>;
    type SerializeTupleStruct = TypedSerializer<'a, B>;
    type SerializeTupleVariant = TypedSerializer<'a, B>;
    type SerializeMap = TypedSerializer<'a, B>;
    type SerializeStruct = TypedSerializer<'a, B>;
    type SerializeStructVariant = TypedSerializer<'a, B>;

    fn serialize_bool(self, v: bool) -> Result<Self::Ok> {
        self.maybe_some()?;
        self.out.put_u8(v.into());
        Ok(())
    }

    fn serialize_i8(self, v: i8) -> Result<Self::Ok> {
        self.maybe_some()?;
        self.out.put_i8(v);
        Ok(())
    }

    fn serialize_i16(self, v: i16) -> Result<Self::Ok> {
        self.maybe_some()?;
        self.out.put_i16_le(v);
        Ok(())
    }

    fn serialize_i32(self, v: i32) -> Result<Self::Ok> {
        self.maybe_some()?;
        self.out.put_i32_le(v);
        Ok(())
    }

    fn serialize_i64(self, v: i64) -> Result<Self::Ok> {
        match self.ty {
            Some(SerdeType::I8) => self.serialize_i8(v as i8)?,
            Some(SerdeType::I16) => self.serialize_i16(v as i16)?,
            Some(SerdeType::I32) => self.serialize_i32(v as i32)?,
            _ => {
                self.maybe_some()?;
                self.out.put_i64_le(v)
            }
        }
        Ok(())
    }

    fn serialize_u8(self, v: u8) -> Result<Self::Ok> {
        self.maybe_some()?;
        self.out.put_u8(v);
        Ok(())
    }

    fn serialize_u16(self, v: u16) -> Result<Self::Ok> {
        self.maybe_some()?;
        self.out.put_u16_le(v);
        Ok(())
    }

    fn serialize_u32(self, v: u32) -> Result<Self::Ok> {
        self.maybe_some()?;
        self.out.put_u32_le(v);
        Ok(())
    }

    fn serialize_u64(self, v: u64) -> Result<Self::Ok> {
        // all numbers in serde_json are the same
        match self.ty {
            Some(SerdeType::I8) => self.serialize_i8(v as i8)?,
            Some(SerdeType::I16) => self.serialize_i16(v as i16)?,
            Some(SerdeType::I32) => self.serialize_i32(v as i32)?,
            Some(SerdeType::U8) => self.serialize_u8(v as u8)?,
            Some(SerdeType::U16) => self.serialize_u16(v as u16)?,
            Some(SerdeType::U32) => self.serialize_u32(v as u32)?,
            _ => {
                self.maybe_some()?;
                self.out.put_u64_le(v);
            }
        }
        Ok(())
    }

    fn serialize_f32(self, _v: f32) -> Result<Self::Ok> {
        unimplemented!()
    }

    fn serialize_f64(self, _v: f64) -> Result<Self::Ok> {
        unimplemented!()
    }

    fn serialize_char(self, _v: char) -> Result<Self::Ok> {
        unimplemented!()
    }

    fn serialize_str(self, v: &str) -> Result<Self::Ok> {
        self.maybe_some()?;
        if let Some(ref mut var @ SerdeType::Variant(_, _, None)) = &mut self.ty {
            var.pick_mut(to_vec(v)?, |k| to_vec(k.name()).unwrap());
            self.out.put_u8(var.variant_id());
            return Ok(());
        }
        compact_number(v.len(), &mut self.out);
        self.out.put(v.as_bytes());
        Ok(())
    }

    fn serialize_bytes(self, v: &[u8]) -> Result<Self::Ok> {
        self.maybe_some()?;
        self.out.put(v);
        Ok(())
    }

    fn serialize_none(self) -> Result<Self::Ok> {
        self.out.put_u8(0x00);
        Ok(())
    }

    fn serialize_some<T: ?Sized>(self, value: &T) -> Result<Self::Ok>
    where
        T: Serialize,
    {
        self.out.put_u8(0x01);
        value.serialize(self)
    }

    fn serialize_unit(self) -> Result<Self::Ok> {
        self.maybe_some()?;
        Ok(())
    }

    fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok> {
        self.maybe_some()?;
        Ok(())
    }

    fn serialize_unit_variant(
        self,
        __name: &'static str,
        variant_index: u32,
        _variant: &'static str,
    ) -> Result<Self::Ok> {
        self.maybe_some()?;
        (variant_index as u8).serialize(self)
    }

    fn serialize_newtype_struct<T: ?Sized>(self, _name: &'static str, value: &T) -> Result<Self::Ok>
    where
        T: Serialize,
    {
        self.maybe_some()?;
        value.serialize(self)
    }

    fn serialize_newtype_variant<T: ?Sized>(
        self,
        __name: &'static str,
        variant_index: u32,
        _variant: &'static str,
        value: &T,
    ) -> Result<Self::Ok>
    where
        T: Serialize,
    {
        self.maybe_some()?;
        self.out.put_u8(variant_index as u8);
        value.serialize(self)
    }

    fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq> {
        self.maybe_some()?;
        if matches!(self.ty, None | Some(SerdeType::Sequence(_))) {
            compact_number(len.expect("known length"), &mut self.out);
        }
        Ok(self.into())
    }

    fn serialize_tuple(self, _len: usize) -> Result<Self::SerializeTuple> {
        self.maybe_some()?;
        Ok(self.into())
    }

    fn serialize_tuple_struct(
        self,
        __name: &'static str,
        __len: usize,
    ) -> Result<Self::SerializeTupleStruct> {
        self.maybe_some()?;
        Ok(self.into())
    }

    fn serialize_tuple_variant(
        self,
        __name: &'static str,
        variant_index: u32,
        _variant: &'static str,
        __len: usize,
    ) -> Result<Self::SerializeTupleVariant> {
        self.maybe_some()?;
        self.out.put_u8(variant_index as u8);
        Ok(self.into())
    }

    fn serialize_map(self, len: Option<usize>) -> Result<Self::SerializeMap> {
        self.maybe_some()?;
        if matches!(self.ty, None | Some(SerdeType::Map(_, _))) {
            compact_number(len.expect("known length"), &mut self.out);
        }
        Ok(self.into())
    }

    fn serialize_struct(self, _name: &'static str, _len: usize) -> Result<Self::SerializeStruct> {
        self.maybe_some()?;
        Ok(self.into())
    }

    fn serialize_struct_variant(
        self,
        __name: &'static str,
        variant_index: u32,
        _variant: &'static str,
        __len: usize,
    ) -> Result<Self::SerializeStructVariant> {
        self.maybe_some()?;
        self.out.put_u8(variant_index as u8);
        Ok(self.into())
    }
}

impl<'a, B> Serializer<B>
where
    B: BufMut,
{
    // A check to run for every serialize fn since any type could be an Option::Some
    // if the type info says its an Option assume its Some and extract the inner type
    fn maybe_some(&mut self) -> Result<()> {
        match &self.ty {
            Some(SerdeType::Variant(ref name, v, _)) if name == "Option" => {
                self.ty = v[1].fields().first().map(|f| f.ty().type_info().into());
                self.out.put_u8(0x01);
            }
            _ => (),
        }
        Ok(())
    }
}

///
pub enum TypedSerializer<'a, B> {
    Empty(&'a mut Serializer<B>),
    Composite(&'a mut Serializer<B>, Vec<Type>),
    Sequence(&'a mut Serializer<B>, Type),
    Enum(&'a mut Serializer<B>),
}

impl<'a, B: 'a> From<&'a mut Serializer<B>> for TypedSerializer<'a, B> {
    fn from(ser: &'a mut Serializer<B>) -> Self {
        use SerdeType::*;
        match ser.ty.take() {
            Some(Struct(fields) | StructTuple(fields)) => {
                Self::Composite(ser, fields.iter().map(|f| f.ty().type_info()).collect())
            }
            Some(Tuple(TupleOrArray::Array(ty, _))) => Self::Sequence(ser, ty),
            Some(Tuple(TupleOrArray::Tuple(fields))) => Self::Composite(ser, fields),
            Some(Sequence(ty)) => Self::Sequence(ser, ty),
            Some(Map(_, _)) => Self::Empty(ser),
            Some(var @ Variant(_, _, Some(_))) => match (&var).into() {
                EnumVariant::Tuple(_, _, types) => Self::Composite(ser, types),
                EnumVariant::Struct(_, _, types) => {
                    Self::Composite(ser, types.iter().map(|(_, ty)| ty.clone()).collect())
                }
                _ => Self::Empty(ser),
            },
            Some(var @ Variant(_, _, None)) => {
                ser.ty = Some(var);
                Self::Enum(ser)
            }
            _ => Self::Empty(ser),
        }
    }
}

impl<'a, B> TypedSerializer<'a, B> {
    fn serializer(&mut self) -> &mut Serializer<B> {
        match self {
            Self::Empty(ser)
            | Self::Composite(ser, _)
            | Self::Enum(ser)
            | Self::Sequence(ser, _) => ser,
        }
    }
}

impl<'a, B> ser::SerializeMap for TypedSerializer<'a, B>
where
    B: BufMut,
{
    type Ok = ();
    type Error = Error;

    fn serialize_key<T: ?Sized>(&mut self, key: &T) -> Result<()>
    where
        T: Serialize,
    {
        match self {
            TypedSerializer::Enum(ser) => {
                if let Some(ref mut var @ SerdeType::Variant(_, _, None)) = ser.ty {
                    let key_data = to_vec(key)?;
                    // assume the key is the name of the variant
                    var.pick_mut(key_data, |v| to_vec(v.name()).unwrap())
                        .variant_id()
                        .serialize(&mut **ser)?;
                }
                Ok(())
            }
            TypedSerializer::Empty(ser) => key.serialize(&mut **ser),
            _ => Ok(()),
        }
    }

    fn serialize_value<T: ?Sized>(&mut self, value: &T) -> Result<()>
    where
        T: Serialize,
    {
        match self {
            TypedSerializer::Composite(ser, types) => {
                let mut ty = types.remove(0).into();
                // serde_json unwraps newtypes
                if let SerdeType::StructNewType(t) = ty {
                    ty = t.into()
                }
                ser.ty = Some(ty);
            }
            TypedSerializer::Enum(ser) => {
                if let Some(var @ SerdeType::Variant(_, _, Some(_))) = &ser.ty {
                    if let EnumVariant::NewType(_, _, ty) = var.into() {
                        ser.ty = Some(ty.into());
                    }
                }
            }
            _ => {}
        }
        value.serialize(self.serializer())
    }

    fn end(self) -> Result<Self::Ok> {
        Ok(())
    }
}

impl<'a, B> ser::SerializeSeq for TypedSerializer<'a, B>
where
    B: BufMut,
{
    type Ok = ();
    type Error = Error;

    fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<()>
    where
        T: Serialize,
    {
        match self {
            TypedSerializer::Composite(ser, types) => {
                let mut ty = types.remove(0).into();
                if let SerdeType::StructNewType(t) = ty {
                    ty = t.into();
                }
                ser.ty = Some(ty);
            }
            TypedSerializer::Sequence(ser, ty) => {
                ser.ty = Some(match ty.into() {
                    SerdeType::StructNewType(t) => t.into(),
                    _ => ty.into(),
                });
            }
            _ => {}
        };
        value.serialize(self.serializer())
    }

    fn end(self) -> Result<Self::Ok> {
        Ok(())
    }
}

impl<'a, B> ser::SerializeStruct for TypedSerializer<'a, B>
where
    B: BufMut,
{
    type Ok = ();
    type Error = Error;

    fn serialize_field<T: ?Sized>(&mut self, _key: &'static str, value: &T) -> Result<()>
    where
        T: Serialize,
    {
        value.serialize(self.serializer())
    }

    fn end(self) -> Result<Self::Ok> {
        Ok(())
    }
}

impl<'a, B> ser::SerializeStructVariant for TypedSerializer<'a, B>
where
    B: BufMut,
{
    type Ok = ();
    type Error = Error;

    fn serialize_field<T: ?Sized>(&mut self, _key: &'static str, value: &T) -> Result<()>
    where
        T: Serialize,
    {
        value.serialize(self.serializer())
    }

    fn end(self) -> Result<Self::Ok> {
        Ok(())
    }
}

impl<'a, B> ser::SerializeTuple for TypedSerializer<'a, B>
where
    B: BufMut,
{
    type Ok = ();
    type Error = Error;

    fn serialize_element<T: ?Sized>(&mut self, value: &T) -> Result<()>
    where
        T: Serialize,
    {
        value.serialize(self.serializer())
    }

    fn end(self) -> Result<Self::Ok> {
        Ok(())
    }
}

impl<'a, B> ser::SerializeTupleStruct for TypedSerializer<'a, B>
where
    B: BufMut,
{
    type Ok = ();
    type Error = Error;

    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
    where
        T: Serialize,
    {
        value.serialize(self.serializer())
    }

    fn end(self) -> Result<Self::Ok> {
        Ok(())
    }
}

impl<'a, B> ser::SerializeTupleVariant for TypedSerializer<'a, B>
where
    B: BufMut,
{
    type Ok = ();
    type Error = Error;

    fn serialize_field<T: ?Sized>(&mut self, value: &T) -> Result<()>
    where
        T: Serialize,
    {
        value.serialize(self.serializer())
    }

    fn end(self) -> Result<Self::Ok> {
        Ok(())
    }
}

#[derive(Debug)]
pub enum Error {
    Ser(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Error::Ser(msg) => write!(f, "{}", msg),
        }
    }
}

impl ser::StdError for Error {}

impl ser::Error for Error {
    fn custom<T>(msg: T) -> Self
    where
        T: fmt::Display,
    {
        Error::Ser(msg.to_string())
    }
}

// from https://github.com/paritytech/parity-scale-codec/blob/master/src/compact.rs#L336
#[allow(clippy::all)]
fn compact_number(n: usize, mut dest: impl BufMut) {
    match n {
        0..=0b0011_1111 => dest.put_u8((n as u8) << 2),
        0..=0b0011_1111_1111_1111 => dest.put_u16_le(((n as u16) << 2) | 0b01),
        0..=0b0011_1111_1111_1111_1111_1111_1111_1111 => dest.put_u32_le(((n as u32) << 2) | 0b10),
        _ => {
            let bytes_needed = 8 - n.leading_zeros() / 8;
            assert!(
                bytes_needed >= 4,
                "Previous match arm matches anyting less than 2^30; qed"
            );
            dest.put_u8(0b11 + ((bytes_needed - 4) << 2) as u8);
            let mut v = n;
            for _ in 0..bytes_needed {
                dest.put_u8(v as u8);
                v >>= 8;
            }
            assert_eq!(
                v, 0,
                "shifted sufficient bits right to lead only leading zeros; qed"
            )
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use codec::Encode;
    use core::mem::size_of;
    use scale_info::TypeInfo;
    use serde_json::to_value;

    #[test]
    fn primitive_u8() -> Result<()> {
        let mut out = [0u8];
        to_bytes(&mut out[..], &123u8)?;

        let expected = [123];

        assert_eq!(out, expected);
        Ok(())
    }

    #[test]
    fn primitive_u16() -> Result<()> {
        const INPUT: u16 = 0xFF_EE;
        let mut out = [0u8; size_of::<u16>()];
        let expected = INPUT.encode();

        to_bytes(out.as_mut(), &INPUT)?;

        assert_eq!(out.as_ref(), expected);
        Ok(())
    }

    #[test]
    fn primitive_u32() -> Result<()> {
        const INPUT: u32 = 0xFF_EE_DD_CC;
        let mut out = [0u8; size_of::<u32>()];
        let expected = INPUT.encode();

        to_bytes(out.as_mut(), &INPUT)?;

        assert_eq!(out.as_ref(), expected);
        Ok(())
    }

    #[test]
    fn primitive_u64() -> Result<()> {
        const INPUT: u64 = 0xFF_EE_DD_CC__BB_AA_99_88;
        let mut out = [0u8; size_of::<u64>()];
        let expected = INPUT.encode();

        to_bytes(out.as_mut(), &INPUT)?;

        assert_eq!(out.as_mut(), expected);
        Ok(())
    }

    #[test]
    fn primitive_i16() -> Result<()> {
        const INPUT: i16 = i16::MIN;
        let mut out = [0u8; size_of::<i16>()];
        let expected = INPUT.encode();

        to_bytes(out.as_mut(), &INPUT)?;

        assert_eq!(out.as_mut(), expected);
        Ok(())
    }

    #[test]
    fn primitive_i32() -> Result<()> {
        const INPUT: i32 = i32::MIN;
        let mut out = [0u8; size_of::<i32>()];
        let expected = INPUT.encode();

        to_bytes(out.as_mut(), &INPUT)?;

        assert_eq!(out.as_mut(), expected);
        Ok(())
    }

    #[test]
    fn primitive_i64() -> Result<()> {
        const INPUT: i64 = i64::MIN;
        let mut out = [0u8; size_of::<i64>()];
        let expected = INPUT.encode();

        to_bytes(out.as_mut(), &INPUT)?;

        assert_eq!(out.as_mut(), expected);
        Ok(())
    }

    #[test]
    fn primitive_bool() -> Result<()> {
        const INPUT: bool = true;
        let mut out = [0u8];
        let expected = INPUT.encode();

        to_bytes(out.as_mut(), &INPUT)?;

        assert_eq!(out.as_mut(), expected);
        Ok(())
    }

    #[test]
    fn str() -> Result<()> {
        const INPUT: &str = "ac orci phasellus egestas tellus rutrum tellus pellentesque";
        let mut out = Vec::<u8>::new();
        let expected = INPUT.encode();

        to_bytes(&mut out, &INPUT)?;

        assert_eq!(out, expected);
        Ok(())
    }

    #[test]
    fn bytes() -> Result<()> {
        const INPUT: &[u8] = b"dictumst quisque sagittis purus sit amet volutpat consequat";
        let mut out = Vec::<u8>::new();
        let expected = INPUT.encode();

        to_bytes(&mut out, &INPUT)?;

        assert_eq!(out, expected);
        Ok(())
    }

    #[test]
    fn tuple_simple() -> Result<()> {
        const INPUT: (u8, bool, u64) = (0xD0, false, u64::MAX);
        let mut out = Vec::<u8>::new();
        let expected = INPUT.encode();

        to_bytes(&mut out, &INPUT)?;

        assert_eq!(out, expected);
        Ok(())
    }

    #[test]
    fn enum_simple() -> Result<()> {
        #[derive(Serialize, Encode)]
        enum X {
            _A,
            B,
        }

        const INPUT: X = X::B;
        let mut out = Vec::<u8>::new();
        let expected = INPUT.encode();

        to_bytes(&mut out, &INPUT)?;

        assert_eq!(out, expected);
        Ok(())
    }

    #[test]
    fn tuple_enum_mix() -> Result<()> {
        #[derive(Serialize, Encode)]
        enum X {
            A,
            B,
        }

        let input: (Option<()>, Option<String>, X, X) = (None, Some("hello".into()), X::A, X::B);
        let mut out = Vec::<u8>::new();
        let expected = input.encode();

        to_bytes(&mut out, &input)?;

        assert_eq!(out, expected);
        Ok(())
    }

    #[test]
    fn struct_simple() -> Result<()> {
        #[derive(Serialize, Encode)]
        struct Foo {
            a: Bar,
            b: Option<Baz>,
        }
        #[derive(Serialize, Encode)]
        struct Bar(u8);
        #[derive(Serialize, Encode)]
        struct Baz(String, u16);

        let input = Foo {
            a: Bar(0xFF),
            b: Some(Baz("lol".into(), u16::MAX)),
        };
        let mut out = Vec::<u8>::new();
        let expected = input.encode();

        to_bytes(&mut out, &input)?;

        assert_eq!(out, expected);
        Ok(())
    }

    #[test]
    fn vec_simple() -> Result<()> {
        let input: Vec<String> = vec!["hello".into(), "beautiful".into(), "people".into()];
        let mut out = Vec::<u8>::new();
        let expected = input.encode();

        to_bytes(&mut out, &input)?;

        assert_eq!(out, expected);
        Ok(())
    }

    #[test]
    fn struct_mix() -> Result<()> {
        #[derive(Serialize, Encode)]
        struct Foo<'a> {
            a: Vec<String>,
            b: (Bar<'a>, Bar<'a>, Bar<'a>),
        }
        #[derive(Serialize, Encode)]
        enum Bar<'a> {
            A { thing: &'a str },
            B(Baz),
            C(BTreeMap<String, bool>, i64),
        }
        #[derive(Serialize, Encode)]
        struct Baz;

        let input = Foo {
            a: vec!["hello".into(), "beautiful".into(), "people".into()],
            b: (
                Bar::A { thing: "barbarbar" },
                Bar::B(Baz),
                Bar::C(
                    {
                        let mut h = BTreeMap::new();
                        h.insert("key".into(), false);
                        h
                    },
                    i64::MIN,
                ),
            ),
        };
        let mut out = Vec::<u8>::new();
        let expected = input.encode();

        to_bytes(&mut out, &input)?;

        assert_eq!(out, expected);
        Ok(())
    }

    #[test]
    fn json_simple() -> Result<()> {
        #[derive(Debug, Serialize, Encode, TypeInfo)]
        struct Foo {
            a: Bar,
            b: Option<Baz>,
        }
        #[derive(Debug, Serialize, Encode, TypeInfo)]
        struct Bar(u8);
        #[derive(Debug, Serialize, Encode, TypeInfo)]
        struct Baz(String, i32);

        let input = Foo {
            a: Bar(0xFF),
            b: Some(Baz("lol".into(), i32::MIN)),
        };
        let mut out = Vec::<u8>::new();
        let expected = input.encode();

        let json_input = to_value(&input).unwrap();
        to_bytes_with_info(&mut out, &json_input, Foo::type_info())?;

        assert_eq!(out, expected);
        Ok(())
    }

    #[test]
    fn json_mix() -> Result<()> {
        #[derive(Debug, Serialize, Encode, TypeInfo)]
        struct Foo<'a> {
            a: Vec<String>,
            b: (Bar<'a>, Bar<'a>, Bar<'a>),
        }
        #[derive(Debug, Serialize, Encode, TypeInfo)]
        enum Bar<'a> {
            A { thing: &'a str },
            B(Baz),
            C(BTreeMap<String, bool>, i64),
        }
        #[derive(Debug, Serialize, Encode, TypeInfo)]
        struct Baz;

        let input = Foo {
            a: vec!["hello".into(), "beautiful".into(), "people".into()],
            b: (
                Bar::A { thing: "barbarbar" },
                Bar::B(Baz),
                Bar::C(
                    {
                        let mut h = BTreeMap::new();
                        h.insert("key1".into(), false);
                        h.insert("key2".into(), true);
                        h
                    },
                    i64::MIN,
                ),
            ),
        };
        let mut out = Vec::<u8>::new();
        let expected = input.encode();

        let json_input = to_value(&input).unwrap();
        to_bytes_with_info(&mut out, &json_input, Foo::type_info())?;

        assert_eq!(out, expected);
        Ok(())
    }

    #[test]
    fn json_mix2() -> Result<()> {
        #[derive(Debug, Encode, Serialize, TypeInfo)]
        enum Bar {
            This,
            That(i16),
        }
        #[derive(Debug, Encode, Serialize, TypeInfo)]
        struct Baz(String);
        #[derive(Debug, Encode, Serialize, TypeInfo)]
        struct Foo {
            bar: Vec<Bar>,
            baz: Option<Baz>,
            lol: &'static [u8],
        }
        let input = Foo {
            bar: [Bar::That(i16::MAX), Bar::This].into(),
            baz: Some(Baz("lorem ipsum".into())),
            lol: b"\0xFFsome stuff\0x00",
        };
        let mut out = Vec::<u8>::new();
        let expected = input.encode();

        let json_input = to_value(&input).unwrap();
        to_bytes_with_info(&mut out, &json_input, Foo::type_info())?;

        assert_eq!(out, expected);
        Ok(())
    }
}
