/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package config

import (
	"github.com/creasty/defaults"

	"github.com/dubbogo/gost/log/logger"

	"github.com/pkg/errors"

	"go.opentelemetry.io/otel"
)

import (
	"dubbo.apache.org/dubbo-go/v3/common/extension"
	"dubbo.apache.org/dubbo-go/v3/otel/trace"
)

type OtelConfig struct {
	TraceConfig *OtelTraceConfig `yaml:"tracing" json:"trace,omitempty" property:"trace"`
}

type OtelTraceConfig struct {
	Enable      *bool   `default:"false" yaml:"enable" json:"enable,omitempty" property:"enable"`
	Exporter    string  `default:"stdout" yaml:"exporter" json:"exporter,omitempty" property:"exporter"` // stdout, jaeger, zipkin, otlp-http, otlp-grpc
	Endpoint    string  `default:"" yaml:"endpoint" json:"endpoint,omitempty" property:"endpoint"`
	Propagator  string  `default:"w3c" yaml:"propagator" json:"propagator,omitempty" property:"propagator"`       // one of w3c(standard), b3(for zipkin),
	SampleMode  string  `default:"ratio" yaml:"sample-mode" json:"sample-mode,omitempty" property:"sample-mode"`  // one of always, never, ratio
	SampleRatio float64 `default:"0.5" yaml:"sample-ratio" json:"sample-ratio,omitempty" property:"sample-ratio"` // [0.0, 1.0]
	Insecure    bool    `default:"false" yaml:"insecure" json:"insecure,omitempty" property:"insecure"`
}

func (oc *OtelConfig) Init(appConfig *ApplicationConfig) error {
	if oc == nil {
		return errors.New("otel config is nil")
	}
	if err := defaults.Set(oc); err != nil {
		return err
	}
	if err := verify(oc); err != nil {
		return err
	}
	if *oc.TraceConfig.Enable {
		extension.AddCustomShutdownCallback(extension.GetTraceShutdownCallback())
		return oc.TraceConfig.init(appConfig)
	}

	return nil
}

func (c *OtelTraceConfig) init(appConfig *ApplicationConfig) error {
	exporter, err := extension.GetTraceExporter(c.Exporter, c.toTraceProviderConfig(appConfig))
	if err != nil {
		return err
	}
	otel.SetTracerProvider(exporter.GetTracerProvider())
	otel.SetTextMapPropagator(exporter.GetPropagator())

	// print trace exporter configuration
	if c.Exporter == "stdout" {
		logger.Infof("enable %s trace provider with propagator: %s", c.Exporter, c.Propagator)
	} else {
		logger.Infof("enable %s trace provider with endpoint: %s, propagator: %s", c.Exporter, c.Endpoint, c.Propagator)
	}
	logger.Infof("sample mode: %s", c.SampleMode)
	if c.SampleMode == "ratio" {
		logger.Infof("sample ratio: %.2f", c.SampleRatio)
	}

	return nil
}

func (c *OtelTraceConfig) toTraceProviderConfig(a *ApplicationConfig) *trace.ExporterConfig {
	tpc := &trace.ExporterConfig{
		Exporter:         c.Exporter,
		Endpoint:         c.Endpoint,
		SampleMode:       c.SampleMode,
		SampleRatio:      c.SampleRatio,
		Propagator:       c.Propagator,
		ServiceNamespace: a.Organization,
		ServiceName:      a.Name,
		ServiceVersion:   a.Version,
		Insecure:         c.Insecure,
	}
	return tpc
}

type OtelConfigBuilder struct {
	otelConfig *OtelConfig
}

func NewOtelConfigBuilder() *OtelConfigBuilder {
	return &OtelConfigBuilder{
		otelConfig: &OtelConfig{
			TraceConfig: &OtelTraceConfig{},
		},
	}
}

func (ocb *OtelConfigBuilder) Build() *OtelConfig {
	return ocb.otelConfig
}

// TODO: dynamic config
