//revive:disable:var-naming

package grpc_http

//revive:enable:var-naming

import (
	"context"
	"net/http"
	"strings"

	"github.com/pingcap/errors"
	"github.com/tidbcloud/pkgv2/auth/auth0"
	"github.com/tidbcloud/pkgv2/auth/util"
	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
)

const (
	setHeaderPrefix    = "x-set-header_"
	queryHeaderPrefix  = "x-query-header_"
	cookieHeaderPrefix = "x-cookie-header_"
	httpMethod         = "x-http-method"
	httpURI            = "x-http-uri"
)

func Method(ctx context.Context) string {
	method, ok := grpc.Method(ctx)
	if !ok {
		return ""
	}
	return method
}

// FirstHeader retrives the header content from the context.
// If multiples of the header were given, it gives the first.
func FirstHeader(ctx context.Context, header string) string {
	headers, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return ""
	}
	headerTexts := headers[strings.ToLower(header)]
	if len(headerTexts) < 1 {
		return ""
	}
	return headerTexts[0]
}

func SetHeader(grpcCtx context.Context, name string, value string) error {
	return grpc.SetHeader(grpcCtx, metadata.New(map[string]string{
		setHeaderPrefix + name: value,
	}))
}

func GetHttpMethod(ctx context.Context) string {
	httpMethod := strings.ToUpper(FirstHeader(ctx, httpMethod))
	httpMethodFromContext := ctx.Value(auth0.AuthHttpMethod)
	if httpMethodFromContext != nil {
		httpMethod = strings.ToUpper(httpMethodFromContext.(string))
	}
	return httpMethod
}

func GetHttpURI(ctx context.Context) string {
	httpURI := FirstHeader(ctx, httpURI)
	httpURIFromContext := ctx.Value(auth0.AuthHttpURI)
	if httpURIFromContext != nil {
		httpURI = httpURIFromContext.(string)
	}
	return httpURI
}

func GetQuery(ctx context.Context, name string) string {
	return FirstHeader(ctx, strings.ToLower(queryHeaderPrefix+name))
}

func GetCookie(ctx context.Context, name string) string {
	return FirstHeader(ctx, strings.ToLower(cookieHeaderPrefix+name))
}

// GetBearerToken gets the bearer token string in `Bearer {xxx}` format in the Authorization header
func GetBearerToken(ctx context.Context) (string, error) {
	bearerToken := FirstHeader(ctx, "authorization")
	if bearerToken != "" {
		tokenRaw, err := util.ExtractBearerToken(bearerToken)
		if err != nil {
			newErr := errors.Annotate(err, "Auth Header invalid")
			return "", newErr
		}
		return tokenRaw, nil
	}
	return GetQuery(ctx, "jwt"), nil
}

func AddHeaderInResponseMatcher(key string) (string, bool) {
	if strings.HasPrefix(key, setHeaderPrefix) {
		return strings.TrimPrefix(key, setHeaderPrefix), true
	}
	return key, false
}

func AddHttpDetail(_ context.Context, r *http.Request) metadata.MD {
	m := make(map[string]string)
	m[httpURI] = r.RequestURI
	m[httpMethod] = r.Method
	for key, value := range r.URL.Query() {
		m[queryHeaderPrefix+key] = value[0]
	}
	for _, c := range r.Cookies() {
		m[cookieHeaderPrefix+c.Name] = c.Value
	}
	return metadata.New(m)
}
