package executor

import (
	"fmt"
	"image"
	"image/color"
	"image/draw"
	"image/png"
	"math"
	"os"
	"regexp"
	"strconv"
	"strings"

	"golang.org/x/image/font"
	"golang.org/x/image/font/gofont/gobold"
	"golang.org/x/image/font/opentype"
	"golang.org/x/image/math/fixed"
)

var hslCSSRe = regexp.MustCompile(`hsl\(\s*([\d.]+)[,\s]+([\d.]+)%[,\s]+([\d.]+)%\s*\)`)

// parseCSSColor parses the color formats theme.json palettes carry —
// hsl(H S% L%) and #rrggbb — returning fallback when unparseable.
func parseCSSColor(s string, fallback color.Color) color.Color {
	s = strings.TrimSpace(s)
	if m := hslCSSRe.FindStringSubmatch(s); m != nil {
		h, _ := strconv.ParseFloat(m[1], 64)
		sat, _ := strconv.ParseFloat(m[2], 64)
		l, _ := strconv.ParseFloat(m[3], 64)
		return hslToRGBA(h, sat/100, l/100)
	}
	if strings.HasPrefix(s, "#") && len(s) == 7 {
		var r, g, b uint8
		if _, err := fmt.Sscanf(s, "#%02x%02x%02x", &r, &g, &b); err == nil {
			return color.RGBA{R: r, G: g, B: b, A: 255}
		}
	}
	return fallback
}

func hslToRGBA(h, s, l float64) color.RGBA {
	c := (1 - math.Abs(2*l-1)) * s
	x := c * (1 - math.Abs(math.Mod(h/60, 2)-1))
	m := l - c/2
	var r, g, b float64
	switch {
	case h < 60:
		r, g, b = c, x, 0
	case h < 120:
		r, g, b = x, c, 0
	case h < 180:
		r, g, b = 0, c, x
	case h < 240:
		r, g, b = 0, x, c
	case h < 300:
		r, g, b = x, 0, c
	default:
		r, g, b = c, 0, x
	}
	return color.RGBA{R: uint8((r + m) * 255), G: uint8((g + m) * 255), B: uint8((b + m) * 255), A: 255}
}

func fillRect(img *image.RGBA, r image.Rectangle, c color.Color) {
	draw.Draw(img, r, image.NewUniform(c), image.Point{}, draw.Src)
}

// GenerateThemeScreenshot writes a deterministic 1200x900 branding-card PNG
// (the wp.org screenshot format) built from the theme's palette: base
// background, accent bands, theme name centered in the contrast color.
func GenerateThemeScreenshot(path, themeName string, palette map[string]string) error {
	const w, h = 1200, 900
	base := parseCSSColor(palette["base"], color.RGBA{R: 250, G: 250, B: 250, A: 255})
	contrast := parseCSSColor(palette["contrast"], color.RGBA{R: 20, G: 20, B: 24, A: 255})
	primary := parseCSSColor(palette["primary"], color.RGBA{R: 80, G: 70, B: 200, A: 255})
	secondary := parseCSSColor(palette["secondary"], primary)

	img := image.NewRGBA(image.Rect(0, 0, w, h))
	fillRect(img, img.Bounds(), base)
	fillRect(img, image.Rect(0, h-110, w, h), primary)
	fillRect(img, image.Rect(0, h-126, w, h-110), secondary)
	fillRect(img, image.Rect(80, 300, 240, 312), primary) // small accent mark above the name

	ft, err := opentype.Parse(gobold.TTF)
	if err != nil {
		return err
	}
	face, err := opentype.NewFace(ft, &opentype.FaceOptions{Size: 84, DPI: 72, Hinting: font.HintingFull})
	if err != nil {
		return err
	}
	defer func() { _ = face.Close() }()

	name := strings.TrimSpace(themeName)
	if name == "" {
		name = "Block Theme"
	}
	if r := []rune(name); len(r) > 24 {
		name = string(r[:23]) + "…"
	}
	d := &font.Drawer{Dst: img, Src: image.NewUniform(contrast), Face: face}
	width := d.MeasureString(name)
	x := (w - width.Ceil()) / 2
	if x < 40 {
		x = 40
	}
	d.Dot = fixed.P(x, h/2+30)
	d.DrawString(name)

	f, err := os.Create(path)
	if err != nil {
		return err
	}
	defer func() { _ = f.Close() }()
	return png.Encode(f, img)
}
