package jdenticon

import (
	"context"
	"fmt"

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

// Generator provides thread-safe identicon generation with caching.
// It wraps the internal engine.Generator to provide a clean public API.
type Generator struct {
	engine *engine.Generator
	config Config // Store the configuration for validation during Generate calls
}

// NewGenerator creates a new Generator with default configuration and default caching.
func NewGenerator() (*Generator, error) {
	engineGen, err := engine.NewDefaultGenerator()
	if err != nil {
		return nil, NewErrGenerationFailed("", 0, err)
	}

	return &Generator{
		engine: engineGen,
		config: DefaultConfig(),
	}, nil
}

// NewGeneratorWithCacheSize creates a new Generator with the specified cache size.
func NewGeneratorWithCacheSize(cacheSize int) (*Generator, error) {
	if cacheSize <= 0 {
		return nil, NewErrInvalidInput("cacheSize", fmt.Sprintf("%d", cacheSize), "must be positive")
	}

	config := engine.DefaultGeneratorConfig()
	config.CacheSize = cacheSize

	engineGen, err := engine.NewGeneratorWithConfig(config)
	if err != nil {
		return nil, NewErrCacheCreationFailed(cacheSize, err)
	}

	return &Generator{
		engine: engineGen,
		config: DefaultConfig(),
	}, nil
}

// NewGeneratorWithConfig creates a new Generator with custom configuration and caching.
func NewGeneratorWithConfig(config Config, cacheSize int) (*Generator, error) {
	if cacheSize <= 0 {
		return nil, NewErrInvalidInput("cacheSize", fmt.Sprintf("%d", cacheSize), "must be positive")
	}

	// Convert public config to internal config
	colorConfig, err := config.toEngineColorConfig()
	if err != nil {
		return nil, err
	}

	generatorConfig := engine.GeneratorConfig{
		ColorConfig:   colorConfig,
		CacheSize:     cacheSize,
		MaxComplexity: config.MaxComplexity,
		MaxIconSize:   config.MaxIconSize,
	}

	engineGen, err := engine.NewGeneratorWithConfig(generatorConfig)
	if err != nil {
		return nil, NewErrCacheCreationFailed(cacheSize, err)
	}

	return &Generator{
		engine: engineGen,
		config: config,
	}, nil
}

// Generate creates an identicon for the given input string and size with context support.
// The context can be used to set timeouts or cancel generation.
//
// The input string is hashed to generate a deterministic identicon.
// Size must be positive and represents the width/height in pixels.
//
// This method applies the security limits that were configured when the Generator was created.
// For different limits, create a new Generator with NewGeneratorWithConfig.
//
// This method is thread-safe and uses caching if configured.
func (g *Generator) Generate(ctx context.Context, input string, size int) (*Icon, error) {
	// Apply validation using the generator's stored configuration
	if err := validateInputs(input, size, g.config); err != nil {
		return nil, err
	}

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

	// Convert input to hash
	hash := util.ComputeHash(input)

	// Validate complexity before generation
	if err := validateComplexity(hash, g.config); err != nil {
		return nil, err
	}

	// Generate using the internal engine with context
	engineIcon, err := g.engine.Generate(ctx, hash, float64(size))
	if err != nil {
		return nil, NewErrGenerationFailed(input, size, err)
	}

	// Wrap in public Icon directly
	return newIcon(engineIcon), nil
}

// GetCacheMetrics returns the cache hit and miss counts.
// These metrics are thread-safe to read.
func (g *Generator) GetCacheMetrics() (hits, misses int64) {
	return g.engine.GetCacheMetrics()
}
