// Code generated by mdatagen. DO NOT EDIT.

package metadata

import (
	"regexp"
	"slices"
	"strconv"
	"time"

	"go.uber.org/zap"

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

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

func mustParseInt64(s string) int64 {
	re := regexp.MustCompile(`-?\d+`)
	match := re.FindString(s)
	if match == "" {
		return 0
	}
	v, _ := strconv.ParseInt(match, 10, 64)
	return v
}

// AttributeCPUMode specifies the value cpu.mode attribute.
type AttributeCPUMode int

const (
	_ AttributeCPUMode = iota
	AttributeCPUModeSystem
	AttributeCPUModeUser
	AttributeCPUModeIowait
)

// String returns the string representation of the AttributeCPUMode.
func (av AttributeCPUMode) String() string {
	switch av {
	case AttributeCPUModeSystem:
		return "system"
	case AttributeCPUModeUser:
		return "user"
	case AttributeCPUModeIowait:
		return "iowait"
	}
	return ""
}

// MapAttributeCPUMode is a helper map of string to AttributeCPUMode attribute value.
var MapAttributeCPUMode = map[string]AttributeCPUMode{
	"system": AttributeCPUModeSystem,
	"user":   AttributeCPUModeUser,
	"iowait": AttributeCPUModeIowait,
}

// AttributeState specifies the value state attribute.
type AttributeState int

const (
	_ AttributeState = iota
	AttributeStateIdle
	AttributeStateInterrupt
	AttributeStateNice
	AttributeStateSoftirq
	AttributeStateSteal
	AttributeStateSystem
	AttributeStateUser
	AttributeStateWait
)

// String returns the string representation of the AttributeState.
func (av AttributeState) String() string {
	switch av {
	case AttributeStateIdle:
		return "idle"
	case AttributeStateInterrupt:
		return "interrupt"
	case AttributeStateNice:
		return "nice"
	case AttributeStateSoftirq:
		return "softirq"
	case AttributeStateSteal:
		return "steal"
	case AttributeStateSystem:
		return "system"
	case AttributeStateUser:
		return "user"
	case AttributeStateWait:
		return "wait"
	}
	return ""
}

// MapAttributeState is a helper map of string to AttributeState attribute value.
var MapAttributeState = map[string]AttributeState{
	"idle":      AttributeStateIdle,
	"interrupt": AttributeStateInterrupt,
	"nice":      AttributeStateNice,
	"softirq":   AttributeStateSoftirq,
	"steal":     AttributeStateSteal,
	"system":    AttributeStateSystem,
	"user":      AttributeStateUser,
	"wait":      AttributeStateWait,
}

var MetricsInfo = metricsInfo{
	LinuxMemoryAvailable: metricInfo{
		Name: "linux.memory.available",
	},
	SystemCPUFoo: metricInfo{
		Name: "system.cpu.foo",
	},
	SystemCPUUtilization: metricInfo{
		Name:       "system.cpu.utilization",
		Attributes: []string{"cpu", "state"},
	},
	SystemCPUUtilizationV1: metricInfo{
		Name:       "system.cpu.utilization",
		Attributes: []string{"cpu.logical_number", "cpu.mode"},
	},
	SystemMemoryLinuxAvailableV1: metricInfo{
		Name: "system.memory.linux.available",
	},
}

type metricsInfo struct {
	LinuxMemoryAvailable         metricInfo
	SystemCPUFoo                 metricInfo
	SystemCPUUtilization         metricInfo
	SystemCPUUtilizationV1       metricInfo
	SystemMemoryLinuxAvailableV1 metricInfo
}

type metricInfo struct {
	Name       string
	Attributes []string
}

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

// init fills linux.memory.available metric with initial data.
func (m *metricLinuxMemoryAvailable) init() {
	m.data.SetName("linux.memory.available")
	m.data.SetDescription("Legacy Linux available memory estimate.")
	m.data.SetUnit("By")
	m.data.SetEmptySum()
	m.data.Sum().SetIsMonotonic(false)
	m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
}

func (m *metricLinuxMemoryAvailable) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) {
	if !m.config.Enabled {
		return
	}
	dp := m.data.Sum().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 *metricLinuxMemoryAvailable) 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 *metricLinuxMemoryAvailable) emit(metrics pmetric.MetricSlice) {
	if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 {
		m.updateCapacity()
		m.data.MoveTo(metrics.AppendEmpty())
		m.init()
	}
}

