{%- import "cloudevents.jinja.include" as cloudEvents -%}
{%- import "amqp.jinja.include" as amqp -%}
{%- import "util.jinja.include" as util -%}
{{ util.CommonFileHeader() }}
{%- set messagegroups = root.messagegroups %}
{%- set uses_cloudevents_message = (root | exists("envelope","CloudEvents/1.0")) %}
{%- set uses_amqp_message = amqp.uses_amqp_protocol(root) %}
{%- set uses_amqp_endpoint = amqp.uses_amqp_endpoint(root) %}
// 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 CloudNative.CloudEvents;
using CloudNative.CloudEvents.SystemTextJson;
using Azure.Messaging.EventHubs;

{% for messagegroupid, messagegroup in messagegroups.items() -%}
{%- set groupname = messagegroupid  | pascal -%}
namespace {{ groupname | concat_namespace(project_name) | pascal }}
{
    {%- set class_name = ( groupname | strip_namespace )+"EventFactory" %}
    /// <summary>
    /// Factory class to create Azure Event Hubs `EventData` for the messages declared in the `{{ messagegroupid }}` message group.
    /// </summary>
    public partial class {{ class_name }}
    {
        {%- if uses_cloudevents_message %}
        private const string cePrefix = "cloudEvents_";
        private static CloudEventFormatter defaultFormatter = new JsonEventFormatter();
        private const string applicationOctetStream = "application/octet-stream";
        {%- endif %}
        
        private delegate byte[] Serialize<T>(T data, string contentType);
        
        {%- 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 == "amqp" -%}
        {%- set options = endpoint.protocoloptions -%}
        {%- set endpoints = endpoint.endpoints %}
        {%- macro createforbody(class_name, endpoints, options) -%}
            if ( fullyQualifiedNamespace == null )
            {
                {%- if endpoints %}
                fullyQualifiedNamespace = "{{ endpoints[0].uri }}";
                {%- else %}
                throw new ArgumentNullException(nameof(fullyQualifiedNamespace));
                {%- endif %}
            }
            if ( eventHubName == null )
            {
                {%- if options and 'node' in options %}
                eventHubName = "{{ options['node'] }}";
                {%- else %}
                throw new ArgumentNullException(nameof(eventHubName));
                {%- endif %}
            }
            return new EventHubProducerClient(fullyQualifiedNamespace, eventHubName, credential);
        {%- endmacro %}
        
        /// <summary>
        /// Create a new instance of the `EventHubProducerClient` for the {{ endpointid }} endpoint.
        /// </summary>
        /// <param name="credential">The Azure credential to use for authentication.</param>
        /// <param name="fullyQualifiedNamespace">The fully qualified namespace of the Event Hub.</param>
        /// <param name="eventHubName">The name of the Event Hub.</param>
        /// <returns>A new instance of the `EventHubProducerClient`.</returns>
        public static EventHubProducerClient CreateFor{{ endpointid | pascal | strip_namespace }}(TokenCredential credential, string? fullyQualifiedNamespace = null, string? eventHubName = null) 
        {   
            {{ createforbody(class_name, endpoints, options) }}
        }

        /// <summary>
        /// Create a new instance of the `EventHubProducerClient` for the {{ endpointid }} endpoint.
        /// </summary>
        /// <param name="credential">The Azure credential to use for authentication.</param>
        /// <param name="fullyQualifiedNamespace">The fully qualified namespace of the Event Hub.</param>
        /// <param name="eventHubName">The name of the Event Hub.</param>
        /// <returns>A new instance of the `EventHubProducerClient`.</returns>
        public static EventHubProducerClient CreateFor{{ endpointid | pascal | strip_namespace }}(AzureNamedKeyCredential credential, string? fullyQualifiedNamespace = null, string? eventHubName = null) 
        {       
            {{ createforbody(class_name, endpoints, options) }}
        }

        /// <summary>
        /// Create a new instance of the `EventHubProducerClient` for the {{ endpointid }} endpoint.
        /// </summary>
        /// <param name="credential">The Azure credential to use for authentication.</param>
        /// <param name="fullyQualifiedNamespace">The fully qualified namespace of the Event Hub.</param>
        /// <param name="eventHubName">The name of the Event Hub.</param>
        /// <returns>A new instance of the `EventHubProducerClient`.</returns>
        public static EventHubProducerClient CreateFor{{ endpointid | pascal | strip_namespace }}(AzureSasCredential credential, string? fullyQualifiedNamespace = null, string? eventHubName = null) 
        {       
            {{ createforbody(class_name, endpoints, options) }}
        }
        {%- endif -%}
        {%- endif -%}
        {%- endfor -%}
        {% endif %}

