package cli

import "fmt"

// Region represents one of the 5 HLCRF regions.
type Region rune

const (
	// RegionHeader is the top region of the layout.
	RegionHeader Region = 'H'
	// RegionLeft is the left sidebar region.
	RegionLeft Region = 'L'
	// RegionContent is the main content region.
	RegionContent Region = 'C'
	// RegionRight is the right sidebar region.
	RegionRight Region = 'R'
	// RegionFooter is the bottom region of the layout.
	RegionFooter Region = 'F'
)

// Composite represents an HLCRF layout node.
type Composite struct {
	variant string
	path    string
	regions map[Region]*Slot
	parent  *Composite
}

// Slot holds content for a region.
type Slot struct {
	region Region
	path   string
	blocks []Renderable
	child  *Composite
}

// Renderable is anything that can be rendered to terminal.
type Renderable interface {
	Render() string
}

// StringBlock is a simple string that implements Renderable.
type StringBlock string

// Render returns the string content.
func (s StringBlock) Render() string { return string(s) }

// Layout creates a new layout from a variant string.
func Layout(variant string) *Composite {
	c, err := ParseVariant(variant)
	if err != nil {
		return &Composite{variant: variant, regions: make(map[Region]*Slot)}
	}
	return c
}

// ParseVariant parses a variant string like "H[LC]C[HCF]F".
func ParseVariant(variant string) (*Composite, error) {
	c := &Composite{
		variant: variant,
		path:    "",
		regions: make(map[Region]*Slot),
	}

	i := 0
	for i < len(variant) {
		r := Region(variant[i])
		if !isValidRegion(r) {
			return nil, fmt.Errorf("invalid region: %c", r)
		}

		slot := &Slot{region: r, path: string(r)}
		c.regions[r] = slot
		i++

		if i < len(variant) && variant[i] == '[' {
			end := findMatchingBracket(variant, i)
			if end == -1 {
				return nil, fmt.Errorf("unmatched bracket at %d", i)
			}
			nested, err := ParseVariant(variant[i+1 : end])
			if err != nil {
				return nil, err
			}
			nested.path = string(r) + "-"
			nested.parent = c
			slot.child = nested
			i = end + 1
		}
	}
	return c, nil
}

func isValidRegion(r Region) bool {
	return r == 'H' || r == 'L' || r == 'C' || r == 'R' || r == 'F'
}

func findMatchingBracket(s string, start int) int {
	depth := 0
	for i := start; i < len(s); i++ {
		switch s[i] {
		case '[':
			depth++
		case ']':
			depth--
			if depth == 0 {
				return i
			}
		}
	}
	return -1
}

// H adds content to Header region.
func (c *Composite) H(items ...any) *Composite { c.addToRegion(RegionHeader, items...); return c }

// L adds content to Left region.
func (c *Composite) L(items ...any) *Composite { c.addToRegion(RegionLeft, items...); return c }

// C adds content to Content region.
func (c *Composite) C(items ...any) *Composite { c.addToRegion(RegionContent, items...); return c }

// R adds content to Right region.
func (c *Composite) R(items ...any) *Composite { c.addToRegion(RegionRight, items...); return c }

// F adds content to Footer region.
func (c *Composite) F(items ...any) *Composite { c.addToRegion(RegionFooter, items...); return c }

func (c *Composite) addToRegion(r Region, items ...any) {
	slot, ok := c.regions[r]
	if !ok {
		return
	}
	for _, item := range items {
		slot.blocks = append(slot.blocks, toRenderable(item))
	}
}

func toRenderable(item any) Renderable {
	switch v := item.(type) {
	case Renderable:
		return v
	case string:
		return StringBlock(v)
	default:
		return StringBlock(fmt.Sprint(v))
	}
}
