package jdenticon

import (
	"fmt"
)

// Error types for structured error handling following Go best practices.

// ErrInvalidInput represents an error due to invalid input parameters.
type ErrInvalidInput struct {
	Field  string // The field or parameter that was invalid
	Value  string // The invalid value (may be truncated for display)
	Reason string // Human-readable explanation of why it's invalid
}

func (e *ErrInvalidInput) Error() string {
	if e.Field != "" {
		return fmt.Sprintf("jdenticon: invalid input for %s: %s (got: %s)", e.Field, e.Reason, e.Value)
	}
	return fmt.Sprintf("jdenticon: invalid input: %s", e.Reason)
}

// NewErrInvalidInput creates a new ErrInvalidInput.
func NewErrInvalidInput(field, value, reason string) *ErrInvalidInput {
	return &ErrInvalidInput{
		Field:  field,
		Value:  value,
		Reason: reason,
	}
}

// ErrInvalidSize represents an error due to invalid size parameter.
type ErrInvalidSize int

func (e ErrInvalidSize) Error() string {
	return fmt.Sprintf("jdenticon: invalid size: must be positive, got %d", int(e))
}

// ErrInvalidIcon represents an error due to invalid icon state.
type ErrInvalidIcon string

func (e ErrInvalidIcon) Error() string {
	return fmt.Sprintf("jdenticon: invalid icon: %s", string(e))
}

// ErrRenderFailed represents an error during rendering.
type ErrRenderFailed struct {
	Format string // The format being rendered (SVG, PNG)
	Cause  error  // The underlying error
}

func (e *ErrRenderFailed) Error() string {
	return fmt.Sprintf("jdenticon: %s rendering failed: %v", e.Format, e.Cause)
}

func (e *ErrRenderFailed) Unwrap() error {
	return e.Cause
}

// NewErrRenderFailed creates a new ErrRenderFailed.
func NewErrRenderFailed(format string, cause error) *ErrRenderFailed {
	return &ErrRenderFailed{
		Format: format,
		Cause:  cause,
	}
}

// ErrGenerationFailed represents an error during identicon generation.
type ErrGenerationFailed struct {
	Input string // The input string that failed to generate (may be truncated)
	Size  int    // The requested size
	Cause error  // The underlying error
}

func (e *ErrGenerationFailed) Error() string {
	return fmt.Sprintf("jdenticon: generation failed for input %q (size %d): %v", e.Input, e.Size, e.Cause)
}

func (e *ErrGenerationFailed) Unwrap() error {
	return e.Cause
}

// NewErrGenerationFailed creates a new ErrGenerationFailed.
func NewErrGenerationFailed(input string, size int, cause error) *ErrGenerationFailed {
	// Truncate long inputs for display
	displayInput := input
	if len(input) > 50 {
		displayInput = input[:47] + "..."
	}

	return &ErrGenerationFailed{
		Input: displayInput,
		Size:  size,
		Cause: cause,
	}
}

// ErrCacheCreationFailed represents an error during cache creation.
type ErrCacheCreationFailed struct {
	Size  int   // The requested cache size
	Cause error // The underlying error
}

func (e *ErrCacheCreationFailed) Error() string {
	return fmt.Sprintf("jdenticon: cache creation failed (size %d): %v", e.Size, e.Cause)
}

func (e *ErrCacheCreationFailed) Unwrap() error {
	return e.Cause
}

// NewErrCacheCreationFailed creates a new ErrCacheCreationFailed.
func NewErrCacheCreationFailed(size int, cause error) *ErrCacheCreationFailed {
	return &ErrCacheCreationFailed{
		Size:  size,
		Cause: cause,
	}
}

// ErrValueTooLarge is returned when a numeric input exceeds a configured limit.
// This supports the configurable DoS protection system.
type ErrValueTooLarge struct {
	ParameterName string // The name of the parameter being validated (e.g., "IconSize", "InputLength")
	Limit         int    // The configured limit that was exceeded
	Actual        int    // The actual value that was provided
}

func (e *ErrValueTooLarge) Error() string {
	return fmt.Sprintf("jdenticon: %s of %d exceeds configured limit of %d",
		e.ParameterName, e.Actual, e.Limit)
}

// NewErrValueTooLarge creates a new ErrValueTooLarge.
func NewErrValueTooLarge(parameterName string, limit, actual int) *ErrValueTooLarge {
	return &ErrValueTooLarge{
		ParameterName: parameterName,
		Limit:         limit,
		Actual:        actual,
	}
}

// ErrEffectiveSizeTooLarge is returned when the effective PNG size (size * supersampling) exceeds limits.
// This provides specific context for PNG rendering with supersampling.
type ErrEffectiveSizeTooLarge struct {
	Limit         int // The configured size limit
	Actual        int // The calculated effective size (size * supersampling)
	Size          int // The requested icon size
	Supersampling int // The supersampling factor applied
}

func (e *ErrEffectiveSizeTooLarge) Error() string {
	return fmt.Sprintf("jdenticon: effective PNG size of %d (size %d × supersampling %d) exceeds limit of %d",
		e.Actual, e.Size, e.Supersampling, e.Limit)
}

// NewErrEffectiveSizeTooLarge creates a new ErrEffectiveSizeTooLarge.
func NewErrEffectiveSizeTooLarge(limit, actual, size, supersampling int) *ErrEffectiveSizeTooLarge {
	return &ErrEffectiveSizeTooLarge{
		Limit:         limit,
		Actual:        actual,
		Size:          size,
		Supersampling: supersampling,
	}
}

// ErrComplexityLimitExceeded is returned when the calculated geometric complexity exceeds the configured limit.
// This prevents resource exhaustion attacks through extremely complex identicon generation.
type ErrComplexityLimitExceeded struct {
	Limit     int    // The configured complexity limit
	Actual    int    // The calculated complexity score
	InputHash string // The input hash that caused the high complexity (truncated for display)
}

func (e *ErrComplexityLimitExceeded) Error() string {
	return fmt.Sprintf("jdenticon: complexity score of %d exceeds limit of %d for input hash %s",
		e.Actual, e.Limit, e.InputHash)
}

// NewErrComplexityLimitExceeded creates a new ErrComplexityLimitExceeded.
func NewErrComplexityLimitExceeded(limit, actual int, inputHash string) *ErrComplexityLimitExceeded {
	// Truncate long hash for display
	displayHash := inputHash
	if len(inputHash) > 16 {
		displayHash = inputHash[:13] + "..."
	}

	return &ErrComplexityLimitExceeded{
		Limit:     limit,
		Actual:    actual,
		InputHash: displayHash,
	}
}
