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

package sdk

import (
	
	
	
	
	
	
	
	
	

	
	
	semconv 
	
	

	
)

type span struct {
	noop.Span

	spanContext trace.SpanContext
	sampled     atomic.Bool

	mu     sync.Mutex
	traces *telemetry.Traces
	span   *telemetry.Span
}

func ( *span) () trace.SpanContext {
	if  == nil {
		return trace.SpanContext{}
	}
	// s.spanContext is immutable, do not acquire lock s.mu.
	return .spanContext
}

func ( *span) () bool {
	if  == nil {
		return false
	}

	return .sampled.Load()
}

func ( *span) ( codes.Code,  string) {
	if  == nil || !.sampled.Load() {
		return
	}

	.mu.Lock()
	defer .mu.Unlock()

	if .span.Status == nil {
		.span.Status = new(telemetry.Status)
	}

	.span.Status.Message = 

	switch  {
	case codes.Unset:
		.span.Status.Code = telemetry.StatusCodeUnset
	case codes.Error:
		.span.Status.Code = telemetry.StatusCodeError
	case codes.Ok:
		.span.Status.Code = telemetry.StatusCodeOK
	}
}

func ( *span) ( ...attribute.KeyValue) {
	if  == nil || !.sampled.Load() {
		return
	}

	.mu.Lock()
	defer .mu.Unlock()

	 := maxSpan.Attrs
	if  == 0 {
		// No attributes allowed.
		.span.DroppedAttrs += uint32(len())
		return
	}

	 := make(map[string]int)
	for ,  := range .span.Attrs {
		[.Key] = 
	}

	for ,  := range  {
		 := convAttrValue(.Value)
		if .Empty() {
			.span.DroppedAttrs++
			continue
		}

		if ,  := [string(.Key)];  {
			.span.Attrs[] = telemetry.Attr{
				Key:   string(.Key),
				Value: ,
			}
		} else if  < 0 || len(.span.Attrs) <  {
			.span.Attrs = append(.span.Attrs, telemetry.Attr{
				Key:   string(.Key),
				Value: ,
			})
			[string(.Key)] = len(.span.Attrs) - 1
		} else {
			.span.DroppedAttrs++
		}
	}
}

// convCappedAttrs converts up to limit attrs into a []telemetry.Attr. The
// number of dropped attributes is also returned.
func convCappedAttrs( int,  []attribute.KeyValue) ([]telemetry.Attr, uint32) {
	if  == 0 {
		return nil, uint32(len())
	}

	if  < 0 {
		// Unlimited.
		return convAttrs(), 0
	}

	 = min(len(), )
	return convAttrs([:]), uint32(len() - )
}

func convAttrs( []attribute.KeyValue) []telemetry.Attr {
	if len() == 0 {
		// Avoid allocations if not necessary.
		return nil
	}

	 := make([]telemetry.Attr, 0, len())
	for ,  := range  {
		 := string(.Key)
		 := convAttrValue(.Value)
		if .Empty() {
			continue
		}
		 = append(, telemetry.Attr{Key: , Value: })
	}
	return 
}

func convAttrValue( attribute.Value) telemetry.Value {
	switch .Type() {
	case attribute.BOOL:
		return telemetry.BoolValue(.AsBool())
	case attribute.INT64:
		return telemetry.Int64Value(.AsInt64())
	case attribute.FLOAT64:
		return telemetry.Float64Value(.AsFloat64())
	case attribute.STRING:
		 := truncate(maxSpan.AttrValueLen, .AsString())
		return telemetry.StringValue()
	case attribute.BOOLSLICE:
		 := .AsBoolSlice()
		 := make([]telemetry.Value, 0, len())
		for ,  := range  {
			 = append(, telemetry.BoolValue())
		}
		return telemetry.SliceValue(...)
	case attribute.INT64SLICE:
		 := .AsInt64Slice()
		 := make([]telemetry.Value, 0, len())
		for ,  := range  {
			 = append(, telemetry.Int64Value())
		}
		return telemetry.SliceValue(...)
	case attribute.FLOAT64SLICE:
		 := .AsFloat64Slice()
		 := make([]telemetry.Value, 0, len())
		for ,  := range  {
			 = append(, telemetry.Float64Value())
		}
		return telemetry.SliceValue(...)
	case attribute.STRINGSLICE:
		 := .AsStringSlice()
		 := make([]telemetry.Value, 0, len())
		for ,  := range  {
			 = truncate(maxSpan.AttrValueLen, )
			 = append(, telemetry.StringValue())
		}
		return telemetry.SliceValue(...)
	}
	return telemetry.Value{}
}

