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

package awsentity

import (
	"context"
	"os"
	"strings"

	"github.com/go-playground/validator/v10"
	"go.opentelemetry.io/collector/client"
	"go.opentelemetry.io/collector/pdata/pcommon"
	"go.opentelemetry.io/collector/pdata/pmetric"
	semconv "go.opentelemetry.io/collector/semconv/v1.22.0"
	"go.uber.org/zap"

	"github.com/aws/amazon-cloudwatch-agent/extension/entitystore"
	"github.com/aws/amazon-cloudwatch-agent/extension/k8smetadata"
	"github.com/aws/amazon-cloudwatch-agent/internal/clientutil"
	"github.com/aws/amazon-cloudwatch-agent/internal/k8sCommon/k8sclient"
	"github.com/aws/amazon-cloudwatch-agent/plugins/processors/awsentity/entityattributes"
	"github.com/aws/amazon-cloudwatch-agent/plugins/processors/awsentity/internal/entitytransformer"
	"github.com/aws/amazon-cloudwatch-agent/plugins/processors/awsentity/internal/k8sattributescraper"
	"github.com/aws/amazon-cloudwatch-agent/plugins/processors/ec2tagger"
	"github.com/aws/amazon-cloudwatch-agent/translator/config"
)

const (
	attributeAwsLogGroupNames              = "aws.log.group.names"
	attributeDeploymentEnvironment         = "deployment.environment"
	attributeServiceName                   = "service.name"
	attributeService                       = "Service"
	attributeEC2TagAwsAutoscalingGroupName = "ec2.tag.aws:autoscaling:groupName"
	EMPTY                                  = ""
	unknownService                         = "unknown_service"
)

type scraper interface {
	Scrape(rm pcommon.Resource, podMeta k8sclient.PodMetadata)
	Reset()
}

type EC2ServiceAttributes struct {
	InstanceId        string `validate:"required"`
	AutoScalingGroup  string `validate:"omitempty"`
	ServiceNameSource string `validate:"omitempty"`
}

type K8sServiceAttributes struct {
	Cluster           string `validate:"required"`
	Namespace         string `validate:"required"`
	Workload          string `validate:"required"`
	Node              string `validate:"required"`
	InstanceId        string `validate:"omitempty"`
	ServiceNameSource string `validate:"omitempty"`
}

// use a single instance of Validate, it caches struct info
var validate = validator.New(validator.WithRequiredStructEnabled())

// exposed as a variable for unit testing
var addToEntityStore = func(logGroupName entitystore.LogGroupName, serviceName string, environmentName string) {
	es := entitystore.GetEntityStore()
	if es == nil {
		return
	}
	es.AddServiceAttrEntryForLogGroup(logGroupName, serviceName, environmentName)
}

var addPodToServiceEnvironmentMap = func(podName string, serviceName string, environmentName string, serviceNameSource string) {
	es := entitystore.GetEntityStore()
	if es == nil {
		return
	}
	es.AddPodServiceEnvironmentMapping(podName, serviceName, environmentName, serviceNameSource)
}

var setAutoScalingGroup = func(asg string) {
	es := entitystore.GetEntityStore()
	if es == nil {
		return
	}
	es.SetAutoScalingGroup(asg)
}

var getEC2InfoFromEntityStore = func() entitystore.EC2Info {
	es := entitystore.GetEntityStore()
	if es == nil {
		return entitystore.EC2Info{}
	}

	return es.EC2Info()
}

var getAutoScalingGroupFromEntityStore = func() string {
	// Get the following metric attributes from the EntityStore: EC2.AutoScalingGroup
	es := entitystore.GetEntityStore()
	if es == nil {
		return ""
	}
	return es.GetAutoScalingGroup()
}

var getServiceNameSource = func() (string, string) {
	es := entitystore.GetEntityStore()
	if es == nil {
		return EMPTY, EMPTY
	}
	return es.GetMetricServiceNameAndSource()
}

