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

package global

import (
	"encoding/binary"
	"math"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"

	"go.opentelemetry.io/collector/pdata/pcommon"
	"go.opentelemetry.io/collector/pdata/ptrace"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/trace"

	"go.opentelemetry.io/auto/internal/pkg/instrumentation/context"
	"go.opentelemetry.io/auto/internal/pkg/instrumentation/kernel"
	"go.opentelemetry.io/auto/internal/pkg/instrumentation/pdataconv"
)

func TestProbeConvertEvent(t *testing.T) {
	start := time.Unix(0, time.Now().UnixNano()) // No wall clock.
	end := start.Add(1 * time.Second)

	startOffset := kernel.TimeToBootOffset(start)
	endOffset := kernel.TimeToBootOffset(end)

	traceID := trace.TraceID{1}
	spanID := trace.SpanID{1}

	var floatBuf [128]byte
	binary.LittleEndian.PutUint64(floatBuf[:], math.Float64bits(math.Pi))

	scope, url, spans := processFn(&event{
		BaseSpanProperties: context.BaseSpanProperties{
			StartTime:   startOffset,
			EndTime:     endOffset,
			SpanContext: context.EBPFSpanContext{TraceID: traceID, SpanID: spanID},
		},
		// span name: "Foo"
		SpanName: [64]byte{0x46, 0x6f, 0x6f},

		Attributes: attributesBuffer{
			AttrsKv: [16]attributeKeyVal{
				{
					ValLength: 0,
					Vtype:     uint8(attribute.BOOL),
					Reserved:  0,
					// "bool_key"
					Key: [32]byte{0x62, 0x6f, 0x6f, 0x6c, 0x5f, 0x6b, 0x65, 0x79},
					// true
					Value: [128]byte{0x01},
				},
				{
					ValLength: 0,
					Vtype:     uint8(attribute.STRING),
					Reserved:  0,
					// "string_key1"
					Key: [32]byte{0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6b, 0x65, 0x79, 0x31},
					// "string value 1"
					Value: [128]byte{
						0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20,
						0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x31,
					},
				},
				{
					ValLength: 0,
					Vtype:     uint8(attribute.FLOAT64),
					Reserved:  0,
					// "float_key"
					Key: [32]byte{0x66, 0x6c, 0x6f, 0x61, 0x74, 0x5f, 0x6b, 0x65, 0x79},
					// math.Pi
					Value: floatBuf,
				},
				{
					ValLength: 0,
					Vtype:     uint8(attribute.INT64),
					Reserved:  0,
					// "int_key"
					Key: [32]byte{0x69, 0x6e, 0x74, 0x5f, 0x6b, 0x65, 0x79},
					// 42
					Value: [128]byte{42},
				},
				{
					ValLength: 0,
					Vtype:     uint8(attribute.STRING),
					Reserved:  0,
					// "string_key2"
					Key: [32]byte{0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6b, 0x65, 0x79, 0x32},
					// "string value 2"
					Value: [128]byte{
						0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x20,
						0x76, 0x61, 0x6c, 0x75, 0x65, 0x20, 0x32,
					},
				},
			},
			ValidAttrs: 5,
		},
		TracerID: tracerID{
			// "user-tracer"
			Name: [128]byte{0x75, 0x73, 0x65, 0x72, 0x2d, 0x74, 0x72, 0x61, 0x63, 0x65, 0x72},
			// "v1"
			Version: [32]byte{0x76, 0x31},
			// "user-schema"
			SchemaURL: [128]byte{0x75, 0x73, 0x65, 0x72, 0x2d, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61},
		},
	})

	wantScope := pcommon.NewInstrumentationScope()
	wantScope.SetName("user-tracer")
	wantScope.SetVersion("v1")
	assert.Equal(t, wantScope, scope)

	assert.Equal(t, "user-schema", url)

	wantSpans := ptrace.NewSpanSlice()
	span := wantSpans.AppendEmpty()
	span.SetName("Foo")
	span.SetKind(ptrace.SpanKindClient)
	span.SetStartTimestamp(kernel.BootOffsetToTimestamp(startOffset))
	span.SetEndTimestamp(kernel.BootOffsetToTimestamp(endOffset))
	span.SetTraceID(pcommon.TraceID(traceID))
	span.SetSpanID(pcommon.SpanID(spanID))
	span.SetFlags(uint32(trace.FlagsSampled))
	pdataconv.Attributes(
		span.Attributes(),
		attribute.Bool("bool_key", true),
		attribute.String("string_key1", "string value 1"),
		attribute.Float64("float_key", math.Pi),
		attribute.Int64("int_key", 42),
		attribute.String("string_key2", "string value 2"),
	)
	assert.Equal(t, wantSpans, spans)
}
