// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package metric // import "go.opentelemetry.io/otel/metric"

import 

// Observable is used as a grouping mechanism for all instruments that are
// updated within a Callback.
type Observable interface {
	observable()
}

// InstrumentOption applies options to all instruments.
type InstrumentOption interface {
	Int64CounterOption
	Int64UpDownCounterOption
	Int64HistogramOption
	Int64GaugeOption
	Int64ObservableCounterOption
	Int64ObservableUpDownCounterOption
	Int64ObservableGaugeOption

	Float64CounterOption
	Float64UpDownCounterOption
	Float64HistogramOption
	Float64GaugeOption
	Float64ObservableCounterOption
	Float64ObservableUpDownCounterOption
	Float64ObservableGaugeOption
}

// HistogramOption applies options to histogram instruments.
type HistogramOption interface {
	Int64HistogramOption
	Float64HistogramOption
}

type descOpt string

func ( descOpt) ( Float64CounterConfig) Float64CounterConfig {
	.description = string()
	return 
}

func ( descOpt) ( Float64UpDownCounterConfig) Float64UpDownCounterConfig {
	.description = string()
	return 
}

func ( descOpt) ( Float64HistogramConfig) Float64HistogramConfig {
	.description = string()
	return 
}

func ( descOpt) ( Float64GaugeConfig) Float64GaugeConfig {
	.description = string()
	return 
}

func ( descOpt) ( Float64ObservableCounterConfig) Float64ObservableCounterConfig {
	.description = string()
	return 
}

func ( descOpt) (
	 Float64ObservableUpDownCounterConfig,
) Float64ObservableUpDownCounterConfig {
	.description = string()
	return 
}

func ( descOpt) ( Float64ObservableGaugeConfig) Float64ObservableGaugeConfig {
	.description = string()
	return 
}

func ( descOpt) ( Int64CounterConfig) Int64CounterConfig {
	.description = string()
	return 
}

func ( descOpt) ( Int64UpDownCounterConfig) Int64UpDownCounterConfig {
	.description = string()
	return 
}

func ( descOpt) ( Int64HistogramConfig) Int64HistogramConfig {
	.description = string()
	return 
}

func ( descOpt) ( Int64GaugeConfig) Int64GaugeConfig {
	.description = string()
	return 
}

func ( descOpt) ( Int64ObservableCounterConfig) Int64ObservableCounterConfig {
	.description = string()
	return 
}

func ( descOpt) (
	 Int64ObservableUpDownCounterConfig,
) Int64ObservableUpDownCounterConfig {
	.description = string()
	return 
}

func ( descOpt) ( Int64ObservableGaugeConfig) Int64ObservableGaugeConfig {
	.description = string()
	return 
}

// WithDescription sets the instrument description.
func ( string) InstrumentOption { return descOpt() }

type unitOpt string

func ( unitOpt) ( Float64CounterConfig) Float64CounterConfig {
	.unit = string()
	return 
}

func ( unitOpt) ( Float64UpDownCounterConfig) Float64UpDownCounterConfig {
	.unit = string()
	return 
}

func ( unitOpt) ( Float64HistogramConfig) Float64HistogramConfig {
	.unit = string()
	return 
}

func ( unitOpt) ( Float64GaugeConfig) Float64GaugeConfig {
	.unit = string()
	return 
}

func ( unitOpt) ( Float64ObservableCounterConfig) Float64ObservableCounterConfig {
	.unit = string()
	return 
}

func ( unitOpt) (
	 Float64ObservableUpDownCounterConfig,
) Float64ObservableUpDownCounterConfig {
	.unit = string()
	return 
}

func ( unitOpt) ( Float64ObservableGaugeConfig) Float64ObservableGaugeConfig {
	.unit = string()
	return 
}

func ( unitOpt) ( Int64CounterConfig) Int64CounterConfig {
	.unit = string()
	return 
}

func ( unitOpt) ( Int64UpDownCounterConfig) Int64UpDownCounterConfig {
	.unit = string()
	return 
}

func ( unitOpt) ( Int64HistogramConfig) Int64HistogramConfig {
	.unit = string()
	return 
}

func ( unitOpt) ( Int64GaugeConfig) Int64GaugeConfig {
	.unit = string()
	return 
}

func ( unitOpt) ( Int64ObservableCounterConfig) Int64ObservableCounterConfig {
	.unit = string()
	return 
}

func ( unitOpt) (
	 Int64ObservableUpDownCounterConfig,
) Int64ObservableUpDownCounterConfig {
	.unit = string()
	return 
}

func ( unitOpt) ( Int64ObservableGaugeConfig) Int64ObservableGaugeConfig {
	.unit = string()
	return 
}

// WithUnit sets the instrument unit.
//
// The unit u should be defined using the appropriate [UCUM](https://ucum.org) case-sensitive code.
func ( string) InstrumentOption { return unitOpt() }

// WithExplicitBucketBoundaries sets the instrument explicit bucket boundaries.
//
// This option is considered "advisory", and may be ignored by API implementations.
func ( ...float64) HistogramOption { return bucketOpt() }

