{%- import "cloudevents.jinja.include" as cloudEvents -%}
{%- import "servicebus.jinja.include" as servicebus -%}
{%- import "util.jinja.include" as util -%}
{{ util.CommonFileHeader() }}
{%- import "amqp.jinja.include" as amqp %}
{%- 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) %}
{%- set function_name = project_name | pascal -%}
using System;
using System.Threading.Tasks;
using Azure;
using Azure.Core;
using Azure.Messaging.ServiceBus;
using Microsoft.Extensions.Logging;
{%- if uses_cloudevents_message %}
{{ servicebus.CloudEventsUsings() }}
{%- endif %}
 
namespace {{ project_name | pascal }}
{
    namespace Tools
    {
        public class DispatcherBase
        {
            {{ servicebus.CloudEventsMethods() | indent(12) }}
        }
    }
} 

{% for messagegroupid, messagegroup in messagegroups.items() -%}
{%- set uses_cloudevents_message = (messagegroup | exists("envelope","CloudEvents/1.0")) %}
{%- set uses_plain_amqp_message = (messagegroup | existswithout( "binding", "amqp", "format", "cloudevents" )) %}
{%- set messagegroupname = messagegroupid  | pascal -%}
{%- set class_name = ( messagegroupname | strip_namespace )+"EventDispatcher" %}
namespace {{ messagegroupname | concat_namespace(project_name) | pascal }} 
{
    public class {{ class_name }} : global::{{ project_name | pascal }}.Tools.DispatcherBase
    {
        {%- if (messagegroup | exists("envelope","CloudEvents/1.0")) %}
        
        {%- endif %}
        {%- for messageid, message in messagegroup.messages.items() -%}
        {%- set messagename = messageid | strip_namespace | pascal -%}
        {%- set message_body_type = util.body_type(data_project_name, root, message) -%}
        {%- if message.description %}   
        /// <summary>
        /// {{ message.description }}
        /// </summary>
        {%- endif -%}
        {%- set isCloudEvent = cloudEvents.isCloudEvent(message) %}
        {%- set is_amqp = amqp.is_amqp(message) %}
        public event Func<ServiceBusReceivedMessage, {%if isCloudEvent%}CloudEvent?, {%endif%}{{message_body_type}}?, Task>? {{ messagename | strip_namespace }}Async;
        {%- endfor %}

        public async Task ProcessMessageAsync(ProcessMessageEventArgs messageArgs)
        {
            {%- if uses_cloudevents_message %}
            if ( IsCloudEvent(messageArgs.Message) )
            {
                var cloudEvent = CloudEventFromServiceBusMessage(messageArgs.Message);
                await DispatchCloudEventAsync(messageArgs.Message, cloudEvent);
            }
            {%- endif %}
            {%- if uses_amqp_message %}
            var serviceBusMessage = messageArgs.Message;
            if (serviceBusMessage != null)
            {
                await DispatchMessageAsync(serviceBusMessage);
            }
            {%- endif %}
        }
        
        {%- if uses_cloudevents_message %}        
        private async Task DispatchCloudEventAsync(ServiceBusReceivedMessage message, CloudEvent cloudEvent)
        {
            var cloudEventType = cloudEvent.Type;
            switch (cloudEventType)
            {
                {% for messageid, message in messagegroup.messages.items() -%}
                {%- set messagename = messageid | pascal %}
                {%- set isCloudEvent = cloudEvents.isCloudEvent(message) %}
                {%- set message_body_type = util.body_type(data_project_name, root, message) -%}
                {%- if "type" in message.envelopemetadata and "value" in message.envelopemetadata["type"] -%}
                case "{{ message.envelopemetadata["type"]["value"] }}":
                {%- else -%}
                case "{{ messageid }}":
                {%- endif %}
                    if ({{ messagename | strip_namespace }}Async != null) 
                        {
                            await Task.WhenAll(Array.ConvertAll({{ messagename | strip_namespace }}Async.GetInvocationList(), e => (Task)(e.DynamicInvoke(message,
                            {%- if isCloudEvent %}cloudEvent, {%- endif -%}
                            {%- if message_body_type != "byte[]" -%}
                            {{ message_body_type }}.FromData(cloudEvent.Data, cloudEvent.DataContentType)
                            {%- else -%}
                            (cloudEvent.Data != null && cloudEvent.Data is BinaryData binaryData) ? binaryData.ToArray() : null
                            {%- endif %})??Task.CompletedTask)));
                        }
                        break;
                {%- endfor %}
                default:
                    break;
            }
        }
        {%- endif %}

