package metrics

import (
	"context"
	"strconv"
	"strings"
	"time"

	"github.com/tidbcloud/tiinf/errs"
	"google.golang.org/grpc"
)

const (
	namespace = "dataservices"
)

func RegisterCentralMetrics() {
	subsystem := "central"
	httpRegister(subsystem)
	dataappMgmtRegister(subsystem)
	apikeyMgmtRegister(subsystem)
	endpointMgmtRegister(subsystem)
	endpointExecRegister(subsystem)
	downstreamRpcRegister(subsystem)
	downstreamHttpRegister(subsystem)
	sqlExecRegister(subsystem)
	ddbcacheRegister(subsystem)
	workerMetricsRegister(subsystem)
}

func RegisterDataApiExecutorMetrics() {
	subsystem := "dataapi_executor"
	sqlExecRegister(subsystem)
	httpRegister(subsystem)
	rpcRegister(subsystem)
	endpointExecRegister(subsystem)
	downstreamHttpRegister(subsystem)
	downstreamRpcRegister(subsystem)
	systemRegister(subsystem)
	ddbcacheRegister(subsystem)
}

func RegisterDataApiGatewayMetrics() {
	subsystem := "dataapi_gateway"
	sqlExecRegister(subsystem)
	httpRegister(subsystem)
	rpcRegister(subsystem)
	endpointExecRegister(subsystem)
	endpointCacheRegister(subsystem)
	downstreamHttpRegister(subsystem)
	downstreamRpcRegister(subsystem)
	ddbcacheRegister(subsystem)
}

func RegisterDataApiControlMetrics() {
	subsystem := "dataapi_control"
	httpRegister(subsystem)
	privateLinkRegister(subsystem)
	endpointExecRegister(subsystem)
	dataappMgmtRegister(subsystem)
	downstreamHttpRegister(subsystem)
	ddbcacheRegister(subsystem)
}

func RegisterDataApiMetrics() {
	subsystem := "dataapi"
	sqlExecRegister(subsystem)
	dataappMgmtRegister(subsystem)
	apikeyMgmtRegister(subsystem)
	endpointMgmtRegister(subsystem)
	endpointExecRegister(subsystem)
	rpcRegister(subsystem)
	downstreamHttpRegister(subsystem)
	downstreamRpcRegister(subsystem)
	ddbcacheRegister(subsystem)
}

func RegisterToolsMetrics() {
	subsystem := "tools"
	downstreamHttpRegister(subsystem)
	downstreamRpcRegister(subsystem)
}

func UnaryServerInterceptor() grpc.UnaryServerInterceptor {
	return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
		var (
			serviceName string
			methodName  string
			code        string
		)
		serviceName, methodName = splitMethodName(info.FullMethod)
		startTime := time.Now()
		resp, err := handler(ctx, req)

		if baseResp := errs.GetBaseResp(resp); baseResp != nil {
			code = strconv.FormatInt(baseResp.ErrCode, 10)
		} else if err != nil {
			code = "error"
		} else {
			code = "0"
		}
		RpcTotalCounter.WithLabelValues(serviceName, methodName, code).Inc()
		RpcCostHistogram.WithLabelValues(serviceName, methodName).Observe(time.Since(startTime).Seconds())
		return resp, err
	}
}

func splitMethodName(fullMethodName string) (string, string) {
	fullMethodName = strings.TrimPrefix(fullMethodName, "/") // remove leading slash
	if i := strings.Index(fullMethodName, "/"); i >= 0 {
		return fullMethodName[:i], fullMethodName[i+1:]
	}
	return "unknown", "unknown"
}