func newMetricLinuxMemoryAvailable(cfg LinuxMemoryAvailableMetricConfig) metricLinuxMemoryAvailable {
	m := metricLinuxMemoryAvailable{config: cfg}

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

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

// init fills system.cpu.foo metric with initial data.
func (m *metricSystemCPUFoo) init() {
	m.data.SetName("system.cpu.foo")
	m.data.SetDescription("Example v1-only metric, disabled by default.")
	m.data.SetUnit("1")
	m.data.SetEmptyGauge()
}

func (m *metricSystemCPUFoo) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val float64) {
	if !m.config.Enabled {
		return
	}
	dp := m.data.Gauge().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 *metricSystemCPUFoo) 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 *metricSystemCPUFoo) emit(metrics pmetric.MetricSlice) {
	if m.config.Enabled && m.data.Gauge().DataPoints().Len() > 0 {
		m.updateCapacity()
		m.data.MoveTo(metrics.AppendEmpty())
		m.init()
	}
}

func newMetricSystemCPUFoo(cfg SystemCPUFooMetricConfig) metricSystemCPUFoo {
	m := metricSystemCPUFoo{config: cfg}

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

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

// init fills system.cpu.utilization metric with initial data.
func (m *metricSystemCPUUtilization) init() {
	m.data.SetName("system.cpu.utilization")
	m.data.SetDescription("CPU utilization as a ratio.")
	m.data.SetUnit("1")
	m.data.SetEmptyGauge()
	m.data.Gauge().DataPoints().EnsureCapacity(m.capacity)
	m.aggDataPoints = m.aggDataPoints[:0]
}

func (m *metricSystemCPUUtilization) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val float64, cpuAttributeValue string, stateAttributeValue string) {
	if !m.config.Enabled {
		return
	}

	dp := pmetric.NewNumberDataPoint()
	dp.SetStartTimestamp(start)
	dp.SetTimestamp(ts)
	if slices.Contains(m.config.EnabledAttributes, SystemCPUUtilizationMetricAttributeKeyCPU) {
		dp.Attributes().PutStr("cpu", cpuAttributeValue)
	}
	if slices.Contains(m.config.EnabledAttributes, SystemCPUUtilizationMetricAttributeKeyState) {
		dp.Attributes().PutStr("state", stateAttributeValue)
	}

	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.SetDoubleValue(dpi.DoubleValue() + val)
				m.aggDataPoints[i] += 1
				return
			case AggregationStrategyMin:
				if dpi.DoubleValue() > val {
					dpi.SetDoubleValue(val)
				}
				return
			case AggregationStrategyMax:
				if dpi.DoubleValue() < val {
					dpi.SetDoubleValue(val)
				}
				return
			}
		}
	}

	dp.SetDoubleValue(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 *metricSystemCPUUtilization) 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 *metricSystemCPUUtilization) 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).SetDoubleValue(m.data.Gauge().DataPoints().At(i).DoubleValue() / aggCount)
			}
		}
		m.updateCapacity()
		m.data.MoveTo(metrics.AppendEmpty())
		m.init()
	}
}

