#![cfg(feature = "deserialize")]

use armour_core::{
    EnumType, Fields, KeyScheme, KeyType, ScalarTyp, SimpleEnumType, StructType, Typ,
};

#[test]
fn typ_primitive_roundtrip() {
    let cases = [
        Typ::Scalar(ScalarTyp::Bool),
        Typ::Scalar(ScalarTyp::U8),
        Typ::Scalar(ScalarTyp::U16),
        Typ::Scalar(ScalarTyp::U32),
        Typ::Scalar(ScalarTyp::U64),
        Typ::Scalar(ScalarTyp::I32),
        Typ::Scalar(ScalarTyp::I64),
        Typ::Scalar(ScalarTyp::F32),
        Typ::Scalar(ScalarTyp::F64),
        Typ::Scalar(ScalarTyp::Str),
        Typ::Scalar(ScalarTyp::Datetime),
        Typ::Scalar(ScalarTyp::Timestamp),
        Typ::Scalar(ScalarTyp::Decimal),
        Typ::Scalar(ScalarTyp::Id32),
        Typ::Scalar(ScalarTyp::Id64),
        Typ::Scalar(ScalarTyp::Fuid),
        Typ::Scalar(ScalarTyp::LowId),
        Typ::Scalar(ScalarTyp::Bytes),
        Typ::Scalar(ScalarTyp::Void),
        Typ::Scalar(ScalarTyp::RustJson),
        Typ::Scalar(ScalarTyp::JsonBytes),
        Typ::Scalar(ScalarTyp::ArrayBytes(16)),
        Typ::Array(4, &Typ::Scalar(ScalarTyp::U32)),
        Typ::Vec(&Typ::Scalar(ScalarTyp::Str)),
        Typ::Optional(&Typ::Scalar(ScalarTyp::U64)),
    ];
    for t in cases {
        let json = serde_json::to_string(&t).unwrap();
        let back: Typ = serde_json::from_str(&json).unwrap();
        assert_eq!(t, back, "failed round-trip for {t:?}; json = {json}");
    }
}

#[test]
fn typ_struct_named_roundtrip() {
    let original = Typ::Struct(StructType {
        name: "User",
        fields: Fields::Named(&[
            ("id", Typ::Scalar(ScalarTyp::Fuid)),
            ("name", Typ::Scalar(ScalarTyp::Str)),
        ]),
    });
    let json = serde_json::to_string(&original).unwrap();
    let back: Typ = serde_json::from_str(&json).unwrap();
    assert_eq!(original, back);
}

#[test]
fn typ_struct_unnamed_roundtrip() {
    let original = Typ::Struct(StructType {
        name: "Pair",
        fields: Fields::Unnamed(&[Typ::Scalar(ScalarTyp::U32), Typ::Scalar(ScalarTyp::Str)]),
    });
    let json = serde_json::to_string(&original).unwrap();
    let back: Typ = serde_json::from_str(&json).unwrap();
    assert_eq!(original, back);
}

#[test]
fn typ_enum_roundtrip() {
    let original = Typ::Enum(EnumType {
        name: "Event",
        variants: &[
            (0, ("Created", Typ::Scalar(ScalarTyp::U64))),
            (1, ("Deleted", Typ::Scalar(ScalarTyp::Void))),
        ],
    });
    let json = serde_json::to_string(&original).unwrap();
    let back: Typ = serde_json::from_str(&json).unwrap();
    assert_eq!(original, back);
}

#[test]
fn typ_simple_enum_roundtrip() {
    let original = Typ::SimpleEnum(SimpleEnumType {
        name: "Kind",
        variants: &[(0, "Foo"), (1, "Bar")],
    });
    let json = serde_json::to_string(&original).unwrap();
    let back: Typ = serde_json::from_str(&json).unwrap();
    assert_eq!(original, back);
}

#[test]
fn key_scheme_typed_roundtrip() {
    let s = KeyScheme::Typed(&[KeyType::U8, KeyType::Fuid]);
    let json = serde_json::to_string(&s).unwrap();
    let back: KeyScheme = serde_json::from_str(&json).unwrap();
    assert_eq!(s, back);
}

#[test]
fn key_scheme_bytes_roundtrip() {
    let s = KeyScheme::Bytes;
    let json = serde_json::to_string(&s).unwrap();
    let back: KeyScheme = serde_json::from_str(&json).unwrap();
    assert_eq!(s, back);
}

#[test]
fn typ_enum_malformed_variant_key_returns_err() {
    let json = r#"{"type":"Enum","data":{"name":"Event","variants":{"not-a-tag":["Created",{"type":"U64"}]}}}"#;
    let err = serde_json::from_str::<Typ>(json).unwrap_err();
    assert!(
        err.to_string().contains("Enum variant key must be u8"),
        "unexpected error: {err}"
    );
}

#[test]
fn typ_simple_enum_wrong_value_type_returns_err() {
    let json = r#"{"type":"SimpleEnum","data":{"name":"Kind","variants":{"0":42}}}"#;
    let err = serde_json::from_str::<Typ>(json).unwrap_err();
    assert!(
        err.to_string()
            .contains("SimpleEnum variant value must be string"),
        "unexpected error: {err}"
    );
}

#[test]
fn typ_enum_wrong_variant_shape_returns_err() {
    let json = r#"{"type":"Enum","data":{"name":"Event","variants":{"0":"not-a-pair"}}}"#;
    let err = serde_json::from_str::<Typ>(json).unwrap_err();
    assert!(
        err.to_string().contains("Enum variant value shape"),
        "unexpected error: {err}"
    );
}
