package otel

import (
	"context"
	"fmt"
	"time"

	"github.com/gogf/gf/v2/net/gipv4"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/codes"
	otellog "go.opentelemetry.io/otel/log"
	oteltrace "go.opentelemetry.io/otel/trace"
)

var (
	globalManager         *Manager
	globalLogger          otellog.Logger
	globalRecoveryManager *ConnRecoveryManager
	serviceName           string
	collectorEndpoint     string
)

// InitWithConfig 使用配置初始化OTel系统
func InitWithConfig(config *Config) error {
	manager, err := NewOTelManager(config)
	if err != nil {
		return err
	}

	globalManager = manager
	serviceName = config.ServiceName
	collectorEndpoint = config.CollectorURL

	// 创建并启动连接恢复管理器
	globalRecoveryManager = NewConnRecoveryManager(manager)
	globalRecoveryManager.Start()

	return nil
}

// GetCurrentManager 获取当前OTel管理器
func GetCurrentManager() *Manager {
	return globalManager
}

// Shutdown 关闭OTel系统
func Shutdown(ctx context.Context) error {
	// 先关闭恢复管理器
	if globalRecoveryManager != nil {
		globalRecoveryManager.Stop()
	}

	if globalManager != nil {
		return globalManager.Shutdown(ctx)
	}
	return nil
}

// GetLogger 获取全局logger
func GetLogger() otellog.Logger {
	if globalManager != nil {
		return globalManager.CreateLogger("")
	}
	if globalLogger != nil {
		return globalLogger
	}
	return nil
}

// CreateSpan 创建新的span
func CreateSpan(ctx context.Context, name string, opts ...oteltrace.SpanStartOption) (context.Context, oteltrace.Span) {
	if globalManager != nil && globalManager.IsTracingEnabled() {
		tracer := globalManager.CreateTracer(name)
		return tracer.Start(ctx, name, opts...)
	}
	// 返回noop span
	tracer := oteltrace.NewNoopTracerProvider().Tracer(name)
	return tracer.Start(ctx, name, opts...)
}

// AddSpanAttribute 添加span属性
func AddSpanAttribute(ctx context.Context, key string, value interface{}) {
	span := oteltrace.SpanFromContext(ctx)
	if span != nil {
		switch v := value.(type) {
		case string:
			span.SetAttributes(attribute.String(key, v))
		case int:
			span.SetAttributes(attribute.Int(key, v))
		case int64:
			span.SetAttributes(attribute.Int64(key, v))
		case float64:
			span.SetAttributes(attribute.Float64(key, v))
		case bool:
			span.SetAttributes(attribute.Bool(key, v))
		default:
			span.SetAttributes(attribute.String(key, fmt.Sprintf("%v", v)))
		}
	}
}

// AddSpanEvent 添加span事件
func AddSpanEvent(ctx context.Context, name string, attrs ...attribute.KeyValue) {
	span := oteltrace.SpanFromContext(ctx)
	if span != nil {
		span.AddEvent(name, oteltrace.WithAttributes(attrs...))
	}
}

// SetSpanError 设置span错误
func SetSpanError(ctx context.Context, err error) {
	span := oteltrace.SpanFromContext(ctx)
	if span != nil {
		span.RecordError(err)
		span.SetStatus(codes.Error, err.Error())
	}
}

// SetSpanStatus 设置span状态
func SetSpanStatus(ctx context.Context, code codes.Code, message string) {
	span := oteltrace.SpanFromContext(ctx)
	if span != nil {
		span.SetStatus(code, message)
	}
}

