{%- 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 package_name = project_name | lower | replace('-', '_') %}

{% for messagegroupid, messagegroup in messagegroups.items() -%}
{%- set pascal_group_name = messagegroupid | pascal %}
{%- set group_package = (package_name ~ "." ~ messagegroupid) | lower | replace('-', '_') %}

package {{ group_package }};

import com.azure.messaging.servicebus.ServiceBusMessage;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

{%- if cloudEvents.usesCloudEvents(root) %}
import io.cloudevents.CloudEvent;
import io.cloudevents.core.builder.CloudEventBuilder;
import io.cloudevents.jackson.JsonFormat;
{%- endif %}

import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.UUID;

/**
 * Factory class to create Azure Service Bus messages for the messages declared in the `{{ messagegroupid }}` message group.
 */
public class {{ pascal_group_name | strip_namespace }}MessageFactory {
    private static final Logger logger = LogManager.getLogger({{ pascal_group_name | strip_namespace }}MessageFactory.class);
    {%- if cloudEvents.usesCloudEvents(root) %}
    private static final String CE_PREFIX = "cloudEvents_";
    private static final JsonFormat JSON_FORMAT = new JsonFormat();
    
    /**
     * Content mode for CloudEvents serialization.
     */
    enum ContentMode {
        /** Binary content mode - CloudEvent attributes as properties, data as body */
        BINARY,
        /** Structured content mode - entire CloudEvent as JSON body */
        STRUCTURED
    }
    {%- endif %}

    {%- for messageid, message in messagegroup.messages.items() %}
    {%- set messagename = messageid | pascal | strip_namespace %}
    {%- set message_body_type = util.get_data_type(data_project_name, root, message) %}
    {%- set dataschema = message.dataschemaformat if message.dataschemaformat else message.metadataschemaformat if message.metadataschemaformat else root.dataschemaformat if root.dataschemaformat else "JSON" %}
    {%- set isCloudEvent = cloudEvents.isCloudEvent(message) %}

