// Code generated by mdatagen. DO NOT EDIT.

package metadata

import (
	"slices"
	"time"

	conventions "go.opentelemetry.io/otel/semconv/v1.40.0"

	"go.opentelemetry.io/collector/component"
	"go.opentelemetry.io/collector/filter"
	"go.opentelemetry.io/collector/pdata/pcommon"
	"go.opentelemetry.io/collector/pdata/pmetric"
	"go.opentelemetry.io/collector/receiver"
)

const (
	AggregationStrategySum = "sum"
	AggregationStrategyAvg = "avg"
	AggregationStrategyMin = "min"
	AggregationStrategyMax = "max"
)

// AttributePhase specifies the value phase attribute.
type AttributePhase int

const (
	_ AttributePhase = iota
	AttributePhasePending
	AttributePhaseRunning
	AttributePhaseSucceeded
	AttributePhaseFailed
	AttributePhaseUnknown
)

// String returns the string representation of the AttributePhase.
func (av AttributePhase) String() string {
	switch av {
	case AttributePhasePending:
		return "Pending"
	case AttributePhaseRunning:
		return "Running"
	case AttributePhaseSucceeded:
		return "Succeeded"
	case AttributePhaseFailed:
		return "Failed"
	case AttributePhaseUnknown:
		return "Unknown"
	}
	return ""
}

// MapAttributePhase is a helper map of string to AttributePhase attribute value.
var MapAttributePhase = map[string]AttributePhase{
	"Pending":   AttributePhasePending,
	"Running":   AttributePhaseRunning,
	"Succeeded": AttributePhaseSucceeded,
	"Failed":    AttributePhaseFailed,
	"Unknown":   AttributePhaseUnknown,
}

var MetricsInfo = metricsInfo{
	K8sPodCPUTime: metricInfo{
		Name: "k8s.pod.cpu_time",
	},
	K8sPodPhase: metricInfo{
		Name:       "k8s.pod.phase",
		Attributes: []string{"phase"},
	},
	K8sReplicasetDesired: metricInfo{
		Name: "k8s.replicaset.desired",
	},
}

type metricsInfo struct {
	K8sPodCPUTime        metricInfo
	K8sPodPhase          metricInfo
	K8sReplicasetDesired metricInfo
}

type metricInfo struct {
	Name       string
	Attributes []string
}

type metricK8sPodCPUTime struct {
	data     pmetric.Metric            // data buffer for generated metric.
	config   K8sPodCPUTimeMetricConfig // metric config provided by user.
	capacity int                       // max observed number of data points added to the metric.
}

// init fills k8s.pod.cpu_time metric with initial data.
func (m *metricK8sPodCPUTime) init() {
	m.data.SetName("k8s.pod.cpu_time")
	m.data.SetDescription("CPU time consumed by the pod")
	m.data.SetUnit("s")
	m.data.SetEmptySum()
	m.data.Sum().SetIsMonotonic(true)
	m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
}

func (m *metricK8sPodCPUTime) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val float64) {
	if !m.config.Enabled {
		return
	}
	dp := m.data.Sum().DataPoints().AppendEmpty()
	dp.SetStartTimestamp(start)
	dp.SetTimestamp(ts)
	dp.SetDoubleValue(val)
}

// updateCapacity saves max length of data point slices that will be used for the slice capacity.
func (m *metricK8sPodCPUTime) updateCapacity() {
	if m.data.Sum().DataPoints().Len() > m.capacity {
		m.capacity = m.data.Sum().DataPoints().Len()
	}
}

// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points.
func (m *metricK8sPodCPUTime) emit(metrics pmetric.MetricSlice) {
	if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 {
		m.updateCapacity()
		m.data.MoveTo(metrics.AppendEmpty())
		m.init()
	}
}

func newMetricK8sPodCPUTime(cfg K8sPodCPUTimeMetricConfig) metricK8sPodCPUTime {
	m := metricK8sPodCPUTime{config: cfg}

	if cfg.Enabled {
		m.data = pmetric.NewMetric()
		m.init()
	}
	return m
}

type metricK8sPodPhase struct {
	data          pmetric.Metric          // data buffer for generated metric.
	config        K8sPodPhaseMetricConfig // metric config provided by user.
	capacity      int                     // max observed number of data points added to the metric.
	aggDataPoints []int64                 // slice containing number of aggregated datapoints at each index
}

