package face

import (
	"context"
	"encoding/json"
	"sync"
	"time"
)

// CacheService defines the interface for caching face search results
type CacheService interface {
	// Get retrieves a cached value by key
	Get(ctx context.Context, key string) (string, error)

	// Set stores a value with expiration
	Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error

	// Delete removes cached values
	Delete(ctx context.Context, keys ...string) error

	// GetJSON retrieves and unmarshals a cached JSON value
	GetJSON(ctx context.Context, key string, dest interface{}) error

	// SetJSON marshals and stores a JSON value
	SetJSON(ctx context.Context, key string, value interface{}, expiration time.Duration) error
}

// MemoryCache is an in-memory cache implementation for testing and development
type MemoryCache struct {
	mu      sync.RWMutex
	entries map[string]*cacheEntry
}

type cacheEntry struct {
	value     string
	expiresAt time.Time
}

// NewMemoryCache creates a new in-memory cache
func NewMemoryCache() *MemoryCache {
	return &MemoryCache{
		entries: make(map[string]*cacheEntry),
	}
}

// Get retrieves a value from the cache
func (c *MemoryCache) Get(ctx context.Context, key string) (string, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	entry, ok := c.entries[key]
	if !ok {
		return "", nil
	}

	if time.Now().After(entry.expiresAt) {
		return "", nil
	}

	return entry.value, nil
}

// Set stores a value in the cache
func (c *MemoryCache) Set(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	var strValue string
	switch v := value.(type) {
	case string:
		strValue = v
	default:
		bytes, err := json.Marshal(v)
		if err != nil {
			return err
		}
		strValue = string(bytes)
	}

	c.entries[key] = &cacheEntry{
		value:     strValue,
		expiresAt: time.Now().Add(expiration),
	}

	return nil
}

// Delete removes values from the cache
func (c *MemoryCache) Delete(ctx context.Context, keys ...string) error {
	c.mu.Lock()
	defer c.mu.Unlock()

	for _, key := range keys {
		delete(c.entries, key)
	}

	return nil
}

// GetJSON retrieves and unmarshals a cached JSON value
func (c *MemoryCache) GetJSON(ctx context.Context, key string, dest interface{}) error {
	value, err := c.Get(ctx, key)
	if err != nil {
		return err
	}

	if value == "" {
		return nil
	}

	return json.Unmarshal([]byte(value), dest)
}

// SetJSON marshals and stores a JSON value
func (c *MemoryCache) SetJSON(ctx context.Context, key string, value interface{}, expiration time.Duration) error {
	return c.Set(ctx, key, value, expiration)
}

// Cleanup removes expired entries (call periodically)
func (c *MemoryCache) Cleanup() {
	c.mu.Lock()
	defer c.mu.Unlock()

	now := time.Now()
	for key, entry := range c.entries {
		if now.After(entry.expiresAt) {
			delete(c.entries, key)
		}
	}
}

// CacheKeys returns common cache key patterns
type CacheKeys struct{}

// Search returns the cache key for search results
func (CacheKeys) Search(spaceID string, imageHash string) string {
	return "search:" + spaceID + ":" + imageHash
}

// Photo returns the cache key for photo metadata
func (CacheKeys) Photo(photoID string) string {
	return "photo:" + photoID
}

// Face returns the cache key for face embeddings
func (CacheKeys) Face(faceID string) string {
	return "face:" + faceID
}

// Indexed returns the cache key for indexed photos
func (CacheKeys) Indexed(photoID string) string {
	return "indexed:" + photoID
}
