{%- import "cloudevents.jinja.include" as cloudEvents %}
{%- import "util.jinja.include" as util -%}
{%- set messagegroups = root.messagegroups %}
{%- set uses_cloudevents_message = (root | exists("envelope","CloudEvents/1.0")) %}
{%- set uses_plain_amqp_message = (root | exists("protocol","AMQP/1.0")) %}
{%- 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 { EventData, ReceivedEventData } from '@azure/event-hubs';
{%- if uses_cloudevents_message %}
import { CloudEvent } 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/1.0")) %}
{%- set uses_plain_amqp_message = (messagegroup | existswithout( "binding", "amqp", "format", "cloudevents" )) %}
{%- set messagegroupname = messagegroupid | pascal -%}
{%- set class_name = ( messagegroupname | strip_namespace ) + "EventDispatcher" %}

/**
 * Dispatcher for {{ 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) %}
    
    {%- if message.description %}
    /**
     * {{ message.description }}
     */
    {%- endif %}
    public {{ messagename }}Handler?: (eventData: ReceivedEventData{% if isCloudEvent %}, cloudEvent: CloudEvent{% endif %}, data: {{ message_body_type }}) => Promise<void>;
    {%- endfor %}
    
    public unhandledMessageHandler?: (eventData: ReceivedEventData) => Promise<void>;

    /**
     * Process an event from Event Hubs
     */
    async processEvent(eventData: ReceivedEventData): Promise<boolean> {
        console.log(`Processing event from partition ${eventData.partitionKey}`);
        
        {%- if uses_cloudevents_message %}
        if (this.isCloudEvent(eventData)) {
            const cloudEvent = this.cloudEventFromEventData(eventData);
            if (await this.dispatchCloudEvent(eventData, cloudEvent)) {
                return true;
            }
        }
        {%- else %}
        if (await this.dispatchEventData(eventData)) {
            return true;
        }
        {%- endif %}
        
        if (this.unhandledMessageHandler) {
            await this.unhandledMessageHandler(eventData);
        }
        return false;
    }

    {%- if uses_cloudevents_message %}
    
    /**
     * Check if event is a CloudEvent
     */
    private isCloudEvent(eventData: ReceivedEventData): boolean {
        if (!eventData.properties) return false;
        
        const contentType = eventData.contentType;
        if (contentType && contentType.startsWith('application/cloudevents')) {
            return true;
        }
        
        for (const key in eventData.properties) {
            if (key.toLowerCase().startsWith('cloudEvents:')) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * Extract CloudEvent from Event Hubs EventData
     */
    private cloudEventFromEventData(eventData: ReceivedEventData): CloudEvent {
        const contentType = eventData.contentType;
        
        // Structured mode
        if (contentType && contentType.startsWith('application/cloudevents')) {
            const eventBody = eventData.body;
            return new CloudEvent(eventBody);
        }
        
        // Binary mode - properties contain CloudEvent attributes
        const ceProps: Record<string, string> = {};
        if (eventData.properties) {
            for (const key in eventData.properties) {
                if (key.toLowerCase().startsWith('cloudevents:')) {
                    const ceKey = key.substring('cloudevents:'.length);
                    const value = eventData.properties[key];
                    ceProps[ceKey] = typeof value === 'string' ? value : String(value);
                }
            }
        }
        
        const cloudEvent = new CloudEvent({
            type: ceProps['type'] || '',
            source: ceProps['source'] || '',
            id: ceProps['id'] || '',
            time: ceProps['time'],
            datacontenttype: ceProps['datacontenttype'] || contentType || 'application/json',
            data: eventData.body,
            ...ceProps
        });
        
        return cloudEvent;
    }
    
    /**
     * Dispatch CloudEvent to appropriate handler
     */
    private async dispatchCloudEvent(eventData: ReceivedEventData, cloudEvent: CloudEvent): Promise<boolean> {
        const cloudEventType = cloudEvent.type;
        console.log(`Dispatching CloudEvent of type ${cloudEventType}`);
        
        switch (cloudEventType) {
            {% for messageid, message in messagegroup.messages.items() if cloudEvents.isCloudEvent(message) -%}
            {%- set messagename = messageid | pascal %}
            {%- 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 (this.{{ messagename | strip_namespace }}Handler) {
                    const data = cloudEvent.data as unknown as {{ message_body_type }};
                    await this.{{ messagename | strip_namespace }}Handler(eventData, cloudEvent, data);
                }
                return true;
            {%- endfor %}
            default:
                return false;
        }
    }
    {%- endif %}

    {%- if uses_plain_amqp_message %}
    
    /**
     * Dispatch plain AMQP event to appropriate handler
     */
    private async dispatchEventData(eventData: ReceivedEventData): Promise<boolean> {
        const subject = eventData.properties?.subject;
        console.log(`Dispatching event with subject ${subject}`);
        
        switch (subject) {
            {% for messageid, message in messagegroup.messages.items() if not cloudEvents.isCloudEvent(message) -%}
            {%- set messagename = messageid | pascal %}
            {%- set message_body_type = util.body_type(data_project_name, root, message) -%}
            case "{{ messageid }}":
                if (this.{{ messagename | strip_namespace }}Handler) {
                    const data = eventData.body as {{ message_body_type }};
                    await this.{{ messagename | strip_namespace }}Handler(eventData, data);
                }
                return true;
            {%- endfor %}
            default:
                return false;
        }
    }
    {%- endif %}
}

{% endfor -%}
