package licensing

import (
	"sync"
	"time"

	"github.com/google/uuid"
)

// CacheEntry holds cached license validation data
type CacheEntry struct {
	Response  *ValidationResponse
	ExpiresAt time.Time
}

// LicenseCache provides in-memory caching for license validations
type LicenseCache struct {
	mu      sync.RWMutex
	entries map[string]*CacheEntry
	ttl     time.Duration
}

// NewLicenseCache creates a new license cache with specified TTL
func NewLicenseCache(ttl time.Duration) *LicenseCache {
	cache := &LicenseCache{
		entries: make(map[string]*CacheEntry),
		ttl:     ttl,
	}

	// Start cleanup goroutine
	go cache.cleanup()

	return cache
}

// Get retrieves cached validation response if available and not expired
func (c *LicenseCache) Get(deploymentID uuid.UUID, feature string) *ValidationResponse {
	c.mu.RLock()
	defer c.mu.RUnlock()

	key := c.cacheKey(deploymentID, feature)
	entry, exists := c.entries[key]

	if !exists || time.Now().After(entry.ExpiresAt) {
		return nil
	}

	return entry.Response
}

// Set stores validation response in cache with TTL
func (c *LicenseCache) Set(deploymentID uuid.UUID, feature string, response *ValidationResponse) {
	c.mu.Lock()
	defer c.mu.Unlock()

	key := c.cacheKey(deploymentID, feature)
	c.entries[key] = &CacheEntry{
		Response:  response,
		ExpiresAt: time.Now().Add(c.ttl),
	}
}

// Invalidate removes specific cache entry
func (c *LicenseCache) Invalidate(deploymentID uuid.UUID, feature string) {
	c.mu.Lock()
	defer c.mu.Unlock()

	key := c.cacheKey(deploymentID, feature)
	delete(c.entries, key)
}

// InvalidateAll removes all cached entries for a deployment
func (c *LicenseCache) InvalidateAll(deploymentID uuid.UUID) {
	c.mu.Lock()
	defer c.mu.Unlock()

	prefix := deploymentID.String() + ":"
	for key := range c.entries {
		if len(key) > len(prefix) && key[:len(prefix)] == prefix {
			delete(c.entries, key)
		}
	}
}

// Clear removes all cached entries
func (c *LicenseCache) Clear() {
	c.mu.Lock()
	defer c.mu.Unlock()

	c.entries = make(map[string]*CacheEntry)
}

// Stats returns cache statistics
func (c *LicenseCache) Stats() map[string]interface{} {
	c.mu.RLock()
	defer c.mu.RUnlock()

	totalEntries := len(c.entries)
	expiredEntries := 0
	now := time.Now()

	for _, entry := range c.entries {
		if now.After(entry.ExpiresAt) {
			expiredEntries++
		}
	}

	return map[string]interface{}{
		"total_entries":   totalEntries,
		"expired_entries": expiredEntries,
		"active_entries":  totalEntries - expiredEntries,
		"ttl_seconds":     int(c.ttl.Seconds()),
	}
}

// cacheKey generates cache key from deployment ID and feature
func (c *LicenseCache) cacheKey(deploymentID uuid.UUID, feature string) string {
	return deploymentID.String() + ":" + feature
}

// cleanup removes expired entries periodically
func (c *LicenseCache) cleanup() {
	ticker := time.NewTicker(c.ttl / 2) // Clean up twice as often as TTL
	defer ticker.Stop()

	for range ticker.C {
		c.mu.Lock()
		now := time.Now()
		for key, entry := range c.entries {
			if now.After(entry.ExpiresAt) {
				delete(c.entries, key)
			}
		}
		c.mu.Unlock()
	}
}