{%- import "cloudevents.jinja.include" as cloudEvents -%}
{%- import "kafka.jinja.include" as kafka -%}
{%- import "util.jinja.include" as util -%}
// This code was auto-generated by xRegistry CLI
// Changes to this file may cause incorrect behavior and will be lost if the code is regenerated.

package {{ project_name | snake }}

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	cloudevents "github.com/cloudevents/sdk-go/v2"
	"github.com/segmentio/kafka-go"
	"github.com/google/uuid"
	
	{{ data_project_name | snake }} "github.com/username/{{ data_project_name | snake }}/pkg/{{ data_project_name }}"
)

// ContentMode specifies how CloudEvents are serialized to Kafka
type ContentMode string

const (
	// ContentModeStructured serializes CloudEvents as JSON in the message body
	ContentModeStructured ContentMode = "structured"
	// ContentModeBinary serializes CloudEvents properties as Kafka headers with ce_ prefix
	ContentModeBinary ContentMode = "binary"
)

{%- set messagegroups = root.messagegroups %}
{%- for messagegroupid, messagegroup in messagegroups.items() %}
{%- set groupname = messagegroupid | pascal | strip_namespace %}
{%- set class_name = groupname + "Producer" %}

// {{ class_name }} is a Kafka producer for the {{ messagegroupid }} message group
type {{ class_name }} struct {
	writer       *kafka.Writer
	defaultTopic string
	contentMode  ContentMode
}

// New{{ class_name }} creates a new {{ class_name }} instance with structured content mode (default)
func New{{ class_name }}(bootstrapServers string) (*{{ class_name }}, error) {
	return New{{ class_name }}WithContentMode(bootstrapServers, ContentModeStructured)
}

// New{{ class_name }}WithContentMode creates a new {{ class_name }} instance with specified content mode
func New{{ class_name }}WithContentMode(bootstrapServers string, contentMode ContentMode) (*{{ class_name }}, error) {
	// Parse bootstrap servers - segmentio/kafka-go expects a slice
	servers := []string{bootstrapServers}
	
	writer := &kafka.Writer{
		Addr:     kafka.TCP(servers...),
		Balancer: &kafka.LeastBytes{},
	}
	
	{%- set first_message = messagegroup.messages.values() | first %}
	{%- set default_topic = kafka.get_topic(first_message) %}
	
	return &{{ class_name }}{
		writer:      writer,
		{%- if default_topic %}
		defaultTopic: "{{ default_topic }}",
		{%- endif %}
		contentMode: contentMode,
	}, nil
}

// New{{ class_name }}WithWriter creates a new {{ class_name }} with an existing Kafka writer
func New{{ class_name }}WithWriter(writer *kafka.Writer, defaultTopic string) *{{ class_name }} {
	return &{{ class_name }}{
		writer:       writer,
		defaultTopic: defaultTopic,
		contentMode:  ContentModeStructured, // Default to structured mode
	}
}

// SetContentMode sets the content mode for CloudEvents serialization
func (p *{{ class_name }}) SetContentMode(mode ContentMode) {
	p.contentMode = mode
}

// Close closes the producer
func (p *{{ class_name }}) Close() error {
	if p.writer != nil {
		return p.writer.Close()
	}
	return nil
}

// GetWriter returns the underlying Kafka writer
func (p *{{ class_name }}) GetWriter() *kafka.Writer {
	return p.writer
}

{% for messageid, message in messagegroup.messages.items() -%}
{%- set messagename = messageid | pascal | strip_namespace %}
{%- set message_body_type = util.body_type(data_project_name, root, message) %}
{%- set topic = kafka.get_topic(message) or messageid %}
{%- set isCloudEvent = cloudEvents.isCloudEvent(message) %}

