{%- 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"
	"sync"
	"time"

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

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

// {{ class_name }}Handler defines the interface for handling consumed messages
type {{ class_name }}Handler interface {
{%- 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 isCloudEvent = cloudEvents.isCloudEvent(message) %}
	// Handle{{ messagename }} handles a {{ messagename }} message
	{%- if isCloudEvent %}
	Handle{{ messagename }}(ctx context.Context, event cloudevents.Event, data {{ message_body_type }}) error
	{%- else %}
	Handle{{ messagename }}(ctx context.Context, data {{ message_body_type }}) error
	{%- endif %}
{%- endfor %}
	// HandleError handles any errors that occur during message processing
	HandleError(ctx context.Context, err error)
}

// {{ class_name }} is a Kafka consumer for the {{ messagegroupid }} message group
type {{ class_name }} struct {
	reader   *kafka.Reader
	handler  {{ class_name }}Handler
	running  bool
	mu       sync.Mutex
	stopChan chan struct{}
}

// New{{ class_name }} creates a new {{ class_name }} instance
func New{{ class_name }}(bootstrapServers string, groupID string, topics []string, handler {{ class_name }}Handler) (*{{ class_name }}, error) {
	servers := []string{bootstrapServers}
	
	reader := kafka.NewReader(kafka.ReaderConfig{
		Brokers:         servers,
		GroupID:         groupID,
		GroupTopics:     topics,
		StartOffset:     kafka.LastOffset,
		CommitInterval:  time.Second,
		SessionTimeout:  10 * time.Second,
		RebalanceTimeout: 30 * time.Second,
	})
	
	return &{{ class_name }}{
		reader:   reader,
		handler:  handler,
		stopChan: make(chan struct{}),
	}, nil
}

// New{{ class_name }}WithReader creates a new {{ class_name }} with an existing Kafka reader
func New{{ class_name }}WithReader(reader *kafka.Reader, handler {{ class_name }}Handler) *{{ class_name }} {
	return &{{ class_name }}{
		reader:   reader,
		handler:  handler,
		stopChan: make(chan struct{}),
	}
}

// Start begins consuming messages from Kafka
func (c *{{ class_name }}) Start(ctx context.Context) error {
	c.mu.Lock()
	if c.running {
		c.mu.Unlock()
		return fmt.Errorf("consumer is already running")
	}
	c.running = true
	c.mu.Unlock()
	
	defer func() {
		c.mu.Lock()
		c.running = false
		c.mu.Unlock()
	}()
	
	for {
		select {
		case <-ctx.Done():
			return ctx.Err()
		case <-c.stopChan:
			return nil
		default:
			msg, err := c.reader.FetchMessage(ctx)
			if err != nil {
				if err == context.Canceled {
					return nil
				}
				c.handler.HandleError(ctx, fmt.Errorf("failed to read message: %w", err))
				continue
			}
			
			if err := c.processMessage(ctx, &msg); err != nil {
				c.handler.HandleError(ctx, err)
			}
			
			if err := c.reader.CommitMessages(ctx, msg); err != nil {
				c.handler.HandleError(ctx, fmt.Errorf("failed to commit message: %w", err))
			}
		}
	}
}

// Stop stops the consumer
func (c *{{ class_name }}) Stop() {
	c.mu.Lock()
	defer c.mu.Unlock()
	
	if c.running {
		close(c.stopChan)
	}
}

// Close closes the consumer
func (c *{{ class_name }}) Close() error {
	c.Stop()
	if c.reader != nil {
		return c.reader.Close()
	}
	return nil
}
// processMessage processes a single Kafka message
func (c *{{ class_name }}) processMessage(ctx context.Context, msg *kafka.Message) error {
	// Check if this is a CloudEvent by looking for CloudEvents headers or JSON structure
	isCloudEvent := false
	contentType := ""
	
	// Check headers first
	for _, header := range msg.Headers {
		if header.Key == "content-type" && string(header.Value) == "application/cloudevents+json" {
			isCloudEvent = true
			contentType = "structured"
			break
		}
		if header.Key == "ce_specversion" {
			// CloudEvent in binary mode
			isCloudEvent = true
			contentType = "binary"
			break
		}
	}
	
	// If no CloudEvent headers, try to parse as CloudEvent JSON
	if !isCloudEvent && len(msg.Value) > 0 && msg.Value[0] == '{' {
		var checkCE map[string]interface{}
		if err := json.Unmarshal(msg.Value, &checkCE); err == nil {
			if _, ok := checkCE["specversion"]; ok {
				isCloudEvent = true
				contentType = "structured"
			}
		}
	}
	
	if isCloudEvent {
		return c.processCloudEvent(ctx, msg, contentType)
	}
	
	return c.processPlainMessage(ctx, msg)
}

// Helper to extract header value from Kafka message
func (c *{{ class_name }}) getHeader(msg *kafka.Message, key string) string {
	for _, header := range msg.Headers {
		if header.Key == key {
			return string(header.Value)
		}
	}
	return ""
}

// processCloudEvent processes a CloudEvent message
func (c *{{ class_name }}) processCloudEvent(ctx context.Context, msg *kafka.Message, contentType string) error {
	event := cloudevents.NewEvent()
	
	if contentType == "structured" {
		// Structured mode - CloudEvent is in the message body
		if err := json.Unmarshal(msg.Value, &event); err != nil {
			return fmt.Errorf("failed to unmarshal CloudEvent: %w", err)
		}
	} else {
		// Binary mode - CloudEvent properties are in headers
		event.SetSpecVersion(c.getHeader(msg, "ce_specversion"))
		event.SetType(c.getHeader(msg, "ce_type"))
		event.SetSource(c.getHeader(msg, "ce_source"))
		event.SetID(c.getHeader(msg, "ce_id"))
		if ts := c.getHeader(msg, "ce_time"); ts != "" {
			if t, err := time.Parse(time.RFC3339, ts); err == nil {
				event.SetTime(t)
			}
		}
		if dataContentType := c.getHeader(msg, "ce_datacontenttype"); dataContentType != "" {
			event.SetDataContentType(dataContentType)
		}
		if err := event.SetData(cloudevents.ApplicationJSON, msg.Value); err != nil {
			return fmt.Errorf("failed to set CloudEvent data: %w", err)
		}
	}
	
	eventType := event.Type()
	{%- 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 isCloudEvent = cloudEvents.isCloudEvent(message) %}
	{%- if isCloudEvent %}
	{%- if "type" in message.envelopemetadata and message.envelopemetadata.type.value %}
	if eventType == "{{ message.envelopemetadata.type.value }}" {
		{%- if message_body_type == "[]byte" %}
		// Binary data - use directly
		data := event.Data()
		return c.handler.Handle{{ messagename }}(ctx, event, data)
		{%- else %}
		// Use avrotize-generated FromData function for proper deserialization
		dataContentType := event.DataContentType()
		if dataContentType == "" {
			{%- set default_content_type = util.get_content_type(message) %}
			dataContentType = {{ default_content_type }}
		}
		
		// Get the raw data from the event
		rawData := event.Data()
		
		// Use the avrotize-generated FromData function
		{%- set type_name = message_body_type | replace("*", "") | replace(data_project_name | snake + ".", "") %}
		data, err := {{ data_project_name | snake }}.{{ type_name }}FromData(rawData, dataContentType)
		if err != nil {
			return fmt.Errorf("failed to deserialize {{ messagename }} data: %w", err)
		}
		// FromData returns a pointer, dereference it to match handler signature
		return c.handler.Handle{{ messagename }}(ctx, event, *data)
		{%- endif %}
	}
	{%- endif %}
	{%- endif %}
	{%- endfor %}
	
	return fmt.Errorf("unknown CloudEvent type: %s", eventType)
}

// processPlainMessage processes a plain Kafka message
func (c *{{ class_name }}) processPlainMessage(ctx context.Context, msg *kafka.Message) error {
	// Try to determine message type from topic or other metadata
	topic := msg.Topic
	{%- 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 isCloudEvent = cloudEvents.isCloudEvent(message) %}
	{%- set topic_value = kafka.get_topic(message) or messageid %}
	{%- if not isCloudEvent %}
	
	if topic == "{{ topic_value }}" {
		{%- if message_body_type == "[]byte" %}
		return c.handler.Handle{{ messagename }}(ctx, msg.Value)
		{%- else %}
		var data {{ message_body_type }}
		if err := json.Unmarshal(msg.Value, &data); err != nil {
			return fmt.Errorf("failed to unmarshal {{ messagename }} data: %w", err)
		}
		return c.handler.Handle{{ messagename }}(ctx, data)
		{%- endif %}
	}
	{%- endif %}
	{%- endfor %}
	
	return fmt.Errorf("unknown topic: %s", topic)
}

{%- endfor %}
