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

package trace // import "go.opentelemetry.io/otel/sdk/trace"

import (
	
	
	

	
	
)

// Sampler decides whether a trace should be sampled and exported.
type Sampler interface {
	// DO NOT CHANGE: any modification will not be backwards compatible and
	// must never be done outside of a new major release.

	// ShouldSample returns a SamplingResult based on a decision made from the
	// passed parameters.
	ShouldSample(parameters SamplingParameters) SamplingResult
	// DO NOT CHANGE: any modification will not be backwards compatible and
	// must never be done outside of a new major release.

	// Description returns information describing the Sampler.
	Description() string
	// DO NOT CHANGE: any modification will not be backwards compatible and
	// must never be done outside of a new major release.
}

// SamplingParameters contains the values passed to a Sampler.
type SamplingParameters struct {
	ParentContext context.Context
	TraceID       trace.TraceID
	Name          string
	Kind          trace.SpanKind
	Attributes    []attribute.KeyValue
	Links         []trace.Link
}

// SamplingDecision indicates whether a span is dropped, recorded and/or sampled.
type SamplingDecision uint8

// Valid sampling decisions.
const (
	// Drop will not record the span and all attributes/events will be dropped.
	Drop SamplingDecision = iota

	// RecordOnly indicates the span's IsRecording method returns true, but trace.FlagsSampled flag
	// must not be set.
	RecordOnly

	// RecordAndSample indicates the span's IsRecording method returns true and trace.FlagsSampled flag
	// must be set.
	RecordAndSample
)

// SamplingResult conveys a SamplingDecision, set of Attributes and a Tracestate.
type SamplingResult struct {
	Decision   SamplingDecision
	Attributes []attribute.KeyValue
	Tracestate trace.TraceState
}

type traceIDRatioSampler struct {
	traceIDUpperBound uint64
	description       string
}

func ( traceIDRatioSampler) ( SamplingParameters) SamplingResult {
	 := trace.SpanContextFromContext(.ParentContext)
	 := binary.BigEndian.Uint64(.TraceID[8:16]) >> 1
	if  < .traceIDUpperBound {
		return SamplingResult{
			Decision:   RecordAndSample,
			Tracestate: .TraceState(),
		}
	}
	return SamplingResult{
		Decision:   Drop,
		Tracestate: .TraceState(),
	}
}

func ( traceIDRatioSampler) () string {
	return .description
}

// TraceIDRatioBased samples a given fraction of traces. Fractions >= 1 will
// always sample. Fractions < 0 are treated as zero. To respect the
// parent trace's `SampledFlag`, the `TraceIDRatioBased` sampler should be used
// as a delegate of a `Parent` sampler.
//
//nolint:revive // revive complains about stutter of `trace.TraceIDRatioBased`
func ( float64) Sampler {
	if  >= 1 {
		return AlwaysSample()
	}

	if  <= 0 {
		 = 0
	}

	return &traceIDRatioSampler{
		traceIDUpperBound: uint64( * (1 << 63)),
		description:       fmt.Sprintf("TraceIDRatioBased{%g}", ),
	}
}

type alwaysOnSampler struct{}

func (alwaysOnSampler) ( SamplingParameters) SamplingResult {
	return SamplingResult{
		Decision:   RecordAndSample,
		Tracestate: trace.SpanContextFromContext(.ParentContext).TraceState(),
	}
}

func (alwaysOnSampler) () string {
	return "AlwaysOnSampler"
}

// AlwaysSample returns a Sampler that samples every trace.
// Be careful about using this sampler in a production application with
// significant traffic: a new trace will be started and exported for every
// request.
func () Sampler {
	return alwaysOnSampler{}
}

type alwaysOffSampler struct{}

func (alwaysOffSampler) ( SamplingParameters) SamplingResult {
	return SamplingResult{
		Decision:   Drop,
		Tracestate: trace.SpanContextFromContext(.ParentContext).TraceState(),
	}
}

func (alwaysOffSampler) () string {
	return "AlwaysOffSampler"
}

