using AyCode.Core.Serializers.Binaries;
using AyCode.Core.Tests.TestModels;

namespace AyCode.Core.Tests.Serialization;

[TestClass]
public class AcBinarySerializerSGenNullComplexPropertyTests
{
    [TestMethod]
    [DataRow(true, true)]
    [DataRow(true, false)]
    [DataRow(false, false)]
    [DataRow(false, true)]
    public void Serialize_SGenComplexPropertyNull_DoesNotThrow_AndRoundTripsAsNull(bool useSgen, bool fastMode)
    {
        var model = new SGenNullComplexParent
        {
            Id = 7,
            Customer = null!,
            Note = "regression"
        };

        var options = fastMode ? AcBinarySerializerOptions.FastMode: AcBinarySerializerOptions.Default;
        options.UseGeneratedCode = useSgen;

        var bytes = AcBinarySerializer.Serialize(model, options);
        var roundTrip = AcBinaryDeserializer.Deserialize<SGenNullComplexParent>(bytes, options);

        Assert.IsNotNull(roundTrip);
        Assert.AreEqual(model.Id, roundTrip.Id);
        Assert.AreEqual(model.Note, roundTrip.Note);
        Assert.IsNull(roundTrip.Customer,
            "complex reference property must round-trip as null when source was null " +
            "(regression for SGen WriteObjectGenerated fallback else-branch null-check)");

        Assert.IsTrue(System.Array.IndexOf(bytes, (byte)BinaryTypeCode.PropertySkip) >= 0,
            "writer must emit PropertySkip marker on the null Customer slot " +
            "(deeper verification: confirms the fix took the PropertySkip path, " +
            "not an unrelated null-safe code path)");
    }

    [TestMethod]
    [DataRow(true, true)]
    [DataRow(true, false)]
    [DataRow(false, false)]
    [DataRow(false, true)]
    public void Serialize_SGenComplexPropertyNonNull_RoundTripsCorrectly(bool useSgen, bool fastMode)
    {
        var model = new SGenNullComplexParent
        {
            Id = 13,
            Customer = new NonGeneratedComplexCustomer { Id = 42, Name = "child" },
            Note = "positive"
        };

        var options = fastMode ? AcBinarySerializerOptions.FastMode: AcBinarySerializerOptions.Default;
        options.UseGeneratedCode = useSgen;

        var bytes = AcBinarySerializer.Serialize(model, options);
        var roundTrip = AcBinaryDeserializer.Deserialize<SGenNullComplexParent>(bytes, options);

        Assert.IsNotNull(roundTrip);
        Assert.AreEqual(model.Id, roundTrip.Id);
        Assert.AreEqual(model.Note, roundTrip.Note);
        Assert.IsNotNull(roundTrip.Customer,
            "non-null complex reference property must round-trip (null-check fix must not break the non-null path)");
        Assert.AreEqual(model.Customer.Id, roundTrip.Customer.Id);
        Assert.AreEqual(model.Customer.Name, roundTrip.Customer.Name);
    }

    [TestMethod]
    [DataRow(true, true)]
    [DataRow(true, false)]
    [DataRow(false, false)]
    [DataRow(false, true)]
    public void Serialize_SGenCollectionPropertyNull_DoesNotThrow_AndRoundTripsAsNull(bool useSgen, bool fastMode)
    {
        var model = new SGenNullCollectionParent
        {
            Id = 11,
            Items = null!,
            Note = "regression-collection"
        };

        var options = fastMode ? AcBinarySerializerOptions.FastMode: AcBinarySerializerOptions.Default;
        options.UseGeneratedCode = useSgen;

        var bytes = AcBinarySerializer.Serialize(model, options);
        var roundTrip = AcBinaryDeserializer.Deserialize<SGenNullCollectionParent>(bytes, options);

        Assert.IsNotNull(roundTrip);
        Assert.AreEqual(model.Id, roundTrip.Id);
        Assert.AreEqual(model.Note, roundTrip.Note);
        Assert.IsNull(roundTrip.Items,
            "collection property (with non-SGen element type) must round-trip as null when source was null " +
            "(regression for SGen Collection fallback WriteValueGenerated else-branch null-check)");

        Assert.IsTrue(System.Array.IndexOf(bytes, (byte)BinaryTypeCode.PropertySkip) >= 0,
            "writer must emit PropertySkip marker on the null Items slot " +
            "(confirms the fix took the PropertySkip path, not an unrelated null-safe code path)");
    }

