{%- import "cloudevents.jinja.include" as cloudEvents -%}
{%- import "kafka.jinja.include" as kafka -%}
{%- import "util.jinja.include" as util -%}
{{ util.CommonFileHeader() }}
{%- set messagegroups = root.messagegroups %}
{%- set uses_cloudevents_message = (root | exists("envelope","CloudEvents/1.0")) %}
{%- set uses_kafka_message = (root | exists( "protocol", "kafka")) %}
{%- set uses_kafka_endpoint = (root | exists("protocol", "kafka")) %}
// This code was generated by the xRegistry tool.
// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.

#nullable enable

using System;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Threading;
using Xunit;
using Microsoft.Extensions.Logging;
using DotNet.Testcontainers.Containers;
using DotNet.Testcontainers.Builders;
using Confluent.Kafka;
using Confluent.Kafka.Admin;
using Testcontainers.Kafka;

using {{ project_name | pascal }};
{%- for messagegroupid, messagegroup in messagegroups.items() %}
{%- set pascal_group_name = messagegroupid | pascal %}
using {{ project_name | pascal }}.{{ pascal_group_name | pascal  }};
{%- endfor %}
{%- if uses_cloudevents_message %}
using CloudNative.CloudEvents;
using CloudNative.CloudEvents.SystemTextJson;
{%- endif %}

namespace {{ project_name | pascal }}.Test
{
    public class {{ project_name | pascal | strip_dots}}Fixture : IAsyncLifetime
    {
        public KafkaContainer? KafkaContainerInstance { get; protected set; }
        public string? KafkaBootstrapServers { get; protected set; }
        private ILoggerFactory _loggerFactory;
        private ILogger _logger;

        public ILoggerFactory GetLoggerFactory()
        {
            return _loggerFactory;
        }

        public string TopicName 
        {
            get
            {
                return "testttopic";
            }
        }

        public {{ project_name | pascal | strip_dots}}Fixture()
        {
            _loggerFactory = LoggerFactory.Create(builder =>
            {
                builder.AddDebug().AddConsole();
            });
            _logger = _loggerFactory.CreateLogger<{{ project_name | pascal | strip_dots}}Fixture>();
        }

        public async Task InitializeAsync()
        {
            try
            {
                KafkaContainerInstance = new KafkaBuilder()
                        .Build();

                await KafkaContainerInstance.StartAsync();
                _logger.LogDebug("Kafka container started.");

                KafkaBootstrapServers = KafkaContainerInstance.GetBootstrapAddress();
                _logger.LogDebug($"Kafka bootstrap servers: {KafkaBootstrapServers}");

                await CreateKafkaTopic(TopicName, KafkaContainerInstance.GetBootstrapAddress());
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "An error occurred during InitializeAsync");
                throw;
            }
        }

        private static async Task CreateKafkaTopic(string topicName, string bootstrapServers)
        {
            using var adminClient = 
                new AdminClientBuilder(new AdminClientConfig { BootstrapServers = bootstrapServers }).Build();

            await adminClient.CreateTopicsAsync(new TopicSpecification[]
            {
                new() { Name = topicName, ReplicationFactor = 1, NumPartitions = 1 }
            });
        }