    /**
     * Create a ServiceBusMessage for the {{ messagename }} message.
     * 
     * @param data The message data
     {%- if isCloudEvent %}
     * @param contentMode The content mode (BINARY or STRUCTURED)
     * @param format The content format (e.g., "application/json")
     {%- endif %}
     * @return A ServiceBusMessage instance
     */
    public static ServiceBusMessage create{{ messagename }}Message({{ message_body_type }} data{% if isCloudEvent %}, ContentMode contentMode, String format{% endif %}) {
        try {
            {%- if isCloudEvent %}
            // Create CloudEvent
            CloudEventBuilder builder = CloudEventBuilder.v1()
                .withId(UUID.randomUUID().toString())
                {%- if message.envelope and message.envelope == "CloudEvents/1.0" %}
                {%- if message.envelopemetadata and message.envelopemetadata.type and message.envelopemetadata.type.value %}
                .withType("{{ message.envelopemetadata.type.value }}")
                {%- else %}
                .withType("{{ messageid }}")
                {%- endif %}
                {%- if message.envelopemetadata and message.envelopemetadata.source %}
                {%- if message.envelopemetadata.source.value and message.envelopemetadata.source.type != "uritemplate" %}
                .withSource(URI.create("{{ message.envelopemetadata.source.value }}"))
                {%- else %}
                .withSource(URI.create("urn:{{ messagegroupid }}"))
                {%- endif %}
                {%- else %}
                .withSource(URI.create("urn:{{ messagegroupid }}"))
                {%- endif %}
                {%- endif %}
                {%- if dataschema and ("Avro" in dataschema or "avro" in dataschema) %}
                .withDataContentType("application/avro")
                .withData(data.toByteArray("avro/binary"))
                {%- elif dataschema and ("Protobuf" in dataschema or "protobuf" in dataschema or "proto" in dataschema) %}
                .withDataContentType("application/protobuf")
                .withData(data.toByteArray())
                {%- else %}
                .withDataContentType("application/json")
                .withData(new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsBytes(data))
                {%- endif %};

            CloudEvent cloudEvent = builder.build();
            
            ServiceBusMessage serviceBusMessage;
            
            if (contentMode == ContentMode.STRUCTURED) {
                // Structured content mode - entire CloudEvent as JSON body
                byte[] bodyData = JSON_FORMAT.serialize(cloudEvent);
                serviceBusMessage = new ServiceBusMessage(bodyData);
                serviceBusMessage.setContentType(format);
            } else {
                // Binary content mode - CloudEvent attributes in properties, data in body
                byte[] bodyData;
                {%- if dataschema and ("Avro" in dataschema or "avro" in dataschema) %}
                bodyData = data.toByteArray("avro/binary");
                {%- elif dataschema and ("Protobuf" in dataschema or "protobuf" in dataschema or "proto" in dataschema) %}
                bodyData = data.toByteArray();
                {%- else %}
                bodyData = new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsBytes(data);
                {%- endif %}

                serviceBusMessage = new ServiceBusMessage(bodyData);
                serviceBusMessage = new ServiceBusMessage(bodyData);
            
                // Set CloudEvent attributes as application properties
                serviceBusMessage.getApplicationProperties().put(CE_PREFIX + "specversion", cloudEvent.getSpecVersion().toString());
                serviceBusMessage.getApplicationProperties().put(CE_PREFIX + "type", cloudEvent.getType());
                serviceBusMessage.getApplicationProperties().put(CE_PREFIX + "source", cloudEvent.getSource().toString());
                serviceBusMessage.getApplicationProperties().put(CE_PREFIX + "id", cloudEvent.getId());
                
                if (cloudEvent.getDataContentType() != null) {
                    serviceBusMessage.setContentType(cloudEvent.getDataContentType());
                    serviceBusMessage.getApplicationProperties().put(CE_PREFIX + "datacontenttype", cloudEvent.getDataContentType());
                }
                if (cloudEvent.getDataSchema() != null) {
                    serviceBusMessage.getApplicationProperties().put(CE_PREFIX + "dataschema", cloudEvent.getDataSchema().toString());
                }
                if (cloudEvent.getSubject() != null) {
                    serviceBusMessage.setSubject(cloudEvent.getSubject());
                    serviceBusMessage.getApplicationProperties().put(CE_PREFIX + "subject", cloudEvent.getSubject());
                }
                if (cloudEvent.getTime() != null) {
                    serviceBusMessage.getApplicationProperties().put(CE_PREFIX + "time", cloudEvent.getTime().toString());
                }

                {%- if message.envelopemetadata %}
                {%- for key, value in message.envelopemetadata.items() %}
                {%- if key not in ['id', 'type', 'source', 'time', 'datacontenttype', 'dataschema', 'subject'] %}
                {%- if value.value %}
                serviceBusMessage.getApplicationProperties().put(CE_PREFIX + "{{ key }}", "{{ value.value }}");
                {%- endif %}
                {%- endif %}
                {%- endfor %}
                {%- endif %}
            }

            // Set message ID from CloudEvent ID
            serviceBusMessage.setMessageId(cloudEvent.getId());

            {%- else %}
            // Create plain Service Bus message
            byte[] bodyData;
            String contentType;
            {%- if dataschema and ("Avro" in dataschema or "avro" in dataschema) %}
            bodyData = data.toByteArray("avro/binary");
            contentType = "application/avro";
            {%- elif dataschema and ("Protobuf" in dataschema or "protobuf" in dataschema or "proto" in dataschema) %}
            bodyData = data.toByteArray();
            contentType = "application/protobuf";
            {%- else %}
            bodyData = new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsBytes(data);
            contentType = "application/json";
            {%- endif %}

            ServiceBusMessage serviceBusMessage = new ServiceBusMessage(bodyData);
            serviceBusMessage.setContentType(contentType);
            serviceBusMessage.setMessageId(UUID.randomUUID().toString());
            
            {%- if message.metadata %}
            {%- for key, value in message.metadata.items() %}
            {%- if value.value %}
            serviceBusMessage.getApplicationProperties().put("{{ key }}", "{{ value.value }}");
            {%- endif %}
            {%- endfor %}
            {%- endif %}
            {%- endif %}

            return serviceBusMessage;
        } catch (Exception e) {
            logger.error("Error creating {{ messagename }} message", e);
            throw new RuntimeException(e);
        }
    }
    {%- endfor %}
}
{% endfor %}
