package singleton

import (
	"fmt"
	"reflect"
	"sync"

	"github.com/navidrome/navidrome/log"
)

var (
	instances = make(map[string]any)
	lock      sync.RWMutex
)

// GetInstance returns an existing instance of object. If it is not yet created, calls `constructor`, stores the
// result for future calls and returns it
func GetInstance[T any](constructor func() T) T {
	var v T
	name := reflect.TypeOf(v).String()

	v, available := func() (T, bool) {
		lock.RLock()
		defer lock.RUnlock()
		v, available := instances[name].(T)
		return v, available
	}()

	if available {
		return v
	}

	lock.Lock()
	defer lock.Unlock()
	v, available = instances[name].(T)
	if available {
		return v
	}

	v = constructor()
	log.Trace("Created new singleton", "type", name, "instance", fmt.Sprintf("%+v", v))
	instances[name] = v
	return v
}