        /// <summary>
        /// Create an `EventData` instance for the specified message.
        /// </summary>
        /// <typeparam name="T">The type of the message data.</typeparam>
        {%- if uses_cloudevents_message %}
        /// <param name="cloudEvent">The CloudEvent instance to use for the message.</param>
        {%- endif %}
        /// <param name="data">The message data.</param>
        /// <param name="contentType">The content type of the message data.</param>
        /// <param name="bodySerializer">The serializer to use for the message data.</param>
        {%- if uses_cloudevents_message %}
        /// <param name="formatter">The CloudEvent formatter to use for the message.</param>
        {%- endif %}
        /// <returns>An `EventData` instance for the specified message.</returns>
        private static EventData CreateEventData<T>({% if uses_cloudevents_message %}CloudEvent? cloudEvent, {% endif -%}T data, string contentType, Serialize<T>? bodySerializer = null{% if uses_cloudevents_message %}, CloudEventFormatter? formatter = null{% endif %}) where T : notnull
        {
            {%- set extraindent = 0 %}
            {%- if uses_cloudevents_message %}
            {%- set extraindent = 4 %}
            if (formatter == null)
            {
            {%- endif %}
            {%- filter indent(extraindent) %}
            bool isRawBytes = typeof(T) == typeof(byte[]);
            if (bodySerializer == null && !isRawBytes)
            {
                throw new ArgumentNullException(nameof(bodySerializer));
            }
            var eventPayload = bodySerializer == null ? (byte[])(object)data : bodySerializer(data, contentType);
            EventData eventData = new EventData(eventPayload);
            eventData.ContentType = contentType;
            {%- if uses_cloudevents_message %}
            if (cloudEvent != null)
            {
                foreach (var attr in cloudEvent.GetPopulatedAttributes())
                {
                    var attrValue = (attr.Value is Uri) ? attr.Value.ToString() : attr.Value;
                    if (attrValue != null)
                    {
                        eventData.Properties.Add(cePrefix + attr.Key.Name, attrValue);
                    }
                }
            }
            {%- endif %}
            return eventData;
            {%- endfilter %}
            {%- if uses_cloudevents_message %}
            }            
            else
            {
                if (cloudEvent == null)
                {
                    throw new ArgumentNullException(nameof(cloudEvent));
                }
                if ( formatter is CloudNative.CloudEvents.SystemTextJson.JsonEventFormatter && 
                     new System.Net.Mime.ContentType(contentType).MediaType == System.Net.Mime.MediaTypeNames.Application.Json )
                {
                    cloudEvent.Data = data;
                }
                else
                {
                    bool isRawBytes = typeof(T) == typeof(byte[]);
                    if (bodySerializer == null && !isRawBytes)
                    {
                        throw new ArgumentNullException(nameof(bodySerializer));
                    }
                    cloudEvent.Data = bodySerializer == null ? data : bodySerializer(data, contentType);
                }
                var eventBody = formatter.EncodeStructuredModeMessage(cloudEvent, out var eventContentType);
                var eventData = new EventData(eventBody)
                {
                    ContentType = eventContentType.ToString()
                };
                return eventData;
            }
            {%- endif %}
        }

        {% for messageid, message in messagegroup.messages.items() -%}
        {%- set messagename = messageid | pascal %}
        {%- set isCloudEvent = cloudEvents.isCloudEvent(message) %}
        {%- set is_amqp = amqp.is_amqp(message) %}
        {%- if is_amqp or isCloudEvent %}
        {%- set type_name = util.body_type(data_project_name, root, message) %}
        {%- if isCloudEvent %}
        {%- set uriargs = cloudEvents.DeclareUriTemplateArguments(message) -%}
        {%- elif is_amqp %}
        {%- set uriargs = amqp.DeclareUriTemplateArguments(message) -%}
        {%- endif %}
        /// <summary>
        /// Create an `EventData` instance for 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 %}
        /// <returns>An `EventData` instance for the {{ messagename }} message.</returns>
        public static EventData Create{{ messagename | strip_namespace }}Event(
        {{- 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 type_name != "byte[]" %}
            Serialize<{{ type_name }}>? bodySerializer = {%- if isCloudEvent %}(formatter != null)?null:{%-endif-%}({{ type_name }} data, string contentType) => {
                return data.ToByteArray(contentType);
            };
            {%- if isCloudEvent %}
            {{ cloudEvents.DeclareCloudNativeCloudEvent("cloudEvent", message) | indent(12) }}
            return CreateEventData(cloudEvent, data, contentType, bodySerializer, formatter);
            {%- elif is_amqp %}
            return CreateEventData(data, contentType, bodySerializer);
            {%- endif %}
            {% else %}
            {%- if isCloudEvent %}
            {{ cloudEvents.DeclareCloudNativeCloudEvent("cloudEvent", message) | indent(12) }}
            return CreateEventData(cloudEvent, data, contentType, null, formatter);
            {%- elif is_amqp %}
            return CreateEventData(data, contentType, null);
            {%- endif %}
            {%- endif %}            
        }
        {%- endif %}
        {% endfor %}
    }
}
{% endfor -%}