package grpctrace

import (
	"encoding/binary"
	"encoding/hex"
	"strconv"
	"strings"

	"code.justin.tv/common/chitin/internal/trace"
	"golang.org/x/net/context"
	"google.golang.org/grpc/metadata"
)

var (
	metadataKey = "chitin-txid"
)

// contextFromGRPCMetadata issues a new Context based on parent, but including
// the Trace values in the MD. If the Trace values are missing, a new
// transaction id (with high entropy) is issued.
func contextFromGRPCMetadata(parent context.Context, md metadata.MD) context.Context {
	var id [2]uint64
	var path []uint32

	// TODO(rhys): pull data out of MD
	slc := md[metadataKey]
	if len(slc) != 1 {
		return trace.NewTransaction(parent)
	}
	str := slc[0]
	rest := strings.TrimLeft(str, "0123456789abcdefABCDEF")
	hexPrefix := str[:len(str)-len(rest)]
	if len(hexPrefix) != len(id)*16 {
		return trace.NewTransaction(parent)
	}

	for i := range id {
		b, err := hex.DecodeString(hexPrefix[i*16 : (i+1)*16])
		if err != nil {
			return trace.NewTransaction(parent)
		}
		id[i] = binary.LittleEndian.Uint64(b)
	}

	for i, seg := range strings.Split(rest, ".") {
		if i == 0 {
			if seg != "" {
				return trace.NewTransaction(parent)
			}
			continue
		}
		id, err := strconv.ParseUint(seg, 10, 32)
		if err != nil {
			return trace.NewTransaction(parent)
		}
		path = append(path, uint32(id))
	}

	value := &trace.Transaction{
		TxID: id,
		Path: path,
	}
	return trace.NewContext(parent, value)
}

// augmentGRPCMetadata adds Trace transaction information to a gRPC metadata
// value. It does not modify the internal Trace record state.
func augmentGRPCMetadata(ctx context.Context, md metadata.MD) {
	value, ok := trace.FromContext(ctx)
	if !ok {
		delete(md, metadataKey)
		return
	}

	txid := trace.TXIDString(value.TxID)
	path := value.Path.String()

	md[metadataKey] = []string{txid + path}
}
