package patterns

import (
	"fmt"
	"sync"
)

// EventHandler represents an event handler.
type EventHandler interface {
	Invoke(event interface{}, data interface{}) error
}

// EventHandlerFunc is an EventHandler function.
type EventHandlerFunc func(event interface{}, data interface{}) error

func (fn EventHandlerFunc) Invoke(event interface{}, data interface{}) error {
	return fn(event, data)
}

// EventDelegator represents an event delegator that invoke multiple EventHandler at a time.
type EventDelegator interface {
	EventHandler

	// AddHandler adds an EventHandler to the delegator.
	AddHandler(handler EventHandler) error
}

type eventDelegatorImpl struct {
	mu       sync.Mutex
	handlers map[string]EventHandler
}

func NewEventDelegator() EventDelegator {
	return &eventDelegatorImpl{
		mu:       sync.Mutex{},
		handlers: map[string]EventHandler{},
	}
}

func (d *eventDelegatorImpl) Invoke(event interface{}, data interface{}) error {
	for _, h := range d.handlers {
		err := h.Invoke(event, data)
		if err != nil {
			return err
		}
	}

	return nil
}

func (d *eventDelegatorImpl) AddHandler(handler EventHandler) error {
	ptr := fmt.Sprintf("%v", handler)
	d.handlers[ptr] = handler
	return nil
}

// EventManager abstracts an asynchronous event manager that allows registering a set of events that can be fired and
// handled by their respective handlers.
type EventManager interface {
	// RegisterEventHandler registers an event handler for associated event.
	RegisterEventHandler(event interface{}, handler EventHandler) error

	// Raise raises an event with given data by invoking its respective event handlers.
	Raise(event interface{}, data interface{}) error
}

// eventManagerImpl is an implementation of EventManager.
type eventManagerImpl struct {
	mu         sync.Mutex
	delegators map[interface{}]EventDelegator
}

// NewEventManager returns a new instance of EventManager.
func NewEventManager() EventManager {
	return &eventManagerImpl{
		delegators: map[interface{}]EventDelegator{},
	}
}

func (m *eventManagerImpl) RegisterEventHandler(event interface{}, handler EventHandler) error {
	m.mu.Lock()
	defer m.mu.Unlock()

	d, ok := m.delegators[event]
	if !ok {
		d = NewEventDelegator()
		m.delegators[event] = d
	}

	if err := d.AddHandler(handler); err != nil {
		return err
	}

	return nil
}

func (m *eventManagerImpl) Raise(event interface{}, data interface{}) error {
	hs, ok := m.delegators[event]
	if ok {
		return hs.Invoke(event, data)
	}
	return nil
}
