package util

import (
	"slices"
	"sync"
)

type Collection[K comparable, T interface{ GetKey() K }] struct {
	L               *sync.RWMutex
	Items           []T
	m               map[K]T
	Length          int
	addListeners    []func(T)
	removeListeners []func(T)
}

func (c *Collection[K, T]) OnAdd(listener func(T)) {
	c.addListeners = append(c.addListeners, listener)
}

func (c *Collection[K, T]) OnRemove(listener func(T)) {
	c.removeListeners = append(c.removeListeners, listener)
}

func (c *Collection[K, T]) Add(item T) {
	if c.L != nil {
		c.L.Lock()
		defer c.L.Unlock()
	}
	c.Items = append(c.Items, item)
	if c.Length > 100 || c.m != nil {
		if c.m == nil {
			c.m = make(map[K]T)
			for _, v := range c.Items {
				c.m[v.GetKey()] = v
			}
		}
		c.m[item.GetKey()] = item
	}
	c.Length++
	for _, listener := range c.addListeners {
		listener(item)
	}
}

func (c *Collection[K, T]) AddUnique(item T) (ok bool) {
	if _, ok = c.Get(item.GetKey()); !ok {
		c.Add(item)
	}
	return !ok
}

func (c *Collection[K, T]) Set(item T) (added bool) {
	key := item.GetKey()
	if c.m != nil {
		c.m[key] = item
	}
	for i := range c.Items {
		if c.Items[i].GetKey() == key {
			c.Items[i] = item
			return false
		}
	}
	c.Add(item)
	return true
}

func (c *Collection[K, T]) Range(f func(T) bool) {
	if c.L != nil {
		c.L.RLock()
		defer c.L.RUnlock()
	}
	for _, item := range c.Items {
		if !f(item) {
			break
		}
	}
}

func (c *Collection[K, T]) Remove(item T) bool {
	return c.RemoveByKey(item.GetKey())
}

func (c *Collection[K, T]) RemoveByKey(key K) bool {
	if c.L != nil {
		c.L.Lock()
		defer c.L.Unlock()
	}
	delete(c.m, key)
	for i := range c.Length {
		if c.Items[i].GetKey() == key {
			item := c.Items[i]
			c.Items = slices.Delete(c.Items, i, i+1)
			c.Length--
			for _, listener := range c.removeListeners {
				listener(item)
			}
			return true
		}
	}
	return false
}

//	func (c *Collection[K, T]) GetOrCreate(key K) (item T, find bool) {
//		if c.L != nil {
//			c.L.Lock()
//			defer c.L.Unlock()
//		}
//		if c.m != nil {
//			item, find = c.m[key]
//			return item, find
//		}
//		for _, item = range c.Items {
//			if item.GetKey() == key {
//				return item, true
//			}
//		}
//		item = reflect.New(reflect.TypeOf(item).Elem()).Interface().(T)
//		return
//	}
func (c *Collection[K, T]) Has(key K) bool {
	_, ok := c.Get(key)
	return ok
}

func (c *Collection[K, T]) Get(key K) (item T, ok bool) {
	if c.L != nil {
		c.L.RLock()
		defer c.L.RUnlock()
	}
	if c.m != nil {
		item, ok = c.m[key]
		return
	}
	for _, item := range c.Items {
		if item.GetKey() == key {
			return item, true
		}
	}
	return
}

func (c *Collection[K, T]) Find(f func(T) bool) (item T, ok bool) {
	if c.L != nil {
		c.L.RLock()
		defer c.L.RUnlock()
	}
	for _, item := range c.Items {
		if f(item) {
			return item, true
		}
	}
	return
}

func (c *Collection[K, T]) Search(f func(T) bool) func(yield func(item T) bool) {
	if c.L != nil {
		c.L.RLock()
		defer c.L.RUnlock()
	}
	return func(yield func(item T) bool) {
		for _, item := range c.Items {
			if f(item) {
				if !yield(item) {
					break
				}
			}
		}
	}
}

func (c *Collection[K, T]) GetKey() K {
	return c.Items[0].GetKey()
}

func (c *Collection[K, T]) Clear() {
	if c.L != nil {
		c.L.Lock()
		defer c.L.Unlock()
	}
	c.Items = nil
	c.m = nil
	c.Length = 0
}
