package engine

import (
	"context"
	"fmt"

	"gitea.dockr.co/kev/go-jdenticon/internal/util"
)

// Generate creates an identicon with the specified hash and size
// This method includes caching and singleflight to prevent duplicate work
func (g *Generator) Generate(ctx context.Context, hash string, size float64) (*Icon, error) {
	// Basic validation
	if hash == "" {
		return nil, fmt.Errorf("jdenticon: engine: generation failed: hash cannot be empty")
	}
	if size <= 0 {
		return nil, fmt.Errorf("jdenticon: engine: generation failed: invalid size: %f", size)
	}

	// Check icon size limits
	if g.maxIconSize > 0 && int(size) > g.maxIconSize {
		return nil, fmt.Errorf("jdenticon: engine: generation failed: icon size %d exceeds maximum allowed size %d", int(size), g.maxIconSize)
	}

	// Validate hash format
	if !util.IsValidHash(hash) {
		return nil, fmt.Errorf("jdenticon: engine: generation failed: invalid hash format: %s", hash)
	}

	// Check for context cancellation before proceeding
	if err := ctx.Err(); err != nil {
		return nil, err
	}

	// Generate cache key
	key := g.cacheKey(hash, size)

	// Check cache first (with read lock)
	g.mu.RLock()
	if cached, ok := g.cache.Get(key); ok {
		g.mu.RUnlock()
		g.metrics.recordHit()
		return cached, nil
	}
	g.mu.RUnlock()

	// Use singleflight to prevent multiple concurrent generations for the same key
	result, err, _ := g.sf.Do(key, func() (interface{}, error) {
		// Check cache again inside singleflight (another goroutine might have populated it)
		g.mu.RLock()
		if cached, ok := g.cache.Get(key); ok {
			g.mu.RUnlock()
			g.metrics.recordHit()
			return cached, nil
		}
		g.mu.RUnlock()

		// Generate the icon
		icon, err := g.generateIcon(ctx, hash, size)
		if err != nil {
			return nil, err
		}

		// Store in cache (with write lock)
		g.mu.Lock()
		g.cache.Add(key, icon)
		g.mu.Unlock()

		g.metrics.recordMiss()
		return icon, nil
	})

	if err != nil {
		return nil, err
	}

	return result.(*Icon), nil
}

// GenerateWithoutCache creates an identicon without using cache
// This method is useful for testing or when caching is not desired
func (g *Generator) GenerateWithoutCache(ctx context.Context, hash string, size float64) (*Icon, error) {
	// Basic validation
	if hash == "" {
		return nil, fmt.Errorf("jdenticon: engine: generation failed: hash cannot be empty")
	}
	if size <= 0 {
		return nil, fmt.Errorf("jdenticon: engine: generation failed: invalid size: %f", size)
	}

	// Validate hash format
	if !util.IsValidHash(hash) {
		return nil, fmt.Errorf("jdenticon: engine: generation failed: invalid hash format: %s", hash)
	}

	// Check for context cancellation
	if err := ctx.Err(); err != nil {
		return nil, err
	}

	return g.generateIcon(ctx, hash, size)
}
