package ctxinfo

import (
	"context"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"

	"github.com/pingcap/errors"
	"github.com/tidbcloud/pkgv2/logid"
)

const (
	CtxInfoHeaderKey = "x-pingcap-ctxinfo"

	// signKey the sign hash salt
	signKey = "M8hvlXD788N4UlQO"
)

type ctxInfo struct {
	Logid string
}

type ctxInfoPkg struct {
	Payload   string
	Signature string
}

// InjectCtxInfo2Ctx inject all ctxInfo into ctx, such as: logid
func InjectCtxInfo2Ctx(ctx context.Context, ci *ctxInfo) context.Context {
	if ci.Logid != "" {
		ctx = logid.WithLogid(ctx, ci.Logid)
	}

	return ctx
}

// PackCtxInfo pack ctxInfo as ctxInfoPackage which has been sent via http header
// nolint: deadcode
func PackCtxInfo(ci *ctxInfo) ([]byte, error) {
	ciStr, err := json.Marshal(ci)
	if err != nil {
		return nil, errors.Errorf("can't marshal ctxInfo: %s", err)
	}
	signer := md5.New()
	_, _ = signer.Write(ciStr)
	_, _ = signer.Write([]byte(signKey))

	pkg := ctxInfoPkg{
		Payload:   string(ciStr),
		Signature: hex.EncodeToString(signer.Sum(nil)),
	}
	return json.Marshal(pkg)
}

// UnpackCtxInfo unpack ctxInfo from ctxInfoPackage data which can transfer via http header
func UnpackCtxInfo(data []byte) (*ctxInfo, error) {
	ciPkg := &ctxInfoPkg{}
	err := json.Unmarshal(data, ciPkg)
	if err != nil {
		return nil, errors.Errorf("can't unmarshal ctxInfoPkg: %s", err)
	}
	signer := md5.New()
	_, _ = signer.Write([]byte(ciPkg.Payload))
	_, _ = signer.Write([]byte(signKey))
	sign := hex.EncodeToString(signer.Sum(nil))
	if sign != ciPkg.Signature {
		return nil, errors.Errorf("invalid sign, actual=%s,expected=%s", ciPkg.Signature, sign)
	}

	ci := &ctxInfo{}
	err = json.Unmarshal([]byte(ciPkg.Payload), ci)
	if err != nil {
		return nil, errors.Errorf("can't unmarshal ctxInfo: %s", err)
	}
	return ci, nil
}

// makeCtxInfo make a context info struct. extract info from context, such as: logid
// nolint: unused
func makeCtxInfo(ctx context.Context) *ctxInfo {
	ci := &ctxInfo{}
	ci.Logid = logid.GetLogid(ctx)
	return ci
}