// init fills k8s.pod.phase metric with initial data.
func (m *metricK8sPodPhase) init() {
	m.data.SetName("k8s.pod.phase")
	m.data.SetDescription("Current phase of the pod")
	m.data.SetUnit("1")
	m.data.SetEmptyGauge()
	m.data.Gauge().DataPoints().EnsureCapacity(m.capacity)
	m.aggDataPoints = m.aggDataPoints[:0]
}

func (m *metricK8sPodPhase) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64, phaseAttributeValue string) {
	if !m.config.Enabled {
		return
	}

	dp := pmetric.NewNumberDataPoint()
	dp.SetStartTimestamp(start)
	dp.SetTimestamp(ts)
	if slices.Contains(m.config.EnabledAttributes, K8sPodPhaseMetricAttributeKeyPhase) {
		dp.Attributes().PutStr("phase", phaseAttributeValue)
	}

	var s string
	dps := m.data.Gauge().DataPoints()
	for i := 0; i < dps.Len(); i++ {
		dpi := dps.At(i)
		if dp.Attributes().Equal(dpi.Attributes()) && dp.StartTimestamp() == dpi.StartTimestamp() && dp.Timestamp() == dpi.Timestamp() {
			switch s = m.config.AggregationStrategy; s {
			case AggregationStrategySum, AggregationStrategyAvg:
				dpi.SetIntValue(dpi.IntValue() + val)
				m.aggDataPoints[i] += 1
				return
			case AggregationStrategyMin:
				if dpi.IntValue() > val {
					dpi.SetIntValue(val)
				}
				return
			case AggregationStrategyMax:
				if dpi.IntValue() < val {
					dpi.SetIntValue(val)
				}
				return
			}
		}
	}

	dp.SetIntValue(val)
	m.aggDataPoints = append(m.aggDataPoints, 1)
	dp.MoveTo(dps.AppendEmpty())
}

// updateCapacity saves max length of data point slices that will be used for the slice capacity.
func (m *metricK8sPodPhase) updateCapacity() {
	if m.data.Gauge().DataPoints().Len() > m.capacity {
		m.capacity = m.data.Gauge().DataPoints().Len()
	}
}

// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points.
func (m *metricK8sPodPhase) emit(metrics pmetric.MetricSlice) {
	if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 {
		if m.config.AggregationStrategy == AggregationStrategyAvg {
			for i, aggCount := range m.aggDataPoints {
				m.data.Gauge().DataPoints().At(i).SetIntValue(m.data.Gauge().DataPoints().At(i).IntValue() / aggCount)
			}
		}
		m.updateCapacity()
		m.data.MoveTo(metrics.AppendEmpty())
		m.init()
	}
}

func newMetricK8sPodPhase(cfg K8sPodPhaseMetricConfig) metricK8sPodPhase {
	m := metricK8sPodPhase{config: cfg}

	if cfg.Enabled {
		m.data = pmetric.NewMetric()
		m.init()
	}
	return m
}

type metricK8sReplicasetDesired struct {
	data     pmetric.Metric                   // data buffer for generated metric.
	config   K8sReplicasetDesiredMetricConfig // metric config provided by user.
	capacity int                              // max observed number of data points added to the metric.
}

// init fills k8s.replicaset.desired metric with initial data.
func (m *metricK8sReplicasetDesired) init() {
	m.data.SetName("k8s.replicaset.desired")
	m.data.SetDescription("Number of desired replicas")
	m.data.SetUnit("{replicas}")
	m.data.SetEmptyGauge()
}

func (m *metricK8sReplicasetDesired) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) {
	if !m.config.Enabled {
		return
	}
	dp := m.data.Gauge().DataPoints().AppendEmpty()
	dp.SetStartTimestamp(start)
	dp.SetTimestamp(ts)
	dp.SetIntValue(val)
}

// updateCapacity saves max length of data point slices that will be used for the slice capacity.
func (m *metricK8sReplicasetDesired) updateCapacity() {
	if m.data.Gauge().DataPoints().Len() > m.capacity {
		m.capacity = m.data.Gauge().DataPoints().Len()
	}
}

// emit appends recorded metric data to a metrics slice and prepares it for recording another set of data points.
func (m *metricK8sReplicasetDesired) emit(metrics pmetric.MetricSlice) {
	if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 {
		m.updateCapacity()
		m.data.MoveTo(metrics.AppendEmpty())
		m.init()
	}
}

