using System;
using System.Collections.Generic;
using NUnit.Framework;

{% if namespace %}
namespace {{ namespace }}
{
{% endif %}
{% set ind=4 if namespace else 0 %}
{% filter indent(width=ind, first=True) %}
/// <summary> Test class for {{ class_base_name }} </summary> 
[TestFixture]
public class {{ test_class_name }}
{
    private {{ class_base_name }} _instance;

    /// <summary> Constructor </summary>
    public {{ test_class_name }}()
    {
        _instance = CreateInstance();
    }

    /// <summary> Create instance of {{ class_base_name }} </summary>
    public {{ class_base_name }} CreateInstance()
    {
        var instance = new {{ class_base_name }}();
        {%- for field in fields %}
        {%- if not field.is_const %}
        instance.{{ field.field_name }} = {{ field.test_value }};
        {%- endif %}
        {%- endfor %}
        return instance;
    }

    /// <summary> Testing Equals and GetHashCode - positive case </summary>
    [Test]
    public void TestEqualsAndHashCode_PositiveCase()
    {
        var instance1 = CreateInstance();
        var instance2 = CreateInstance();
        
        // Test equality
        Assert.That(instance1.Equals(instance2), Is.True, "Two instances with same values should be equal");
        Assert.That(instance1 == instance2, Is.False, "Operator == should use reference equality");
        
        // Test hash codes
        Assert.That(instance1.GetHashCode(), Is.EqualTo(instance2.GetHashCode()), 
            "Equal instances should have equal hash codes");
    }

    /// <summary> Testing Equals and GetHashCode - negative case </summary>
    [Test]
    public void TestEqualsAndHashCode_NegativeCase()
    {
        var instance1 = CreateInstance();
        var instance2 = CreateInstance();
        
        {%- set ns = {'found': false, 'is_complex_non_nullable': false} %}
        {%- for field in fields if not field.is_const %}
        {%- if not ns.found %}
        {%- set _ = ns.update({'found': true}) %}
        // Modify {{ field.field_name }} and verify inequality
        {%- set base_type = field.field_type.rstrip('?') %}
        {%- if base_type == "string" %}
        instance2.{{ field.field_name }} = (instance2.{{ field.field_name }} ?? "test") + "_modified";
        {%- elif base_type in ["int", "long", "short", "byte", "sbyte", "uint", "ulong", "ushort"] %}
        instance2.{{ field.field_name }} = ({{ base_type }})({{ "(instance2." + field.field_name + " ?? 0)" if field.field_type.endswith("?") else "instance2." + field.field_name }} + 1);
        {%- elif base_type in ["float", "double", "decimal"] %}
        instance2.{{ field.field_name }} = {{ "(instance2." + field.field_name + " ?? 0)" if field.field_type.endswith("?") else "instance2." + field.field_name }} + 1.0{{ "f" if base_type == "float" else "m" if base_type == "decimal" else "" }};
        {%- elif base_type == "bool" %}
        instance2.{{ field.field_name }} = !{{ "(instance2." + field.field_name + " ?? false)" if field.field_type.endswith("?") else "instance2." + field.field_name }};
        {%- elif field.field_type.endswith("?") %}
        // For nullable complex types (including global:: types), set to null to make it different
        instance2.{{ field.field_name }} = null;
        {%- elif base_type.startswith("global::") and field.is_enum %}
        // For non-nullable enum, cast to int and increment
        instance2.{{ field.field_name }} = ({{ base_type }})((int)instance2.{{ field.field_name }} + 1);
        {%- else %}
        {%- set _ = ns.update({'is_complex_non_nullable': true}) %}
        {%- endif %}
        {%- if not ns.is_complex_non_nullable %}
        Assert.That(instance1.Equals(instance2), Is.False, 
            "Instances with different {{ field.field_name }} should not be equal");
        {%- endif %}
        {%- endif %}
        {%- endfor %}
        {%- if ns.is_complex_non_nullable %}
        // For non-nullable complex types (like unions), verify the positive case works correctly
        // since we cannot create a meaningfully different value without knowing the type internals
        Assert.Pass("Skipped negative test for complex non-nullable type - positive case validates Equals implementation");
        {%- endif %}
    }