type bucketOpt []float64

func ( bucketOpt) ( Float64HistogramConfig) Float64HistogramConfig {
	.explicitBucketBoundaries = 
	return 
}

func ( bucketOpt) ( Int64HistogramConfig) Int64HistogramConfig {
	.explicitBucketBoundaries = 
	return 
}

// AddOption applies options to an addition measurement. See
// [MeasurementOption] for other options that can be used as an AddOption.
type AddOption interface {
	applyAdd(AddConfig) AddConfig
}

// AddConfig contains options for an addition measurement.
type AddConfig struct {
	attrs attribute.Set
}

// NewAddConfig returns a new [AddConfig] with all opts applied.
func ( []AddOption) AddConfig {
	 := AddConfig{attrs: *attribute.EmptySet()}
	for ,  := range  {
		 = .applyAdd()
	}
	return 
}

// Attributes returns the configured attribute set.
func ( AddConfig) () attribute.Set {
	return .attrs
}

// RecordOption applies options to an addition measurement. See
// [MeasurementOption] for other options that can be used as a RecordOption.
type RecordOption interface {
	applyRecord(RecordConfig) RecordConfig
}

// RecordConfig contains options for a recorded measurement.
type RecordConfig struct {
	attrs attribute.Set
}

// NewRecordConfig returns a new [RecordConfig] with all opts applied.
func ( []RecordOption) RecordConfig {
	 := RecordConfig{attrs: *attribute.EmptySet()}
	for ,  := range  {
		 = .applyRecord()
	}
	return 
}

// Attributes returns the configured attribute set.
func ( RecordConfig) () attribute.Set {
	return .attrs
}

// ObserveOption applies options to an addition measurement. See
// [MeasurementOption] for other options that can be used as a ObserveOption.
type ObserveOption interface {
	applyObserve(ObserveConfig) ObserveConfig
}

// ObserveConfig contains options for an observed measurement.
type ObserveConfig struct {
	attrs attribute.Set
}

// NewObserveConfig returns a new [ObserveConfig] with all opts applied.
func ( []ObserveOption) ObserveConfig {
	 := ObserveConfig{attrs: *attribute.EmptySet()}
	for ,  := range  {
		 = .applyObserve()
	}
	return 
}

// Attributes returns the configured attribute set.
func ( ObserveConfig) () attribute.Set {
	return .attrs
}

// MeasurementOption applies options to all instrument measurement.
type MeasurementOption interface {
	AddOption
	RecordOption
	ObserveOption
}

type attrOpt struct {
	set attribute.Set
}

// mergeSets returns the union of keys between a and b. Any duplicate keys will
// use the value associated with b.
func mergeSets(,  attribute.Set) attribute.Set {
	// NewMergeIterator uses the first value for any duplicates.
	 := attribute.NewMergeIterator(&, &)
	 := make([]attribute.KeyValue, 0, .Len()+.Len())
	for .Next() {
		 = append(, .Attribute())
	}
	return attribute.NewSet(...)
}

func ( attrOpt) ( AddConfig) AddConfig {
	switch {
	case .set.Len() == 0:
	case .attrs.Len() == 0:
		.attrs = .set
	default:
		.attrs = mergeSets(.attrs, .set)
	}
	return 
}

func ( attrOpt) ( RecordConfig) RecordConfig {
	switch {
	case .set.Len() == 0:
	case .attrs.Len() == 0:
		.attrs = .set
	default:
		.attrs = mergeSets(.attrs, .set)
	}
	return 
}

func ( attrOpt) ( ObserveConfig) ObserveConfig {
	switch {
	case .set.Len() == 0:
	case .attrs.Len() == 0:
		.attrs = .set
	default:
		.attrs = mergeSets(.attrs, .set)
	}
	return 
}

// WithAttributeSet sets the attribute Set associated with a measurement is
// made with.
//
// If multiple WithAttributeSet or WithAttributes options are passed the
// attributes will be merged together in the order they are passed. Attributes
// with duplicate keys will use the last value passed.
func ( attribute.Set) MeasurementOption {
	return attrOpt{set: }
}

// WithAttributes converts attributes into an attribute Set and sets the Set to
// be associated with a measurement. This is shorthand for:
//
//	cp := make([]attribute.KeyValue, len(attributes))
//	copy(cp, attributes)
//	WithAttributeSet(attribute.NewSet(cp...))
//
// [attribute.NewSet] may modify the passed attributes so this will make a copy
// of attributes before creating a set in order to ensure this function is
// concurrent safe. This makes this option function less optimized in
// comparison to [WithAttributeSet]. Therefore, [WithAttributeSet] should be
// preferred for performance sensitive code.
//
// See [WithAttributeSet] for information about how multiple WithAttributes are
// merged.
func ( ...attribute.KeyValue) MeasurementOption {
	 := make([]attribute.KeyValue, len())
	copy(, )
	return attrOpt{set: attribute.NewSet(...)}
}