//go:build !amd64
// +build !amd64

package tool

import (
	"context"
	"net/http"

	sky "github.com/SkyAPM/go2sky"
	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
	"google.golang.org/grpc/metadata"
	v3 "skywalking.apache.org/repo/goapi/collect/language/agent/v3"
)

type ComponentType int32

const (
	GRPC                     ComponentType = 23
	ServiceCombServiceCenter ComponentType = 5002
	GoMuxServer              ComponentType = 5017
	MOSN                     ComponentType = 5003
	GoHttpServer             ComponentType = 5004
)

func (c ComponentType) Int32() int32 {
	return int32(c)
}

func EntrySpanForGrpc(ctx context.Context, operationName string) (sky.Span, context.Context, error) {
	md, _ := metadata.FromIncomingContext(ctx)
	var tracer = sky.GetGlobalTracer()

	span, ctx, err := tracer.CreateEntrySpan(ctx, operationName, func(headerKey string) (string, error) {
		kv := md.Get(headerKey)
		if len(kv) > 0 {
			return kv[0], nil
		}
		return ``, nil
	})
	if err != nil {
		return span, ctx, err
	}

	span.SetSpanLayer(v3.SpanLayer_RPCFramework)

	return span, ctx, err
}

func ExitSpanForGrpc(ctx context.Context, operationName, peer string) (sky.Span, context.Context, error) {
	md, _ := metadata.FromIncomingContext(ctx)
	var tracer = sky.GetGlobalTracer()

	span, err := tracer.CreateExitSpan(ctx, operationName, peer, func(headerKey, headerValue string) error {
		// should not use md.Append
		md.Set(headerKey, headerValue)
		return nil
	})

	if err != nil {
		return span, ctx, err
	}
	span.SetComponent(GRPC.Int32())
	span.SetSpanLayer(v3.SpanLayer_RPCFramework)

	return span, metadata.NewOutgoingContext(ctx, md), err
}

func LocalSpanForGrpc(ctx context.Context) (sky.Span, context.Context, error) {
	md, _ := metadata.FromIncomingContext(ctx)
	var tracer = sky.GetGlobalTracer()

	span, ctx, err := tracer.CreateLocalSpan(ctx)
	if err != nil {
		return span, ctx, err
	}

	span.SetSpanLayer(v3.SpanLayer_RPCFramework)
	span.SetOperationName(GetCallFunc(2))

	return span, metadata.NewOutgoingContext(ctx, md), err
}

func EntrySpanForHttp(ctx context.Context, r *http.Request, operationName string) (sky.Span, context.Context, error) {
	var tracer = sky.GetGlobalTracer()

	span, ctx, err := tracer.CreateEntrySpan(ctx, operationName, func(headerKey string) (string, error) {
		return r.Header.Get(headerKey), nil
	})
	if err != nil {
		return span, ctx, err
	}

	span.SetSpanLayer(v3.SpanLayer_Http)

	return span, ctx, err
}

func ExitSpanForHttp(ctx context.Context, r *http.Request, operationName, peer string) (sky.Span, context.Context, error) {
	var tracer = sky.GetGlobalTracer()

	span, err := tracer.CreateExitSpan(ctx, operationName, peer, func(headerKey, headerValue string) error {
		r.Header.Add(headerKey, headerValue)
		r.Header.Add(runtime.MetadataHeaderPrefix+headerKey, headerValue)
		return nil
	})
	if err != nil {
		return span, ctx, err
	}
	span.SetSpanLayer(v3.SpanLayer_Http)

	return span, ctx, err
}

func LocalSpan(ctx context.Context) (sky.Span, context.Context, error) {
	var tracer = sky.GetGlobalTracer()

	span, ctx1, err := tracer.CreateLocalSpan(ctx)
	if err != nil {
		return span, ctx, err
	}

	// user should set after create
	//span.SetComponent(GRPC.Int32())
	//span.SetSpanLayer(v3.SpanLayer_RPCFramework)
	span.SetOperationName(GetCallFunc(3))

	return span, ctx1, err
}
