extern crate alloc;

use serde::Serialize;

use crate::*;

#[test]
fn null() {
    let mut data = [0u8; 1];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    ().serialize(&mut ser).unwrap_or_else(|_| panic!());

    assert_eq!(data, [0b11011110]);
}

#[test]
fn bool() {
    let mut data = [0u8; 2];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    true.serialize(&mut ser).unwrap_or_else(|_| panic!());
    false.serialize(&mut ser).unwrap_or_else(|_| panic!());

    assert_eq!(data, [0b11011101, 0b11011100]);
}

#[test]
fn unsigned() {
    let mut data = [0u8; 20];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    10u8.serialize(&mut ser).unwrap_or_else(|_| panic!());
    u8::MAX.serialize(&mut ser).unwrap_or_else(|_| panic!());
    u16::MAX.serialize(&mut ser).unwrap_or_else(|_| panic!());
    u32::MAX.serialize(&mut ser).unwrap_or_else(|_| panic!());
    u64::MAX.serialize(&mut ser).unwrap_or_else(|_| panic!());

    assert_eq!(
        data,
        [
            10, // Imm
            0b11001000, 0xff, // U8
            0b11001001, 0xff, 0xff, // U16
            0b11001010, 0xff, 0xff, 0xff, 0xff, // U32
            0b11001011, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // U64
        ]
    );
}

#[test]
fn signed() {
    let mut data = [0u8; 22];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    10i8.serialize(&mut ser).unwrap_or_else(|_| panic!());
    (-32).serialize(&mut ser).unwrap_or_else(|_| panic!());
    i8::MAX.serialize(&mut ser).unwrap_or_else(|_| panic!());
    i8::MIN.serialize(&mut ser).unwrap_or_else(|_| panic!());
    i16::MAX.serialize(&mut ser).unwrap_or_else(|_| panic!());
    i32::MAX.serialize(&mut ser).unwrap_or_else(|_| panic!());
    i64::MAX.serialize(&mut ser).unwrap_or_else(|_| panic!());

    assert_eq!(
        data,
        [
            10,         // Imm
            0b11100000, // Neg Imm
            127,        // Imm
            0b11001100, 0x80, // I8
            0b11001101, 0x7f, 0xff, // I16
            0b11001110, 0x7f, 0xff, 0xff, 0xff, // UI2
            0b11001111, 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // I64
        ]
    );
}

#[test]
fn f32() {
    let mut data = [0u8; 5];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    3.4028234e38_f32
        .serialize(&mut ser)
        .unwrap_or_else(|_| panic!());

    assert_eq!(data, [0b11011010, 0x7f, 0x7f, 0xff, 0xff]);
}