        public async Task DisposeAsync()
        {
            try
            {
                if (KafkaContainerInstance != null)
                {
                    await KafkaContainerInstance.StopAsync();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"An error occurred during DisposeAsync: {ex.Message}");
            }
        }
    }

    [CollectionDefinition("Kafka container")]
    public class {{ project_name | pascal | strip_dots}}CollectionFixture : ICollectionFixture<{{ project_name | pascal | strip_dots}}Fixture>
    {
    }


    {%- for messagegroupid, messagegroup in messagegroups.items() %}
    {%- set pascal_group_name = messagegroupid | pascal %}
    {%- set class_name = "global::" + ( pascal_group_name | concat_namespace(project_name) | pascal ) + '.' + ( pascal_group_name | strip_namespace | pascal ) + "Producer" %}
    {%- set test_class_name = ( project_name | strip_dots | pascal )+( pascal_group_name | strip_dots )+"ProducerTests" %}
    [Collection("Kafka container")]
    public class {{ test_class_name }} 
    {
        private readonly {{ project_name | pascal | strip_dots }}Fixture _fixture;
        private readonly ILogger _logger;
        private readonly {{ class_name }} _producer;

        public {{ test_class_name }}({{ project_name | pascal | strip_dots}}Fixture fixture)
        {
            _fixture = fixture;
            _logger = _fixture.GetLoggerFactory().CreateLogger<{{ test_class_name }}>();
            var producerConfig = new ProducerConfig { BootstrapServers = _fixture.KafkaBootstrapServers };
            var producerBuilder = new ProducerBuilder<byte[], byte[]>(producerConfig).Build();
            _producer = new {{ class_name }}(producerBuilder);
        }

#pragma warning disable CS8604 // init takes care of nullables 

        {%- for messageid, message in messagegroup.messages.items() %}
        {%- set messagename = messageid | strip_namespace | pascal %}
        {%- set isCloudEvent = cloudEvents.isCloudEvent(message) %}
        {%- set isKafka = not isCloudEvent and message.envelope.lower().startswith("kafka") %}
        [Fact]
        public async Task Test{{ messagename }}Message()
        {
            _logger.LogInformation("Starting Test{{ messagename }}Message");
            try
            {   
                {%- set message_body_type = util.body_type(data_project_name, root, message) -%}
                {%- if isCloudEvent %}
                {%- set uriargs = cloudEvents.DeclareUriTemplateArguments(message) -%}
                {%- elif isKafka %}
                {%- set uriargs = kafka.DeclareUriTemplateArguments(message) -%}
                {%- endif %}
                {%- if message_body_type != "byte[]" %}
                var eventDataTest = new {{ message_body_type }}Tests();
                var eventDataInstance = eventDataTest.CreateInstance();
                await _producer.Send{{ messagename }}Async(eventDataInstance
                {%- if uriargs -%}
                {%- for arg in uriargs.split(',') if arg.strip() -%}
                , {{ arg.strip().split(' ')[1] -}}: "test"
                {%- endfor -%}
                {%- endif -%}
                {%- if not (message.protocolmetadata and "topic" in message.protocolmetadata) -%}
                , topic: _fixture.TopicName
                {%- endif -%}
                );
                {%- else %}
                await _producer.Send{{ messagename }}Async(new byte[0]
                {%- if uriargs -%}
                {%- for arg in uriargs.split(',') if arg.strip() -%}
                , {{ arg.strip().split(' ')[1] -}}: "test"
                {%- endfor -%}
                {%- endif -%}
                {%- if not (message.protocolmetadata and "topic" in message.protocolmetadata) -%}
                , topic: _fixture.TopicName
                {%- endif -%}
                );
                {%- endif %}
                _logger.LogInformation("Test event sent");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "An error occurred during Test{{ messagename }}Message");
                throw;
            }
        }

        [Fact]
        public async Task Test{{ messagename }}Batch()
        {
            _logger.LogInformation("Starting Test{{ messagename }}Batch");
            try
            {   
                {%- set message_body_type = util.body_type(data_project_name, root, message) -%}
                {%- if isCloudEvent %}
                {%- set uriargs = cloudEvents.DeclareUriTemplateArguments(message) -%}
                {%- elif isKafka %}
                {%- set uriargs = kafka.DeclareUriTemplateArguments(message) -%}
                {%- endif %}
                {%- if message_body_type != "byte[]" %}
                var eventDataTest = new {{ message_body_type }}Tests();
                var eventDataInstances = new {{ message_body_type }}[10];
                for (int i = 0; i < 10; i++)
                {
                    eventDataInstances[i] = eventDataTest.CreateInstance();
                }
                await _producer.Send{{ messagename }}BatchAsync(eventDataInstances
                {%- if uriargs -%}
                {%- for arg in uriargs.split(',') if arg.strip() -%}
                , {{ arg.strip().split(' ')[1] -}}:"test"
                {%- endfor -%}
                {%- endif -%}
                {%- if not (message.protocolmetadata and "topic" in message.protocolmetadata) -%}
                , topic: _fixture.TopicName
                {%- endif -%}
                );
                {%- else %}
                var eventDataInstances = new byte[10][];
                for (int i = 0; i < 10; i++)
                {
                    eventDataInstances[i] = new byte[0];
                }
                await _producer.Send{{ messagename }}BatchAsync(eventDataInstances
                {%- if uriargs -%}
                {%- for arg in uriargs.split(',') if arg.strip() -%}
                , {{ arg.strip().split(' ')[1] -}}:"test"
                {%- endfor -%}
                {%- endif -%}
                {%- if not (message.protocolmetadata and "topic" in message.protocolmetadata) -%}
                , topic: _fixture.TopicName
                {%- endif -%}
                );
                {%- endif %}
                _logger.LogInformation("Test batch event sent");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "An error occurred during Test{{ messagename }}Batch");
                throw;
            }
        }
        {%- endfor %}
    }
    {%- endfor %}
}
