{%- 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_kafka_message = (root | exists( "protocol", "kafka")) %}
{%- 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 { Producer, ProducerRecord, Message, Kafka } from 'kafkajs';
import { CloudEvent } from 'cloudevents';
import { v4 as uuidv4 } from 'uuid';

// 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 groupname = messagegroupid | pascal -%}
{%- set class_name = (groupname | strip_namespace) + "Producer" %}

/**
 * Producer class to send events in the `{{ messagegroupid }}` message group.
 */
export class {{ class_name }} {
    private producer: Producer;
    private topic: string;
    private mode: 'binary' | 'structured';
    private format: string;
    
    /**
     * Create a new {{ class_name }}
     * @param producer The KafkaJS producer instance
     * @param topic The Kafka topic to send messages to (optional, defaults to message-specific topics)
     * @param mode CloudEvents binding mode: 'binary' or 'structured' (default: 'structured')
     * @param format CloudEvents format for structured mode (default: 'application/json')
     */
    constructor(producer: Producer, topic?: string, mode: 'binary' | 'structured' = 'structured', format: string = 'application/json') {
        this.producer = producer;
        this.topic = topic || '{{ messagegroupid }}';
        this.mode = mode;
        this.format = format;
    }
    
    {%- 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 endpoints = endpoint.endpoints %}
    {%- set mode = endpoint.envelopeoptions.mode if endpoint.envelopeoptions and endpoint.envelopeoptions.mode else 'structured' %}
    {%- set format = endpoint.envelopeoptions.format if endpoint.envelopeoptions and endpoint.envelopeoptions.format else 'application/json' %}
    
    /**
     * Create a new producer for the {{ endpointid }} endpoint
     * @param kafka The Kafka instance
     * @returns A new {{ class_name }} instance
     */
    static async createFor{{ endpointid | pascal | strip_namespace }}(kafka: Kafka): Promise<{{ class_name }}> {
        const producer = kafka.producer();
        await producer.connect();
        return new {{ class_name }}(producer, undefined, '{{ mode }}', '{{ format }}');
    }
    {%- endif -%}
    {%- endif -%}
    {%- endfor -%}
    {% endif %}

    {% for messageid, message in messagegroup.messages.items() -%}
    {%- set messagename = messageid | pascal %}
    {%- set isCloudEvent = cloudEvents.isCloudEvent(message) %}
    {%- set isKafka = not isCloudEvent and (message | exists("protocol", "kafka")) %}
    {%- set type_name = util.body_type(data_project_name, root, message) -%}
    {%- if message.description %}
    /**
     * {{ message.description }}
     */
    {%- endif %}
    
    /**
     * Send a {{ messagename }} event
     * @param data The event data
     {%- if isCloudEvent %}
     {%- for attrname in ['source', 'type'] if attrname not in message.envelopemetadata %}
     * @param {{ attrname }} CloudEvent required attribute '{{ attrname }}'
     {%- endfor %}
     {%- for attrname, attribute in message.envelopemetadata.items() if attribute.type == "uritemplate" %}
     {%- for placeholder in attribute.value | regex_search('\\{([A-Za-z0-9_]+)\\}') %}
     * @param {{ placeholder }} Value for placeholder {{ placeholder }} in attribute {{ attrname }}
     {%- endfor %}
     {%- endfor %}
     {%- for attrname, attribute in message.envelopemetadata.items() if attribute.value is not defined and attrname not in ["time", "id", "datacontenttype", "dataschema"] %}
     * @param {{ attrname }} CloudEvent {{ attrname }} attribute{% if not attribute.required %} (optional){% endif %}
     {%- endfor %}
     {%- endif %}
     * @param contentType The content type (defaults to application/json)
     * @returns Promise that resolves when the message is sent
     */
    async send{{ messagename | strip_namespace }}(
        data: {{ type_name }},
        {%- if isCloudEvent %}
        {%- for attrname in ['source', 'type'] if attrname not in message.envelopemetadata %}
        {{ attrname }}: string,
        {%- endfor %}
        {%- for attrname, attribute in message.envelopemetadata.items() if attribute.type == "uritemplate" %}
        {%- for placeholder in attribute.value | regex_search('\\{([A-Za-z0-9_]+)\\}') %}
        {{ placeholder }}: string,
        {%- endfor %}
        {%- endfor %}
        {%- for attrname, attribute in message.envelopemetadata.items() if attribute.value is not defined and attrname not in ["time", "id", "datacontenttype", "dataschema"] %}
        {{ attrname }}{% if not attribute.required %}?{% endif %}: string,
        {%- endfor %}
        {%- endif %}
        contentType: string = 'application/json'
    ): Promise<void> {
        const message = this.create{{ messagename | strip_namespace }}Message(
            data,
            {%- if isCloudEvent %}
            {%- for attrname in ['source', 'type'] if attrname not in message.envelopemetadata %}
            {{ attrname }},
            {%- endfor %}
            {%- for attrname, attribute in message.envelopemetadata.items() if attribute.type == "uritemplate" %}
            {%- for placeholder in attribute.value | regex_search('\\{([A-Za-z0-9_]+)\\}') %}
            {{ placeholder }},
            {%- endfor %}
            {%- endfor %}
            {%- for attrname, attribute in message.envelopemetadata.items() if attribute.value is not defined and attrname not in ["time", "id", "datacontenttype", "dataschema"] %}
            {{ attrname }},
            {%- endfor %}
            {%- endif %}
            contentType
        );
        
        await this.producer.send({
            topic: this.topic,
            messages: [message]
        });
    }
    
    /**
     * Send a batch of {{ messagename }} events
     * @param dataArray Array of event data
     {%- if isCloudEvent %}
     {%- for attrname in ['source', 'type'] if attrname not in message.envelopemetadata %}
     * @param {{ attrname }} CloudEvent required attribute '{{ attrname }}'
     {%- endfor %}
     {%- for attrname, attribute in message.envelopemetadata.items() if attribute.type == "uritemplate" %}
     {%- for placeholder in attribute.value | regex_search('\\{([A-Za-z0-9_]+)\\}') %}
     * @param {{ placeholder }} Value for placeholder {{ placeholder }} in attribute {{ attrname }}
     {%- endfor %}
     {%- endfor %}
     {%- for attrname, attribute in message.envelopemetadata.items() if attribute.value is not defined and attrname not in ["time", "id", "datacontenttype", "dataschema"] %}
     * @param {{ attrname }} CloudEvent {{ attrname }} attribute{% if not attribute.required %} (optional){% endif %}
     {%- endfor %}
     {%- endif %}
     * @param contentType The content type (defaults to application/json)
     * @returns Promise that resolves when all messages are sent
     */
    async send{{ messagename | strip_namespace }}Batch(
        dataArray: {{ type_name }}[],
        {%- if isCloudEvent %}
        {%- for attrname in ['source', 'type'] if attrname not in message.envelopemetadata %}
        {{ attrname }}: string,
        {%- endfor %}
        {%- for attrname, attribute in message.envelopemetadata.items() if attribute.type == "uritemplate" %}
        {%- for placeholder in attribute.value | regex_search('\\{([A-Za-z0-9_]+)\\}') %}
        {{ placeholder }}: string,
        {%- endfor %}
        {%- endfor %}
        {%- for attrname, attribute in message.envelopemetadata.items() if attribute.value is not defined and attrname not in ["time", "id", "datacontenttype", "dataschema"] %}
        {{ attrname }}{% if not attribute.required %}?{% endif %}: string,
        {%- endfor %}
        {%- endif %}
        contentType: string = 'application/json'
    ): Promise<void> {
        const messages = dataArray.map(data => 
            this.create{{ messagename | strip_namespace }}Message(
                data,
                {%- if isCloudEvent %}
                {%- for attrname in ['source', 'type'] if attrname not in message.envelopemetadata %}
                {{ attrname }},
                {%- endfor %}
                {%- for attrname, attribute in message.envelopemetadata.items() if attribute.type == "uritemplate" %}
                {%- for placeholder in attribute.value | regex_search('\\{([A-Za-z0-9_]+)\\}') %}
                {{ placeholder }},
                {%- endfor %}
                {%- endfor %}
                {%- for attrname, attribute in message.envelopemetadata.items() if attribute.value is not defined and attrname not in ["time", "id", "datacontenttype", "dataschema"] %}
                {{ attrname }},
                {%- endfor %}
                {%- endif %}
                contentType
            )
        );
        
        await this.producer.send({
            topic: this.topic,
            messages
        });
    }
    
    /**
     * Create a Kafka message for {{ messagename }}
     */
    private create{{ messagename | strip_namespace }}Message(
        data: {{ type_name }},
        {%- if isCloudEvent %}
        {%- for attrname in ['source', 'type'] if attrname not in message.envelopemetadata %}
        {{ attrname }}: string,
        {%- endfor %}
        {%- for attrname, attribute in message.envelopemetadata.items() if attribute.type == "uritemplate" %}
        {%- for placeholder in attribute.value | regex_search('\\{([A-Za-z0-9_]+)\\}') %}
        {{ placeholder }}: string,
        {%- endfor %}
        {%- endfor %}
        {%- for attrname, attribute in message.envelopemetadata.items() if attribute.value is not defined and attrname not in ["time", "id", "datacontenttype", "dataschema"] %}
        {{ attrname }}{% if not attribute.required %}?{% endif %}: string,
        {%- endfor %}
        {%- endif %}
        contentType: string = 'application/json'
    ): Message {
        {%- if isCloudEvent %}
        const cloudEvent = new CloudEvent({
        {%- for attrname in ['source', 'type'] if attrname not in message.envelopemetadata %}
        {%- if attrname == "type" %}
            type: {{ attrname }},
        {%- elif attrname == "source" %}
            source: {{ attrname }},
        {%- endif %}
        {%- endfor %}
        {%- for attrname, attribute in message.envelopemetadata.items() %}
        {%- if attrname == "type" %}
            type: {% if attribute.value %}'{{ attribute.value }}'{% else %}{{ attrname }}{% endif %},
        {%- elif attrname == "source" %}
            source: {% if attribute.value %}{% if attribute.type == "uritemplate" %}`{{ attribute.value | regex_replace('\\{([A-Za-z0-9_]+)\\}', '${\\1}') }}`{% else %}'{{ attribute.value }}'{% endif %}{% else %}{{ attrname }} || `{{ messagegroupid }}/producer`{% endif %},
        {%- elif attrname in ["id", "time"] %}
            {# id and time are auto-generated #}
        {%- elif attrname == "datacontenttype" %}
            {# handled separately #}
        {%- elif attrname == "dataschema" %}
            {# handled separately if needed #}
        {%- elif attribute.value %}
            {{ attrname }}: {% if attribute.type == "uritemplate" %}`{{ attribute.value | regex_replace('\\{([A-Za-z0-9_]+)\\}', '${\\1}') }}`{% else %}'{{ attribute.value }}'{% endif %},
        {%- else %}
            {% if not attribute.required %}...({{ attrname }} && { {{ attrname }} }),{% else %}{{ attrname }},{% endif %}
        {%- endif %}
        {%- endfor %}
            id: uuidv4(),
            datacontenttype: contentType,
            data: data
        });
        
        if (this.mode === 'structured') {
            // Structured content mode - entire CloudEvent as JSON payload
            return {
                headers: {
                    'content-type': this.format
                } as Record<string, string>,
                value: Buffer.from(JSON.stringify(cloudEvent))
            };
        } else {
            // Binary content mode - CloudEvent attributes as headers, data as payload
            const headers: Record<string, string> = {
                'content-type': contentType,
                'ce_specversion': '1.0',
                'ce_id': cloudEvent.id,
                'ce_type': cloudEvent.type,
                'ce_source': cloudEvent.source
            };
            
            {%- for attrname, attribute in message.envelopemetadata.items() %}
            {%- if attrname not in ["id", "type", "source", "specversion", "datacontenttype", "data"] %}
            if (cloudEvent.{{ attrname }}) {
                headers['ce_{{ attrname }}'] = String(cloudEvent.{{ attrname }});
            }
            {%- endif %}
            {%- endfor %}
            
            return {
                headers,
                value: Buffer.from(JSON.stringify(data))
            };
        }
        {%- else %}
        return {
            headers: {
                'subject': '{{ messageid }}',
                'content-type': contentType
            } as Record<string, string>,
            value: Buffer.from(JSON.stringify(data))
        };
        {%- endif %}
    }
    {% endfor %}
    
    /**
     * Disconnect the producer
     */
    async disconnect(): Promise<void> {
        await this.producer.disconnect();
    }
}

{% endfor -%}
