{%- import "cloudevents.jinja.include" as cloudEvents -%}
{%- import "util.jinja.include" as util -%}
{%- set data_module_name = data_project_name | strip_invalid_identifier_characters %}
// 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.

import { ServiceBusReceivedMessage } from '@azure/service-bus';
{%- set messagegroups = root.messagegroups %}
{%- set uses_cloudevents_message = cloudEvents.usesCloudEvents(root) %}
{%- if uses_cloudevents_message %}
import { CloudEvent, HTTP } from 'cloudevents';
{%- endif %}

// Import data types (need to compile data project first: cd ../{{ data_project_name }} && npm install && npm run build)
// NOTE: In real usage, the data project should be a proper npm package dependency
import * as {{ data_module_name }} from '../../{{ data_project_name }}/dist/index.js';

{% for messagegroupid, messagegroup in messagegroups.items() -%}
{%- set uses_cloudevents_message = (messagegroup | exists("envelope","CloudEvents")) %}
{%- set messagegroupname = messagegroupid | pascal -%}
{%- set class_name = (messagegroupname | strip_namespace) + "EventDispatcher" %}

/**
 * Event dispatcher for the {{ messagegroupid }} message group
 */
export class {{ class_name }} {
    {%- 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) %}
    {%- set isCloudEvent = cloudEvents.isCloudEvent(message) %}
    
    /**
     * Handler for {{ messagename }} messages
     {%- if message.description %}
     * {{ message.description }}
     {%- endif %}
     */
    public {{ messagename }}Handler?: (message: ServiceBusReceivedMessage, {% if isCloudEvent %}cloudEvent: CloudEvent, {% endif %}data: {{ message_body_type }}) => Promise<void>;
    {%- endfor %}
    
    /**
     * Process a Service Bus message
     * @param message The Service Bus received message
     */
    public async processMessage(message: ServiceBusReceivedMessage): Promise<void> {
        {%- if uses_cloudevents_message %}
        if (this.isCloudEvent(message)) {
            const cloudEvent = this.cloudEventFromServiceBusMessage(message);
            await this.dispatchCloudEvent(message, cloudEvent);
            return;
        }
        {%- endif %}
        
        await this.dispatchMessage(message);
    }
    
    {%- if uses_cloudevents_message %}
    
    /**
     * Check if a Service Bus message is a CloudEvent
     */
    private isCloudEvent(message: ServiceBusReceivedMessage): boolean {
        const contentType = message.contentType;
        if (contentType && (contentType.startsWith('application/cloudevents+json') || 
                           contentType.startsWith('application/cloudevents'))) {
            return true;
        }
        
        // Check for binary mode CloudEvent (supports ce-, ce_, cloudEvents:, and cloudEvents_ prefixes)
        if (message.applicationProperties) {
            const props = message.applicationProperties;
            if (props['ce-specversion'] || props['ce_specversion'] || props['cloudEvents:specversion'] || props['cloudEvents_specversion']) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Convert a Service Bus message to a CloudEvent
     */
    private cloudEventFromServiceBusMessage(message: ServiceBusReceivedMessage): CloudEvent {
        const contentType = message.contentType;
        
        // Structured mode
        if (contentType && contentType.startsWith('application/cloudevents+json')) {
            const body = message.body;
            if (typeof body === 'string') {
                return HTTP.toEvent({ body, headers: {} }) as CloudEvent;
            } else if (Buffer.isBuffer(body)) {
                return HTTP.toEvent({ body: body.toString(), headers: {} }) as CloudEvent;
            } else if (typeof body === 'object') {
                return new CloudEvent(body);
            }
        }
        
        // Binary mode (supports ce-, ce_, cloudEvents:, and cloudEvents_ prefixes)
        const props = message.applicationProperties || {};
        const timeValue = props['ce-time'] || props['ce_time'] || props['cloudEvents:time'] || props['cloudEvents_time'];
        const ceData: any = {
            specversion: (props['ce-specversion'] || props['ce_specversion'] || props['cloudEvents:specversion'] || props['cloudEvents_specversion']) as string,
            type: (props['ce-type'] || props['ce_type'] || props['cloudEvents:type'] || props['cloudEvents_type']) as string,
            source: (props['ce-source'] || props['ce_source'] || props['cloudEvents:source'] || props['cloudEvents_source']) as string,
            id: (props['ce-id'] || props['ce_id'] || props['cloudEvents:id'] || props['cloudEvents_id']) as string,
            datacontenttype: message.contentType || 'application/json',
            data: message.body
        };
        if (timeValue) {
            ceData.time = String(timeValue);
        }
        return new CloudEvent(ceData);
    }
    
    /**
     * Dispatch a CloudEvent message
     */
    private async dispatchCloudEvent(message: ServiceBusReceivedMessage, cloudEvent: CloudEvent): Promise<void> {
        const cloudEventType = cloudEvent.type;
        
        switch (cloudEventType) {
            {% for messageid, message in messagegroup.messages.items() -%}
            {%- set messagename = messageid | pascal | strip_namespace %}
            {%- set isCloudEvent = cloudEvents.isCloudEvent(message) %}
            {%- set message_body_type = util.body_type(data_project_name, root, message) %}
            case '{{ messageid }}':
                if (this.{{ messagename }}Handler) {
                    const data = cloudEvent.data as unknown as {{ message_body_type }};
                    await this.{{ messagename }}Handler(message, {% if isCloudEvent %}cloudEvent, {% endif %}data);
                }
                break;
            {% endfor %}
            default:
                break;
        }
    }
    {%- endif %}
    
    /**
     * Dispatch a plain AMQP message
     */
    private async dispatchMessage(message: ServiceBusReceivedMessage): Promise<void> {
        const messageSubject = message.subject;
        
        switch (messageSubject) {
            {% for messageid, message in messagegroup.messages.items() if not cloudEvents.isCloudEvent(message) -%}
            {%- set messagename = messageid | pascal | strip_namespace %}
            {%- set message_body_type = util.body_type(data_project_name, root, message) %}
            case '{{ messageid }}':
                if (this.{{ messagename }}Handler) {
                    const data = message.body as {{ message_body_type }};
                    await this.{{ messagename }}Handler(message, data);
                }
                break;
            {% endfor %}
            default:
                break;
        }
    }
}
{% endfor %}
