package jdenticon

import (
	"fmt"

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

// Icon represents a generated identicon that can be rendered as SVG or PNG.
// It wraps the internal engine.Icon to provide a clean public API.
type Icon struct {
	engineIcon *engine.Icon
}

// newIcon creates a new public Icon from an internal engine.Icon.
func newIcon(engineIcon *engine.Icon) *Icon {
	return &Icon{
		engineIcon: engineIcon,
	}
}

// ToSVG renders the icon as an SVG string.
//
// Returns the SVG markup as a string, or an error if rendering fails.
func (i *Icon) ToSVG() (string, error) {
	if i.engineIcon == nil {
		return "", ErrInvalidIcon("icon data is nil")
	}

	size := int(i.engineIcon.Size)
	svgRenderer := renderer.NewSVGRenderer(size)

	if err := i.renderToRenderer(svgRenderer); err != nil {
		return "", NewErrRenderFailed("SVG", err)
	}

	return svgRenderer.ToSVG(), nil
}

// ToPNG renders the icon as PNG bytes.
//
// Returns the PNG data as a byte slice, or an error if rendering fails.
func (i *Icon) ToPNG() ([]byte, error) {
	if i.engineIcon == nil {
		return nil, ErrInvalidIcon("icon data is nil")
	}

	size := int(i.engineIcon.Size)
	pngRenderer := renderer.NewPNGRenderer(size)

	if err := i.renderToRenderer(pngRenderer); err != nil {
		return nil, NewErrRenderFailed("PNG", err)
	}

	png, err := pngRenderer.ToPNG()
	if err != nil {
		return nil, NewErrRenderFailed("PNG", err)
	}

	return png, nil
}

// renderToRenderer renders the icon to any renderer that implements the Renderer interface.
func (i *Icon) renderToRenderer(r renderer.Renderer) error {
	// Set background if specified
	if i.engineIcon.Config.BackColor != nil {
		color := i.engineIcon.Config.BackColor
		colorStr := color.String()
		opacity := float64(color.A) / 255.0
		r.SetBackground(colorStr, opacity)
	}

	// Render each shape group
	for _, shapeGroup := range i.engineIcon.Shapes {
		colorStr := shapeGroup.Color.String()

		r.BeginShape(colorStr)

		for _, shape := range shapeGroup.Shapes {
			if err := i.renderShape(r, shape); err != nil {
				return fmt.Errorf("shape rendering failed: %w", err)
			}
		}

		r.EndShape()
	}

	return nil
}

// renderShape renders a single shape to the renderer.
func (i *Icon) renderShape(r renderer.Renderer, shape engine.Shape) error {
	switch shape.Type {
	case "polygon":
		if len(shape.Points) < 3 {
			return fmt.Errorf("polygon must have at least 3 points, got %d", len(shape.Points))
		}
		r.AddPolygon(shape.Points)

	case "circle":
		topLeft := engine.Point{X: shape.CircleX, Y: shape.CircleY}
		r.AddCircle(topLeft, shape.CircleSize, shape.Invert)

	default:
		return fmt.Errorf("unsupported shape type: %s", shape.Type)
	}

	return nil
}
