package kafka

import (
	"context"
	"nms_nbi/src/framework/config"
	"nms_nbi/src/framework/logger"
	"time"

	"github.com/IBM/sarama"
)

// KInitConm Kafka初始配置连接实例
var KInitConm Kafka

// InitConfig 连接Kafka实例
func InitConfig() {
	// 服务地址
	addrs := []string{}
	addrsArr := config.Get("cxy.kafka.addrs").([]any)
	for _, v := range addrsArr {
		addrs = append(addrs, v.(string))
	}
	k := Kafka{
		Addrs: addrs,
	}
	k.NewConfig()
	k.Config.Net.SASL.Enable = false
	KInitConm = k
}

// Kafka 实例连接
type Kafka struct {
	Addrs  []string
	Config *sarama.Config
}

func (k *Kafka) NewConfig() {
	// 设置Kafka配置
	config := sarama.NewConfig()
	// 生产者
	config.Producer.RequiredAcks = sarama.WaitForAll
	config.Producer.Retry.Max = 5
	config.Producer.Return.Successes = true

	k.Config = config
}

// MessageSyncSend 消息同步发送
func (k *Kafka) MessageSyncSend(topic string, partition int32, msg string) (int32, int64, error) {
	// 创建Kafka生产者
	producer, err := sarama.NewSyncProducer(k.Addrs, k.Config)
	if err != nil {
		logger.Warnf("MessageSyncSend NewSyncProducer Topic:%s, Partition:%d => %s", topic, partition, err.Error())
		return partition, 0, err
	}
	defer producer.Close()

	// 发送消息
	message := &sarama.ProducerMessage{
		Topic:     topic,
		Partition: partition,
		Value:     sarama.StringEncoder(msg),
	}
	partition, offset, err := producer.SendMessage(message)
	if err != nil {
		logger.Errorf("MessageSyncSend SendMessage Topic:%s, Partition:%d => %s", topic, partition, err.Error())
	}
	return partition, offset, err
}

// MessageSyncRead 消息同步读取-最旧历史积压
func (k *Kafka) MessageSyncRead(topic string, partition int32) (string, error) {
	// 创建Kafka消费者
	consumer, err := sarama.NewConsumer(k.Addrs, k.Config)
	if err != nil {
		logger.Warnf("MessageSyncRead NewConsumer Topic:%s, Partition:%d => %s", topic, partition, err.Error())
		return "", err
	}
	defer consumer.Close()

	// 消费消息
	partitionConsumer, err := consumer.ConsumePartition(topic, partition, sarama.OffsetOldest)
	if err != nil {
		logger.Errorf("MessageSyncRead ConsumePartition Topic:%s, Partition:%d => %s", topic, partition, err.Error())
		return "", err
	}
	defer partitionConsumer.Close()

	// 设置超时时间为1秒
	timeout := time.After(1 * time.Second)
	for {
		select {
		case msg := <-partitionConsumer.Messages():
			return string(msg.Value), nil
		case <-timeout:
			logger.Infof("MessageSyncRead timeout Topic:%s, Partition:%d", topic, partition)
			return "", nil
		}
	}
}

// MessageSyncReadMark 消息同步读取并消费-最旧历史积压
func (k *Kafka) MessageSyncReadMark(topic string, partition int32, group string) (string, error) {
	consumerGroup, err := sarama.NewConsumerGroup(k.Addrs, group, k.Config)
	if err != nil {
		logger.Warnf("MessageSyncReadMark NewConsumerGroup Topic:%s, Partition:%d, Group:%s => %s", topic, partition, group, err.Error())
		return "", err
	}
	defer consumerGroup.Close()

	consumer := readAndMarkConsumerGroup{
		MessageChan: make(chan []byte, 1),
	}

	// `Consume` should be called inside an infinite loop, when a
	// server-side rebalance happens, the consumer session will need to be
	// recreated to get the new claims
	err = consumerGroup.Consume(context.Background(), []string{topic}, consumer)
	if err != nil {
		logger.Errorf("MessageSyncReadMark Consume Topic:%s, Partition:%d, Group:%s => %s", topic, partition, group, err.Error())
		return "", err
	}

	messageByte := <-consumer.MessageChan
	return string(messageByte), err
}

// readAndMarkConsumerGroup 消费组积压消息读取立即标记
type readAndMarkConsumerGroup struct {
	MessageChan chan []byte // 消息通道
}

func (readAndMarkConsumerGroup) Setup(_ sarama.ConsumerGroupSession) error   { return nil }
func (readAndMarkConsumerGroup) Cleanup(_ sarama.ConsumerGroupSession) error { return nil }
func (g readAndMarkConsumerGroup) ConsumeClaim(session sarama.ConsumerGroupSession, claim sarama.ConsumerGroupClaim) error {
	// 设置超时时间为1秒
	timeout := time.After(1 * time.Second)
	for {
		select {
		case message, ok := <-claim.Messages():
			if !ok {
				logger.Warnf("ConsumeClaim Messages fail Topic:%s, Partition:%d, point:%d", claim.Topic(), claim.Partition(), claim.InitialOffset())
				g.MessageChan <- []byte{}
				return nil
			}

			session.MarkMessage(message, "")
			g.MessageChan <- message.Value
			return nil
		// Should return when `session.Context()` is done.
		// If not, will raise `ErrRebalanceInProgress` or `read tcp <ip>:<port>: i/o timeout` when kafka rebalance. see:
		// https://github.com/IBM/sarama/issues/1192
		case <-session.Context().Done():
			logger.Infof("ConsumeClaim done Topic:%s, Partition:%d, point:%d", claim.Topic(), claim.Partition(), claim.InitialOffset())
			g.MessageChan <- []byte{}
			return nil
		case <-timeout:
			logger.Infof("ConsumeClaim timeout Topic:%s, Partition:%d, point:%d", claim.Topic(), claim.Partition(), claim.InitialOffset())
			g.MessageChan <- []byte{}
			return nil
		}
	}
}