// NeverSample returns a Sampler that samples no traces.
func () Sampler {
	return alwaysOffSampler{}
}

// ParentBased returns a sampler decorator which behaves differently,
// based on the parent of the span. If the span has no parent,
// the decorated sampler is used to make sampling decision. If the span has
// a parent, depending on whether the parent is remote and whether it
// is sampled, one of the following samplers will apply:
//   - remoteParentSampled(Sampler) (default: AlwaysOn)
//   - remoteParentNotSampled(Sampler) (default: AlwaysOff)
//   - localParentSampled(Sampler) (default: AlwaysOn)
//   - localParentNotSampled(Sampler) (default: AlwaysOff)
func ( Sampler,  ...ParentBasedSamplerOption) Sampler {
	return parentBased{
		root:   ,
		config: configureSamplersForParentBased(),
	}
}

type parentBased struct {
	root   Sampler
	config samplerConfig
}

func configureSamplersForParentBased( []ParentBasedSamplerOption) samplerConfig {
	 := samplerConfig{
		remoteParentSampled:    AlwaysSample(),
		remoteParentNotSampled: NeverSample(),
		localParentSampled:     AlwaysSample(),
		localParentNotSampled:  NeverSample(),
	}

	for ,  := range  {
		 = .apply()
	}

	return 
}

// samplerConfig is a group of options for parentBased sampler.
type samplerConfig struct {
	remoteParentSampled, remoteParentNotSampled Sampler
	localParentSampled, localParentNotSampled   Sampler
}

// ParentBasedSamplerOption configures the sampler for a particular sampling case.
type ParentBasedSamplerOption interface {
	apply(samplerConfig) samplerConfig
}

// WithRemoteParentSampled sets the sampler for the case of sampled remote parent.
func ( Sampler) ParentBasedSamplerOption {
	return remoteParentSampledOption{}
}

type remoteParentSampledOption struct {
	s Sampler
}

func ( remoteParentSampledOption) ( samplerConfig) samplerConfig {
	.remoteParentSampled = .s
	return 
}

// WithRemoteParentNotSampled sets the sampler for the case of remote parent
// which is not sampled.
func ( Sampler) ParentBasedSamplerOption {
	return remoteParentNotSampledOption{}
}

type remoteParentNotSampledOption struct {
	s Sampler
}

func ( remoteParentNotSampledOption) ( samplerConfig) samplerConfig {
	.remoteParentNotSampled = .s
	return 
}

// WithLocalParentSampled sets the sampler for the case of sampled local parent.
func ( Sampler) ParentBasedSamplerOption {
	return localParentSampledOption{}
}

type localParentSampledOption struct {
	s Sampler
}

func ( localParentSampledOption) ( samplerConfig) samplerConfig {
	.localParentSampled = .s
	return 
}

// WithLocalParentNotSampled sets the sampler for the case of local parent
// which is not sampled.
func ( Sampler) ParentBasedSamplerOption {
	return localParentNotSampledOption{}
}

type localParentNotSampledOption struct {
	s Sampler
}

func ( localParentNotSampledOption) ( samplerConfig) samplerConfig {
	.localParentNotSampled = .s
	return 
}

func ( parentBased) ( SamplingParameters) SamplingResult {
	 := trace.SpanContextFromContext(.ParentContext)
	if .IsValid() {
		if .IsRemote() {
			if .IsSampled() {
				return .config.remoteParentSampled.ShouldSample()
			}
			return .config.remoteParentNotSampled.ShouldSample()
		}

		if .IsSampled() {
			return .config.localParentSampled.ShouldSample()
		}
		return .config.localParentNotSampled.ShouldSample()
	}
	return .root.ShouldSample()
}

func ( parentBased) () string {
	return fmt.Sprintf("ParentBased{root:%s,remoteParentSampled:%s,"+
		"remoteParentNotSampled:%s,localParentSampled:%s,localParentNotSampled:%s}",
		.root.Description(),
		.config.remoteParentSampled.Description(),
		.config.remoteParentNotSampled.Description(),
		.config.localParentSampled.Description(),
		.config.localParentNotSampled.Description(),
	)
}