package tracing

import (
	"context"
	"net/http"
	"strings"
	"sync"

	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/propagation"
	"go.opentelemetry.io/otel/trace"
)

const TraceIDHeader = "X-Trace-ID"

var instrumentDefaultHTTPClientOnce sync.Once

func ConfigurePropagation() {
	otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
		traceIDPropagator{},
		propagation.TraceContext{},
		propagation.Baggage{},
	))
}

// InstrumentDefaultHTTPClient instruments package-level helpers such as
// http.Get without changing http.DefaultTransport's concrete type. Some SDKs,
// including Alibaba Cloud credentials-go, require the default transport to
// remain a *http.Transport so they can clone it.
func InstrumentDefaultHTTPClient() {
	instrumentDefaultHTTPClientOnce.Do(func() {
		http.DefaultClient.Transport = NewHTTPTransport(http.DefaultClient.Transport)
	})
}

// NewHTTPTransport instruments a Clinic-owned HTTP client transport. A nil
// base uses the process default transport without mutating it.
func NewHTTPTransport(base http.RoundTripper) http.RoundTripper {
	if base == nil {
		base = http.DefaultTransport
	}
	if _, alreadyInstrumented := base.(*otelhttp.Transport); alreadyInstrumented {
		return base
	}
	return otelhttp.NewTransport(base)
}

type traceIDPropagator struct{}

func (traceIDPropagator) Inject(ctx context.Context, carrier propagation.TextMapCarrier) {
	spanContext := trace.SpanContextFromContext(ctx)
	if spanContext.HasTraceID() {
		carrier.Set(TraceIDHeader, spanContext.TraceID().String())
	}
}

func (traceIDPropagator) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context {
	traceID, err := trace.TraceIDFromHex(strings.TrimSpace(carrier.Get(TraceIDHeader)))
	if err != nil || !traceID.IsValid() {
		return ctx
	}
	spanContext := trace.NewSpanContext(trace.SpanContextConfig{
		TraceID:    traceID,
		SpanID:     trace.SpanID{1},
		TraceFlags: trace.FlagsSampled,
		Remote:     true,
	})
	return trace.ContextWithRemoteSpanContext(ctx, spanContext)
}

func (traceIDPropagator) Fields() []string {
	return []string{TraceIDHeader}
}