#[test]
fn f64() {
    let mut data = [0u8; 9];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    42f64.serialize(&mut ser).unwrap_or_else(|_| panic!());

    assert_eq!(
        data,
        [0b11011011, 0x40, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
    );
}

#[test]
fn char() {
    let mut data = [0u8; 2];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    '!'.serialize(&mut ser).unwrap_or_else(|_| panic!());

    assert!(
        'π'.serialize(&mut ser)
            .is_err_and(|error| matches!(error, Error::BadEncoding))
    );

    assert_eq!(data, [0b10000001, 0x21]);
}

#[test]
fn ascii() {
    let mut data = [0u8; 11];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    "good to go"
        .serialize(&mut ser)
        .unwrap_or_else(|_| panic!());

    assert!(
        "δεν πάμε καλά"
            .serialize(&mut ser)
            .is_err_and(|error| matches!(error, Error::BadEncoding))
    );

    assert_eq!(
        data,
        [
            0b10000000 + 10,
            b'g',
            b'o',
            b'o',
            b'd',
            b' ',
            b't',
            b'o',
            b' ',
            b'g',
            b'o',
        ],
    );
}

#[test]
fn tuple() {
    let mut data = [0u8; 15];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    (42u32, (), "ok", -1.75e9f64)
        .serialize(&mut ser)
        .unwrap_or_else(|_| panic!());

    assert_eq!(
        data,
        [
            0b10100000 + 4, // Imm Array
            42,             // Imm Pos
            0b11011110,     // Null
            0b10000000 + 2, // Str
            b'o',
            b'k',
            0b11011011, // F64
            0xc1,
            0xda,
            0x13,
            0xb8,
            0x60,
            0x00,
            0x00,
            0x00,
        ]
    );
}

#[test]
fn option() {
    let mut data = [0u8; 2];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    Some(100u8).serialize(&mut ser).unwrap_or_else(|_| panic!());
    Option::<u8>::None
        .serialize(&mut ser)
        .unwrap_or_else(|_| panic!());

    assert_eq!(data, [100, 0b11011110]);
}

#[test]
fn seq() {
    let mut data = [0u8; 10];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    ["it", "works"]
        .serialize(&mut ser)
        .unwrap_or_else(|_| panic!());

    assert_eq!(
        data,
        [
            0b10100000 + 2, // Imm Array,
            0b10000000 + 2, // Imm Str
            b'i',
            b't',
            0b10000000 + 5, // Imm Str,
            b'w',
            b'o',
            b'r',
            b'k',
            b's',
        ],
    );
}

#[test]
fn map() {
    use alloc::collections::BTreeMap;

    let mut data = [0u8; 13];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    let map: BTreeMap<_, _> = [(0u8, "it"), (1u8, "works")].into();
    map.serialize(&mut ser).unwrap_or_else(|_| panic!());

    let map = BTreeMap::<u8, &str>::default();
    map.serialize(&mut ser).unwrap_or_else(|_| panic!());

    assert_eq!(
        data,
        [
            0b10110000 + 2, // Imm Map
            0,
            0b10000000 + 2, // Imm Str
            b'i',
            b't',
            1,
            0b10000000 + 5, // Imm Str,
            b'w',
            b'o',
            b'r',
            b'k',
            b's',
            0b10110000 + 0, // Imm Map
        ]
    );
}

#[test]
fn unit_struct() {
    #[derive(Serialize)]
    struct Unit;

    let mut data = [0u8; 1];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    Unit.serialize(&mut ser).unwrap_or_else(|_| panic!());

    // Expect: []
    assert_eq!(data, [0b10100000]);
}

#[test]
fn unit_variant() {
    #[derive(Serialize)]
    enum Enum {
        V1,
        V2,
    }

    let mut data = [0u8; 6];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    Enum::V1.serialize(&mut ser).unwrap_or_else(|_| panic!());
    Enum::V2.serialize(&mut ser).unwrap_or_else(|_| panic!());

    // Expect: "V1", "V2"
    assert_eq!(
        data,
        [
            0b10000000 + 2, // Imm Str
            b'V',
            b'1',
            0b10000000 + 2, // Imm Str
            b'V',
            b'2',
        ]
    );
}

#[test]
fn newtype_struct() {
    #[derive(Serialize)]
    struct Struct(u64);

    let mut data = [0u8; 1];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    Struct(42).serialize(&mut ser).unwrap_or_else(|_| panic!());

    // Expect: 42
    assert_eq!(data, [42]);
}

#[test]
fn newtype_variant() {
    #[derive(Serialize)]
    enum Enum {
        V2(u64),
    }

    let mut data = [0u8; 5];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    Enum::V2(42)
        .serialize(&mut ser)
        .unwrap_or_else(|_| panic!());

    // Expect: {"V2" => 42}
    assert_eq!(
        data,
        [
            0b10110000 + 1, // Imm Map
            0b10000000 + 2, // Imm Str
            b'V',
            b'2',
            42, // Imm Pos
        ]
    );
}

#[test]
fn untagged_newtype_variant() {
    #[derive(Serialize)]
    #[serde(untagged)]
    enum Enum1 {
        A(u64),
        B(Enum2),
    }

    #[derive(Serialize)]
    enum Enum2 {
        C,
    }

    let mut data = [0u8; 3];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    Enum1::A(123)
        .serialize(&mut ser)
        .unwrap_or_else(|_| panic!());
    Enum1::B(Enum2::C)
        .serialize(&mut ser)
        .unwrap_or_else(|_| panic!());

    // Expect: 123, "C"
    assert_eq!(
        data,
        [
            123,            // ImmPos
            0b10000000 + 1, // Imm Str
            b'C',
        ]
    );
}

#[test]
fn tuple_struct() {
    #[derive(Serialize)]
    struct Struct(u32, u64);

    let mut data = [0u8; 7];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    Struct(42, 100500)
        .serialize(&mut ser)
        .unwrap_or_else(|_| panic!());

    // Expect: [42, 100500].
    assert_eq!(
        data,
        [
            0b10100000 + 2, // Imm Array
            42,             // Imm Pos
            0b11001010,     // U32
            0x00,
            0x01,
            0x88,
            0x94
        ]
    );
}

#[test]
fn tuple_variant() {
    #[derive(Serialize)]
    enum Enum {
        V1,
        V2(u32, u64),
    }

    let mut data = [0u8; 14];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    Enum::V1.serialize(&mut ser).unwrap_or_else(|_| panic!());
    Enum::V2(42, 100500)
        .serialize(&mut ser)
        .unwrap_or_else(|_| panic!());

    // Expect: "V1", {"V2" => [42, 100500] }
    assert_eq!(
        data,
        [
            0b10000000 + 2, // Imm Str
            b'V',
            b'1',
            0b10110000 + 1, // Imm Map
            0b10000000 + 2, // Imm Str
            b'V',
            b'2',
            0b10100000 + 2, // Imm Array
            42,             // Imm Pos
            0b11001010,     // U32
            0x00,
            0x01,
            0x88,
            0x94
        ],
    );
}

#[test]
fn struct_() {
    #[derive(Serialize)]
    struct Struct {
        f1: u32,
        f2: u32,
    }

    let mut data = [0u8; 13];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    Struct { f1: 42, f2: 100500 }
        .serialize(&mut ser)
        .unwrap_or_else(|_| panic!());

    // Expect: { "f1" => 42, "f2" => 100500 }.
    assert_eq!(
        data,
        [
            0b10110000 + 2, // Imm Map
            0b10000000 + 2, // Imm Str
            b'f',
            b'1',
            42,             // Imm Pos
            0b10000000 + 2, // Imm Str
            b'f',
            b'2',
            0b11001010, // U32
            0x00,
            0x01,
            0x88,
            0x94
        ]
    );
}

#[test]
fn serialize_struct_variant() {
    #[derive(Serialize)]
    enum Enum {
        V1 { f1: u32 },
        V2 { f1: u32 },
    }

    let mut data = [0u8; 18];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    Enum::V1 { f1: 42 }
        .serialize(&mut ser)
        .unwrap_or_else(|_| panic!());
    Enum::V2 { f1: 43 }
        .serialize(&mut ser)
        .unwrap_or_else(|_| panic!());

    // Expect: { "V1" => { "f1" => 42 } } { "V2" => { "f2" => 43 } }
    assert_eq!(
        data,
        [
            0b10110000 + 1, // Imm Map
            0b10000000 + 2, // Imm Str
            b'V',
            b'1',
            0b10110000 + 1, // Imm Map
            0b10000000 + 2, // Imm Str
            b'f',
            b'1',
            42,             // Imm Pos
            0b10110000 + 1, // Imm Map
            0b10000000 + 2, // Imm Str
            b'V',
            b'2',
            0b10110000 + 1, // Imm Map
            0b10000000 + 2, // Imm Str
            b'f',
            b'1',
            43, // Imm Pos
        ],
    );
}

#[test]
fn serialize_struct_with_flattened_map_field() {
    use alloc::collections::BTreeMap;

    #[derive(Serialize)]
    struct Struct {
        f1: u32,

        // not flattend!
        f2: BTreeMap<&'static str, &'static str>,

        #[serde(flatten)]
        f3: BTreeMap<&'static str, &'static str>,
    }

    let mut data = [0u8; 28];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    Struct {
        f1: 42,
        f2: [("test", "ok")].into(),
        f3: [("works", "yes")].into(),
    }
    .serialize(&mut ser)
    .unwrap_or_else(|_| panic!());

    // Expect: { "f1": 42, "f2": { "test": "ok" }, "works": "yes" }.
    assert_eq!(
        data,
        [
            0b11000111,     // Flex Map
            0b10000000 + 2, // Imm Str
            b'f',
            b'1',
            42,             // Imm Pos
            0b10000000 + 2, // Imm Str
            b'f',
            b'2',
            0b10110000 + 1, // Imm Map
            0b10000000 + 4, // Imm Str
            b't',
            b'e',
            b's',
            b't',
            0b10000000 + 2, // Imm Str
            b'o',
            b'k',
            0b10000000 + 5, // Imm Str
            b'w',
            b'o',
            b'r',
            b'k',
            b's',
            0b10000000 + 3, // Imm Str
            b'y',
            b'e',
            b's',
            0b11011111, // Terminator
        ],
    );
}

#[test]
fn serialize_struct_with_flattened_struct_field() {
    #[derive(Serialize)]
    struct Struct {
        f1: u32,

        // not flattend!
        f2: InnerStruct,

        #[serde(flatten)]
        f3: InnerStruct,
    }

    #[derive(Serialize)]
    struct InnerStruct {
        f4: u32,
        f5: u32,
    }

    let mut data = [0u8; 26];
    let mut buffer = WriteBuffer::from(&mut data);
    let mut ser = ser::Serializer::from_buffer(&mut buffer);

    Struct {
        f1: 42,
        f2: InnerStruct { f4: 8, f5: 13 },
        f3: InnerStruct { f4: 21, f5: 34 },
    }
    .serialize(&mut ser)
    .unwrap_or_else(|_| panic!());

    // Expect: { "f1": 42, "f2": { "f4": 8, "f5": 13 }, "f4": 21, "f5": 34 }.
    assert_eq!(
        data,
        [
            0b11000111,     // Flex Map,
            0b10000000 + 2, // Imm Str
            b'f',
            b'1',
            42,             // Imm Pos
            0b10000000 + 2, // Imm Str
            b'f',
            b'2',
            0b10110000 + 2, // Imm Map
            0b10000000 + 2, // Imm Str
            b'f',
            b'4',
            8,              // Imm Pos
            0b10000000 + 2, // Imm Str
            b'f',
            b'5',
            13,             // Imm Pos
            0b10000000 + 2, // Imm Str
            b'f',
            b'4',
            21,             // Imm Pos
            0b10000000 + 2, // Imm Str
            b'f',
            b'5',
            34,         // Imm Pos
            0b11011111, // Terminator
        ]
    );
}