    [TestMethod]
    [DataRow(true, true)]
    [DataRow(true, false)]
    [DataRow(false, false)]
    [DataRow(false, true)]
    public void Serialize_SGenCollectionPropertyNonNull_RoundTripsCorrectly(bool useSgen, bool fastMode)
    {
        var model = new SGenNullCollectionParent
        {
            Id = 17,
            Items = new List<NonGeneratedComplexCustomer>
            {
                new() { Id = 1, Name = "first" },
                new() { Id = 2, Name = "second" }
            },
            Note = "positive-collection"
        };

        var options = fastMode ? AcBinarySerializerOptions.FastMode: AcBinarySerializerOptions.Default;
        options.UseGeneratedCode = useSgen;

        var bytes = AcBinarySerializer.Serialize(model, options);
        var roundTrip = AcBinaryDeserializer.Deserialize<SGenNullCollectionParent>(bytes, options);

        Assert.IsNotNull(roundTrip);
        Assert.AreEqual(model.Id, roundTrip.Id);
        Assert.AreEqual(model.Note, roundTrip.Note);
        Assert.IsNotNull(roundTrip.Items,
            "non-null collection property must round-trip (null-check fix must not break the non-null path)");
        Assert.AreEqual(model.Items.Count, roundTrip.Items.Count);
        Assert.AreEqual(model.Items[0].Id, roundTrip.Items[0].Id);
        Assert.AreEqual(model.Items[0].Name, roundTrip.Items[0].Name);
        Assert.AreEqual(model.Items[1].Id, roundTrip.Items[1].Id);
        Assert.AreEqual(model.Items[1].Name, roundTrip.Items[1].Name);
    }

    [TestMethod]
    [DataRow(true, true)]
    [DataRow(true, false)]
    [DataRow(false, false)]
    [DataRow(false, true)]
    public void Serialize_SGenDictionaryPropertyNull_DoesNotThrow_AndRoundTripsAsNull(bool useSgen, bool fastMode)
    {
        var model = new SGenNullDictionaryParent
        {
            Id = 23,
            Mapping = null!,
            Note = "regression-dictionary"
        };

        var options = fastMode ? AcBinarySerializerOptions.FastMode: AcBinarySerializerOptions.Default;
        options.UseGeneratedCode = useSgen;

        var bytes = AcBinarySerializer.Serialize(model, options);
        var roundTrip = AcBinaryDeserializer.Deserialize<SGenNullDictionaryParent>(bytes, options);

        Assert.IsNotNull(roundTrip);
        Assert.AreEqual(model.Id, roundTrip.Id);
        Assert.AreEqual(model.Note, roundTrip.Note);
        Assert.IsNull(roundTrip.Mapping,
            "dictionary property must round-trip as null when source was null " +
            "(pins EmitDirectDictionaryWrite line ~1037 null-check against future regression)");

        Assert.IsTrue(System.Array.IndexOf(bytes, (byte)BinaryTypeCode.PropertySkip) >= 0,
            "writer must emit PropertySkip marker on the null Mapping slot " +
            "(confirms the PropertySkip path, not an unrelated null-safe code path)");
    }

    [TestMethod]
    [DataRow(true, true)]
    [DataRow(true, false)]
    [DataRow(false, false)]
    [DataRow(false, true)]
    public void Serialize_SGenDictionaryPropertyNonNull_RoundTripsCorrectly(bool useSgen, bool fastMode)
    {
        var model = new SGenNullDictionaryParent
        {
            Id = 29,
            Mapping = new Dictionary<string, NonGeneratedComplexCustomer>
            {
                ["alpha"] = new() { Id = 1, Name = "first" },
                ["beta"]  = new() { Id = 2, Name = "second" }
            },
            Note = "positive-dictionary"
        };

        var options = fastMode ? AcBinarySerializerOptions.FastMode: AcBinarySerializerOptions.Default;
        options.UseGeneratedCode = useSgen;

        var bytes = AcBinarySerializer.Serialize(model, options);
        var roundTrip = AcBinaryDeserializer.Deserialize<SGenNullDictionaryParent>(bytes, options);

        Assert.IsNotNull(roundTrip);
        Assert.AreEqual(model.Id, roundTrip.Id);
        Assert.AreEqual(model.Note, roundTrip.Note);
        Assert.IsNotNull(roundTrip.Mapping,
            "non-null dictionary property must round-trip (null-check pin must not break the non-null path)");
        Assert.AreEqual(model.Mapping.Count, roundTrip.Mapping.Count);
        Assert.AreEqual(model.Mapping["alpha"].Id, roundTrip.Mapping["alpha"].Id);
        Assert.AreEqual(model.Mapping["alpha"].Name, roundTrip.Mapping["alpha"].Name);
        Assert.AreEqual(model.Mapping["beta"].Id, roundTrip.Mapping["beta"].Id);
        Assert.AreEqual(model.Mapping["beta"].Name, roundTrip.Mapping["beta"].Name);
    }
}
