// Package otelx 是 OpenTelemetry 链路追踪的统一启动器与门面。
//
// 各服务 main 启动时调一次 Init(ctx, "服务名")，即:
//   - 注册全局 W3C TraceContext 传播器（即便禁用导出，trace_id 仍能跨服务串起来）；
//   - 把 span 经 OTLP/HTTP 批量导出到 OTEL_EXPORTER_OTLP_ENDPOINT（默认 localhost:4318，即 docker 里的 Jaeger）。
//
// 业务代码用 otelx.Tracer().Start(ctx, "span 名") 开 span；跨 NATS 的传播由 bus 层自动完成。
// 设 OTEL_SDK_DISABLED=true 可整体关闭导出（仍保留传播器，零依赖运行）。
package otelx

import (
	"context"
	"log"
	"os"
	"strings"

	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp"
	"go.opentelemetry.io/otel/propagation"
	"go.opentelemetry.io/otel/sdk/resource"
	sdktrace "go.opentelemetry.io/otel/sdk/trace"
	semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
	"go.opentelemetry.io/otel/trace"
)

// scopeName 是本项目所有 span 的 instrumentation scope（Jaeger 里按此归组）。
const scopeName = "github.com/sundynix/sundynix-agentix"

const defaultEndpoint = "localhost:4318" // Jaeger all-in-one 的 OTLP/HTTP 入口

// Init 初始化全局链路追踪。返回 shutdown，进程退出前调用以 flush 残留 span。
// 导出器构建失败 / Jaeger 不在线都不阻断启动（批处理异步丢弃），保证可观测性是「增益」而非「依赖」。
func Init(ctx context.Context, serviceName string) (func(context.Context) error, error) {
	// 传播器始终注册：哪怕本服务不导出，也要把上游 traceparent 透传给下游。
	otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
		propagation.TraceContext{}, propagation.Baggage{},
	))

	noop := func(context.Context) error { return nil }

	if strings.EqualFold(os.Getenv("OTEL_SDK_DISABLED"), "true") {
		log.Printf("[otel] 导出已禁用 (OTEL_SDK_DISABLED=true)，service=%s", serviceName)
		return noop, nil
	}

	endpoint := os.Getenv("OTEL_EXPORTER_OTLP_ENDPOINT")
	if endpoint == "" {
		endpoint = defaultEndpoint
	}
	// 容忍带 scheme 的写法（http://host:port），otlptracehttp.WithEndpoint 只要 host:port。
	endpoint = strings.TrimPrefix(strings.TrimPrefix(endpoint, "http://"), "https://")
	endpoint = strings.TrimSuffix(endpoint, "/")

	exp, err := otlptracehttp.New(ctx,
		otlptracehttp.WithEndpoint(endpoint),
		otlptracehttp.WithInsecure(), // 本地 Jaeger 无 TLS
	)
	if err != nil {
		log.Printf("[otel] 导出器构建失败（降级为不导出）: %v", err)
		return noop, nil
	}

	res, _ := resource.Merge(resource.Default(), resource.NewWithAttributes(
		semconv.SchemaURL, semconv.ServiceName(serviceName),
	))
	tp := sdktrace.NewTracerProvider(
		sdktrace.WithBatcher(exp),
		sdktrace.WithResource(res),
	)
	otel.SetTracerProvider(tp)
	log.Printf("[otel] 链路追踪已启用 service=%s → otlp http://%s", serviceName, endpoint)
	return tp.Shutdown, nil
}

// Tracer 返回统一 scope 的 tracer，供业务开 span。
func Tracer() trace.Tracer { return otel.Tracer(scopeName) }
