package eventbus

import (
	"context"
	"fmt"

	"github.com/pingcap/log"
	"github.com/tidbcloud/serverless-service/internal/config"
	"github.com/tidbcloud/tiinf/transport/tieventbus"
	"go.uber.org/multierr"
	"go.uber.org/zap"
)

type EventbusService struct {
	tieventbus.Eventbus
}

func NewEventBusService() *EventbusService {
	appName := "serverless-service"
	if config.IsLocalStage() {
		appName += "-" + config.MyNamespace
	}
	eventbus, err := tieventbus.NewEventbus(tieventbus.EventbusConfig{
		AppName: appName,
	})
	if err != nil {
		panic(fmt.Sprintf("failed to create eventbus: %s", err))
	}
	return &EventbusService{eventbus}
}

type EventHandler[ET any] interface {
	Handle(ctx context.Context, event *ET) error
}

type Subscriber[ET any] struct {
	Eventbus tieventbus.Eventbus
}

func (s *Subscriber[ET]) RegisterHandlers(group, eventType string, handlers ...EventHandler[ET]) {
	s.Eventbus.RegisterHandler(tieventbus.HandlerConfig{
		Group:      group,
		EventType:  eventType,
		DataStruct: new(ET),
		RetryTimes: 5,
		HandleFunc: func(event *tieventbus.Event) error {
			log.Info("event received", zap.String("type", eventType), zap.Any("event", event))
			evt, ok := event.Data.(*ET)
			if !ok {
				log.Warn("event type mismatched", zap.String("type", eventType), zap.Any("event", event))
				return nil
			}
			ctx := context.WithValue(context.Background(), config.CtxEventIDKey, event.ID)

			var errs error
			for _, handler := range handlers {
				errs = multierr.Append(errs, handler.Handle(ctx, evt))
			}

			if errs != nil {
				log.Error("failed to consume event", zap.Error(errs))
			}
			return errs
		},
	})
}
