{%- 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")) %}

#nullable enable

using CloudNative.CloudEvents;
using CloudNative.CloudEvents.SystemTextJson;
using Confluent.Kafka;

{% for messagegroupid, messagegroup in messagegroups.items() -%}
{%- set groupname = messagegroupid | pascal -%}
namespace {{ groupname | concat_namespace(project_name) | pascal }}
{
    {%- set class_name = (groupname | strip_namespace) + "Producer" %}
    /// <summary>
    /// Producer class to send events in the `{{ messagegroupid }}` message group.
    /// </summary>
    public partial class {{ class_name }}
    {
        {%- if root.endpoints -%}
        {%- for endpointid, endpoint in root.endpoints.items() -%}
                {%- if endpoint.usage and "producer" in endpoint.usage -%}
        {%- set protocol = endpoint.protocol | lower -%}
        {%- if protocol == "kafka" -%}
        {%- set options = endpoint.protocoloptions -%}
        {%- set endpoints = endpoint.endpoints %}
        {%- macro createforbody(class_name, endpoints, options) -%}
            if (bootstrapServers == null || bootstrapServers.Length == 0)
            {
                throw new ArgumentNullException(nameof(bootstrapServers));
            }
            var config = new ProducerConfig
            {
                BootstrapServers = string.Join(",", bootstrapServers?.Select(uri => uri.AbsoluteUri))
                {%- if options and "acks" in options -%}
                , Acks = Acks.{{ "All" if options.acks == -1 else ("None" if options.acks == 0 else "Leader") }}
                {%- endif %}
            };
            var producer = new ProducerBuilder<byte[], byte[]>(config).Build();
            return new {{ class_name }}(producer, {{ '"' + options.topic + '"' if options and "topic" in options else "null" }});
        {%- endmacro %}

        /// <summary>
        /// Create a new instance of the IProducer&lt;byte[], byte[]&gt; for the {{ endpointid }} endpoint.
        /// </summary>
        /// <returns>A new instance of the IProducer&lt;byte[], byte[]&gt;.</returns>
        public static {{ class_name }} CreateFor{{ endpointid | pascal | strip_namespace }}(Uri[]? bootstrapServers) 
        {   
            {{ createforbody(class_name, endpoints, options) }}
        }
        {%- endif -%}
        {%- endif -%}
        {%- endfor -%}
        {% endif %}

        /// <summary>
        /// Constructor
        /// </summary>
        public {{ class_name }}(IProducer<byte[], byte[]> producer, string? defaultTopic = null)
        {
            this.Producer = producer;
            this.DefaultTopic = defaultTopic;
        }

        /// <summary>
        /// Kafka producer client
        /// </summary>
        public IProducer<byte[], byte[]> Producer
        {
            get; private set;
        }

        /// <summary>
        /// Default topic for messages (from endpoint configuration)
        /// </summary>
        public string? DefaultTopic
        {
            get; private set;
        }

        {% for messageid, message in messagegroup.messages.items() -%}
        {%- set messagename = messageid | pascal %}
        {%- set isCloudEvent = cloudEvents.isCloudEvent(message) %}
        {%- set isKafka = not isCloudEvent and message.envelope.lower().startswith("kafka") %}
        {%- set type_name = util.body_type(data_project_name, root, message) -%}
        {%- if isCloudEvent %}
        {%- set uriargs = cloudEvents.DeclareUriTemplateArguments(message) -%}
        {%- elif isKafka %}
        {%- set uriargs = kafka.DeclareUriTemplateArguments(message) -%}
        {%- endif %}
        /// <summary>
        /// Publish the `{{ messagename }}` event
        {%- if message.description %}
        /// Event description: "{{ message.description }}"
        {%- endif %}
        /// </summary>
        /// <param name="data">The event data object.</param>
        {%- if uriargs %}
        {%- for arg in uriargs.split(',') if arg.strip() %}
        {%- set splitarg = arg.strip().split(' ') %}
        /// <param name="{{ splitarg[1] }}"> URI template argument</param>
        {%- endfor %}
        {%- endif %}
        /// <param name="contentType">The desired content type of the message data.</param>
        {%- if isCloudEvent %}
        /// <param name="formatter">The CloudEvent formatter to use for structured mode, e.g. JsonEventFormatter.</param>
        {%- endif %}
        {%- if not ("protocolmetadata" in message and "topic" in message.protocolmetadata and "value" in message.protocolmetadata.topic) %}
        /// <param name="topic">The Kafka topic to send to. Required when not specified in endpoint or message metadata.</param>
        {%- endif %}
        /// <returns>A `Task` object representing the asynchronous publish operation.</returns>
        public Task Send{{ messagename | strip_namespace }}Async(
        {{ type_name }} data
        {%- if uriargs -%}
        {{- uriargs -}}
        {%- endif -%}
        {%- if isCloudEvent and "datacontenttype" in message.envelopemetadata and "value" in message.envelopemetadata["datacontenttype"] -%}
        , string contentType = "{{ message.envelopemetadata["datacontenttype"]["value"] }}"
        {%- else -%}
        , string contentType = System.Net.Mime.MediaTypeNames.Application.Json
        {%- endif %}
        {%- if isCloudEvent %}, CloudEventFormatter? formatter = null{% endif %}
        {%- if not ("protocolmetadata" in message and "topic" in message.protocolmetadata and "value" in message.protocolmetadata.topic) %}
        , string? topic = null
        {%- endif %})
        {
            var message = {{groupname | strip_namespace}}EventFactory.Create{{ messagename | strip_namespace }}Message(data,
                {%- if uriargs %}
                {%- for arg in uriargs.split(',') if arg.strip() %}
                {%- set splitarg = arg.strip().split(' ') %}
                {{- splitarg[1] -}},
                {%- endfor -%}
                {%- endif -%}
                contentType
                {%- if isCloudEvent %}, formatter{% endif -%}
            );
            {%- if "protocolmetadata" in message and "topic" in message.protocolmetadata and "value" in message.protocolmetadata.topic %}
            string resolvedTopic = "{{ message.protocolmetadata.topic.value }}";
            {%- else %}
            string resolvedTopic = topic ?? this.DefaultTopic ?? throw new ArgumentNullException(nameof(topic), "Topic must be specified either in metadata, endpoint configuration, or as a parameter");
            {%- endif %}
            return this.Producer.ProduceAsync(resolvedTopic, message);
        }

        /// <summary>
        /// Send a batch of `{{ messagename }}` events
        {%- if message.description %}
        /// Event description: "{{ message.description }}"
        {%- endif %}
        /// </summary>
        /// <param name="data">The event data objects.</param>
        {%- if uriargs %}
        {%- for arg in uriargs.split(',') if arg.strip() %}
        {%- set splitarg = arg.strip().split(' ') %}
        /// <param name="{{ splitarg[1] }}"> URI template argument</param>
        {%- endfor %}
        {%- endif %}
        /// <param name="contentType">The desired content type of the message data.</param>
        {%- if isCloudEvent %}
        /// <param name="formatter">The CloudEvent formatter to use for structured mode, e.g. JsonEventFormatter.</param>
        {%- endif %}
        {%- if not ("protocolmetadata" in message and "topic" in message.protocolmetadata and "value" in message.protocolmetadata.topic) %}
        /// <param name="topic">The Kafka topic to send to. Required when not specified in endpoint or message metadata.</param>
        {%- endif %}
        /// <returns>A `Task` object representing the asynchronous batch publish operation.</returns>
        public async Task Send{{ messagename | strip_namespace }}BatchAsync(
        {{ type_name }}[] data
        {%- if uriargs -%}
        {{- uriargs -}}
        {%- endif -%}
        {%- if isCloudEvent and "datacontenttype" in message.envelopemetadata and "value" in message.envelopemetadata["datacontenttype"] -%}
        , string contentType = "{{ message.envelopemetadata["datacontenttype"]["value"] }}"
        {%- else -%}
        , string contentType = System.Net.Mime.MediaTypeNames.Application.Json
        {%- endif %}
        {%- if isCloudEvent %}, CloudEventFormatter? formatter = null{% endif %}
        {%- if not ("protocolmetadata" in message and "topic" in message.protocolmetadata and "value" in message.protocolmetadata.topic) %}
        , string? topic = null
        {%- endif %})
        {
            var messages = data.Select(record =>
                {{groupname | strip_namespace}}EventFactory.Create{{ messagename | strip_namespace }}Message(record,
                    {%- if uriargs %}
                    {%- for arg in uriargs.split(',') if arg.strip() %}
                    {%- set splitarg = arg.strip().split(' ') %}
                    {{- splitarg[1] -}},
                    {%- endfor -%}
                    {%- endif -%}
                    contentType
                    {%- if isCloudEvent %}, formatter{% endif -%}
                )
            ).ToArray();
            
            {%- if "protocolmetadata" in message and "topic" in message.protocolmetadata and "value" in message.protocolmetadata.topic %}
            string resolvedTopic = "{{ message.protocolmetadata.topic.value }}";
            {%- else %}
            string resolvedTopic = topic ?? this.DefaultTopic ?? throw new ArgumentNullException(nameof(topic), "Topic must be specified either in metadata, endpoint configuration, or as a parameter");
            {%- endif %}
            foreach (var message in messages)
            {
                await this.Producer.ProduceAsync(resolvedTopic, message);
            }
        }
        {% endfor %}
    }
}
{% endfor -%}
