package tracing

import (
	"context"
	"net/http"
	"testing"

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

func TestNewTraceContext(t *testing.T) {
	tc := NewTraceContext()
	assert.NotEmpty(t, tc.TraceID)
	assert.NotEmpty(t, tc.SpanID)
	assert.True(t, tc.Sampled)
	assert.Empty(t, tc.ParentSpan)
}

func TestNewChildSpan(t *testing.T) {
	parent := NewTraceContext()
	child := parent.NewChildSpan()

	assert.Equal(t, parent.TraceID, child.TraceID, "Child should have same trace ID")
	assert.NotEqual(t, parent.SpanID, child.SpanID, "Child should have different span ID")
	assert.Equal(t, parent.SpanID, child.ParentSpan, "Child parent should be parent's span ID")
	assert.Equal(t, parent.Sampled, child.Sampled, "Child should inherit sampled flag")
}

func TestToW3CTraceParent(t *testing.T) {
	tc := NewTraceContext()
	traceParent := tc.ToW3CTraceParent()

	// Format: version-trace_id-parent_id-flags
	// Version: 00
	// Trace ID: 32 hex chars
	// Parent ID: 16 hex chars (0000000000000000 si pas de parent)
	// Flags: 01 (sampled) ou 00 (not sampled)
	assert.Contains(t, traceParent, "00-", "Should start with version 00")
	assert.Len(t, traceParent, 55, "W3C traceparent should be 55 characters") // 00-32-16-2
}

func TestFromW3CTraceParent(t *testing.T) {
	// Format valide: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
	traceParent := "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01"

	tc, err := FromW3CTraceParent(traceParent)
	require.NoError(t, err)
	assert.Equal(t, "4bf92f3577b34da6a3ce929d0e0e4736", tc.TraceID)
	assert.Equal(t, "00f067aa0ba902b7", tc.ParentSpan)
	assert.True(t, tc.Sampled)
	assert.NotEmpty(t, tc.SpanID) // Nouveau span ID généré
}

func TestFromW3CTraceParent_InvalidFormat(t *testing.T) {
	_, err := FromW3CTraceParent("invalid")
	assert.Error(t, err)
}

func TestFromW3CTraceParent_UnsupportedVersion(t *testing.T) {
	_, err := FromW3CTraceParent("01-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01")
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "unsupported")
}

func TestExtractTraceContext_FromW3C(t *testing.T) {
	req, _ := http.NewRequest("GET", "http://example.com", nil)
	req.Header.Set(TraceParentHeader, "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01")

	tc := ExtractTraceContext(req)
	assert.NotNil(t, tc)
	assert.Equal(t, "4bf92f3577b34da6a3ce929d0e0e4736", tc.TraceID)
}

func TestExtractTraceContext_FromLegacy(t *testing.T) {
	req, _ := http.NewRequest("GET", "http://example.com", nil)
	req.Header.Set(LegacyTraceIDHeader, "test-trace-id")
	req.Header.Set(LegacySpanIDHeader, "test-span-id")

	tc := ExtractTraceContext(req)
	assert.NotNil(t, tc)
	assert.Equal(t, "test-trace-id", tc.TraceID)
	assert.Equal(t, "test-span-id", tc.SpanID)
}

func TestExtractTraceContext_New(t *testing.T) {
	req, _ := http.NewRequest("GET", "http://example.com", nil)

	tc := ExtractTraceContext(req)
	assert.NotNil(t, tc)
	assert.NotEmpty(t, tc.TraceID)
	assert.NotEmpty(t, tc.SpanID)
}

func TestInjectTraceContext(t *testing.T) {
	req, _ := http.NewRequest("GET", "http://example.com", nil)
	tc := NewTraceContext()

	InjectTraceContext(req, tc)

	assert.Equal(t, tc.ToW3CTraceParent(), req.Header.Get(TraceParentHeader))
	assert.Equal(t, tc.TraceID, req.Header.Get(LegacyTraceIDHeader))
	assert.Equal(t, tc.SpanID, req.Header.Get(LegacySpanIDHeader))
}

func TestWithTraceContext(t *testing.T) {
	ctx := context.Background()
	tc := NewTraceContext()

	ctx = WithTraceContext(ctx, tc)

	extracted := FromContext(ctx)
	assert.Equal(t, tc, extracted)
}

func TestFromContext_NotFound(t *testing.T) {
	ctx := context.Background()
	tc := FromContext(ctx)
	assert.Nil(t, tc)
}

func TestGetOrCreateTraceContext_Existing(t *testing.T) {
	ctx := context.Background()
	tc := NewTraceContext()
	ctx = WithTraceContext(ctx, tc)

	result := GetOrCreateTraceContext(ctx)
	assert.Equal(t, tc, result)
}

func TestGetOrCreateTraceContext_New(t *testing.T) {
	ctx := context.Background()
	result := GetOrCreateTraceContext(ctx)
	assert.NotNil(t, result)
	assert.NotEmpty(t, result.TraceID)
}
