package messagingimp

import (
	"context"
	"errors"

	"github.com/tech/sendico/pkg/messaging"
	mb "github.com/tech/sendico/pkg/messaging/broker"
	notifications "github.com/tech/sendico/pkg/messaging/notifications/processor"
	mip "github.com/tech/sendico/pkg/messaging/producer"
	"github.com/tech/sendico/pkg/mlogger"
	"go.uber.org/zap"
)

type MessagingRouter struct {
	logger    mlogger.Logger
	messaging mb.Broker
	consumers []messaging.Consumer
	producer  messaging.Producer
}

func (mr *MessagingRouter) consumeMessages(c messaging.Consumer, processor notifications.EnvelopeProcessor) {
	if err := c.ConsumeMessages(processor.Process); err != nil {
		if !errors.Is(err, context.Canceled) {
			mr.logger.Warn("Error consuming messages", zap.Error(err), zap.String("event", processor.GetSubject().ToString()))
		} else {
			mr.logger.Info("Finishing as context has been cancelled", zap.String("event", processor.GetSubject().ToString()))
		}
	}
}

func (mr *MessagingRouter) Consumer(processor notifications.EnvelopeProcessor) error {
	c, err := NewConsumer(mr.logger, mr.messaging, processor.GetSubject())
	if err != nil {
		mr.logger.Warn("Failed to register message consumer", zap.Error(err), zap.String("event", processor.GetSubject().ToString()))
		return err
	}
	mr.consumers = append(mr.consumers, c)
	go mr.consumeMessages(c, processor)
	return nil
}

func (mr *MessagingRouter) Finish() {
	mr.logger.Info("Closing consumer channels")
	for _, consumer := range mr.consumers {
		consumer.Close()
	}
}

func (mr *MessagingRouter) Producer() messaging.Producer {
	return mr.producer
}

func NewMessagingRouterImp(logger mlogger.Logger, config *messaging.Config) (*MessagingRouter, error) {
	l := logger.Named("messaging")
	broker, err := messaging.CreateMessagingBroker(l, config)
	if err != nil {
		l.Error("Failed to create messaging broker", zap.Error(err), zap.String("broker", string(config.Driver)))
		return nil, err
	}
	return &MessagingRouter{
		logger:    l,
		messaging: broker,
		producer:  mip.NewProducer(logger, broker),
		consumers: make([]messaging.Consumer, 0),
	}, nil
}