func newMetricSystemCPUUtilization(cfg SystemCPUUtilizationMetricConfig) metricSystemCPUUtilization {
	m := metricSystemCPUUtilization{config: cfg}

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

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

// init fills system.cpu.utilization@v1 metric with initial data.
func (m *metricSystemCPUUtilizationV1) init() {
	m.data.SetName("system.cpu.utilization")
	m.data.SetDescription("CPU utilization as a monotonic cumulative sum.")
	m.data.SetUnit("1")
	m.data.SetEmptySum()
	m.data.Sum().SetIsMonotonic(true)
	m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
	m.data.Sum().DataPoints().EnsureCapacity(m.capacity)
	m.aggDataPoints = m.aggDataPoints[:0]
}

func (m *metricSystemCPUUtilizationV1) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val float64, cpuLogicalNumberAttributeValue int64, cpuModeAttributeValue string, cpuAttributeValue string, stateAttributeValue string, emitLegacyAttrs bool) {
	if !m.config.Enabled {
		return
	}

	dp := pmetric.NewNumberDataPoint()
	dp.SetStartTimestamp(start)
	dp.SetTimestamp(ts)
	if slices.Contains(m.config.EnabledAttributes, SystemCPUUtilizationV1MetricAttributeKeyCPULogicalNumber) {
		dp.Attributes().PutInt("cpu.logical_number", cpuLogicalNumberAttributeValue)
	}
	if slices.Contains(m.config.EnabledAttributes, SystemCPUUtilizationV1MetricAttributeKeyCPUMode) {
		dp.Attributes().PutStr("cpu.mode", cpuModeAttributeValue)
	}
	if emitLegacyAttrs {
		dp.Attributes().PutStr("cpu", cpuAttributeValue)
		dp.Attributes().PutStr("state", stateAttributeValue)
	}

	var s string
	dps := m.data.Sum().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.SetDoubleValue(dpi.DoubleValue() + val)
				m.aggDataPoints[i] += 1
				return
			case AggregationStrategyMin:
				if dpi.DoubleValue() > val {
					dpi.SetDoubleValue(val)
				}
				return
			case AggregationStrategyMax:
				if dpi.DoubleValue() < val {
					dpi.SetDoubleValue(val)
				}
				return
			}
		}
	}

	dp.SetDoubleValue(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 *metricSystemCPUUtilizationV1) 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 *metricSystemCPUUtilizationV1) emit(metrics pmetric.MetricSlice) {
	if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 {
		if m.config.AggregationStrategy == AggregationStrategyAvg {
			for i, aggCount := range m.aggDataPoints {
				m.data.Sum().DataPoints().At(i).SetDoubleValue(m.data.Sum().DataPoints().At(i).DoubleValue() / aggCount)
			}
		}
		m.updateCapacity()
		m.data.MoveTo(metrics.AppendEmpty())
		m.init()
	}
}

func newMetricSystemCPUUtilizationV1(cfg SystemCPUUtilizationV1MetricConfig) metricSystemCPUUtilizationV1 {
	m := metricSystemCPUUtilizationV1{config: cfg}

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

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

// init fills system.memory.linux.available@v1 metric with initial data.
func (m *metricSystemMemoryLinuxAvailableV1) init() {
	m.data.SetName("system.memory.linux.available")
	m.data.SetDescription("An estimate of how much memory is available without swapping. (Linux only)")
	m.data.SetUnit("By")
	m.data.SetEmptySum()
	m.data.Sum().SetIsMonotonic(false)
	m.data.Sum().SetAggregationTemporality(pmetric.AggregationTemporalityCumulative)
}

func (m *metricSystemMemoryLinuxAvailableV1) recordDataPoint(start pcommon.Timestamp, ts pcommon.Timestamp, val int64) {
	if !m.config.Enabled {
		return
	}
	dp := m.data.Sum().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 *metricSystemMemoryLinuxAvailableV1) 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 *metricSystemMemoryLinuxAvailableV1) emit(metrics pmetric.MetricSlice) {
	if m.config.Enabled && m.data.Sum().DataPoints().Len() > 0 {
		m.updateCapacity()
		m.data.MoveTo(metrics.AppendEmpty())
		m.init()
	}
}

