package engine

import (
	"fmt"
	"strconv"
	"sync/atomic"

	lru "github.com/hashicorp/golang-lru/v2"
	"gitea.dockr.co/kev/go-jdenticon/internal/constants"
)

// CacheMetrics holds cache performance metrics using atomic operations for efficiency
type CacheMetrics struct {
	hits   int64 // Use atomic operations, no mutex needed
	misses int64 // Use atomic operations, no mutex needed
}

// GetHits returns the number of cache hits
func (m *CacheMetrics) GetHits() int64 {
	return atomic.LoadInt64(&m.hits)
}

// GetMisses returns the number of cache misses
func (m *CacheMetrics) GetMisses() int64 {
	return atomic.LoadInt64(&m.misses)
}

// recordHit records a cache hit atomically
func (m *CacheMetrics) recordHit() {
	atomic.AddInt64(&m.hits, 1)
}

// recordMiss records a cache miss atomically
func (m *CacheMetrics) recordMiss() {
	atomic.AddInt64(&m.misses, 1)
}

// cacheKey generates a cache key from hash and size
func (g *Generator) cacheKey(hash string, size float64) string {
	// Use a simple concatenation approach for better performance
	// Convert float64 size to string with appropriate precision
	return hash + ":" + strconv.FormatFloat(size, 'f', 1, 64)
}

// ClearCache removes all entries from the cache and resets metrics
func (g *Generator) ClearCache() {
	g.mu.Lock()
	defer g.mu.Unlock()

	g.cache.Purge()
	// Reset metrics
	atomic.StoreInt64(&g.metrics.hits, 0)
	atomic.StoreInt64(&g.metrics.misses, 0)
}

// GetCacheSize returns the number of items currently in the cache
func (g *Generator) GetCacheSize() int {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.cache.Len()
}

// GetCacheCapacity returns the maximum number of items the cache can hold
func (g *Generator) GetCacheCapacity() int {
	g.mu.RLock()
	defer g.mu.RUnlock()
	// LRU cache doesn't expose capacity, return the configured capacity from config
	return g.config.CacheSize
}

// GetCacheMetrics returns the cache hit and miss counts
func (g *Generator) GetCacheMetrics() (hits int64, misses int64) {
	return g.metrics.GetHits(), g.metrics.GetMisses()
}

// SetConfig updates the generator's color configuration and clears the cache
func (g *Generator) SetConfig(colorConfig ColorConfig) {
	g.mu.Lock()
	defer g.mu.Unlock()

	g.config.ColorConfig = colorConfig
	g.cache.Purge() // Clear cache since config changed
}

// SetGeneratorConfig updates the generator's configuration, including cache size
func (g *Generator) SetGeneratorConfig(config GeneratorConfig) error {
	g.mu.Lock()
	defer g.mu.Unlock()

	// Validate cache size
	if config.CacheSize <= 0 {
		return fmt.Errorf("jdenticon: engine: invalid cache size: %d", config.CacheSize)
	}

	// Create new cache with updated size if necessary
	if config.CacheSize != g.config.CacheSize {
		newCache, err := lru.New[string, *Icon](config.CacheSize)
		if err != nil {
			return fmt.Errorf("jdenticon: engine: failed to create new cache: %w", err)
		}
		g.cache = newCache
	} else {
		// Same cache size, just clear existing cache
		g.cache.Purge()
	}

	g.config = config

	// Update resolved max icon size
	if config.MaxIconSize > 0 {
		g.maxIconSize = config.MaxIconSize
	} else {
		g.maxIconSize = constants.DefaultMaxIconSize
	}

	return nil
}

// GetConfig returns the current color configuration
func (g *Generator) GetConfig() ColorConfig {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.config.ColorConfig
}

// GetGeneratorConfig returns the current generator configuration
func (g *Generator) GetGeneratorConfig() GeneratorConfig {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.config
}