        {%- if uses_plain_amqp_message %}
        private async Task DispatchEventDataAsync(ServiceBusReceivedMessage message)
        {
            var messageSubject = message.GetRawAmqpMessage().Properties.Subject;
            switch (messageSubject)
            {
            {% for messageid, message in messagegroup.messages.items() if ((message | exists("protocol","AMQP/1.0")) and not (message | exists("envelope","CloudEvents/1.0"))) -%}
            {%- set messagename = messageid | pascal %}
            {%- set isCloudEvent = cloudEvents.isCloudEvent(message) %}
            {%- set message_body_type = util.body_type(data_project_name, root, message) -%}
                case "{{ messageid }}":
                        if ({{ messagename | strip_namespace }}Async != null) 
                        {
                            await Task.WhenAll(Array.ConvertAll({{ messagename | strip_namespace }}Async.GetInvocationList(), e => (Task)(e.DynamicInvoke(message,
                            {%- if isCloudEvent%}null, {% endif %}
                            {%- if message_body_type != "byte[]" -%}
                            {{ message_body_type }}.FromData(eventData.Body, eventData.ContentType)
                            {%- else -%} 
                            {{ message }}.Data{%- endif %}))));
                        }
                        break;
            {%- endfor %}
                default:
                    break;
            }
        }
        {%- endif %}

        public static ServiceBusProcessor CreateServiceBusProcessor(ServiceBusClient serviceBusClient, string queueName, ServiceBusProcessorOptions options, {{ class_name }} dispatcher) 
        {       
            var serviceBusProcessor = serviceBusClient.CreateProcessor(queueName, options);
            serviceBusProcessor.ProcessMessageAsync += dispatcher.ProcessMessageAsync;
            return serviceBusProcessor;
        }

        public static ServiceBusProcessor CreateServiceBusProcessor(ServiceBusClient serviceBusClient, string topicName, string subscriptionName, ServiceBusProcessorOptions options, {{ class_name }} dispatcher) 
        {       
            var serviceBusProcessor = serviceBusClient.CreateProcessor(topicName, subscriptionName, options);
            serviceBusProcessor.ProcessMessageAsync += dispatcher.ProcessMessageAsync;
            return serviceBusProcessor;
        }
    }
}
{% endfor %}    


{%- if root.endpoints -%} 
{%- for endpointid, endpoint in root.endpoints.items() -%}
{%- set endpointname = endpointid | default(endpointid) | pascal -%}
{%- if endpoint.usage and "consumer" in endpoint.usage -%}
{%- set protocol = endpoint.protocol | lower -%}
{%- if protocol.startswith("amqp") -%}
{%- set options = endpoint.protocoloptions -%}
{%- set endpoints = endpoint.endpoints -%}
{%- set messagegroupuris = endpoint.messagegroups %}

{%- macro MessageGroupArgs() -%}
{%- for uri in messagegroupuris %}
{%- set messagegroup = schema_object( root, uri ) -%}
{%- if messagegroup %}
{%- set messagegroupid = uri.split('/')[-1] -%}
{%- set messagegroupname = messagegroupid | pascal -%}
global::{{ messagegroupname | concat_namespace(project_name) | pascal }}.{{ messagegroupname | strip_namespace | pascal }}EventDispatcher? {{ messagegroupname | strip_namespace | camel }}Dispatcher = null
{%- if not loop.last -%}, {%- endif -%}
{%- endif %}
{%- endfor -%}
{%- endmacro -%}    

{%- macro MessageGroupHooks(arg) -%}
Task.WhenAll(
{%- for uri in messagegroupuris %}
{%- set messagegroup = schema_object( root, uri ) -%}
{% if messagegroup %}
{%- set messagegroupid = uri.split('/')[-1] -%}
{%- set messagegroupname = messagegroupid | pascal -%}
{{- messagegroupname | strip_namespace | camel }}Dispatcher != null?{{- messagegroupname | strip_namespace | camel }}Dispatcher.ProcessMessageAsync({{ arg }}):Task.CompletedTask
{%- if not loop.last -%}, {%- endif -%}
{%- endif %}
{%- endfor -%}
)
{%- endmacro -%}    


namespace {{ endpointname | namespace(project_name) | pascal  }}
{
    {%- set class_name = ( endpointname | strip_namespace )+"ServiceBusProcessorFactory" %}
    public class {{ class_name }} 
    {
        public static ServiceBusProcessor CreateServiceBusProcessorFor{{ endpointid | pascal | strip_namespace }}(ServiceBusClient serviceBusClient, string queueName, ServiceBusProcessorOptions options, {{ MessageGroupArgs() }}) 
        {   
            var serviceBusProcessor = serviceBusClient.CreateProcessor(queueName, options);
            serviceBusProcessor.ProcessMessageAsync += (eventArgs) => {{ MessageGroupHooks("eventArgs") }};
            return serviceBusProcessor;
        }

        public static ServiceBusProcessor CreateEventProcessorClientFor{{ endpointid | pascal | strip_namespace }}(ServiceBusClient serviceBusClient, string topicName, string subscriptionName, ServiceBusProcessorOptions options, {{ MessageGroupArgs() }}) 
        {       
            var serviceBusProcessor = serviceBusClient.CreateProcessor(topicName, subscriptionName, options);
            serviceBusProcessor.ProcessMessageAsync += (eventArgs) => {{ MessageGroupHooks("eventArgs") }};
            return serviceBusProcessor;
        }
    }
}
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{% endif %}