func newMetricSystemMemoryLinuxAvailableV1(cfg SystemMemoryLinuxAvailableV1MetricConfig) metricSystemMemoryLinuxAvailableV1 {
	m := metricSystemMemoryLinuxAvailableV1{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.
	metricLinuxMemoryAvailable         metricLinuxMemoryAvailable
	metricSystemCPUFoo                 metricSystemCPUFoo
	metricSystemCPUUtilization         metricSystemCPUUtilization
	metricSystemCPUUtilizationV1       metricSystemCPUUtilizationV1
	metricSystemMemoryLinuxAvailableV1 metricSystemMemoryLinuxAvailableV1
}

// 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 scraper.Settings, options ...MetricBuilderOption) *MetricsBuilder {
	mb := &MetricsBuilder{
		config:                             mbc,
		startTime:                          pcommon.NewTimestampFromTime(time.Now()),
		metricsBuffer:                      pmetric.NewMetrics(),
		buildInfo:                          settings.BuildInfo,
		metricLinuxMemoryAvailable:         newMetricLinuxMemoryAvailable(mbc.Metrics.LinuxMemoryAvailable),
		metricSystemCPUFoo:                 newMetricSystemCPUFoo(mbc.Metrics.SystemCPUFoo),
		metricSystemCPUUtilization:         newMetricSystemCPUUtilization(mbc.Metrics.SystemCPUUtilization),
		metricSystemCPUUtilizationV1:       newMetricSystemCPUUtilizationV1(mbc.Metrics.SystemCPUUtilizationV1),
		metricSystemMemoryLinuxAvailableV1: newMetricSystemMemoryLinuxAvailableV1(mbc.Metrics.SystemMemoryLinuxAvailableV1),
	}
	if ScraperSamplemigrationEmitV1SystemConventionsFeatureGate.IsEnabled() {
		if mb.metricLinuxMemoryAvailable.config.Enabled && !mb.metricSystemMemoryLinuxAvailableV1.config.enabledSetByUser {
			mb.metricSystemMemoryLinuxAvailableV1.config.Enabled = true
			mb.metricSystemMemoryLinuxAvailableV1.data = pmetric.NewMetric()
			mb.metricSystemMemoryLinuxAvailableV1.init()
		}
	}
	if !ScraperSamplemigrationEmitV1SystemConventionsFeatureGate.IsEnabled() && mb.metricSystemMemoryLinuxAvailableV1.config.Enabled {
		mb.metricSystemMemoryLinuxAvailableV1.config.Enabled = false
		settings.Logger.Warn("[WARNING] metric `system.memory.linux.available@v1` requires feature gate `scraper.samplemigration.EmitV1SystemConventions` to be enabled, metric has been disabled")
	}
	if ScraperSamplemigrationEmitV1SystemConventionsFeatureGate.IsEnabled() {
		if mb.metricSystemCPUUtilization.config.Enabled && !mb.metricSystemCPUUtilizationV1.config.enabledSetByUser {
			mb.metricSystemCPUUtilizationV1.config.Enabled = true
			mb.metricSystemCPUUtilizationV1.data = pmetric.NewMetric()
			mb.metricSystemCPUUtilizationV1.init()
		}
	}
	if ScraperSamplemigrationEmitV1SystemConventionsFeatureGate.IsEnabled() {
		if mb.metricSystemCPUUtilization.config.Enabled && mb.metricSystemCPUUtilizationV1.config.Enabled {
			var disable bool
			if mb.metricSystemCPUUtilization.data.Type() != mb.metricSystemCPUUtilizationV1.data.Type() {
				// Disable legacy metric if legacy and latest have same name but different types
				disable = true
				settings.Logger.Warn("[WARNING] Legacy metric `system.cpu.utilization` disabled: same emitted name as `system.cpu.utilization@v1` with different type; only latest will be emitted")
			}
			if !slices.Equal(MetricsInfo.SystemCPUUtilization.Attributes, MetricsInfo.SystemCPUUtilizationV1.Attributes) {
				// Disable legacy metric if legacy and latest have same name but different attributes
				// The latest metric will emit both attribute sets during migration
				disable = true
				settings.Logger.Warn("[WARNING] Legacy metric `system.cpu.utilization` disabled: same emitted name as `system.cpu.utilization@v1` with different attributes; only latest will be emitted with combined attributes",
					zap.Strings("legacy_attributes", MetricsInfo.SystemCPUUtilization.Attributes),
					zap.Strings("latest_attributes", MetricsInfo.SystemCPUUtilizationV1.Attributes))
			}
			if disable {
				mb.metricSystemCPUUtilization.config.Enabled = false
			}
		}
	} else {
		if mb.metricSystemCPUUtilizationV1.config.Enabled {
			mb.metricSystemCPUUtilizationV1.config.Enabled = false
			settings.Logger.Warn("[WARNING] metric `system.cpu.utilization@v1` requires feature gate `scraper.samplemigration.EmitV1SystemConventions` to be enabled, metric has been disabled")
		}
	}

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

// 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())
	})
}

