package project

import (
	"sync"

	"efprojects.com/kitten-ipc/kitcom/internal/tsgo/collections"
	"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tsoptions"
	"efprojects.com/kitten-ipc/kitcom/internal/tsgo/tspath"
	"github.com/zeebo/xxh3"
)

type extendedConfigCache struct {
	entries collections.SyncMap[tspath.Path, *extendedConfigCacheEntry]
}

type extendedConfigCacheEntry struct {
	mu       sync.Mutex
	entry    *tsoptions.ExtendedConfigCacheEntry
	hash     xxh3.Uint128
	refCount int
}

func (c *extendedConfigCache) Acquire(fh FileHandle, path tspath.Path, parse func() *tsoptions.ExtendedConfigCacheEntry) *tsoptions.ExtendedConfigCacheEntry {
	entry, loaded := c.loadOrStoreNewLockedEntry(path)
	defer entry.mu.Unlock()
	var hash xxh3.Uint128
	if fh != nil {
		hash = fh.Hash()
	}
	if !loaded || entry.hash != hash {
		// Reparse the config if the hash has changed, or parse for the first time.
		entry.entry = parse()
		entry.hash = hash
	}
	return entry.entry
}

func (c *extendedConfigCache) Ref(path tspath.Path) {
	if entry, ok := c.entries.Load(path); ok {
		entry.mu.Lock()
		entry.refCount++
		entry.mu.Unlock()
	}
}

func (c *extendedConfigCache) Deref(path tspath.Path) {
	if entry, ok := c.entries.Load(path); ok {
		entry.mu.Lock()
		entry.refCount--
		remove := entry.refCount <= 0
		entry.mu.Unlock()
		if remove {
			c.entries.Delete(path)
		}
	}
}

func (c *extendedConfigCache) Has(path tspath.Path) bool {
	_, ok := c.entries.Load(path)
	return ok
}

// loadOrStoreNewLockedEntry loads an existing entry or creates a new one. The returned
// entry's mutex is locked and its refCount is incremented (or initialized to 1
// in the case of a new entry).
func (c *extendedConfigCache) loadOrStoreNewLockedEntry(path tspath.Path) (*extendedConfigCacheEntry, bool) {
	entry := &extendedConfigCacheEntry{refCount: 1}
	entry.mu.Lock()
	if existing, loaded := c.entries.LoadOrStore(path, entry); loaded {
		existing.mu.Lock()
		existing.refCount++
		return existing, true
	}
	return entry, false
}