    {%- for field in fields %}
    /// <summary> Testing property {{ field.field_name }}  </summary>
    [Test]
    public void Test{{ field.field_name | replace ('@','') | pascal }}Property()
    {
        var testValue = {{ field.test_value }};
        {%- if not field.is_const %}
        _instance.{{ field.field_name }} = testValue;
        {%- endif %}
        {%- if field.is_primitive %}
        {%- if field.field_type.startswith("List<") or field.field_type.startswith("Dictionary<") %}
        Assert.That(_instance.{{ field.field_name }}, Is.EquivalentTo(testValue));
        {%- else %}
        Assert.That(_instance.{{ field.field_name }}, Is.EqualTo(testValue));
        {%- endif %}
        {%- else %}
        Assert.That(_instance.{{ field.field_name }}, Is.EqualTo(testValue));
        {%- endif %}
    }
    {%- endfor %}
    {%- if avro_annotation %}
    /// <summary> Testing Avro serializer </summary>
    [Test]
    public void Test_ToByteArray_FromData_Avro()
    {
        var mediaType = "application/vnd.apache.avro+avro";
        var bytes = _instance.ToByteArray(mediaType);
        var newInstance = {{ class_base_name }}.FromData(bytes, mediaType);
        Assert.That(newInstance, Is.EqualTo(_instance));
    }
    {%- endif %}
    {%- if protobuf_net_annotation %}
    /// <summary> Testing Protobuf serializer </summary>
    [Test]
    public void Test_ToByteArray_FromData_Protobuf()
    {
        var mediaType = "application/x-protobuf";
        var bytes = _instance.ToByteArray(mediaType);
        var newInstance = {{ class_base_name }}.FromData(bytes, mediaType);
        Assert.That(newInstance, Is.EqualTo(_instance));
    }
    {%- endif %}
    {%- if system_xml_annotation %}
    /// <summary> Testing XML serializer </summary>
    [Test]
    public void Test_ToByteArray_FromData_Xml()
    {
        var mediaType = "application/xml";
        var bytes = _instance.ToByteArray(mediaType);
        var newInstance = {{ class_base_name }}.FromData(bytes, mediaType);
        Assert.That(newInstance, Is.EqualTo(_instance));
    }
    {%- endif %}
    {%- if system_text_json_annotation %}
    /// <summary> Testing JSON serializer </summary>
    [Test]
    public void Test_ToByteArray_FromData_Json()
    {
        var mediaType = "application/json";
        var bytes = _instance.ToByteArray(mediaType);
        var newInstance = {{ class_base_name }}.FromData(bytes, mediaType);
        Assert.That(newInstance, Is.EqualTo(_instance));
    }
    {%- endif %}
    {%- if msgpack_annotation %}
    /// <summary> Testing MsgPack serializer </summary>
    [Test]
    public void Test_ToByteArray_FromData_MsgPack()
    {
        var mediaType = "application/msgpack";
        var bytes = _instance.ToByteArray(mediaType);
        var newInstance = {{ class_base_name }}.FromData(bytes, mediaType);
        Assert.That(newInstance, Is.EqualTo(_instance));
    }
    {%- endif %}
    {%- if avro_annotation or system_text_json_annotation or newtonsoft_json_annotation or system_xml_annotation or msgpack_annotation or protobuf_net_annotation %}

    /// <summary> Testing FromData with edge cases </summary>
    [Test]
    public void Test_FromData_EdgeCases()
    {
        {%- if system_text_json_annotation %}
        // Test null data - FromData returns null for null input
        Assert.That({{ class_base_name }}.FromData((byte[])null!, "application/json"), Is.Null);
        // Test empty data - should throw JsonException for invalid JSON
        Assert.Throws<System.Text.Json.JsonException>(() => {{ class_base_name }}.FromData(Array.Empty<byte>(), "application/json"));
        {%- elif newtonsoft_json_annotation %}
        // Test null data - FromData returns null for null input
        Assert.That({{ class_base_name }}.FromData((byte[])null!, "application/json"), Is.Null);
        // Test empty data - Newtonsoft.Json returns null for empty string
        Assert.That({{ class_base_name }}.FromData(Array.Empty<byte>(), "application/json"), Is.Null);
        {%- elif avro_annotation %}
        // Test null data - FromData returns null for null input
        Assert.That({{ class_base_name }}.FromData((byte[])null!, "avro/binary"), Is.Null);
        {%- elif msgpack_annotation %}
        // Test null data - FromData returns null for null input
        Assert.That({{ class_base_name }}.FromData((byte[])null!, "application/msgpack"), Is.Null);
        {%- elif protobuf_net_annotation %}
        // Test null data - FromData returns null for null input
        Assert.That({{ class_base_name }}.FromData((byte[])null!, "application/x-protobuf"), Is.Null);
        {%- elif system_xml_annotation %}
        // Test null data - FromData returns null for null input
        Assert.That({{ class_base_name }}.FromData((byte[])null!, "application/xml"), Is.Null);
        {%- endif %}
    }
    {%- endif %}
    {%- if system_text_json_annotation %}

