package zipkin

import (
	"fmt"
	"time"

	protogen "github.com/go-kratos/kratos/pkg/net/trace/proto"

	"github.com/openzipkin/zipkin-go/model"
	"github.com/openzipkin/zipkin-go/reporter"
	"github.com/openzipkin/zipkin-go/reporter/http"

	"github.com/go-kratos/kratos/pkg/net/trace"
)

type report struct {
	rpt reporter.Reporter
}

func newReport(c *Config) *report {
	return &report{
		rpt: http.NewReporter(c.Endpoint,
			http.Timeout(time.Duration(c.Timeout)),
			http.BatchSize(c.BatchSize),
		),
	}
}

// WriteSpan write a trace span to queue.
func (r *report) WriteSpan(raw *trace.Span) (err error) {
	ctx := raw.Context()
	traceID := model.TraceID{Low: ctx.TraceID}
	spanID := model.ID(ctx.SpanID)
	parentID := model.ID(ctx.ParentID)
	tags := raw.Tags()
	span := model.SpanModel{
		SpanContext: model.SpanContext{
			TraceID:  traceID,
			ID:       spanID,
			ParentID: &parentID,
		},
		Name:      raw.OperationName(),
		Timestamp: raw.StartTime(),
		Duration:  raw.Duration(),
		Tags:      make(map[string]string, len(tags)),
	}
	span.LocalEndpoint = &model.Endpoint{ServiceName: raw.ServiceName()}
	for _, tag := range tags {
		switch tag.Key {
		case trace.TagSpanKind:
			switch tag.Value.(string) {
			case "client":
				span.Kind = model.Client
			case "server":
				span.Kind = model.Server
			case "producer":
				span.Kind = model.Producer
			case "consumer":
				span.Kind = model.Consumer
			}
		default:
			v, ok := tag.Value.(string)
			if ok {
				span.Tags[tag.Key] = v
			} else {
				span.Tags[tag.Key] = fmt.Sprint(v)
			}
		}
	}
	//log save to zipkin annotation
	span.Annotations = r.converLogsToAnnotations(raw.Logs())
	r.rpt.Send(span)
	return
}

func (r *report) converLogsToAnnotations(logs []*protogen.Log) (annotations []model.Annotation) {
	annotations = make([]model.Annotation, 0, len(annotations))
	for _, lg := range logs {
		annotations = append(annotations, r.converLogToAnnotation(lg)...)
	}
	return annotations
}
func (r *report) converLogToAnnotation(log *protogen.Log) (annotations []model.Annotation) {
	annotations = make([]model.Annotation, 0, len(log.Fields))
	for _, field := range log.Fields {
		val := string(field.Value)
		annotation := model.Annotation{
			Timestamp: time.Unix(0, log.Timestamp),
			Value:     field.Key + " : " + val,
		}
		annotations = append(annotations, annotation)
	}
	return annotations
}

// Close close the report.
func (r *report) Close() error {
	return r.rpt.Close()
}
