package tracer

import (
	"context"
	"fmt"
	"os"

	semconv "go.opentelemetry.io/collector/model/semconv/v1.5.0"
	"go.opentelemetry.io/contrib/propagators/b3"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace"
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
	"go.opentelemetry.io/otel/propagation"
	"go.opentelemetry.io/otel/sdk/resource"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/encoding/gzip"

	"github.com/tidbcloud/infra-provider/regional-server/pkg/common/utils"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/consts"
)

func initLightstepExporter(token string) (*otlptrace.Exporter, error) {
	headers := map[string]string{
		"lightstep-access-token": token,
	}

	return otlptrace.New(
		context.Background(),
		otlptracegrpc.NewClient(
			otlptracegrpc.WithTLSCredentials(credentials.NewClientTLSFromCert(nil, "")),
			otlptracegrpc.WithEndpoint(consts.LightstepEndpoint),
			otlptracegrpc.WithHeaders(headers),
			otlptracegrpc.WithCompressor(gzip.Name),
		),
	)
}

func newResource(svcName string) *resource.Resource {
	r := resource.Environment()
	hostnameSet := false
	for iter := r.Iter(); iter.Next(); {
		if iter.Attribute().Key == semconv.AttributeHostName && len(iter.Attribute().Value.Emit()) > 0 {
			hostnameSet = true
		}
	}

	attributes := []attribute.KeyValue{
		attribute.String(semconv.AttributeTelemetrySDKName, "launcher"),
		attribute.String(semconv.AttributeTelemetrySDKLanguage, "go"),
		attribute.String(semconv.AttributeTelemetrySDKVersion, "1.0.0"),
		attribute.String(semconv.AttributeServiceName, svcName),
		attribute.String(semconv.AttributeServiceVersion, utils.GitHash),
	}

	if !hostnameSet {
		hostname, err := os.Hostname()
		if err == nil {
			attributes = append(attributes, attribute.String(semconv.AttributeHostName, hostname))
		}
	}

	attributes = append(r.Attributes(), attributes...)

	res, _ := resource.New(
		context.Background(),
		resource.WithAttributes(attributes...),
	)

	return res
}

func initLightstepTracerProvider(token string, svcName string) (*sdktrace.TracerProvider, error) {
	props := []propagation.TextMapPropagator{
		b3.New(b3.WithInjectEncoding(b3.B3MultipleHeader)),
	}
	otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(props...))

	exporter, err := initLightstepExporter(token)
	if err != nil {
		return nil, fmt.Errorf("failed to init the Lightstep exporter: %v", err)
	}

	bsp := sdktrace.NewBatchSpanProcessor(exporter)

	return sdktrace.NewTracerProvider(
		sdktrace.WithSampler(sdktrace.AlwaysSample()),
		sdktrace.WithSpanProcessor(bsp),
		sdktrace.WithResource(newResource(svcName)),
	), nil
}
