// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: MIT

package metrics

import (
	"sort"

	"github.com/aws/amazon-cloudwatch-agent/translator"
	"github.com/aws/amazon-cloudwatch-agent/translator/jsonconfig/mergeJsonRule"
	"github.com/aws/amazon-cloudwatch-agent/translator/jsonconfig/mergeJsonUtil"
	parent "github.com/aws/amazon-cloudwatch-agent/translator/translate"
	"github.com/aws/amazon-cloudwatch-agent/translator/translate/metrics/config"
	"github.com/aws/amazon-cloudwatch-agent/translator/translate/util"
)

type Rule translator.Rule

var (
	ChildRule          = map[string]Rule{}
	GlobalMetricConfig = Metrics{}

	serviceName           ServiceName
	deploymentEnvironment DeploymentEnvironment
)

const (
	SectionKey = "metrics"
	OutputsKey = "outputs"
)

func GetCurPath() string {
	curPath := parent.GetCurPath() + SectionKey + "/"
	return curPath
}

func RegisterRule(fieldName string, r Rule) {
	ChildRule[fieldName] = r
}

type Metrics struct {
	ServiceName           string
	DeploymentEnvironment string
}

func (m *Metrics) ApplyRule(input interface{}) (returnKey string, returnVal interface{}) {
	im := input.(map[string]interface{})
	result := map[string]interface{}{}
	outputPlugInfo := map[string]interface{}{}

	//Apply Environment and ServiceName rules
	serviceName.ApplyRule(im[SectionKey])
	deploymentEnvironment.ApplyRule(im[SectionKey])

	//Check if this plugin exist in the input instance
	//If not, not process
	if _, ok := im[SectionKey]; !ok {
		returnKey = ""
		returnVal = ""
	} else {
		//If yes, process it
		for _, rule := range ChildRule {
			key, val := rule.ApplyRule(im[SectionKey])
			//If key == "", then no instance of this class in input
			if key != "" {
				if key == OutputsKey {
					outputPlugInfo = translator.MergeTwoUniqueMaps(outputPlugInfo, val.(map[string]interface{}))
				} else if config.ContainsKey(key) {
					addCloudWatchOutputConfig(key, val, outputPlugInfo)
				} else {
					result[key] = val
				}
			}
		}

		cloudwatchInfo := map[string]interface{}{}
		cloudwatchInfo["cloudwatch"] = []interface{}{map[string]interface{}{}}
		result["outputs"] = cloudwatchInfo
		returnKey = SectionKey
		returnVal = result
	}
	return
}

func addCloudWatchOutputConfig(key string, val interface{}, outputPlugInfo map[string]interface{}) {
	if val1, isSlice := val.([]interface{}); isSlice && len(val1) > 0 {
		outputPlugInfo[key] = val
	} else if val2, isMap := val.(map[string][]string); isMap && len(val2) > 0 {
		sortItems(val2)
		outputPlugInfo[key] = val2
	}

}

var MergeRuleMap = map[string]mergeJsonRule.MergeRule{}

func (m *Metrics) Merge(source map[string]interface{}, result map[string]interface{}) {
	mergeJsonUtil.MergeMap(source, result, SectionKey, MergeRuleMap, GetCurPath())
}

// Sort items in map alphabetically to temporarily avoid unstable tests before introduce Toml-to-Toml comparison.
func sortItems(vals map[string][]string) {
	for _, val := range vals {
		sort.Strings(val)
	}
}

func init() {
	m := new(Metrics)
	parent.RegisterLinuxRule(SectionKey, m)
	parent.RegisterDarwinRule(SectionKey, m)
	parent.RegisterWindowsRule(SectionKey, m)
	ChildRule["globalcredentials"] = util.GetCredsRule(OutputsKey)
	ChildRule["region"] = util.GetRegionRule(OutputsKey)

	mergeJsonUtil.MergeRuleMap[SectionKey] = m
}