var getPodMeta = func(ctx context.Context) k8sclient.PodMetadata {
	podMeta := k8sclient.PodMetadata{}
	k8sMetadata := k8smetadata.GetKubernetesMetadata()

	if k8sMetadata != nil {
		// Get the pod IP from the context
		podIP := clientutil.Address(client.FromContext(ctx))
		podMeta = k8sMetadata.GetPodMetadataFromPodIP(podIP)
	}

	return podMeta
}

// awsEntityProcessor looks for metrics that have the aws.log.group.names and either the service.name or
// deployment.environment resource attributes set, then adds the association between the log group(s) and the
// service/environment names to the entitystore extension.
type awsEntityProcessor struct {
	config            *Config
	k8sscraper        scraper
	entityTransformer *entitytransformer.EntityTransformer
	logger            *zap.Logger
}

func newAwsEntityProcessor(config *Config, logger *zap.Logger) *awsEntityProcessor {
	return &awsEntityProcessor{
		config:            config,
		k8sscraper:        k8sattributescraper.NewK8sAttributeScraper(config.ClusterName),
		entityTransformer: entitytransformer.NewEntityTransformer(config.TransformEntity, logger),
		logger:            logger,
	}
}

func (p *awsEntityProcessor) processMetrics(ctx context.Context, md pmetric.Metrics) (pmetric.Metrics, error) {
	// Get the following metric attributes from the EntityStore: PlatformType, EC2.InstanceId, EC2.AutoScalingGroup

	rm := md.ResourceMetrics()
	for i := 0; i < rm.Len(); i++ {
		var logGroupNames, serviceName, environmentName string
		var entityServiceNameSource, entityPlatformType string
		var ec2Info entitystore.EC2Info
		resourceAttrs := rm.At(i).Resource().Attributes()
		switch p.config.EntityType {
		case entityattributes.Resource:
			if p.config.KubernetesMode != "" {
				switch p.config.KubernetesMode {
				case config.ModeEKS:
					resourceAttrs.PutStr(entityattributes.AttributeEntityPlatformType, entityattributes.AttributeEntityEKSPlatform)
				default:
					resourceAttrs.PutStr(entityattributes.AttributeEntityPlatformType, entityattributes.AttributeEntityK8sPlatform)
				}
			} else if p.config.Platform == config.ModeEC2 {
				// ec2tagger processor may have picked up the ASG name from an ec2:DescribeTags call
				if getAutoScalingGroupFromEntityStore() == EMPTY && p.config.ScrapeDatapointAttribute {
					if autoScalingGroup := p.scrapeResourceEntityAttribute(rm.At(i).ScopeMetrics()); autoScalingGroup != EMPTY {
						setAutoScalingGroup(autoScalingGroup)
					}
				}
				ec2Info = getEC2InfoFromEntityStore()
				if ec2Info.GetInstanceID() != EMPTY {
					resourceAttrs.PutStr(entityattributes.AttributeEntityType, entityattributes.AttributeEntityAWSResource)
					resourceAttrs.PutStr(entityattributes.AttributeEntityResourceType, entityattributes.AttributeEntityEC2InstanceResource)
					resourceAttrs.PutStr(entityattributes.AttributeEntityIdentifier, ec2Info.GetInstanceID())
				}
				AddAttributeIfNonEmpty(resourceAttrs, entityattributes.AttributeEntityAwsAccountId, ec2Info.GetAccountID())
			}
		case entityattributes.Service:
			if logGroupNamesAttr, ok := resourceAttrs.Get(attributeAwsLogGroupNames); ok {
				logGroupNames = logGroupNamesAttr.Str()
			}
			if serviceNameAttr, ok := resourceAttrs.Get(attributeServiceName); ok {
				serviceName = serviceNameAttr.Str()
			}
			if environmentNameAttr, ok := resourceAttrs.Get(attributeDeploymentEnvironment); ok {
				environmentName = environmentNameAttr.Str()
			}
			if serviceNameSource, sourceExists := resourceAttrs.Get(entityattributes.AttributeEntityServiceNameSource); sourceExists {
				entityServiceNameSource = serviceNameSource.Str()
			}
			// resourcedetection processor may have picked up the ASG name from an ec2:DescribeTags call
			if autoScalingGroupNameAttr, ok := resourceAttrs.Get(attributeEC2TagAwsAutoscalingGroupName); ok {
				setAutoScalingGroup(autoScalingGroupNameAttr.Str())
			}

			entityServiceName := getServiceAttributes(resourceAttrs)
			entityEnvironmentName := environmentName
			if (entityServiceName == EMPTY || entityEnvironmentName == EMPTY) && p.config.ScrapeDatapointAttribute {
				entityServiceName, entityEnvironmentName, entityServiceNameSource = p.scrapeServiceAttribute(rm.At(i).ScopeMetrics())
				// If the entityServiceNameSource is empty here, that means it was not configured via instrumentation
				// If entityServiceName is a datapoint attribute, that means the service name is coming from the UserConfiguration source
				if entityServiceNameSource == entityattributes.AttributeServiceNameSourceUserConfig && entityServiceName != EMPTY {
					entityServiceNameSource = entityattributes.AttributeServiceNameSourceUserConfig
				}
			}
			if p.config.KubernetesMode != "" {
				p.k8sscraper.Scrape(rm.At(i).Resource(), getPodMeta(ctx))
				if p.config.Platform == config.ModeEC2 {
					ec2Info = getEC2InfoFromEntityStore()
				}

				if p.config.KubernetesMode == config.ModeEKS {
					entityPlatformType = entityattributes.AttributeEntityEKSPlatform
				} else {
					entityPlatformType = entityattributes.AttributeEntityK8sPlatform
				}

				podInfo, ok := p.k8sscraper.(*k8sattributescraper.K8sAttributeScraper)
				// Perform fallback mechanism for service name if it is empty
				// or has prefix unknown_service ( unknown_service will be set by OTEL SDK if the service name is empty on application pod)
				// https://opentelemetry.io/docs/specs/semconv/attributes-registry/service/
				if shouldUseFallbackServiceName(entityServiceName) && ok && podInfo != nil && podInfo.Workload != EMPTY {
					entityServiceName = podInfo.Workload
					entityServiceNameSource = entitystore.ServiceNameSourceK8sWorkload
				}
				// Set the service name source to Instrumentation if the operator doesn't set it
				if entityServiceName != EMPTY && entityServiceNameSource == EMPTY && getTelemetrySDKEnabledAttribute(resourceAttrs) {
					entityServiceNameSource = entitystore.ServiceNameSourceInstrumentation
				}
				// Perform fallback mechanism for environment if it is empty
				if entityEnvironmentName == EMPTY && ok && podInfo.Cluster != EMPTY && podInfo.Namespace != EMPTY {
					if p.config.KubernetesMode == config.ModeEKS {
						entityEnvironmentName = "eks:" + p.config.ClusterName + "/" + podInfo.Namespace
					} else if p.config.KubernetesMode == config.ModeK8sEC2 || p.config.KubernetesMode == config.ModeK8sOnPrem {
						entityEnvironmentName = "k8s:" + p.config.ClusterName + "/" + podInfo.Namespace
					}
				}

				// Add service information for a pod to the pod association map
				// so that agent can host this information in a server
				fullPodName := scrapeK8sPodName(resourceAttrs)
				if fullPodName != EMPTY && entityServiceName != EMPTY && entityServiceNameSource != EMPTY {
					addPodToServiceEnvironmentMap(fullPodName, entityServiceName, entityEnvironmentName, entityServiceNameSource)
				} else if fullPodName != EMPTY && entityServiceName != EMPTY && entityServiceNameSource == EMPTY {
					addPodToServiceEnvironmentMap(fullPodName, entityServiceName, entityEnvironmentName, entitystore.ServiceNameSourceUnknown)
				}
				eksAttributes := K8sServiceAttributes{
					Cluster:           podInfo.Cluster,
					Namespace:         podInfo.Namespace,
					Workload:          podInfo.Workload,
					Node:              podInfo.Node,
					InstanceId:        ec2Info.GetInstanceID(),
					ServiceNameSource: entityServiceNameSource,
				}
				AddAttributeIfNonEmpty(resourceAttrs, entityattributes.AttributeEntityType, entityattributes.Service)
				AddAttributeIfNonEmpty(resourceAttrs, entityattributes.AttributeEntityServiceName, entityServiceName)
				AddAttributeIfNonEmpty(resourceAttrs, entityattributes.AttributeEntityDeploymentEnvironment, entityEnvironmentName)

				if err := validate.Struct(eksAttributes); err == nil {
					resourceAttrs.PutStr(entityattributes.AttributeEntityPlatformType, entityPlatformType)
					resourceAttrs.PutStr(entityattributes.AttributeEntityCluster, eksAttributes.Cluster)
					resourceAttrs.PutStr(entityattributes.AttributeEntityNamespace, eksAttributes.Namespace)
					resourceAttrs.PutStr(entityattributes.AttributeEntityWorkload, eksAttributes.Workload)
					resourceAttrs.PutStr(entityattributes.AttributeEntityNode, eksAttributes.Node)
					//Add Instance id attribute only if the application node is same as agent node
					if eksAttributes.Node == os.Getenv("K8S_NODE_NAME") {
						AddAttributeIfNonEmpty(resourceAttrs, entityattributes.AttributeEntityInstanceID, eksAttributes.InstanceId)
					}
					AddAttributeIfNonEmpty(resourceAttrs, entityattributes.AttributeEntityAwsAccountId, ec2Info.GetAccountID())
					AddAttributeIfNonEmpty(resourceAttrs, entityattributes.AttributeEntityServiceNameSource, entityServiceNameSource)
				}
				p.k8sscraper.Reset()
			} else if p.config.Platform == config.ModeEC2 {
				//If entityServiceNameSource is empty, it was not configured via the config. Get the source in descending priority
				//  1. Incoming telemetry attributes
				//  2. CWA config
				//  3. instance tags - The tags attached to the EC2 instance. Only scrape for tag with the following key: service, application, app
				//  4. IAM Role - The IAM role name retrieved through IMDS(Instance Metadata Service)
				if shouldUseFallbackServiceName(entityServiceName) {
					entityServiceName, entityServiceNameSource = getServiceNameSource()
				} else if entityServiceName != EMPTY && entityServiceNameSource == EMPTY {
					entityServiceNameSource = entitystore.ServiceNameSourceInstrumentation
				}

				entityPlatformType = entityattributes.AttributeEntityEC2Platform
				ec2Info = getEC2InfoFromEntityStore()

				if entityEnvironmentName == EMPTY {
					if getAutoScalingGroupFromEntityStore() != EMPTY {
						entityEnvironmentName = entityattributes.DeploymentEnvironmentFallbackPrefix + getAutoScalingGroupFromEntityStore()
					} else {
						entityEnvironmentName = entityattributes.DeploymentEnvironmentDefault
					}
				}

				AddAttributeIfNonEmpty(resourceAttrs, entityattributes.AttributeEntityType, entityattributes.Service)
				AddAttributeIfNonEmpty(resourceAttrs, entityattributes.AttributeEntityServiceName, entityServiceName)
				AddAttributeIfNonEmpty(resourceAttrs, entityattributes.AttributeEntityDeploymentEnvironment, entityEnvironmentName)
				AddAttributeIfNonEmpty(resourceAttrs, entityattributes.AttributeEntityAwsAccountId, ec2Info.GetAccountID())

				ec2Attributes := EC2ServiceAttributes{
					InstanceId:        ec2Info.GetInstanceID(),
					AutoScalingGroup:  getAutoScalingGroupFromEntityStore(),
					ServiceNameSource: entityServiceNameSource,
				}
				if err := validate.Struct(ec2Attributes); err == nil {
					resourceAttrs.PutStr(entityattributes.AttributeEntityPlatformType, entityPlatformType)
					AddAttributeIfNonEmpty(resourceAttrs, entityattributes.AttributeEntityInstanceID, ec2Attributes.InstanceId)
					AddAttributeIfNonEmpty(resourceAttrs, entityattributes.AttributeEntityAutoScalingGroup, ec2Attributes.AutoScalingGroup)
					AddAttributeIfNonEmpty(resourceAttrs, entityattributes.AttributeEntityServiceNameSource, ec2Attributes.ServiceNameSource)
					if ec2Attributes.ServiceNameSource != entitystore.ServiceNameSourceInstrumentation {
						// Instrumentation Service Name Source has highest priority
						// Therefore only apply when service name source is not
						// Instrumentation. Service instrumented with "unknown_service" name
						// will not be an issue since we have logics to modify it with propoer
						// service name and source
						p.entityTransformer.ApplyTransforms(resourceAttrs)
					}

				}
			}
			if logGroupNames == EMPTY || (serviceName == EMPTY && environmentName == EMPTY) {
				continue
			}

			logGroupNamesSlice := strings.Split(logGroupNames, "&")
			for _, logGroupName := range logGroupNamesSlice {
				if logGroupName == EMPTY {
					continue
				}
				addToEntityStore(entitystore.LogGroupName(logGroupName), serviceName, environmentName)
			}
		}

	}
	return md, nil
}

