package db

import (
	"sync"
	"time"
)

type entityCacheEntry struct {
	slug  string
	id    string
	attrs string
	exp   time.Time
}

// EntityCache is a TTL cache mapping entity IDs to slugs and back,
// keyed for the hot resolution paths.
type EntityCache struct {
	mu   sync.RWMutex
	m    map[string]entityCacheEntry
	ttl  time.Duration
}

// NewEntityCache builds a cache with the given TTL.
func NewEntityCache(ttl time.Duration) *EntityCache {
	return &EntityCache{
		m:   make(map[string]entityCacheEntry),
		ttl: ttl,
	}
}

// GetSlug resolves an entity ID to its slug.
func (c *EntityCache) GetSlug(id string) (string, bool) {
	c.mu.RLock()
	e, ok := c.m[id]
	c.mu.RUnlock()
	if !ok || time.Now().After(e.exp) {
		return "", false
	}
	return e.slug, true
}

// GetID resolves a slug to its entity ID.
func (c *EntityCache) GetID(slug string) (string, bool) {
	c.mu.RLock()
	e, ok := c.m[slug]
	c.mu.RUnlock()
	if !ok || time.Now().After(e.exp) {
		return "", false
	}
	return e.id, true
}

// Set records the slug/id pair and serialized attributes.
func (c *EntityCache) Set(slug, id, attrs string) {
	exp := time.Now().Add(c.ttl)
	c.mu.Lock()
	c.m[slug] = entityCacheEntry{slug: slug, id: id, attrs: attrs, exp: exp}
	c.m[id] = entityCacheEntry{slug: slug, id: id, attrs: attrs, exp: exp}
	c.mu.Unlock()
}

// Invalidate drops the cached entries for one slug/id pair.
func (c *EntityCache) Invalidate(slug, id string) {
	c.mu.Lock()
	delete(c.m, slug)
	delete(c.m, id)
	c.mu.Unlock()
}