func newMetricK8sReplicasetDesired(cfg K8sReplicasetDesiredMetricConfig) metricK8sReplicasetDesired {
	m := metricK8sReplicasetDesired{config: cfg}

	if cfg.Enabled {
		m.data = pmetric.NewMetric()
		m.init()
	}
	return m
}

// MetricsBuilder provides an interface for scrapers to report metrics while taking care of all the transformations
// required to produce metric representation defined in metadata and user config.
type MetricsBuilder struct {
	config                         MetricsBuilderConfig // config of the metrics builder.
	startTime                      pcommon.Timestamp    // start time that will be applied to all recorded data points.
	metricsCapacity                int                  // maximum observed number of metrics per resource.
	metricsBuffer                  pmetric.Metrics      // accumulates metrics data before emitting.
	buildInfo                      component.BuildInfo  // contains version information.
	resourceAttributeIncludeFilter map[string]filter.Filter
	resourceAttributeExcludeFilter map[string]filter.Filter
	metricK8sPodCPUTime            metricK8sPodCPUTime
	metricK8sPodPhase              metricK8sPodPhase
	metricK8sReplicasetDesired     metricK8sReplicasetDesired
}

// MetricBuilderOption applies changes to default metrics builder.
type MetricBuilderOption interface {
	apply(*MetricsBuilder)
}

type metricBuilderOptionFunc func(mb *MetricsBuilder)

func (mbof metricBuilderOptionFunc) apply(mb *MetricsBuilder) {
	mbof(mb)
}

// WithStartTime sets startTime on the metrics builder.
func WithStartTime(startTime pcommon.Timestamp) MetricBuilderOption {
	return metricBuilderOptionFunc(func(mb *MetricsBuilder) {
		mb.startTime = startTime
	})
}
func NewMetricsBuilder(mbc MetricsBuilderConfig, settings receiver.Settings, options ...MetricBuilderOption) *MetricsBuilder {
	mb := &MetricsBuilder{
		config:                         mbc,
		startTime:                      pcommon.NewTimestampFromTime(time.Now()),
		metricsBuffer:                  pmetric.NewMetrics(),
		buildInfo:                      settings.BuildInfo,
		metricK8sPodCPUTime:            newMetricK8sPodCPUTime(mbc.Metrics.K8sPodCPUTime),
		metricK8sPodPhase:              newMetricK8sPodPhase(mbc.Metrics.K8sPodPhase),
		metricK8sReplicasetDesired:     newMetricK8sReplicasetDesired(mbc.Metrics.K8sReplicasetDesired),
		resourceAttributeIncludeFilter: make(map[string]filter.Filter),
		resourceAttributeExcludeFilter: make(map[string]filter.Filter),
	}
	if mbc.ResourceAttributes.K8sNamespaceName.MetricsInclude != nil {
		mb.resourceAttributeIncludeFilter["k8s.namespace.name"] = filter.CreateFilter(mbc.ResourceAttributes.K8sNamespaceName.MetricsInclude)
	}
	if mbc.ResourceAttributes.K8sNamespaceName.MetricsExclude != nil {
		mb.resourceAttributeExcludeFilter["k8s.namespace.name"] = filter.CreateFilter(mbc.ResourceAttributes.K8sNamespaceName.MetricsExclude)
	}
	if mbc.ResourceAttributes.K8sPodName.MetricsInclude != nil {
		mb.resourceAttributeIncludeFilter["k8s.pod.name"] = filter.CreateFilter(mbc.ResourceAttributes.K8sPodName.MetricsInclude)
	}
	if mbc.ResourceAttributes.K8sPodName.MetricsExclude != nil {
		mb.resourceAttributeExcludeFilter["k8s.pod.name"] = filter.CreateFilter(mbc.ResourceAttributes.K8sPodName.MetricsExclude)
	}
	if mbc.ResourceAttributes.K8sPodUID.MetricsInclude != nil {
		mb.resourceAttributeIncludeFilter["k8s.pod.uid"] = filter.CreateFilter(mbc.ResourceAttributes.K8sPodUID.MetricsInclude)
	}
	if mbc.ResourceAttributes.K8sPodUID.MetricsExclude != nil {
		mb.resourceAttributeExcludeFilter["k8s.pod.uid"] = filter.CreateFilter(mbc.ResourceAttributes.K8sPodUID.MetricsExclude)
	}
	if mbc.ResourceAttributes.K8sReplicasetName.MetricsInclude != nil {
		mb.resourceAttributeIncludeFilter["k8s.replicaset.name"] = filter.CreateFilter(mbc.ResourceAttributes.K8sReplicasetName.MetricsInclude)
	}
	if mbc.ResourceAttributes.K8sReplicasetName.MetricsExclude != nil {
		mb.resourceAttributeExcludeFilter["k8s.replicaset.name"] = filter.CreateFilter(mbc.ResourceAttributes.K8sReplicasetName.MetricsExclude)
	}
	if mbc.ResourceAttributes.K8sReplicasetUID.MetricsInclude != nil {
		mb.resourceAttributeIncludeFilter["k8s.replicaset.uid"] = filter.CreateFilter(mbc.ResourceAttributes.K8sReplicasetUID.MetricsInclude)
	}
	if mbc.ResourceAttributes.K8sReplicasetUID.MetricsExclude != nil {
		mb.resourceAttributeExcludeFilter["k8s.replicaset.uid"] = filter.CreateFilter(mbc.ResourceAttributes.K8sReplicasetUID.MetricsExclude)
	}

	for _, op := range options {
		op.apply(mb)
	}
	return mb
}

