package engine

import (
	"fmt"
	"image/color"
	"regexp"
	"strconv"
	"sync"
)

var (
	// Compiled regex pattern for hex color validation
	hexColorRegex *regexp.Regexp
	// Initialization guard for hex color regex
	hexColorRegexOnce sync.Once
)

// getHexColorRegex returns the compiled hex color regex pattern, compiling it only once.
// Supports formats: #RGB, #RGBA, #RRGGBB, #RRGGBBAA
func getHexColorRegex() *regexp.Regexp {
	hexColorRegexOnce.Do(func() {
		hexColorRegex = regexp.MustCompile(`^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$`)
	})
	return hexColorRegex
}

// ParseHexColorToRGBA is the consolidated hex color parsing function for the entire codebase.
// It parses a hexadecimal color string and returns color.RGBA and an error.
// Supports formats: #RGB, #RGBA, #RRGGBB, #RRGGBBAA
// Returns error if the format is invalid.
//
// This function replaces all other hex color parsing implementations and provides
// consistent error handling for all color operations, following REQ-1.3.
func ParseHexColorToRGBA(hexStr string) (color.RGBA, error) {
	if len(hexStr) == 0 || hexStr[0] != '#' {
		return color.RGBA{}, fmt.Errorf("jdenticon: engine: hex color parsing failed: invalid color format: %s", hexStr)
	}

	// Validate the hex color format using regex
	if !getHexColorRegex().MatchString(hexStr) {
		return color.RGBA{}, fmt.Errorf("jdenticon: engine: hex color parsing failed: invalid hex color format: %s", hexStr)
	}

	hex := hexStr[1:]                   // Remove '#' prefix
	var r, g, b, a uint8 = 0, 0, 0, 255 // Default alpha is fully opaque

	// Helper to parse a 2-character hex component
	parse := func(target *uint8, hexStr string) error {
		val, err := hexToByte(hexStr)
		if err != nil {
			return err
		}
		*target = val
		return nil
	}

	// Helper to parse a single hex digit and expand it (e.g., 'F' -> 'FF' = 255)
	parseShort := func(target *uint8, hexChar byte) error {
		var val uint8
		if hexChar >= '0' && hexChar <= '9' {
			val = hexChar - '0'
		} else if hexChar >= 'a' && hexChar <= 'f' {
			val = hexChar - 'a' + 10
		} else if hexChar >= 'A' && hexChar <= 'F' {
			val = hexChar - 'A' + 10
		} else {
			return fmt.Errorf("jdenticon: engine: hex digit parsing failed: invalid hex character: %c", hexChar)
		}
		*target = val * 17 // Expand single digit: 0xF * 17 = 0xFF
		return nil
	}

	switch len(hex) {
	case 3: // #RGB -> expand to #RRGGBB
		if err := parseShort(&r, hex[0]); err != nil {
			return color.RGBA{}, fmt.Errorf("jdenticon: engine: hex color parsing failed: failed to parse red component: %w", err)
		}
		if err := parseShort(&g, hex[1]); err != nil {
			return color.RGBA{}, fmt.Errorf("jdenticon: engine: hex color parsing failed: failed to parse green component: %w", err)
		}
		if err := parseShort(&b, hex[2]); err != nil {
			return color.RGBA{}, fmt.Errorf("jdenticon: engine: hex color parsing failed: failed to parse blue component: %w", err)
		}

	case 4: // #RGBA -> expand to #RRGGBBAA
		if err := parseShort(&r, hex[0]); err != nil {
			return color.RGBA{}, fmt.Errorf("jdenticon: engine: hex color parsing failed: failed to parse red component: %w", err)
		}
		if err := parseShort(&g, hex[1]); err != nil {
			return color.RGBA{}, fmt.Errorf("jdenticon: engine: hex color parsing failed: failed to parse green component: %w", err)
		}
		if err := parseShort(&b, hex[2]); err != nil {
			return color.RGBA{}, fmt.Errorf("jdenticon: engine: hex color parsing failed: failed to parse blue component: %w", err)
		}
		if err := parseShort(&a, hex[3]); err != nil {
			return color.RGBA{}, fmt.Errorf("jdenticon: engine: hex color parsing failed: failed to parse alpha component: %w", err)
		}

	case 6: // #RRGGBB
		if err := parse(&r, hex[0:2]); err != nil {
			return color.RGBA{}, fmt.Errorf("jdenticon: engine: hex color parsing failed: failed to parse red component: %w", err)
		}
		if err := parse(&g, hex[2:4]); err != nil {
			return color.RGBA{}, fmt.Errorf("jdenticon: engine: hex color parsing failed: failed to parse green component: %w", err)
		}
		if err := parse(&b, hex[4:6]); err != nil {
			return color.RGBA{}, fmt.Errorf("jdenticon: engine: hex color parsing failed: failed to parse blue component: %w", err)
		}

	case 8: // #RRGGBBAA
		if err := parse(&r, hex[0:2]); err != nil {
			return color.RGBA{}, fmt.Errorf("jdenticon: engine: hex color parsing failed: failed to parse red component: %w", err)
		}
		if err := parse(&g, hex[2:4]); err != nil {
			return color.RGBA{}, fmt.Errorf("jdenticon: engine: hex color parsing failed: failed to parse green component: %w", err)
		}
		if err := parse(&b, hex[4:6]); err != nil {
			return color.RGBA{}, fmt.Errorf("jdenticon: engine: hex color parsing failed: failed to parse blue component: %w", err)
		}
		if err := parse(&a, hex[6:8]); err != nil {
			return color.RGBA{}, fmt.Errorf("jdenticon: engine: hex color parsing failed: failed to parse alpha component: %w", err)
		}

	default:
		// This case should be unreachable due to the regex validation above.
		// Return an error instead of panicking to ensure library never panics.
		return color.RGBA{}, fmt.Errorf("jdenticon: engine: hex color parsing failed: unsupported color format with length %d", len(hex))
	}

	return color.RGBA{R: r, G: g, B: b, A: a}, nil
}