// scrapeServiceAttribute expands the datapoint attributes and search for
// service name and environment attributes. This is only used for components
// that only emit attributes on datapoint level. This code block contains a lot
// of repeated code because OTEL metrics type do not have a common interface.
func (p *awsEntityProcessor) scrapeServiceAttribute(scopeMetric pmetric.ScopeMetricsSlice) (string, string, string) {
	entityServiceName := EMPTY
	entityServiceNameSource := EMPTY
	entityEnvironmentName := EMPTY
	for j := 0; j < scopeMetric.Len(); j++ {
		metric := scopeMetric.At(j).Metrics()
		for k := 0; k < metric.Len(); k++ {
			if entityServiceName != EMPTY && entityEnvironmentName != EMPTY && entityServiceNameSource != EMPTY {
				return entityServiceName, entityEnvironmentName, entityServiceNameSource
			}
			m := metric.At(k)
			switch m.Type() {
			case pmetric.MetricTypeGauge:
				dps := m.Gauge().DataPoints()
				for l := 0; l < dps.Len(); l++ {
					dpService := getServiceAttributes(dps.At(l).Attributes())
					if dpService != EMPTY {
						entityServiceName = dpService
					}
					if dpServiceNameSource, ok := dps.At(l).Attributes().Get(entityattributes.AttributeServiceNameSource); ok {
						entityServiceNameSource = dpServiceNameSource.Str()
						dps.At(l).Attributes().Remove(semconv.AttributeServiceName)
						dps.At(l).Attributes().Remove(entityattributes.AttributeServiceNameSource)
					}
					if dpEnvironment, ok := dps.At(l).Attributes().Get(semconv.AttributeDeploymentEnvironment); ok {
						entityEnvironmentName = dpEnvironment.Str()
					}
					if _, ok := dps.At(l).Attributes().Get(entityattributes.AttributeDeploymentEnvironmentSource); ok {
						dps.At(l).Attributes().Remove(semconv.AttributeDeploymentEnvironment)
						dps.At(l).Attributes().Remove(entityattributes.AttributeDeploymentEnvironmentSource)
					}

				}
			case pmetric.MetricTypeSum:
				dps := m.Sum().DataPoints()
				for l := 0; l < dps.Len(); l++ {
					dpService := getServiceAttributes(dps.At(l).Attributes())
					if dpService != EMPTY {
						entityServiceName = dpService
					}
					if dpServiceNameSource, ok := dps.At(l).Attributes().Get(entityattributes.AttributeServiceNameSource); ok {
						entityServiceNameSource = dpServiceNameSource.Str()
						dps.At(l).Attributes().Remove(semconv.AttributeServiceName)
						dps.At(l).Attributes().Remove(entityattributes.AttributeServiceNameSource)
					}
					if dpEnvironment, ok := dps.At(l).Attributes().Get(semconv.AttributeDeploymentEnvironment); ok {
						entityEnvironmentName = dpEnvironment.Str()
					}
					if _, ok := dps.At(l).Attributes().Get(entityattributes.AttributeDeploymentEnvironmentSource); ok {
						dps.At(l).Attributes().Remove(semconv.AttributeDeploymentEnvironment)
						dps.At(l).Attributes().Remove(entityattributes.AttributeDeploymentEnvironmentSource)
					}
				}
			case pmetric.MetricTypeHistogram:
				dps := m.Histogram().DataPoints()
				for l := 0; l < dps.Len(); l++ {
					dpService := getServiceAttributes(dps.At(l).Attributes())
					if dpService != EMPTY {
						entityServiceName = dpService
					}
					if dpServiceNameSource, ok := dps.At(l).Attributes().Get(entityattributes.AttributeServiceNameSource); ok {
						entityServiceNameSource = dpServiceNameSource.Str()
						dps.At(l).Attributes().Remove(semconv.AttributeServiceName)
						dps.At(l).Attributes().Remove(entityattributes.AttributeServiceNameSource)
					}
					if dpEnvironment, ok := dps.At(l).Attributes().Get(semconv.AttributeDeploymentEnvironment); ok {
						entityEnvironmentName = dpEnvironment.Str()
					}
					if _, ok := dps.At(l).Attributes().Get(entityattributes.AttributeDeploymentEnvironmentSource); ok {
						dps.At(l).Attributes().Remove(semconv.AttributeDeploymentEnvironment)
						dps.At(l).Attributes().Remove(entityattributes.AttributeDeploymentEnvironmentSource)
					}
				}
			case pmetric.MetricTypeExponentialHistogram:
				dps := m.ExponentialHistogram().DataPoints()
				for l := 0; l < dps.Len(); l++ {
					dpService := getServiceAttributes(dps.At(l).Attributes())
					if dpService != EMPTY {
						entityServiceName = dpService
					}
					if dpServiceNameSource, ok := dps.At(l).Attributes().Get(entityattributes.AttributeServiceNameSource); ok {
						entityServiceNameSource = dpServiceNameSource.Str()
						dps.At(l).Attributes().Remove(semconv.AttributeServiceName)
						dps.At(l).Attributes().Remove(entityattributes.AttributeServiceNameSource)
					}
					if dpEnvironment, ok := dps.At(l).Attributes().Get(semconv.AttributeDeploymentEnvironment); ok {
						entityEnvironmentName = dpEnvironment.Str()
					}
					if _, ok := dps.At(l).Attributes().Get(entityattributes.AttributeDeploymentEnvironmentSource); ok {
						dps.At(l).Attributes().Remove(semconv.AttributeDeploymentEnvironment)
						dps.At(l).Attributes().Remove(entityattributes.AttributeDeploymentEnvironmentSource)
					}
				}
			case pmetric.MetricTypeSummary:
				dps := m.Sum().DataPoints()
				for l := 0; l < dps.Len(); l++ {
					dpService := getServiceAttributes(dps.At(l).Attributes())
					if dpService != EMPTY {
						entityServiceName = dpService
					}
					if dpServiceNameSource, ok := dps.At(l).Attributes().Get(entityattributes.AttributeServiceNameSource); ok {
						entityServiceNameSource = dpServiceNameSource.Str()
						dps.At(l).Attributes().Remove(semconv.AttributeServiceName)
						dps.At(l).Attributes().Remove(entityattributes.AttributeServiceNameSource)
					}
					if dpEnvironment, ok := dps.At(l).Attributes().Get(semconv.AttributeDeploymentEnvironment); ok {
						entityEnvironmentName = dpEnvironment.Str()
					}
					if _, ok := dps.At(l).Attributes().Get(entityattributes.AttributeDeploymentEnvironmentSource); ok {
						dps.At(l).Attributes().Remove(semconv.AttributeDeploymentEnvironment)
						dps.At(l).Attributes().Remove(entityattributes.AttributeDeploymentEnvironmentSource)
					}
				}
			default:
				p.logger.Debug("Ignore unknown metric type", zap.String("type", m.Type().String()))
			}

		}
	}
	return entityServiceName, entityEnvironmentName, entityServiceNameSource
}

