package templating

import (
	"sort"
	"strings"
)

const (
	// DefaultSeparator is the default separation character to use when separating template parts.
	DefaultSeparator = "."
)

// Engine uses a Matcher to retrieve the appropriate template and applies the template
// to the input string
type Engine struct {
	joiner  string
	matcher *matcher
}

// Apply extracts the template fields from the given line and returns the measurement
// name, tags and field name
//
//nolint:revive //function-result-limit conditionally 4 return results allowed
func (e *Engine) Apply(line string) (measurementName string, tags map[string]string, field string, err error) {
	return e.matcher.match(line).Apply(line, e.joiner)
}

// NewEngine creates a new templating engine
func NewEngine(joiner string, defaultTemplate *Template, templates []string) (*Engine, error) {
	engine := Engine{
		joiner:  joiner,
		matcher: newMatcher(defaultTemplate),
	}
	templateSpecs := parseTemplateSpecs(templates)

	for _, templateSpec := range templateSpecs {
		if err := engine.matcher.addSpec(templateSpec); err != nil {
			return nil, err
		}
	}

	return &engine, nil
}

func parseTemplateSpecs(templates []string) templateSpecs {
	tmplts := templateSpecs{}
	for _, pattern := range templates {
		tmplt := templateSpec{
			separator: DefaultSeparator,
		}

		// Format is [separator] [filter] <template> [tag1=value1,tag2=value2]
		parts := strings.Fields(pattern)
		partsLength := len(parts)
		if partsLength < 1 {
			// ignore
			continue
		}
		if partsLength == 1 {
			tmplt.template = pattern
		} else if partsLength == 4 {
			tmplt.separator = parts[0]
			tmplt.filter = parts[1]
			tmplt.template = parts[2]
			tmplt.tagstring = parts[3]
		} else {
			hasTagstring := strings.Contains(parts[partsLength-1], "=")
			if hasTagstring {
				tmplt.tagstring = parts[partsLength-1]
				tmplt.template = parts[partsLength-2]
				if partsLength == 3 {
					tmplt.filter = parts[0]
				}
			} else {
				tmplt.template = parts[partsLength-1]
				if partsLength == 2 {
					tmplt.filter = parts[0]
				} else { // length == 3
					tmplt.separator = parts[0]
					tmplt.filter = parts[1]
				}
			}
		}
		tmplts = append(tmplts, tmplt)
	}
	sort.Sort(tmplts)
	return tmplts
}