// NewResourceBuilder returns a new resource builder that should be used to build a resource associated with for the emitted metrics.
func (mb *MetricsBuilder) NewResourceBuilder() *ResourceBuilder {
	return NewResourceBuilder(mb.config.ResourceAttributes)
}

// updateCapacity updates max length of metrics and resource attributes that will be used for the slice capacity.
func (mb *MetricsBuilder) updateCapacity(rm pmetric.ResourceMetrics) {
	if mb.metricsCapacity < rm.ScopeMetrics().At(0).Metrics().Len() {
		mb.metricsCapacity = rm.ScopeMetrics().At(0).Metrics().Len()
	}
}

// ResourceMetricsOption applies changes to provided resource metrics.
type ResourceMetricsOption interface {
	apply(pmetric.ResourceMetrics)
}

type resourceMetricsOptionFunc func(pmetric.ResourceMetrics)

func (rmof resourceMetricsOptionFunc) apply(rm pmetric.ResourceMetrics) {
	rmof(rm)
}

// WithResource sets the provided resource on the emitted ResourceMetrics.
// It's recommended to use ResourceBuilder to create the resource.
func WithResource(res pcommon.Resource) ResourceMetricsOption {
	return resourceMetricsOptionFunc(func(rm pmetric.ResourceMetrics) {
		res.CopyTo(rm.Resource())
	})
}

func withResourceMoved(res pcommon.Resource) ResourceMetricsOption {
	return resourceMetricsOptionFunc(func(rm pmetric.ResourceMetrics) {
		res.MoveTo(rm.Resource())
	})
}

// WithStartTimeOverride overrides start time for all the resource metrics data points.
// This option should be only used if different start time has to be set on metrics coming from different resources.
func WithStartTimeOverride(start pcommon.Timestamp) ResourceMetricsOption {
	return resourceMetricsOptionFunc(func(rm pmetric.ResourceMetrics) {
		var dps pmetric.NumberDataPointSlice
		metrics := rm.ScopeMetrics().At(0).Metrics()
		for i := 0; i < metrics.Len(); i++ {
			switch metrics.At(i).Type() {
			case pmetric.MetricTypeGauge:
				dps = metrics.At(i).Gauge().DataPoints()
			case pmetric.MetricTypeSum:
				dps = metrics.At(i).Sum().DataPoints()
			}
			for j := 0; j < dps.Len(); j++ {
				dps.At(j).SetStartTimestamp(start)
			}
		}
	})
}

// ForK8sReplicaset returns a K8sReplicasetMetricsBuilder that restricts metric recording
// to metrics belonging to the k8s.replicaset entity.
func (mb *MetricsBuilder) ForK8sReplicaset(e *K8sReplicasetEntity) *K8sReplicasetMetricsBuilder {
	return &K8sReplicasetMetricsBuilder{mb: mb, entity: e}
}