// scrapeResourceEntityAttribute expands the datapoint attributes and search for
// resource entity related attributes. This is only used for components
// that only emit attributes on datapoint level. This code block contains a lot
// of repeated code because OTEL metrics type do not have a common interface.
func (p *awsEntityProcessor) scrapeResourceEntityAttribute(scopeMetric pmetric.ScopeMetricsSlice) string {
	autoScalingGroup := EMPTY
	for j := 0; j < scopeMetric.Len(); j++ {
		metric := scopeMetric.At(j).Metrics()
		for k := 0; k < metric.Len(); k++ {
			if autoScalingGroup != EMPTY {
				return autoScalingGroup
			}
			m := metric.At(k)
			switch m.Type() {
			case pmetric.MetricTypeGauge:
				dps := m.Gauge().DataPoints()
				for l := 0; l < dps.Len(); l++ {
					if dpAutoScalingGroup, ok := dps.At(l).Attributes().Get(ec2tagger.CWDimensionASG); ok {
						autoScalingGroup = dpAutoScalingGroup.Str()
					}
				}
			case pmetric.MetricTypeSum:
				dps := m.Sum().DataPoints()
				for l := 0; l < dps.Len(); l++ {
					if dpAutoScalingGroup, ok := dps.At(l).Attributes().Get(ec2tagger.CWDimensionASG); ok {
						autoScalingGroup = dpAutoScalingGroup.Str()
					}
				}
			case pmetric.MetricTypeHistogram:
				dps := m.Histogram().DataPoints()
				for l := 0; l < dps.Len(); l++ {
					if dpAutoScalingGroup, ok := dps.At(l).Attributes().Get(ec2tagger.CWDimensionASG); ok {
						autoScalingGroup = dpAutoScalingGroup.Str()
					}
				}
			case pmetric.MetricTypeExponentialHistogram:
				dps := m.ExponentialHistogram().DataPoints()
				for l := 0; l < dps.Len(); l++ {
					if dpAutoScalingGroup, ok := dps.At(l).Attributes().Get(ec2tagger.CWDimensionASG); ok {
						autoScalingGroup = dpAutoScalingGroup.Str()
					}
				}
			case pmetric.MetricTypeSummary:
				dps := m.Sum().DataPoints()
				for l := 0; l < dps.Len(); l++ {
					if dpAutoScalingGroup, ok := dps.At(l).Attributes().Get(ec2tagger.CWDimensionASG); ok {
						autoScalingGroup = dpAutoScalingGroup.Str()
					}
				}
			default:
				p.logger.Debug("Ignore unknown metric type", zap.String("type", m.Type().String()))
			}

		}
	}
	return autoScalingGroup
}

// getServiceAttributes prioritize service name retrieval based on
// following attribute priority
// 1. service.name
// 2. Service
// Service is needed because Container Insights mainly uses Service as
// attribute for customer workflows
// https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Container-Insights-metrics-EKS.html
func getServiceAttributes(p pcommon.Map) string {
	if serviceName, ok := p.Get(semconv.AttributeServiceName); ok {
		return serviceName.Str()
	}
	if serviceName, ok := p.Get(attributeService); ok {
		return serviceName.Str()
	}
	return EMPTY
}

func getTelemetrySDKEnabledAttribute(p pcommon.Map) bool {
	if _, ok := p.Get(semconv.AttributeTelemetrySDKName); ok {
		return true
	}
	return false
}

// scrapeK8sPodName gets the k8s pod name which is full pod name from the resource attributes
// This is needed to map the pod to the service/environment
func scrapeK8sPodName(p pcommon.Map) string {
	if podAttr, ok := p.Get(semconv.AttributeK8SPodName); ok {
		return podAttr.Str()
	}
	return EMPTY
}

func shouldUseFallbackServiceName(serviceName string) bool {
	return serviceName == EMPTY || strings.HasPrefix(serviceName, unknownService)
}
