package patterns

import (
	"errors"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"
)

type eventHandlerWrapper struct {
	fn EventHandlerFunc
}

func (w *eventHandlerWrapper) Invoke(event interface{}, data interface{}) error {
	return w.fn.Invoke(event, data)
}

var _ = Describe("EventManager", func() {
	var (
		manager EventManager
	)

	type eventA struct{}
	type eventB struct {
		subscriberID int
	}

	BeforeEach(func() {
		manager = NewEventManager()
	})

	Describe("RegisterEventManager", func() {
		It("should register event handler", func() {
			err := manager.RegisterEventHandler(eventA{}, &eventHandlerWrapper{fn: func(event interface{}, data interface{}) error {
				return nil
			}})

			Expect(err).Should(BeNil())
		})

		It("should register event handler function", func() {
			err := manager.RegisterEventHandler(eventA{}, EventHandlerFunc(func(event interface{}, data interface{}) error {
				return nil
			}))

			Expect(err).Should(BeNil())
		})
	})

	Describe("Raise", func() {
		DescribeTable("should call respective handler on registered event", func(raisedEvent interface{}, raisedData interface{}, raisedErr error) {
			var (
				receivedEvent interface{}
				receivedData  interface{}
			)

			_ = manager.RegisterEventHandler(raisedEvent, EventHandlerFunc(func(event interface{}, data interface{}) error {
				receivedEvent = event
				receivedData = data
				return raisedErr
			}))

			receivedErr := manager.Raise(raisedEvent, raisedData)

			if raisedErr == nil {
				Expect(receivedErr).Should(BeNil())
			} else {
				Expect(receivedErr).Should(Equal(raisedErr))
			}

			Expect(receivedEvent).Should(Equal(raisedEvent))

			if raisedData == nil {
				Expect(raisedData).Should(BeNil())
			} else {
				Expect(receivedData).Should(Equal(raisedData))
			}
		},
			Entry("case", eventA{}, "event-data", nil),
			Entry("case", eventA{}, "event-data", errors.New("some error")),
			Entry("case", eventA{}, nil, nil),
			Entry("case", eventA{}, nil, errors.New("some error")),
			Entry("case", eventB{subscriberID: 1}, "event-data", nil),
			Entry("case", eventB{subscriberID: 1}, "event-data", errors.New("some error")),
			Entry("case", eventB{subscriberID: 1}, nil, nil),
			Entry("case", eventB{subscriberID: 1}, nil, errors.New("some error")),
		)

		DescribeTable("should not raise un-registered event", func(registeredEvent interface{}, raisedEvent interface{}) {
			called := false

			_ = manager.RegisterEventHandler(registeredEvent, EventHandlerFunc(func(event interface{}, data interface{}) error {
				called = true
				return nil
			}))

			err := manager.Raise(raisedEvent, "some-data")
			Expect(err).Should(BeNil())
			Expect(called).Should(BeFalse())
		},
			Entry("case", eventA{}, eventB{}),
			Entry("case", eventA{}, eventB{subscriberID: 1}),
			Entry("case", eventB{subscriberID: 1}, eventB{subscriberID: 2}),
		)
	})
})
