package util

import (
	"errors"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestHexStringToTraceID(t *testing.T) {
	tc := []struct {
		id          string
		expected    []byte
		expectError error
	}{
		{
			id:       "12",
			expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12},
		},
		{
			id:       "1234567890abcdef", // 64 bit
			expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef},
		},
		{
			id:       "1234567890abcdef1234567890abcdef", // 128 bit
			expected: []byte{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef},
		},
		{
			id:          "121234567890abcdef1234567890abcdef", // value too long
			expected:    []byte{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef},
			expectError: errors.New("trace IDs can't be larger than 128 bits"),
		},
		{
			id:       "234567890abcdef", // odd length
			expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef},
		},
		{
			id:          "1234567890abcdef ", // trailing space
			expected:    nil,
			expectError: errors.New("trace IDs can only contain hex characters: invalid character ' ' at position 17"),
		},
	}

	for _, tt := range tc {
		t.Run(tt.id, func(t *testing.T) {
			actual, err := HexStringToTraceID(tt.id)

			if tt.expectError != nil {
				assert.Equal(t, tt.expectError, err)
				assert.Nil(t, actual)
				return
			}

			assert.NoError(t, err)
			assert.Equal(t, tt.expected, actual)
		})
	}
}

func TestTraceIDToHexString(t *testing.T) {
	tc := []struct {
		byteID  []byte
		traceID string
	}{
		{
			byteID:  []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12},
			traceID: "12",
		},
		{
			byteID:  []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef},
			traceID: "1234567890abcdef", // 64 bit
		},
		{
			byteID:  []byte{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef},
			traceID: "1234567890abcdef1234567890abcdef", // 128 bit
		},
		{
			byteID:  []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xa0},
			traceID: "12a0", // trailing zero
		},
	}

	for _, tt := range tc {
		t.Run(tt.traceID, func(t *testing.T) {
			actual := TraceIDToHexString(tt.byteID)

			assert.Equal(t, tt.traceID, actual)
		})
	}
}

func TestSpanIDToHexString(t *testing.T) {
	tc := []struct {
		byteID []byte
		spanID string
	}{
		{
			byteID: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12},
			spanID: "0000000000000012",
		},
		{
			byteID: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef},
			spanID: "1234567890abcdef", // 64 bit
		},
		{
			byteID: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0xa0},
			spanID: "00000000000012a0", // trailing zero
		},
		{
			byteID: []byte{0x12, 0xa0},
			spanID: "00000000000012a0", // less than 64 bytes
		},
		{
			byteID: []byte{0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef},
			spanID: "1234567890abcdef1234567890abcdef", // 128 bit
		},
		{
			byteID: []byte{0x00, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef, 0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef},
			spanID: "34567890abcdef1234567890abcdef", // 128 bit with leading zeroes
		},
	}

	for _, tt := range tc {
		t.Run(tt.spanID, func(t *testing.T) {
			actual := SpanIDToHexString(tt.byteID)

			assert.Equal(t, tt.spanID, actual)
		})
	}
}

func TestEqualHexStringTraceIDs(t *testing.T) {
	a := "82f6471b46d25e23418a0a99d4c2cda"
	b := "082f6471b46d25e23418a0a99d4c2cda"

	v, err := EqualHexStringTraceIDs(a, b)
	assert.Nil(t, err)
	assert.True(t, v)
}

func TestPadTraceIDTo16Bytes(t *testing.T) {

	tc := []struct {
		name     string
		tid      []byte
		expected []byte
	}{
		{
			name:     "small",
			tid:      []byte{0x01, 0x02},
			expected: []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02},
		},
		{
			name:     "exact",
			tid:      []byte{0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02},
			expected: []byte{0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02},
		},
		{ // least significant bits are preserved
			name:     "large",
			tid:      []byte{0x05, 0x05, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02},
			expected: []byte{0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02, 0x01, 0x02},
		},
	}

	for _, tt := range tc {
		t.Run(tt.name, func(t *testing.T) {
			assert.Equal(t, tt.expected, PadTraceIDTo16Bytes(tt.tid))
		})
	}
}
