package interval

import (
	"errors"
	"math/rand"
	"sync"
	"time"
)

type MutexMap[K comparable, V any] struct {
	mutex sync.Mutex
	m     map[K]V
}

func (m *MutexMap[K, V]) Get(key K) (V, error) {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	v, ok := m.m[key]
	if !ok {
		return v, errors.New("unknown key")
	}
	return v, nil
}

func (m *MutexMap[K, V]) Set(key K, value V) {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	m.m[key] = value
}

func (m *MutexMap[K, V]) Delete(key K) {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	delete(m.m, key)
}

var stopChannels = MutexMap[int, chan bool]{
	m: make(map[int]chan bool),
}

// SetInterval schedules a repeating task to be executed at a specified interval.
func SetInterval(f func(), milliseconds int) (id int) {
	for {
		id = rand.Int()
		if _, err := stopChannels.Get(id); err == nil {
			continue // ID collision, keep looking for another unique random value
		}
		break
	}

	stop := make(chan bool)
	stopChannels.Set(id, stop)

	ticker := time.NewTicker(time.Duration(milliseconds) * time.Millisecond)

	go func() {
		for {
			select {
			case <-stop:
				ticker.Stop()
				return
			case <-ticker.C:
				f()
			}
		}
	}()

	return
}

// ClearInterval stops a scheduled interval identified by the specified interval ID.
func ClearInterval(id int) error {
	stop, err := stopChannels.Get(id)
	if err != nil {
		return err
	}
	stop <- true
	stopChannels.Delete(id)

	return nil
}

// SetTimeout schedules a one-time task to be executed after a specified interval.
func SetTimeout(f func(), milliseconds int) {
	timer := time.NewTimer(time.Duration(milliseconds) * time.Millisecond)
	go func() {
		<-timer.C
		timer.Stop()
		f()
	}()
}
