{%- import 'util.jinja.include' as util -%}
{%- import 'cloudevents.jinja.include' as cloudEvents -%}
{{ util.CommonFileHeader() }}

{%- set messagegroups = root.messagegroups %}
{%- set package_name = project_name | lower | replace('-', '_') %}

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

import com.azure.messaging.servicebus.ServiceBusReceivedMessageContext;
import com.fasterxml.jackson.databind.ObjectMapper;
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;
{%- endif %}
import java.net.URI;

{%- for messageid, message in messagegroup.messages.items() %}
{%- set message_body_type = util.get_data_type(data_project_name, root, message) %}
{%- if '.' in message_body_type %}
import {{ message_body_type.rsplit('.', 1)[0] }}.*;
{%- endif %}
{%- endfor %}

{%- set consumer_class = (groupname | strip_namespace) ~ 'MessageConsumer' %}

/**
 * Message consumer dispatcher for {{ messagegroupid }} message group.
 * Processes messages from Azure Service Bus and dispatches them to registered handlers.
 */
public abstract class {{ consumer_class }} {
    private static final Logger logger = LogManager.getLogger({{ consumer_class }}.class);
    {%- if cloudEvents.usesCloudEvents(root) %}
    private static final String CE_PREFIX = "cloudEvents_";
    {%- endif %}
    
    protected final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * Process a single message from Service Bus.
     * This method is called by the ServiceBusProcessorClient for each received message.
     * 
     * @param messageContext The message context containing the message data
     */
    public void processMessage(ServiceBusReceivedMessageContext messageContext) {
        try {
            byte[] bodyData = messageContext.getMessage().getBody().toBytes();
            String contentType = messageContext.getMessage().getContentType();
            String mediaType = contentType != null ? contentType.split(";")[0].trim().toLowerCase() : "application/json";
            
            {%- if cloudEvents.usesCloudEvents(root) %}
            // Check if this is a CloudEvents message (binary content mode)
            if (messageContext.getMessage().getApplicationProperties().containsKey(CE_PREFIX + "type")) {
                var properties = messageContext.getMessage().getApplicationProperties();
                String ceType = (String) properties.get(CE_PREFIX + "type");
                String dataContentType = (String) properties.getOrDefault(CE_PREFIX + "datacontenttype", "application/json");
                dispatchByType(ceType, bodyData, dataContentType, messageContext);
                return;
            }
            {%- endif %}
            
            // Process as plain Service Bus message - dispatch by subject
            String subject = messageContext.getMessage().getSubject();
            if (subject != null && !subject.isEmpty()) {
                dispatchByType(subject, bodyData, mediaType, messageContext);
            } else {
                logger.warn("Message has no subject, cannot dispatch");
                handleError(new IllegalArgumentException("Message has no subject"), messageContext);
            }
            
        } catch (Exception ex) {
            logger.error("Error processing message", ex);
            handleError(ex, messageContext);
        }
    }

    /**
     * Dispatch the message to the appropriate handler based on message type/subject.
     */
    private void dispatchByType(String messageType, byte[] bodyData, String contentType, ServiceBusReceivedMessageContext messageContext) throws Exception {
        switch (messageType) {
        {%- 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 ce_type_value = message.envelopemetadata["type"]["value"] if "type" in message.envelopemetadata and "value" in message.envelopemetadata["type"] else None %}
        {%- if ce_type_value and ce_type_value != messageid %}
            case "{{ ce_type_value }}":
        {%- endif %}
            case "{{ messageid }}":
                {
                    {%- if message_body_type == "byte[]" %}
                    byte[] data = bodyData;
                    {%- else %}
                    {{ message_body_type }} data = {{ message_body_type }}.fromData(bodyData, contentType);
                    {%- endif %}
                    on{{ messagename }}(data, messageContext);
                    messageContext.complete();
                }
                break;
        {%- endfor %}
            default:
                logger.warn("Unknown message type: {}", messageType);
                handleError(new IllegalArgumentException("Unknown message type: " + messageType), messageContext);
                break;
        }
    }

    {%- 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) %}

    /**
     * Handler for {{ messagename }} messages.
     * Override this method to process {{ messagename }} messages.
     * 
     * @param data The deserialized message data
     * @param context The Service Bus message context
     */
    protected abstract void on{{ messagename }}({{ message_body_type }} data, ServiceBusReceivedMessageContext context);
    {%- endfor %}

    /**
     * Error handler for processing failures.
     * Override this method to implement custom error handling.
     * By default, this abandons the message so it can be reprocessed.
     * 
     * @param ex The exception that occurred
     * @param context The Service Bus message context
     */
    protected void handleError(Exception ex, ServiceBusReceivedMessageContext context) {
        logger.error("Error processing message: " + context.getMessage().getMessageId(), ex);
        try {
            context.abandon(); // Return message to queue/subscription for retry
        } catch (Exception abandonEx) {
            logger.error("Failed to abandon message", abandonEx);
        }
    }
}
{%- endfor %}