// truncate returns a truncated version of s such that it contains less than
// the limit number of characters. Truncation is applied by returning the limit
// number of valid characters contained in s.
//
// If limit is negative, it returns the original string.
//
// UTF-8 is supported. When truncating, all invalid characters are dropped
// before applying truncation.
//
// If s already contains less than the limit number of bytes, it is returned
// unchanged. No invalid characters are removed.
func truncate( int,  string) string {
	// This prioritize performance in the following order based on the most
	// common expected use-cases.
	//
	//  - Short values less than the default limit (128).
	//  - Strings with valid encodings that exceed the limit.
	//  - No limit.
	//  - Strings with invalid encodings that exceed the limit.
	if  < 0 || len() <=  {
		return 
	}

	// Optimistically, assume all valid UTF-8.
	var  strings.Builder
	 := 0
	for ,  := range  {
		if  != utf8.RuneError {
			++
			if  >  {
				return [:]
			}
			continue
		}

		,  := utf8.DecodeRuneInString([:])
		if  == 1 {
			// Invalid encoding.
			.Grow(len() - 1)
			_, _ = .WriteString([:])
			 = [:]
			break
		}
	}

	// Fast-path, no invalid input.
	if .Cap() == 0 {
		return 
	}

	// Truncate while validating UTF-8.
	for  := 0;  < len() &&  < ; {
		 := []
		if  < utf8.RuneSelf {
			// Optimization for single byte runes (common case).
			_ = .WriteByte()
			++
			++
			continue
		}

		,  := utf8.DecodeRuneInString([:])
		if  == 1 {
			// We checked for all 1-byte runes above, this is a RuneError.
			++
			continue
		}

		_, _ = .WriteString([ : +])
		 += 
		++
	}

	return .String()
}

func ( *span) ( ...trace.SpanEndOption) {
	if  == nil || !.sampled.Swap(false) {
		return
	}

	// s.end exists so the lock (s.mu) is not held while s.ended is called.
	.ended(.end())
}

func ( *span) ( []trace.SpanEndOption) []byte {
	.mu.Lock()
	defer .mu.Unlock()

	 := trace.NewSpanEndConfig(...)
	if  := .Timestamp(); !.IsZero() {
		.span.EndTime = .Timestamp()
	} else {
		.span.EndTime = time.Now()
	}

	,  := json.Marshal(.traces) // TODO: do not ignore this error.
	return 
}

// Expected to be implemented in eBPF.
//
//go:noinline
func (*span) ( []byte) { ended() }

// ended is used for testing.
var ended = func([]byte) {}

func ( *span) ( error,  ...trace.EventOption) {
	if  == nil ||  == nil || !.sampled.Load() {
		return
	}

	 := trace.NewEventConfig(...)

	 := .Attributes()
	 = append(,
		semconv.ExceptionType(typeStr()),
		semconv.ExceptionMessage(.Error()),
	)
	if .StackTrace() {
		 := make([]byte, 2048)
		 := runtime.Stack(, false)
		 = append(, semconv.ExceptionStacktrace(string([0:])))
	}

	.mu.Lock()
	defer .mu.Unlock()

	.addEvent(semconv.ExceptionEventName, .Timestamp(), )
}

func typeStr( any) string {
	 := reflect.TypeOf()
	if .PkgPath() == "" && .Name() == "" {
		// Likely a builtin type.
		return .String()
	}
	return fmt.Sprintf("%s.%s", .PkgPath(), .Name())
}

func ( *span) ( string,  ...trace.EventOption) {
	if  == nil || !.sampled.Load() {
		return
	}

	 := trace.NewEventConfig(...)

	.mu.Lock()
	defer .mu.Unlock()

	.addEvent(, .Timestamp(), .Attributes())
}

// addEvent adds an event with name and attrs at tStamp to the span. The span
// lock (s.mu) needs to be held by the caller.
func ( *span) ( string,  time.Time,  []attribute.KeyValue) {
	 := maxSpan.Events

	if  == 0 {
		.span.DroppedEvents++
		return
	}

	if  > 0 && len(.span.Events) ==  {
		// Drop head while avoiding allocation of more capacity.
		copy(.span.Events[:-1], .span.Events[1:])
		.span.Events = .span.Events[:-1]
		.span.DroppedEvents++
	}

	 := &telemetry.SpanEvent{Time: , Name: }
	.Attrs, .DroppedAttrs = convCappedAttrs(maxSpan.EventAttrs, )

	.span.Events = append(.span.Events, )
}

func ( *span) ( trace.Link) {
	if  == nil || !.sampled.Load() {
		return
	}

	 := maxSpan.Links

	.mu.Lock()
	defer .mu.Unlock()

	if  == 0 {
		.span.DroppedLinks++
		return
	}

	if  > 0 && len(.span.Links) ==  {
		// Drop head while avoiding allocation of more capacity.
		copy(.span.Links[:-1], .span.Links[1:])
		.span.Links = .span.Links[:-1]
		.span.DroppedLinks++
	}

	.span.Links = append(.span.Links, convLink())
}

func convLinks( []trace.Link) []*telemetry.SpanLink {
	 := make([]*telemetry.SpanLink, 0, len())
	for ,  := range  {
		 = append(, convLink())
	}
	return 
}

func convLink( trace.Link) *telemetry.SpanLink {
	 := &telemetry.SpanLink{
		TraceID:    telemetry.TraceID(.SpanContext.TraceID()),
		SpanID:     telemetry.SpanID(.SpanContext.SpanID()),
		TraceState: .SpanContext.TraceState().String(),
		Flags:      uint32(.SpanContext.TraceFlags()),
	}
	.Attrs, .DroppedAttrs = convCappedAttrs(maxSpan.LinkAttrs, .Attributes)

	return 
}

func ( *span) ( string) {
	if  == nil || !.sampled.Load() {
		return
	}

	.mu.Lock()
	defer .mu.Unlock()

	.span.Name = 
}

func (*span) () trace.TracerProvider { return TracerProvider() }