package project

import (
	"fmt"

	"code.justin.tv/cplat/twitchling/internal/syntax"
	"code.justin.tv/cplat/twitchling/internal/syntax/ast"
)

type Message struct {
	ID              string            `json:"id" toml:"id"`
	Variants        map[string]string `json:"variants" toml:"variants"`
	TranslationNote string            `json:"translationNote" toml:"translation-note,omitempty"`
}

func (m *Message) Placeholders() ([]string, error) {
	placeholderSet := map[string]struct{}{}

	// Iterate each variant, building up a list of placeholders across all variants and ignoring duplicates.
	for _, input := range m.Variants {
		result, err := syntax.Parse(input)
		if err != nil {
			return nil, fmt.Errorf("failed to create message: %v", err)
		}

		for _, placeholder := range getPlaceholders(result) {
			placeholderSet[placeholder] = struct{}{}
		}
	}

	var placeholders []string
	for placeholder := range placeholderSet {
		placeholders = append(placeholders, placeholder)
	}

	return placeholders, nil
}

func getPlaceholders(input syntax.ParseResult) []string {
	return visitNodes(input.Root())
}

func visitNodes(nodes []ast.Node) []string {
	var placeholders []string

	for _, node := range nodes {
		placeholders = append(placeholders, visitNode(node)...)
	}

	return placeholders
}

func visitNode(node ast.Node) []string {
	switch n := node.(type) {
	case *ast.Element:
		return visitNodes(n.Nodes())

	case *ast.Argument:
		return []string{n.Argument()}

	case *ast.NumberFunction:
		return []string{n.Argument()}

	case *ast.PluralFunction:
		var placeholders []string
		for _, inner := range n.Variants() {
			placeholders = append(placeholders, visitNodes(inner)...)
		}

		return append(placeholders, n.Argument())

	default:
		return nil
	}
}
