package singleton

import (
	"fmt"
	"reflect"

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

var (
	instances    = make(map[string]any)
	getOrCreateC = make(chan entry)
)

type entry struct {
	f       func() any
	object  any
	resultC chan any
}

// GetInstance returns an existing instance of object. If it is not yet created, calls `constructor`, stores the
// result for future calls and return it
func GetInstance[T any](constructor func() T) T {
	var t T
	e := entry{
		object: t,
		f: func() any {
			return constructor()
		},
		resultC: make(chan any),
	}
	getOrCreateC <- e
	v := <-e.resultC
	return v.(T)
}

func init() {
	go func() {
		for {
			e := <-getOrCreateC
			name := reflect.TypeOf(e.object).String()
			v, created := instances[name]
			if !created {
				v = e.f()
				log.Trace("Created new singleton", "type", name, "instance", fmt.Sprintf("%+v", v))
				instances[name] = v
			}
			e.resultC <- v
		}
	}()
}