package playwright

import (
	"math"
	"reflect"
	"slices"
	"sync"
)

type EventEmitter interface {
	Emit(name string, payload ...interface{}) bool
	ListenerCount(name string) int
	On(name string, handler interface{})
	Once(name string, handler interface{})
	RemoveListener(name string, handler interface{})
	RemoveListeners(name string)
}

type (
	eventEmitter struct {
		eventsMutex sync.Mutex
		events      map[string]*eventRegister
		hasInit     bool
	}
	eventRegister struct {
		sync.Mutex
		listeners []listener
	}
	listener struct {
		handler interface{}
		once    bool
	}
)

func NewEventEmitter() EventEmitter {
	return &eventEmitter{}
}

func (e *eventEmitter) Emit(name string, payload ...interface{}) (hasListener bool) {
	e.eventsMutex.Lock()
	e.init()

	evt, ok := e.events[name]
	if !ok {
		e.eventsMutex.Unlock()
		return
	}
	e.eventsMutex.Unlock()
	return evt.callHandlers(payload...) > 0
}

func (e *eventEmitter) Once(name string, handler interface{}) {
	e.addEvent(name, handler, true)
}

func (e *eventEmitter) On(name string, handler interface{}) {
	e.addEvent(name, handler, false)
}

func (e *eventEmitter) RemoveListener(name string, handler interface{}) {
	e.eventsMutex.Lock()
	defer e.eventsMutex.Unlock()
	e.init()

	if evt, ok := e.events[name]; ok {
		evt.Lock()
		defer evt.Unlock()
		evt.removeHandler(handler)
	}
}

func (e *eventEmitter) RemoveListeners(name string) {
	e.eventsMutex.Lock()
	defer e.eventsMutex.Unlock()
	e.init()
	delete(e.events, name)
}

// ListenerCount count the listeners by name, count all if name is empty
func (e *eventEmitter) ListenerCount(name string) int {
	e.eventsMutex.Lock()
	defer e.eventsMutex.Unlock()
	e.init()

	if name != "" {
		evt, ok := e.events[name]
		if !ok {
			return 0
		}
		return evt.count()
	}

	count := 0
	for key := range e.events {
		count += e.events[key].count()
	}

	return count
}

func (e *eventEmitter) addEvent(name string, handler interface{}, once bool) {
	e.eventsMutex.Lock()
	defer e.eventsMutex.Unlock()
	e.init()

	if _, ok := e.events[name]; !ok {
		e.events[name] = &eventRegister{
			listeners: make([]listener, 0),
		}
	}
	e.events[name].addHandler(handler, once)
}

func (e *eventEmitter) init() {
	if !e.hasInit {
		e.events = make(map[string]*eventRegister, 0)
		e.hasInit = true
	}
}

func (er *eventRegister) addHandler(handler interface{}, once bool) {
	er.Lock()
	defer er.Unlock()
	er.listeners = append(er.listeners, listener{handler: handler, once: once})
}

func (er *eventRegister) count() int {
	er.Lock()
	defer er.Unlock()
	return len(er.listeners)
}

func (er *eventRegister) removeHandler(handler interface{}) {
	handlerPtr := reflect.ValueOf(handler).Pointer()

	er.listeners = slices.DeleteFunc(er.listeners, func(l listener) bool {
		return reflect.ValueOf(l.handler).Pointer() == handlerPtr
	})
}

func (er *eventRegister) callHandlers(payloads ...interface{}) int {
	payloadV := make([]reflect.Value, 0)

	for _, p := range payloads {
		payloadV = append(payloadV, reflect.ValueOf(p))
	}

	handle := func(l listener) {
		handlerV := reflect.ValueOf(l.handler)
		handlerV.Call(payloadV[:int(math.Min(float64(handlerV.Type().NumIn()), float64(len(payloadV))))])
	}

	er.Lock()
	defer er.Unlock()
	count := len(er.listeners)
	for _, l := range er.listeners {
		if l.once {
			defer er.removeHandler(l.handler)
		}
		handle(l)
	}
	return count
}