// ForK8sPod returns a K8sPodMetricsBuilder that restricts metric recording
// to metrics belonging to the k8s.pod entity.
func (mb *MetricsBuilder) ForK8sPod(e *K8sPodEntity) *K8sPodMetricsBuilder {
	return &K8sPodMetricsBuilder{mb: mb, entity: e}
}

// EmitForResource saves all the generated metrics under a new resource and updates the internal state to be ready for
// recording another set of data points as part of another resource. This function can be helpful when one scraper
// needs to emit metrics from several resources. Otherwise calling this function is not required,
// just `Emit` function can be called instead.
// Resource attributes should be provided as ResourceMetricsOption arguments.
//
// Deprecated: Use the For<EntityType> methods to get entity-scoped builders and call Emit() on them instead.
func (mb *MetricsBuilder) EmitForResource(options ...ResourceMetricsOption) {
	rm := pmetric.NewResourceMetrics()
	rm.SetSchemaUrl(conventions.SchemaURL)
	ils := rm.ScopeMetrics().AppendEmpty()
	ils.Scope().SetName(ScopeName)
	ils.Scope().SetVersion(mb.buildInfo.Version)
	ils.Metrics().EnsureCapacity(mb.metricsCapacity)
	mb.metricK8sPodCPUTime.emit(ils.Metrics())
	mb.metricK8sPodPhase.emit(ils.Metrics())
	mb.metricK8sReplicasetDesired.emit(ils.Metrics())

	for _, op := range options {
		op.apply(rm)
	}
	for attr, filter := range mb.resourceAttributeIncludeFilter {
		if val, ok := rm.Resource().Attributes().Get(attr); ok && !filter.Matches(val.AsString()) {
			return
		}
	}
	for attr, filter := range mb.resourceAttributeExcludeFilter {
		if val, ok := rm.Resource().Attributes().Get(attr); ok && filter.Matches(val.AsString()) {
			return
		}
	}

	if ils.Metrics().Len() > 0 {
		mb.updateCapacity(rm)
		rm.MoveTo(mb.metricsBuffer.ResourceMetrics().AppendEmpty())
	}
}

// Emit returns all the metrics accumulated by the metrics builder and updates the internal state to be ready for
// recording another set of metrics. This function will be responsible for applying all the transformations required to
// produce metric representation defined in metadata and user config, e.g. delta or cumulative.
func (mb *MetricsBuilder) Emit(options ...ResourceMetricsOption) pmetric.Metrics {
	mb.EmitForResource(options...)
	metrics := mb.metricsBuffer
	mb.metricsBuffer = pmetric.NewMetrics()
	return metrics
}

// RecordK8sPodCPUTimeDataPoint adds a data point to k8s.pod.cpu_time metric.
//
// Deprecated: Use mb.ForK8sPod(entity).RecordK8sPodCPUTimeDataPoint(...) instead.
func (mb *MetricsBuilder) RecordK8sPodCPUTimeDataPoint(ts pcommon.Timestamp, val float64) {
	mb.metricK8sPodCPUTime.recordDataPoint(mb.startTime, ts, val)
}

// RecordK8sPodPhaseDataPoint adds a data point to k8s.pod.phase metric.
//
// Deprecated: Use mb.ForK8sPod(entity).RecordK8sPodPhaseDataPoint(...) instead.
func (mb *MetricsBuilder) RecordK8sPodPhaseDataPoint(ts pcommon.Timestamp, val int64, phaseAttributeValue AttributePhase) {
	mb.metricK8sPodPhase.recordDataPoint(mb.startTime, ts, val, phaseAttributeValue.String())
}

// RecordK8sReplicasetDesiredDataPoint adds a data point to k8s.replicaset.desired metric.
//
// Deprecated: Use mb.ForK8sReplicaset(entity).RecordK8sReplicasetDesiredDataPoint(...) instead.
func (mb *MetricsBuilder) RecordK8sReplicasetDesiredDataPoint(ts pcommon.Timestamp, val int64) {
	mb.metricK8sReplicasetDesired.recordDataPoint(mb.startTime, ts, val)
}

// Reset resets metrics builder to its initial state. It should be used when external metrics source is restarted,
// and metrics builder should update its startTime and reset it's internal state accordingly.
func (mb *MetricsBuilder) Reset(options ...MetricBuilderOption) {
	mb.startTime = pcommon.NewTimestampFromTime(time.Now())
	for _, op := range options {
		op.apply(mb)
	}
}
