package kafka

import (
	"encoding/json"
	"fmt"
	"strings"

	"github.com/IBM/sarama"
	"github.com/genex/chain-indexer/internal/domain/event"
)

// Compile-time check: KafkaEventPublisher implements event.EventPublisher.
var _ event.EventPublisher = (*KafkaEventPublisher)(nil)

// KafkaEventPublisher implements event.EventPublisher by publishing domain events
// to Kafka using the IBM/sarama client.
type KafkaEventPublisher struct {
	producer sarama.SyncProducer
}

// NewKafkaEventPublisher creates a new Kafka event publisher connected to the given brokers.
func NewKafkaEventPublisher(brokers []string) (*KafkaEventPublisher, error) {
	config := sarama.NewConfig()
	config.Producer.RequiredAcks = sarama.WaitForAll
	config.Producer.Retry.Max = 3
	config.Producer.Return.Successes = true

	producer, err := sarama.NewSyncProducer(brokers, config)
	if err != nil {
		return nil, fmt.Errorf("failed to create Kafka producer: %w", err)
	}

	return &KafkaEventPublisher{producer: producer}, nil
}

// Publish serializes a domain event to JSON and publishes it to the appropriate Kafka topic.
func (p *KafkaEventPublisher) Publish(evt event.DomainEvent) error {
	payload, err := json.Marshal(evt)
	if err != nil {
		return fmt.Errorf("failed to marshal event %s: %w", evt.EventName(), err)
	}

	topic := resolveTopic(evt.EventName())

	msg := &sarama.ProducerMessage{
		Topic: topic,
		Key:   sarama.StringEncoder(evt.EventName()),
		Value: sarama.ByteEncoder(payload),
	}

	_, _, err = p.producer.SendMessage(msg)
	if err != nil {
		return fmt.Errorf("failed to publish event %s to topic %s: %w", evt.EventName(), topic, err)
	}

	return nil
}

// Close shuts down the Kafka producer gracefully.
func (p *KafkaEventPublisher) Close() error {
	if p.producer != nil {
		return p.producer.Close()
	}
	return nil
}

// resolveTopic maps event names to Kafka topics.
func resolveTopic(eventName string) string {
	if strings.HasPrefix(eventName, "chain.block.") {
		return "chain.blocks"
	}
	return "chain.transactions"
}
