package postman

import (
	"fmt"
	"regexp"
	"strings"
)

var subRe = regexp.MustCompile(`\{\{[^{}]+\}\}`)

type VariableInfo struct {
	value    string
	Metadata Metadata
}

type Substitution struct {
	variables map[string][]VariableInfo
}

func NewSubstitution() *Substitution {
	return &Substitution{
		variables: make(map[string][]VariableInfo),
	}
}

func (sub *Substitution) add(metadata Metadata, key string, value string) {
	sub.variables[key] = append(sub.variables[key], VariableInfo{
		value:    value,
		Metadata: metadata,
	})
}

func (s *Source) keywordCombinations(str string) string {
	data := ""
	for keyword := range s.keywords {
		data += fmt.Sprintf("%s:%s\n", keyword, str)
	}

	return data
}

func (s *Source) formatAndInjectKeywords(data []string) string {
	var ret []string
	for _, d := range data {
		ret = append(ret, s.keywordCombinations(d))
	}
	return strings.Join(ret, "")
}

func (s *Source) buildSubstitueSet(metadata Metadata, data string) []string {
	var ret []string
	combos := make(map[string]struct{})

	s.buildSubstitution(data, metadata, &combos)

	for combo := range combos {
		ret = append(ret, combo)
	}

	if len(ret) == 0 {
		return []string{data}
	}
	return ret
}

func (s *Source) buildSubstitution(data string, metadata Metadata, combos *map[string]struct{}) {
	matches := removeDuplicateStr(subRe.FindAllString(data, -1))
	for _, match := range matches {
		for _, slice := range s.sub.variables[strings.Trim(match, "{}")] {
			if slice.Metadata.CollectionInfo.PostmanID != "" && slice.Metadata.CollectionInfo.PostmanID != metadata.CollectionInfo.PostmanID {
				continue
			}

			// to ensure we don't infinitely recurse, we will trim all `{{}}` from the values before replacement.
			// this is to prevent the case where a variable is replaced with a value that contains the same variable causing
			// an infinite loop
			removedBrackets := strings.ReplaceAll(strings.ReplaceAll(slice.value, "{{", ""), "}}", "")

			d := strings.ReplaceAll(data, match, removedBrackets)
			s.buildSubstitution(d, metadata, combos)
		}
	}

	if len(matches) == 0 {
		// add to combos
		(*combos)[data] = struct{}{}
	}
}

func removeDuplicateStr(strSlice []string) []string {
	allKeys := make(map[string]bool)
	list := []string{}
	for _, item := range strSlice {
		if _, value := allKeys[item]; !value {
			allKeys[item] = true
			list = append(list, item)
		}
	}
	return list
}