// LogWithContext 在上下文中记录日志
func LogWithContext(ctx context.Context, level string, message string, attrs ...attribute.KeyValue) {
	logger := GetLogger()
	if logger == nil {
		return
	}

	record := otellog.Record{}
	record.SetTimestamp(time.Now())
	record.SetSeverityText(level)
	record.SetBody(otellog.StringValue(message))

	// 转换属性为otel log格式
	logAttrs := make([]otellog.KeyValue, 0, len(attrs))
	for _, attr := range attrs {
		switch attr.Value.Type() {
		case attribute.STRING:
			logAttrs = append(logAttrs, otellog.String(string(attr.Key), attr.Value.AsString()))
		case attribute.INT64:
			logAttrs = append(logAttrs, otellog.Int64(string(attr.Key), attr.Value.AsInt64()))
		case attribute.FLOAT64:
			logAttrs = append(logAttrs, otellog.Float64(string(attr.Key), attr.Value.AsFloat64()))
		case attribute.BOOL:
			logAttrs = append(logAttrs, otellog.Bool(string(attr.Key), attr.Value.AsBool()))
		default:
			logAttrs = append(logAttrs, otellog.String(string(attr.Key), attr.Value.Emit()))
		}
	}

	// 添加上下文属性
	spanCtx := oteltrace.SpanFromContext(ctx)
	if spanCtx != nil && spanCtx.SpanContext().IsValid() {
		spanAttrs := []otellog.KeyValue{
			otellog.String("trace_id", spanCtx.SpanContext().TraceID().String()),
			otellog.String("span_id", spanCtx.SpanContext().SpanID().String()),
		}
		record.AddAttributes(append(spanAttrs, logAttrs...)...)
	} else {
		record.AddAttributes(logAttrs...)
	}

	logger.Emit(ctx, record)
}

// WithTracer 使用tracer执行函数
func WithTracer(ctx context.Context, operationName string, fn func(context.Context) error, opts ...oteltrace.SpanStartOption) error {
	ctx, span := CreateSpan(ctx, operationName, opts...)
	defer span.End()

	if err := fn(ctx); err != nil {
		SetSpanError(ctx, err)
		return err
	}

	SetSpanStatus(ctx, codes.Ok, "operation completed successfully")
	return nil
}

// GetTraceID 获取当前trace ID
func GetTraceID(ctx context.Context) string {
	span := oteltrace.SpanFromContext(ctx)
	if span != nil && span.SpanContext().IsValid() {
		return span.SpanContext().TraceID().String()
	}
	return ""
}

// GetSpanID 获取当前span ID
func GetSpanID(ctx context.Context) string {
	span := oteltrace.SpanFromContext(ctx)
	if span != nil && span.SpanContext().IsValid() {
		return span.SpanContext().SpanID().String()
	}
	return ""
}

// SetGlobalManager 设置全局管理器（主要用于测试）
func SetGlobalManager(manager *Manager) {
	globalManager = manager
	if manager != nil {
		config := manager.GetConfig()
		serviceName = config.ServiceName
		collectorEndpoint = config.CollectorURL
	}
}

// GetServiceName 获取服务名
func GetServiceName() string {
	return serviceName
}

// GetCollectorEndpoint 获取收集器端点
func GetCollectorEndpoint() string {
	return collectorEndpoint
}

// Reset 重置全局状态（主要用于测试）
func Reset() {
	globalManager = nil
	globalLogger = nil
	globalRecoveryManager = nil
	serviceName = ""
	collectorEndpoint = ""
}

// GetRecoveryManager 获取连接恢复管理器
func GetRecoveryManager() *ConnRecoveryManager {
	return globalRecoveryManager
}

// IsConnected 检查OTel连接是否正常
func IsConnected() bool {
	if globalRecoveryManager != nil {
		return globalRecoveryManager.IsConnected()
	}
	return globalManager != nil
}

// getHostIP 获取主机IP
func getHostIP() string {
	if intranetIPArray, err := gipv4.GetIntranetIpArray(); err == nil && len(intranetIPArray) > 0 {
		return intranetIPArray[0]
	}
	if ipArray, err := gipv4.GetIpArray(); err == nil && len(ipArray) > 0 {
		return ipArray[0]
	}
	return "localhost"
}
