package uiapi

import (
	"sync"
	"time"
)

type cacheItem struct {
	data      interface{}
	expiresAt time.Time
}

type cache struct {
	sync.RWMutex
	items     map[string]cacheItem
	ttl       time.Duration
	stop      chan struct{}
	closeOnce sync.Once
}

func newCache(ttl time.Duration) *cache {
	c := &cache{
		items: make(map[string]cacheItem),
		ttl:   ttl,
		stop:  make(chan struct{}),
	}
	go c.cleanup()
	return c
}

func (c *cache) get(key string) (interface{}, bool) {
	c.RLock()
	defer c.RUnlock()
	item, ok := c.items[key]
	if !ok {
		return nil, false
	}
	if time.Now().After(item.expiresAt) {
		return nil, false
	}
	return item.data, true
}

func (c *cache) set(key string, data interface{}) {
	c.Lock()
	defer c.Unlock()
	c.items[key] = cacheItem{
		data:      data,
		expiresAt: time.Now().Add(c.ttl),
	}
}

// Invalidate removes a specific key from the cache
func (c *cache) Invalidate(key string) {
	c.Lock()
	defer c.Unlock()
	delete(c.items, key)
}

// InvalidateAll clears all cache entries
func (c *cache) InvalidateAll() {
	c.Lock()
	defer c.Unlock()
	c.items = make(map[string]cacheItem)
}

func (c *cache) cleanup() {
	ticker := time.NewTicker(time.Minute)
	defer ticker.Stop()
	for {
		select {
		case <-ticker.C:
			c.Lock()
			now := time.Now()
			for k, v := range c.items {
				if now.After(v.expiresAt) {
					delete(c.items, k)
				}
			}
			c.Unlock()
		case <-c.stop:
			return
		}
	}
}

// Close stops the cleanup goroutine. Safe to call multiple times (idempotent).
func (c *cache) Close() {
	c.closeOnce.Do(func() {
		close(c.stop)
	})
}