    /// <summary> Testing IsJsonMatch </summary>
    [Test]
    public void Test_IsJsonMatch()
    {
        var json = System.Text.Json.JsonSerializer.Serialize(_instance);
        using var doc = System.Text.Json.JsonDocument.Parse(json);
        Assert.That({{ class_base_name }}.IsJsonMatch(doc.RootElement), Is.True);
    }
    {%- endif %}
    {%- if system_text_json_annotation or newtonsoft_json_annotation %}

    /// <summary> Testing Gzip compression </summary>
    [Test]
    public void Test_ToByteArray_Gzip()
    {
        var mediaType = "application/json";
        var gzipMediaType = "application/json+gzip";
        var plainBytes = _instance.ToByteArray(mediaType);
        var gzipBytes = _instance.ToByteArray(gzipMediaType);
        // Gzip compressed data should start with magic bytes 0x1f 0x8b
        Assert.That(gzipBytes.Length >= 2 && gzipBytes[0] == 0x1f && gzipBytes[1] == 0x8b, Is.True,
            "Gzip compressed data should have correct magic bytes");
        var newInstance = {{ class_base_name }}.FromData(gzipBytes, gzipMediaType);
        Assert.That(newInstance, Is.EqualTo(_instance));
    }
    {%- endif %}
    {%- if msgpack_annotation and not (system_text_json_annotation or newtonsoft_json_annotation) %}

    /// <summary> Testing Gzip compression with MessagePack </summary>
    [Test]
    public void Test_ToByteArray_Gzip_MsgPack()
    {
        var mediaType = "application/msgpack";
        var gzipMediaType = "application/msgpack+gzip";
        var plainBytes = _instance.ToByteArray(mediaType);
        var gzipBytes = _instance.ToByteArray(gzipMediaType);
        // Gzip compressed data should start with magic bytes 0x1f 0x8b
        Assert.That(gzipBytes.Length >= 2 && gzipBytes[0] == 0x1f && gzipBytes[1] == 0x8b, Is.True,
            "Gzip compressed data should have correct magic bytes");
        var newInstance = {{ class_base_name }}.FromData(gzipBytes, gzipMediaType);
        Assert.That(newInstance, Is.EqualTo(_instance));
    }
    {%- endif %}
    {%- if protobuf_net_annotation and not (system_text_json_annotation or newtonsoft_json_annotation or msgpack_annotation) %}

    /// <summary> Testing Gzip compression with Protobuf </summary>
    [Test]
    public void Test_ToByteArray_Gzip_Protobuf()
    {
        var mediaType = "application/x-protobuf";
        var gzipMediaType = "application/x-protobuf+gzip";
        var plainBytes = _instance.ToByteArray(mediaType);
        var gzipBytes = _instance.ToByteArray(gzipMediaType);
        // Gzip compressed data should start with magic bytes 0x1f 0x8b
        Assert.That(gzipBytes.Length >= 2 && gzipBytes[0] == 0x1f && gzipBytes[1] == 0x8b, Is.True,
            "Gzip compressed data should have correct magic bytes");
        var newInstance = {{ class_base_name }}.FromData(gzipBytes, gzipMediaType);
        Assert.That(newInstance, Is.EqualTo(_instance));
    }
    {%- endif %}
}
{% endfilter %}
{% if namespace %}
}
{% endif %}