{%- if isCloudEvent %}
// Send{{ messagename }} sends a {{ messagename }} CloudEvent to Kafka
{%- if message.description %}
// {{ message.description }}
{%- endif %}
{%- set needs_source_param = "source" not in message.envelopemetadata %}
{%- if "source" in message.envelopemetadata and message.envelopemetadata.source.value %}
{%- set source_value = message.envelopemetadata.source.value %}
{%- if "{" in source_value and "}" in source_value %}
{%- set needs_source_param = true %}
{%- endif %}
{%- endif %}
func (p *{{ class_name }}) Send{{ messagename }}(ctx context.Context, data {{ message_body_type }}{% if needs_source_param %}, source string{% endif %}{% if "type" not in message.envelopemetadata %}, eventType string{% endif %}) error {
	{%- if topic %}
	topic := "{{ topic }}"
	{%- else %}
	topic := p.defaultTopic
	if topic == "" {
		return fmt.Errorf("default topic not set for {{ class_name }}")
	}
	{%- endif %}
	
	event := cloudevents.NewEvent()
	event.SetID(uuid.New().String())
	event.SetTime(time.Now())
	{%- if "source" in message.envelopemetadata and message.envelopemetadata.source.value %}
	{%- set source_value = message.envelopemetadata.source.value %}
	{%- if "{" in source_value and "}" in source_value %}
	// Source parameter must be provided by caller
	if source == "" {
		return fmt.Errorf("source parameter is required (e.g., 'tenant123/device456')")
	}
	event.SetSource(source)
	{%- else %}
	event.SetSource("{{ source_value }}")
	{%- endif %}
	{%- else %}
	event.SetSource(source)
	{%- endif %}
	{%- if "type" in message.envelopemetadata and message.envelopemetadata.type.value %}
	event.SetType("{{ message.envelopemetadata.type.value }}")
	{%- else %}
	event.SetType(eventType)
	{%- endif %}
	{%- for attrname, attribute in message.envelopemetadata.items() if attrname not in ["source", "type", "id", "time", "specversion", "datacontenttype"] %}
	{%- if attribute.value %}
	event.SetExtension("{{ attrname }}", "{{ attribute.value }}")
	{%- elif attribute.required and not attribute.value %}
	event.SetExtension("{{ attrname }}", {{ attrname | snake }}Val)
	{%- endif %}
	{%- endfor %}
	
	{%- if "datacontenttype" in message.envelopemetadata and message.envelopemetadata.datacontenttype.value %}
	event.SetDataContentType("{{ message.envelopemetadata.datacontenttype.value }}")
	{%- else %}
	{%- set content_type = util.get_content_type(message) %}
	event.SetDataContentType({{ content_type }})
	{%- endif %}
	
	if err := event.SetData(cloudevents.ApplicationJSON, data); err != nil {
		return fmt.Errorf("failed to set event data: %w", err)
	}
	
	// Prepare Kafka message based on content mode
	var msg kafka.Message
	
	if p.contentMode == ContentModeBinary {
		// Binary mode - CloudEvent properties in headers, data in body
		var dataBytes []byte
		{%- if message_body_type == "[]byte" %}
		dataBytes = data
		{%- else %}
		// Use avrotize-generated ToByteArray for proper serialization
		{%- set content_type = util.get_content_type(message) %}
		contentType := event.DataContentType()
		if contentType == "" {
			contentType = {{ content_type }}
		}
		var err error
		dataBytes, err = data.ToByteArray(contentType)
		if err != nil {
			return fmt.Errorf("failed to serialize data: %w", err)
		}
		{%- endif %}
		
		headers := []kafka.Header{
			{Key: "ce_specversion", Value: []byte(event.SpecVersion())},
			{Key: "ce_type", Value: []byte(event.Type())},
			{Key: "ce_source", Value: []byte(event.Source())},
			{Key: "ce_id", Value: []byte(event.ID())},
			{Key: "ce_time", Value: []byte(event.Time().Format(time.RFC3339))},
		}
		
		if event.DataContentType() != "" {
			headers = append(headers, kafka.Header{Key: "ce_datacontenttype", Value: []byte(event.DataContentType())})
		}
		
		// Add extension attributes
		for name, val := range event.Extensions() {
			if strVal, ok := val.(string); ok {
				headers = append(headers, kafka.Header{Key: "ce_" + name, Value: []byte(strVal)})
			}
		}
		
		msg = kafka.Message{
			Topic:   topic,
			Value:   dataBytes,
			Headers: headers,
		}
	} else {
		// Structured mode - CloudEvent as JSON in body
		eventBytes, err := json.Marshal(event)
		if err != nil {
			return fmt.Errorf("failed to marshal CloudEvent: %w", err)
		}
		
		msg = kafka.Message{
			Topic: topic,
			Value: eventBytes,
			Headers: []kafka.Header{
				{Key: "content-type", Value: []byte("application/cloudevents+json")},
			},
		}
	}
	
	if err := p.writer.WriteMessages(ctx, msg); err != nil {
		return fmt.Errorf("failed to write message: %w", err)
	}
	
	return nil
}
{%- else %}
// Send{{ messagename }} sends a {{ messagename }} message to Kafka
{%- if message.description %}
// {{ message.description }}
{%- endif %}
func (p *{{ class_name }}) Send{{ messagename }}(ctx context.Context, data {{ message_body_type }}, topic string) error {
	if topic == "" {
		{%- if topic %}
		topic = "{{ topic }}"
		{%- else %}
		if p.defaultTopic == "" {
			return fmt.Errorf("topic must be specified")
		}
		topic = p.defaultTopic
		{%- endif %}
	}
	
	var value []byte
	{%- if message_body_type == "[]byte" %}
	value = data
	{%- else %}
	var err error
	value, err = json.Marshal(data)
	if err != nil {
		return fmt.Errorf("failed to marshal data: %w", err)
	}
	{%- endif %}
	
	msg := kafka.Message{
		Topic: topic,
		Value: value,
	}
	
	if err := p.writer.WriteMessages(ctx, msg); err != nil {
		return fmt.Errorf("failed to write message: %w", err)
	}
	
	return nil
}
{%- endif %}

{% endfor %}
{% endfor %}