// ValidateHexColor validates that a color string is a valid hex color format.
// Returns nil if valid, error if invalid.
func ValidateHexColor(hexStr string) error {
	if !getHexColorRegex().MatchString(hexStr) {
		return fmt.Errorf("jdenticon: engine: hex color validation failed: color must be a hex color like #fff, #ffffff, or #ffffff80")
	}
	return nil
}

// ParseHexColorToEngine parses a hex color string and returns an engine.Color.
// This is a convenience function for converting hex colors to the engine's internal Color type.
func ParseHexColorToEngine(hexStr string) (Color, error) {
	rgba, err := ParseHexColorToRGBA(hexStr)
	if err != nil {
		return Color{}, err
	}
	return NewColorRGBA(rgba.R, rgba.G, rgba.B, rgba.A), nil
}

// ParseHexColorForRenderer parses a hex color for use in renderers.
// Returns color.RGBA with the specified opacity applied.
// This function provides compatibility with the fast PNG renderer's parseColor function.
func ParseHexColorForRenderer(hexStr string, opacity float64) (color.RGBA, error) {
	rgba, err := ParseHexColorToRGBA(hexStr)
	if err != nil {
		return color.RGBA{}, err
	}

	// Apply opacity to the alpha channel
	rgba.A = uint8(float64(rgba.A) * opacity)
	return rgba, nil
}

// hexToByte converts a 2-character hex string to a byte value.
// This is a helper function used by ParseHexColor.
func hexToByte(hex string) (uint8, error) {
	if len(hex) != 2 {
		return 0, fmt.Errorf("jdenticon: engine: hex byte parsing failed: invalid hex string length: expected 2 characters, got %d", len(hex))
	}

	n, err := strconv.ParseUint(hex, 16, 8)
	if err != nil {
		return 0, fmt.Errorf("jdenticon: engine: hex byte parsing failed: invalid hex value '%s': %w", hex, err)
	}
	return uint8(n), nil
}