// 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)
			}
		}
	})
}

// 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.
func (mb *MetricsBuilder) EmitForResource(options ...ResourceMetricsOption) {
	rm := pmetric.NewResourceMetrics()
	ils := rm.ScopeMetrics().AppendEmpty()
	ils.Scope().SetName(ScopeName)
	ils.Scope().SetVersion(mb.buildInfo.Version)
	ils.Metrics().EnsureCapacity(mb.metricsCapacity)
	mb.metricLinuxMemoryAvailable.emit(ils.Metrics())
	mb.metricSystemCPUFoo.emit(ils.Metrics())
	mb.metricSystemCPUUtilization.emit(ils.Metrics())
	mb.metricSystemCPUUtilizationV1.emit(ils.Metrics())
	mb.metricSystemMemoryLinuxAvailableV1.emit(ils.Metrics())

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

	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
}

// RecordLinuxMemoryAvailableDataPoint adds a data point to linux.memory.available metric.
func (mb *MetricsBuilder) RecordLinuxMemoryAvailableDataPoint(ts pcommon.Timestamp, val int64) {
	// Dual-schema emission controlled by feature gates
	if !ScraperSamplemigrationDontEmitV0SystemConventionsFeatureGate.IsEnabled() {
		mb.metricLinuxMemoryAvailable.recordDataPoint(mb.startTime, ts, val)
	}
	if ScraperSamplemigrationEmitV1SystemConventionsFeatureGate.IsEnabled() {
		mb.metricSystemMemoryLinuxAvailableV1.recordDataPoint(mb.startTime, ts, val)
	}
}

// RecordSystemCPUFooDataPoint adds a data point to system.cpu.foo metric.
func (mb *MetricsBuilder) RecordSystemCPUFooDataPoint(ts pcommon.Timestamp, val float64) {
	mb.metricSystemCPUFoo.recordDataPoint(mb.startTime, ts, val)
}

// RecordSystemCPUUtilizationDataPoint adds a data point to system.cpu.utilization metric.
func (mb *MetricsBuilder) RecordSystemCPUUtilizationDataPoint(ts pcommon.Timestamp, val float64, cpuAttributeValue string, stateAttributeValue AttributeState, cpuLogicalNumberAttributeValue int64, cpuModeAttributeValue AttributeCPUMode) {
	// Dual-schema emission controlled by feature gates
	if !ScraperSamplemigrationDontEmitV0SystemConventionsFeatureGate.IsEnabled() {
		mb.metricSystemCPUUtilization.recordDataPoint(mb.startTime, ts, val, cpuAttributeValue, stateAttributeValue.String())
	}
	if ScraperSamplemigrationEmitV1SystemConventionsFeatureGate.IsEnabled() {
		mb.metricSystemCPUUtilizationV1.recordDataPoint(mb.startTime, ts, val, cpuLogicalNumberAttributeValue, cpuModeAttributeValue.String(), cpuAttributeValue, stateAttributeValue.String(), true)
	}
}

// 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)
	}
}
